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