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