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