]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/directory.c
minor formatting change to cname()
[netatalk.git] / etc / afpd / directory.c
1 /*
2  * $Id: directory.c,v 1.30 2002-04-29 06:53:25 morgana 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 * cname( vol, dir, cpath )
738 const struct vol        *vol;
739 struct dir      *dir;
740 char    **cpath;
741 {
742     struct dir          *cdir;
743     static char         path[ MAXPATHLEN + 1];
744     char                *data, *p;
745     int                 extend = 0;
746     int                 len;
747
748     data = *cpath;
749     if ( *data++ != 2 ) {                       /* path type */
750         return( NULL );
751     }
752     len = (unsigned char) *data++;
753     *cpath += len + 2;
754     *path = '\0';
755
756     for ( ;; ) {
757         if ( len == 0 ) {
758             if ( !extend && movecwd( vol, dir ) < 0 ) {
759                 return( NULL );
760             }
761             return( path );
762         }
763
764         if ( *data == '\0' ) {
765             data++;
766             len--;
767         }
768
769         while ( *data == '\0' && len > 0 ) {
770             if ( dir->d_parent == NULL ) {
771                 return( NULL );
772             }
773             dir = dir->d_parent;
774             data++;
775             len--;
776         }
777
778         /* would this be faster with strlen + strncpy? */
779         p = path;
780         while ( *data != '\0' && len > 0 ) {
781             *p++ = *data++;
782             len--;
783         }
784
785         /* short cut bits by chopping off a trailing \0. this also
786                   makes the traversal happy w/ filenames at the end of the
787                   cname. */
788         if (len == 1)
789             len--;
790
791 #ifdef notdef
792         /*
793          * Dung Nguyen <ntd@adb.fr>
794          *
795          * AFPD cannot handle paths with "::" if the "::" notation is
796          * not at the beginning of the path. The following path will not
797          * be interpreted correctly:
798          *
799          * :a:b:::c: (directory c at the same level as directory a) */
800         if ( len > 0 ) {
801             data++;
802             len--;
803         }
804 #endif /* notdef */
805         *p = '\0';
806
807         if ( p != path ) { /* we got something */
808             if ( !extend ) {
809                 cdir = dir->d_child;
810                 while (cdir) {
811                     if ( strcasecmp( cdir->d_name, path ) == 0 ) {
812                         break;
813                     }
814                     cdir = (cdir == dir->d_child->d_prev) ? NULL :
815                            cdir->d_next;
816                 }
817                 if ( cdir == NULL ) {
818                     ++extend;
819                     if ( movecwd( vol, dir ) < 0 ) {
820                         return( NULL );
821                     }
822                     cdir = extenddir( vol, dir, path );
823                 }
824
825             } else {
826                 cdir = extenddir( vol, dir, path );
827             }
828
829             if ( cdir == NULL ) {
830                 if ( len > 0 ) {
831                     return( NULL );
832                 }
833
834             } else {
835                 dir = cdir;
836                 *path = '\0';
837             }
838         }
839     }
840 }
841
842 /*
843  * Move curdir to dir, with a possible chdir()
844  */
845 int movecwd( vol, dir)
846 const struct vol        *vol;
847 struct dir      *dir;
848 {
849     char path[MAXPATHLEN + 1];
850     struct dir  *d;
851     char        *p, *u;
852     int         n;
853
854     if ( dir == curdir ) {
855         return( 0 );
856     }
857     if ( dir->d_did == DIRDID_ROOT_PARENT) {
858         return( -1 );
859     }
860
861     p = path + sizeof(path) - 1;
862     *p-- = '\0';
863     *p = '.';
864     for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) {
865         *--p = '/';
866         u = mtoupath(vol, d->d_name );
867         n = strlen( u );
868         p -= n;
869         strncpy( p, u, n );
870     }
871     if ( d != curdir ) {
872         *--p = '/';
873         n = strlen( vol->v_path );
874         p -= n;
875         strncpy( p, vol->v_path, n );
876     }
877     if ( chdir( p ) < 0 ) {
878         return( -1 );
879     }
880     curdir = dir;
881     return( 0 );
882 }
883
884 int getdirparams(const struct vol *vol,
885                  u_int16_t bitmap,
886                  char *upath, struct dir *dir, struct stat *st,
887                  char *buf, int *buflen )
888 {
889     struct maccess      ma;
890     struct adouble      ad;
891     char                *data, *nameoff = NULL;
892     DIR                 *dp;
893     struct dirent       *de;
894     int                 bit = 0, isad = 1;
895     u_int32_t           aint;
896     u_int16_t           ashort;
897 #ifdef FORCE_UIDGID
898     uidgidset           *uidgid;
899     memset(&uidgid, 0, sizeof(uidgid));
900 #endif /* FORCE_UIDGID */
901
902     memset(&ad, 0, sizeof(ad));
903
904 #ifdef FORCE_UIDGID
905     save_uidgid ( &uidgid );
906     set_uidgid ( vol );
907 #endif /* FORCE_UIDGID */
908
909     if ( ad_open( upath, ADFLAGS_HF|ADFLAGS_DIR, O_RDONLY,
910                   DIRBITS | 0777, &ad) < 0 ) {
911         isad = 0;
912     }
913
914     data = buf;
915     while ( bitmap != 0 ) {
916         while (( bitmap & 1 ) == 0 ) {
917             bitmap = bitmap>>1;
918             bit++;
919         }
920
921         switch ( bit ) {
922         case DIRPBIT_ATTR :
923             if ( isad ) {
924                 ad_getattr(&ad, &ashort);
925             } else if (*upath == '.' && strcmp(upath, ".") &&
926                        strcmp(upath, "..")) {
927                 ashort = htons(ATTRBIT_INVISIBLE);
928             } else
929                 ashort = 0;
930             memcpy( data, &ashort, sizeof( ashort ));
931             data += sizeof( ashort );
932             break;
933
934         case DIRPBIT_PDID :
935             if ( dir->d_did == DIRDID_ROOT) {
936                 aint = DIRDID_ROOT_PARENT;
937             } else if (dir->d_did == DIRDID_ROOT_PARENT) {
938                 aint = 0;
939             } else {
940                 aint = dir->d_parent->d_did;
941             }
942             memcpy( data, &aint, sizeof( aint ));
943             data += sizeof( aint );
944             break;
945
946         case DIRPBIT_CDATE :
947             if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
948                 aint = AD_DATE_FROM_UNIX(st->st_mtime);
949             memcpy( data, &aint, sizeof( aint ));
950             data += sizeof( aint );
951             break;
952
953         case DIRPBIT_MDATE :
954             aint = AD_DATE_FROM_UNIX(st->st_mtime);
955             memcpy( data, &aint, sizeof( aint ));
956             data += sizeof( aint );
957             break;
958
959         case DIRPBIT_BDATE :
960             if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
961                 aint = AD_DATE_START;
962             memcpy( data, &aint, sizeof( aint ));
963             data += sizeof( aint );
964             break;
965
966         case DIRPBIT_FINFO :
967             if ( isad ) {
968                 memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
969             } else { /* no appledouble */
970                 memset( data, 0, 32 );
971                 /* set default view -- this also gets done in ad_open() */
972                 ashort = htons(FINDERINFO_CLOSEDVIEW);
973                 memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
974
975                 /* dot files are by default invisible */
976                 if (*upath == '.' && strcmp(upath, ".") &&
977                         strcmp(upath, "..")) {
978                     ashort = htons(FINDERINFO_INVISIBLE);
979                     memcpy(data + FINDERINFO_FRFLAGOFF,
980                            &ashort, sizeof(ashort));
981                 }
982             }
983             data += 32;
984             break;
985
986         case DIRPBIT_LNAME :
987             if (dir->d_name) /* root of parent can have a null name */
988                 nameoff = data;
989             else
990                 memset(data, 0, sizeof(u_int16_t));
991             data += sizeof( u_int16_t );
992             break;
993
994         case DIRPBIT_SNAME :
995             memset(data, 0, sizeof(u_int16_t));
996             data += sizeof( u_int16_t );
997             break;
998
999         case DIRPBIT_DID :
1000             memcpy( data, &dir->d_did, sizeof( aint ));
1001             data += sizeof( aint );
1002             break;
1003
1004         case DIRPBIT_OFFCNT :
1005             ashort = 0;
1006             /* this needs to handle current directory access rights */
1007             if ((dp = opendir( upath ))) {
1008                 while (( de = readdir( dp )) != NULL ) {
1009                     if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, "."))
1010                         continue;
1011
1012                     if (!validupath(vol, de->d_name))
1013                         continue;
1014
1015                     /* check for vetoed filenames */
1016                     if (veto_file(vol->v_veto, de->d_name))
1017                         continue;
1018
1019                     /* now check against too long a filename */
1020                     if (strlen(utompath(vol, de->d_name)) > MACFILELEN)
1021                         continue;
1022
1023                     ashort++;
1024                 }
1025                 closedir( dp );
1026             }
1027             ashort = htons( ashort );
1028             memcpy( data, &ashort, sizeof( ashort ));
1029             data += sizeof( ashort );
1030             break;
1031
1032         case DIRPBIT_UID :
1033             aint = htonl(st->st_uid);
1034             memcpy( data, &aint, sizeof( aint ));
1035             data += sizeof( aint );
1036             break;
1037
1038         case DIRPBIT_GID :
1039             aint = htonl(st->st_gid);
1040             memcpy( data, &aint, sizeof( aint ));
1041             data += sizeof( aint );
1042             break;
1043
1044         case DIRPBIT_ACCESS :
1045             utommode( st, &ma );
1046 #ifndef SENDFILE_FLAVOR_LINUX /* ignore this section if it's linux */
1047 #ifdef HAVE_ACCESS
1048             accessmode( upath, &ma, dir );
1049 #endif /* HAVE_ACCESS */
1050 #endif /* SENDFILE_FLAVOR_LINUX */
1051 #ifdef AFS      /* If only AFS defined, access() works only for AFS filesystems */ 
1052             afsmode( upath, &ma, dir );
1053 #endif /* AFS */
1054             *data++ = ma.ma_user;
1055             *data++ = ma.ma_world;
1056             *data++ = ma.ma_group;
1057             *data++ = ma.ma_owner;
1058             break;
1059
1060             /* Client has requested the ProDOS information block.
1061                Just pass back the same basic block for all
1062                directories. <shirsch@ibm.net> */
1063         case DIRPBIT_PDINFO :                     /* ProDOS Info Block */
1064             *data++ = 0x0f;
1065             *data++ = 0;
1066             ashort = htons( 0x0200 );
1067             memcpy( data, &ashort, sizeof( ashort ));
1068             data += sizeof( ashort );
1069             memset( data, 0, sizeof( ashort ));
1070             data += sizeof( ashort );
1071             break;
1072
1073         default :
1074             if ( isad ) {
1075                 ad_close( &ad, ADFLAGS_HF );
1076             }
1077 #ifdef FORCE_UIDGID
1078             restore_uidgid ( &uidgid );
1079 #endif /* FORCE_UIDGID */
1080             return( AFPERR_BITMAP );
1081         }
1082         bitmap = bitmap>>1;
1083         bit++;
1084     }
1085     if ( nameoff ) {
1086         ashort = htons( data - buf );
1087         memcpy( nameoff, &ashort, sizeof( ashort ));
1088
1089         if ((aint = strlen( dir->d_name )) > MACFILELEN)
1090             aint = MACFILELEN;
1091
1092         *data++ = aint;
1093         memcpy( data, dir->d_name, aint );
1094         data += aint;
1095     }
1096     if ( isad ) {
1097         ad_close( &ad, ADFLAGS_HF );
1098     }
1099     *buflen = data - buf;
1100     return( AFP_OK );
1101 }
1102
1103 int afp_setdirparams(obj, ibuf, ibuflen, rbuf, rbuflen )
1104 AFPObj      *obj;
1105 char    *ibuf, *rbuf;
1106 int             ibuflen, *rbuflen;
1107 {
1108     struct vol  *vol;
1109     struct dir  *dir;
1110     char        *path;
1111     u_int16_t   vid, bitmap;
1112     u_int32_t   did;
1113     int         rc;
1114
1115     *rbuflen = 0;
1116     ibuf += 2;
1117     memcpy( &vid, ibuf, sizeof( vid ));
1118     ibuf += sizeof( vid );
1119
1120     if (( vol = getvolbyvid( vid )) == NULL ) {
1121         return( AFPERR_PARAM );
1122     }
1123
1124     if (vol->v_flags & AFPVOL_RO)
1125         return AFPERR_VLOCK;
1126
1127     memcpy( &did, ibuf, sizeof( did ));
1128     ibuf += sizeof( int );
1129
1130     if (( dir = dirsearch( vol, did )) == NULL ) {
1131         return( AFPERR_NOOBJ );
1132     }
1133
1134     memcpy( &bitmap, ibuf, sizeof( bitmap ));
1135     bitmap = ntohs( bitmap );
1136     ibuf += sizeof( bitmap );
1137
1138     if (( path = cname( vol, dir, &ibuf )) == NULL ) {
1139         return( AFPERR_NOOBJ );
1140     }
1141
1142     /*
1143      * If ibuf is odd, make it even.
1144      */
1145     if ((u_long)ibuf & 1 ) {
1146         ibuf++;
1147     }
1148
1149     if (( rc = setdirparams(vol, path, bitmap, ibuf )) == AFP_OK ) {
1150         setvoltime(obj, vol );
1151     }
1152     return( rc );
1153 }
1154
1155 int setdirparams(const struct vol *vol,
1156                  char *path, u_int16_t bitmap, char *buf )
1157 {
1158     struct maccess      ma;
1159     struct adouble      ad;
1160     struct utimbuf      ut;
1161     char                *upath;
1162     int                 bit = 0, aint, isad = 1;
1163     u_int16_t           ashort, bshort;
1164     int                 err = AFP_OK;
1165 #ifdef FORCE_UIDGID
1166     uidgidset           *uidgid;
1167
1168     memset(&uidgid, 0, sizeof(uidgid));
1169 #endif /* FORCE_UIDGID */
1170
1171     upath = mtoupath(vol, path);
1172     memset(&ad, 0, sizeof(ad));
1173 #ifdef FORCE_UIDGID
1174     save_uidgid ( &uidgid );
1175 #endif /* FORCE_UIDGID */
1176     if (ad_open( upath, vol_noadouble(vol)|ADFLAGS_HF|ADFLAGS_DIR,
1177                  O_RDWR|O_CREAT, 0666, &ad) < 0) {
1178         /*
1179          * Check to see what we're trying to set.  If it's anything
1180          * but ACCESS, UID, or GID, give an error.  If it's any of those
1181          * three, we don't need the ad to be open, so just continue.
1182          *
1183          * note: we also don't need to worry about mdate. also, be quiet
1184          *       if we're using the noadouble option.
1185          */
1186         if (!vol_noadouble(vol) && (bitmap &
1187                                     ~((1<<DIRPBIT_ACCESS)|(1<<DIRPBIT_UID)|(1<<DIRPBIT_GID)|
1188                                       (1<<DIRPBIT_MDATE)|(1<<DIRPBIT_PDINFO)))) {
1189 #ifdef FORCE_UIDGID
1190             restore_uidgid ( &uidgid );
1191 #endif /* FORCE_UIDGID */
1192             return AFPERR_ACCESS;
1193         }
1194
1195         isad = 0;
1196     } else {
1197         /*
1198          * Check to see if a create was necessary. If it was, we'll want
1199          * to set our name, etc.
1200          */
1201         if ( ad_getoflags( &ad, ADFLAGS_HF ) & O_CREAT ) {
1202             ad_setentrylen( &ad, ADEID_NAME, strlen( curdir->d_name ));
1203             memcpy( ad_entry( &ad, ADEID_NAME ), curdir->d_name,
1204                     ad_getentrylen( &ad, ADEID_NAME ));
1205         }
1206     }
1207
1208     while ( bitmap != 0 ) {
1209         while (( bitmap & 1 ) == 0 ) {
1210             bitmap = bitmap>>1;
1211             bit++;
1212         }
1213
1214         switch( bit ) {
1215         case DIRPBIT_ATTR :
1216             if (isad) {
1217                 memcpy( &ashort, buf, sizeof( ashort ));
1218                 ad_getattr(&ad, &bshort);
1219                 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1220                     bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1221                 } else {
1222                     bshort &= ~ashort;
1223                 }
1224                 ad_setattr(&ad, bshort);
1225             }
1226             buf += sizeof( ashort );
1227             break;
1228
1229         case DIRPBIT_CDATE :
1230             if (isad) {
1231                 memcpy(&aint, buf, sizeof(aint));
1232                 ad_setdate(&ad, AD_DATE_CREATE, aint);
1233             }
1234             buf += sizeof( aint );
1235             break;
1236
1237         case DIRPBIT_MDATE :
1238             memcpy(&aint, buf, sizeof(aint));
1239             if (isad)
1240                 ad_setdate(&ad, AD_DATE_MODIFY, aint);
1241             ut.actime = ut.modtime = AD_DATE_TO_UNIX(aint);
1242             utime(upath, &ut);
1243             buf += sizeof( aint );
1244             break;
1245
1246         case DIRPBIT_BDATE :
1247             if (isad) {
1248                 memcpy(&aint, buf, sizeof(aint));
1249                 ad_setdate(&ad, AD_DATE_BACKUP, aint);
1250             }
1251             buf += sizeof( aint );
1252             break;
1253
1254         case DIRPBIT_FINFO :
1255             /*
1256              * Alright, we admit it, this is *really* sick!
1257              * The 4 bytes that we don't copy, when we're dealing
1258              * with the root of a volume, are the directory's
1259              * location information. This eliminates that annoying
1260              * behavior one sees when mounting above another mount
1261              * point.
1262              */
1263             if (isad) {
1264                 if (  curdir->d_did == DIRDID_ROOT ) {
1265                     memcpy( ad_entry( &ad, ADEID_FINDERI ), buf, 10 );
1266                     memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, buf + 14, 18 );
1267                 } else {
1268                     memcpy( ad_entry( &ad, ADEID_FINDERI ), buf, 32 );
1269                 }
1270             }
1271             buf += 32;
1272             break;
1273
1274         case DIRPBIT_UID :      /* What kind of loser mounts as root? */
1275             memcpy( &aint, buf, sizeof(aint));
1276             buf += sizeof( aint );
1277             if ( (curdir->d_did == DIRDID_ROOT) &&
1278                     (setdeskowner( ntohl(aint), -1 ) < 0)) {
1279                 switch ( errno ) {
1280                 case EPERM :
1281                 case EACCES :
1282                     err = AFPERR_ACCESS;
1283                     goto setdirparam_done;
1284                     break;
1285                 case EROFS :
1286                     err = AFPERR_VLOCK;
1287                     goto setdirparam_done;
1288                     break;
1289                 default :
1290                     LOG(log_error, logtype_afpd, "setdirparam: setdeskowner: %s",
1291                         strerror(errno) );
1292                     if (!isad) {
1293                         err = AFPERR_PARAM;
1294                         goto setdirparam_done;
1295                     }
1296                     break;
1297                 }
1298             }
1299             if ( setdirowner( ntohl(aint), -1, vol_noadouble(vol) ) < 0 ) {
1300                 switch ( errno ) {
1301                 case EPERM :
1302                 case EACCES :
1303                     err = AFPERR_ACCESS;
1304                     goto setdirparam_done;
1305                     break;
1306                 case EROFS :
1307                     err = AFPERR_VLOCK;
1308                     goto setdirparam_done;
1309                     break;
1310                 default :
1311                     LOG(log_error, logtype_afpd, "setdirparam: setdirowner: %s",
1312                         strerror(errno) );
1313                     break;
1314                 }
1315             }
1316             break;
1317         case DIRPBIT_GID :
1318             memcpy( &aint, buf, sizeof( aint ));
1319             buf += sizeof( aint );
1320             if (curdir->d_did == DIRDID_ROOT)
1321                 setdeskowner( -1, ntohl(aint) );
1322
1323 #if 0       /* don't error if we can't set the desktop owner. */
1324             switch ( errno ) {
1325             case EPERM :
1326             case EACCES :
1327                 err = AFPERR_ACCESS;
1328                 goto setdirparam_done;
1329                 break;
1330             case EROFS :
1331                 err = AFPERR_VLOCK;
1332                 goto setdirparam_done;
1333                 break;
1334             default :
1335                 LOG(log_error, logtype_afpd, "setdirparam: setdeskowner: %m" );
1336                 if (!isad) {
1337                     err = AFPERR_PARAM;
1338                     goto setdirparam_done;
1339                 }
1340                 break;
1341             }
1342 #endif /* 0 */
1343
1344             if ( setdirowner( -1, ntohl(aint), vol_noadouble(vol) ) < 0 ) {
1345                 switch ( errno ) {
1346                 case EPERM :
1347                 case EACCES :
1348                     err = AFPERR_ACCESS;
1349                     goto setdirparam_done;
1350                     break;
1351                 case EROFS :
1352                     err = AFPERR_VLOCK;
1353                     goto setdirparam_done;
1354                     break;
1355                 default :
1356                     LOG(log_error, logtype_afpd, "setdirparam: setdirowner: %s",
1357                         strerror(errno) );
1358                     break;
1359                 }
1360             }
1361             break;
1362
1363         case DIRPBIT_ACCESS :
1364             ma.ma_user = *buf++;
1365             ma.ma_world = *buf++;
1366             ma.ma_group = *buf++;
1367             ma.ma_owner = *buf++;
1368
1369             if (curdir->d_did == DIRDID_ROOT)
1370                 setdeskmode(mtoumode( &ma ));
1371 #if 0 /* don't error if we can't set the desktop mode */
1372             switch ( errno ) {
1373             case EPERM :
1374             case EACCES :
1375                 err = AFPERR_ACCESS;
1376                 goto setdirparam_done;
1377             case EROFS :
1378                 err = AFPERR_VLOCK;
1379                 goto setdirparam_done;
1380             default :
1381                 LOG(log_error, logtype_afpd, "setdirparam: setdeskmode: %s",
1382                     strerror(errno) );
1383                 break;
1384                 err = AFPERR_PARAM;
1385                 goto setdirparam_done;
1386             }
1387         }
1388 #endif /* 0 */
1389
1390         if ( setdirmode( mtoumode( &ma ), vol_noadouble(vol),
1391                          (vol->v_flags & AFPVOL_DROPBOX)) < 0 ) {
1392             switch ( errno ) {
1393             case EPERM :
1394             case EACCES :
1395                 err = AFPERR_ACCESS;
1396                 goto setdirparam_done;
1397             case EROFS :
1398                 err = AFPERR_VLOCK;
1399                 goto setdirparam_done;
1400             default :
1401                 LOG(log_error, logtype_afpd, "setdirparam: setdirmode: %s",
1402                     strerror(errno) );
1403                 err = AFPERR_PARAM;
1404                 goto setdirparam_done;
1405             }
1406         }
1407         break;
1408
1409         /* Ignore what the client thinks we should do to the
1410            ProDOS information block.  Skip over the data and
1411            report nothing amiss. <shirsch@ibm.net> */
1412     case DIRPBIT_PDINFO :
1413         buf += 6;
1414         break;
1415
1416     default :
1417         err = AFPERR_BITMAP;
1418         goto setdirparam_done;
1419         break;
1420     }
1421
1422     bitmap = bitmap>>1;
1423     bit++;
1424 }
1425
1426
1427 setdirparam_done:
1428 if ( isad ) {
1429     ad_flush( &ad, ADFLAGS_HF );
1430     ad_close( &ad, ADFLAGS_HF );
1431 }
1432
1433 #ifdef FORCE_UIDGID
1434 restore_uidgid ( &uidgid );
1435 #endif /* FORCE_UIDGID */
1436 return err;
1437 }
1438
1439 int afp_createdir(obj, ibuf, ibuflen, rbuf, rbuflen )
1440 AFPObj      *obj;
1441 char    *ibuf, *rbuf;
1442 int             ibuflen, *rbuflen;
1443 {
1444     struct adouble      ad;
1445     struct stat         st;
1446     struct vol          *vol;
1447     struct dir          *dir;
1448     char                *path, *upath;
1449     u_int32_t           did;
1450     u_int16_t           vid;
1451 #ifdef FORCE_UIDGID
1452     uidgidset           *uidgid;
1453
1454     memset(&uidgid, 0, sizeof(uidgid));
1455 #endif /* FORCE_UIDGID */
1456
1457     *rbuflen = 0;
1458     ibuf += 2;
1459
1460     memcpy( &vid, ibuf, sizeof( vid ));
1461     ibuf += sizeof( vid );
1462     if (( vol = getvolbyvid( vid )) == NULL ) {
1463         return( AFPERR_PARAM );
1464     }
1465
1466     if (vol->v_flags & AFPVOL_RO)
1467         return AFPERR_VLOCK;
1468
1469     memcpy( &did, ibuf, sizeof( did ));
1470     ibuf += sizeof( did );
1471     if (( dir = dirsearch( vol, did )) == NULL ) {
1472         return( AFPERR_NOOBJ );
1473     }
1474
1475     if (( path = cname( vol, dir, &ibuf )) == NULL ) {
1476         switch( errno ) {
1477         case EACCES:
1478             return( AFPERR_ACCESS );
1479         case EEXIST:
1480             return( AFPERR_EXIST );
1481         default:
1482             return( AFPERR_NOOBJ );
1483         }
1484     }
1485
1486     upath = mtoupath(vol, path);
1487
1488     /* check for illegal bits in the unix filename */
1489     if (!wincheck(vol, upath))
1490         return AFPERR_PARAM;
1491
1492     if ((vol->v_flags & AFPVOL_NOHEX) && strchr(upath, '/'))
1493         return AFPERR_PARAM;
1494
1495     if (!validupath(vol, upath))
1496         return AFPERR_EXIST;
1497
1498     /* check for vetoed filenames */
1499     if (veto_file(vol->v_veto, upath))
1500         return AFPERR_EXIST;
1501
1502 #ifdef FORCE_UIDGID
1503     save_uidgid ( &uidgid );
1504     set_uidgid  ( vol );
1505 #endif /* FORCE_UIDGID */
1506
1507     if ( ad_mkdir( upath, DIRBITS | 0777 ) < 0 ) {
1508 #ifdef FORCE_UIDGID
1509         restore_uidgid ( &uidgid );
1510 #endif /* FORCE_UIDGID */
1511         switch ( errno ) {
1512         case ENOENT :
1513             return( AFPERR_NOOBJ );
1514         case EROFS :
1515             return( AFPERR_VLOCK );
1516         case EACCES :
1517             return( AFPERR_ACCESS );
1518         case EEXIST :
1519             return( AFPERR_EXIST );
1520         case ENOSPC :
1521         case EDQUOT :
1522             return( AFPERR_DFULL );
1523         default :
1524             return( AFPERR_PARAM );
1525         }
1526     }
1527
1528     if (stat(upath, &st) < 0) {
1529 #ifdef FORCE_UIDGID
1530         restore_uidgid ( &uidgid );
1531 #endif /* FORCE_UIDGID */
1532         return AFPERR_MISC;
1533     }
1534
1535     if ((dir = adddir( vol, curdir, path, strlen( path ), upath,
1536                        strlen(upath), &st)) == NULL) {
1537 #ifdef FORCE_UIDGID
1538         restore_uidgid ( &uidgid );
1539 #endif /* FORCE_UIDGID */
1540         return AFPERR_MISC;
1541     }
1542
1543     if ( movecwd( vol, dir ) < 0 ) {
1544 #ifdef FORCE_UIDGID
1545         restore_uidgid ( &uidgid );
1546 #endif /* FORCE_UIDGID */
1547         return( AFPERR_PARAM );
1548     }
1549
1550     memset(&ad, 0, sizeof(ad));
1551     if (ad_open( "", vol_noadouble(vol)|ADFLAGS_HF|ADFLAGS_DIR,
1552                  O_RDWR|O_CREAT, 0666, &ad ) < 0)  {
1553         if (vol_noadouble(vol))
1554             goto createdir_done;
1555 #ifdef FORCE_UIDGID
1556         restore_uidgid ( &uidgid );
1557 #endif /* FORCE_UIDGID */
1558         return( AFPERR_ACCESS );
1559     }
1560
1561     ad_setentrylen( &ad, ADEID_NAME, strlen( path ));
1562     memcpy( ad_entry( &ad, ADEID_NAME ), path,
1563             ad_getentrylen( &ad, ADEID_NAME ));
1564     ad_flush( &ad, ADFLAGS_HF );
1565     ad_close( &ad, ADFLAGS_HF );
1566
1567 createdir_done:
1568     memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
1569     *rbuflen = sizeof( u_int32_t );
1570     setvoltime(obj, vol );
1571 #ifdef FORCE_UIDGID
1572     restore_uidgid ( &uidgid );
1573 #endif /* FORCE_UIDGID */
1574     return( AFP_OK );
1575 }
1576
1577
1578 int renamedir(src, dst, dir, newparent, newname, noadouble)
1579 char    *src, *dst, *newname;
1580 struct dir      *dir, *newparent;
1581 const int noadouble;
1582 {
1583     struct adouble      ad;
1584     struct dir          *parent;
1585     char                *buf;
1586     int                 len, err;
1587
1588     /* existence check moved to afp_moveandrename */
1589     if ( rename( src, dst ) < 0 ) {
1590         switch ( errno ) {
1591         case ENOENT :
1592             return( AFPERR_NOOBJ );
1593         case EACCES :
1594             return( AFPERR_ACCESS );
1595         case EROFS:
1596             return AFPERR_VLOCK;
1597         case EINVAL:
1598             /* tried to move directory into a subdirectory of itself */
1599             return AFPERR_CANTMOVE;
1600         case EXDEV:
1601             /* this needs to copy and delete. bleah. that means we have
1602              * to deal with entire directory hierarchies. */
1603             if ((err = copydir(src, dst, noadouble)) < 0) {
1604                 deletedir(dst);
1605                 return err;
1606             }
1607             if ((err = deletedir(src)) < 0)
1608                 return err;
1609             break;
1610         default :
1611             return( AFPERR_PARAM );
1612         }
1613     }
1614
1615     memset(&ad, 0, sizeof(ad));
1616     if ( ad_open( dst, ADFLAGS_HF|ADFLAGS_DIR, O_RDWR, 0, &ad) < 0 ) {
1617         switch ( errno ) {
1618         case ENOENT :
1619             if (noadouble) {
1620                 len = strlen(newname);
1621                 goto renamedir_done;
1622             }
1623             return( AFPERR_NOOBJ );
1624         case EACCES :
1625             return( AFPERR_ACCESS );
1626         default :
1627             return( AFPERR_PARAM );
1628         }
1629     }
1630     len = strlen( newname );
1631     ad_setentrylen( &ad, ADEID_NAME, len );
1632     memcpy( ad_entry( &ad, ADEID_NAME ), newname, len );
1633     ad_flush( &ad, ADFLAGS_HF );
1634     ad_close( &ad, ADFLAGS_HF );
1635
1636 renamedir_done:
1637     if ((buf = (char *) realloc( dir->d_name, len + 1 )) == NULL ) {
1638         LOG(log_error, logtype_afpd, "renamedir: realloc: %s", strerror(errno) );
1639         return AFPERR_MISC;
1640     }
1641     dir->d_name = buf;
1642     strcpy( dir->d_name, newname );
1643
1644     if (( parent = dir->d_parent ) == NULL ) {
1645         return( AFP_OK );
1646     }
1647     if ( parent == newparent ) {
1648         return( AFP_OK );
1649     }
1650
1651     /* detach from old parent and add to new one. */
1652     dirchildremove(parent, dir);
1653     dir->d_parent = newparent;
1654     dirchildadd(newparent, dir);
1655     return( AFP_OK );
1656 }
1657
1658 #define DOT_APPLEDOUBLE_LEN 13
1659 /* delete an empty directory */
1660 int deletecurdir( vol, path, pathlen )
1661 const struct vol        *vol;
1662 char *path;
1663 int pathlen;
1664 {
1665     struct dirent *de;
1666     struct stat st;
1667     struct dir  *fdir;
1668     DIR *dp;
1669     struct adouble      ad;
1670     u_int16_t           ashort;
1671 #ifdef FORCE_UIDGID
1672     uidgidset           *uidgid;
1673
1674     memset(&uidgid, 0, sizeof(uidgid));
1675 #endif /* FORCE_UIDGID */
1676
1677     if ( curdir->d_parent == NULL ) {
1678         return( AFPERR_ACCESS );
1679     }
1680
1681     if ( curdir->d_child != NULL ) {
1682         return( AFPERR_DIRNEMPT );
1683     }
1684
1685     fdir = curdir;
1686
1687 #ifdef FORCE_UIDGID
1688     save_uidgid ( &uidgid );
1689     set_uidgid  ( vol );
1690 #endif /* FORCE_UIDGID */
1691
1692     if ( ad_open( ".", ADFLAGS_HF|ADFLAGS_DIR, O_RDONLY,
1693                   DIRBITS | 0777, &ad) == 0 ) {
1694
1695         ad_getattr(&ad, &ashort);
1696         ad_close( &ad, ADFLAGS_HF );
1697         if ((ashort & htons(ATTRBIT_NODELETE))) {
1698 #ifdef FORCE_UIDGID
1699             restore_uidgid ( &uidgid );
1700 #endif /* FORCE_UIDGID */
1701             return  AFPERR_OLOCK;
1702         }
1703     }
1704
1705     /* delete stray .AppleDouble files. this happens to get .Parent files
1706        as well. */
1707     if ((dp = opendir(".AppleDouble"))) {
1708         strcpy(path, ".AppleDouble/");
1709         while ((de = readdir(dp))) {
1710             /* skip this and previous directory */
1711             if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
1712                 continue;
1713
1714             /* bail if the file exists in the current directory.
1715              * note: this will not fail with dangling symlinks */
1716             if (stat(de->d_name, &st) == 0) {
1717                 closedir(dp);
1718 #ifdef FORCE_UIDGID
1719                 restore_uidgid ( &uidgid );
1720 #endif /* FORCE_UIDGID */
1721                 return AFPERR_DIRNEMPT;
1722             }
1723
1724             strcpy(path + DOT_APPLEDOUBLE_LEN, de->d_name);
1725             if (unlink(path) < 0) {
1726                 closedir(dp);
1727                 switch (errno) {
1728                 case EPERM:
1729                 case EACCES :
1730 #ifdef FORCE_UIDGID
1731                     restore_uidgid ( &uidgid );
1732 #endif /* FORCE_UIDGID */
1733                     return( AFPERR_ACCESS );
1734                 case EROFS:
1735 #ifdef FORCE_UIDGID
1736                     restore_uidgid ( &uidgid );
1737 #endif /* FORCE_UIDGID */
1738                     return AFPERR_VLOCK;
1739                 case ENOENT :
1740                     continue;
1741                 default :
1742 #ifdef FORCE_UIDGID
1743                     restore_uidgid ( &uidgid );
1744 #endif /* FORCE_UIDGID */
1745                     return( AFPERR_PARAM );
1746                 }
1747             }
1748         }
1749         closedir(dp);
1750     }
1751
1752     if ( rmdir( ".AppleDouble" ) < 0 ) {
1753         switch ( errno ) {
1754         case ENOENT :
1755             break;
1756         case ENOTEMPTY :
1757 #ifdef FORCE_UIDGID
1758             restore_uidgid ( &uidgid );
1759 #endif /* FORCE_UIDGID */
1760             return( AFPERR_DIRNEMPT );
1761         case EROFS:
1762 #ifdef FORCE_UIDGID
1763             restore_uidgid ( &uidgid );
1764 #endif /* FORCE_UIDGID */
1765             return AFPERR_VLOCK;
1766         case EPERM:
1767         case EACCES :
1768 #ifdef FORCE_UIDGID
1769             restore_uidgid ( &uidgid );
1770 #endif /* FORCE_UIDGID */
1771             return( AFPERR_ACCESS );
1772         default :
1773 #ifdef FORCE_UIDGID
1774             restore_uidgid ( &uidgid );
1775 #endif /* FORCE_UIDGID */
1776             return( AFPERR_PARAM );
1777         }
1778     }
1779
1780     /* now get rid of dangling symlinks */
1781     if ((dp = opendir("."))) {
1782         while ((de = readdir(dp))) {
1783             /* skip this and previous directory */
1784             if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
1785                 continue;
1786
1787             /* bail if it's not a symlink */
1788             if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
1789 #ifdef FORCE_UIDGID
1790                 restore_uidgid ( &uidgid );
1791 #endif /* FORCE_UIDGID */
1792                 return AFPERR_DIRNEMPT;
1793             }
1794
1795             if (unlink(de->d_name) < 0) {
1796                 switch (errno) {
1797                 case EPERM:
1798                 case EACCES :
1799 #ifdef FORCE_UIDGID
1800                     restore_uidgid ( &uidgid );
1801 #endif /* FORCE_UIDGID */
1802                     return( AFPERR_ACCESS );
1803                 case EROFS:
1804 #ifdef FORCE_UIDGID
1805                     restore_uidgid ( &uidgid );
1806 #endif /* FORCE_UIDGID */
1807                     return AFPERR_VLOCK;
1808                 case ENOENT :
1809                     continue;
1810                 default :
1811 #ifdef FORCE_UIDGID
1812                     restore_uidgid ( &uidgid );
1813 #endif /* FORCE_UIDGID */
1814                     return( AFPERR_PARAM );
1815                 }
1816             }
1817         }
1818         closedir(dp);
1819     }
1820
1821     if ( movecwd( vol, curdir->d_parent ) < 0 ) {
1822 #ifdef FORCE_UIDGID
1823         restore_uidgid ( &uidgid );
1824 #endif /* FORCE_UIDGID */
1825         return( AFPERR_NOOBJ );
1826     }
1827
1828     if ( rmdir(mtoupath(vol, fdir->d_name)) < 0 ) {
1829         switch ( errno ) {
1830         case ENOENT :
1831 #ifdef FORCE_UIDGID
1832             restore_uidgid ( &uidgid );
1833 #endif /* FORCE_UIDGID */
1834             return( AFPERR_NOOBJ );
1835         case ENOTEMPTY :
1836 #ifdef FORCE_UIDGID
1837             restore_uidgid ( &uidgid );
1838 #endif /* FORCE_UIDGID */
1839             return( AFPERR_DIRNEMPT );
1840         case EPERM:
1841         case EACCES :
1842 #ifdef FORCE_UIDGID
1843             restore_uidgid ( &uidgid );
1844 #endif /* FORCE_UIDGID */
1845             return( AFPERR_ACCESS );
1846         case EROFS:
1847 #ifdef FORCE_UIDGID
1848             restore_uidgid ( &uidgid );
1849 #endif /* FORCE_UIDGID */
1850             return AFPERR_VLOCK;
1851         default :
1852 #ifdef FORCE_UIDGID
1853             restore_uidgid ( &uidgid );
1854 #endif /* FORCE_UIDGID */
1855             return( AFPERR_PARAM );
1856         }
1857     }
1858
1859     dirchildremove(curdir, fdir);
1860 #ifdef CNID_DB
1861     cnid_delete(vol->v_db, fdir->d_did);
1862 #endif /* CNID_DB */
1863     dir_remove( vol, fdir );
1864
1865 #ifdef FORCE_UIDGID
1866     restore_uidgid ( &uidgid );
1867 #endif /* FORCE_UIDGID */
1868     return( AFP_OK );
1869 }
1870
1871 int afp_mapid(obj, ibuf, ibuflen, rbuf, rbuflen )
1872 AFPObj      *obj;
1873 char    *ibuf, *rbuf;
1874 int             ibuflen, *rbuflen;
1875 {
1876     struct passwd       *pw;
1877     struct group        *gr;
1878     char                *name;
1879     u_int32_t           id;
1880     int                 len, sfunc;
1881
1882     ibuf++;
1883     sfunc = (unsigned char) *ibuf++;
1884     memcpy( &id, ibuf, sizeof( id ));
1885
1886     id = ntohl(id);
1887
1888     if ( id != 0 ) {
1889         switch ( sfunc ) {
1890         case 1 :
1891             if (( pw = getpwuid( id )) == NULL ) {
1892                 *rbuflen = 0;
1893                 return( AFPERR_NOITEM );
1894             }
1895             name = pw->pw_name;
1896             break;
1897
1898         case 2 :
1899             if (( gr = (struct group *)getgrgid( id )) == NULL ) {
1900                 *rbuflen = 0;
1901                 return( AFPERR_NOITEM );
1902             }
1903             name = gr->gr_name;
1904             break;
1905
1906         default :
1907             *rbuflen = 0;
1908             return( AFPERR_PARAM );
1909         }
1910
1911         len = strlen( name );
1912
1913     } else {
1914         len = 0;
1915         name = NULL;
1916     }
1917
1918     *rbuf++ = len;
1919     if ( len > 0 ) {
1920         memcpy( rbuf, name, len );
1921     }
1922     *rbuflen = len + 1;
1923     return( AFP_OK );
1924 }
1925
1926 int afp_mapname(obj, ibuf, ibuflen, rbuf, rbuflen )
1927 AFPObj      *obj;
1928 char    *ibuf, *rbuf;
1929 int             ibuflen, *rbuflen;
1930 {
1931     struct passwd       *pw;
1932     struct group        *gr;
1933     int                 len, sfunc;
1934     u_int32_t           id;
1935
1936     ibuf++;
1937     sfunc = (unsigned char) *ibuf++;
1938     len = (unsigned char) *ibuf++;
1939     ibuf[ len ] = '\0';
1940
1941     if ( len != 0 ) {
1942         switch ( sfunc ) {
1943         case 3 :
1944             if (( pw = (struct passwd *)getpwnam( ibuf )) == NULL ) {
1945                 *rbuflen = 0;
1946                 return( AFPERR_NOITEM );
1947             }
1948             id = pw->pw_uid;
1949             break;
1950
1951         case 4 :
1952             if (( gr = (struct group *)getgrnam( ibuf )) == NULL ) {
1953                 *rbuflen = 0;
1954                 return( AFPERR_NOITEM );
1955             }
1956             id = gr->gr_gid;
1957             break;
1958         default :
1959             *rbuflen = 0;
1960             return( AFPERR_PARAM );
1961         }
1962     } else {
1963         id = 0;
1964     }
1965     id = htonl(id);
1966     memcpy( rbuf, &id, sizeof( id ));
1967     *rbuflen = sizeof( id );
1968     return( AFP_OK );
1969 }
1970
1971 /* variable DID support */
1972 int afp_closedir(obj, ibuf, ibuflen, rbuf, rbuflen )
1973 AFPObj      *obj;
1974 char    *ibuf, *rbuf;
1975 int             ibuflen, *rbuflen;
1976 {
1977 #if 0
1978     struct vol   *vol;
1979     struct dir   *dir;
1980     u_int16_t    vid;
1981     u_int32_t    did;
1982 #endif /* 0 */
1983
1984     *rbuflen = 0;
1985
1986     /* do nothing as dids are static for the life of the process. */
1987 #if 0
1988     ibuf += 2;
1989
1990     memcpy(&vid,  ibuf, sizeof( vid ));
1991     ibuf += sizeof( vid );
1992     if (( vol = getvolbyvid( vid )) == NULL ) {
1993         return( AFPERR_PARAM );
1994     }
1995
1996     memcpy( &did, ibuf, sizeof( did ));
1997     ibuf += sizeof( did );
1998     if (( dir = dirsearch( vol, did )) == NULL ) {
1999         return( AFPERR_PARAM );
2000     }
2001
2002     /* dir_remove -- deletedid */
2003 #endif /* 0 */
2004
2005     return AFP_OK;
2006 }
2007
2008 /* did creation gets done automatically */
2009 int afp_opendir(obj, ibuf, ibuflen, rbuf, rbuflen )
2010 AFPObj      *obj;
2011 char    *ibuf, *rbuf;
2012 int             ibuflen, *rbuflen;
2013 {
2014     struct vol          *vol;
2015     struct dir          *dir, *parentdir;
2016     struct stat         st;
2017     char                *path, *upath;
2018     u_int32_t           did;
2019     u_int16_t           vid;
2020 #ifdef FORCE_UIDGID
2021     uidgidset           *uidgid;
2022
2023     memset(&uidgid, 0, sizeof(uidgid));
2024 #endif /* FORCE_UIDGID */
2025
2026     *rbuflen = 0;
2027     ibuf += 2;
2028
2029     memcpy(&vid, ibuf, sizeof(vid));
2030     ibuf += sizeof( vid );
2031
2032     if (( vol = getvolbyvid( vid )) == NULL ) {
2033         return( AFPERR_PARAM );
2034     }
2035
2036     memcpy(&did, ibuf, sizeof(did));
2037     ibuf += sizeof(did);
2038
2039     if (( parentdir = dirsearch( vol, did )) == NULL ) {
2040         return( AFPERR_NOOBJ );
2041     }
2042
2043     if (( path = cname( vol, parentdir, &ibuf )) == NULL ) {
2044         switch( errno ) {
2045         case EACCES:
2046             return( AFPERR_ACCESS );
2047         default:
2048             return( AFPERR_NOOBJ );
2049         }
2050     }
2051
2052     /* see if we already have the directory. */
2053     upath = mtoupath(vol, path);
2054     if ( stat( upath, &st ) < 0 ) {
2055         return( AFPERR_NOOBJ );
2056     }
2057
2058     dir = parentdir->d_child;
2059     while (dir) {
2060         if (strdiacasecmp(dir->d_name, path) == 0) {
2061             memcpy(rbuf, &dir->d_did, sizeof(dir->d_did));
2062             *rbuflen = sizeof(dir->d_did);
2063             return AFP_OK;
2064         }
2065         dir = (dir == parentdir->d_child->d_prev) ? NULL : dir->d_next;
2066     }
2067
2068 #ifdef FORCE_UIDGID
2069     save_uidgid ( &uidgid );
2070     set_uidgid  ( vol );
2071 #endif /* FORCE_UIDGID */
2072
2073     /* we don't already have a did. add one in. */
2074     if ((dir = adddir(vol, parentdir, path, strlen(path),
2075                       upath, strlen(upath), &st)) == NULL) {
2076 #ifdef FORCE_UIDGID
2077         restore_uidgid ( &uidgid );
2078 #endif /* FORCE_UIDGID */
2079         return AFPERR_MISC;
2080     }
2081
2082     memcpy(rbuf, &dir->d_did, sizeof(dir->d_did));
2083     *rbuflen = sizeof(dir->d_did);
2084 #ifdef FORCE_UIDGID
2085     restore_uidgid ( &uidgid );
2086 #endif /* FORCE_UIDGID */
2087     return AFP_OK;
2088 }