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