]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/directory.c
try to get the directory's cnid from the adouble header when adding it to the cache...
[netatalk.git] / etc / afpd / directory.c
1 /*
2  * $Id: directory.c,v 1.129 2010-01-21 12:10:19 didg Exp $
3  *
4  * Copyright (c) 1990,1993 Regents of The University of Michigan.
5  * All Rights Reserved.  See COPYRIGHT.
6  *
7  * 19 jan 2000 implemented red-black trees for directory lookups
8  * (asun@cobalt.com).
9  */
10
11 #ifdef HAVE_CONFIG_H
12 #include "config.h"
13 #endif /* HAVE_CONFIG_H */
14
15 /* STDC check */
16 #if STDC_HEADERS
17 #include <string.h>
18 #else /* STDC_HEADERS */
19 #ifndef HAVE_STRCHR
20 #define strchr index
21 #define strrchr index
22 #endif /* HAVE_STRCHR */
23 char *strchr (), *strrchr ();
24 #ifndef HAVE_MEMCPY
25 #define memcpy(d,s,n) bcopy ((s), (d), (n))
26 #define memmove(d,s,n) bcopy ((s), (d), (n))
27 #endif /* ! HAVE_MEMCPY */
28 #endif /* STDC_HEADERS */
29 #ifdef HAVE_STRINGS_H
30 #include <strings.h>
31 #endif
32 #include <stdio.h>
33 #include <stdlib.h>
34
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 (stat(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 (stat(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 (stat(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
1546     if ( dir == curdir ) {
1547         return( 0 );
1548     }
1549     if ( dir->d_did == DIRDID_ROOT_PARENT) {
1550         afp_errno = AFPERR_DID1; /* AFPERR_PARAM;*/
1551         return( -1 );
1552     }
1553
1554     p = path + sizeof(path) - 1;
1555     *p-- = '\0';
1556     *p = '.';
1557     for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) {
1558         u = d->d_u_name;
1559         if (!u) {
1560             /* parent directory is deleted */
1561             afp_errno = AFPERR_NOOBJ;
1562             return -1;
1563         }
1564         n = strlen( u );
1565         if (p -n -1 < path) {
1566             afp_errno = AFPERR_PARAM;
1567             return -1;
1568         }
1569         *--p = '/';
1570         p -= n;
1571         memcpy( p, u, n );
1572     }
1573     if ( d != curdir ) {
1574         n = strlen( vol->v_path );
1575         if (p -n -1 < path) {
1576             afp_errno = AFPERR_PARAM;
1577             return -1;
1578         }
1579         *--p = '/';
1580         p -= n;
1581         memcpy( p, vol->v_path, n );
1582     }
1583     if ( chdir( p ) < 0 ) {
1584         switch (errno) {
1585         case EACCES:
1586         case EPERM:
1587             afp_errno = AFPERR_ACCESS;
1588             break;
1589         default:
1590             afp_errno = AFPERR_NOOBJ;
1591
1592         }
1593         return( -1 );
1594     }
1595     vol->v_curdir = curdir = dir;
1596     return( 0 );
1597 }
1598
1599 /*
1600  * We can't use unix file's perm to support Apple's inherited protection modes.
1601  * If we aren't the file's owner we can't change its perms when moving it and smb
1602  * nfs,... don't even try.
1603  */
1604 #define AFP_CHECK_ACCESS
1605
1606 int check_access(char *path, int mode)
1607 {
1608 #ifdef AFP_CHECK_ACCESS
1609     struct maccess ma;
1610     char *p;
1611
1612     p = ad_dir(path);
1613     if (!p)
1614         return -1;
1615
1616     accessmode(p, &ma, curdir, NULL);
1617     if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1618         return -1;
1619     if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1620         return -1;
1621 #endif
1622     return 0;
1623 }
1624
1625 /* --------------------- */
1626 int file_access(struct path *path, int mode)
1627 {
1628     struct maccess ma;
1629
1630     accessmode(path->u_name, &ma, curdir, &path->st);
1631     if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1632         return -1;
1633     if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1634         return -1;
1635     return 0;
1636
1637 }
1638
1639 /* --------------------- */
1640 void setdiroffcnt(struct dir *dir, struct stat *st,  u_int32_t count)
1641 {
1642     dir->offcnt = count;
1643     dir->ctime = st->st_ctime;
1644     dir->d_flags &= ~DIRF_CNID;
1645 }
1646
1647 /* ---------------------
1648  * is our cached offspring count valid?
1649  */
1650
1651 static int diroffcnt(struct dir *dir, struct stat *st)
1652 {
1653     return st->st_ctime == dir->ctime;
1654 }
1655
1656 /* ---------------------
1657  * is our cached also for reenumerate id?
1658  */
1659
1660 int dirreenumerate(struct dir *dir, struct stat *st)
1661 {
1662     return st->st_ctime == dir->ctime && (dir->d_flags & DIRF_CNID);
1663 }
1664
1665 /* --------------------- */
1666 static int invisible_dots(const struct vol *vol, const char *name)
1667 {
1668     return vol_inv_dots(vol) && *name  == '.' && strcmp(name, ".") && strcmp(name, "..");
1669 }
1670
1671 /* ------------------------------
1672    (".", curdir)
1673    (name, dir) with curdir:name == dir, from afp_enumerate
1674 */
1675
1676 int getdirparams(const struct vol *vol,
1677                  u_int16_t bitmap, struct path *s_path,
1678                  struct dir *dir,
1679                  char *buf, size_t *buflen )
1680 {
1681     struct maccess  ma;
1682     struct adouble  ad;
1683     char        *data, *l_nameoff = NULL, *utf_nameoff = NULL;
1684     int         bit = 0, isad = 0;
1685     u_int32_t           aint;
1686     u_int16_t       ashort;
1687     int                 ret;
1688     u_int32_t           utf8 = 0;
1689     cnid_t              pdid;
1690     struct stat *st = &s_path->st;
1691     char *upath = s_path->u_name;
1692
1693     if ((bitmap & ((1 << DIRPBIT_ATTR)  |
1694                    (1 << DIRPBIT_CDATE) |
1695                    (1 << DIRPBIT_MDATE) |
1696                    (1 << DIRPBIT_BDATE) |
1697                    (1 << DIRPBIT_FINFO)))) {
1698
1699         ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1700         if ( !ad_metadata( upath, ADFLAGS_CREATE|ADFLAGS_DIR, &ad) ) {
1701             isad = 1;
1702             if (ad.ad_md->adf_flags & O_CREAT) {
1703                 /* We just created it */
1704                 ad_setname(&ad, s_path->m_name);
1705                 ad_setid( &ad,
1706                           s_path->st.st_dev,
1707                           s_path->st.st_ino,
1708                           dir->d_did,
1709                           dir->d_parent->d_did,
1710                           vol->v_stamp);
1711                 ad_flush( &ad);
1712             }
1713         }
1714     }
1715
1716     if ( dir->d_did == DIRDID_ROOT) {
1717         pdid = DIRDID_ROOT_PARENT;
1718     } else if (dir->d_did == DIRDID_ROOT_PARENT) {
1719         pdid = 0;
1720     } else {
1721         pdid = dir->d_parent->d_did;
1722     }
1723
1724     data = buf;
1725     while ( bitmap != 0 ) {
1726         while (( bitmap & 1 ) == 0 ) {
1727             bitmap = bitmap>>1;
1728             bit++;
1729         }
1730
1731         switch ( bit ) {
1732         case DIRPBIT_ATTR :
1733             if ( isad ) {
1734                 ad_getattr(&ad, &ashort);
1735             } else if (invisible_dots(vol, dir->d_u_name)) {
1736                 ashort = htons(ATTRBIT_INVISIBLE);
1737             } else
1738                 ashort = 0;
1739             ashort |= htons(ATTRBIT_SHARED);
1740             memcpy( data, &ashort, sizeof( ashort ));
1741             data += sizeof( ashort );
1742             break;
1743
1744         case DIRPBIT_PDID :
1745             memcpy( data, &pdid, sizeof( pdid ));
1746             data += sizeof( pdid );
1747             break;
1748
1749         case DIRPBIT_CDATE :
1750             if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
1751                 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1752             memcpy( data, &aint, sizeof( aint ));
1753             data += sizeof( aint );
1754             break;
1755
1756         case DIRPBIT_MDATE :
1757             aint = AD_DATE_FROM_UNIX(st->st_mtime);
1758             memcpy( data, &aint, sizeof( aint ));
1759             data += sizeof( aint );
1760             break;
1761
1762         case DIRPBIT_BDATE :
1763             if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
1764                 aint = AD_DATE_START;
1765             memcpy( data, &aint, sizeof( aint ));
1766             data += sizeof( aint );
1767             break;
1768
1769         case DIRPBIT_FINFO :
1770             if ( isad ) {
1771                 memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
1772             } else { /* no appledouble */
1773                 memset( data, 0, 32 );
1774                 /* set default view -- this also gets done in ad_open() */
1775                 ashort = htons(FINDERINFO_CLOSEDVIEW);
1776                 memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
1777
1778                 /* dot files are by default visible */
1779                 if (invisible_dots(vol, dir->d_u_name)) {
1780                     ashort = htons(FINDERINFO_INVISIBLE);
1781                     memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
1782                 }
1783             }
1784             data += 32;
1785             break;
1786
1787         case DIRPBIT_LNAME :
1788             if (dir->d_m_name) /* root of parent can have a null name */
1789                 l_nameoff = data;
1790             else
1791                 memset(data, 0, sizeof(u_int16_t));
1792             data += sizeof( u_int16_t );
1793             break;
1794
1795         case DIRPBIT_SNAME :
1796             memset(data, 0, sizeof(u_int16_t));
1797             data += sizeof( u_int16_t );
1798             break;
1799
1800         case DIRPBIT_DID :
1801             memcpy( data, &dir->d_did, sizeof( aint ));
1802             data += sizeof( aint );
1803             break;
1804
1805         case DIRPBIT_OFFCNT :
1806             ashort = 0;
1807             /* this needs to handle current directory access rights */
1808             if (diroffcnt(dir, st)) {
1809                 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1810             }
1811             else if ((ret = for_each_dirent(vol, upath, NULL,NULL)) >= 0) {
1812                 setdiroffcnt(dir, st,  ret);
1813                 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1814             }
1815             ashort = htons( ashort );
1816             memcpy( data, &ashort, sizeof( ashort ));
1817             data += sizeof( ashort );
1818             break;
1819
1820         case DIRPBIT_UID :
1821             aint = htonl(st->st_uid);
1822             memcpy( data, &aint, sizeof( aint ));
1823             data += sizeof( aint );
1824             break;
1825
1826         case DIRPBIT_GID :
1827             aint = htonl(st->st_gid);
1828             memcpy( data, &aint, sizeof( aint ));
1829             data += sizeof( aint );
1830             break;
1831
1832         case DIRPBIT_ACCESS :
1833             accessmode( upath, &ma, dir , st);
1834
1835             *data++ = ma.ma_user;
1836             *data++ = ma.ma_world;
1837             *data++ = ma.ma_group;
1838             *data++ = ma.ma_owner;
1839             break;
1840
1841             /* Client has requested the ProDOS information block.
1842                Just pass back the same basic block for all
1843                directories. <shirsch@ibm.net> */
1844         case DIRPBIT_PDINFO :
1845             if (afp_version >= 30) { /* UTF8 name */
1846                 utf8 = kTextEncodingUTF8;
1847                 if (dir->d_m_name) /* root of parent can have a null name */
1848                     utf_nameoff = data;
1849                 else
1850                     memset(data, 0, sizeof(u_int16_t));
1851                 data += sizeof( u_int16_t );
1852                 aint = 0;
1853                 memcpy(data, &aint, sizeof( aint ));
1854                 data += sizeof( aint );
1855             }
1856             else { /* ProDOS Info Block */
1857                 *data++ = 0x0f;
1858                 *data++ = 0;
1859                 ashort = htons( 0x0200 );
1860                 memcpy( data, &ashort, sizeof( ashort ));
1861                 data += sizeof( ashort );
1862                 memset( data, 0, sizeof( ashort ));
1863                 data += sizeof( ashort );
1864             }
1865             break;
1866
1867         case DIRPBIT_UNIXPR :
1868             aint = htonl(st->st_uid);
1869             memcpy( data, &aint, sizeof( aint ));
1870             data += sizeof( aint );
1871             aint = htonl(st->st_gid);
1872             memcpy( data, &aint, sizeof( aint ));
1873             data += sizeof( aint );
1874
1875             aint = st->st_mode;
1876             aint = htonl ( aint & ~S_ISGID );  /* Remove SGID, OSX doesn't like it ... */
1877             memcpy( data, &aint, sizeof( aint ));
1878             data += sizeof( aint );
1879
1880             accessmode( upath, &ma, dir , st);
1881
1882             *data++ = ma.ma_user;
1883             *data++ = ma.ma_world;
1884             *data++ = ma.ma_group;
1885             *data++ = ma.ma_owner;
1886             break;
1887
1888         default :
1889             if ( isad ) {
1890                 ad_close_metadata( &ad );
1891             }
1892             return( AFPERR_BITMAP );
1893         }
1894         bitmap = bitmap>>1;
1895         bit++;
1896     }
1897     if ( l_nameoff ) {
1898         ashort = htons( data - buf );
1899         memcpy( l_nameoff, &ashort, sizeof( ashort ));
1900         data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, 0);
1901     }
1902     if ( utf_nameoff ) {
1903         ashort = htons( data - buf );
1904         memcpy( utf_nameoff, &ashort, sizeof( ashort ));
1905         data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, utf8);
1906     }
1907     if ( isad ) {
1908         ad_close_metadata( &ad );
1909     }
1910     *buflen = data - buf;
1911     return( AFP_OK );
1912 }
1913
1914 /* ----------------------------- */
1915 int path_error(struct path *path, int error)
1916 {
1917 /* - a dir with access error
1918  * - no error it's a file
1919  * - file not found
1920  */
1921     if (path_isadir(path))
1922         return afp_errno;
1923     if (path->st_valid && path->st_errno)
1924         return error;
1925     return AFPERR_BADTYPE ;
1926 }
1927
1928 /* ----------------------------- */
1929 int afp_setdirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1930 {
1931     struct vol  *vol;
1932     struct dir  *dir;
1933     struct path *path;
1934     u_int16_t   vid, bitmap;
1935     u_int32_t   did;
1936     int     rc;
1937
1938     *rbuflen = 0;
1939     ibuf += 2;
1940     memcpy( &vid, ibuf, sizeof( vid ));
1941     ibuf += sizeof( vid );
1942
1943     if (NULL == ( vol = getvolbyvid( vid )) ) {
1944         return( AFPERR_PARAM );
1945     }
1946
1947     if (vol->v_flags & AFPVOL_RO)
1948         return AFPERR_VLOCK;
1949
1950     memcpy( &did, ibuf, sizeof( did ));
1951     ibuf += sizeof( int );
1952
1953     if (NULL == ( dir = dirlookup( vol, did )) ) {
1954         return afp_errno;
1955     }
1956
1957     memcpy( &bitmap, ibuf, sizeof( bitmap ));
1958     bitmap = ntohs( bitmap );
1959     ibuf += sizeof( bitmap );
1960
1961     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1962         return get_afp_errno(AFPERR_NOOBJ);
1963     }
1964
1965     if ( *path->m_name != '\0' ) {
1966         rc = path_error(path, AFPERR_NOOBJ);
1967         /* maybe we are trying to set perms back */
1968         if (rc != AFPERR_ACCESS)
1969             return rc;
1970     }
1971
1972     /*
1973      * If ibuf is odd, make it even.
1974      */
1975     if ((u_long)ibuf & 1 ) {
1976         ibuf++;
1977     }
1978
1979     if (AFP_OK == ( rc = setdirparams(vol, path, bitmap, ibuf )) ) {
1980         setvoltime(obj, vol );
1981     }
1982     return( rc );
1983 }
1984
1985 /*
1986  * cf AFP3.0.pdf page 244 for change_mdate and change_parent_mdate logic
1987  *
1988  * assume path == '\0' eg. it's a directory in canonical form
1989  */
1990
1991 struct path Cur_Path = {
1992     0,
1993     "",  /* mac name */
1994     ".", /* unix name */
1995     0,   /* id */
1996     NULL,/* struct dir */
1997     0,   /* stat is not set */
1998 };
1999
2000 /* ------------------ */
2001 static int set_dir_errors(struct path *path, const char *where, int err)
2002 {
2003     switch ( err ) {
2004     case EPERM :
2005     case EACCES :
2006         return AFPERR_ACCESS;
2007     case EROFS :
2008         return AFPERR_VLOCK;
2009     }
2010     LOG(log_error, logtype_afpd, "setdirparam(%s): %s: %s", fullpathname(path->u_name), where, strerror(err) );
2011     return AFPERR_PARAM;
2012 }
2013
2014 /* ------------------ */
2015 int setdirparams(struct vol *vol,
2016                  struct path *path, u_int16_t d_bitmap, char *buf )
2017 {
2018     struct maccess  ma;
2019     struct adouble  ad;
2020     struct utimbuf      ut;
2021     struct timeval      tv;
2022
2023     char                *upath;
2024     struct dir          *dir;
2025     int         bit, isad = 1;
2026     int                 cdate, bdate;
2027     int                 owner, group;
2028     u_int16_t       ashort, bshort;
2029     int                 err = AFP_OK;
2030     int                 change_mdate = 0;
2031     int                 change_parent_mdate = 0;
2032     int                 newdate = 0;
2033     u_int16_t           bitmap = d_bitmap;
2034     u_char              finder_buf[32];
2035     u_int32_t       upriv;
2036     mode_t              mpriv = 0;
2037     u_int16_t           upriv_bit = 0;
2038
2039     bit = 0;
2040     upath = path->u_name;
2041     dir   = path->d_dir;
2042     while ( bitmap != 0 ) {
2043         while (( bitmap & 1 ) == 0 ) {
2044             bitmap = bitmap>>1;
2045             bit++;
2046         }
2047
2048         switch( bit ) {
2049         case DIRPBIT_ATTR :
2050             change_mdate = 1;
2051             memcpy( &ashort, buf, sizeof( ashort ));
2052             buf += sizeof( ashort );
2053             break;
2054         case DIRPBIT_CDATE :
2055             change_mdate = 1;
2056             memcpy(&cdate, buf, sizeof(cdate));
2057             buf += sizeof( cdate );
2058             break;
2059         case DIRPBIT_MDATE :
2060             memcpy(&newdate, buf, sizeof(newdate));
2061             buf += sizeof( newdate );
2062             break;
2063         case DIRPBIT_BDATE :
2064             change_mdate = 1;
2065             memcpy(&bdate, buf, sizeof(bdate));
2066             buf += sizeof( bdate );
2067             break;
2068         case DIRPBIT_FINFO :
2069             change_mdate = 1;
2070             memcpy( finder_buf, buf, 32 );
2071             buf += 32;
2072             break;
2073         case DIRPBIT_UID :  /* What kind of loser mounts as root? */
2074             change_parent_mdate = 1;
2075             memcpy( &owner, buf, sizeof(owner));
2076             buf += sizeof( owner );
2077             break;
2078         case DIRPBIT_GID :
2079             change_parent_mdate = 1;
2080             memcpy( &group, buf, sizeof( group ));
2081             buf += sizeof( group );
2082             break;
2083         case DIRPBIT_ACCESS :
2084             change_mdate = 1;
2085             change_parent_mdate = 1;
2086             ma.ma_user = *buf++;
2087             ma.ma_world = *buf++;
2088             ma.ma_group = *buf++;
2089             ma.ma_owner = *buf++;
2090             mpriv = mtoumode( &ma ) | vol->v_dperm;
2091             if (dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2092                 err = set_dir_errors(path, "setdirmode", errno);
2093                 bitmap = 0;
2094             }
2095             break;
2096             /* Ignore what the client thinks we should do to the
2097                ProDOS information block.  Skip over the data and
2098                report nothing amiss. <shirsch@ibm.net> */
2099         case DIRPBIT_PDINFO :
2100             if (afp_version < 30) {
2101                 buf += 6;
2102             }
2103             else {
2104                 err = AFPERR_BITMAP;
2105                 bitmap = 0;
2106             }
2107             break;
2108         case DIRPBIT_UNIXPR :
2109             if (vol_unix_priv(vol)) {
2110                 memcpy( &owner, buf, sizeof(owner)); /* FIXME need to change owner too? */
2111                 buf += sizeof( owner );
2112                 memcpy( &group, buf, sizeof( group ));
2113                 buf += sizeof( group );
2114
2115                 change_mdate = 1;
2116                 change_parent_mdate = 1;
2117                 memcpy( &upriv, buf, sizeof( upriv ));
2118                 buf += sizeof( upriv );
2119                 upriv = ntohl (upriv) | vol->v_dperm;
2120                 if (dir_rx_set(upriv)) {
2121                     /* maybe we are trying to set perms back */
2122                     if ( setdirunixmode(vol, upath, upriv) < 0 ) {
2123                         bitmap = 0;
2124                         err = set_dir_errors(path, "setdirunixmode", errno);
2125                     }
2126                 }
2127                 else {
2128                     /* do it later */
2129                     upriv_bit = 1;
2130                 }
2131                 break;
2132             }
2133             /* fall through */
2134         default :
2135             err = AFPERR_BITMAP;
2136             bitmap = 0;
2137             break;
2138         }
2139
2140         bitmap = bitmap>>1;
2141         bit++;
2142     }
2143     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2144
2145     if (ad_open_metadata( upath, ADFLAGS_DIR, O_CREAT, &ad) < 0) {
2146         /*
2147          * Check to see what we're trying to set.  If it's anything
2148          * but ACCESS, UID, or GID, give an error.  If it's any of those
2149          * three, we don't need the ad to be open, so just continue.
2150          *
2151          * note: we also don't need to worry about mdate. also, be quiet
2152          *       if we're using the noadouble option.
2153          */
2154         if (!vol_noadouble(vol) && (d_bitmap &
2155                                     ~((1<<DIRPBIT_ACCESS)|(1<<DIRPBIT_UNIXPR)|
2156                                       (1<<DIRPBIT_UID)|(1<<DIRPBIT_GID)|
2157                                       (1<<DIRPBIT_MDATE)|(1<<DIRPBIT_PDINFO)))) {
2158             return AFPERR_ACCESS;
2159         }
2160
2161         isad = 0;
2162     } else {
2163         /*
2164          * Check to see if a create was necessary. If it was, we'll want
2165          * to set our name, etc.
2166          */
2167         if ( (ad_get_HF_flags( &ad ) & O_CREAT)) {
2168             ad_setname(&ad, curdir->d_m_name);
2169         }
2170     }
2171
2172     bit = 0;
2173     bitmap = d_bitmap;
2174     while ( bitmap != 0 ) {
2175         while (( bitmap & 1 ) == 0 ) {
2176             bitmap = bitmap>>1;
2177             bit++;
2178         }
2179
2180         switch( bit ) {
2181         case DIRPBIT_ATTR :
2182             if (isad) {
2183                 ad_getattr(&ad, &bshort);
2184                 if ((bshort & htons(ATTRBIT_INVISIBLE)) !=
2185                     (ashort & htons(ATTRBIT_INVISIBLE) & htons(ATTRBIT_SETCLR)) )
2186                     change_parent_mdate = 1;
2187                 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
2188                     bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
2189                 } else {
2190                     bshort &= ~ashort;
2191                 }
2192                 ad_setattr(&ad, bshort);
2193             }
2194             break;
2195         case DIRPBIT_CDATE :
2196             if (isad) {
2197                 ad_setdate(&ad, AD_DATE_CREATE, cdate);
2198             }
2199             break;
2200         case DIRPBIT_MDATE :
2201             break;
2202         case DIRPBIT_BDATE :
2203             if (isad) {
2204                 ad_setdate(&ad, AD_DATE_BACKUP, bdate);
2205             }
2206             break;
2207         case DIRPBIT_FINFO :
2208             if (isad) {
2209                 /* Fixes #2802236 */
2210                 u_int16_t *fflags = (u_int16_t *)(finder_buf + FINDERINFO_FRFLAGOFF);
2211                 *fflags &= htons(~FINDERINFO_ISHARED);
2212                 /* #2802236 end */
2213                 if (  dir->d_did == DIRDID_ROOT ) {
2214                     /*
2215                      * Alright, we admit it, this is *really* sick!
2216                      * The 4 bytes that we don't copy, when we're dealing
2217                      * with the root of a volume, are the directory's
2218                      * location information. This eliminates that annoying
2219                      * behavior one sees when mounting above another mount
2220                      * point.
2221                      */
2222                     memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 10 );
2223                     memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, finder_buf + 14, 18 );
2224                 } else {
2225                     memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 32 );
2226                 }
2227             }
2228             break;
2229         case DIRPBIT_UID :  /* What kind of loser mounts as root? */
2230             if ( (dir->d_did == DIRDID_ROOT) &&
2231                  (setdeskowner( ntohl(owner), -1 ) < 0)) {
2232                 err = set_dir_errors(path, "setdeskowner", errno);
2233                 if (isad && err == AFPERR_PARAM) {
2234                     err = AFP_OK; /* ???*/
2235                 }
2236                 else {
2237                     goto setdirparam_done;
2238                 }
2239             }
2240             if ( setdirowner(vol, upath, ntohl(owner), -1 ) < 0 ) {
2241                 err = set_dir_errors(path, "setdirowner", errno);
2242                 goto setdirparam_done;
2243             }
2244             break;
2245         case DIRPBIT_GID :
2246             if (dir->d_did == DIRDID_ROOT)
2247                 setdeskowner( -1, ntohl(group) );
2248             if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2249                 err = set_dir_errors(path, "setdirowner", errno);
2250                 goto setdirparam_done;
2251             }
2252             break;
2253         case DIRPBIT_ACCESS :
2254             if (dir->d_did == DIRDID_ROOT) {
2255                 setdeskmode(mpriv);
2256                 if (!dir_rx_set(mpriv)) {
2257                     /* we can't remove read and search for owner on volume root */
2258                     err = AFPERR_ACCESS;
2259                     goto setdirparam_done;
2260                 }
2261             }
2262
2263             if (!dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2264                 err = set_dir_errors(path, "setdirmode", errno);
2265                 goto setdirparam_done;
2266             }
2267             break;
2268         case DIRPBIT_PDINFO :
2269             if (afp_version >= 30) {
2270                 err = AFPERR_BITMAP;
2271                 goto setdirparam_done;
2272             }
2273             break;
2274         case DIRPBIT_UNIXPR :
2275             if (vol_unix_priv(vol)) {
2276                 if (dir->d_did == DIRDID_ROOT) {
2277                     if (!dir_rx_set(upriv)) {
2278                         /* we can't remove read and search for owner on volume root */
2279                         err = AFPERR_ACCESS;
2280                         goto setdirparam_done;
2281                     }
2282                     setdeskowner( -1, ntohl(group) );
2283                     setdeskmode( upriv );
2284                 }
2285                 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2286                     err = set_dir_errors(path, "setdirowner", errno);
2287                     goto setdirparam_done;
2288                 }
2289
2290                 if ( upriv_bit && setdirunixmode(vol, upath, upriv) < 0 ) {
2291                     err = set_dir_errors(path, "setdirunixmode", errno);
2292                     goto setdirparam_done;
2293                 }
2294             }
2295             else {
2296                 err = AFPERR_BITMAP;
2297                 goto setdirparam_done;
2298             }
2299             break;
2300         default :
2301             err = AFPERR_BITMAP;
2302             goto setdirparam_done;
2303             break;
2304         }
2305
2306         bitmap = bitmap>>1;
2307         bit++;
2308     }
2309
2310 setdirparam_done:
2311     if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
2312         newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2313     }
2314     if (newdate) {
2315         if (isad)
2316             ad_setdate(&ad, AD_DATE_MODIFY, newdate);
2317         ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
2318         utime(upath, &ut);
2319     }
2320
2321     if ( isad ) {
2322         if (path->st_valid && !path->st_errno) {
2323             struct stat *st = &path->st;
2324
2325             if (dir && dir->d_parent) {
2326                 ad_setid(&ad, st->st_dev, st->st_ino,  dir->d_did, dir->d_parent->d_did, vol->v_stamp);
2327             }
2328         }
2329         ad_flush( &ad);
2330         ad_close_metadata( &ad);
2331     }
2332
2333     if (change_parent_mdate && dir->d_did != DIRDID_ROOT
2334         && gettimeofday(&tv, NULL) == 0) {
2335         if (!movecwd(vol, dir->d_parent)) {
2336             newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2337             /* be careful with bitmap because now dir is null */
2338             bitmap = 1<<DIRPBIT_MDATE;
2339             setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
2340             /* should we reset curdir ?*/
2341         }
2342     }
2343
2344     return err;
2345 }
2346
2347 int afp_syncdir(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2348 {
2349 #ifdef HAVE_DIRFD
2350     DIR                  *dp;
2351 #endif
2352     int                  dfd;
2353     struct vol           *vol;
2354     struct dir           *dir;
2355     u_int32_t            did;
2356     u_int16_t            vid;
2357
2358     *rbuflen = 0;
2359     ibuf += 2;
2360
2361     memcpy( &vid, ibuf, sizeof( vid ));
2362     ibuf += sizeof( vid );
2363     if (NULL == (vol = getvolbyvid( vid )) ) {
2364         return( AFPERR_PARAM );
2365     }
2366
2367     memcpy( &did, ibuf, sizeof( did ));
2368     ibuf += sizeof( did );
2369
2370     /*
2371      * Here's the deal:
2372      * if it's CNID 2 our only choice to meet the specs is call sync.
2373      * For any other CNID just sync that dir. To my knowledge the
2374      * intended use of FPSyncDir is to sync the volume so all we're
2375      * ever going to see here is probably CNID 2. Anyway, we' prepared.
2376      */
2377
2378     if ( ntohl(did) == 2 ) {
2379         sync();
2380     } else {
2381         if (NULL == ( dir = dirlookup( vol, did )) ) {
2382             return afp_errno; /* was AFPERR_NOOBJ */
2383         }
2384
2385         if (movecwd( vol, dir ) < 0 )
2386             return ( AFPERR_NOOBJ );
2387
2388         /*
2389          * Assuming only OSens that have dirfd also may require fsyncing directories
2390          * in order to flush metadata e.g. Linux.
2391          */
2392
2393 #ifdef HAVE_DIRFD
2394         if (NULL == ( dp = opendir( "." )) ) {
2395             switch( errno ) {
2396             case ENOENT :
2397                 return( AFPERR_NOOBJ );
2398             case EACCES :
2399                 return( AFPERR_ACCESS );
2400             default :
2401                 return( AFPERR_PARAM );
2402             }
2403         }
2404
2405         LOG(log_debug, logtype_afpd, "afp_syncdir: dir: '%s'", dir->d_u_name);
2406
2407         dfd = dirfd( dp );
2408         if ( fsync ( dfd ) < 0 )
2409             LOG(log_error, logtype_afpd, "afp_syncdir(%s):  %s",
2410                 dir->d_u_name, strerror(errno) );
2411         closedir(dp); /* closes dfd too */
2412 #endif
2413
2414         if ( -1 == (dfd = open(vol->ad_path(".", ADFLAGS_DIR), O_RDWR))) {
2415             switch( errno ) {
2416             case ENOENT:
2417                 return( AFPERR_NOOBJ );
2418             case EACCES:
2419                 return( AFPERR_ACCESS );
2420             default:
2421                 return( AFPERR_PARAM );
2422             }
2423         }
2424
2425         LOG(log_debug, logtype_afpd, "afp_syncdir: ad-file: '%s'",
2426             vol->ad_path(".", ADFLAGS_DIR) );
2427
2428         if ( fsync(dfd) < 0 )
2429             LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2430                 vol->ad_path(dir->d_u_name, ADFLAGS_DIR), strerror(errno) );
2431         close(dfd);
2432     }
2433
2434     return ( AFP_OK );
2435 }
2436
2437 int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2438 {
2439     struct adouble  ad;
2440     struct vol      *vol;
2441     struct dir      *dir;
2442     char        *upath;
2443     struct path         *s_path;
2444     u_int32_t       did;
2445     u_int16_t       vid;
2446     int                 err;
2447
2448     *rbuflen = 0;
2449     ibuf += 2;
2450
2451     memcpy( &vid, ibuf, sizeof( vid ));
2452     ibuf += sizeof( vid );
2453     if (NULL == ( vol = getvolbyvid( vid )) ) {
2454         return( AFPERR_PARAM );
2455     }
2456
2457     if (vol->v_flags & AFPVOL_RO)
2458         return AFPERR_VLOCK;
2459
2460     memcpy( &did, ibuf, sizeof( did ));
2461     ibuf += sizeof( did );
2462     if (NULL == ( dir = dirlookup( vol, did )) ) {
2463         return afp_errno; /* was AFPERR_NOOBJ */
2464     }
2465     /* for concurrent access we need to be sure we are not in the
2466      * folder we want to create...
2467      */
2468     movecwd(vol, dir);
2469
2470     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
2471         return get_afp_errno(AFPERR_PARAM);
2472     }
2473     /* cname was able to move curdir to it! */
2474     if (*s_path->m_name == '\0')
2475         return AFPERR_EXIST;
2476
2477     upath = s_path->u_name;
2478
2479     if (AFP_OK != (err = netatalk_mkdir( upath))) {
2480         return err;
2481     }
2482
2483     if (of_stat(s_path) < 0) {
2484         return AFPERR_MISC;
2485     }
2486     curdir->offcnt++;
2487     if ((dir = adddir( vol, curdir, s_path)) == NULL) {
2488         return AFPERR_MISC;
2489     }
2490
2491     if ( movecwd( vol, dir ) < 0 ) {
2492         return( AFPERR_PARAM );
2493     }
2494
2495     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2496     if (ad_open_metadata( ".", ADFLAGS_DIR, O_CREAT, &ad ) < 0)  {
2497         if (vol_noadouble(vol))
2498             goto createdir_done;
2499         return( AFPERR_ACCESS );
2500     }
2501     ad_setname(&ad, s_path->m_name);
2502     ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
2503
2504     ad_flush( &ad);
2505     ad_close_metadata( &ad);
2506
2507 createdir_done:
2508 #ifdef HAVE_NFSv4_ACLS
2509     /* FIXME: are we really inside the created dir? */
2510     addir_inherit_acl(vol);
2511 #endif
2512
2513     memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
2514     *rbuflen = sizeof( u_int32_t );
2515     setvoltime(obj, vol );
2516     return( AFP_OK );
2517 }
2518
2519 /*
2520  * dst       new unix filename (not a pathname)
2521  * newname   new mac name
2522  * newparent curdir
2523  *
2524  */
2525 int renamedir(const struct vol *vol, char *src, char *dst,
2526               struct dir *dir,
2527               struct dir *newparent,
2528               char *newname)
2529 {
2530     struct adouble  ad;
2531     struct dir      *parent;
2532     char                *buf;
2533     int         len, err;
2534
2535     /* existence check moved to afp_moveandrename */
2536     if ( unix_rename( src, dst ) < 0 ) {
2537         switch ( errno ) {
2538         case ENOENT :
2539             return( AFPERR_NOOBJ );
2540         case EACCES :
2541             return( AFPERR_ACCESS );
2542         case EROFS:
2543             return AFPERR_VLOCK;
2544         case EINVAL:
2545             /* tried to move directory into a subdirectory of itself */
2546             return AFPERR_CANTMOVE;
2547         case EXDEV:
2548             /* this needs to copy and delete. bleah. that means we have
2549              * to deal with entire directory hierarchies. */
2550             if ((err = copydir(vol, src, dst)) < 0) {
2551                 deletedir(dst);
2552                 return err;
2553             }
2554             if ((err = deletedir(src)) < 0)
2555                 return err;
2556             break;
2557         default :
2558             return( AFPERR_PARAM );
2559         }
2560     }
2561
2562     vol->vfs->vfs_renamedir(vol, src, dst);
2563
2564     len = strlen( newname );
2565     /* rename() succeeded so we need to update our tree even if we can't open
2566      * metadata
2567      */
2568
2569     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2570
2571     if (!ad_open_metadata( dst, ADFLAGS_DIR, 0, &ad)) {
2572         ad_setname(&ad, newname);
2573         ad_flush( &ad);
2574         ad_close_metadata( &ad);
2575     }
2576
2577     dir_hash_del(vol, dir);
2578     if (dir->d_m_name == dir->d_u_name)
2579         dir->d_u_name = NULL;
2580
2581     if ((buf = (char *) realloc( dir->d_m_name, len + 1 )) == NULL ) {
2582         LOG(log_error, logtype_afpd, "renamedir: realloc mac name: %s", strerror(errno) );
2583         /* FIXME : fatal ? */
2584         return AFPERR_MISC;
2585     }
2586     dir->d_m_name = buf;
2587     strcpy( dir->d_m_name, newname );
2588
2589     if (newname == dst) {
2590         free(dir->d_u_name);
2591         dir->d_u_name = dir->d_m_name;
2592     }
2593     else {
2594         if ((buf = (char *) realloc( dir->d_u_name, strlen(dst) + 1 )) == NULL ) {
2595             LOG(log_error, logtype_afpd, "renamedir: realloc unix name: %s", strerror(errno) );
2596             return AFPERR_MISC;
2597         }
2598         dir->d_u_name = buf;
2599         strcpy( dir->d_u_name, dst );
2600     }
2601
2602     if (dir->d_m_name_ucs2)
2603         free(dir->d_m_name_ucs2);
2604
2605     dir->d_m_name_ucs2 = NULL;
2606     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))
2607         dir->d_m_name_ucs2 = NULL;
2608
2609     if (( parent = dir->d_parent ) == NULL ) {
2610         return( AFP_OK );
2611     }
2612     if ( parent == newparent ) {
2613         hash_alloc_insert(vol->v_hash, dir, dir);
2614         return( AFP_OK );
2615     }
2616
2617     /* detach from old parent and add to new one. */
2618     dirchildremove(parent, dir);
2619     dir->d_parent = newparent;
2620     dirchildadd(vol, newparent, dir);
2621     return( AFP_OK );
2622 }
2623
2624 /* delete an empty directory */
2625 int deletecurdir(struct vol *vol)
2626 {
2627     struct dirent *de;
2628     struct stat st;
2629     struct dir  *fdir;
2630     DIR *dp;
2631     struct adouble  ad;
2632     u_int16_t       ashort;
2633     int err;
2634
2635     if ( curdir->d_parent == NULL ) {
2636         return( AFPERR_ACCESS );
2637     }
2638
2639     fdir = curdir;
2640
2641     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2642     /* we never want to create a resource fork here, we are going to delete it */
2643     if ( ad_metadata( ".", ADFLAGS_DIR, &ad) == 0 ) {
2644
2645         ad_getattr(&ad, &ashort);
2646         ad_close( &ad, ADFLAGS_HF );
2647         if ((ashort & htons(ATTRBIT_NODELETE))) {
2648             return  AFPERR_OLOCK;
2649         }
2650     }
2651     err = vol->vfs->vfs_deletecurdir(vol);
2652     if (err) {
2653         return err;
2654     }
2655
2656     /* now get rid of dangling symlinks */
2657     if ((dp = opendir("."))) {
2658         while ((de = readdir(dp))) {
2659             /* skip this and previous directory */
2660             if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
2661                 continue;
2662
2663             /* bail if it's not a symlink */
2664             if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
2665                 closedir(dp);
2666                 return AFPERR_DIRNEMPT;
2667             }
2668
2669             if ((err = netatalk_unlink(de->d_name))) {
2670                 closedir(dp);
2671                 return err;
2672             }
2673         }
2674     }
2675
2676     if ( movecwd( vol, curdir->d_parent ) < 0 ) {
2677         err = afp_errno;
2678         goto delete_done;
2679     }
2680
2681     if ( !(err = netatalk_rmdir(fdir->d_u_name))) {
2682         dirchildremove(curdir, fdir);
2683         cnid_delete(vol->v_cdb, fdir->d_did);
2684         dir_remove( vol, fdir );
2685         err = AFP_OK;
2686     }
2687 delete_done:
2688     if (dp) {
2689         /* inode is used as key for cnid.
2690          * Close the descriptor only after cnid_delete
2691          * has been called.
2692          */
2693         closedir(dp);
2694     }
2695     return err;
2696 }
2697
2698 int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2699 {
2700     struct passwd   *pw;
2701     struct group    *gr;
2702     char        *name;
2703     u_int32_t           id;
2704     int         len, sfunc;
2705     int         utf8 = 0;
2706
2707     ibuf++;
2708     sfunc = (unsigned char) *ibuf++;
2709     *rbuflen = 0;
2710
2711
2712     if (sfunc >= 3 && sfunc <= 6) {
2713         if (afp_version < 30) {
2714             return( AFPERR_PARAM );
2715         }
2716         utf8 = 1;
2717     }
2718
2719     switch ( sfunc ) {
2720     case 1 :
2721     case 3 :/* unicode */
2722         memcpy( &id, ibuf, sizeof( id ));
2723         id = ntohl(id);
2724         if ( id != 0 ) {
2725             if (( pw = getpwuid( id )) == NULL ) {
2726                 return( AFPERR_NOITEM );
2727             }
2728             len = convert_string_allocate( obj->options.unixcharset, ((!utf8)?obj->options.maccharset:CH_UTF8_MAC),
2729                                            pw->pw_name, -1, &name);
2730         } else {
2731             len = 0;
2732             name = NULL;
2733         }
2734         break;
2735     case 2 :
2736     case 4 : /* unicode */
2737         memcpy( &id, ibuf, sizeof( id ));
2738         id = ntohl(id);
2739         if ( id != 0 ) {
2740             if (NULL == ( gr = (struct group *)getgrgid( id ))) {
2741                 return( AFPERR_NOITEM );
2742             }
2743             len = convert_string_allocate( obj->options.unixcharset, (!utf8)?obj->options.maccharset:CH_UTF8_MAC,
2744                                            gr->gr_name, -1, &name);
2745         } else {
2746             len = 0;
2747             name = NULL;
2748         }
2749         break;
2750 #ifdef HAVE_NFSv4_ACLS
2751     case 5 : /* UUID -> username */
2752     case 6 : /* UUID -> groupname */
2753         if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2754             return AFPERR_PARAM;
2755         LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request");
2756         uuidtype_t type;
2757         len = getnamefromuuid( ibuf, &name, &type);
2758         if (len != 0)       /* its a error code, not len */
2759             return AFPERR_NOITEM;
2760         if (type == UUID_USER) {
2761             if (( pw = getpwnam( name )) == NULL )
2762                 return( AFPERR_NOITEM );
2763             LOG(log_debug, logtype_afpd, "afp_mapid: name:%s -> uid:%d", name, pw->pw_uid);
2764             id = htonl(UUID_USER);
2765             memcpy( rbuf, &id, sizeof( id ));
2766             id = htonl( pw->pw_uid);
2767             rbuf += sizeof( id );
2768             memcpy( rbuf, &id, sizeof( id ));
2769             rbuf += sizeof( id );
2770             *rbuflen = 2 * sizeof( id );
2771         } else {        /* type == UUID_GROUP */
2772             if (( gr = getgrnam( name )) == NULL )
2773                 return( AFPERR_NOITEM );
2774             LOG(log_debug, logtype_afpd, "afp_mapid: group:%s -> gid:%d", name, gr->gr_gid);
2775             id = htonl(UUID_GROUP);
2776             memcpy( rbuf, &id, sizeof( id ));
2777             rbuf += sizeof( id );
2778             id = htonl( gr->gr_gid);
2779             memcpy( rbuf, &id, sizeof( id ));
2780             rbuf += sizeof( id );
2781             *rbuflen = 2 * sizeof( id );
2782         }
2783         break;
2784 #endif
2785     default :
2786         return( AFPERR_PARAM );
2787     }
2788
2789     if (name)
2790         len = strlen( name );
2791
2792     if (utf8) {
2793         u_int16_t tp = htons(len);
2794         memcpy(rbuf, &tp, sizeof(tp));
2795         rbuf += sizeof(tp);
2796         *rbuflen += 2;
2797     }
2798     else {
2799         *rbuf++ = len;
2800         *rbuflen += 1;
2801     }
2802     if ( len > 0 ) {
2803         memcpy( rbuf, name, len );
2804     }
2805     *rbuflen += len;
2806     if (name)
2807         free(name);
2808     return( AFP_OK );
2809 }
2810
2811 int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2812 {
2813     struct passwd   *pw;
2814     struct group    *gr;
2815     int             len, sfunc;
2816     u_int32_t       id;
2817     u_int16_t       ulen;
2818
2819     ibuf++;
2820     sfunc = (unsigned char) *ibuf++;
2821     *rbuflen = 0;
2822     LOG(log_debug, logtype_afpd, "afp_mapname: sfunc: %d, afp_version: %d", sfunc, afp_version);
2823     switch ( sfunc ) {
2824     case 1 :
2825     case 2 : /* unicode */
2826         if (afp_version < 30) {
2827             return( AFPERR_PARAM );
2828         }
2829         memcpy(&ulen, ibuf, sizeof(ulen));
2830         len = ntohs(ulen);
2831         ibuf += 2;
2832         LOG(log_debug, logtype_afpd, "afp_mapname: alive");
2833         break;
2834     case 3 :
2835     case 4 :
2836         len = (unsigned char) *ibuf++;
2837         break;
2838 #ifdef HAVE_NFSv4_ACLS
2839     case 5 : /* username -> UUID  */
2840     case 6 : /* groupname -> UUID */
2841         if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2842             return AFPERR_PARAM;
2843         memcpy(&ulen, ibuf, sizeof(ulen));
2844         len = ntohs(ulen);
2845         ibuf += 2;
2846         break;
2847 #endif
2848     default :
2849         return( AFPERR_PARAM );
2850     }
2851
2852     ibuf[ len ] = '\0';
2853
2854     if ( len == 0 )
2855         return AFPERR_PARAM;
2856     else {
2857         switch ( sfunc ) {
2858         case 1 : /* unicode */
2859         case 3 :
2860             if (NULL == ( pw = (struct passwd *)getpwnam( ibuf )) ) {
2861                 return( AFPERR_NOITEM );
2862             }
2863             id = pw->pw_uid;
2864             id = htonl(id);
2865             memcpy( rbuf, &id, sizeof( id ));
2866             *rbuflen = sizeof( id );
2867             break;
2868
2869         case 2 : /* unicode */
2870         case 4 :
2871             LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s",ibuf);
2872             if (NULL == ( gr = (struct group *)getgrnam( ibuf ))) {
2873                 return( AFPERR_NOITEM );
2874             }
2875             id = gr->gr_gid;
2876             LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s -> id: %d",ibuf, id);
2877             id = htonl(id);
2878             memcpy( rbuf, &id, sizeof( id ));
2879             *rbuflen = sizeof( id );
2880             break;
2881 #ifdef HAVE_NFSv4_ACLS
2882         case 5 :        /* username -> UUID */
2883             LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2884             if (0 != getuuidfromname(ibuf, UUID_USER, rbuf))
2885                 return AFPERR_NOITEM;
2886             *rbuflen = UUID_BINSIZE;
2887             break;
2888         case 6 :        /* groupname -> UUID */
2889             LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2890             if (0 != getuuidfromname(ibuf, UUID_GROUP, rbuf))
2891                 return AFPERR_NOITEM;
2892             *rbuflen = UUID_BINSIZE;
2893             break;
2894 #endif
2895         }
2896     }
2897     return( AFP_OK );
2898 }
2899
2900 /* ------------------------------------
2901    variable DID support
2902 */
2903 int afp_closedir(AFPObj *obj _U_, char *ibuf _U_, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2904 {
2905 #if 0
2906     struct vol   *vol;
2907     struct dir   *dir;
2908     u_int16_t    vid;
2909     u_int32_t    did;
2910 #endif /* 0 */
2911
2912     *rbuflen = 0;
2913
2914     /* do nothing as dids are static for the life of the process. */
2915 #if 0
2916     ibuf += 2;
2917
2918     memcpy(&vid,  ibuf, sizeof( vid ));
2919     ibuf += sizeof( vid );
2920     if (( vol = getvolbyvid( vid )) == NULL ) {
2921         return( AFPERR_PARAM );
2922     }
2923
2924     memcpy( &did, ibuf, sizeof( did ));
2925     ibuf += sizeof( did );
2926     if (( dir = dirlookup( vol, did )) == NULL ) {
2927         return( AFPERR_PARAM );
2928     }
2929
2930     /* dir_remove -- deletedid */
2931 #endif /* 0 */
2932
2933     return AFP_OK;
2934 }
2935
2936 /* did creation gets done automatically
2937  * there's a pb again with case but move it to cname
2938  */
2939 int afp_opendir(AFPObj *obj _U_, char *ibuf, size_t ibuflen  _U_, char *rbuf, size_t *rbuflen)
2940 {
2941     struct vol      *vol;
2942     struct dir      *parentdir;
2943     struct path     *path;
2944     u_int32_t       did;
2945     u_int16_t       vid;
2946
2947     *rbuflen = 0;
2948     ibuf += 2;
2949
2950     memcpy(&vid, ibuf, sizeof(vid));
2951     ibuf += sizeof( vid );
2952
2953     if (NULL == ( vol = getvolbyvid( vid )) ) {
2954         return( AFPERR_PARAM );
2955     }
2956
2957     memcpy(&did, ibuf, sizeof(did));
2958     ibuf += sizeof(did);
2959
2960     if (NULL == ( parentdir = dirlookup( vol, did )) ) {
2961         return afp_errno;
2962     }
2963
2964     if (NULL == ( path = cname( vol, parentdir, &ibuf )) ) {
2965         return get_afp_errno(AFPERR_PARAM);
2966     }
2967
2968     if ( *path->m_name != '\0' ) {
2969         return path_error(path, AFPERR_NOOBJ);
2970     }
2971
2972     if ( !path->st_valid && of_stat(path ) < 0 ) {
2973         return( AFPERR_NOOBJ );
2974     }
2975     if ( path->st_errno ) {
2976         return( AFPERR_NOOBJ );
2977     }
2978
2979     memcpy(rbuf, &curdir->d_did, sizeof(curdir->d_did));
2980     *rbuflen = sizeof(curdir->d_did);
2981     return AFP_OK;
2982 }