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