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