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