]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/directory.c
1. Respect the kFPDeleteInhibitBit attribute
[netatalk.git] / etc / afpd / directory.c
1 /*
2  * $Id: directory.c,v 1.29 2002-03-24 17:43:39 jmarcus Exp $
3  *
4  * Copyright (c) 1990,1993 Regents of The University of Michigan.
5  * All Rights Reserved.  See COPYRIGHT.
6  *
7  * 19 jan 2000 implemented red-black trees for directory lookups
8  * (asun@cobalt.com).
9  */
10
11 #ifdef HAVE_CONFIG_H
12 #include "config.h"
13 #endif /* HAVE_CONFIG_H */
14
15 #include <atalk/logger.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <errno.h>
19 #include <sys/time.h>
20 #include <sys/param.h>
21 #include <netatalk/endian.h>
22 #include <atalk/adouble.h>
23 #include <atalk/afp.h>
24 #include <atalk/util.h>
25 #ifdef CNID_DB
26 #include <atalk/cnid.h>
27 #endif /* CNID_DB */
28 #include <utime.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <dirent.h>
32 #ifdef HAVE_FCNTL_H
33 #include <fcntl.h>
34 #endif /* HAVE_FCNTL_H */
35 #include <grp.h>
36 #include <pwd.h>
37
38 /* STDC check */
39 #if STDC_HEADERS
40 #include <string.h>
41 #else /* STDC_HEADERS */
42 #ifndef HAVE_STRCHR
43 #define strchr index
44 #define strrchr index
45 #endif /* HAVE_STRCHR */
46 char *strchr (), *strrchr ();
47 #ifndef HAVE_MEMCPY
48 #define memcpy(d,s,n) bcopy ((s), (d), (n))
49 #define memmove(d,s,n) bcopy ((s), (d), (n))
50 #endif /* ! HAVE_MEMCPY */
51 #endif /* STDC_HEADERS */
52
53 #include "directory.h"
54 #include "desktop.h"
55 #include "volume.h"
56 #include "fork.h"
57 #include "file.h"
58 #include "filedir.h"
59 #include "globals.h"
60 #include "unix.h"
61
62 #ifdef FORCE_UIDGID
63 #include "uid.h"
64 #endif /* FORCE_UIDGID */
65
66 struct dir      *curdir;
67
68 #define SENTINEL (&sentinel)
69 static struct dir sentinel = { SENTINEL, SENTINEL, NULL, DIRTREE_COLOR_BLACK,
70                                  NULL, NULL, NULL, NULL, NULL, 0, 0, NULL };
71 static struct dir       rootpar = { SENTINEL, SENTINEL, NULL, 0,
72                                 NULL, NULL, NULL, NULL, NULL, 0, 0, NULL };
73
74 /* (from IM: Toolbox Essentials)
75  * dirFinderInfo (DInfo) fields:
76  * field        bytes
77  * frRect       8    folder's window rectangle
78  * frFlags      2    flags
79  * frLocation   4    folder's location in window
80  * frView       2    folder's view (default == closedView (256))
81  *
82  * extended dirFinderInfo (DXInfo) fields:
83  * frScroll     4    scroll position
84  * frOpenChain: 4    directory ID chain of open folders
85  * frScript:    1    script flag and code
86  * frXFlags:    1    reserved
87  * frComment:   2    comment ID
88  * frPutAway:   4    home directory ID
89  */
90
91 /*
92  * redid did assignment for directories. now we use red-black trees.
93  * how exciting.
94  */
95 struct dir *
96             dirsearch( vol, did )
97             const struct vol    *vol;
98 u_int32_t       did;
99 {
100     struct dir  *dir;
101
102
103     /* check for 0 did */
104     if (!did)
105         return NULL;
106
107     if ( did == DIRDID_ROOT_PARENT ) {
108         if (!rootpar.d_did)
109             rootpar.d_did = DIRDID_ROOT_PARENT;
110         rootpar.d_child = vol->v_dir;
111         return( &rootpar );
112     }
113
114     dir = vol->v_root;
115     while ( dir != SENTINEL ) {
116         if (dir->d_did == did)
117             return dir->d_name ? dir : NULL;
118         dir = (dir->d_did > did) ? dir->d_left : dir->d_right;
119     }
120     return NULL;
121 }
122
123 /* -----------------------------------------
124  * if did is not in the cache resolve it with cnid 
125  * 
126  */
127 struct dir *
128             dirlookup( vol, did )
129             const struct vol    *vol;
130 u_int32_t       did;
131 {
132 #ifdef CNID_DB
133     struct dir *ret;
134     char                *upath;
135     u_int32_t   id;
136     static char         path[MAXPATHLEN + 1];
137     int len;
138     int pathlen;
139     char *ptr;
140     static char buffer[12 + MAXPATHLEN + 1];
141     int buflen = 12 + MAXPATHLEN + 1;
142
143     ret = dirsearch(vol, did);
144     if (ret != NULL)
145         return ret;
146
147     id = did;
148     if ((upath = cnid_resolve(vol->v_db, &id, buffer, buflen)) == NULL) {
149         return NULL;
150     }
151     ptr = path + MAXPATHLEN;
152     len = strlen(upath);
153     pathlen = len;          /* no 0 in the last part */
154     len++;
155     strcpy(ptr - len, upath);
156     ptr -= len;
157     while (1) {
158         ret = dirsearch(vol,id);
159         if (ret != NULL) {
160             break;
161         }
162         if ((upath = cnid_resolve(vol->v_db, &id, buffer, buflen)) == NULL)
163             return NULL;
164         len = strlen(upath) + 1;
165         pathlen += len;
166         if (pathlen > 255)
167             return NULL;
168         strcpy(ptr - len, upath);
169         ptr -= len;
170     }
171     /* fill the cache */
172     ptr--;
173     *ptr = (unsigned char)pathlen;
174     ptr--;
175     *ptr = 2;
176     /* cname is not efficient */
177     if (cname( vol, ret, &ptr ) == NULL )
178         return NULL;
179 #endif
180     return dirsearch(vol, did);
181 }
182
183 /* --------------------------- */
184 /* rotate the tree to the left */
185 static void dir_leftrotate(vol, dir)
186 struct vol *vol;
187 struct dir *dir;
188 {
189     struct dir *right = dir->d_right;
190
191     /* whee. move the right's left tree into dir's right tree */
192     dir->d_right = right->d_left;
193     if (right->d_left != SENTINEL)
194         right->d_left->d_back = dir;
195
196     if (right != SENTINEL) {
197         right->d_back = dir->d_back;
198         right->d_left = dir;
199     }
200
201     if (!dir->d_back) /* no parent. move the right tree to the top. */
202         vol->v_root = right;
203     else if (dir == dir->d_back->d_left) /* we were on the left */
204         dir->d_back->d_left = right;
205     else
206         dir->d_back->d_right = right; /* we were on the right */
207
208     /* re-insert dir on the left tree */
209     if (dir != SENTINEL)
210         dir->d_back = right;
211 }
212
213
214
215 /* rotate the tree to the right */
216 static void dir_rightrotate(vol, dir)
217 struct vol *vol;
218 struct dir *dir;
219 {
220     struct dir *left = dir->d_left;
221
222     /* whee. move the left's right tree into dir's left tree */
223     dir->d_left = left->d_right;
224     if (left->d_right != SENTINEL)
225         left->d_right->d_back = dir;
226
227     if (left != SENTINEL) {
228         left->d_back = dir->d_back;
229         left->d_right = dir;
230     }
231
232     if (!dir->d_back) /* no parent. move the left tree to the top. */
233         vol->v_root = left;
234     else if (dir == dir->d_back->d_right) /* we were on the right */
235         dir->d_back->d_right = left;
236     else
237         dir->d_back->d_left = left; /* we were on the left */
238
239     /* re-insert dir on the right tree */
240     if (dir != SENTINEL)
241         dir->d_back = left;
242 }
243
244 #if 0
245 /* recolor after a removal */
246 static struct dir *dir_rmrecolor(vol, dir)
247             struct vol *vol;
248 struct dir *dir;
249 {
250     struct dir *leaf;
251
252     while ((dir != vol->v_root) && (dir->d_color == DIRTREE_COLOR_BLACK)) {
253         /* are we on the left tree? */
254         if (dir == dir->d_back->d_left) {
255             leaf = dir->d_back->d_right; /* get right side */
256             if (leaf->d_color == DIRTREE_COLOR_RED) {
257                 /* we're red. we need to change to black. */
258                 leaf->d_color = DIRTREE_COLOR_BLACK;
259                 dir->d_back->d_color = DIRTREE_COLOR_RED;
260                 dir_leftrotate(vol, dir->d_back);
261                 leaf = dir->d_back->d_right;
262             }
263
264             /* right leaf has black end nodes */
265             if ((leaf->d_left->d_color == DIRTREE_COLOR_BLACK) &&
266                     (leaf->d_right->d_color = DIRTREE_COLOR_BLACK)) {
267                 leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
268                 dir = dir->d_back; /* ascend */
269             } else {
270                 if (leaf->d_right->d_color == DIRTREE_COLOR_BLACK) {
271                     leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
272                     leaf->d_color = DIRTREE_COLOR_RED;
273                     dir_rightrotate(vol, leaf);
274                     leaf = dir->d_back->d_right;
275                 }
276                 leaf->d_color = dir->d_back->d_color;
277                 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
278                 leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
279                 dir_leftrotate(vol, dir->d_back);
280                 dir = vol->v_root;
281             }
282         } else { /* right tree */
283             leaf = dir->d_back->d_left; /* left tree */
284             if (leaf->d_color == DIRTREE_COLOR_RED) {
285                 leaf->d_color = DIRTREE_COLOR_BLACK;
286                 dir->d_back->d_color = DIRTREE_COLOR_RED;
287                 dir_rightrotate(vol, dir->d_back);
288                 leaf = dir->d_back->d_left;
289             }
290
291             /* left leaf has black end nodes */
292             if ((leaf->d_right->d_color == DIRTREE_COLOR_BLACK) &&
293                     (leaf->d_left->d_color = DIRTREE_COLOR_BLACK)) {
294                 leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
295                 dir = dir->d_back; /* ascend */
296             } else {
297                 if (leaf->d_left->d_color == DIRTREE_COLOR_BLACK) {
298                     leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
299                     leaf->d_color = DIRTREE_COLOR_RED;
300                     dir_leftrotate(vol, leaf);
301                     leaf = dir->d_back->d_left;
302                 }
303                 leaf->d_color = dir->d_back->d_color;
304                 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
305                 leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
306                 dir_rightrotate(vol, dir->d_back);
307                 dir = vol->v_root;
308             }
309         }
310     }
311     dir->d_color = DIRTREE_COLOR_BLACK;
312
313     return dir;
314 }
315 #endif /* 0 */
316
317
318 /* remove the node from the tree. this is just like insertion, but
319  * different. actually, it has to worry about a bunch of things that
320  * insertion doesn't care about. */
321 static void dir_remove( vol, dir )
322 struct vol      *vol;
323 struct dir      *dir;
324 {
325 #ifdef REMOVE_NODES
326     struct ofork *of, *last;
327     struct dir *node, *leaf;
328 #endif /* REMOVE_NODES */
329
330     if (!dir || (dir == SENTINEL))
331         return;
332
333     /* i'm not sure if it really helps to delete stuff. */
334 #ifndef REMOVE_NODES 
335     free(dir->d_name);
336     dir->d_name = NULL;
337 #else /* ! REMOVE_NODES */
338
339     /* go searching for a node with at most one child */
340     if ((dir->d_left == SENTINEL) || (dir->d_right == SENTINEL)) {
341         node = dir;
342     } else {
343         node = dir->d_right;
344         while (node->d_left != SENTINEL)
345             node = node->d_left;
346     }
347
348     /* get that child */
349     leaf = (node->d_left != SENTINEL) ? node->d_left : node->d_right;
350
351     /* detach node */
352     leaf->d_back = node->d_back;
353     if (!node->d_back) {
354         vol->v_root = leaf;
355     } else if (node == node->d_back->d_left) { /* left tree */
356         node->d_back->d_left = leaf;
357     } else {
358         node->d_back->d_right = leaf;
359     }
360
361     /* we want to free node, but we also want to free the data in dir.
362     * currently, that's d_name and the directory traversal bits.
363     * we just copy the necessary bits and then fix up all the
364     * various pointers to the directory. needless to say, there are
365     * a bunch of places that store the directory struct. */
366     if (node != dir) {
367         struct dir save, *tmp;
368
369         memcpy(&save, dir, sizeof(save));
370         memcpy(dir, node, sizeof(struct dir));
371
372         /* restore the red-black bits */
373         dir->d_left = save.d_left;
374         dir->d_right = save.d_right;
375         dir->d_back = save.d_back;
376         dir->d_color = save.d_color;
377
378         if (node == vol->v_dir) {/* we may need to fix up this pointer */
379             vol->v_dir = dir;
380             rootpar.d_child = vol->v_dir;
381         } else {
382             /* if we aren't the root directory, we have parents and
383             * siblings to worry about */
384             if (dir->d_parent->d_child == node)
385                 dir->d_parent->d_child = dir;
386             dir->d_next->d_prev = dir;
387             dir->d_prev->d_next = dir;
388         }
389
390         /* fix up children. */
391         tmp = dir->d_child;
392         while (tmp) {
393             tmp->d_parent = dir;
394             tmp = (tmp == dir->d_child->d_prev) ? NULL : tmp->d_next;
395         }
396
397         if (node == curdir) /* another pointer to fixup */
398             curdir = dir;
399
400         /* we also need to fix up oforks. bleah */
401         if ((of = dir->d_ofork)) {
402             last = of->of_d_prev;
403             while (of) {
404                 of->of_dir = dir;
405                 of = (last == of) ? NULL : of->of_d_next;
406             }
407         }
408
409         /* set the node's d_name */
410         node->d_name = save.d_name;
411     }
412
413     if (node->d_color == DIRTREE_COLOR_BLACK)
414         dir_rmrecolor(vol, leaf);
415     free(node->d_name);
416     free(node);
417 #endif /* ! REMOVE_NODES */
418 }
419
420
421 static struct dir *dir_insert(vol, dir)
422             const struct vol *vol;
423 struct dir *dir;
424 {
425     struct dir  *pdir;
426
427     pdir = vol->v_root;
428     while (pdir->d_did != dir->d_did ) {
429         if ( pdir->d_did > dir->d_did ) {
430             if ( pdir->d_left == SENTINEL ) {
431                 pdir->d_left = dir;
432                 dir->d_back = pdir;
433                 return NULL;
434             }
435             pdir = pdir->d_left;
436         } else {
437             if ( pdir->d_right == SENTINEL ) {
438                 pdir->d_right = dir;
439                 dir->d_back = pdir;
440                 return NULL;
441             }
442             pdir = pdir->d_right;
443         }
444     }
445     return pdir;
446 }
447
448
449 /*
450  * attempt to extend the current dir. tree to include path
451  * as a side-effect, movecwd to that point and return the new dir
452  */
453
454 static struct dir *
455             extenddir( vol, dir, path )
456             struct vol  *vol;
457 struct dir      *dir;
458 char    *path;
459 {
460     char        *p;
461     struct stat st;
462
463     p = mtoupath(vol, path );
464     if ( stat( p, &st ) != 0 ) {
465         return( NULL );
466     }
467     if (!S_ISDIR(st.st_mode)) {
468         return( NULL );
469     }
470
471     if (( dir = adddir( vol, dir, path, strlen( path ), p, strlen(p),
472                         &st)) == NULL ) {
473         return( NULL );
474     }
475
476     if ( movecwd( vol, dir ) < 0 ) {
477         return( NULL );
478     }
479
480     return( dir );
481 }
482
483 static int deletedir(char *dir)
484 {
485     char path[MAXPATHLEN + 1];
486     DIR *dp;
487     struct dirent       *de;
488     struct stat st;
489     int len, err;
490
491     if ((len = strlen(dir)) > sizeof(path))
492         return AFPERR_PARAM;
493
494     /* already gone */
495     if ((dp = opendir(dir)) == NULL)
496         return AFP_OK;
497
498     strcpy(path, dir);
499     strcat(path, "/");
500     len++;
501     while ((de = readdir(dp))) {
502         /* skip this and previous directory */
503         if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
504             continue;
505
506         strncpy(path + len, de->d_name, sizeof(path) - len);
507         if (stat(path, &st) == 0) {
508             if (S_ISDIR(st.st_mode)) {
509                 if ((err = deletedir(path)) < 0) {
510                     closedir(dp);
511                     return err;
512                 }
513             } else if (unlink(path) < 0) {
514                 switch (errno) {
515                 case ENOENT :
516                     continue; /* somebody went and deleted it behind our backs. */
517                 case EROFS:
518                     err = AFPERR_VLOCK;
519                     break;
520                 case EPERM:
521                 case EACCES :
522                     err = AFPERR_ACCESS;
523                     break;
524                 default :
525                     err = AFPERR_PARAM;
526                 }
527                 closedir(dp);
528                 return err;
529             }
530         }
531     }
532     closedir(dp);
533
534     /* okay. the directory is empty. delete it. note: we already got rid
535        of .AppleDouble.  */
536     if (rmdir(dir) < 0) {
537         switch ( errno ) {
538         case ENOENT :
539             break;
540         case ENOTEMPTY : /* should never happen */
541             return( AFPERR_DIRNEMPT );
542         case EPERM:
543         case EACCES :
544             return( AFPERR_ACCESS );
545         case EROFS:
546             return AFPERR_VLOCK;
547         default :
548             return( AFPERR_PARAM );
549         }
550     }
551     return AFP_OK;
552 }
553
554 /* do a recursive copy. */
555 static int copydir(char *src, char *dst, int noadouble)
556 {
557     char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
558     DIR *dp;
559     struct dirent       *de;
560     struct stat st;
561     struct utimbuf      ut;
562     int slen, dlen, err;
563
564     /* doesn't exist or the path is too long. */
565     if (((slen = strlen(src)) > sizeof(spath) - 2) ||
566             ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
567             ((dp = opendir(src)) == NULL))
568         return AFPERR_PARAM;
569
570     /* try to create the destination directory */
571     if (ad_mkdir(dst, DIRBITS | 0777) < 0) {
572         closedir(dp);
573         switch ( errno ) {
574         case ENOENT :
575             return( AFPERR_NOOBJ );
576         case EROFS :
577             return( AFPERR_VLOCK );
578         case EPERM:
579         case EACCES :
580             return( AFPERR_ACCESS );
581         case EEXIST :
582             return( AFPERR_EXIST );
583         case ENOSPC :
584         case EDQUOT :
585             return( AFPERR_DFULL );
586         default :
587             return( AFPERR_PARAM );
588         }
589     }
590
591     /* set things up to copy */
592     strcpy(spath, src);
593     strcat(spath, "/");
594     slen++;
595     strcpy(dpath, dst);
596     strcat(dpath, "/");
597     dlen++;
598     err = AFP_OK;
599     while ((de = readdir(dp))) {
600         /* skip this and previous directory */
601         if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
602             continue;
603
604         strncpy(spath + slen, de->d_name, sizeof(spath) - slen);
605         if (stat(spath, &st) == 0) {
606             strncpy(dpath + dlen, de->d_name, sizeof(dpath) - dlen);
607
608             if (S_ISDIR(st.st_mode)) {
609                 if ((err = copydir(spath, dpath, noadouble)) < 0)
610                     goto copydir_done;
611             } else if ((err = copyfile(spath, dpath, NULL, noadouble)) < 0) {
612                 goto copydir_done;
613
614             } else {
615                 /* keep the same time stamp. */
616                 ut.actime = ut.modtime = st.st_mtime;
617                 utime(dpath, &ut);
618             }
619         }
620     }
621
622     /* keep the same time stamp. */
623     if (stat(src, &st) == 0) {
624         ut.actime = ut.modtime = st.st_mtime;
625         utime(dst, &ut);
626     }
627
628 copydir_done:
629     closedir(dp);
630     return err;
631 }
632
633
634 /* --- public functions follow --- */
635
636 /* NOTE: we start off with at least one node (the root directory). */
637 struct dir *dirinsert( vol, dir )
638             struct vol  *vol;
639 struct dir      *dir;
640 {
641     struct dir *node;
642
643     if ((node = dir_insert(vol, dir)))
644         return node;
645
646     /* recolor the tree. the current node is red. */
647     dir->d_color = DIRTREE_COLOR_RED;
648
649     /* parent of this node has to be black. if the parent node
650     * is red, then we have a grandparent. */
651     while ((dir != vol->v_root) &&
652             (dir->d_back->d_color == DIRTREE_COLOR_RED)) {
653         /* are we on the left tree? */
654         if (dir->d_back == dir->d_back->d_back->d_left) {
655             node = dir->d_back->d_back->d_right;  /* get the right node */
656             if (node->d_color == DIRTREE_COLOR_RED) {
657                 /* we're red. we need to change to black. */
658                 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
659                 node->d_color = DIRTREE_COLOR_BLACK;
660                 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
661                 dir = dir->d_back->d_back; /* finished. go up. */
662             } else {
663                 if (dir == dir->d_back->d_right) {
664                     dir = dir->d_back;
665                     dir_leftrotate(vol, dir);
666                 }
667                 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
668                 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
669                 dir_rightrotate(vol, dir->d_back->d_back);
670             }
671         } else {
672             node = dir->d_back->d_back->d_left;
673             if (node->d_color == DIRTREE_COLOR_RED) {
674                 /* we're red. we need to change to black. */
675                 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
676                 node->d_color = DIRTREE_COLOR_BLACK;
677                 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
678                 dir = dir->d_back->d_back; /* finished. ascend */
679             } else {
680                 if (dir == dir->d_back->d_left) {
681                     dir = dir->d_back;
682                     dir_rightrotate(vol, dir);
683                 }
684                 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
685                 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
686                 dir_leftrotate(vol, dir->d_back->d_back);
687             }
688         }
689     }
690
691     vol->v_root->d_color = DIRTREE_COLOR_BLACK;
692     return NULL;
693 }
694
695 /* free everything down. we don't bother to recolor as this is only
696  * called to free the entire tree */
697 void dirfree( dir )
698 struct dir      *dir;
699 {
700     if (!dir || (dir == SENTINEL))
701         return;
702
703     if ( dir->d_left != SENTINEL ) {
704         dirfree( dir->d_left );
705     }
706     if ( dir->d_right != SENTINEL ) {
707         dirfree( dir->d_right );
708     }
709
710     if (dir != SENTINEL) {
711         free( dir->d_name );
712         free( dir );
713     }
714 }
715
716
717 struct dir *dirnew(const int len)
718 {
719     struct dir *dir;
720
721     dir = (struct dir *) calloc(1, sizeof( struct dir ));
722     if (!dir)
723         return NULL;
724
725     if ((dir->d_name = (char *) malloc(sizeof(char)*len)) == NULL) {
726         free(dir);
727         return NULL;
728     }
729
730     dir->d_left = dir->d_right = SENTINEL;
731     dir->d_next = dir->d_prev = dir;
732     return dir;
733 }
734
735
736 /* XXX: this needs to be changed to handle path types */
737 char *
738 cname( vol, dir, cpath )
739 const struct vol        *vol;
740 struct dir      *dir;
741 char    **cpath;
742 {
743     struct dir          *cdir;
744     static char         path[ MAXPATHLEN + 1];
745     char                *data, *p;
746     int                 extend = 0;
747     int                 len;
748
749     data = *cpath;
750     if ( *data++ != 2 ) {                       /* path type */
751         return( NULL );
752     }
753     len = (unsigned char) *data++;
754     *cpath += len + 2;
755     *path = '\0';
756
757     for ( ;; ) {
758         if ( len == 0 ) {
759             if ( !extend && movecwd( vol, dir ) < 0 ) {
760                 return( NULL );
761             }
762             return( path );
763         }
764
765         if ( *data == '\0' ) {
766             data++;
767             len--;
768         }
769
770         while ( *data == '\0' && len > 0 ) {
771             if ( dir->d_parent == NULL ) {
772                 return( NULL );
773             }
774             dir = dir->d_parent;
775             data++;
776             len--;
777         }
778
779         /* would this be faster with strlen + strncpy? */
780         p = path;
781         while ( *data != '\0' && len > 0 ) {
782             *p++ = *data++;
783             len--;
784         }
785
786         /* short cut bits by chopping off a trailing \0. this also
787                   makes the traversal happy w/ filenames at the end of the
788                   cname. */
789         if (len == 1)
790             len--;
791
792 #ifdef notdef
793         /*
794          * Dung Nguyen <ntd@adb.fr>
795          *
796          * AFPD cannot handle paths with "::" if the "::" notation is
797          * not at the beginning of the path. The following path will not
798          * be interpreted correctly:
799          *
800          * :a:b:::c: (directory c at the same level as directory a) */
801         if ( len > 0 ) {
802             data++;
803             len--;
804         }
805 #endif /* notdef */
806         *p = '\0';
807
808         if ( p != path ) { /* we got something */
809             if ( !extend ) {
810                 cdir = dir->d_child;
811                 while (cdir) {
812                     if ( strcasecmp( cdir->d_name, path ) == 0 ) {
813                         break;
814                     }
815                     cdir = (cdir == dir->d_child->d_prev) ? NULL :
816                            cdir->d_next;
817                 }
818                 if ( cdir == NULL ) {
819                     ++extend;
820                     if ( movecwd( vol, dir ) < 0 ) {
821                         return( NULL );
822                     }
823                     cdir = extenddir( vol, dir, path );
824                 }
825
826             } else {
827                 cdir = extenddir( vol, dir, path );
828             }
829
830             if ( cdir == NULL ) {
831                 if ( len > 0 ) {
832                     return( NULL );
833                 }
834
835             } else {
836                 dir = cdir;
837                 *path = '\0';
838             }
839         }
840     }
841 }
842
843 /*
844  * Move curdir to dir, with a possible chdir()
845  */
846 int movecwd( vol, dir)
847 const struct vol        *vol;
848 struct dir      *dir;
849 {
850     char path[MAXPATHLEN + 1];
851     struct dir  *d;
852     char        *p, *u;
853     int         n;
854
855     if ( dir == curdir ) {
856         return( 0 );
857     }
858     if ( dir->d_did == DIRDID_ROOT_PARENT) {
859         return( -1 );
860     }
861
862     p = path + sizeof(path) - 1;
863     *p-- = '\0';
864     *p = '.';
865     for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) {
866         *--p = '/';
867         u = mtoupath(vol, d->d_name );
868         n = strlen( u );
869         p -= n;
870         strncpy( p, u, n );
871     }
872     if ( d != curdir ) {
873         *--p = '/';
874         n = strlen( vol->v_path );
875         p -= n;
876         strncpy( p, vol->v_path, n );
877     }
878     if ( chdir( p ) < 0 ) {
879         return( -1 );
880     }
881     curdir = dir;
882     return( 0 );
883 }
884
885 int getdirparams(const struct vol *vol,
886                  u_int16_t bitmap,
887                  char *upath, struct dir *dir, struct stat *st,
888                  char *buf, int *buflen )
889 {
890     struct maccess      ma;
891     struct adouble      ad;
892     char                *data, *nameoff = NULL;
893     DIR                 *dp;
894     struct dirent       *de;
895     int                 bit = 0, isad = 1;
896     u_int32_t           aint;
897     u_int16_t           ashort;
898 #ifdef FORCE_UIDGID
899     uidgidset           *uidgid;
900     memset(&uidgid, 0, sizeof(uidgid));
901 #endif /* FORCE_UIDGID */
902
903     memset(&ad, 0, sizeof(ad));
904
905 #ifdef FORCE_UIDGID
906     save_uidgid ( &uidgid );
907     set_uidgid ( vol );
908 #endif /* FORCE_UIDGID */
909
910     if ( ad_open( upath, ADFLAGS_HF|ADFLAGS_DIR, O_RDONLY,
911                   DIRBITS | 0777, &ad) < 0 ) {
912         isad = 0;
913     }
914
915     data = buf;
916     while ( bitmap != 0 ) {
917         while (( bitmap & 1 ) == 0 ) {
918             bitmap = bitmap>>1;
919             bit++;
920         }
921
922         switch ( bit ) {
923         case DIRPBIT_ATTR :
924             if ( isad ) {
925                 ad_getattr(&ad, &ashort);
926             } else if (*upath == '.' && strcmp(upath, ".") &&
927                        strcmp(upath, "..")) {
928                 ashort = htons(ATTRBIT_INVISIBLE);
929             } else
930                 ashort = 0;
931             memcpy( data, &ashort, sizeof( ashort ));
932             data += sizeof( ashort );
933             break;
934
935         case DIRPBIT_PDID :
936             if ( dir->d_did == DIRDID_ROOT) {
937                 aint = DIRDID_ROOT_PARENT;
938             } else if (dir->d_did == DIRDID_ROOT_PARENT) {
939                 aint = 0;
940             } else {
941                 aint = dir->d_parent->d_did;
942             }
943             memcpy( data, &aint, sizeof( aint ));
944             data += sizeof( aint );
945             break;
946
947         case DIRPBIT_CDATE :
948             if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
949                 aint = AD_DATE_FROM_UNIX(st->st_mtime);
950             memcpy( data, &aint, sizeof( aint ));
951             data += sizeof( aint );
952             break;
953
954         case DIRPBIT_MDATE :
955             aint = AD_DATE_FROM_UNIX(st->st_mtime);
956             memcpy( data, &aint, sizeof( aint ));
957             data += sizeof( aint );
958             break;
959
960         case DIRPBIT_BDATE :
961             if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
962                 aint = AD_DATE_START;
963             memcpy( data, &aint, sizeof( aint ));
964             data += sizeof( aint );
965             break;
966
967         case DIRPBIT_FINFO :
968             if ( isad ) {
969                 memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
970             } else { /* no appledouble */
971                 memset( data, 0, 32 );
972                 /* set default view -- this also gets done in ad_open() */
973                 ashort = htons(FINDERINFO_CLOSEDVIEW);
974                 memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
975
976                 /* dot files are by default invisible */
977                 if (*upath == '.' && strcmp(upath, ".") &&
978                         strcmp(upath, "..")) {
979                     ashort = htons(FINDERINFO_INVISIBLE);
980                     memcpy(data + FINDERINFO_FRFLAGOFF,
981                            &ashort, sizeof(ashort));
982                 }
983             }
984             data += 32;
985             break;
986
987         case DIRPBIT_LNAME :
988             if (dir->d_name) /* root of parent can have a null name */
989                 nameoff = data;
990             else
991                 memset(data, 0, sizeof(u_int16_t));
992             data += sizeof( u_int16_t );
993             break;
994
995         case DIRPBIT_SNAME :
996             memset(data, 0, sizeof(u_int16_t));
997             data += sizeof( u_int16_t );
998             break;
999
1000         case DIRPBIT_DID :
1001             memcpy( data, &dir->d_did, sizeof( aint ));
1002             data += sizeof( aint );
1003             break;
1004
1005         case DIRPBIT_OFFCNT :
1006             ashort = 0;
1007             /* this needs to handle current directory access rights */
1008             if ((dp = opendir( upath ))) {
1009                 while (( de = readdir( dp )) != NULL ) {
1010                     if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, "."))
1011                         continue;
1012
1013                     if (!validupath(vol, de->d_name))
1014                         continue;
1015
1016                     /* check for vetoed filenames */
1017                     if (veto_file(vol->v_veto, de->d_name))
1018                         continue;
1019
1020                     /* now check against too long a filename */
1021                     if (strlen(utompath(vol, de->d_name)) > MACFILELEN)
1022                         continue;
1023
1024                     ashort++;
1025                 }
1026                 closedir( dp );
1027             }
1028             ashort = htons( ashort );
1029             memcpy( data, &ashort, sizeof( ashort ));
1030             data += sizeof( ashort );
1031             break;
1032
1033         case DIRPBIT_UID :
1034             aint = htonl(st->st_uid);
1035             memcpy( data, &aint, sizeof( aint ));
1036             data += sizeof( aint );
1037             break;
1038
1039         case DIRPBIT_GID :
1040             aint = htonl(st->st_gid);
1041             memcpy( data, &aint, sizeof( aint ));
1042             data += sizeof( aint );
1043             break;
1044
1045         case DIRPBIT_ACCESS :
1046             utommode( st, &ma );
1047 #ifndef SENDFILE_FLAVOR_LINUX /* ignore this section if it's linux */
1048 #ifdef HAVE_ACCESS
1049             accessmode( upath, &ma, dir );
1050 #endif /* HAVE_ACCESS */
1051 #endif /* SENDFILE_FLAVOR_LINUX */
1052 #ifdef AFS      /* If only AFS defined, access() works only for AFS filesystems */ 
1053             afsmode( upath, &ma, dir );
1054 #endif /* AFS */
1055             *data++ = ma.ma_user;
1056             *data++ = ma.ma_world;
1057             *data++ = ma.ma_group;
1058             *data++ = ma.ma_owner;
1059             break;
1060
1061             /* Client has requested the ProDOS information block.
1062                Just pass back the same basic block for all
1063                directories. <shirsch@ibm.net> */
1064         case DIRPBIT_PDINFO :                     /* ProDOS Info Block */
1065             *data++ = 0x0f;
1066             *data++ = 0;
1067             ashort = htons( 0x0200 );
1068             memcpy( data, &ashort, sizeof( ashort ));
1069             data += sizeof( ashort );
1070             memset( data, 0, sizeof( ashort ));
1071             data += sizeof( ashort );
1072             break;
1073
1074         default :
1075             if ( isad ) {
1076                 ad_close( &ad, ADFLAGS_HF );
1077             }
1078 #ifdef FORCE_UIDGID
1079             restore_uidgid ( &uidgid );
1080 #endif /* FORCE_UIDGID */
1081             return( AFPERR_BITMAP );
1082         }
1083         bitmap = bitmap>>1;
1084         bit++;
1085     }
1086     if ( nameoff ) {
1087         ashort = htons( data - buf );
1088         memcpy( nameoff, &ashort, sizeof( ashort ));
1089
1090         if ((aint = strlen( dir->d_name )) > MACFILELEN)
1091             aint = MACFILELEN;
1092
1093         *data++ = aint;
1094         memcpy( data, dir->d_name, aint );
1095         data += aint;
1096     }
1097     if ( isad ) {
1098         ad_close( &ad, ADFLAGS_HF );
1099     }
1100     *buflen = data - buf;
1101     return( AFP_OK );
1102 }
1103
1104 int afp_setdirparams(obj, ibuf, ibuflen, rbuf, rbuflen )
1105 AFPObj      *obj;
1106 char    *ibuf, *rbuf;
1107 int             ibuflen, *rbuflen;
1108 {
1109     struct vol  *vol;
1110     struct dir  *dir;
1111     char        *path;
1112     u_int16_t   vid, bitmap;
1113     u_int32_t   did;
1114     int         rc;
1115
1116     *rbuflen = 0;
1117     ibuf += 2;
1118     memcpy( &vid, ibuf, sizeof( vid ));
1119     ibuf += sizeof( vid );
1120
1121     if (( vol = getvolbyvid( vid )) == NULL ) {
1122         return( AFPERR_PARAM );
1123     }
1124
1125     if (vol->v_flags & AFPVOL_RO)
1126         return AFPERR_VLOCK;
1127
1128     memcpy( &did, ibuf, sizeof( did ));
1129     ibuf += sizeof( int );
1130
1131     if (( dir = dirsearch( vol, did )) == NULL ) {
1132         return( AFPERR_NOOBJ );
1133     }
1134
1135     memcpy( &bitmap, ibuf, sizeof( bitmap ));
1136     bitmap = ntohs( bitmap );
1137     ibuf += sizeof( bitmap );
1138
1139     if (( path = cname( vol, dir, &ibuf )) == NULL ) {
1140         return( AFPERR_NOOBJ );
1141     }
1142
1143     /*
1144      * If ibuf is odd, make it even.
1145      */
1146     if ((u_long)ibuf & 1 ) {
1147         ibuf++;
1148     }
1149
1150     if (( rc = setdirparams(vol, path, bitmap, ibuf )) == AFP_OK ) {
1151         setvoltime(obj, vol );
1152     }
1153     return( rc );
1154 }
1155
1156 int setdirparams(const struct vol *vol,
1157                  char *path, u_int16_t bitmap, char *buf )
1158 {
1159     struct maccess      ma;
1160     struct adouble      ad;
1161     struct utimbuf      ut;
1162     char                *upath;
1163     int                 bit = 0, aint, isad = 1;
1164     u_int16_t           ashort, bshort;
1165     int                 err = AFP_OK;
1166 #ifdef FORCE_UIDGID
1167     uidgidset           *uidgid;
1168
1169     memset(&uidgid, 0, sizeof(uidgid));
1170 #endif /* FORCE_UIDGID */
1171
1172     upath = mtoupath(vol, path);
1173     memset(&ad, 0, sizeof(ad));
1174 #ifdef FORCE_UIDGID
1175     save_uidgid ( &uidgid );
1176 #endif /* FORCE_UIDGID */
1177     if (ad_open( upath, vol_noadouble(vol)|ADFLAGS_HF|ADFLAGS_DIR,
1178                  O_RDWR|O_CREAT, 0666, &ad) < 0) {
1179         /*
1180          * Check to see what we're trying to set.  If it's anything
1181          * but ACCESS, UID, or GID, give an error.  If it's any of those
1182          * three, we don't need the ad to be open, so just continue.
1183          *
1184          * note: we also don't need to worry about mdate. also, be quiet
1185          *       if we're using the noadouble option.
1186          */
1187         if (!vol_noadouble(vol) && (bitmap &
1188                                     ~((1<<DIRPBIT_ACCESS)|(1<<DIRPBIT_UID)|(1<<DIRPBIT_GID)|
1189                                       (1<<DIRPBIT_MDATE)|(1<<DIRPBIT_PDINFO)))) {
1190 #ifdef FORCE_UIDGID
1191             restore_uidgid ( &uidgid );
1192 #endif /* FORCE_UIDGID */
1193             return AFPERR_ACCESS;
1194         }
1195
1196         isad = 0;
1197     } else {
1198         /*
1199          * Check to see if a create was necessary. If it was, we'll want
1200          * to set our name, etc.
1201          */
1202         if ( ad_getoflags( &ad, ADFLAGS_HF ) & O_CREAT ) {
1203             ad_setentrylen( &ad, ADEID_NAME, strlen( curdir->d_name ));
1204             memcpy( ad_entry( &ad, ADEID_NAME ), curdir->d_name,
1205                     ad_getentrylen( &ad, ADEID_NAME ));
1206         }
1207     }
1208
1209     while ( bitmap != 0 ) {
1210         while (( bitmap & 1 ) == 0 ) {
1211             bitmap = bitmap>>1;
1212             bit++;
1213         }
1214
1215         switch( bit ) {
1216         case DIRPBIT_ATTR :
1217             if (isad) {
1218                 memcpy( &ashort, buf, sizeof( ashort ));
1219                 ad_getattr(&ad, &bshort);
1220                 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1221                     bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1222                 } else {
1223                     bshort &= ~ashort;
1224                 }
1225                 ad_setattr(&ad, bshort);
1226             }
1227             buf += sizeof( ashort );
1228             break;
1229
1230         case DIRPBIT_CDATE :
1231             if (isad) {
1232                 memcpy(&aint, buf, sizeof(aint));
1233                 ad_setdate(&ad, AD_DATE_CREATE, aint);
1234             }
1235             buf += sizeof( aint );
1236             break;
1237
1238         case DIRPBIT_MDATE :
1239             memcpy(&aint, buf, sizeof(aint));
1240             if (isad)
1241                 ad_setdate(&ad, AD_DATE_MODIFY, aint);
1242             ut.actime = ut.modtime = AD_DATE_TO_UNIX(aint);
1243             utime(upath, &ut);
1244             buf += sizeof( aint );
1245             break;
1246
1247         case DIRPBIT_BDATE :
1248             if (isad) {
1249                 memcpy(&aint, buf, sizeof(aint));
1250                 ad_setdate(&ad, AD_DATE_BACKUP, aint);
1251             }
1252             buf += sizeof( aint );
1253             break;
1254
1255         case DIRPBIT_FINFO :
1256             /*
1257              * Alright, we admit it, this is *really* sick!
1258              * The 4 bytes that we don't copy, when we're dealing
1259              * with the root of a volume, are the directory's
1260              * location information. This eliminates that annoying
1261              * behavior one sees when mounting above another mount
1262              * point.
1263              */
1264             if (isad) {
1265                 if (  curdir->d_did == DIRDID_ROOT ) {
1266                     memcpy( ad_entry( &ad, ADEID_FINDERI ), buf, 10 );
1267                     memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, buf + 14, 18 );
1268                 } else {
1269                     memcpy( ad_entry( &ad, ADEID_FINDERI ), buf, 32 );
1270                 }
1271             }
1272             buf += 32;
1273             break;
1274
1275         case DIRPBIT_UID :      /* What kind of loser mounts as root? */
1276             memcpy( &aint, buf, sizeof(aint));
1277             buf += sizeof( aint );
1278             if ( (curdir->d_did == DIRDID_ROOT) &&
1279                     (setdeskowner( ntohl(aint), -1 ) < 0)) {
1280                 switch ( errno ) {
1281                 case EPERM :
1282                 case EACCES :
1283                     err = AFPERR_ACCESS;
1284                     goto setdirparam_done;
1285                     break;
1286                 case EROFS :
1287                     err = AFPERR_VLOCK;
1288                     goto setdirparam_done;
1289                     break;
1290                 default :
1291                     LOG(log_error, logtype_afpd, "setdirparam: setdeskowner: %s",
1292                         strerror(errno) );
1293                     if (!isad) {
1294                         err = AFPERR_PARAM;
1295                         goto setdirparam_done;
1296                     }
1297                     break;
1298                 }
1299             }
1300             if ( setdirowner( ntohl(aint), -1, vol_noadouble(vol) ) < 0 ) {
1301                 switch ( errno ) {
1302                 case EPERM :
1303                 case EACCES :
1304                     err = AFPERR_ACCESS;
1305                     goto setdirparam_done;
1306                     break;
1307                 case EROFS :
1308                     err = AFPERR_VLOCK;
1309                     goto setdirparam_done;
1310                     break;
1311                 default :
1312                     LOG(log_error, logtype_afpd, "setdirparam: setdirowner: %s",
1313                         strerror(errno) );
1314                     break;
1315                 }
1316             }
1317             break;
1318         case DIRPBIT_GID :
1319             memcpy( &aint, buf, sizeof( aint ));
1320             buf += sizeof( aint );
1321             if (curdir->d_did == DIRDID_ROOT)
1322                 setdeskowner( -1, ntohl(aint) );
1323
1324 #if 0       /* don't error if we can't set the desktop owner. */
1325             switch ( errno ) {
1326             case EPERM :
1327             case EACCES :
1328                 err = AFPERR_ACCESS;
1329                 goto setdirparam_done;
1330                 break;
1331             case EROFS :
1332                 err = AFPERR_VLOCK;
1333                 goto setdirparam_done;
1334                 break;
1335             default :
1336                 LOG(log_error, logtype_afpd, "setdirparam: setdeskowner: %m" );
1337                 if (!isad) {
1338                     err = AFPERR_PARAM;
1339                     goto setdirparam_done;
1340                 }
1341                 break;
1342             }
1343 #endif /* 0 */
1344
1345             if ( setdirowner( -1, ntohl(aint), vol_noadouble(vol) ) < 0 ) {
1346                 switch ( errno ) {
1347                 case EPERM :
1348                 case EACCES :
1349                     err = AFPERR_ACCESS;
1350                     goto setdirparam_done;
1351                     break;
1352                 case EROFS :
1353                     err = AFPERR_VLOCK;
1354                     goto setdirparam_done;
1355                     break;
1356                 default :
1357                     LOG(log_error, logtype_afpd, "setdirparam: setdirowner: %s",
1358                         strerror(errno) );
1359                     break;
1360                 }
1361             }
1362             break;
1363
1364         case DIRPBIT_ACCESS :
1365             ma.ma_user = *buf++;
1366             ma.ma_world = *buf++;
1367             ma.ma_group = *buf++;
1368             ma.ma_owner = *buf++;
1369
1370             if (curdir->d_did == DIRDID_ROOT)
1371                 setdeskmode(mtoumode( &ma ));
1372 #if 0 /* don't error if we can't set the desktop mode */
1373             switch ( errno ) {
1374             case EPERM :
1375             case EACCES :
1376                 err = AFPERR_ACCESS;
1377                 goto setdirparam_done;
1378             case EROFS :
1379                 err = AFPERR_VLOCK;
1380                 goto setdirparam_done;
1381             default :
1382                 LOG(log_error, logtype_afpd, "setdirparam: setdeskmode: %s",
1383                     strerror(errno) );
1384                 break;
1385                 err = AFPERR_PARAM;
1386                 goto setdirparam_done;
1387             }
1388         }
1389 #endif /* 0 */
1390
1391         if ( setdirmode( mtoumode( &ma ), vol_noadouble(vol),
1392                          (vol->v_flags & AFPVOL_DROPBOX)) < 0 ) {
1393             switch ( errno ) {
1394             case EPERM :
1395             case EACCES :
1396                 err = AFPERR_ACCESS;
1397                 goto setdirparam_done;
1398             case EROFS :
1399                 err = AFPERR_VLOCK;
1400                 goto setdirparam_done;
1401             default :
1402                 LOG(log_error, logtype_afpd, "setdirparam: setdirmode: %s",
1403                     strerror(errno) );
1404                 err = AFPERR_PARAM;
1405                 goto setdirparam_done;
1406             }
1407         }
1408         break;
1409
1410         /* Ignore what the client thinks we should do to the
1411            ProDOS information block.  Skip over the data and
1412            report nothing amiss. <shirsch@ibm.net> */
1413     case DIRPBIT_PDINFO :
1414         buf += 6;
1415         break;
1416
1417     default :
1418         err = AFPERR_BITMAP;
1419         goto setdirparam_done;
1420         break;
1421     }
1422
1423     bitmap = bitmap>>1;
1424     bit++;
1425 }
1426
1427
1428 setdirparam_done:
1429 if ( isad ) {
1430     ad_flush( &ad, ADFLAGS_HF );
1431     ad_close( &ad, ADFLAGS_HF );
1432 }
1433
1434 #ifdef FORCE_UIDGID
1435 restore_uidgid ( &uidgid );
1436 #endif /* FORCE_UIDGID */
1437 return err;
1438 }
1439
1440 int afp_createdir(obj, ibuf, ibuflen, rbuf, rbuflen )
1441 AFPObj      *obj;
1442 char    *ibuf, *rbuf;
1443 int             ibuflen, *rbuflen;
1444 {
1445     struct adouble      ad;
1446     struct stat         st;
1447     struct vol          *vol;
1448     struct dir          *dir;
1449     char                *path, *upath;
1450     u_int32_t           did;
1451     u_int16_t           vid;
1452 #ifdef FORCE_UIDGID
1453     uidgidset           *uidgid;
1454
1455     memset(&uidgid, 0, sizeof(uidgid));
1456 #endif /* FORCE_UIDGID */
1457
1458     *rbuflen = 0;
1459     ibuf += 2;
1460
1461     memcpy( &vid, ibuf, sizeof( vid ));
1462     ibuf += sizeof( vid );
1463     if (( vol = getvolbyvid( vid )) == NULL ) {
1464         return( AFPERR_PARAM );
1465     }
1466
1467     if (vol->v_flags & AFPVOL_RO)
1468         return AFPERR_VLOCK;
1469
1470     memcpy( &did, ibuf, sizeof( did ));
1471     ibuf += sizeof( did );
1472     if (( dir = dirsearch( vol, did )) == NULL ) {
1473         return( AFPERR_NOOBJ );
1474     }
1475
1476     if (( path = cname( vol, dir, &ibuf )) == NULL ) {
1477         switch( errno ) {
1478         case EACCES:
1479             return( AFPERR_ACCESS );
1480         case EEXIST:
1481             return( AFPERR_EXIST );
1482         default:
1483             return( AFPERR_NOOBJ );
1484         }
1485     }
1486
1487     upath = mtoupath(vol, path);
1488
1489     /* check for illegal bits in the unix filename */
1490     if (!wincheck(vol, upath))
1491         return AFPERR_PARAM;
1492
1493     if ((vol->v_flags & AFPVOL_NOHEX) && strchr(upath, '/'))
1494         return AFPERR_PARAM;
1495
1496     if (!validupath(vol, upath))
1497         return AFPERR_EXIST;
1498
1499     /* check for vetoed filenames */
1500     if (veto_file(vol->v_veto, upath))
1501         return AFPERR_EXIST;
1502
1503 #ifdef FORCE_UIDGID
1504     save_uidgid ( &uidgid );
1505     set_uidgid  ( vol );
1506 #endif /* FORCE_UIDGID */
1507
1508     if ( ad_mkdir( upath, DIRBITS | 0777 ) < 0 ) {
1509 #ifdef FORCE_UIDGID
1510         restore_uidgid ( &uidgid );
1511 #endif /* FORCE_UIDGID */
1512         switch ( errno ) {
1513         case ENOENT :
1514             return( AFPERR_NOOBJ );
1515         case EROFS :
1516             return( AFPERR_VLOCK );
1517         case EACCES :
1518             return( AFPERR_ACCESS );
1519         case EEXIST :
1520             return( AFPERR_EXIST );
1521         case ENOSPC :
1522         case EDQUOT :
1523             return( AFPERR_DFULL );
1524         default :
1525             return( AFPERR_PARAM );
1526         }
1527     }
1528
1529     if (stat(upath, &st) < 0) {
1530 #ifdef FORCE_UIDGID
1531         restore_uidgid ( &uidgid );
1532 #endif /* FORCE_UIDGID */
1533         return AFPERR_MISC;
1534     }
1535
1536     if ((dir = adddir( vol, curdir, path, strlen( path ), upath,
1537                        strlen(upath), &st)) == NULL) {
1538 #ifdef FORCE_UIDGID
1539         restore_uidgid ( &uidgid );
1540 #endif /* FORCE_UIDGID */
1541         return AFPERR_MISC;
1542     }
1543
1544     if ( movecwd( vol, dir ) < 0 ) {
1545 #ifdef FORCE_UIDGID
1546         restore_uidgid ( &uidgid );
1547 #endif /* FORCE_UIDGID */
1548         return( AFPERR_PARAM );
1549     }
1550
1551     memset(&ad, 0, sizeof(ad));
1552     if (ad_open( "", vol_noadouble(vol)|ADFLAGS_HF|ADFLAGS_DIR,
1553                  O_RDWR|O_CREAT, 0666, &ad ) < 0)  {
1554         if (vol_noadouble(vol))
1555             goto createdir_done;
1556 #ifdef FORCE_UIDGID
1557         restore_uidgid ( &uidgid );
1558 #endif /* FORCE_UIDGID */
1559         return( AFPERR_ACCESS );
1560     }
1561
1562     ad_setentrylen( &ad, ADEID_NAME, strlen( path ));
1563     memcpy( ad_entry( &ad, ADEID_NAME ), path,
1564             ad_getentrylen( &ad, ADEID_NAME ));
1565     ad_flush( &ad, ADFLAGS_HF );
1566     ad_close( &ad, ADFLAGS_HF );
1567
1568 createdir_done:
1569     memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
1570     *rbuflen = sizeof( u_int32_t );
1571     setvoltime(obj, vol );
1572 #ifdef FORCE_UIDGID
1573     restore_uidgid ( &uidgid );
1574 #endif /* FORCE_UIDGID */
1575     return( AFP_OK );
1576 }
1577
1578
1579 int renamedir(src, dst, dir, newparent, newname, noadouble)
1580 char    *src, *dst, *newname;
1581 struct dir      *dir, *newparent;
1582 const int noadouble;
1583 {
1584     struct adouble      ad;
1585     struct dir          *parent;
1586     char                *buf;
1587     int                 len, err;
1588
1589     /* existence check moved to afp_moveandrename */
1590     if ( rename( src, dst ) < 0 ) {
1591         switch ( errno ) {
1592         case ENOENT :
1593             return( AFPERR_NOOBJ );
1594         case EACCES :
1595             return( AFPERR_ACCESS );
1596         case EROFS:
1597             return AFPERR_VLOCK;
1598         case EINVAL:
1599             /* tried to move directory into a subdirectory of itself */
1600             return AFPERR_CANTMOVE;
1601         case EXDEV:
1602             /* this needs to copy and delete. bleah. that means we have
1603              * to deal with entire directory hierarchies. */
1604             if ((err = copydir(src, dst, noadouble)) < 0) {
1605                 deletedir(dst);
1606                 return err;
1607             }
1608             if ((err = deletedir(src)) < 0)
1609                 return err;
1610             break;
1611         default :
1612             return( AFPERR_PARAM );
1613         }
1614     }
1615
1616     memset(&ad, 0, sizeof(ad));
1617     if ( ad_open( dst, ADFLAGS_HF|ADFLAGS_DIR, O_RDWR, 0, &ad) < 0 ) {
1618         switch ( errno ) {
1619         case ENOENT :
1620             if (noadouble) {
1621                 len = strlen(newname);
1622                 goto renamedir_done;
1623             }
1624             return( AFPERR_NOOBJ );
1625         case EACCES :
1626             return( AFPERR_ACCESS );
1627         default :
1628             return( AFPERR_PARAM );
1629         }
1630     }
1631     len = strlen( newname );
1632     ad_setentrylen( &ad, ADEID_NAME, len );
1633     memcpy( ad_entry( &ad, ADEID_NAME ), newname, len );
1634     ad_flush( &ad, ADFLAGS_HF );
1635     ad_close( &ad, ADFLAGS_HF );
1636
1637 renamedir_done:
1638     if ((buf = (char *) realloc( dir->d_name, len + 1 )) == NULL ) {
1639         LOG(log_error, logtype_afpd, "renamedir: realloc: %s", strerror(errno) );
1640         return AFPERR_MISC;
1641     }
1642     dir->d_name = buf;
1643     strcpy( dir->d_name, newname );
1644
1645     if (( parent = dir->d_parent ) == NULL ) {
1646         return( AFP_OK );
1647     }
1648     if ( parent == newparent ) {
1649         return( AFP_OK );
1650     }
1651
1652     /* detach from old parent and add to new one. */
1653     dirchildremove(parent, dir);
1654     dir->d_parent = newparent;
1655     dirchildadd(newparent, dir);
1656     return( AFP_OK );
1657 }
1658
1659 #define DOT_APPLEDOUBLE_LEN 13
1660 /* delete an empty directory */
1661 int deletecurdir( vol, path, pathlen )
1662 const struct vol        *vol;
1663 char *path;
1664 int pathlen;
1665 {
1666     struct dirent *de;
1667     struct stat st;
1668     struct dir  *fdir;
1669     DIR *dp;
1670     struct adouble      ad;
1671     u_int16_t           ashort;
1672 #ifdef FORCE_UIDGID
1673     uidgidset           *uidgid;
1674
1675     memset(&uidgid, 0, sizeof(uidgid));
1676 #endif /* FORCE_UIDGID */
1677
1678     if ( curdir->d_parent == NULL ) {
1679         return( AFPERR_ACCESS );
1680     }
1681
1682     if ( curdir->d_child != NULL ) {
1683         return( AFPERR_DIRNEMPT );
1684     }
1685
1686     fdir = curdir;
1687
1688 #ifdef FORCE_UIDGID
1689     save_uidgid ( &uidgid );
1690     set_uidgid  ( vol );
1691 #endif /* FORCE_UIDGID */
1692
1693     if ( ad_open( ".", ADFLAGS_HF|ADFLAGS_DIR, O_RDONLY,
1694                   DIRBITS | 0777, &ad) == 0 ) {
1695
1696         ad_getattr(&ad, &ashort);
1697         ad_close( &ad, ADFLAGS_HF );
1698         if ((ashort & htons(ATTRBIT_NODELETE))) {
1699 #ifdef FORCE_UIDGID
1700             restore_uidgid ( &uidgid );
1701 #endif /* FORCE_UIDGID */
1702             return  AFPERR_OLOCK;
1703         }
1704     }
1705
1706     /* delete stray .AppleDouble files. this happens to get .Parent files
1707        as well. */
1708     if ((dp = opendir(".AppleDouble"))) {
1709         strcpy(path, ".AppleDouble/");
1710         while ((de = readdir(dp))) {
1711             /* skip this and previous directory */
1712             if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
1713                 continue;
1714
1715             /* bail if the file exists in the current directory.
1716              * note: this will not fail with dangling symlinks */
1717             if (stat(de->d_name, &st) == 0) {
1718                 closedir(dp);
1719 #ifdef FORCE_UIDGID
1720                 restore_uidgid ( &uidgid );
1721 #endif /* FORCE_UIDGID */
1722                 return AFPERR_DIRNEMPT;
1723             }
1724
1725             strcpy(path + DOT_APPLEDOUBLE_LEN, de->d_name);
1726             if (unlink(path) < 0) {
1727                 closedir(dp);
1728                 switch (errno) {
1729                 case EPERM:
1730                 case EACCES :
1731 #ifdef FORCE_UIDGID
1732                     restore_uidgid ( &uidgid );
1733 #endif /* FORCE_UIDGID */
1734                     return( AFPERR_ACCESS );
1735                 case EROFS:
1736 #ifdef FORCE_UIDGID
1737                     restore_uidgid ( &uidgid );
1738 #endif /* FORCE_UIDGID */
1739                     return AFPERR_VLOCK;
1740                 case ENOENT :
1741                     continue;
1742                 default :
1743 #ifdef FORCE_UIDGID
1744                     restore_uidgid ( &uidgid );
1745 #endif /* FORCE_UIDGID */
1746                     return( AFPERR_PARAM );
1747                 }
1748             }
1749         }
1750         closedir(dp);
1751     }
1752
1753     if ( rmdir( ".AppleDouble" ) < 0 ) {
1754         switch ( errno ) {
1755         case ENOENT :
1756             break;
1757         case ENOTEMPTY :
1758 #ifdef FORCE_UIDGID
1759             restore_uidgid ( &uidgid );
1760 #endif /* FORCE_UIDGID */
1761             return( AFPERR_DIRNEMPT );
1762         case EROFS:
1763 #ifdef FORCE_UIDGID
1764             restore_uidgid ( &uidgid );
1765 #endif /* FORCE_UIDGID */
1766             return AFPERR_VLOCK;
1767         case EPERM:
1768         case EACCES :
1769 #ifdef FORCE_UIDGID
1770             restore_uidgid ( &uidgid );
1771 #endif /* FORCE_UIDGID */
1772             return( AFPERR_ACCESS );
1773         default :
1774 #ifdef FORCE_UIDGID
1775             restore_uidgid ( &uidgid );
1776 #endif /* FORCE_UIDGID */
1777             return( AFPERR_PARAM );
1778         }
1779     }
1780
1781     /* now get rid of dangling symlinks */
1782     if ((dp = opendir("."))) {
1783         while ((de = readdir(dp))) {
1784             /* skip this and previous directory */
1785             if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
1786                 continue;
1787
1788             /* bail if it's not a symlink */
1789             if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
1790 #ifdef FORCE_UIDGID
1791                 restore_uidgid ( &uidgid );
1792 #endif /* FORCE_UIDGID */
1793                 return AFPERR_DIRNEMPT;
1794             }
1795
1796             if (unlink(de->d_name) < 0) {
1797                 switch (errno) {
1798                 case EPERM:
1799                 case EACCES :
1800 #ifdef FORCE_UIDGID
1801                     restore_uidgid ( &uidgid );
1802 #endif /* FORCE_UIDGID */
1803                     return( AFPERR_ACCESS );
1804                 case EROFS:
1805 #ifdef FORCE_UIDGID
1806                     restore_uidgid ( &uidgid );
1807 #endif /* FORCE_UIDGID */
1808                     return AFPERR_VLOCK;
1809                 case ENOENT :
1810                     continue;
1811                 default :
1812 #ifdef FORCE_UIDGID
1813                     restore_uidgid ( &uidgid );
1814 #endif /* FORCE_UIDGID */
1815                     return( AFPERR_PARAM );
1816                 }
1817             }
1818         }
1819         closedir(dp);
1820     }
1821
1822     if ( movecwd( vol, curdir->d_parent ) < 0 ) {
1823 #ifdef FORCE_UIDGID
1824         restore_uidgid ( &uidgid );
1825 #endif /* FORCE_UIDGID */
1826         return( AFPERR_NOOBJ );
1827     }
1828
1829     if ( rmdir(mtoupath(vol, fdir->d_name)) < 0 ) {
1830         switch ( errno ) {
1831         case ENOENT :
1832 #ifdef FORCE_UIDGID
1833             restore_uidgid ( &uidgid );
1834 #endif /* FORCE_UIDGID */
1835             return( AFPERR_NOOBJ );
1836         case ENOTEMPTY :
1837 #ifdef FORCE_UIDGID
1838             restore_uidgid ( &uidgid );
1839 #endif /* FORCE_UIDGID */
1840             return( AFPERR_DIRNEMPT );
1841         case EPERM:
1842         case EACCES :
1843 #ifdef FORCE_UIDGID
1844             restore_uidgid ( &uidgid );
1845 #endif /* FORCE_UIDGID */
1846             return( AFPERR_ACCESS );
1847         case EROFS:
1848 #ifdef FORCE_UIDGID
1849             restore_uidgid ( &uidgid );
1850 #endif /* FORCE_UIDGID */
1851             return AFPERR_VLOCK;
1852         default :
1853 #ifdef FORCE_UIDGID
1854             restore_uidgid ( &uidgid );
1855 #endif /* FORCE_UIDGID */
1856             return( AFPERR_PARAM );
1857         }
1858     }
1859
1860     dirchildremove(curdir, fdir);
1861 #ifdef CNID_DB
1862     cnid_delete(vol->v_db, fdir->d_did);
1863 #endif /* CNID_DB */
1864     dir_remove( vol, fdir );
1865
1866 #ifdef FORCE_UIDGID
1867     restore_uidgid ( &uidgid );
1868 #endif /* FORCE_UIDGID */
1869     return( AFP_OK );
1870 }
1871
1872 int afp_mapid(obj, ibuf, ibuflen, rbuf, rbuflen )
1873 AFPObj      *obj;
1874 char    *ibuf, *rbuf;
1875 int             ibuflen, *rbuflen;
1876 {
1877     struct passwd       *pw;
1878     struct group        *gr;
1879     char                *name;
1880     u_int32_t           id;
1881     int                 len, sfunc;
1882
1883     ibuf++;
1884     sfunc = (unsigned char) *ibuf++;
1885     memcpy( &id, ibuf, sizeof( id ));
1886
1887     id = ntohl(id);
1888
1889     if ( id != 0 ) {
1890         switch ( sfunc ) {
1891         case 1 :
1892             if (( pw = getpwuid( id )) == NULL ) {
1893                 *rbuflen = 0;
1894                 return( AFPERR_NOITEM );
1895             }
1896             name = pw->pw_name;
1897             break;
1898
1899         case 2 :
1900             if (( gr = (struct group *)getgrgid( id )) == NULL ) {
1901                 *rbuflen = 0;
1902                 return( AFPERR_NOITEM );
1903             }
1904             name = gr->gr_name;
1905             break;
1906
1907         default :
1908             *rbuflen = 0;
1909             return( AFPERR_PARAM );
1910         }
1911
1912         len = strlen( name );
1913
1914     } else {
1915         len = 0;
1916         name = NULL;
1917     }
1918
1919     *rbuf++ = len;
1920     if ( len > 0 ) {
1921         memcpy( rbuf, name, len );
1922     }
1923     *rbuflen = len + 1;
1924     return( AFP_OK );
1925 }
1926
1927 int afp_mapname(obj, ibuf, ibuflen, rbuf, rbuflen )
1928 AFPObj      *obj;
1929 char    *ibuf, *rbuf;
1930 int             ibuflen, *rbuflen;
1931 {
1932     struct passwd       *pw;
1933     struct group        *gr;
1934     int                 len, sfunc;
1935     u_int32_t           id;
1936
1937     ibuf++;
1938     sfunc = (unsigned char) *ibuf++;
1939     len = (unsigned char) *ibuf++;
1940     ibuf[ len ] = '\0';
1941
1942     if ( len != 0 ) {
1943         switch ( sfunc ) {
1944         case 3 :
1945             if (( pw = (struct passwd *)getpwnam( ibuf )) == NULL ) {
1946                 *rbuflen = 0;
1947                 return( AFPERR_NOITEM );
1948             }
1949             id = pw->pw_uid;
1950             break;
1951
1952         case 4 :
1953             if (( gr = (struct group *)getgrnam( ibuf )) == NULL ) {
1954                 *rbuflen = 0;
1955                 return( AFPERR_NOITEM );
1956             }
1957             id = gr->gr_gid;
1958             break;
1959         default :
1960             *rbuflen = 0;
1961             return( AFPERR_PARAM );
1962         }
1963     } else {
1964         id = 0;
1965     }
1966     id = htonl(id);
1967     memcpy( rbuf, &id, sizeof( id ));
1968     *rbuflen = sizeof( id );
1969     return( AFP_OK );
1970 }
1971
1972 /* variable DID support */
1973 int afp_closedir(obj, ibuf, ibuflen, rbuf, rbuflen )
1974 AFPObj      *obj;
1975 char    *ibuf, *rbuf;
1976 int             ibuflen, *rbuflen;
1977 {
1978 #if 0
1979     struct vol   *vol;
1980     struct dir   *dir;
1981     u_int16_t    vid;
1982     u_int32_t    did;
1983 #endif /* 0 */
1984
1985     *rbuflen = 0;
1986
1987     /* do nothing as dids are static for the life of the process. */
1988 #if 0
1989     ibuf += 2;
1990
1991     memcpy(&vid,  ibuf, sizeof( vid ));
1992     ibuf += sizeof( vid );
1993     if (( vol = getvolbyvid( vid )) == NULL ) {
1994         return( AFPERR_PARAM );
1995     }
1996
1997     memcpy( &did, ibuf, sizeof( did ));
1998     ibuf += sizeof( did );
1999     if (( dir = dirsearch( vol, did )) == NULL ) {
2000         return( AFPERR_PARAM );
2001     }
2002
2003     /* dir_remove -- deletedid */
2004 #endif /* 0 */
2005
2006     return AFP_OK;
2007 }
2008
2009 /* did creation gets done automatically */
2010 int afp_opendir(obj, ibuf, ibuflen, rbuf, rbuflen )
2011 AFPObj      *obj;
2012 char    *ibuf, *rbuf;
2013 int             ibuflen, *rbuflen;
2014 {
2015     struct vol          *vol;
2016     struct dir          *dir, *parentdir;
2017     struct stat         st;
2018     char                *path, *upath;
2019     u_int32_t           did;
2020     u_int16_t           vid;
2021 #ifdef FORCE_UIDGID
2022     uidgidset           *uidgid;
2023
2024     memset(&uidgid, 0, sizeof(uidgid));
2025 #endif /* FORCE_UIDGID */
2026
2027     *rbuflen = 0;
2028     ibuf += 2;
2029
2030     memcpy(&vid, ibuf, sizeof(vid));
2031     ibuf += sizeof( vid );
2032
2033     if (( vol = getvolbyvid( vid )) == NULL ) {
2034         return( AFPERR_PARAM );
2035     }
2036
2037     memcpy(&did, ibuf, sizeof(did));
2038     ibuf += sizeof(did);
2039
2040     if (( parentdir = dirsearch( vol, did )) == NULL ) {
2041         return( AFPERR_NOOBJ );
2042     }
2043
2044     if (( path = cname( vol, parentdir, &ibuf )) == NULL ) {
2045         switch( errno ) {
2046         case EACCES:
2047             return( AFPERR_ACCESS );
2048         default:
2049             return( AFPERR_NOOBJ );
2050         }
2051     }
2052
2053     /* see if we already have the directory. */
2054     upath = mtoupath(vol, path);
2055     if ( stat( upath, &st ) < 0 ) {
2056         return( AFPERR_NOOBJ );
2057     }
2058
2059     dir = parentdir->d_child;
2060     while (dir) {
2061         if (strdiacasecmp(dir->d_name, path) == 0) {
2062             memcpy(rbuf, &dir->d_did, sizeof(dir->d_did));
2063             *rbuflen = sizeof(dir->d_did);
2064             return AFP_OK;
2065         }
2066         dir = (dir == parentdir->d_child->d_prev) ? NULL : dir->d_next;
2067     }
2068
2069 #ifdef FORCE_UIDGID
2070     save_uidgid ( &uidgid );
2071     set_uidgid  ( vol );
2072 #endif /* FORCE_UIDGID */
2073
2074     /* we don't already have a did. add one in. */
2075     if ((dir = adddir(vol, parentdir, path, strlen(path),
2076                       upath, strlen(upath), &st)) == NULL) {
2077 #ifdef FORCE_UIDGID
2078         restore_uidgid ( &uidgid );
2079 #endif /* FORCE_UIDGID */
2080         return AFPERR_MISC;
2081     }
2082
2083     memcpy(rbuf, &dir->d_did, sizeof(dir->d_did));
2084     *rbuflen = sizeof(dir->d_did);
2085 #ifdef FORCE_UIDGID
2086     restore_uidgid ( &uidgid );
2087 #endif /* FORCE_UIDGID */
2088     return AFP_OK;
2089 }