]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/directory.c
dbb0e65a06a1d0ef302e41a1a35a7cbdce78548a
[netatalk.git] / etc / afpd / directory.c
1 /*
2  * $Id: directory.c,v 1.59 2003-01-26 10:42:40 didg Exp $
3  *
4  * Copyright (c) 1990,1993 Regents of The University of Michigan.
5  * All Rights Reserved.  See COPYRIGHT.
6  *
7  * 19 jan 2000 implemented red-black trees for directory lookups
8  * (asun@cobalt.com).
9  */
10
11 #ifdef HAVE_CONFIG_H
12 #include "config.h"
13 #endif /* HAVE_CONFIG_H */
14
15 #include <atalk/logger.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <errno.h>
19 #include <sys/time.h>
20 #include <sys/param.h>
21 #include <netatalk/endian.h>
22 #include <atalk/adouble.h>
23 #include <atalk/afp.h>
24 #include <atalk/util.h>
25 #ifdef CNID_DB
26 #include <atalk/cnid.h>
27 #endif /* CNID_DB */
28 #include <utime.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <dirent.h>
32 #ifdef HAVE_FCNTL_H
33 #include <fcntl.h>
34 #endif /* HAVE_FCNTL_H */
35 #include <grp.h>
36 #include <pwd.h>
37
38 /* STDC check */
39 #if STDC_HEADERS
40 #include <string.h>
41 #else /* STDC_HEADERS */
42 #ifndef HAVE_STRCHR
43 #define strchr index
44 #define strrchr index
45 #endif /* HAVE_STRCHR */
46 char *strchr (), *strrchr ();
47 #ifndef HAVE_MEMCPY
48 #define memcpy(d,s,n) bcopy ((s), (d), (n))
49 #define memmove(d,s,n) bcopy ((s), (d), (n))
50 #endif /* ! HAVE_MEMCPY */
51 #endif /* STDC_HEADERS */
52
53 #include "directory.h"
54 #include "desktop.h"
55 #include "volume.h"
56 #include "fork.h"
57 #include "file.h"
58 #include "filedir.h"
59 #include "globals.h"
60 #include "unix.h"
61
62 struct dir      *curdir;
63 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_getoflags( &ad, ADFLAGS_HF ) & 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     if ( curdir->d_child != NULL ) {
1927         return( AFPERR_DIRNEMPT );
1928     }
1929
1930     fdir = curdir;
1931
1932     memset(&ad, 0, sizeof(ad));
1933     if ( ad_open( ".", ADFLAGS_HF|ADFLAGS_DIR, O_RDONLY,
1934                   DIRBITS | 0777, &ad) == 0 ) {
1935
1936         ad_getattr(&ad, &ashort);
1937         ad_close( &ad, ADFLAGS_HF );
1938         if ((ashort & htons(ATTRBIT_NODELETE))) {
1939             return  AFPERR_OLOCK;
1940         }
1941     }
1942
1943     /* delete stray .AppleDouble files. this happens to get .Parent files
1944        as well. */
1945     if ((dp = opendir(".AppleDouble"))) {
1946         strcpy(path, ".AppleDouble/");
1947         while ((de = readdir(dp))) {
1948             /* skip this and previous directory */
1949             if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
1950                 continue;
1951
1952             /* bail if the file exists in the current directory.
1953              * note: this will not fail with dangling symlinks */
1954             if (stat(de->d_name, &st) == 0) {
1955                 closedir(dp);
1956                 return AFPERR_DIRNEMPT;
1957             }
1958
1959             strcpy(path + DOT_APPLEDOUBLE_LEN, de->d_name);
1960             if ((err = netatalk_unlink(path))) {
1961                 closedir(dp);
1962                 return err;
1963             }
1964         }
1965         closedir(dp);
1966     }
1967
1968     if ( (err = netatalk_rmdir( ".AppleDouble" ))  ) {
1969         return err;
1970     }
1971
1972     /* now get rid of dangling symlinks */
1973     if ((dp = opendir("."))) {
1974         while ((de = readdir(dp))) {
1975             /* skip this and previous directory */
1976             if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
1977                 continue;
1978
1979             /* bail if it's not a symlink */
1980             if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
1981                 closedir(dp);
1982                 return AFPERR_DIRNEMPT;
1983             }
1984
1985             if ((err = netatalk_unlink(de->d_name))) {
1986                 closedir(dp);
1987                 return err;
1988             }
1989         }
1990         closedir(dp);
1991     }
1992
1993     if ( movecwd( vol, curdir->d_parent ) < 0 ) {
1994         return afp_errno;
1995     }
1996
1997     if ( (err = netatalk_rmdir(fdir->d_u_name))) {
1998         return err;
1999     }
2000
2001     dirchildremove(curdir, fdir);
2002 #ifdef CNID_DB
2003     cnid_delete(vol->v_db, fdir->d_did);
2004 #endif /* CNID_DB */
2005     dir_remove( vol, fdir );
2006
2007     return( AFP_OK );
2008 }
2009
2010 int afp_mapid(obj, ibuf, ibuflen, rbuf, rbuflen )
2011 AFPObj      *obj;
2012 char    *ibuf, *rbuf;
2013 int             ibuflen, *rbuflen;
2014 {
2015     struct passwd       *pw;
2016     struct group        *gr;
2017     char                *name;
2018     u_int32_t           id;
2019     int                 len, sfunc;
2020     int         utf8 = 0;
2021     
2022     ibuf++;
2023     sfunc = (unsigned char) *ibuf++;
2024     memcpy( &id, ibuf, sizeof( id ));
2025
2026     id = ntohl(id);
2027     *rbuflen = 0;
2028
2029     if ( id != 0 ) {
2030         switch ( sfunc ) {
2031         case 1 :
2032         case 3 :/* unicode */
2033             if (( pw = getpwuid( id )) == NULL ) {
2034                 return( AFPERR_NOITEM );
2035             }
2036             name = pw->pw_name;
2037             break;
2038
2039         case 2 :
2040         case 4 : /* unicode */
2041             if (( gr = (struct group *)getgrgid( id )) == NULL ) {
2042                 return( AFPERR_NOITEM );
2043             }
2044             name = gr->gr_name;
2045             break;
2046
2047         default :
2048             return( AFPERR_PARAM );
2049         }
2050         switch ( sfunc ) {
2051         case 3:
2052         case 4:
2053             if (afp_version < 30) {
2054                 return( AFPERR_PARAM );
2055             }
2056             utf8 = 1;
2057             /* map to unicode */
2058             break;            
2059         }
2060         len = strlen( name );
2061
2062     } else {
2063         len = 0;
2064         name = NULL;
2065     }
2066     if (utf8) {
2067         u_int16_t tp = htons(len);
2068         memcpy(rbuf, &tp, sizeof(tp));
2069         rbuf += sizeof(tp);
2070         *rbuflen += 2;
2071     }
2072     else {
2073         *rbuf++ = len;
2074         *rbuflen += 1;
2075     }
2076     if ( len > 0 ) {
2077         memcpy( rbuf, name, len );
2078     }
2079     *rbuflen += len;
2080     return( AFP_OK );
2081 }
2082
2083 int afp_mapname(obj, ibuf, ibuflen, rbuf, rbuflen )
2084 AFPObj      *obj;
2085 char    *ibuf, *rbuf;
2086 int             ibuflen, *rbuflen;
2087 {
2088     struct passwd       *pw;
2089     struct group        *gr;
2090     int             len, sfunc;
2091     u_int32_t       id;
2092     u_int16_t       ulen;
2093
2094     ibuf++;
2095     sfunc = (unsigned char) *ibuf++;
2096     switch ( sfunc ) {
2097     case 1 : 
2098     case 2 : /* unicode */
2099         memcpy(&ulen, ibuf, sizeof(ulen));
2100         len = ntohs(ulen);
2101         ibuf += 2;
2102         break;
2103     case 3 :
2104     case 4 :
2105         len = (unsigned char) *ibuf++;
2106         break;
2107     default :
2108         *rbuflen = 0;
2109         return( AFPERR_PARAM );
2110     }
2111
2112     ibuf[ len ] = '\0';
2113
2114     if ( len != 0 ) {
2115         switch ( sfunc ) {
2116         case 1 : /* unicode */
2117         case 3 :
2118             if (( pw = (struct passwd *)getpwnam( ibuf )) == NULL ) {
2119                 *rbuflen = 0;
2120                 return( AFPERR_NOITEM );
2121             }
2122             id = pw->pw_uid;
2123             break;
2124
2125         case 2 : /* unicode */
2126         case 4 :
2127             if (( gr = (struct group *)getgrnam( ibuf )) == NULL ) {
2128                 *rbuflen = 0;
2129                 return( AFPERR_NOITEM );
2130             }
2131             id = gr->gr_gid;
2132             break;
2133         }
2134     } else {
2135         id = 0;
2136     }
2137     id = htonl(id);
2138     memcpy( rbuf, &id, sizeof( id ));
2139     *rbuflen = sizeof( id );
2140     return( AFP_OK );
2141 }
2142
2143 /* ------------------------------------
2144   variable DID support 
2145 */
2146 int afp_closedir(obj, ibuf, ibuflen, rbuf, rbuflen )
2147 AFPObj      *obj;
2148 char    *ibuf, *rbuf;
2149 int             ibuflen, *rbuflen;
2150 {
2151 #if 0
2152     struct vol   *vol;
2153     struct dir   *dir;
2154     u_int16_t    vid;
2155     u_int32_t    did;
2156 #endif /* 0 */
2157
2158     *rbuflen = 0;
2159
2160     /* do nothing as dids are static for the life of the process. */
2161 #if 0
2162     ibuf += 2;
2163
2164     memcpy(&vid,  ibuf, sizeof( vid ));
2165     ibuf += sizeof( vid );
2166     if (( vol = getvolbyvid( vid )) == NULL ) {
2167         return( AFPERR_PARAM );
2168     }
2169
2170     memcpy( &did, ibuf, sizeof( did ));
2171     ibuf += sizeof( did );
2172     if (( dir = dirlookup( vol, did )) == NULL ) {
2173         return( AFPERR_PARAM );
2174     }
2175
2176     /* dir_remove -- deletedid */
2177 #endif /* 0 */
2178
2179     return AFP_OK;
2180 }
2181
2182 /* did creation gets done automatically 
2183  * there's a pb again with case but move it to cname
2184 */
2185 int afp_opendir(obj, ibuf, ibuflen, rbuf, rbuflen )
2186 AFPObj      *obj;
2187 char    *ibuf, *rbuf;
2188 int             ibuflen, *rbuflen;
2189 {
2190     struct vol          *vol;
2191     struct dir          *parentdir;
2192     struct path         *path;
2193     u_int32_t           did;
2194     u_int16_t           vid;
2195
2196     *rbuflen = 0;
2197     ibuf += 2;
2198
2199     memcpy(&vid, ibuf, sizeof(vid));
2200     ibuf += sizeof( vid );
2201
2202     if (NULL == ( vol = getvolbyvid( vid )) ) {
2203         return( AFPERR_PARAM );
2204     }
2205
2206     memcpy(&did, ibuf, sizeof(did));
2207     ibuf += sizeof(did);
2208
2209     if (NULL == ( parentdir = dirlookup( vol, did )) ) {
2210         return afp_errno;
2211     }
2212
2213     if (NULL == ( path = cname( vol, parentdir, &ibuf )) ) {
2214         return afp_errno;
2215     }
2216
2217     if ( *path->m_name != '\0' ) {
2218         return (path_isadir(path))? afp_errno:AFPERR_BADTYPE ;
2219     }
2220
2221     if ( !path->st_valid && of_stat(path ) < 0 ) {
2222         return( AFPERR_NOOBJ );
2223     }
2224     if ( path->st_errno ) {
2225         return( AFPERR_NOOBJ );
2226     }
2227
2228     memcpy(rbuf, &curdir->d_did, sizeof(curdir->d_did));
2229     *rbuflen = sizeof(curdir->d_did);
2230     return AFP_OK;
2231 }