]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/directory.c
desktop.c:
[netatalk.git] / etc / afpd / directory.c
1 /*
2  * $Id: directory.c,v 1.62 2003-02-16 12:35:04 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 (NULL == (upath = cnid_resolve(vol->v_db, &id, buffer, buflen))) {
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 dirfreename(struct dir *dir)
821 {
822     if (dir->d_u_name != dir->d_m_name) {
823         free(dir->d_u_name);
824     }
825     free(dir->d_m_name);
826 }
827
828 void dirfree( dir )
829 struct dir      *dir;
830 {
831     if (!dir || (dir == SENTINEL))
832         return;
833
834     if ( dir->d_left != SENTINEL ) {
835         dirfree( dir->d_left );
836     }
837     if ( dir->d_right != SENTINEL ) {
838         dirfree( dir->d_right );
839     }
840
841     if (dir != SENTINEL) {
842         dirfreename(dir);
843         free( dir );
844     }
845 }
846
847 /* --------------------------------------------
848  * most of the time mac name and unix name are the same 
849 */
850 struct dir *dirnew(const char *m_name, const char *u_name)
851 {
852     struct dir *dir;
853
854     dir = (struct dir *) calloc(1, sizeof( struct dir ));
855     if (!dir)
856         return NULL;
857
858     if ((dir->d_m_name = strdup(m_name)) == NULL) {
859         free(dir);
860         return NULL;
861     }
862
863     if (m_name == u_name) {
864         dir->d_u_name = dir->d_m_name;
865     }
866     else if ((dir->d_u_name = strdup(u_name)) == NULL) {
867         free(dir->d_m_name);
868         free(dir);
869         return NULL;
870     }
871
872     dir->d_left = dir->d_right = SENTINEL;
873     dir->d_next = dir->d_prev = dir;
874     return dir;
875 }
876
877 /* -------------------------------------------------- */
878 /* cname 
879  return
880  if it's a filename:
881       in extenddir:
882          compute unix name
883          stat the file or errno 
884       return 
885          filename
886          curdir: filename parent directory
887          
888  if it's a dirname:
889       not in the cache
890           in extenddir
891               compute unix name
892               stat the dir or errno
893           return
894               if chdir error 
895                  dirname 
896                  curdir: dir parent directory
897               sinon
898                  dirname: ""
899                  curdir: dir   
900       in the cache 
901           return
902               if chdir error
903                  dirname
904                  curdir: dir parent directory 
905               else
906                  dirname: ""
907                  curdir: dir   
908                  
909 */
910 struct path *
911 cname( vol, dir, cpath )
912 const struct vol        *vol;
913 struct dir      *dir;
914 char    **cpath;
915 {
916     struct dir             *cdir;
917     static char            path[ MAXPATHLEN + 1];
918     static struct path ret;
919
920     char                *data, *p;
921     int                 extend = 0;
922     int                 len;
923     u_int32_t   hint;
924     u_int16_t   len16;
925     int         size = 0;
926     char        sep;
927         
928     data = *cpath;
929     afp_errno = AFPERR_NOOBJ;
930     switch (ret.m_type = *data) { /* path type */
931     case 2:
932        data++;
933        len = (unsigned char) *data++;
934        size = 2;
935        sep = 0;
936        break;
937     case 3:
938        if (afp_version >= 30) {
939            data++;
940            memcpy(&hint, data, sizeof(hint));
941            hint = ntohl(hint);
942            data += sizeof(hint);
943            
944            memcpy(&len16, data, sizeof(len16));
945            len = ntohs(len16);
946            data += 2;
947            size = 7;
948            sep = 0; /* '/';*/
949            break;
950         }
951         /* else it's an error */
952     default:
953         afp_errno = AFPERR_PARAM;
954         return( NULL );
955     
956     }
957     *cpath += len + size;
958     *path = '\0';
959     ret.m_name = path;
960     ret.st_errno = 0;
961     ret.st_valid = 0;
962     for ( ;; ) {
963         if ( len == 0 ) {
964             if (movecwd( vol, dir ) < 0 ) {
965                 /* it's tricky:
966                    movecwd failed some of dir path are not there anymore.
967                    FIXME Is it true with other errors?
968                    so we remove dir from the cache 
969                 */
970                 if (dir->d_did == DIRDID_ROOT_PARENT)
971                     return NULL;
972                 if (afp_errno == AFPERR_ACCESS) {
973                     if ( movecwd( vol, dir->d_parent ) < 0 ) {
974                          return NULL;                   
975                     }
976                     ret.m_name = dir->d_m_name;
977                     ret.u_name = dir->d_u_name;
978                     return &ret;
979                 }
980
981                 dir_invalidate(vol, dir);
982                 return NULL;
983             }
984             if (*path == '\0') {
985                ret.u_name = ".";
986             }               
987             return &ret;
988         }
989
990         if (*data == sep ) {
991             data++;
992             len--;
993         }
994         while (*data == sep && len > 0 ) {
995             if ( dir->d_parent == NULL ) {
996                 return NULL;
997             }
998             dir = dir->d_parent;
999             data++;
1000             len--;
1001         }
1002
1003         /* would this be faster with strlen + strncpy? */
1004         p = path;
1005         while ( *data != sep && len > 0 ) {
1006             *p++ = *data++;
1007             len--;
1008         }
1009
1010         /* short cut bits by chopping off a trailing \0. this also
1011                   makes the traversal happy w/ filenames at the end of the
1012                   cname. */
1013         if (len == 1)
1014             len--;
1015
1016         *p = '\0';
1017
1018         if ( p != path ) { /* we got something */
1019             if ( !extend ) {
1020                 cdir = dir->d_child;
1021                 while (cdir) {
1022                     if ( strcmp( cdir->d_m_name, path ) == 0 ) {
1023                         break;
1024                     }
1025                     cdir = (cdir == dir->d_child->d_prev) ? NULL :
1026                            cdir->d_next;
1027                 }
1028                 if ( cdir == NULL ) {
1029                     ++extend;
1030                     /* if dir == curdir it always succeed,
1031                        even if curdir is deleted. 
1032                        it's not a pb because it will fail in extenddir
1033                     */
1034                     if ( movecwd( vol, dir ) < 0 ) {
1035                         /* dir is not valid anymore 
1036                            we delete dir from the cache and abort.
1037                         */
1038                         if ( dir->d_did == DIRDID_ROOT_PARENT)
1039                             return NULL;
1040                         if (afp_errno == AFPERR_ACCESS)
1041                             return NULL;
1042                         dir_invalidate(vol, dir);
1043                         return NULL;
1044                     }
1045                     cdir = extenddir( vol, dir, &ret );
1046                 }
1047
1048             } else {
1049                 cdir = extenddir( vol, dir, &ret );
1050             }
1051
1052             if ( cdir == NULL ) {
1053
1054                 if ( len > 0 ) {
1055                     return NULL;
1056                 }
1057
1058             } else {
1059                 dir = cdir;     
1060                 *path = '\0';
1061             }
1062         }
1063     }
1064 }
1065
1066 /*
1067  * Move curdir to dir, with a possible chdir()
1068  */
1069 int movecwd( vol, dir)
1070 const struct vol        *vol;
1071 struct dir      *dir;
1072 {
1073     char path[MAXPATHLEN + 1];
1074     struct dir  *d;
1075     char        *p, *u;
1076     int         n;
1077
1078     if ( dir == curdir ) {
1079         return( 0 );
1080     }
1081     if ( dir->d_did == DIRDID_ROOT_PARENT) {
1082         afp_errno = AFPERR_PARAM;
1083         return( -1 );
1084     }
1085
1086     p = path + sizeof(path) - 1;
1087     *p-- = '\0';
1088     *p = '.';
1089     for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) {
1090         u = d->d_u_name;
1091         n = strlen( u );
1092         if (p -n -1 < path) {
1093             afp_errno = AFPERR_PARAM;
1094             return -1;
1095         }
1096         *--p = '/';
1097         p -= n;
1098         strncpy( p, u, n );
1099     }
1100     if ( d != curdir ) {
1101         n = strlen( vol->v_path );
1102         if (p -n -1 < path) {
1103             afp_errno = AFPERR_PARAM;
1104             return -1;
1105         }
1106         *--p = '/';
1107         p -= n;
1108         strncpy( p, vol->v_path, n );
1109     }
1110     if ( chdir( p ) < 0 ) {
1111         switch (errno) {
1112         case EACCES:
1113         case EPERM:
1114             afp_errno = AFPERR_ACCESS;
1115             break;
1116         default:
1117             afp_errno = AFPERR_NOOBJ;
1118         
1119         }
1120         return( -1 );
1121     }
1122     curdir = dir;
1123     return( 0 );
1124 }
1125
1126 /*
1127  * We can't use unix file's perm to support Apple's inherited protection modes.
1128  * If we aren't the file's owner we can't change its perms when moving it and smb
1129  * nfs,... don't even try.
1130 */
1131 #define AFP_CHECK_ACCESS 
1132
1133 int check_access(char *path, int mode)
1134 {
1135 #ifdef AFP_CHECK_ACCESS
1136 struct maccess ma;
1137 char *p;
1138
1139     p = ad_dir(path);
1140     if (!p)
1141        return -1;
1142
1143     accessmode(p, &ma, curdir, NULL);
1144     if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1145         return -1;
1146     if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1147         return -1;
1148 #endif
1149     return 0;
1150 }
1151
1152 /* ------------------------------ 
1153    (".", curdir)
1154    (name, dir) with curdir:name == dir, from afp_enumerate
1155 */
1156
1157 int getdirparams(const struct vol *vol,
1158                  u_int16_t bitmap, struct path *s_path,
1159                  struct dir *dir, 
1160                  char *buf, int *buflen )
1161 {
1162     struct maccess      ma;
1163     struct adouble      ad;
1164     char                *data, *l_nameoff = NULL, *utf_nameoff = NULL;
1165     int                 bit = 0, isad = 0;
1166     u_int32_t           aint;
1167     u_int16_t           ashort;
1168     int                 ret;
1169     u_int32_t           utf8 = 0;
1170     struct stat *st = &s_path->st;
1171     char *upath = s_path->u_name;
1172     
1173     if ((bitmap & ((1 << DIRPBIT_ATTR)  |
1174                    (1 << DIRPBIT_CDATE) |
1175                    (1 << DIRPBIT_MDATE) |
1176                    (1 << DIRPBIT_BDATE) |
1177                    (1 << DIRPBIT_FINFO)))) {
1178         memset(&ad, 0, sizeof(ad));
1179         if ( !ad_open( upath, ADFLAGS_HF|ADFLAGS_DIR, O_RDONLY,
1180                   DIRBITS | 0777, &ad)) {
1181             isad = 1;
1182         }
1183     }
1184
1185     data = buf;
1186     while ( bitmap != 0 ) {
1187         while (( bitmap & 1 ) == 0 ) {
1188             bitmap = bitmap>>1;
1189             bit++;
1190         }
1191
1192         switch ( bit ) {
1193         case DIRPBIT_ATTR :
1194             if ( isad ) {
1195                 ad_getattr(&ad, &ashort);
1196             } else if (*upath == '.' && strcmp(upath, ".") &&
1197                        strcmp(upath, "..")) {
1198                 ashort = htons(ATTRBIT_INVISIBLE);
1199             } else
1200                 ashort = 0;
1201             ashort |= htons(ATTRBIT_SHARED);
1202             memcpy( data, &ashort, sizeof( ashort ));
1203             data += sizeof( ashort );
1204             break;
1205
1206         case DIRPBIT_PDID :
1207             if ( dir->d_did == DIRDID_ROOT) {
1208                 aint = DIRDID_ROOT_PARENT;
1209             } else if (dir->d_did == DIRDID_ROOT_PARENT) {
1210                 aint = 0;
1211             } else {
1212                 aint = dir->d_parent->d_did;
1213             }
1214             memcpy( data, &aint, sizeof( aint ));
1215             data += sizeof( aint );
1216             break;
1217
1218         case DIRPBIT_CDATE :
1219             if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
1220                 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1221             memcpy( data, &aint, sizeof( aint ));
1222             data += sizeof( aint );
1223             break;
1224
1225         case DIRPBIT_MDATE :
1226             aint = AD_DATE_FROM_UNIX(st->st_mtime);
1227             memcpy( data, &aint, sizeof( aint ));
1228             data += sizeof( aint );
1229             break;
1230
1231         case DIRPBIT_BDATE :
1232             if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
1233                 aint = AD_DATE_START;
1234             memcpy( data, &aint, sizeof( aint ));
1235             data += sizeof( aint );
1236             break;
1237
1238         case DIRPBIT_FINFO :
1239             if ( isad ) {
1240                 memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
1241             } else { /* no appledouble */
1242                 memset( data, 0, 32 );
1243                 /* set default view -- this also gets done in ad_open() */
1244                 ashort = htons(FINDERINFO_CLOSEDVIEW);
1245                 memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
1246
1247                 /* dot files are by default invisible */
1248                 if (*upath == '.' && strcmp(upath, ".") &&
1249                         strcmp(upath, "..")) {
1250                     ashort = htons(FINDERINFO_INVISIBLE);
1251                     memcpy(data + FINDERINFO_FRFLAGOFF,
1252                            &ashort, sizeof(ashort));
1253                 }
1254             }
1255             data += 32;
1256             break;
1257
1258         case DIRPBIT_LNAME :
1259             if (dir->d_m_name) /* root of parent can have a null name */
1260                 l_nameoff = data;
1261             else
1262                 memset(data, 0, sizeof(u_int16_t));
1263             data += sizeof( u_int16_t );
1264             break;
1265
1266         case DIRPBIT_SNAME :
1267             memset(data, 0, sizeof(u_int16_t));
1268             data += sizeof( u_int16_t );
1269             break;
1270
1271         case DIRPBIT_DID :
1272             memcpy( data, &dir->d_did, sizeof( aint ));
1273             data += sizeof( aint );
1274             break;
1275
1276         case DIRPBIT_OFFCNT :
1277             ashort = 0;
1278             /* this needs to handle current directory access rights */
1279             if (st->st_ctime == dir->ctime) {
1280                ashort = dir->offcnt;
1281             }
1282             else if ((ret = for_each_dirent(vol, upath, NULL,NULL)) >= 0) {
1283                 ashort = ret;
1284                 dir->offcnt = ashort;
1285                 dir->ctime = st->st_ctime;
1286             }
1287             ashort = htons( ashort );
1288             memcpy( data, &ashort, sizeof( ashort ));
1289             data += sizeof( ashort );
1290             break;
1291
1292         case DIRPBIT_UID :
1293             aint = htonl(st->st_uid);
1294             memcpy( data, &aint, sizeof( aint ));
1295             data += sizeof( aint );
1296             break;
1297
1298         case DIRPBIT_GID :
1299             aint = htonl(st->st_gid);
1300             memcpy( data, &aint, sizeof( aint ));
1301             data += sizeof( aint );
1302             break;
1303
1304         case DIRPBIT_ACCESS :
1305             accessmode( upath, &ma, dir , st);
1306
1307             *data++ = ma.ma_user;
1308             *data++ = ma.ma_world;
1309             *data++ = ma.ma_group;
1310             *data++ = ma.ma_owner;
1311             break;
1312
1313             /* Client has requested the ProDOS information block.
1314                Just pass back the same basic block for all
1315                directories. <shirsch@ibm.net> */
1316         case DIRPBIT_PDINFO :                     
1317             if (afp_version >= 30) { /* UTF8 name */
1318                 utf8 = kTextEncodingUTF8;
1319                 if (dir->d_m_name) /* root of parent can have a null name */
1320                     utf_nameoff = data;
1321                 else
1322                     memset(data, 0, sizeof(u_int16_t));
1323                 data += sizeof( u_int16_t );
1324                 aint = 0;
1325                 memcpy(data, &aint, sizeof( aint ));
1326                 data += sizeof( aint );
1327             }
1328             else { /* ProDOS Info Block */
1329                 *data++ = 0x0f;
1330                 *data++ = 0;
1331                 ashort = htons( 0x0200 );
1332                 memcpy( data, &ashort, sizeof( ashort ));
1333                 data += sizeof( ashort );
1334                 memset( data, 0, sizeof( ashort ));
1335                 data += sizeof( ashort );
1336             }
1337             break;
1338
1339         default :
1340             if ( isad ) {
1341                 ad_close( &ad, ADFLAGS_HF );
1342             }
1343             return( AFPERR_BITMAP );
1344         }
1345         bitmap = bitmap>>1;
1346         bit++;
1347     }
1348     if ( l_nameoff ) {
1349         ashort = htons( data - buf );
1350         memcpy( l_nameoff, &ashort, sizeof( ashort ));
1351         data = set_name(data, dir->d_m_name, 0);
1352     }
1353     if ( utf_nameoff ) {
1354         ashort = htons( data - buf );
1355         memcpy( utf_nameoff, &ashort, sizeof( ashort ));
1356         data = set_name(data, dir->d_m_name, utf8);
1357     }
1358     if ( isad ) {
1359         ad_close( &ad, ADFLAGS_HF );
1360     }
1361     *buflen = data - buf;
1362     return( AFP_OK );
1363 }
1364
1365 /* ----------------------------- */
1366 int afp_setdirparams(obj, ibuf, ibuflen, rbuf, rbuflen )
1367 AFPObj      *obj;
1368 char    *ibuf, *rbuf;
1369 int             ibuflen, *rbuflen;
1370 {
1371     struct vol  *vol;
1372     struct dir  *dir;
1373     struct path *path;
1374     u_int16_t   vid, bitmap;
1375     u_int32_t   did;
1376     int         rc;
1377
1378     *rbuflen = 0;
1379     ibuf += 2;
1380     memcpy( &vid, ibuf, sizeof( vid ));
1381     ibuf += sizeof( vid );
1382
1383     if (NULL == ( vol = getvolbyvid( vid )) ) {
1384         return( AFPERR_PARAM );
1385     }
1386
1387     if (vol->v_flags & AFPVOL_RO)
1388         return AFPERR_VLOCK;
1389
1390     memcpy( &did, ibuf, sizeof( did ));
1391     ibuf += sizeof( int );
1392
1393     if (NULL == ( dir = dirlookup( vol, did )) ) {
1394         return afp_errno;
1395     }
1396
1397     memcpy( &bitmap, ibuf, sizeof( bitmap ));
1398     bitmap = ntohs( bitmap );
1399     ibuf += sizeof( bitmap );
1400
1401     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1402         return afp_errno;
1403     }
1404
1405     /* FIXME access error or not a file */
1406     if ( *path->m_name != '\0' ) {
1407         return (path_isadir( path))? afp_errno:AFPERR_BADTYPE ;
1408     }
1409
1410     /*
1411      * If ibuf is odd, make it even.
1412      */
1413     if ((u_long)ibuf & 1 ) {
1414         ibuf++;
1415     }
1416
1417     if (AFP_OK == ( rc = setdirparams(vol, path, bitmap, ibuf )) ) {
1418         setvoltime(obj, vol );
1419     }
1420     return( rc );
1421 }
1422
1423 /*
1424  * cf AFP3.0.pdf page 244 for change_mdate and change_parent_mdate logic  
1425  *
1426  * assume path == '\0' eg. it's a directory in canonical form
1427 */
1428
1429 struct path Cur_Path = {
1430     0,
1431     "",  /* mac name */
1432     ".", /* unix name */
1433     0,  /* stat is not set */
1434     0,  /* */
1435 };
1436
1437 int setdirparams(const struct vol *vol, 
1438                  struct path *path, u_int16_t bitmap, char *buf )
1439 {
1440     struct maccess      ma;
1441     struct adouble      ad;
1442     struct utimbuf      ut;
1443     struct timeval      tv;
1444
1445     char                *upath;
1446     int                 bit = 0, aint, isad = 1;
1447     u_int16_t           ashort, bshort;
1448     int                 err = AFP_OK;
1449     int                 change_mdate = 0;
1450     int                 change_parent_mdate = 0;
1451     int                 newdate = 0;
1452
1453     upath = path->u_name;
1454     memset(&ad, 0, sizeof(ad));
1455
1456     if (ad_open( upath, vol_noadouble(vol)|ADFLAGS_HF|ADFLAGS_DIR,
1457                  O_RDWR|O_CREAT, 0666, &ad) < 0) {
1458         /*
1459          * Check to see what we're trying to set.  If it's anything
1460          * but ACCESS, UID, or GID, give an error.  If it's any of those
1461          * three, we don't need the ad to be open, so just continue.
1462          *
1463          * note: we also don't need to worry about mdate. also, be quiet
1464          *       if we're using the noadouble option.
1465          */
1466         if (!vol_noadouble(vol) && (bitmap &
1467                                     ~((1<<DIRPBIT_ACCESS)|(1<<DIRPBIT_UID)|(1<<DIRPBIT_GID)|
1468                                       (1<<DIRPBIT_MDATE)|(1<<DIRPBIT_PDINFO)))) {
1469             return AFPERR_ACCESS;
1470         }
1471
1472         isad = 0;
1473     } else {
1474         /*
1475          * Check to see if a create was necessary. If it was, we'll want
1476          * to set our name, etc.
1477          */
1478         if ( ad_get_HF_flags( &ad ) & O_CREAT ) {
1479             ad_setentrylen( &ad, ADEID_NAME, strlen( curdir->d_m_name ));
1480             memcpy( ad_entry( &ad, ADEID_NAME ), curdir->d_m_name,
1481                     ad_getentrylen( &ad, ADEID_NAME ));
1482         }
1483     }
1484
1485     while ( bitmap != 0 ) {
1486         while (( bitmap & 1 ) == 0 ) {
1487             bitmap = bitmap>>1;
1488             bit++;
1489         }
1490
1491         switch( bit ) {
1492         case DIRPBIT_ATTR :
1493             change_mdate = 1;
1494             if (isad) {
1495                 memcpy( &ashort, buf, sizeof( ashort ));
1496                 ad_getattr(&ad, &bshort);
1497                 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1498                     bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1499                 } else {
1500                     bshort &= ~ashort;
1501                 }
1502                 ad_setattr(&ad, bshort);
1503                 if ((ashort & htons(ATTRBIT_INVISIBLE)))
1504                    change_parent_mdate = 1;
1505             }
1506             buf += sizeof( ashort );
1507             break;
1508
1509         case DIRPBIT_CDATE :
1510             change_mdate = 1;
1511             if (isad) {
1512                 memcpy(&aint, buf, sizeof(aint));
1513                 ad_setdate(&ad, AD_DATE_CREATE, aint);
1514             }
1515             buf += sizeof( aint );
1516             break;
1517
1518         case DIRPBIT_MDATE :
1519             memcpy(&newdate, buf, sizeof(newdate));
1520             buf += sizeof( newdate );
1521             break;
1522
1523         case DIRPBIT_BDATE :
1524             change_mdate = 1;
1525             if (isad) {
1526                 memcpy(&aint, buf, sizeof(aint));
1527                 ad_setdate(&ad, AD_DATE_BACKUP, aint);
1528             }
1529             buf += sizeof( aint );
1530             break;
1531
1532         case DIRPBIT_FINFO :
1533             change_mdate = 1;
1534             /*
1535              * Alright, we admit it, this is *really* sick!
1536              * The 4 bytes that we don't copy, when we're dealing
1537              * with the root of a volume, are the directory's
1538              * location information. This eliminates that annoying
1539              * behavior one sees when mounting above another mount
1540              * point.
1541              */
1542             if (isad) {
1543                 if (  curdir->d_did == DIRDID_ROOT ) {
1544                     memcpy( ad_entry( &ad, ADEID_FINDERI ), buf, 10 );
1545                     memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, buf + 14, 18 );
1546                 } else {
1547                     memcpy( ad_entry( &ad, ADEID_FINDERI ), buf, 32 );
1548                 }
1549             }
1550             buf += 32;
1551             break;
1552
1553         case DIRPBIT_UID :      /* What kind of loser mounts as root? */
1554             change_parent_mdate = 1;
1555             memcpy( &aint, buf, sizeof(aint));
1556             buf += sizeof( aint );
1557             if ( (curdir->d_did == DIRDID_ROOT) &&
1558                     (setdeskowner( ntohl(aint), -1 ) < 0)) {
1559                 switch ( errno ) {
1560                 case EPERM :
1561                 case EACCES :
1562                     err = AFPERR_ACCESS;
1563                     goto setdirparam_done;
1564                     break;
1565                 case EROFS :
1566                     err = AFPERR_VLOCK;
1567                     goto setdirparam_done;
1568                     break;
1569                 default :
1570                     LOG(log_error, logtype_afpd, "setdirparam: setdeskowner: %s",
1571                         strerror(errno) );
1572                     if (!isad) {
1573                         err = AFPERR_PARAM;
1574                         goto setdirparam_done;
1575                     }
1576                     break;
1577                 }
1578             }
1579             if ( setdirowner( ntohl(aint), -1, vol_noadouble(vol) ) < 0 ) {
1580                 switch ( errno ) {
1581                 case EPERM :
1582                 case EACCES :
1583                     err = AFPERR_ACCESS;
1584                     goto setdirparam_done;
1585                     break;
1586                 case EROFS :
1587                     err = AFPERR_VLOCK;
1588                     goto setdirparam_done;
1589                     break;
1590                 default :
1591                     LOG(log_error, logtype_afpd, "setdirparam: setdirowner: %s",
1592                         strerror(errno) );
1593                     break;
1594                 }
1595             }
1596             break;
1597         case DIRPBIT_GID :
1598             change_parent_mdate = 1;
1599             memcpy( &aint, buf, sizeof( aint ));
1600             buf += sizeof( aint );
1601             if (curdir->d_did == DIRDID_ROOT)
1602                 setdeskowner( -1, ntohl(aint) );
1603
1604 #if 0       /* don't error if we can't set the desktop owner. */
1605             switch ( errno ) {
1606             case EPERM :
1607             case EACCES :
1608                 err = AFPERR_ACCESS;
1609                 goto setdirparam_done;
1610                 break;
1611             case EROFS :
1612                 err = AFPERR_VLOCK;
1613                 goto setdirparam_done;
1614                 break;
1615             default :
1616                 LOG(log_error, logtype_afpd, "setdirparam: setdeskowner: %m" );
1617                 if (!isad) {
1618                     err = AFPERR_PARAM;
1619                     goto setdirparam_done;
1620                 }
1621                 break;
1622             }
1623 #endif /* 0 */
1624
1625             if ( setdirowner( -1, ntohl(aint), vol_noadouble(vol) ) < 0 ) {
1626                 switch ( errno ) {
1627                 case EPERM :
1628                 case EACCES :
1629                     err = AFPERR_ACCESS;
1630                     goto setdirparam_done;
1631                     break;
1632                 case EROFS :
1633                     err = AFPERR_VLOCK;
1634                     goto setdirparam_done;
1635                     break;
1636                 default :
1637                     LOG(log_error, logtype_afpd, "setdirparam: setdirowner: %s",
1638                         strerror(errno) );
1639                     break;
1640                 }
1641             }
1642             break;
1643
1644         case DIRPBIT_ACCESS :
1645             change_mdate = 1;
1646             change_parent_mdate = 1;
1647             ma.ma_user = *buf++;
1648             ma.ma_world = *buf++;
1649             ma.ma_group = *buf++;
1650             ma.ma_owner = *buf++;
1651
1652             if (curdir->d_did == DIRDID_ROOT)
1653                 setdeskmode(mtoumode( &ma ));
1654 #if 0 /* don't error if we can't set the desktop mode */
1655             switch ( errno ) {
1656             case EPERM :
1657             case EACCES :
1658                 err = AFPERR_ACCESS;
1659                 goto setdirparam_done;
1660             case EROFS :
1661                 err = AFPERR_VLOCK;
1662                 goto setdirparam_done;
1663             default :
1664                 LOG(log_error, logtype_afpd, "setdirparam: setdeskmode: %s",
1665                     strerror(errno) );
1666                 break;
1667                 err = AFPERR_PARAM;
1668                 goto setdirparam_done;
1669             }
1670 #endif /* 0 */
1671
1672             if ( setdirmode( mtoumode( &ma ), vol_noadouble(vol),
1673                          (vol->v_flags & AFPVOL_DROPBOX)) < 0 ) {
1674                 switch ( errno ) {
1675                 case EPERM :
1676                 case EACCES :
1677                     err = AFPERR_ACCESS;
1678                     goto setdirparam_done;
1679                 case EROFS :
1680                     err = AFPERR_VLOCK;
1681                     goto setdirparam_done;
1682                 default :
1683                     LOG(log_error, logtype_afpd, "setdirparam: setdirmode: %s",
1684                         strerror(errno) );
1685                     err = AFPERR_PARAM;
1686                     goto setdirparam_done;
1687                 }
1688             }
1689             break;
1690
1691         /* Ignore what the client thinks we should do to the
1692            ProDOS information block.  Skip over the data and
1693            report nothing amiss. <shirsch@ibm.net> */
1694         case DIRPBIT_PDINFO :
1695             if (afp_version < 30) {
1696                 buf += 6;
1697                 break;
1698             }
1699         default :
1700             err = AFPERR_BITMAP;
1701             goto setdirparam_done;
1702             break;
1703         }
1704
1705         bitmap = bitmap>>1;
1706         bit++;
1707     }
1708
1709 setdirparam_done:
1710     if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1711        newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1712     }
1713     if (newdate) {
1714        if (isad)
1715           ad_setdate(&ad, AD_DATE_MODIFY, newdate);
1716        ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1717        utime(upath, &ut);
1718     }
1719
1720     if ( isad ) {
1721         ad_flush( &ad, ADFLAGS_HF );
1722         ad_close( &ad, ADFLAGS_HF );
1723     }
1724
1725     if (change_parent_mdate && curdir->d_did != DIRDID_ROOT
1726             && gettimeofday(&tv, NULL) == 0) {
1727        if (!movecwd(vol, curdir->d_parent)) {
1728            newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1729            bitmap = 1<<DIRPBIT_MDATE;
1730            setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1731            /* should we reset curdir ?*/
1732        }
1733     }
1734
1735     return err;
1736 }
1737
1738 int afp_createdir(obj, ibuf, ibuflen, rbuf, rbuflen )
1739 AFPObj      *obj;
1740 char    *ibuf, *rbuf;
1741 int             ibuflen, *rbuflen;
1742 {
1743     struct adouble      ad;
1744     struct vol          *vol;
1745     struct dir          *dir;
1746     char                *upath;
1747     struct path         *s_path;
1748     u_int32_t           did;
1749     u_int16_t           vid;
1750     int                 err;
1751     
1752     *rbuflen = 0;
1753     ibuf += 2;
1754
1755     memcpy( &vid, ibuf, sizeof( vid ));
1756     ibuf += sizeof( vid );
1757     if (NULL == ( vol = getvolbyvid( vid )) ) {
1758         return( AFPERR_PARAM );
1759     }
1760
1761     if (vol->v_flags & AFPVOL_RO)
1762         return AFPERR_VLOCK;
1763
1764     memcpy( &did, ibuf, sizeof( did ));
1765     ibuf += sizeof( did );
1766     if (NULL == ( dir = dirlookup( vol, did )) ) {
1767         return afp_errno; /* was AFPERR_NOOBJ */
1768     }
1769
1770     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1771         return afp_errno;
1772     }
1773     /* cname was able to move curdir to it! */
1774     if (*s_path->m_name == '\0')
1775         return AFPERR_EXIST;
1776
1777     upath = s_path->u_name;
1778     if (0 != (err = check_name(vol, upath))) {
1779        return err;
1780     }
1781
1782     if (AFP_OK != (err = netatalk_mkdir( upath))) {
1783         return err;
1784     }
1785
1786     if (of_stat(s_path) < 0) {
1787         return AFPERR_MISC;
1788     }
1789     curdir->offcnt++;
1790     if ((dir = adddir( vol, curdir, s_path)) == NULL) {
1791         return AFPERR_MISC;
1792     }
1793
1794     if ( movecwd( vol, dir ) < 0 ) {
1795         return( AFPERR_PARAM );
1796     }
1797
1798     memset(&ad, 0, sizeof(ad));
1799     if (ad_open( ".", vol_noadouble(vol)|ADFLAGS_HF|ADFLAGS_DIR,
1800                  O_RDWR|O_CREAT, 0666, &ad ) < 0)  {
1801         if (vol_noadouble(vol))
1802             goto createdir_done;
1803         return( AFPERR_ACCESS );
1804     }
1805
1806     ad_setentrylen( &ad, ADEID_NAME, strlen( s_path->m_name ));
1807     memcpy( ad_entry( &ad, ADEID_NAME ), s_path->m_name,
1808             ad_getentrylen( &ad, ADEID_NAME ));
1809     ad_flush( &ad, ADFLAGS_HF );
1810     ad_close( &ad, ADFLAGS_HF );
1811
1812 createdir_done:
1813     memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
1814     *rbuflen = sizeof( u_int32_t );
1815     setvoltime(obj, vol );
1816     return( AFP_OK );
1817 }
1818
1819 /*
1820  * dst       new unix filename (not a pathname)
1821  * newname   new mac name
1822  * newparent curdir
1823  *
1824 */
1825 int renamedir(src, dst, dir, newparent, newname, noadouble)
1826 char    *src, *dst, *newname;
1827 struct dir      *dir, *newparent;
1828 const int noadouble;
1829 {
1830     struct adouble      ad;
1831     struct dir          *parent;
1832     char                *buf;
1833     int                 len, err;
1834         
1835     /* existence check moved to afp_moveandrename */
1836     if ( unix_rename( src, dst ) < 0 ) {
1837         switch ( errno ) {
1838         case ENOENT :
1839             return( AFPERR_NOOBJ );
1840         case EACCES :
1841             return( AFPERR_ACCESS );
1842         case EROFS:
1843             return AFPERR_VLOCK;
1844         case EINVAL:
1845             /* tried to move directory into a subdirectory of itself */
1846             return AFPERR_CANTMOVE;
1847         case EXDEV:
1848             /* this needs to copy and delete. bleah. that means we have
1849              * to deal with entire directory hierarchies. */
1850             if ((err = copydir(src, dst, noadouble)) < 0) {
1851                 deletedir(dst);
1852                 return err;
1853             }
1854             if ((err = deletedir(src)) < 0)
1855                 return err;
1856             break;
1857         default :
1858             return( AFPERR_PARAM );
1859         }
1860     }
1861
1862     memset(&ad, 0, sizeof(ad));
1863     len = strlen( newname );
1864     /* rename() succeeded so we need to update our tree even if we can't open
1865      * .Parent
1866     */
1867     if ( !ad_open( dst, ADFLAGS_HF|ADFLAGS_DIR, O_RDWR, 0, &ad)) {
1868         ad_setentrylen( &ad, ADEID_NAME, len );
1869         memcpy( ad_entry( &ad, ADEID_NAME ), newname, len );
1870         ad_flush( &ad, ADFLAGS_HF );
1871         ad_close( &ad, ADFLAGS_HF );
1872     }
1873     
1874     if (dir->d_m_name == dir->d_u_name)
1875         dir->d_u_name = NULL;
1876
1877     if ((buf = (char *) realloc( dir->d_m_name, len + 1 )) == NULL ) {
1878         LOG(log_error, logtype_afpd, "renamedir: realloc mac name: %s", strerror(errno) );
1879         /* FIXME : fatal ? */
1880         return AFPERR_MISC;
1881     }
1882     dir->d_m_name = buf;
1883     strcpy( dir->d_m_name, newname );
1884
1885     if (newname == dst) {
1886         free(dir->d_u_name);
1887         dir->d_u_name = dir->d_m_name;
1888     }
1889     else {
1890         if ((buf = (char *) realloc( dir->d_u_name, strlen(dst) + 1 )) == NULL ) {
1891             LOG(log_error, logtype_afpd, "renamedir: realloc unix name: %s", strerror(errno) );
1892             return AFPERR_MISC;
1893         }
1894         dir->d_u_name = buf;
1895         strcpy( dir->d_u_name, dst );
1896     }
1897
1898     if (( parent = dir->d_parent ) == NULL ) {
1899         return( AFP_OK );
1900     }
1901     if ( parent == newparent ) {
1902         return( AFP_OK );
1903     }
1904
1905     /* detach from old parent and add to new one. */
1906     dirchildremove(parent, dir);
1907     dir->d_parent = newparent;
1908     dirchildadd(newparent, dir);
1909     return( AFP_OK );
1910 }
1911
1912 #define DOT_APPLEDOUBLE_LEN 13
1913 /* delete an empty directory */
1914 int deletecurdir( vol, path, pathlen )
1915 const struct vol        *vol;
1916 char *path;
1917 int pathlen;
1918 {
1919     struct dirent *de;
1920     struct stat st;
1921     struct dir  *fdir;
1922     DIR *dp;
1923     struct adouble      ad;
1924     u_int16_t           ashort;
1925     int err;
1926
1927     if ( curdir->d_parent == NULL ) {
1928         return( AFPERR_ACCESS );
1929     }
1930
1931     fdir = curdir;
1932
1933     memset(&ad, 0, sizeof(ad));
1934     if ( ad_open( ".", ADFLAGS_HF|ADFLAGS_DIR, O_RDONLY,
1935                   DIRBITS | 0777, &ad) == 0 ) {
1936
1937         ad_getattr(&ad, &ashort);
1938         ad_close( &ad, ADFLAGS_HF );
1939         if ((ashort & htons(ATTRBIT_NODELETE))) {
1940             return  AFPERR_OLOCK;
1941         }
1942     }
1943
1944     /* delete stray .AppleDouble files. this happens to get .Parent files
1945        as well. */
1946     if ((dp = opendir(".AppleDouble"))) {
1947         strcpy(path, ".AppleDouble/");
1948         while ((de = readdir(dp))) {
1949             /* skip this and previous directory */
1950             if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
1951                 continue;
1952
1953             /* bail if the file exists in the current directory.
1954              * note: this will not fail with dangling symlinks */
1955             if (stat(de->d_name, &st) == 0) {
1956                 closedir(dp);
1957                 return AFPERR_DIRNEMPT;
1958             }
1959
1960             strcpy(path + DOT_APPLEDOUBLE_LEN, de->d_name);
1961             if ((err = netatalk_unlink(path))) {
1962                 closedir(dp);
1963                 return err;
1964             }
1965         }
1966         closedir(dp);
1967     }
1968
1969     if ( (err = netatalk_rmdir( ".AppleDouble" ))  ) {
1970         return err;
1971     }
1972
1973     /* now get rid of dangling symlinks */
1974     if ((dp = opendir("."))) {
1975         while ((de = readdir(dp))) {
1976             /* skip this and previous directory */
1977             if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
1978                 continue;
1979
1980             /* bail if it's not a symlink */
1981             if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
1982                 closedir(dp);
1983                 return AFPERR_DIRNEMPT;
1984             }
1985
1986             if ((err = netatalk_unlink(de->d_name))) {
1987                 closedir(dp);
1988                 return err;
1989             }
1990         }
1991     }
1992
1993     if ( movecwd( vol, curdir->d_parent ) < 0 ) {
1994         err = afp_errno;
1995         goto delete_done;
1996     }
1997
1998     if ( !(err = netatalk_rmdir(fdir->d_u_name))) {
1999         dirchildremove(curdir, fdir);
2000 #ifdef CNID_DB
2001         cnid_delete(vol->v_db, fdir->d_did);
2002 #endif /* CNID_DB */
2003         dir_remove( vol, fdir );
2004         err = AFP_OK;
2005     }
2006 delete_done:
2007     if (dp) {
2008         /* inode is used as key for cnid.
2009          * Close the descriptor only after cnid_delete
2010          * has been called. 
2011         */
2012         closedir(dp);
2013     }
2014     return err;
2015 }
2016
2017 int afp_mapid(obj, ibuf, ibuflen, rbuf, rbuflen )
2018 AFPObj      *obj;
2019 char    *ibuf, *rbuf;
2020 int             ibuflen, *rbuflen;
2021 {
2022     struct passwd       *pw;
2023     struct group        *gr;
2024     char                *name;
2025     u_int32_t           id;
2026     int                 len, sfunc;
2027     int         utf8 = 0;
2028     
2029     ibuf++;
2030     sfunc = (unsigned char) *ibuf++;
2031     memcpy( &id, ibuf, sizeof( id ));
2032
2033     id = ntohl(id);
2034     *rbuflen = 0;
2035
2036     if ( id != 0 ) {
2037         switch ( sfunc ) {
2038         case 1 :
2039         case 3 :/* unicode */
2040             if (( pw = getpwuid( id )) == NULL ) {
2041                 return( AFPERR_NOITEM );
2042             }
2043             name = pw->pw_name;
2044             break;
2045
2046         case 2 :
2047         case 4 : /* unicode */
2048             if (( gr = (struct group *)getgrgid( id )) == NULL ) {
2049                 return( AFPERR_NOITEM );
2050             }
2051             name = gr->gr_name;
2052             break;
2053
2054         default :
2055             return( AFPERR_PARAM );
2056         }
2057         switch ( sfunc ) {
2058         case 3:
2059         case 4:
2060             if (afp_version < 30) {
2061                 return( AFPERR_PARAM );
2062             }
2063             utf8 = 1;
2064             /* map to unicode */
2065             break;            
2066         }
2067         len = strlen( name );
2068
2069     } else {
2070         len = 0;
2071         name = NULL;
2072     }
2073     if (utf8) {
2074         u_int16_t tp = htons(len);
2075         memcpy(rbuf, &tp, sizeof(tp));
2076         rbuf += sizeof(tp);
2077         *rbuflen += 2;
2078     }
2079     else {
2080         *rbuf++ = len;
2081         *rbuflen += 1;
2082     }
2083     if ( len > 0 ) {
2084         memcpy( rbuf, name, len );
2085     }
2086     *rbuflen += len;
2087     return( AFP_OK );
2088 }
2089
2090 int afp_mapname(obj, ibuf, ibuflen, rbuf, rbuflen )
2091 AFPObj      *obj;
2092 char    *ibuf, *rbuf;
2093 int             ibuflen, *rbuflen;
2094 {
2095     struct passwd       *pw;
2096     struct group        *gr;
2097     int             len, sfunc;
2098     u_int32_t       id;
2099     u_int16_t       ulen;
2100
2101     ibuf++;
2102     sfunc = (unsigned char) *ibuf++;
2103     switch ( sfunc ) {
2104     case 1 : 
2105     case 2 : /* unicode */
2106         memcpy(&ulen, ibuf, sizeof(ulen));
2107         len = ntohs(ulen);
2108         ibuf += 2;
2109         break;
2110     case 3 :
2111     case 4 :
2112         len = (unsigned char) *ibuf++;
2113         break;
2114     default :
2115         *rbuflen = 0;
2116         return( AFPERR_PARAM );
2117     }
2118
2119     ibuf[ len ] = '\0';
2120
2121     if ( len != 0 ) {
2122         switch ( sfunc ) {
2123         case 1 : /* unicode */
2124         case 3 :
2125             if (( pw = (struct passwd *)getpwnam( ibuf )) == NULL ) {
2126                 *rbuflen = 0;
2127                 return( AFPERR_NOITEM );
2128             }
2129             id = pw->pw_uid;
2130             break;
2131
2132         case 2 : /* unicode */
2133         case 4 :
2134             if (( gr = (struct group *)getgrnam( ibuf )) == NULL ) {
2135                 *rbuflen = 0;
2136                 return( AFPERR_NOITEM );
2137             }
2138             id = gr->gr_gid;
2139             break;
2140         }
2141     } else {
2142         id = 0;
2143     }
2144     id = htonl(id);
2145     memcpy( rbuf, &id, sizeof( id ));
2146     *rbuflen = sizeof( id );
2147     return( AFP_OK );
2148 }
2149
2150 /* ------------------------------------
2151   variable DID support 
2152 */
2153 int afp_closedir(obj, ibuf, ibuflen, rbuf, rbuflen )
2154 AFPObj      *obj;
2155 char    *ibuf, *rbuf;
2156 int             ibuflen, *rbuflen;
2157 {
2158 #if 0
2159     struct vol   *vol;
2160     struct dir   *dir;
2161     u_int16_t    vid;
2162     u_int32_t    did;
2163 #endif /* 0 */
2164
2165     *rbuflen = 0;
2166
2167     /* do nothing as dids are static for the life of the process. */
2168 #if 0
2169     ibuf += 2;
2170
2171     memcpy(&vid,  ibuf, sizeof( vid ));
2172     ibuf += sizeof( vid );
2173     if (( vol = getvolbyvid( vid )) == NULL ) {
2174         return( AFPERR_PARAM );
2175     }
2176
2177     memcpy( &did, ibuf, sizeof( did ));
2178     ibuf += sizeof( did );
2179     if (( dir = dirlookup( vol, did )) == NULL ) {
2180         return( AFPERR_PARAM );
2181     }
2182
2183     /* dir_remove -- deletedid */
2184 #endif /* 0 */
2185
2186     return AFP_OK;
2187 }
2188
2189 /* did creation gets done automatically 
2190  * there's a pb again with case but move it to cname
2191 */
2192 int afp_opendir(obj, ibuf, ibuflen, rbuf, rbuflen )
2193 AFPObj      *obj;
2194 char    *ibuf, *rbuf;
2195 int             ibuflen, *rbuflen;
2196 {
2197     struct vol          *vol;
2198     struct dir          *parentdir;
2199     struct path         *path;
2200     u_int32_t           did;
2201     u_int16_t           vid;
2202
2203     *rbuflen = 0;
2204     ibuf += 2;
2205
2206     memcpy(&vid, ibuf, sizeof(vid));
2207     ibuf += sizeof( vid );
2208
2209     if (NULL == ( vol = getvolbyvid( vid )) ) {
2210         return( AFPERR_PARAM );
2211     }
2212
2213     memcpy(&did, ibuf, sizeof(did));
2214     ibuf += sizeof(did);
2215
2216     if (NULL == ( parentdir = dirlookup( vol, did )) ) {
2217         return afp_errno;
2218     }
2219
2220     if (NULL == ( path = cname( vol, parentdir, &ibuf )) ) {
2221         return afp_errno;
2222     }
2223
2224     if ( *path->m_name != '\0' ) {
2225         return (path_isadir(path))? afp_errno:AFPERR_BADTYPE ;
2226     }
2227
2228     if ( !path->st_valid && of_stat(path ) < 0 ) {
2229         return( AFPERR_NOOBJ );
2230     }
2231     if ( path->st_errno ) {
2232         return( AFPERR_NOOBJ );
2233     }
2234
2235     memcpy(rbuf, &curdir->d_did, sizeof(curdir->d_did));
2236     *rbuflen = sizeof(curdir->d_did);
2237     return AFP_OK;
2238 }