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