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