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