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