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