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