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