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