]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/directory.c
Use the right type for AFP function buffers size, size_t not int or unsigned int
[netatalk.git] / etc / afpd / directory.c
1 /*
2  * $Id: directory.c,v 1.108 2009-10-15 10:43:13 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 /* STDC check */
16 #if STDC_HEADERS
17 #include <string.h>
18 #else /* STDC_HEADERS */
19 #ifndef HAVE_STRCHR
20 #define strchr index
21 #define strrchr index
22 #endif /* HAVE_STRCHR */
23 char *strchr (), *strrchr ();
24 #ifndef HAVE_MEMCPY
25 #define memcpy(d,s,n) bcopy ((s), (d), (n))
26 #define memmove(d,s,n) bcopy ((s), (d), (n))
27 #endif /* ! HAVE_MEMCPY */
28 #endif /* STDC_HEADERS */
29 #ifdef HAVE_STRINGS_H
30 #include <strings.h>
31 #endif
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <dirent.h>
35
36 #include <grp.h>
37 #include <pwd.h>
38 #include <sys/param.h>
39 #include <errno.h>
40 #include <utime.h>
41
42 #include <atalk/adouble.h>
43 #include <atalk/vfs.h>
44 #include <atalk/afp.h>
45 #include <atalk/util.h>
46 #include <atalk/cnid.h>
47 #include <atalk/logger.h>
48 #include <atalk/uuid.h>
49
50 #include "directory.h"
51 #include "desktop.h"
52 #include "volume.h"
53 #include "fork.h"
54 #include "file.h"
55 #include "filedir.h"
56 #include "globals.h"
57 #include "unix.h"
58 #include "mangle.h"
59 #include "hash.h"
60
61 #ifdef HAVE_NFSv4_ACLS
62 extern void addir_inherit_acl(const struct vol *vol);
63 #endif 
64
65 struct dir      *curdir;
66 int             afp_errno;
67
68 #define SENTINEL (&sentinel)
69 static struct dir sentinel = { SENTINEL, SENTINEL, NULL, DIRTREE_COLOR_BLACK,
70                                  NULL, NULL, NULL, NULL, NULL, 0, 0, 
71                                  0, 0, NULL, NULL, NULL};
72 static struct dir rootpar  = { SENTINEL, SENTINEL, NULL, 0,
73                                  NULL, NULL, NULL, NULL, NULL, 0, 0, 
74                                  0, 0, NULL, NULL, NULL};
75
76 /* (from IM: Toolbox Essentials)
77  * dirFinderInfo (DInfo) fields:
78  * field        bytes
79  * frRect       8    folder's window rectangle
80  * frFlags      2    flags
81  * frLocation   4    folder's location in window
82  * frView       2    folder's view (default == closedView (256))
83  *
84  * extended dirFinderInfo (DXInfo) fields:
85  * frScroll     4    scroll position
86  * frOpenChain: 4    directory ID chain of open folders
87  * frScript:    1    script flag and code
88  * frXFlags:    1    reserved
89  * frComment:   2    comment ID
90  * frPutAway:   4    home directory ID
91  */
92
93 /*
94  * redid did assignment for directories. now we use red-black trees.
95  * how exciting.
96  */
97 struct dir *
98             dirsearch(const struct vol *vol, u_int32_t  did)
99 {
100     struct dir  *dir;
101
102
103     /* check for 0 did */
104     if (!did) {
105         afp_errno = AFPERR_PARAM;
106         return NULL;
107     }
108     if ( did == DIRDID_ROOT_PARENT ) {
109         if (!rootpar.d_did)
110             rootpar.d_did = DIRDID_ROOT_PARENT;
111         rootpar.d_child = vol->v_dir;
112         return( &rootpar );
113     }
114 #if 0
115     /* XXX would be nice to check against curdir but we need to keep its volume  */
116     if (vol->curdir && curdir->d_did == did) {
117         dir = curdir;
118     }
119     else {
120         dir = vol->v_root;
121     }
122 #endif
123     dir = vol->v_root;
124
125     afp_errno = AFPERR_NOOBJ;
126     while ( dir != SENTINEL ) {
127         if (dir->d_did == did)
128             return dir->d_m_name ? dir : NULL;
129         dir = (dir->d_did > did) ? dir->d_left : dir->d_right;
130     }
131     return NULL;
132 }
133
134 /* ------------------- */
135 int get_afp_errno(const int param)
136 {
137     if (afp_errno != AFPERR_DID1)
138         return afp_errno;
139     return param;
140 }
141
142 /* ------------------- */
143 struct dir *
144 dirsearch_byname( const struct vol *vol, struct dir *cdir, char *name)
145 {
146 struct dir *dir = NULL;
147
148     if ((cdir->d_did != DIRDID_ROOT_PARENT) && (cdir->d_child)) {
149         struct dir key;
150         hnode_t *hn;
151         
152         key.d_parent = cdir;
153         key.d_u_name = name;
154         hn = hash_lookup(vol->v_hash, &key);
155         if (hn) {
156             dir = hnode_get(hn);
157         }
158     }
159     return dir;
160 }            
161
162 /* -----------------------------------------
163  * if did is not in the cache resolve it with cnid 
164  * 
165  * FIXME
166  * OSX call it with bogus id, ie file ID not folder ID,
167  * and we are really bad in this case.
168  */
169 struct dir *
170             dirlookup( const struct vol *vol, u_int32_t did)
171 {
172     struct dir   *ret;
173     char         *upath;
174     cnid_t       id, cnid;
175     static char  path[MAXPATHLEN + 1];
176     size_t len,  pathlen;
177     char         *ptr;
178     static char  buffer[12 + MAXPATHLEN + 1];
179     int          buflen = 12 + MAXPATHLEN + 1;
180     char         *mpath;
181     int          utf8;
182     size_t       maxpath;
183     
184     ret = dirsearch(vol, did);
185     if (ret != NULL || afp_errno == AFPERR_PARAM)
186         return ret;
187
188     utf8 = utf8_encoding();
189     maxpath = (utf8)?MAXPATHLEN -7:255;
190     id = did;
191     if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen)) ) {
192         afp_errno = AFPERR_NOOBJ;
193         return NULL;
194     }
195     ptr = path + MAXPATHLEN;
196     if (NULL == ( mpath = utompath(vol, upath, did, utf8) ) ) {
197         afp_errno = AFPERR_NOOBJ;
198         return NULL;
199     }
200     len = strlen(mpath);
201     pathlen = len;          /* no 0 in the last part */
202     len++;
203     strcpy(ptr - len, mpath);
204     ptr -= len;
205     while (1) {
206         ret = dirsearch(vol,id);
207         if (ret != NULL) {
208             break;
209         }
210         cnid = id;
211         if ( NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, buflen))
212              ||
213              NULL == (mpath = utompath(vol, upath, cnid, utf8))
214         ) {
215             afp_errno = AFPERR_NOOBJ;
216             return NULL;
217         }
218
219         len = strlen(mpath) + 1;
220         pathlen += len;
221         if (pathlen > maxpath) {
222             afp_errno = AFPERR_PARAM;
223             return NULL;
224         }
225         strcpy(ptr - len, mpath);
226         ptr -= len;
227     }
228     
229     /* fill the cache, another place where we know about the path type */
230     if (utf8) {
231         u_int16_t temp16;
232         u_int32_t temp;
233
234         ptr -= 2; 
235         temp16 = htons(pathlen);
236         memcpy(ptr, &temp16, sizeof(temp16));
237
238         temp = htonl(kTextEncodingUTF8);
239         ptr -= 4; 
240         memcpy(ptr, &temp, sizeof(temp));
241         ptr--;
242         *ptr = 3;
243     }
244     else {
245         ptr--;
246         *ptr = (unsigned char)pathlen;
247         ptr--;
248         *ptr = 2;
249     }
250     /* cname is not efficient */
251     if (cname( vol, ret, &ptr ) == NULL )
252         return NULL;
253         
254     return dirsearch(vol, did);
255 }
256
257 /* child addition/removal */
258 static void dirchildadd(const struct vol *vol, struct dir *a, struct dir *b) 
259 {
260     if (!a->d_child)
261         a->d_child = b;
262     else {
263         b->d_next = a->d_child;
264         b->d_prev = b->d_next->d_prev;
265         b->d_next->d_prev = b;
266         b->d_prev->d_next = b;
267     }
268     if (!hash_alloc_insert(vol->v_hash, b, b)) { 
269         LOG(log_error, logtype_afpd, "dirchildadd: can't hash %s", b->d_u_name);
270     }
271
272
273 static void dirchildremove(struct dir *a,struct dir *b)
274
275     if (a->d_child == b) 
276         a->d_child = (b == b->d_next) ? NULL : b->d_next;
277     b->d_next->d_prev = b->d_prev;
278     b->d_prev->d_next = b->d_next;
279     b->d_next = b->d_prev = b;
280 }
281
282 /* --------------------------- */
283 /* rotate the tree to the left */
284 static void dir_leftrotate(struct vol *vol, struct dir *dir)
285 {
286     struct dir *right = dir->d_right;
287
288     /* whee. move the right's left tree into dir's right tree */
289     dir->d_right = right->d_left;
290     if (right->d_left != SENTINEL)
291         right->d_left->d_back = dir;
292
293     if (right != SENTINEL) {
294         right->d_back = dir->d_back;
295         right->d_left = dir;
296     }
297
298     if (!dir->d_back) /* no parent. move the right tree to the top. */
299         vol->v_root = right;
300     else if (dir == dir->d_back->d_left) /* we were on the left */
301         dir->d_back->d_left = right;
302     else
303         dir->d_back->d_right = right; /* we were on the right */
304
305     /* re-insert dir on the left tree */
306     if (dir != SENTINEL)
307         dir->d_back = right;
308 }
309
310
311
312 /* rotate the tree to the right */
313 static void dir_rightrotate(struct vol *vol, struct dir *dir)
314 {
315     struct dir *left = dir->d_left;
316
317     /* whee. move the left's right tree into dir's left tree */
318     dir->d_left = left->d_right;
319     if (left->d_right != SENTINEL)
320         left->d_right->d_back = dir;
321
322     if (left != SENTINEL) {
323         left->d_back = dir->d_back;
324         left->d_right = dir;
325     }
326
327     if (!dir->d_back) /* no parent. move the left tree to the top. */
328         vol->v_root = left;
329     else if (dir == dir->d_back->d_right) /* we were on the right */
330         dir->d_back->d_right = left;
331     else
332         dir->d_back->d_left = left; /* we were on the left */
333
334     /* re-insert dir on the right tree */
335     if (dir != SENTINEL)
336         dir->d_back = left;
337 }
338
339 #if 0
340 /* recolor after a removal */
341 static struct dir *dir_rmrecolor(struct vol *vol, struct dir *dir)
342 {
343     struct dir *leaf;
344
345     while ((dir != vol->v_root) && (dir->d_color == DIRTREE_COLOR_BLACK)) {
346         /* are we on the left tree? */
347         if (dir == dir->d_back->d_left) {
348             leaf = dir->d_back->d_right; /* get right side */
349             if (leaf->d_color == DIRTREE_COLOR_RED) {
350                 /* we're red. we need to change to black. */
351                 leaf->d_color = DIRTREE_COLOR_BLACK;
352                 dir->d_back->d_color = DIRTREE_COLOR_RED;
353                 dir_leftrotate(vol, dir->d_back);
354                 leaf = dir->d_back->d_right;
355             }
356
357             /* right leaf has black end nodes */
358             if ((leaf->d_left->d_color == DIRTREE_COLOR_BLACK) &&
359                     (leaf->d_right->d_color = DIRTREE_COLOR_BLACK)) {
360                 leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
361                 dir = dir->d_back; /* ascend */
362             } else {
363                 if (leaf->d_right->d_color == DIRTREE_COLOR_BLACK) {
364                     leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
365                     leaf->d_color = DIRTREE_COLOR_RED;
366                     dir_rightrotate(vol, leaf);
367                     leaf = dir->d_back->d_right;
368                 }
369                 leaf->d_color = dir->d_back->d_color;
370                 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
371                 leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
372                 dir_leftrotate(vol, dir->d_back);
373                 dir = vol->v_root;
374             }
375         } else { /* right tree */
376             leaf = dir->d_back->d_left; /* left tree */
377             if (leaf->d_color == DIRTREE_COLOR_RED) {
378                 leaf->d_color = DIRTREE_COLOR_BLACK;
379                 dir->d_back->d_color = DIRTREE_COLOR_RED;
380                 dir_rightrotate(vol, dir->d_back);
381                 leaf = dir->d_back->d_left;
382             }
383
384             /* left leaf has black end nodes */
385             if ((leaf->d_right->d_color == DIRTREE_COLOR_BLACK) &&
386                     (leaf->d_left->d_color = DIRTREE_COLOR_BLACK)) {
387                 leaf->d_color = DIRTREE_COLOR_RED; /* recolor leaf as red */
388                 dir = dir->d_back; /* ascend */
389             } else {
390                 if (leaf->d_left->d_color == DIRTREE_COLOR_BLACK) {
391                     leaf->d_right->d_color = DIRTREE_COLOR_BLACK;
392                     leaf->d_color = DIRTREE_COLOR_RED;
393                     dir_leftrotate(vol, leaf);
394                     leaf = dir->d_back->d_left;
395                 }
396                 leaf->d_color = dir->d_back->d_color;
397                 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
398                 leaf->d_left->d_color = DIRTREE_COLOR_BLACK;
399                 dir_rightrotate(vol, dir->d_back);
400                 dir = vol->v_root;
401             }
402         }
403     }
404     dir->d_color = DIRTREE_COLOR_BLACK;
405
406     return dir;
407 }
408 #endif /* 0 */
409
410 /* --------------------- */
411 static void dir_hash_del(const struct vol *vol, struct dir *dir)
412 {
413     hnode_t *hn;
414
415     hn = hash_lookup(vol->v_hash, dir);
416     if (!hn) {
417         LOG(log_error, logtype_afpd, "dir_hash_del: %s not hashed", dir->d_u_name);
418     }
419     else {
420         hash_delete(vol->v_hash, hn);
421     }
422 }
423
424 /* remove the node from the tree. this is just like insertion, but
425  * different. actually, it has to worry about a bunch of things that
426  * insertion doesn't care about. */
427
428 static void dir_remove( const struct vol *vol _U_, struct dir   *dir)
429 {
430 #ifdef REMOVE_NODES
431     struct ofork *of, *last;
432     struct dir *node, *leaf;
433 #endif /* REMOVE_NODES */
434         
435     if (!dir || (dir == SENTINEL))
436         return;
437         
438     /* i'm not sure if it really helps to delete stuff. */
439     dir_hash_del(vol, dir);
440 #ifndef REMOVE_NODES 
441     dirfreename(dir);
442     dir->d_m_name = NULL;
443     dir->d_u_name = NULL;
444     dir->d_m_name_ucs2 = NULL;
445 #else /* ! REMOVE_NODES */
446
447     /* go searching for a node with at most one child */
448     if ((dir->d_left == SENTINEL) || (dir->d_right == SENTINEL)) {
449         node = dir;
450     } else {
451         node = dir->d_right;
452         while (node->d_left != SENTINEL)
453             node = node->d_left;
454     }
455
456     /* get that child */
457     leaf = (node->d_left != SENTINEL) ? node->d_left : node->d_right;
458
459     /* detach node */
460     leaf->d_back = node->d_back;
461     if (!node->d_back) {
462         vol->v_root = leaf;
463     } else if (node == node->d_back->d_left) { /* left tree */
464         node->d_back->d_left = leaf;
465     } else {
466         node->d_back->d_right = leaf;
467     }
468
469     /* we want to free node, but we also want to free the data in dir.
470     * currently, that's d_name and the directory traversal bits.
471     * we just copy the necessary bits and then fix up all the
472     * various pointers to the directory. needless to say, there are
473     * a bunch of places that store the directory struct. */
474     if (node != dir) {
475         struct dir save, *tmp;
476
477         memcpy(&save, dir, sizeof(save));
478         memcpy(dir, node, sizeof(struct dir));
479
480         /* restore the red-black bits */
481         dir->d_left = save.d_left;
482         dir->d_right = save.d_right;
483         dir->d_back = save.d_back;
484         dir->d_color = save.d_color;
485
486         if (node == vol->v_dir) {/* we may need to fix up this pointer */
487             vol->v_dir = dir;
488             rootpar.d_child = vol->v_dir;
489         } else {
490             /* if we aren't the root directory, we have parents and
491             * siblings to worry about */
492             if (dir->d_parent->d_child == node)
493                 dir->d_parent->d_child = dir;
494             dir->d_next->d_prev = dir;
495             dir->d_prev->d_next = dir;
496         }
497
498         /* fix up children. */
499         tmp = dir->d_child;
500         while (tmp) {
501             tmp->d_parent = dir;
502             tmp = (tmp == dir->d_child->d_prev) ? NULL : tmp->d_next;
503         }
504
505         if (node == curdir) /* another pointer to fixup */
506             curdir = dir;
507
508         /* we also need to fix up oforks. bleah */
509         if ((of = dir->d_ofork)) {
510             last = of->of_d_prev;
511             while (of) {
512                 of->of_dir = dir;
513                 of = (last == of) ? NULL : of->of_d_next;
514             }
515         }
516
517         /* set the node's d_name */
518         node->d_m_name = save.d_m_name;
519         node->d_u_name = save.d_u_name;
520         node->d_m_name_ucs2 = save.d_m_name_ucs2;
521     }
522
523     if (node->d_color == DIRTREE_COLOR_BLACK)
524         dir_rmrecolor(vol, leaf);
525
526     if (node->d_m_name_ucs2)
527         free(node->d_u_name_ucs2);
528     if (node->d_u_name != node->d_m_name) {
529         free(node->d_u_name);
530     }
531     free(node->d_m_name);
532     free(node);
533 #endif /* ! REMOVE_NODES */
534 }
535
536 /* ---------------------------------------
537  * remove the node and its childs from the tree
538  *
539  * FIXME what about opened forks with refs to it?
540  * it's an afp specs violation because you can't delete
541  * an opened forks. Now afpd doesn't care about forks opened by other 
542  * process. It's fixable within afpd if fnctl_lock, doable with smb and
543  * next to impossible for nfs and local filesystem access.
544  */
545 static void dir_invalidate( const struct vol *vol, struct dir *dir)
546 {
547     if (curdir == dir) {
548         /* v_root can't be deleted */
549         if (movecwd(vol, vol->v_root) < 0) {
550             LOG(log_error, logtype_afpd, "cname can't chdir to : %s", vol->v_root);
551         }
552     }
553     /* FIXME */
554     dirchildremove(dir->d_parent, dir);
555     dir_remove( vol, dir );
556 }
557
558 /* ------------------------------------ */
559 static struct dir *dir_insert(const struct vol *vol, struct dir *dir)
560 {
561     struct dir  *pdir;
562
563     pdir = vol->v_root;
564     while (pdir->d_did != dir->d_did ) {
565         if ( pdir->d_did > dir->d_did ) {
566             if ( pdir->d_left == SENTINEL ) {
567                 pdir->d_left = dir;
568                 dir->d_back = pdir;
569                 return NULL;
570             }
571             pdir = pdir->d_left;
572         } else {
573             if ( pdir->d_right == SENTINEL ) {
574                 pdir->d_right = dir;
575                 dir->d_back = pdir;
576                 return NULL;
577             }
578             pdir = pdir->d_right;
579         }
580     }
581     return pdir;
582 }
583
584 #define ENUMVETO "./../Network Trash Folder/TheVolumeSettingsFolder/TheFindByContentFolder/:2eDS_Store/Contents/Desktop Folder/Trash/Benutzer/"
585
586 int
587 caseenumerate(const struct vol *vol, struct path *path, struct dir *dir)
588 {
589     DIR               *dp;
590     struct dirent     *de;
591     int               ret;
592     static u_int32_t  did = 0;
593     static char       cname[MAXPATHLEN];
594     static char       lname[MAXPATHLEN];
595     ucs2_t            u2_path[MAXPATHLEN];
596     ucs2_t            u2_dename[MAXPATHLEN];
597     char              *tmp, *savepath;
598
599     if (!(vol->v_flags & AFPVOL_CASEINSEN))
600         return -1;
601         
602     if (veto_file(ENUMVETO, path->u_name))
603         return -1;
604
605     savepath = path->u_name;
606
607     /* very simple cache */
608     if ( dir->d_did == did && strcmp(lname, path->u_name) == 0) {
609         path->u_name = cname;
610         path->d_dir = NULL;
611         if (of_stat( path ) == 0 ) {
612             return 0;
613         }
614         /* something changed, we cannot stat ... */
615         did = 0;
616     }
617
618     if (NULL == ( dp = opendir( "." )) ) {
619         LOG(log_debug, logtype_afpd, "caseenumerate: opendir failed: %s", dir->d_u_name);
620         return -1;
621     }
622
623
624     /* LOG(log_debug, logtype_afpd, "caseenumerate: for %s", path->u_name); */
625     if ((size_t) -1 == convert_string(vol->v_volcharset, CH_UCS2, path->u_name, strlen(path->u_name), u2_path, sizeof(u2_path)) ) 
626         LOG(log_debug, logtype_afpd, "caseenumerate: conversion failed for %s", path->u_name);
627
628     /*LOG(log_debug, logtype_afpd, "caseenumerate: dir: %s, path: %s", dir->d_u_name, path->u_name); */
629     ret = -1;
630     for ( de = readdir( dp ); de != NULL; de = readdir( dp )) {
631         if (NULL == check_dirent(vol, de->d_name))
632             continue;
633
634         if ((size_t) -1 == convert_string(vol->v_volcharset, CH_UCS2, de->d_name, strlen(de->d_name), u2_dename, sizeof(u2_dename)) )
635             continue;
636
637         if (strcasecmp_w( u2_path, u2_dename) == 0) {
638             tmp = path->u_name;
639             strlcpy(cname, de->d_name, sizeof(cname));
640             path->u_name = cname;
641             path->d_dir = NULL;
642             if (of_stat( path ) == 0 ) {
643                 LOG(log_debug, logtype_afpd, "caseenumerate: using dir: %s, path: %s", de->d_name, path->u_name);
644                 strlcpy(lname, tmp, sizeof(lname));
645                 did = dir->d_did;
646                 ret = 0;
647                 break;
648             }
649             else 
650                 path->u_name = tmp;
651         }
652
653     }
654     closedir(dp);
655
656     if (ret) {
657         /* invalidate cache */
658         cname[0] = 0;
659         did = 0;
660         path->u_name = savepath;
661     }
662     /* LOG(log_debug, logtype_afpd, "caseenumerate: path on ret: %s", path->u_name); */
663     return ret;
664 }
665
666
667 /*
668  * attempt to extend the current dir. tree to include path
669  * as a side-effect, movecwd to that point and return the new dir
670  */
671 static struct dir *
672             extenddir(struct vol *vol, struct dir *dir, struct path *path)
673 {
674     path->d_dir = NULL;
675
676     if ( path->u_name == NULL) {
677         afp_errno = AFPERR_PARAM;
678         return NULL;
679     }
680
681     if (check_name(vol, path->u_name)) {
682         /* the name is illegal */
683         LOG(log_info, logtype_afpd, "extenddir: illegal path: '%s'", path->u_name);
684         path->u_name = NULL;
685         afp_errno = AFPERR_PARAM;
686         return NULL;
687     }
688
689     if (of_stat( path ) != 0 ) {
690         if (!(vol->v_flags & AFPVOL_CASEINSEN))
691             return NULL;
692         else if(caseenumerate(vol, path, dir) != 0)
693             return(NULL);
694     }
695
696     if (!S_ISDIR(path->st.st_mode)) {
697         return( NULL );
698     }
699
700     /* mac name is always with the right encoding (from cname()) */
701     if (( dir = adddir( vol, dir, path)) == NULL ) {
702         return( NULL );
703     }
704
705     path->d_dir = dir;
706     if ( movecwd( vol, dir ) < 0 ) {
707         return( NULL );
708     }
709
710     return( dir );
711 }
712
713 /* -------------------------
714    appledouble mkdir afp error code.
715 */
716 static int netatalk_mkdir(const char *name)
717 {
718     if (ad_mkdir(name, DIRBITS | 0777) < 0) {
719         switch ( errno ) {
720         case ENOENT :
721             return( AFPERR_NOOBJ );
722         case EROFS :
723             return( AFPERR_VLOCK );
724         case EPERM:
725         case EACCES :
726             return( AFPERR_ACCESS );
727         case EEXIST :
728             return( AFPERR_EXIST );
729         case ENOSPC :
730         case EDQUOT :
731             return( AFPERR_DFULL );
732         default :
733             return( AFPERR_PARAM );
734         }
735     }
736     return AFP_OK;
737 }
738
739 /* ------------------- */
740 static int deletedir(char *dir)
741 {
742     char path[MAXPATHLEN + 1];
743     DIR *dp;
744     struct dirent       *de;
745     struct stat st;
746     size_t len;
747     int err = AFP_OK;
748     size_t remain;
749
750     if ((len = strlen(dir)) +2 > sizeof(path))
751         return AFPERR_PARAM;
752
753     /* already gone */
754     if ((dp = opendir(dir)) == NULL)
755         return AFP_OK;
756
757     strcpy(path, dir);
758     strcat(path, "/");
759     len++;
760     remain = sizeof(path) -len -1;
761     while ((de = readdir(dp)) && err == AFP_OK) {
762         /* skip this and previous directory */
763         if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
764             continue;
765
766         if (strlen(de->d_name) > remain) {
767             err = AFPERR_PARAM;
768             break;
769         }
770         strcpy(path + len, de->d_name);
771         if (stat(path, &st)) {
772             continue;
773         }
774         if (S_ISDIR(st.st_mode)) {
775             err = deletedir(path);
776         } else {
777             err = netatalk_unlink(path);
778         }
779     }
780     closedir(dp);
781
782     /* okay. the directory is empty. delete it. note: we already got rid
783        of .AppleDouble.  */
784     if (err == AFP_OK) {
785         err = netatalk_rmdir(dir);
786     }
787     return err;
788 }
789
790 /* do a recursive copy. */
791 static int copydir(const struct vol *vol, char *src, char *dst)
792 {
793     char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
794     DIR *dp;
795     struct dirent       *de;
796     struct stat st;
797     struct utimbuf      ut;
798     size_t slen, dlen;
799     size_t srem, drem;
800     int err;
801
802     /* doesn't exist or the path is too long. */
803     if (((slen = strlen(src)) > sizeof(spath) - 2) ||
804             ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
805             ((dp = opendir(src)) == NULL))
806         return AFPERR_PARAM;
807
808     /* try to create the destination directory */
809     if (AFP_OK != (err = netatalk_mkdir(dst)) ) {
810         closedir(dp);
811         return err;
812     }
813
814     /* set things up to copy */
815     strcpy(spath, src);
816     strcat(spath, "/");
817     slen++;
818     srem = sizeof(spath) - slen -1;
819     
820     strcpy(dpath, dst);
821     strcat(dpath, "/");
822     dlen++;
823     drem = sizeof(dpath) - dlen -1;
824
825     err = AFP_OK;
826     while ((de = readdir(dp))) {
827         /* skip this and previous directory */
828         if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
829             continue;
830
831         if (strlen(de->d_name) > srem) {
832             err = AFPERR_PARAM;
833             break;
834         }
835         strcpy(spath + slen, de->d_name);
836
837         if (stat(spath, &st) == 0) {
838             if (strlen(de->d_name) > drem) {
839                 err = AFPERR_PARAM;
840                 break;
841             }
842             strcpy(dpath + dlen, de->d_name);
843
844             if (S_ISDIR(st.st_mode)) {
845                 if (AFP_OK != (err = copydir(vol, spath, dpath)))
846                     goto copydir_done;
847             } else if (AFP_OK != (err = copyfile(vol, vol, spath, dpath, NULL, NULL))) {
848                 goto copydir_done;
849
850             } else {
851                 /* keep the same time stamp. */
852                 ut.actime = ut.modtime = st.st_mtime;
853                 utime(dpath, &ut);
854             }
855         }
856     }
857
858     /* keep the same time stamp. */
859     if (stat(src, &st) == 0) {
860         ut.actime = ut.modtime = st.st_mtime;
861         utime(dst, &ut);
862     }
863
864 copydir_done:
865     closedir(dp);
866     return err;
867 }
868
869
870 /* --- public functions follow --- */
871
872 /* NOTE: we start off with at least one node (the root directory). */
873 static struct dir *dirinsert(struct vol *vol, struct dir *dir)
874 {
875     struct dir *node;
876
877     if ((node = dir_insert(vol, dir)))
878         return node;
879
880     /* recolor the tree. the current node is red. */
881     dir->d_color = DIRTREE_COLOR_RED;
882
883     /* parent of this node has to be black. if the parent node
884     * is red, then we have a grandparent. */
885     while ((dir != vol->v_root) &&
886             (dir->d_back->d_color == DIRTREE_COLOR_RED)) {
887         /* are we on the left tree? */
888         if (dir->d_back == dir->d_back->d_back->d_left) {
889             node = dir->d_back->d_back->d_right;  /* get the right node */
890             if (node->d_color == DIRTREE_COLOR_RED) {
891                 /* we're red. we need to change to black. */
892                 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
893                 node->d_color = DIRTREE_COLOR_BLACK;
894                 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
895                 dir = dir->d_back->d_back; /* finished. go up. */
896             } else {
897                 if (dir == dir->d_back->d_right) {
898                     dir = dir->d_back;
899                     dir_leftrotate(vol, dir);
900                 }
901                 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
902                 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
903                 dir_rightrotate(vol, dir->d_back->d_back);
904             }
905         } else {
906             node = dir->d_back->d_back->d_left;
907             if (node->d_color == DIRTREE_COLOR_RED) {
908                 /* we're red. we need to change to black. */
909                 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
910                 node->d_color = DIRTREE_COLOR_BLACK;
911                 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
912                 dir = dir->d_back->d_back; /* finished. ascend */
913             } else {
914                 if (dir == dir->d_back->d_left) {
915                     dir = dir->d_back;
916                     dir_rightrotate(vol, dir);
917                 }
918                 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
919                 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
920                 dir_leftrotate(vol, dir->d_back->d_back);
921             }
922         }
923     }
924
925     vol->v_root->d_color = DIRTREE_COLOR_BLACK;
926     return NULL;
927 }
928
929 /* ---------------------------- */
930 struct dir *
931             adddir(struct vol *vol, struct dir *dir, struct path *path)
932 {
933     struct dir  *cdir, *edir;
934     int         upathlen;
935     char        *name;
936     char        *upath;
937     struct stat *st;
938     int         deleted;
939     cnid_t      id;
940
941     upath = path->u_name;
942     st    = &path->st;
943     upathlen = strlen(upath);
944
945     id = get_id(vol, NULL, st, dir->d_did, upath, upathlen);
946     if (id == 0) {
947         return NULL;
948     }
949     if (!path->m_name && !(path->m_name = utompath(vol, upath, id , utf8_encoding()))) {
950         return NULL;
951     }
952     name  = path->m_name;    
953     if ((cdir = dirnew(name, upath)) == NULL) {
954         LOG(log_error, logtype_afpd, "adddir: malloc: %s", strerror(errno) );
955         return NULL;
956     }
957     if ((size_t)-1 == convert_string_allocate((utf8_encoding())?CH_UTF8_MAC:vol->v_maccharset, CH_UCS2, path->m_name, strlen(path->m_name), &cdir->d_m_name_ucs2)) {
958         LOG(log_error, logtype_afpd, "Couldn't set UCS2 name for %s", name);
959         cdir->d_m_name_ucs2 = NULL;
960     }
961
962     cdir->d_did = id;
963
964     if ((edir = dirinsert( vol, cdir ))) {
965         /* it's not possible with LASTDID
966            for CNID:
967            - someone else have moved the directory.
968            - it's a symlink inside the share.
969            - it's an ID reused, the old directory was deleted but not
970              the cnid record and the server've reused the inode for 
971              the new dir.
972            for HASH (we should get ride of HASH) 
973            - someone else have moved the directory.
974            - it's an ID reused as above
975            - it's a hash duplicate and we are in big trouble
976         */
977         deleted = (edir->d_m_name == NULL);
978         if (!deleted)
979             dir_hash_del(vol, edir);
980         dirfreename(edir);
981         edir->d_m_name = cdir->d_m_name;
982         edir->d_u_name = cdir->d_u_name;
983         edir->d_m_name_ucs2 = cdir->d_m_name_ucs2;
984         free(cdir);
985         cdir = edir;
986         LOG(log_error, logtype_afpd, "adddir: insert %s", edir->d_m_name);
987         if (!cdir->d_parent || (cdir->d_parent == dir && !deleted)) {
988             hash_alloc_insert(vol->v_hash, cdir, cdir);
989             return cdir;
990         }
991         /* the old was not in the same folder */
992         if (!deleted)
993             dirchildremove(cdir->d_parent, cdir);
994     }
995
996     /* parent/child directories */
997     cdir->d_parent = dir;
998     dirchildadd(vol, dir, cdir);
999     return( cdir );
1000 }
1001
1002 /* --- public functions follow --- */
1003 /* free everything down. we don't bother to recolor as this is only
1004  * called to free the entire tree */
1005 void dirfreename(struct dir *dir)
1006 {
1007     if (dir->d_u_name != dir->d_m_name) {
1008         free(dir->d_u_name);
1009     }
1010     if (dir->d_m_name_ucs2)
1011         free(dir->d_m_name_ucs2); 
1012     free(dir->d_m_name);
1013 }
1014
1015 void dirfree(struct dir *dir)
1016 {
1017     if (!dir || (dir == SENTINEL))
1018         return;
1019
1020     if ( dir->d_left != SENTINEL ) {
1021         dirfree( dir->d_left );
1022     }
1023     if ( dir->d_right != SENTINEL ) {
1024         dirfree( dir->d_right );
1025     }
1026
1027     if (dir != SENTINEL) {
1028         dirfreename(dir);
1029         free( dir );
1030     }
1031 }
1032
1033 /* --------------------------------------------
1034  * most of the time mac name and unix name are the same 
1035 */
1036 struct dir *dirnew(const char *m_name, const char *u_name)
1037 {
1038     struct dir *dir;
1039
1040     dir = (struct dir *) calloc(1, sizeof( struct dir ));
1041     if (!dir)
1042         return NULL;
1043
1044     if ((dir->d_m_name = strdup(m_name)) == NULL) {
1045         free(dir);
1046         return NULL;
1047     }
1048
1049     if (m_name == u_name || !strcmp(m_name, u_name)) {
1050         dir->d_u_name = dir->d_m_name;
1051     }
1052     else if ((dir->d_u_name = strdup(u_name)) == NULL) {
1053         free(dir->d_m_name);
1054         free(dir);
1055         return NULL;
1056     }
1057
1058     dir->d_m_name_ucs2 = NULL;
1059     dir->d_left = dir->d_right = SENTINEL;
1060     dir->d_next = dir->d_prev = dir;
1061     return dir;
1062 }
1063
1064 /* ------------------ */
1065 static hash_val_t hash_fun_dir(const void *key)
1066 {
1067 const struct dir *k = key;
1068
1069     static unsigned long randbox[] = {
1070         0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U,
1071         0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU,
1072         0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU,
1073         0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
1074     };
1075
1076     const unsigned char *str = k->d_u_name;
1077     hash_val_t acc = 0;
1078
1079     while (*str) {
1080         acc ^= randbox[(*str + acc) & 0xf];
1081         acc = (acc << 1) | (acc >> 31);
1082         acc &= 0xffffffffU;
1083         acc ^= randbox[((*str++ >> 4) + acc) & 0xf];
1084         acc = (acc << 2) | (acc >> 30);
1085         acc &= 0xffffffffU;
1086     }
1087     return acc;
1088 }
1089
1090 /* ---------------- */
1091 static int hash_comp_dir(const void *key1, const void *key2)
1092 {
1093 const struct dir *k1 = key1;
1094 const struct dir *k2 = key2;
1095
1096     return !(k1->d_parent->d_did == k2->d_parent->d_did && !strcmp(k1->d_u_name, k2->d_u_name));
1097 }
1098
1099 /* ---------------- */
1100 hash_t *
1101 dirhash(void)
1102 {
1103     return hash_create(HASHCOUNT_T_MAX, hash_comp_dir, hash_fun_dir);
1104 }
1105
1106 /* ------------------ */
1107 static struct path *invalidate (const struct vol *vol, struct dir *dir, struct path *ret)
1108 {
1109     /* it's tricky:
1110        movecwd failed some of dir path are not there anymore.
1111        FIXME Is it true with other errors?
1112        so we remove dir from the cache 
1113     */
1114     if (dir->d_did == DIRDID_ROOT_PARENT)
1115         return NULL;
1116     if (afp_errno == AFPERR_ACCESS) {
1117         if ( movecwd( vol, dir->d_parent ) < 0 ) {
1118             return NULL;                
1119         }
1120         /* FIXME should we set these?, don't need to call stat() after:
1121            ret->st_valid = 1;
1122            ret->st_errno = EACCES;
1123        */
1124        ret->m_name = dir->d_m_name;
1125        ret->u_name = dir->d_u_name;
1126        ret->d_dir = dir;
1127        return ret;
1128     } else if (afp_errno == AFPERR_NOOBJ) {
1129        if ( movecwd( vol, dir->d_parent ) < 0 ) {
1130            return NULL;                 
1131        }
1132        strcpy(ret->m_name, dir->d_m_name);
1133        if (dir->d_m_name == dir->d_u_name) {
1134            ret->u_name = ret->m_name;
1135        }
1136        else {
1137            size_t tp = strlen(ret->m_name)+1;
1138            
1139            ret->u_name =  ret->m_name +tp;
1140            strcpy(ret->u_name, dir->d_u_name);
1141        }
1142        /* FIXME should we set :
1143           ret->st_valid = 1;
1144           ret->st_errno = ENOENT;
1145        */
1146        dir_invalidate(vol, dir);
1147        return ret;
1148     }
1149     dir_invalidate(vol, dir);
1150     return NULL;
1151 }
1152
1153 /* -------------------------------------------------- */
1154 /* cname 
1155  return
1156  if it's a filename:
1157       in extenddir:
1158          compute unix name
1159          stat the file or errno 
1160       return 
1161          filename
1162          curdir: filename parent directory
1163          
1164  if it's a dirname:
1165       not in the cache
1166           in extenddir
1167               compute unix name
1168               stat the dir or errno
1169           return
1170               if chdir error 
1171                  dirname 
1172                  curdir: dir parent directory
1173               sinon
1174                  dirname: ""
1175                  curdir: dir   
1176       in the cache 
1177           return
1178               if chdir error
1179                  dirname
1180                  curdir: dir parent directory 
1181               else
1182                  dirname: ""
1183                  curdir: dir   
1184                  
1185 */
1186 struct path *
1187 cname(const struct vol *vol, struct dir *dir, char **cpath)
1188 {
1189     struct dir             *cdir, *scdir=NULL;
1190     static char            path[ MAXPATHLEN + 1];
1191     static struct path ret;
1192
1193     char                *data, *p;
1194     int                 extend = 0;
1195     int                 len;
1196     u_int32_t   hint;
1197     u_int16_t   len16;
1198     int         size = 0;
1199     char        sep;
1200     int         toUTF8 = 0;
1201
1202     data = *cpath;
1203     afp_errno = AFPERR_NOOBJ;
1204     memset(&ret, 0, sizeof(ret));
1205     switch (ret.m_type = *data) { /* path type */
1206     case 2:
1207        data++;
1208        len = (unsigned char) *data++;
1209        size = 2;
1210        sep = 0;
1211        if (afp_version >= 30) {
1212            ret.m_type = 3;
1213            toUTF8 = 1;
1214        }
1215        break;
1216     case 3:
1217        if (afp_version >= 30) {
1218            data++;
1219            memcpy(&hint, data, sizeof(hint));
1220            hint = ntohl(hint);
1221            data += sizeof(hint);
1222            
1223            memcpy(&len16, data, sizeof(len16));
1224            len = ntohs(len16);
1225            data += 2;
1226            size = 7;
1227            sep = 0; /* '/';*/
1228            break;
1229         }
1230         /* else it's an error */
1231     default:
1232         afp_errno = AFPERR_PARAM;
1233         return( NULL );
1234     }
1235     *cpath += len + size;
1236     *path = '\0';
1237     ret.m_name = path;
1238     for ( ;; ) {
1239         if ( len == 0 ) {
1240             if (movecwd( vol, dir ) < 0 ) {
1241                 return invalidate(vol, dir, &ret );
1242             }
1243             if (*path == '\0') {
1244                ret.u_name = ".";
1245                ret.d_dir = dir;
1246             }               
1247             return &ret;
1248         }
1249
1250         if (*data == sep ) {
1251             data++;
1252             len--;
1253         }
1254         while (*data == sep && len > 0 ) {
1255             if ( dir->d_parent == NULL ) {
1256                 return NULL;
1257             }
1258             dir = dir->d_parent;
1259             data++;
1260             len--;
1261         }
1262
1263         /* would this be faster with strlen + strncpy? */
1264         p = path;
1265         while ( *data != sep && len > 0 ) {
1266             *p++ = *data++;
1267             if (p > &path[ MAXPATHLEN]) {
1268                 afp_errno = AFPERR_PARAM;
1269                 return( NULL );
1270             }
1271             len--;
1272         }
1273
1274         /* short cut bits by chopping off a trailing \0. this also
1275                   makes the traversal happy w/ filenames at the end of the
1276                   cname. */
1277         if (len == 1)
1278             len--;
1279
1280         *p = '\0';
1281
1282         if ( p == path ) { /* end of the name parameter */
1283             continue;
1284         }
1285         ret.u_name = NULL;
1286         if (afp_version >= 30) {
1287             char *t;
1288             cnid_t fileid;
1289                 
1290             if (toUTF8) {
1291                 static char     temp[ MAXPATHLEN + 1];
1292
1293                 if (dir->d_did == DIRDID_ROOT_PARENT) {
1294                     /*
1295                       With uft8 volume name is utf8-mac, but requested path may be a mangled longname. See #2611981.
1296                       So we compare it with the longname from the current volume and if they match
1297                       we overwrite the requested path with the utf8 volume name so that the following
1298                       strcmp can match.
1299                     */
1300                     ucs2_to_charset(vol->v_maccharset, vol->v_macname, temp, AFPVOL_MACNAMELEN + 1);
1301                     if (strcasecmp( path, temp) == 0)
1302                         ucs2_to_charset(CH_UTF8_MAC, vol->v_u8mname, path, AFPVOL_U8MNAMELEN);
1303                 } else {
1304                     /* toUTF8 */
1305                     if (mtoUTF8(vol, path, strlen(path), temp, MAXPATHLEN) == (size_t)-1) {
1306                         afp_errno = AFPERR_PARAM;
1307                         return( NULL );
1308                     }
1309                     strcpy(path, temp);
1310                 }
1311             }
1312             /* check for OS X mangled filename :( */
1313             
1314             t = demangle_osx(vol, path, dir->d_did, &fileid);
1315             if (t != path) {
1316                 ret.u_name = t;
1317                 /* duplicate work but we can't reuse all convert_char we did in demangle_osx 
1318                  * flags weren't the same
1319                  */
1320                  if ( (t = utompath(vol, ret.u_name, fileid, utf8_encoding())) ) {
1321                      /* at last got our view of mac name */
1322                      strcpy(path,t);
1323                  }                    
1324             }
1325         }
1326         if (ret.u_name == NULL) {
1327             if (!(ret.u_name = mtoupath(vol, ret.m_name, dir->d_did, utf8_encoding()))) {
1328                 afp_errno = AFPERR_PARAM;
1329                 return NULL;
1330             }
1331         }
1332         if ( !extend ) {
1333             ucs2_t *tmpname;
1334             cdir = dir->d_child;
1335             scdir = NULL;
1336             if ( cdir && (vol->v_flags & AFPVOL_CASEINSEN) &&
1337                     (size_t)-1 != convert_string_allocate(((ret.m_type == 3)?CH_UTF8_MAC:vol->v_maccharset), 
1338                                                           CH_UCS2, path, strlen(path), (char **)&tmpname) )
1339             {
1340                 while (cdir) {
1341                     if (!cdir->d_m_name_ucs2) {
1342                         LOG(log_error, logtype_afpd, "cname: no UCS2 name for %s (did %u)!!!", cdir->d_m_name, ntohl(cdir->d_did) );
1343                         /* this shouldn't happen !!!! */
1344                         goto noucsfallback;
1345                     }
1346
1347                     if ( strcmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
1348                          break;
1349                     }
1350                     if ( strcasecmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
1351                          scdir = cdir;
1352                     }
1353                     cdir = (cdir == dir->d_child->d_prev) ? NULL :cdir->d_next;
1354                 }
1355                 free(tmpname);
1356             }
1357             else {
1358 noucsfallback:
1359                 if (dir->d_did == DIRDID_ROOT_PARENT) {
1360                   /* 
1361                      root parent (did 1) has one child: the volume. Requests for did=1 with some <name>
1362                      must check against the volume name.
1363                   */
1364                   if (!strcmp(vol->v_dir->d_m_name, ret.m_name))
1365                       cdir = vol->v_dir;
1366                   else
1367                       cdir = NULL;
1368                 }
1369                 else {
1370                   cdir = dirsearch_byname(vol, dir, ret.u_name);
1371                 }
1372             }
1373   
1374             if (cdir == NULL && scdir != NULL) {
1375                 cdir = scdir;
1376                 /* LOG(log_debug, logtype_afpd, "cname: using casediff for %s, (%s = %s)", fullpathname(cdir->d_u_name), cdir->d_m_name, path ); */
1377             }
1378
1379             if ( cdir == NULL ) {
1380                 ++extend;
1381                 /* if dir == curdir it always succeed,
1382                  even if curdir is deleted. 
1383                  it's not a pb because it will fail in extenddir
1384                 */
1385                 if ( movecwd( vol, dir ) < 0 ) {
1386                     /* dir is not valid anymore 
1387                        we delete dir from the cache and abort.
1388                     */
1389                     if ( dir->d_did == DIRDID_ROOT_PARENT) {
1390                          afp_errno = AFPERR_NOOBJ;
1391                          return NULL;
1392                     }
1393                     if (afp_errno == AFPERR_ACCESS)
1394                         return NULL;
1395                     dir_invalidate(vol, dir);
1396                     return NULL;
1397                 }
1398                 cdir = extenddir( vol, dir, &ret );
1399             }
1400
1401         } else {
1402             cdir = extenddir( vol, dir, &ret );
1403         } /* if (!extend) */
1404
1405         if ( cdir == NULL ) {
1406
1407             if ( len > 0 || !ret.u_name ) {
1408                 return NULL;
1409             }
1410
1411         } else {
1412             dir = cdir; 
1413             *path = '\0';
1414         }
1415     } /* for (;;) */
1416 }
1417
1418 /*
1419  * Move curdir to dir, with a possible chdir()
1420  */
1421 int movecwd(const struct vol *vol, struct dir *dir)
1422 {
1423     char path[MAXPATHLEN + 1];
1424     struct dir  *d;
1425     char        *p, *u;
1426     int         n;
1427
1428     if ( dir == curdir ) {
1429         return( 0 );
1430     }
1431     if ( dir->d_did == DIRDID_ROOT_PARENT) {
1432         afp_errno = AFPERR_DID1; /* AFPERR_PARAM;*/
1433         return( -1 );
1434     }
1435
1436     p = path + sizeof(path) - 1;
1437     *p-- = '\0';
1438     *p = '.';
1439     for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) {
1440         u = d->d_u_name;
1441         if (!u) {
1442             /* parent directory is deleted */
1443             afp_errno = AFPERR_NOOBJ;
1444             return -1;
1445         }
1446         n = strlen( u );
1447         if (p -n -1 < path) {
1448             afp_errno = AFPERR_PARAM;
1449             return -1;
1450         }
1451         *--p = '/';
1452         p -= n;
1453         memcpy( p, u, n );
1454     }
1455     if ( d != curdir ) {
1456         n = strlen( vol->v_path );
1457         if (p -n -1 < path) {
1458             afp_errno = AFPERR_PARAM;
1459             return -1;
1460         }
1461         *--p = '/';
1462         p -= n;
1463         memcpy( p, vol->v_path, n );
1464     }
1465     if ( chdir( p ) < 0 ) {
1466         switch (errno) {
1467         case EACCES:
1468         case EPERM:
1469             afp_errno = AFPERR_ACCESS;
1470             break;
1471         default:
1472             afp_errno = AFPERR_NOOBJ;
1473         
1474         }
1475         return( -1 );
1476     }
1477     curdir = dir;
1478     return( 0 );
1479 }
1480
1481 /*
1482  * We can't use unix file's perm to support Apple's inherited protection modes.
1483  * If we aren't the file's owner we can't change its perms when moving it and smb
1484  * nfs,... don't even try.
1485 */
1486 #define AFP_CHECK_ACCESS 
1487
1488 int check_access(char *path, int mode)
1489 {
1490 #ifdef AFP_CHECK_ACCESS
1491 struct maccess ma;
1492 char *p;
1493
1494     p = ad_dir(path);
1495     if (!p)
1496        return -1;
1497
1498     accessmode(p, &ma, curdir, NULL);
1499     if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1500         return -1;
1501     if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1502         return -1;
1503 #endif
1504     return 0;
1505 }
1506
1507 /* --------------------- */
1508 int file_access(struct path *path, int mode)
1509 {
1510 struct maccess ma;
1511
1512     accessmode(path->u_name, &ma, curdir, &path->st);
1513     if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1514         return -1;
1515     if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1516         return -1;
1517     return 0;
1518
1519 }
1520
1521 /* --------------------- */
1522 void setdiroffcnt(struct dir *dir, struct stat *st,  u_int32_t count)
1523 {
1524     dir->offcnt = count;
1525     dir->ctime = st->st_ctime;
1526     dir->d_flags &= ~DIRF_CNID;
1527 }
1528
1529 /* ---------------------  
1530  * is our cached offspring count valid?
1531 */
1532
1533 int diroffcnt(struct dir *dir, struct stat *st)
1534 {
1535     return st->st_ctime == dir->ctime;
1536 }
1537
1538 /* ---------------------  
1539  * is our cached also for reenumerate id?
1540 */
1541
1542 int dirreenumerate(struct dir *dir, struct stat *st)
1543 {
1544     return st->st_ctime == dir->ctime && (dir->d_flags & DIRF_CNID);
1545 }
1546
1547 /* --------------------- */
1548 static int invisible_dots(const struct vol *vol, const char *name)
1549
1550   return vol_inv_dots(vol) && *name  == '.' && strcmp(name, ".") && strcmp(name, "..");
1551 }
1552
1553 /* ------------------------------ 
1554    (".", curdir)
1555    (name, dir) with curdir:name == dir, from afp_enumerate
1556 */
1557
1558 int getdirparams(const struct vol *vol,
1559                  u_int16_t bitmap, struct path *s_path,
1560                  struct dir *dir, 
1561                  char *buf, int *buflen )
1562 {
1563     struct maccess      ma;
1564     struct adouble      ad;
1565     char                *data, *l_nameoff = NULL, *utf_nameoff = NULL;
1566     int                 bit = 0, isad = 0;
1567     u_int32_t           aint;
1568     u_int16_t           ashort;
1569     int                 ret;
1570     u_int32_t           utf8 = 0;
1571     cnid_t              pdid;
1572     struct stat *st = &s_path->st;
1573     char *upath = s_path->u_name;
1574     
1575     if ((bitmap & ((1 << DIRPBIT_ATTR)  |
1576                    (1 << DIRPBIT_CDATE) |
1577                    (1 << DIRPBIT_MDATE) |
1578                    (1 << DIRPBIT_BDATE) |
1579                    (1 << DIRPBIT_FINFO)))) {
1580
1581         ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1582         if ( !ad_metadata( upath, ADFLAGS_DIR, &ad) ) {
1583             isad = 1;
1584         }
1585     }
1586     
1587     if ( dir->d_did == DIRDID_ROOT) {
1588         pdid = DIRDID_ROOT_PARENT;
1589     } else if (dir->d_did == DIRDID_ROOT_PARENT) {
1590         pdid = 0;
1591     } else {
1592         pdid = dir->d_parent->d_did;
1593     }
1594     
1595     data = buf;
1596     while ( bitmap != 0 ) {
1597         while (( bitmap & 1 ) == 0 ) {
1598             bitmap = bitmap>>1;
1599             bit++;
1600         }
1601
1602         switch ( bit ) {
1603         case DIRPBIT_ATTR :
1604             if ( isad ) {
1605                 ad_getattr(&ad, &ashort);
1606             } else if (invisible_dots(vol, dir->d_u_name)) {
1607                 ashort = htons(ATTRBIT_INVISIBLE);
1608             } else
1609                 ashort = 0;
1610             ashort |= htons(ATTRBIT_SHARED);
1611             memcpy( data, &ashort, sizeof( ashort ));
1612             data += sizeof( ashort );
1613             break;
1614
1615         case DIRPBIT_PDID :
1616             memcpy( data, &pdid, sizeof( pdid ));
1617             data += sizeof( pdid );
1618             break;
1619
1620         case DIRPBIT_CDATE :
1621             if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
1622                 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1623             memcpy( data, &aint, sizeof( aint ));
1624             data += sizeof( aint );
1625             break;
1626
1627         case DIRPBIT_MDATE :
1628             aint = AD_DATE_FROM_UNIX(st->st_mtime);
1629             memcpy( data, &aint, sizeof( aint ));
1630             data += sizeof( aint );
1631             break;
1632
1633         case DIRPBIT_BDATE :
1634             if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
1635                 aint = AD_DATE_START;
1636             memcpy( data, &aint, sizeof( aint ));
1637             data += sizeof( aint );
1638             break;
1639
1640         case DIRPBIT_FINFO :
1641             if ( isad ) {
1642                 memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
1643             } else { /* no appledouble */
1644                 memset( data, 0, 32 );
1645                 /* set default view -- this also gets done in ad_open() */
1646                 ashort = htons(FINDERINFO_CLOSEDVIEW);
1647                 memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
1648
1649                 /* dot files are by default visible */
1650                 if (invisible_dots(vol, dir->d_u_name)) {
1651                     ashort = htons(FINDERINFO_INVISIBLE);
1652                     memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
1653                 }
1654             }
1655             data += 32;
1656             break;
1657
1658         case DIRPBIT_LNAME :
1659             if (dir->d_m_name) /* root of parent can have a null name */
1660                 l_nameoff = data;
1661             else
1662                 memset(data, 0, sizeof(u_int16_t));
1663             data += sizeof( u_int16_t );
1664             break;
1665
1666         case DIRPBIT_SNAME :
1667             memset(data, 0, sizeof(u_int16_t));
1668             data += sizeof( u_int16_t );
1669             break;
1670
1671         case DIRPBIT_DID :
1672             memcpy( data, &dir->d_did, sizeof( aint ));
1673             data += sizeof( aint );
1674             break;
1675
1676         case DIRPBIT_OFFCNT :
1677             ashort = 0;
1678             /* this needs to handle current directory access rights */
1679             if (diroffcnt(dir, st)) {
1680                 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1681             }
1682             else if ((ret = for_each_dirent(vol, upath, NULL,NULL)) >= 0) {
1683                 setdiroffcnt(dir, st,  ret);
1684                 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1685             }
1686             ashort = htons( ashort );
1687             memcpy( data, &ashort, sizeof( ashort ));
1688             data += sizeof( ashort );
1689             break;
1690
1691         case DIRPBIT_UID :
1692             aint = htonl(st->st_uid);
1693             memcpy( data, &aint, sizeof( aint ));
1694             data += sizeof( aint );
1695             break;
1696
1697         case DIRPBIT_GID :
1698             aint = htonl(st->st_gid);
1699             memcpy( data, &aint, sizeof( aint ));
1700             data += sizeof( aint );
1701             break;
1702
1703         case DIRPBIT_ACCESS :
1704             accessmode( upath, &ma, dir , st);
1705
1706             *data++ = ma.ma_user;
1707             *data++ = ma.ma_world;
1708             *data++ = ma.ma_group;
1709             *data++ = ma.ma_owner;
1710             break;
1711
1712             /* Client has requested the ProDOS information block.
1713                Just pass back the same basic block for all
1714                directories. <shirsch@ibm.net> */
1715         case DIRPBIT_PDINFO :                     
1716             if (afp_version >= 30) { /* UTF8 name */
1717                 utf8 = kTextEncodingUTF8;
1718                 if (dir->d_m_name) /* root of parent can have a null name */
1719                     utf_nameoff = data;
1720                 else
1721                     memset(data, 0, sizeof(u_int16_t));
1722                 data += sizeof( u_int16_t );
1723                 aint = 0;
1724                 memcpy(data, &aint, sizeof( aint ));
1725                 data += sizeof( aint );
1726             }
1727             else { /* ProDOS Info Block */
1728                 *data++ = 0x0f;
1729                 *data++ = 0;
1730                 ashort = htons( 0x0200 );
1731                 memcpy( data, &ashort, sizeof( ashort ));
1732                 data += sizeof( ashort );
1733                 memset( data, 0, sizeof( ashort ));
1734                 data += sizeof( ashort );
1735             }
1736             break;
1737
1738         case DIRPBIT_UNIXPR :
1739             aint = htonl(st->st_uid);
1740             memcpy( data, &aint, sizeof( aint ));
1741             data += sizeof( aint );
1742             aint = htonl(st->st_gid);
1743             memcpy( data, &aint, sizeof( aint ));
1744             data += sizeof( aint );
1745         
1746             aint = st->st_mode;
1747             aint = htonl ( aint & ~S_ISGID );  /* Remove SGID, OSX doesn't like it ... */
1748             memcpy( data, &aint, sizeof( aint ));
1749             data += sizeof( aint );
1750
1751             accessmode( upath, &ma, dir , st);
1752
1753             *data++ = ma.ma_user;
1754             *data++ = ma.ma_world;
1755             *data++ = ma.ma_group;
1756             *data++ = ma.ma_owner;
1757             break;
1758             
1759         default :
1760             if ( isad ) {
1761                 ad_close_metadata( &ad );
1762             }
1763             return( AFPERR_BITMAP );
1764         }
1765         bitmap = bitmap>>1;
1766         bit++;
1767     }
1768     if ( l_nameoff ) {
1769         ashort = htons( data - buf );
1770         memcpy( l_nameoff, &ashort, sizeof( ashort ));
1771         data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, 0);
1772     }
1773     if ( utf_nameoff ) {
1774         ashort = htons( data - buf );
1775         memcpy( utf_nameoff, &ashort, sizeof( ashort ));
1776         data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, utf8);
1777     }
1778     if ( isad ) {
1779         ad_close_metadata( &ad );
1780     }
1781     *buflen = data - buf;
1782     return( AFP_OK );
1783 }
1784
1785 /* ----------------------------- */
1786 int path_error(struct path *path, int error)
1787 {
1788 /* - a dir with access error
1789  * - no error it's a file
1790  * - file not found
1791  */
1792     if (path_isadir(path))
1793         return afp_errno;
1794     if (path->st_valid && path->st_errno)
1795         return error;
1796     return AFPERR_BADTYPE ;
1797 }
1798
1799 /* ----------------------------- */
1800 int afp_setdirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1801 {
1802     struct vol  *vol;
1803     struct dir  *dir;
1804     struct path *path;
1805     u_int16_t   vid, bitmap;
1806     u_int32_t   did;
1807     int         rc;
1808
1809     *rbuflen = 0;
1810     ibuf += 2;
1811     memcpy( &vid, ibuf, sizeof( vid ));
1812     ibuf += sizeof( vid );
1813
1814     if (NULL == ( vol = getvolbyvid( vid )) ) {
1815         return( AFPERR_PARAM );
1816     }
1817
1818     if (vol->v_flags & AFPVOL_RO)
1819         return AFPERR_VLOCK;
1820
1821     memcpy( &did, ibuf, sizeof( did ));
1822     ibuf += sizeof( int );
1823
1824     if (NULL == ( dir = dirlookup( vol, did )) ) {
1825         return afp_errno;
1826     }
1827
1828     memcpy( &bitmap, ibuf, sizeof( bitmap ));
1829     bitmap = ntohs( bitmap );
1830     ibuf += sizeof( bitmap );
1831
1832     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1833         return get_afp_errno(AFPERR_NOOBJ); 
1834     }
1835
1836     if ( *path->m_name != '\0' ) {
1837         rc = path_error(path, AFPERR_NOOBJ);
1838         /* maybe we are trying to set perms back */
1839         if (rc != AFPERR_ACCESS)
1840             return rc;
1841     }
1842
1843     /*
1844      * If ibuf is odd, make it even.
1845      */
1846     if ((u_long)ibuf & 1 ) {
1847         ibuf++;
1848     }
1849
1850     if (AFP_OK == ( rc = setdirparams(vol, path, bitmap, ibuf )) ) {
1851         setvoltime(obj, vol );
1852     }
1853     return( rc );
1854 }
1855
1856 /*
1857  * cf AFP3.0.pdf page 244 for change_mdate and change_parent_mdate logic  
1858  *
1859  * assume path == '\0' eg. it's a directory in canonical form
1860 */
1861
1862 struct path Cur_Path = {
1863     0,
1864     "",  /* mac name */
1865     ".", /* unix name */
1866     0,   /* id */
1867     NULL,/* struct dir */
1868     0,   /* stat is not set */
1869 };
1870
1871 /* ------------------ */
1872 static int set_dir_errors(struct path *path, const char *where, int err)
1873 {
1874     switch ( err ) {
1875     case EPERM :
1876     case EACCES :
1877         return AFPERR_ACCESS;
1878     case EROFS :
1879         return AFPERR_VLOCK;
1880     }
1881     LOG(log_error, logtype_afpd, "setdirparam(%s): %s: %s", fullpathname(path->u_name), where, strerror(err) );
1882     return AFPERR_PARAM;
1883 }
1884  
1885 /* ------------------ */
1886 int setdirparams(const struct vol *vol, 
1887                  struct path *path, u_int16_t d_bitmap, char *buf )
1888 {
1889     struct maccess      ma;
1890     struct adouble      ad;
1891     struct utimbuf      ut;
1892     struct timeval      tv;
1893
1894     char                *upath;
1895     struct dir          *dir;
1896     int                 bit, isad = 1;
1897     int                 cdate, bdate;
1898     int                 owner, group;
1899     u_int16_t           ashort, bshort;
1900     int                 err = AFP_OK;
1901     int                 change_mdate = 0;
1902     int                 change_parent_mdate = 0;
1903     int                 newdate = 0;
1904     u_int16_t           bitmap = d_bitmap;
1905     u_char              finder_buf[32];
1906     u_int32_t           upriv;
1907     mode_t              mpriv = 0;        
1908     u_int16_t           upriv_bit = 0;
1909
1910     bit = 0;
1911     upath = path->u_name;
1912     dir   = path->d_dir;
1913     while ( bitmap != 0 ) {
1914         while (( bitmap & 1 ) == 0 ) {
1915             bitmap = bitmap>>1;
1916             bit++;
1917         }
1918
1919         switch( bit ) {
1920         case DIRPBIT_ATTR :
1921             change_mdate = 1;
1922             memcpy( &ashort, buf, sizeof( ashort ));
1923             buf += sizeof( ashort );
1924             break;
1925         case DIRPBIT_CDATE :
1926             change_mdate = 1;
1927             memcpy(&cdate, buf, sizeof(cdate));
1928             buf += sizeof( cdate );
1929             break;
1930         case DIRPBIT_MDATE :
1931             memcpy(&newdate, buf, sizeof(newdate));
1932             buf += sizeof( newdate );
1933             break;
1934         case DIRPBIT_BDATE :
1935             change_mdate = 1;
1936             memcpy(&bdate, buf, sizeof(bdate));
1937             buf += sizeof( bdate );
1938             break;
1939         case DIRPBIT_FINFO :
1940             change_mdate = 1;
1941             memcpy( finder_buf, buf, 32 );
1942             buf += 32;
1943             break;
1944         case DIRPBIT_UID :      /* What kind of loser mounts as root? */
1945             change_parent_mdate = 1;
1946             memcpy( &owner, buf, sizeof(owner));
1947             buf += sizeof( owner );
1948             break;
1949         case DIRPBIT_GID :
1950             change_parent_mdate = 1;
1951             memcpy( &group, buf, sizeof( group ));
1952             buf += sizeof( group );
1953             break;
1954         case DIRPBIT_ACCESS :
1955             change_mdate = 1;
1956             change_parent_mdate = 1;
1957             ma.ma_user = *buf++;
1958             ma.ma_world = *buf++;
1959             ma.ma_group = *buf++;
1960             ma.ma_owner = *buf++;
1961             mpriv = mtoumode( &ma ) | vol->v_dperm;
1962             if (dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
1963                 err = set_dir_errors(path, "setdirmode", errno);
1964                 bitmap = 0;
1965             }
1966             break;
1967         /* Ignore what the client thinks we should do to the
1968            ProDOS information block.  Skip over the data and
1969            report nothing amiss. <shirsch@ibm.net> */
1970         case DIRPBIT_PDINFO :
1971             if (afp_version < 30) {
1972                 buf += 6;
1973             }
1974             else {
1975                 err = AFPERR_BITMAP;
1976                 bitmap = 0;
1977             }
1978             break;
1979         case DIRPBIT_UNIXPR :
1980             if (vol_unix_priv(vol)) {
1981                 memcpy( &owner, buf, sizeof(owner)); /* FIXME need to change owner too? */
1982                 buf += sizeof( owner );
1983                 memcpy( &group, buf, sizeof( group ));
1984                 buf += sizeof( group );
1985
1986                 change_mdate = 1;
1987                 change_parent_mdate = 1;
1988                 memcpy( &upriv, buf, sizeof( upriv ));
1989                 buf += sizeof( upriv );
1990                 upriv = ntohl (upriv) | vol->v_dperm;
1991                 if (dir_rx_set(upriv)) {
1992                     /* maybe we are trying to set perms back */
1993                     if ( setdirunixmode(vol, upath, upriv) < 0 ) {
1994                         bitmap = 0;
1995                         err = set_dir_errors(path, "setdirunixmode", errno);
1996                     }
1997                 }
1998                 else {
1999                     /* do it later */
2000                     upriv_bit = 1;
2001                 }
2002                 break;
2003             }
2004             /* fall through */
2005         default :
2006             err = AFPERR_BITMAP;
2007             bitmap = 0;
2008             break;
2009         }
2010
2011         bitmap = bitmap>>1;
2012         bit++;
2013     }
2014     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2015
2016     if (ad_open_metadata( upath, vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad) < 0) {
2017         /*
2018          * Check to see what we're trying to set.  If it's anything
2019          * but ACCESS, UID, or GID, give an error.  If it's any of those
2020          * three, we don't need the ad to be open, so just continue.
2021          *
2022          * note: we also don't need to worry about mdate. also, be quiet
2023          *       if we're using the noadouble option.
2024          */
2025         if (!vol_noadouble(vol) && (d_bitmap &
2026                                     ~((1<<DIRPBIT_ACCESS)|(1<<DIRPBIT_UNIXPR)|
2027                                       (1<<DIRPBIT_UID)|(1<<DIRPBIT_GID)|
2028                                       (1<<DIRPBIT_MDATE)|(1<<DIRPBIT_PDINFO)))) {
2029             return AFPERR_ACCESS;
2030         }
2031
2032         isad = 0;
2033     } else {
2034         /*
2035          * Check to see if a create was necessary. If it was, we'll want
2036          * to set our name, etc.
2037          */
2038         if ( (ad_get_HF_flags( &ad ) & O_CREAT)) {
2039             ad_setname(&ad, curdir->d_m_name);
2040         }
2041     }
2042
2043     bit = 0;
2044     bitmap = d_bitmap;
2045     while ( bitmap != 0 ) {
2046         while (( bitmap & 1 ) == 0 ) {
2047             bitmap = bitmap>>1;
2048             bit++;
2049         }
2050
2051         switch( bit ) {
2052         case DIRPBIT_ATTR :
2053             if (isad) {
2054                 ad_getattr(&ad, &bshort);
2055                 if ((bshort & htons(ATTRBIT_INVISIBLE)) != 
2056                     (ashort & htons(ATTRBIT_INVISIBLE) & htons(ATTRBIT_SETCLR)) )
2057                     change_parent_mdate = 1;
2058                 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
2059                     bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
2060                 } else {
2061                     bshort &= ~ashort;
2062                 }
2063                 ad_setattr(&ad, bshort);
2064             }
2065             break;
2066         case DIRPBIT_CDATE :
2067             if (isad) {
2068                 ad_setdate(&ad, AD_DATE_CREATE, cdate);
2069             }
2070             break;
2071         case DIRPBIT_MDATE :
2072             break;
2073         case DIRPBIT_BDATE :
2074             if (isad) {
2075                 ad_setdate(&ad, AD_DATE_BACKUP, bdate);
2076             }
2077             break;
2078         case DIRPBIT_FINFO :
2079             if (isad) {
2080                 /* Fixes #2802236 */
2081                 u_int16_t *fflags = (u_int16_t *)(finder_buf + FINDERINFO_FRFLAGOFF);
2082                 *fflags &= htons(~FINDERINFO_ISHARED);
2083                 /* #2802236 end */
2084                 if (  dir->d_did == DIRDID_ROOT ) {
2085                     /*
2086                      * Alright, we admit it, this is *really* sick!
2087                      * The 4 bytes that we don't copy, when we're dealing
2088                      * with the root of a volume, are the directory's
2089                      * location information. This eliminates that annoying
2090                      * behavior one sees when mounting above another mount
2091                      * point.
2092                      */
2093                     memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 10 );
2094                     memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, finder_buf + 14, 18 );
2095                 } else {
2096                     memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 32 );
2097                 }
2098             }
2099             break;
2100         case DIRPBIT_UID :      /* What kind of loser mounts as root? */
2101             if ( (dir->d_did == DIRDID_ROOT) &&
2102                     (setdeskowner( ntohl(owner), -1 ) < 0)) {
2103                 err = set_dir_errors(path, "setdeskowner", errno);
2104                 if (isad && err == AFPERR_PARAM) {
2105                     err = AFP_OK; /* ???*/
2106                 }
2107                 else {
2108                     goto setdirparam_done;
2109                 }
2110             }
2111             if ( setdirowner(vol, upath, ntohl(owner), -1 ) < 0 ) {
2112                 err = set_dir_errors(path, "setdirowner", errno);
2113                 goto setdirparam_done;
2114             }
2115             break;
2116         case DIRPBIT_GID :
2117             if (dir->d_did == DIRDID_ROOT)
2118                 setdeskowner( -1, ntohl(group) ); 
2119             if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2120                 err = set_dir_errors(path, "setdirowner", errno);
2121                 goto setdirparam_done;
2122             }
2123             break;
2124         case DIRPBIT_ACCESS :
2125             if (dir->d_did == DIRDID_ROOT) {
2126                 setdeskmode(mpriv);
2127                 if (!dir_rx_set(mpriv)) {
2128                     /* we can't remove read and search for owner on volume root */
2129                     err = AFPERR_ACCESS;
2130                     goto setdirparam_done;
2131                 }
2132             }
2133
2134             if (!dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2135                 err = set_dir_errors(path, "setdirmode", errno);
2136                 goto setdirparam_done;
2137             }
2138             break;
2139         case DIRPBIT_PDINFO :
2140             if (afp_version >= 30) {
2141                 err = AFPERR_BITMAP;
2142                 goto setdirparam_done;
2143             }
2144             break;
2145         case DIRPBIT_UNIXPR :
2146             if (vol_unix_priv(vol)) {
2147                 if (dir->d_did == DIRDID_ROOT) {
2148                     if (!dir_rx_set(upriv)) {
2149                         /* we can't remove read and search for owner on volume root */
2150                         err = AFPERR_ACCESS;
2151                         goto setdirparam_done;
2152                     }
2153                     setdeskowner( -1, ntohl(group) ); 
2154                     setdeskmode( upriv );
2155                 }
2156                 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2157                     err = set_dir_errors(path, "setdirowner", errno);
2158                     goto setdirparam_done;
2159                 }
2160
2161                 if ( upriv_bit && setdirunixmode(vol, upath, upriv) < 0 ) {
2162                     err = set_dir_errors(path, "setdirunixmode", errno);
2163                     goto setdirparam_done;
2164                 }
2165             }
2166             else {
2167                 err = AFPERR_BITMAP;
2168                 goto setdirparam_done;
2169             }
2170             break;
2171         default :
2172             err = AFPERR_BITMAP;
2173             goto setdirparam_done;
2174             break;
2175         }
2176
2177         bitmap = bitmap>>1;
2178         bit++;
2179     }
2180
2181 setdirparam_done:
2182     if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
2183        newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2184     }
2185     if (newdate) {
2186        if (isad)
2187           ad_setdate(&ad, AD_DATE_MODIFY, newdate);
2188        ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
2189        utime(upath, &ut);
2190     }
2191
2192     if ( isad ) {
2193         if (path->st_valid && !path->st_errno) {
2194             struct stat *st = &path->st;
2195
2196             if (dir && dir->d_parent) {
2197                 ad_setid(&ad, st->st_dev, st->st_ino,  dir->d_did, dir->d_parent->d_did, vol->v_stamp);
2198             }
2199         }
2200         ad_flush( &ad);
2201         ad_close_metadata( &ad);
2202     }
2203
2204     if (change_parent_mdate && dir->d_did != DIRDID_ROOT
2205             && gettimeofday(&tv, NULL) == 0) {
2206        if (!movecwd(vol, dir->d_parent)) {
2207            newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2208            /* be careful with bitmap because now dir is null */
2209            bitmap = 1<<DIRPBIT_MDATE;
2210            setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
2211            /* should we reset curdir ?*/
2212        }
2213     }
2214
2215     return err;
2216 }
2217
2218 int afp_syncdir(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2219 {
2220 #ifdef HAVE_DIRFD
2221     DIR                  *dp;
2222 #endif
2223     int                  dfd;
2224     struct vol           *vol;
2225     struct dir           *dir;
2226     u_int32_t            did;
2227     u_int16_t            vid;
2228
2229     *rbuflen = 0;
2230     ibuf += 2;
2231
2232     memcpy( &vid, ibuf, sizeof( vid ));
2233     ibuf += sizeof( vid );
2234     if (NULL == (vol = getvolbyvid( vid )) ) {
2235         return( AFPERR_PARAM );
2236     }
2237
2238     memcpy( &did, ibuf, sizeof( did ));
2239     ibuf += sizeof( did );
2240
2241     /* 
2242      * Here's the deal:
2243      * if it's CNID 2 our only choice to meet the specs is call sync.
2244      * For any other CNID just sync that dir. To my knowledge the
2245      * intended use of FPSyncDir is to sync the volume so all we're
2246      * ever going to see here is probably CNID 2. Anyway, we' prepared.
2247     */
2248
2249     if ( ntohl(did) == 2 ) {
2250         sync();
2251     } else {
2252         if (NULL == ( dir = dirlookup( vol, did )) ) {
2253             return afp_errno; /* was AFPERR_NOOBJ */
2254         }
2255         
2256         if (movecwd( vol, dir ) < 0 )
2257             return ( AFPERR_NOOBJ ); 
2258         
2259         /*
2260          * Assuming only OSens that have dirfd also may require fsyncing directories
2261          * in order to flush metadata e.g. Linux.
2262          */
2263     
2264 #ifdef HAVE_DIRFD
2265         if (NULL == ( dp = opendir( "." )) ) {
2266             switch( errno ) {
2267             case ENOENT :
2268                 return( AFPERR_NOOBJ );
2269             case EACCES :
2270             return( AFPERR_ACCESS );
2271             default :
2272                 return( AFPERR_PARAM );
2273             }
2274         }
2275         
2276         LOG(log_debug, logtype_afpd, "afp_syncdir: dir: '%s'", dir->d_u_name);
2277         
2278         dfd = dirfd( dp );
2279         if ( fsync ( dfd ) < 0 )
2280             LOG(log_error, logtype_afpd, "afp_syncdir(%s):  %s",
2281                 dir->d_u_name, strerror(errno) );
2282         closedir(dp); /* closes dfd too */
2283 #endif
2284         
2285         if ( -1 == (dfd = open(vol->ad_path(".", ADFLAGS_DIR), O_RDWR))) {
2286             switch( errno ) {
2287             case ENOENT:
2288                 return( AFPERR_NOOBJ );
2289         case EACCES:
2290             return( AFPERR_ACCESS );
2291             default:
2292                 return( AFPERR_PARAM );
2293             }        
2294         }
2295         
2296         LOG(log_debug, logtype_afpd, "afp_syncdir: ad-file: '%s'",
2297             vol->ad_path(".", ADFLAGS_DIR) );
2298         
2299         if ( fsync(dfd) < 0 )
2300             LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2301                 vol->ad_path(dir->d_u_name, ADFLAGS_DIR), strerror(errno) );
2302         close(dfd);
2303     }
2304
2305     return ( AFP_OK );
2306 }
2307
2308 int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2309 {
2310     struct adouble      ad;
2311     struct vol          *vol;
2312     struct dir          *dir;
2313     char                *upath;
2314     struct path         *s_path;
2315     u_int32_t           did;
2316     u_int16_t           vid;
2317     int                 err;
2318     
2319     *rbuflen = 0;
2320     ibuf += 2;
2321
2322     memcpy( &vid, ibuf, sizeof( vid ));
2323     ibuf += sizeof( vid );
2324     if (NULL == ( vol = getvolbyvid( vid )) ) {
2325         return( AFPERR_PARAM );
2326     }
2327
2328     if (vol->v_flags & AFPVOL_RO)
2329         return AFPERR_VLOCK;
2330
2331     memcpy( &did, ibuf, sizeof( did ));
2332     ibuf += sizeof( did );
2333     if (NULL == ( dir = dirlookup( vol, did )) ) {
2334         return afp_errno; /* was AFPERR_NOOBJ */
2335     }
2336     /* for concurrent access we need to be sure we are not in the
2337      * folder we want to create...
2338     */
2339     movecwd(vol, dir);
2340     
2341     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
2342         return get_afp_errno(AFPERR_PARAM);
2343     }
2344     /* cname was able to move curdir to it! */
2345     if (*s_path->m_name == '\0')
2346         return AFPERR_EXIST;
2347
2348     upath = s_path->u_name;
2349
2350     if (AFP_OK != (err = netatalk_mkdir( upath))) {
2351         return err;
2352     }
2353
2354     if (of_stat(s_path) < 0) {
2355         return AFPERR_MISC;
2356     }
2357     curdir->offcnt++;
2358     if ((dir = adddir( vol, curdir, s_path)) == NULL) {
2359         return AFPERR_MISC;
2360     }
2361
2362     if ( movecwd( vol, dir ) < 0 ) {
2363         return( AFPERR_PARAM );
2364     }
2365
2366     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2367     if (ad_open_metadata( ".", vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad ) < 0)  {
2368         if (vol_noadouble(vol))
2369             goto createdir_done;
2370         return( AFPERR_ACCESS );
2371     }
2372     ad_setname(&ad, s_path->m_name);
2373     ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
2374
2375     ad_flush( &ad);
2376     ad_close_metadata( &ad);
2377
2378 createdir_done:
2379 #ifdef HAVE_NFSv4_ACLS
2380     /* FIXME: are we really inside the created dir? */
2381     addir_inherit_acl(vol);
2382 #endif
2383
2384     memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
2385     *rbuflen = sizeof( u_int32_t );
2386     setvoltime(obj, vol );
2387     return( AFP_OK );
2388 }
2389
2390 /*
2391  * dst       new unix filename (not a pathname)
2392  * newname   new mac name
2393  * newparent curdir
2394  *
2395 */
2396 int renamedir(const struct vol *vol, char *src, char *dst, 
2397     struct dir *dir, 
2398     struct dir *newparent, 
2399     char *newname)
2400 {
2401     struct adouble      ad;
2402     struct dir          *parent;
2403     char                *buf;
2404     int                 len, err;
2405         
2406     /* existence check moved to afp_moveandrename */
2407     if ( unix_rename( src, dst ) < 0 ) {
2408         switch ( errno ) {
2409         case ENOENT :
2410             return( AFPERR_NOOBJ );
2411         case EACCES :
2412             return( AFPERR_ACCESS );
2413         case EROFS:
2414             return AFPERR_VLOCK;
2415         case EINVAL:
2416             /* tried to move directory into a subdirectory of itself */
2417             return AFPERR_CANTMOVE;
2418         case EXDEV:
2419             /* this needs to copy and delete. bleah. that means we have
2420              * to deal with entire directory hierarchies. */
2421             if ((err = copydir(vol, src, dst)) < 0) {
2422                 deletedir(dst);
2423                 return err;
2424             }
2425             if ((err = deletedir(src)) < 0)
2426                 return err;
2427             break;
2428         default :
2429             return( AFPERR_PARAM );
2430         }
2431     }
2432
2433     vol->vfs->vfs_renamedir(vol, src, dst);
2434
2435     len = strlen( newname );
2436     /* rename() succeeded so we need to update our tree even if we can't open
2437      * metadata
2438     */
2439     
2440     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2441
2442     if (!ad_open_metadata( dst, ADFLAGS_DIR, 0, &ad)) {
2443         ad_setname(&ad, newname);
2444         ad_flush( &ad);
2445         ad_close_metadata( &ad);
2446     }
2447
2448     dir_hash_del(vol, dir);
2449     if (dir->d_m_name == dir->d_u_name)
2450         dir->d_u_name = NULL;
2451
2452     if ((buf = (char *) realloc( dir->d_m_name, len + 1 )) == NULL ) {
2453         LOG(log_error, logtype_afpd, "renamedir: realloc mac name: %s", strerror(errno) );
2454         /* FIXME : fatal ? */
2455         return AFPERR_MISC;
2456     }
2457     dir->d_m_name = buf;
2458     strcpy( dir->d_m_name, newname );
2459
2460     if (newname == dst) {
2461         free(dir->d_u_name);
2462         dir->d_u_name = dir->d_m_name;
2463     }
2464     else {
2465         if ((buf = (char *) realloc( dir->d_u_name, strlen(dst) + 1 )) == NULL ) {
2466             LOG(log_error, logtype_afpd, "renamedir: realloc unix name: %s", strerror(errno) );
2467             return AFPERR_MISC;
2468         }
2469         dir->d_u_name = buf;
2470         strcpy( dir->d_u_name, dst );
2471     }
2472
2473     if (dir->d_m_name_ucs2)
2474         free(dir->d_m_name_ucs2);
2475
2476     dir->d_m_name_ucs2 = NULL;
2477     if ((size_t)-1 == convert_string_allocate((utf8_encoding())?CH_UTF8_MAC:vol->v_maccharset, CH_UCS2, dir->d_m_name, strlen(dir->d_m_name), (char**)&dir->d_m_name_ucs2))
2478         dir->d_m_name_ucs2 = NULL;
2479
2480     if (( parent = dir->d_parent ) == NULL ) {
2481         return( AFP_OK );
2482     }
2483     if ( parent == newparent ) {
2484         hash_alloc_insert(vol->v_hash, dir, dir);
2485         return( AFP_OK );
2486     }
2487
2488     /* detach from old parent and add to new one. */
2489     dirchildremove(parent, dir);
2490     dir->d_parent = newparent;
2491     dirchildadd(vol, newparent, dir);
2492     return( AFP_OK );
2493 }
2494
2495 /* delete an empty directory */
2496 int deletecurdir(const struct vol *vol)
2497 {
2498     struct dirent *de;
2499     struct stat st;
2500     struct dir  *fdir;
2501     DIR *dp;
2502     struct adouble      ad;
2503     u_int16_t           ashort;
2504     int err;
2505
2506     if ( curdir->d_parent == NULL ) {
2507         return( AFPERR_ACCESS );
2508     }
2509
2510     fdir = curdir;
2511
2512     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2513     if ( ad_metadata( ".", ADFLAGS_DIR, &ad) == 0 ) {
2514
2515         ad_getattr(&ad, &ashort);
2516         ad_close( &ad, ADFLAGS_HF );
2517         if ((ashort & htons(ATTRBIT_NODELETE))) {
2518             return  AFPERR_OLOCK;
2519         }
2520     }
2521     err = vol->vfs->vfs_deletecurdir(vol);
2522     if (err) {
2523         return err;
2524     }
2525
2526     /* now get rid of dangling symlinks */
2527     if ((dp = opendir("."))) {
2528         while ((de = readdir(dp))) {
2529             /* skip this and previous directory */
2530             if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
2531                 continue;
2532
2533             /* bail if it's not a symlink */
2534             if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
2535                 closedir(dp);
2536                 return AFPERR_DIRNEMPT;
2537             }
2538
2539             if ((err = netatalk_unlink(de->d_name))) {
2540                 closedir(dp);
2541                 return err;
2542             }
2543         }
2544     }
2545
2546     if ( movecwd( vol, curdir->d_parent ) < 0 ) {
2547         err = afp_errno;
2548         goto delete_done;
2549     }
2550
2551     if ( !(err = netatalk_rmdir(fdir->d_u_name))) {
2552         dirchildremove(curdir, fdir);
2553         cnid_delete(vol->v_cdb, fdir->d_did);
2554         dir_remove( vol, fdir );
2555         err = AFP_OK;
2556     }
2557 delete_done:
2558     if (dp) {
2559         /* inode is used as key for cnid.
2560          * Close the descriptor only after cnid_delete
2561          * has been called. 
2562         */
2563         closedir(dp);
2564     }
2565     return err;
2566 }
2567
2568 int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2569 {
2570     struct passwd       *pw;
2571     struct group        *gr;
2572     char                *name;
2573     u_int32_t           id;
2574     int                 len, sfunc;
2575     int         utf8 = 0;
2576     uuidtype_t          type;
2577
2578     ibuf++;
2579     sfunc = (unsigned char) *ibuf++;
2580     *rbuflen = 0;
2581
2582
2583     if (sfunc >= 3 && sfunc <= 6) {
2584         if (afp_version < 30) {
2585             return( AFPERR_PARAM );
2586         }
2587         utf8 = 1;
2588     }
2589
2590         switch ( sfunc ) {
2591         case 1 :
2592         case 3 :/* unicode */
2593         memcpy( &id, ibuf, sizeof( id ));
2594         id = ntohl(id);
2595         if ( id != 0 ) {
2596             if (( pw = getpwuid( id )) == NULL ) {
2597                 return( AFPERR_NOITEM );
2598             }
2599             len = convert_string_allocate( obj->options.unixcharset, ((!utf8)?obj->options.maccharset:CH_UTF8_MAC),
2600                                             pw->pw_name, strlen(pw->pw_name), &name);
2601         } else {
2602             len = 0;
2603             name = NULL;
2604         }
2605             break;
2606         case 2 :
2607         case 4 : /* unicode */
2608         memcpy( &id, ibuf, sizeof( id ));
2609         id = ntohl(id);
2610         if ( id != 0 ) {
2611             if (NULL == ( gr = (struct group *)getgrgid( id ))) {
2612                 return( AFPERR_NOITEM );
2613             }
2614             len = convert_string_allocate( obj->options.unixcharset, (!utf8)?obj->options.maccharset:CH_UTF8_MAC,
2615                                             gr->gr_name, strlen(gr->gr_name), &name);
2616         } else {
2617             len = 0;
2618             name = NULL;
2619         }
2620             break;
2621 #ifdef HAVE_NFSv4_ACLS
2622     case 5 : /* UUID -> username */
2623     case 6 : /* UUID -> groupname */
2624         if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2625             return AFPERR_PARAM;
2626         LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request");
2627         len = getnamefromuuid( ibuf, &name, &type);
2628         if (len != 0)           /* its a error code, not len */
2629             return AFPERR_NOITEM;
2630         if (type == UUID_USER) {
2631             if (( pw = getpwnam( name )) == NULL )
2632                 return( AFPERR_NOITEM );
2633             LOG(log_debug, logtype_afpd, "afp_mapid: name:%s -> uid:%d", name, pw->pw_uid);
2634             id = htonl(UUID_USER);
2635             memcpy( rbuf, &id, sizeof( id ));
2636             id = htonl( pw->pw_uid);
2637             rbuf += sizeof( id );
2638             memcpy( rbuf, &id, sizeof( id ));
2639             rbuf += sizeof( id );
2640             *rbuflen = 2 * sizeof( id );
2641         } else {                /* type == UUID_GROUP */
2642             if (( gr = getgrnam( name )) == NULL )
2643                 return( AFPERR_NOITEM );
2644             LOG(log_debug, logtype_afpd, "afp_mapid: group:%s -> gid:%d", name, gr->gr_gid);
2645             id = htonl(UUID_GROUP);
2646             memcpy( rbuf, &id, sizeof( id ));
2647             rbuf += sizeof( id );
2648             id = htonl( gr->gr_gid);
2649             memcpy( rbuf, &id, sizeof( id ));
2650             rbuf += sizeof( id );
2651             *rbuflen = 2 * sizeof( id );
2652         }
2653         break;
2654 #endif
2655         default :
2656             return( AFPERR_PARAM );
2657         }
2658
2659         if (name)
2660             len = strlen( name );
2661         
2662     if (utf8) {
2663         u_int16_t tp = htons(len);
2664         memcpy(rbuf, &tp, sizeof(tp));
2665         rbuf += sizeof(tp);
2666         *rbuflen += 2;
2667     }
2668     else {
2669         *rbuf++ = len;
2670         *rbuflen += 1;
2671     }
2672     if ( len > 0 ) {
2673         memcpy( rbuf, name, len );
2674     }
2675     *rbuflen += len;
2676     if (name)
2677         free(name);
2678     return( AFP_OK );
2679 }
2680
2681 int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2682 {
2683     struct passwd       *pw;
2684     struct group        *gr;
2685     int             len, sfunc;
2686     u_int32_t       id;
2687     u_int16_t       ulen;
2688
2689     ibuf++;
2690     sfunc = (unsigned char) *ibuf++;
2691     *rbuflen = 0;
2692     LOG(log_debug, logtype_afpd, "afp_mapname: sfunc: %d, afp_version: %d", sfunc, afp_version);
2693     switch ( sfunc ) {
2694     case 1 : 
2695     case 2 : /* unicode */
2696         if (afp_version < 30) {
2697             return( AFPERR_PARAM );
2698         }
2699         memcpy(&ulen, ibuf, sizeof(ulen));
2700         len = ntohs(ulen);
2701         ibuf += 2;
2702         LOG(log_debug, logtype_afpd, "afp_mapname: alive");
2703         break;
2704     case 3 :
2705     case 4 :
2706         len = (unsigned char) *ibuf++;
2707         break;
2708 #ifdef HAVE_NFSv4_ACLS
2709     case 5 : /* username -> UUID  */
2710     case 6 : /* groupname -> UUID */
2711         if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2712             return AFPERR_PARAM;
2713         memcpy(&ulen, ibuf, sizeof(ulen));
2714         len = ntohs(ulen);
2715         ibuf += 2;
2716         break;
2717 #endif
2718     default :
2719         return( AFPERR_PARAM );
2720     }
2721
2722     ibuf[ len ] = '\0';
2723
2724     if ( len == 0 )
2725         return AFPERR_PARAM;
2726     else {
2727         switch ( sfunc ) {
2728         case 1 : /* unicode */
2729         case 3 :
2730             if (NULL == ( pw = (struct passwd *)getpwnam( ibuf )) ) {
2731                 return( AFPERR_NOITEM );
2732             }
2733             id = pw->pw_uid;
2734             id = htonl(id);
2735             memcpy( rbuf, &id, sizeof( id ));
2736             *rbuflen = sizeof( id );
2737             break;
2738
2739         case 2 : /* unicode */
2740         case 4 :
2741             LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s",ibuf);
2742             if (NULL == ( gr = (struct group *)getgrnam( ibuf ))) {
2743                 return( AFPERR_NOITEM );
2744             }
2745             id = gr->gr_gid;
2746             LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s -> id: %d",ibuf, id);
2747     id = htonl(id);
2748     memcpy( rbuf, &id, sizeof( id ));
2749     *rbuflen = sizeof( id );
2750             break;
2751 #ifdef HAVE_NFSv4_ACLS
2752         case 5 :                /* username -> UUID */
2753             LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);    
2754             if (0 != getuuidfromname(ibuf, UUID_USER, rbuf))
2755                 return AFPERR_NOITEM;
2756             *rbuflen = UUID_BINSIZE;
2757             break;
2758         case 6 :                /* groupname -> UUID */
2759             LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);    
2760             if (0 != getuuidfromname(ibuf, UUID_GROUP, rbuf))
2761                 return AFPERR_NOITEM;
2762             *rbuflen = UUID_BINSIZE;
2763             break;
2764 #endif
2765         }
2766     }
2767     return( AFP_OK );
2768 }
2769
2770 /* ------------------------------------
2771   variable DID support 
2772 */
2773 int afp_closedir(AFPObj *obj _U_, char *ibuf _U_, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2774 {
2775 #if 0
2776     struct vol   *vol;
2777     struct dir   *dir;
2778     u_int16_t    vid;
2779     u_int32_t    did;
2780 #endif /* 0 */
2781
2782     *rbuflen = 0;
2783
2784     /* do nothing as dids are static for the life of the process. */
2785 #if 0
2786     ibuf += 2;
2787
2788     memcpy(&vid,  ibuf, sizeof( vid ));
2789     ibuf += sizeof( vid );
2790     if (( vol = getvolbyvid( vid )) == NULL ) {
2791         return( AFPERR_PARAM );
2792     }
2793
2794     memcpy( &did, ibuf, sizeof( did ));
2795     ibuf += sizeof( did );
2796     if (( dir = dirlookup( vol, did )) == NULL ) {
2797         return( AFPERR_PARAM );
2798     }
2799
2800     /* dir_remove -- deletedid */
2801 #endif /* 0 */
2802
2803     return AFP_OK;
2804 }
2805
2806 /* did creation gets done automatically 
2807  * there's a pb again with case but move it to cname
2808 */
2809 int afp_opendir(AFPObj *obj _U_, char *ibuf, size_t ibuflen  _U_, char *rbuf, size_t *rbuflen)
2810 {
2811     struct vol          *vol;
2812     struct dir          *parentdir;
2813     struct path         *path;
2814     u_int32_t           did;
2815     u_int16_t           vid;
2816
2817     *rbuflen = 0;
2818     ibuf += 2;
2819
2820     memcpy(&vid, ibuf, sizeof(vid));
2821     ibuf += sizeof( vid );
2822
2823     if (NULL == ( vol = getvolbyvid( vid )) ) {
2824         return( AFPERR_PARAM );
2825     }
2826
2827     memcpy(&did, ibuf, sizeof(did));
2828     ibuf += sizeof(did);
2829
2830     if (NULL == ( parentdir = dirlookup( vol, did )) ) {
2831         return afp_errno;
2832     }
2833
2834     if (NULL == ( path = cname( vol, parentdir, &ibuf )) ) {
2835         return get_afp_errno(AFPERR_PARAM);
2836     }
2837
2838     if ( *path->m_name != '\0' ) {
2839         return path_error(path, AFPERR_NOOBJ);
2840     }
2841
2842     if ( !path->st_valid && of_stat(path ) < 0 ) {
2843         return( AFPERR_NOOBJ );
2844     }
2845     if ( path->st_errno ) {
2846         return( AFPERR_NOOBJ );
2847     }
2848
2849     memcpy(rbuf, &curdir->d_did, sizeof(curdir->d_did));
2850     *rbuflen = sizeof(curdir->d_did);
2851     return AFP_OK;
2852 }