]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/directory.c
f59cf693d8c1deaa7a06800ac1eb5719dddaf278
[netatalk.git] / etc / afpd / directory.c
1 /*
2  * $Id: directory.c,v 1.103 2009-09-11 09:14:16 franklahm Exp $
3  *
4  * Copyright (c) 1990,1993 Regents of The University of Michigan.
5  * All Rights Reserved.  See COPYRIGHT.
6  *
7  * 19 jan 2000 implemented red-black trees for directory lookups
8  * (asun@cobalt.com).
9  */
10
11 #ifdef HAVE_CONFIG_H
12 #include "config.h"
13 #endif /* HAVE_CONFIG_H */
14
15 /* STDC check */
16 #if STDC_HEADERS
17 #include <string.h>
18 #else /* STDC_HEADERS */
19 #ifndef HAVE_STRCHR
20 #define strchr index
21 #define strrchr index
22 #endif /* HAVE_STRCHR */
23 char *strchr (), *strrchr ();
24 #ifndef HAVE_MEMCPY
25 #define memcpy(d,s,n) bcopy ((s), (d), (n))
26 #define memmove(d,s,n) bcopy ((s), (d), (n))
27 #endif /* ! HAVE_MEMCPY */
28 #endif /* STDC_HEADERS */
29 #ifdef HAVE_STRINGS_H
30 #include <strings.h>
31 #endif
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <dirent.h>
35
36 #include <grp.h>
37 #include <pwd.h>
38 #include <sys/param.h>
39 #include <errno.h>
40 #include <utime.h>
41 #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
708     if (check_name(vol, path->u_name)) {
709         /* the name is illegal */
710         LOG(log_info, logtype_afpd, "extenddir: illegal path: '%s'", path->u_name);
711         path->u_name = NULL;
712         afp_errno = AFPERR_PARAM;
713         return NULL;
714     }
715
716     if (of_stat( path ) != 0 ) {
717         if (!(vol->v_flags & AFPVOL_CASEINSEN))
718             return NULL;
719         else if(caseenumerate(vol, path, dir) != 0)
720             return(NULL);
721     }
722
723     if (!S_ISDIR(path->st.st_mode)) {
724         return( NULL );
725     }
726
727     /* mac name is always with the right encoding (from cname()) */
728     if (( dir = adddir( vol, dir, path)) == NULL ) {
729         return( NULL );
730     }
731
732     path->d_dir = dir;
733     if ( movecwd( vol, dir ) < 0 ) {
734         return( NULL );
735     }
736
737     return( dir );
738 }
739
740 /* -------------------
741    system rmdir with afp error code.
742    ENOENT is not an error.
743  */
744 int netatalk_rmdir(const char *name)
745 {
746     if (rmdir(name) < 0) {
747         switch ( errno ) {
748         case ENOENT :
749             break;
750         case ENOTEMPTY : 
751             return AFPERR_DIRNEMPT;
752         case EPERM:
753         case EACCES :
754             return AFPERR_ACCESS;
755         case EROFS:
756             return AFPERR_VLOCK;
757         default :
758             return AFPERR_PARAM;
759         }
760     }
761     return AFP_OK;
762 }
763
764 /* -------------------------
765    appledouble mkdir afp error code.
766 */
767 static int netatalk_mkdir(const char *name)
768 {
769     if (ad_mkdir(name, DIRBITS | 0777) < 0) {
770         switch ( errno ) {
771         case ENOENT :
772             return( AFPERR_NOOBJ );
773         case EROFS :
774             return( AFPERR_VLOCK );
775         case EPERM:
776         case EACCES :
777             return( AFPERR_ACCESS );
778         case EEXIST :
779             return( AFPERR_EXIST );
780         case ENOSPC :
781         case EDQUOT :
782             return( AFPERR_DFULL );
783         default :
784             return( AFPERR_PARAM );
785         }
786     }
787     return AFP_OK;
788 }
789
790 /* -------------------
791    system unlink with afp error code.
792    ENOENT is not an error.
793  */
794 int netatalk_unlink(const char *name)
795 {
796     if (unlink(name) < 0) {
797         switch (errno) {
798         case ENOENT :
799             break;
800         case EROFS:
801             return AFPERR_VLOCK;
802         case EPERM:
803         case EACCES :
804             return AFPERR_ACCESS;
805         default :
806             return AFPERR_PARAM;
807         }
808     }
809     return AFP_OK;
810 }
811
812 /* ------------------- */
813 static int deletedir(char *dir)
814 {
815     char path[MAXPATHLEN + 1];
816     DIR *dp;
817     struct dirent       *de;
818     struct stat st;
819     size_t len;
820     int err = AFP_OK;
821     size_t remain;
822
823     if ((len = strlen(dir)) +2 > sizeof(path))
824         return AFPERR_PARAM;
825
826     /* already gone */
827     if ((dp = opendir(dir)) == NULL)
828         return AFP_OK;
829
830     strcpy(path, dir);
831     strcat(path, "/");
832     len++;
833     remain = sizeof(path) -len -1;
834     while ((de = readdir(dp)) && err == AFP_OK) {
835         /* skip this and previous directory */
836         if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
837             continue;
838
839         if (strlen(de->d_name) > remain) {
840             err = AFPERR_PARAM;
841             break;
842         }
843         strcpy(path + len, de->d_name);
844         if (stat(path, &st)) {
845             continue;
846         }
847         if (S_ISDIR(st.st_mode)) {
848             err = deletedir(path);
849         } else {
850             err = netatalk_unlink(path);
851         }
852     }
853     closedir(dp);
854
855     /* okay. the directory is empty. delete it. note: we already got rid
856        of .AppleDouble.  */
857     if (err == AFP_OK) {
858         err = netatalk_rmdir(dir);
859     }
860     return err;
861 }
862
863 /* do a recursive copy. */
864 static int copydir(const struct vol *vol, char *src, char *dst)
865 {
866     char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
867     DIR *dp;
868     struct dirent       *de;
869     struct stat st;
870     struct utimbuf      ut;
871     size_t slen, dlen;
872     size_t srem, drem;
873     int err;
874
875     /* doesn't exist or the path is too long. */
876     if (((slen = strlen(src)) > sizeof(spath) - 2) ||
877             ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
878             ((dp = opendir(src)) == NULL))
879         return AFPERR_PARAM;
880
881     /* try to create the destination directory */
882     if (AFP_OK != (err = netatalk_mkdir(dst)) ) {
883         closedir(dp);
884         return err;
885     }
886
887     /* set things up to copy */
888     strcpy(spath, src);
889     strcat(spath, "/");
890     slen++;
891     srem = sizeof(spath) - slen -1;
892     
893     strcpy(dpath, dst);
894     strcat(dpath, "/");
895     dlen++;
896     drem = sizeof(dpath) - dlen -1;
897
898     err = AFP_OK;
899     while ((de = readdir(dp))) {
900         /* skip this and previous directory */
901         if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
902             continue;
903
904         if (strlen(de->d_name) > srem) {
905             err = AFPERR_PARAM;
906             break;
907         }
908         strcpy(spath + slen, de->d_name);
909
910         if (stat(spath, &st) == 0) {
911             if (strlen(de->d_name) > drem) {
912                 err = AFPERR_PARAM;
913                 break;
914             }
915             strcpy(dpath + dlen, de->d_name);
916
917             if (S_ISDIR(st.st_mode)) {
918                 if (AFP_OK != (err = copydir(vol, spath, dpath)))
919                     goto copydir_done;
920             } else if (AFP_OK != (err = copyfile(vol, vol, spath, dpath, NULL, NULL))) {
921                 goto copydir_done;
922
923             } else {
924                 /* keep the same time stamp. */
925                 ut.actime = ut.modtime = st.st_mtime;
926                 utime(dpath, &ut);
927             }
928         }
929     }
930
931     /* keep the same time stamp. */
932     if (stat(src, &st) == 0) {
933         ut.actime = ut.modtime = st.st_mtime;
934         utime(dst, &ut);
935     }
936
937 copydir_done:
938     closedir(dp);
939     return err;
940 }
941
942
943 /* --- public functions follow --- */
944
945 /* NOTE: we start off with at least one node (the root directory). */
946 static struct dir *dirinsert( vol, dir )
947             struct vol  *vol;
948 struct dir      *dir;
949 {
950     struct dir *node;
951
952     if ((node = dir_insert(vol, dir)))
953         return node;
954
955     /* recolor the tree. the current node is red. */
956     dir->d_color = DIRTREE_COLOR_RED;
957
958     /* parent of this node has to be black. if the parent node
959     * is red, then we have a grandparent. */
960     while ((dir != vol->v_root) &&
961             (dir->d_back->d_color == DIRTREE_COLOR_RED)) {
962         /* are we on the left tree? */
963         if (dir->d_back == dir->d_back->d_back->d_left) {
964             node = dir->d_back->d_back->d_right;  /* get the right node */
965             if (node->d_color == DIRTREE_COLOR_RED) {
966                 /* we're red. we need to change to black. */
967                 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
968                 node->d_color = DIRTREE_COLOR_BLACK;
969                 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
970                 dir = dir->d_back->d_back; /* finished. go up. */
971             } else {
972                 if (dir == dir->d_back->d_right) {
973                     dir = dir->d_back;
974                     dir_leftrotate(vol, dir);
975                 }
976                 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
977                 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
978                 dir_rightrotate(vol, dir->d_back->d_back);
979             }
980         } else {
981             node = dir->d_back->d_back->d_left;
982             if (node->d_color == DIRTREE_COLOR_RED) {
983                 /* we're red. we need to change to black. */
984                 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
985                 node->d_color = DIRTREE_COLOR_BLACK;
986                 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
987                 dir = dir->d_back->d_back; /* finished. ascend */
988             } else {
989                 if (dir == dir->d_back->d_left) {
990                     dir = dir->d_back;
991                     dir_rightrotate(vol, dir);
992                 }
993                 dir->d_back->d_color = DIRTREE_COLOR_BLACK;
994                 dir->d_back->d_back->d_color = DIRTREE_COLOR_RED;
995                 dir_leftrotate(vol, dir->d_back->d_back);
996             }
997         }
998     }
999
1000     vol->v_root->d_color = DIRTREE_COLOR_BLACK;
1001     return NULL;
1002 }
1003
1004 /* ---------------------------- */
1005 struct dir *
1006             adddir( vol, dir, path)
1007 struct vol      *vol;
1008 struct dir      *dir;
1009 struct path     *path;
1010 {
1011     struct dir  *cdir, *edir;
1012     int         upathlen;
1013     char        *name;
1014     char        *upath;
1015     struct stat *st;
1016     int         deleted;
1017     cnid_t      id;
1018
1019     upath = path->u_name;
1020     st    = &path->st;
1021     upathlen = strlen(upath);
1022
1023     id = get_id(vol, NULL, st, dir->d_did, upath, upathlen);
1024     if (id == 0) {
1025         return NULL;
1026     }
1027     if (!path->m_name && !(path->m_name = utompath(vol, upath, id , utf8_encoding()))) {
1028         return NULL;
1029     }
1030     name  = path->m_name;    
1031     if ((cdir = dirnew(name, upath)) == NULL) {
1032         LOG(log_error, logtype_afpd, "adddir: malloc: %s", strerror(errno) );
1033         return NULL;
1034     }
1035     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)) {
1036         LOG(log_error, logtype_afpd, "Couldn't set UCS2 name for %s", name);
1037         cdir->d_m_name_ucs2 = NULL;
1038     }
1039
1040     cdir->d_did = id;
1041
1042     if ((edir = dirinsert( vol, cdir ))) {
1043         /* it's not possible with LASTDID
1044            for CNID:
1045            - someone else have moved the directory.
1046            - it's a symlink inside the share.
1047            - it's an ID reused, the old directory was deleted but not
1048              the cnid record and the server've reused the inode for 
1049              the new dir.
1050            for HASH (we should get ride of HASH) 
1051            - someone else have moved the directory.
1052            - it's an ID reused as above
1053            - it's a hash duplicate and we are in big trouble
1054         */
1055         deleted = (edir->d_m_name == NULL);
1056         if (!deleted)
1057             dir_hash_del(vol, edir);
1058         dirfreename(edir);
1059         edir->d_m_name = cdir->d_m_name;
1060         edir->d_u_name = cdir->d_u_name;
1061         edir->d_m_name_ucs2 = cdir->d_m_name_ucs2;
1062         free(cdir);
1063         cdir = edir;
1064         LOG(log_error, logtype_afpd, "adddir: insert %s", edir->d_m_name);
1065         if (!cdir->d_parent || (cdir->d_parent == dir && !deleted)) {
1066             hash_alloc_insert(vol->v_hash, cdir, cdir);
1067             return cdir;
1068         }
1069         /* the old was not in the same folder */
1070         if (!deleted)
1071             dirchildremove(cdir->d_parent, cdir);
1072     }
1073
1074     /* parent/child directories */
1075     cdir->d_parent = dir;
1076     dirchildadd(vol, dir, cdir);
1077     return( cdir );
1078 }
1079
1080 /* --- public functions follow --- */
1081 /* free everything down. we don't bother to recolor as this is only
1082  * called to free the entire tree */
1083 void dirfreename(struct dir *dir)
1084 {
1085     if (dir->d_u_name != dir->d_m_name) {
1086         free(dir->d_u_name);
1087     }
1088     if (dir->d_m_name_ucs2)
1089         free(dir->d_m_name_ucs2); 
1090     free(dir->d_m_name);
1091 }
1092
1093 void dirfree( dir )
1094 struct dir      *dir;
1095 {
1096     if (!dir || (dir == SENTINEL))
1097         return;
1098
1099     if ( dir->d_left != SENTINEL ) {
1100         dirfree( dir->d_left );
1101     }
1102     if ( dir->d_right != SENTINEL ) {
1103         dirfree( dir->d_right );
1104     }
1105
1106     if (dir != SENTINEL) {
1107         dirfreename(dir);
1108         free( dir );
1109     }
1110 }
1111
1112 /* --------------------------------------------
1113  * most of the time mac name and unix name are the same 
1114 */
1115 struct dir *dirnew(const char *m_name, const char *u_name)
1116 {
1117     struct dir *dir;
1118
1119     dir = (struct dir *) calloc(1, sizeof( struct dir ));
1120     if (!dir)
1121         return NULL;
1122
1123     if ((dir->d_m_name = strdup(m_name)) == NULL) {
1124         free(dir);
1125         return NULL;
1126     }
1127
1128     if (m_name == u_name || !strcmp(m_name, u_name)) {
1129         dir->d_u_name = dir->d_m_name;
1130     }
1131     else if ((dir->d_u_name = strdup(u_name)) == NULL) {
1132         free(dir->d_m_name);
1133         free(dir);
1134         return NULL;
1135     }
1136
1137     dir->d_m_name_ucs2 = NULL;
1138     dir->d_left = dir->d_right = SENTINEL;
1139     dir->d_next = dir->d_prev = dir;
1140     return dir;
1141 }
1142
1143 /* ------------------ */
1144 static hash_val_t hash_fun_dir(const void *key)
1145 {
1146 const struct dir *k = key;
1147
1148     static unsigned long randbox[] = {
1149         0x49848f1bU, 0xe6255dbaU, 0x36da5bdcU, 0x47bf94e9U,
1150         0x8cbcce22U, 0x559fc06aU, 0xd268f536U, 0xe10af79aU,
1151         0xc1af4d69U, 0x1d2917b5U, 0xec4c304dU, 0x9ee5016cU,
1152         0x69232f74U, 0xfead7bb3U, 0xe9089ab6U, 0xf012f6aeU,
1153     };
1154
1155     const unsigned char *str = k->d_u_name;
1156     hash_val_t acc = 0;
1157
1158     while (*str) {
1159         acc ^= randbox[(*str + acc) & 0xf];
1160         acc = (acc << 1) | (acc >> 31);
1161         acc &= 0xffffffffU;
1162         acc ^= randbox[((*str++ >> 4) + acc) & 0xf];
1163         acc = (acc << 2) | (acc >> 30);
1164         acc &= 0xffffffffU;
1165     }
1166     return acc;
1167 }
1168
1169 /* ---------------- */
1170 static int hash_comp_dir(const void *key1, const void *key2)
1171 {
1172 const struct dir *k1 = key1;
1173 const struct dir *k2 = key2;
1174
1175     return !(k1->d_parent->d_did == k2->d_parent->d_did && !strcmp(k1->d_u_name, k2->d_u_name));
1176 }
1177
1178 /* ---------------- */
1179 hash_t *
1180 dirhash(void)
1181 {
1182     return hash_create(HASHCOUNT_T_MAX, hash_comp_dir, hash_fun_dir);
1183 }
1184
1185 /* ------------------ */
1186 static struct path *invalidate (const struct vol *vol, struct dir *dir, struct path *ret)
1187 {
1188     /* it's tricky:
1189        movecwd failed some of dir path are not there anymore.
1190        FIXME Is it true with other errors?
1191        so we remove dir from the cache 
1192     */
1193     if (dir->d_did == DIRDID_ROOT_PARENT)
1194         return NULL;
1195     if (afp_errno == AFPERR_ACCESS) {
1196         if ( movecwd( vol, dir->d_parent ) < 0 ) {
1197             return NULL;                
1198         }
1199         /* FIXME should we set these?, don't need to call stat() after:
1200            ret->st_valid = 1;
1201            ret->st_errno = EACCES;
1202        */
1203        ret->m_name = dir->d_m_name;
1204        ret->u_name = dir->d_u_name;
1205        ret->d_dir = dir;
1206        return ret;
1207     } else if (afp_errno == AFPERR_NOOBJ) {
1208        if ( movecwd( vol, dir->d_parent ) < 0 ) {
1209            return NULL;                 
1210        }
1211        strcpy(ret->m_name, dir->d_m_name);
1212        if (dir->d_m_name == dir->d_u_name) {
1213            ret->u_name = ret->m_name;
1214        }
1215        else {
1216            size_t tp = strlen(ret->m_name)+1;
1217            
1218            ret->u_name =  ret->m_name +tp;
1219            strcpy(ret->u_name, dir->d_u_name);
1220        }
1221        /* FIXME should we set :
1222           ret->st_valid = 1;
1223           ret->st_errno = ENOENT;
1224        */
1225        dir_invalidate(vol, dir);
1226        return ret;
1227     }
1228     dir_invalidate(vol, dir);
1229     return NULL;
1230 }
1231
1232 /* -------------------------------------------------- */
1233 /* cname 
1234  return
1235  if it's a filename:
1236       in extenddir:
1237          compute unix name
1238          stat the file or errno 
1239       return 
1240          filename
1241          curdir: filename parent directory
1242          
1243  if it's a dirname:
1244       not in the cache
1245           in extenddir
1246               compute unix name
1247               stat the dir or errno
1248           return
1249               if chdir error 
1250                  dirname 
1251                  curdir: dir parent directory
1252               sinon
1253                  dirname: ""
1254                  curdir: dir   
1255       in the cache 
1256           return
1257               if chdir error
1258                  dirname
1259                  curdir: dir parent directory 
1260               else
1261                  dirname: ""
1262                  curdir: dir   
1263                  
1264 */
1265 struct path *
1266 cname( vol, dir, cpath )
1267 const struct vol        *vol;
1268 struct dir      *dir;
1269 char    **cpath;
1270 {
1271     struct dir             *cdir, *scdir=NULL;
1272     static char            path[ MAXPATHLEN + 1];
1273     static struct path ret;
1274
1275     char                *data, *p;
1276     int                 extend = 0;
1277     int                 len;
1278     u_int32_t   hint;
1279     u_int16_t   len16;
1280     int         size = 0;
1281     char        sep;
1282     int         toUTF8 = 0;
1283
1284     data = *cpath;
1285     afp_errno = AFPERR_NOOBJ;
1286     memset(&ret, 0, sizeof(ret));
1287     switch (ret.m_type = *data) { /* path type */
1288     case 2:
1289        data++;
1290        len = (unsigned char) *data++;
1291        size = 2;
1292        sep = 0;
1293        if (afp_version >= 30) {
1294            ret.m_type = 3;
1295            toUTF8 = 1;
1296        }
1297        break;
1298     case 3:
1299        if (afp_version >= 30) {
1300            data++;
1301            memcpy(&hint, data, sizeof(hint));
1302            hint = ntohl(hint);
1303            data += sizeof(hint);
1304            
1305            memcpy(&len16, data, sizeof(len16));
1306            len = ntohs(len16);
1307            data += 2;
1308            size = 7;
1309            sep = 0; /* '/';*/
1310            break;
1311         }
1312         /* else it's an error */
1313     default:
1314         afp_errno = AFPERR_PARAM;
1315         return( NULL );
1316     }
1317     *cpath += len + size;
1318     *path = '\0';
1319     ret.m_name = path;
1320     for ( ;; ) {
1321         if ( len == 0 ) {
1322             if (movecwd( vol, dir ) < 0 ) {
1323                 return invalidate(vol, dir, &ret );
1324             }
1325             if (*path == '\0') {
1326                ret.u_name = ".";
1327                ret.d_dir = dir;
1328             }               
1329             return &ret;
1330         }
1331
1332         if (*data == sep ) {
1333             data++;
1334             len--;
1335         }
1336         while (*data == sep && len > 0 ) {
1337             if ( dir->d_parent == NULL ) {
1338                 return NULL;
1339             }
1340             dir = dir->d_parent;
1341             data++;
1342             len--;
1343         }
1344
1345         /* would this be faster with strlen + strncpy? */
1346         p = path;
1347         while ( *data != sep && len > 0 ) {
1348             *p++ = *data++;
1349             if (p > &path[ MAXPATHLEN]) {
1350                 afp_errno = AFPERR_PARAM;
1351                 return( NULL );
1352             }
1353             len--;
1354         }
1355
1356         /* short cut bits by chopping off a trailing \0. this also
1357                   makes the traversal happy w/ filenames at the end of the
1358                   cname. */
1359         if (len == 1)
1360             len--;
1361
1362         *p = '\0';
1363
1364         if ( p == path ) { /* end of the name parameter */
1365             continue;
1366         }
1367         ret.u_name = NULL;
1368         if (afp_version >= 30) {
1369             char *t;
1370             cnid_t fileid;
1371                 
1372             if (toUTF8) {
1373                 static char     temp[ MAXPATHLEN + 1];
1374
1375                 if (dir->d_did == DIRDID_ROOT_PARENT) {
1376                     /*
1377                       With uft8 volume name is utf8-mac, but requested path may be a mangled longname. See #2611981.
1378                       So we compare it with the longname from the current volume and if they match
1379                       we overwrite the requested path with the utf8 volume name so that the following
1380                       strcmp can match.
1381                     */
1382                     ucs2_to_charset(vol->v_maccharset, vol->v_macname, temp, AFPVOL_MACNAMELEN + 1);
1383                     if (strcasecmp( path, temp) == 0)
1384                         ucs2_to_charset(CH_UTF8_MAC, vol->v_u8mname, path, AFPVOL_U8MNAMELEN);
1385                 } else {
1386                     /* toUTF8 */
1387                     if (mtoUTF8(vol, path, strlen(path), temp, MAXPATHLEN) == (size_t)-1) {
1388                         afp_errno = AFPERR_PARAM;
1389                         return( NULL );
1390                     }
1391                     strcpy(path, temp);
1392                 }
1393             }
1394             /* check for OS X mangled filename :( */
1395             
1396             t = demangle_osx(vol, path, dir->d_did, &fileid);
1397             if (t != path) {
1398                 ret.u_name = t;
1399                 /* duplicate work but we can't reuse all convert_char we did in demangle_osx 
1400                  * flags weren't the same
1401                  */
1402                  if ( (t = utompath(vol, ret.u_name, fileid, utf8_encoding())) ) {
1403                      /* at last got our view of mac name */
1404                      strcpy(path,t);
1405                  }                    
1406             }
1407         }
1408         if (ret.u_name == NULL) {
1409             if (!(ret.u_name = mtoupath(vol, ret.m_name, dir->d_did, utf8_encoding()))) {
1410                 afp_errno = AFPERR_PARAM;
1411                 return NULL;
1412             }
1413         }
1414         if ( !extend ) {
1415             ucs2_t *tmpname;
1416             cdir = dir->d_child;
1417             scdir = NULL;
1418             if ( cdir && (vol->v_flags & AFPVOL_CASEINSEN) &&
1419                     (size_t)-1 != convert_string_allocate(((ret.m_type == 3)?CH_UTF8_MAC:vol->v_maccharset), 
1420                                                           CH_UCS2, path, strlen(path), (char **)&tmpname) )
1421             {
1422                 while (cdir) {
1423                     if (!cdir->d_m_name_ucs2) {
1424                         LOG(log_error, logtype_afpd, "cname: no UCS2 name for %s (did %u)!!!", cdir->d_m_name, ntohl(cdir->d_did) );
1425                         /* this shouldn't happen !!!! */
1426                         goto noucsfallback;
1427                     }
1428
1429                     if ( strcmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
1430                          break;
1431                     }
1432                     if ( strcasecmp_w( cdir->d_m_name_ucs2, tmpname ) == 0 ) {
1433                          scdir = cdir;
1434                     }
1435                     cdir = (cdir == dir->d_child->d_prev) ? NULL :cdir->d_next;
1436                 }
1437                 free(tmpname);
1438             }
1439             else {
1440 noucsfallback:
1441                 if (dir->d_did == DIRDID_ROOT_PARENT) {
1442                   /* 
1443                      root parent (did 1) has one child: the volume. Requests for did=1 with some <name>
1444                      must check against the volume name.
1445                   */
1446                   if (!strcmp(vol->v_dir->d_m_name, ret.m_name))
1447                       cdir = vol->v_dir;
1448                   else
1449                       cdir = NULL;
1450                 }
1451                 else {
1452                   cdir = dirsearch_byname(vol, dir, ret.u_name);
1453                 }
1454             }
1455   
1456             if (cdir == NULL && scdir != NULL) {
1457                 cdir = scdir;
1458                 /* LOG(log_debug, logtype_afpd, "cname: using casediff for %s, (%s = %s)", fullpathname(cdir->d_u_name), cdir->d_m_name, path ); */
1459             }
1460
1461             if ( cdir == NULL ) {
1462                 ++extend;
1463                 /* if dir == curdir it always succeed,
1464                  even if curdir is deleted. 
1465                  it's not a pb because it will fail in extenddir
1466                 */
1467                 if ( movecwd( vol, dir ) < 0 ) {
1468                     /* dir is not valid anymore 
1469                        we delete dir from the cache and abort.
1470                     */
1471                     if ( dir->d_did == DIRDID_ROOT_PARENT) {
1472                          afp_errno = AFPERR_NOOBJ;
1473                          return NULL;
1474                     }
1475                     if (afp_errno == AFPERR_ACCESS)
1476                         return NULL;
1477                     dir_invalidate(vol, dir);
1478                     return NULL;
1479                 }
1480                 cdir = extenddir( vol, dir, &ret );
1481             }
1482
1483         } else {
1484             cdir = extenddir( vol, dir, &ret );
1485         } /* if (!extend) */
1486
1487         if ( cdir == NULL ) {
1488
1489             if ( len > 0 || !ret.u_name ) {
1490                 return NULL;
1491             }
1492
1493         } else {
1494             dir = cdir; 
1495             *path = '\0';
1496         }
1497     } /* for (;;) */
1498 }
1499
1500 /*
1501  * Move curdir to dir, with a possible chdir()
1502  */
1503 int movecwd( vol, dir)
1504 const struct vol        *vol;
1505 struct dir      *dir;
1506 {
1507     char path[MAXPATHLEN + 1];
1508     struct dir  *d;
1509     char        *p, *u;
1510     int         n;
1511
1512     if ( dir == curdir ) {
1513         return( 0 );
1514     }
1515     if ( dir->d_did == DIRDID_ROOT_PARENT) {
1516         afp_errno = AFPERR_DID1; /* AFPERR_PARAM;*/
1517         return( -1 );
1518     }
1519
1520     p = path + sizeof(path) - 1;
1521     *p-- = '\0';
1522     *p = '.';
1523     for ( d = dir; d->d_parent != NULL && d != curdir; d = d->d_parent ) {
1524         u = d->d_u_name;
1525         if (!u) {
1526             /* parent directory is deleted */
1527             afp_errno = AFPERR_NOOBJ;
1528             return -1;
1529         }
1530         n = strlen( u );
1531         if (p -n -1 < path) {
1532             afp_errno = AFPERR_PARAM;
1533             return -1;
1534         }
1535         *--p = '/';
1536         p -= n;
1537         memcpy( p, u, n );
1538     }
1539     if ( d != curdir ) {
1540         n = strlen( vol->v_path );
1541         if (p -n -1 < path) {
1542             afp_errno = AFPERR_PARAM;
1543             return -1;
1544         }
1545         *--p = '/';
1546         p -= n;
1547         memcpy( p, vol->v_path, n );
1548     }
1549     if ( chdir( p ) < 0 ) {
1550         switch (errno) {
1551         case EACCES:
1552         case EPERM:
1553             afp_errno = AFPERR_ACCESS;
1554             break;
1555         default:
1556             afp_errno = AFPERR_NOOBJ;
1557         
1558         }
1559         return( -1 );
1560     }
1561     curdir = dir;
1562     return( 0 );
1563 }
1564
1565 /*
1566  * We can't use unix file's perm to support Apple's inherited protection modes.
1567  * If we aren't the file's owner we can't change its perms when moving it and smb
1568  * nfs,... don't even try.
1569 */
1570 #define AFP_CHECK_ACCESS 
1571
1572 int check_access(char *path, int mode)
1573 {
1574 #ifdef AFP_CHECK_ACCESS
1575 struct maccess ma;
1576 char *p;
1577
1578     p = ad_dir(path);
1579     if (!p)
1580        return -1;
1581
1582     accessmode(p, &ma, curdir, NULL);
1583     if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1584         return -1;
1585     if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1586         return -1;
1587 #endif
1588     return 0;
1589 }
1590
1591 /* --------------------- */
1592 int file_access(struct path *path, int mode)
1593 {
1594 struct maccess ma;
1595
1596     accessmode(path->u_name, &ma, curdir, &path->st);
1597     if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1598         return -1;
1599     if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1600         return -1;
1601     return 0;
1602
1603 }
1604
1605 /* --------------------- */
1606 void setdiroffcnt(struct dir *dir, struct stat *st,  u_int32_t count)
1607 {
1608     dir->offcnt = count;
1609     dir->ctime = st->st_ctime;
1610     dir->d_flags &= ~DIRF_CNID;
1611 }
1612
1613 /* ---------------------  
1614  * is our cached offspring count valid?
1615 */
1616
1617 int diroffcnt(struct dir *dir, struct stat *st)
1618 {
1619     return st->st_ctime == dir->ctime;
1620 }
1621
1622 /* ---------------------  
1623  * is our cached also for reenumerate id?
1624 */
1625
1626 int dirreenumerate(struct dir *dir, struct stat *st)
1627 {
1628     return st->st_ctime == dir->ctime && (dir->d_flags & DIRF_CNID);
1629 }
1630
1631 /* --------------------- */
1632 static int invisible_dots(const struct vol *vol, const char *name)
1633
1634   return vol_inv_dots(vol) && *name  == '.' && strcmp(name, ".") && strcmp(name, "..");
1635 }
1636
1637 /* ------------------------------ 
1638    (".", curdir)
1639    (name, dir) with curdir:name == dir, from afp_enumerate
1640 */
1641
1642 int getdirparams(const struct vol *vol,
1643                  u_int16_t bitmap, struct path *s_path,
1644                  struct dir *dir, 
1645                  char *buf, int *buflen )
1646 {
1647     struct maccess      ma;
1648     struct adouble      ad;
1649     char                *data, *l_nameoff = NULL, *utf_nameoff = NULL;
1650     int                 bit = 0, isad = 0;
1651     u_int32_t           aint;
1652     u_int16_t           ashort;
1653     int                 ret;
1654     u_int32_t           utf8 = 0;
1655     cnid_t              pdid;
1656     struct stat *st = &s_path->st;
1657     char *upath = s_path->u_name;
1658     
1659     if ((bitmap & ((1 << DIRPBIT_ATTR)  |
1660                    (1 << DIRPBIT_CDATE) |
1661                    (1 << DIRPBIT_MDATE) |
1662                    (1 << DIRPBIT_BDATE) |
1663                    (1 << DIRPBIT_FINFO)))) {
1664
1665         ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1666         if ( !ad_metadata( upath, ADFLAGS_DIR, &ad) ) {
1667             isad = 1;
1668         }
1669     }
1670     
1671     if ( dir->d_did == DIRDID_ROOT) {
1672         pdid = DIRDID_ROOT_PARENT;
1673     } else if (dir->d_did == DIRDID_ROOT_PARENT) {
1674         pdid = 0;
1675     } else {
1676         pdid = dir->d_parent->d_did;
1677     }
1678     
1679     data = buf;
1680     while ( bitmap != 0 ) {
1681         while (( bitmap & 1 ) == 0 ) {
1682             bitmap = bitmap>>1;
1683             bit++;
1684         }
1685
1686         switch ( bit ) {
1687         case DIRPBIT_ATTR :
1688             if ( isad ) {
1689                 ad_getattr(&ad, &ashort);
1690             } else if (invisible_dots(vol, dir->d_u_name)) {
1691                 ashort = htons(ATTRBIT_INVISIBLE);
1692             } else
1693                 ashort = 0;
1694             ashort |= htons(ATTRBIT_SHARED);
1695             memcpy( data, &ashort, sizeof( ashort ));
1696             data += sizeof( ashort );
1697             break;
1698
1699         case DIRPBIT_PDID :
1700             memcpy( data, &pdid, sizeof( pdid ));
1701             data += sizeof( pdid );
1702             break;
1703
1704         case DIRPBIT_CDATE :
1705             if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
1706                 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1707             memcpy( data, &aint, sizeof( aint ));
1708             data += sizeof( aint );
1709             break;
1710
1711         case DIRPBIT_MDATE :
1712             aint = AD_DATE_FROM_UNIX(st->st_mtime);
1713             memcpy( data, &aint, sizeof( aint ));
1714             data += sizeof( aint );
1715             break;
1716
1717         case DIRPBIT_BDATE :
1718             if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
1719                 aint = AD_DATE_START;
1720             memcpy( data, &aint, sizeof( aint ));
1721             data += sizeof( aint );
1722             break;
1723
1724         case DIRPBIT_FINFO :
1725             if ( isad ) {
1726                 memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
1727             } else { /* no appledouble */
1728                 memset( data, 0, 32 );
1729                 /* set default view -- this also gets done in ad_open() */
1730                 ashort = htons(FINDERINFO_CLOSEDVIEW);
1731                 memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
1732
1733                 /* dot files are by default visible */
1734                 if (invisible_dots(vol, dir->d_u_name)) {
1735                     ashort = htons(FINDERINFO_INVISIBLE);
1736                     memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
1737                 }
1738             }
1739             data += 32;
1740             break;
1741
1742         case DIRPBIT_LNAME :
1743             if (dir->d_m_name) /* root of parent can have a null name */
1744                 l_nameoff = data;
1745             else
1746                 memset(data, 0, sizeof(u_int16_t));
1747             data += sizeof( u_int16_t );
1748             break;
1749
1750         case DIRPBIT_SNAME :
1751             memset(data, 0, sizeof(u_int16_t));
1752             data += sizeof( u_int16_t );
1753             break;
1754
1755         case DIRPBIT_DID :
1756             memcpy( data, &dir->d_did, sizeof( aint ));
1757             data += sizeof( aint );
1758             break;
1759
1760         case DIRPBIT_OFFCNT :
1761             ashort = 0;
1762             /* this needs to handle current directory access rights */
1763             if (diroffcnt(dir, st)) {
1764                 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1765             }
1766             else if ((ret = for_each_dirent(vol, upath, NULL,NULL)) >= 0) {
1767                 setdiroffcnt(dir, st,  ret);
1768                 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1769             }
1770             ashort = htons( ashort );
1771             memcpy( data, &ashort, sizeof( ashort ));
1772             data += sizeof( ashort );
1773             break;
1774
1775         case DIRPBIT_UID :
1776             aint = htonl(st->st_uid);
1777             memcpy( data, &aint, sizeof( aint ));
1778             data += sizeof( aint );
1779             break;
1780
1781         case DIRPBIT_GID :
1782             aint = htonl(st->st_gid);
1783             memcpy( data, &aint, sizeof( aint ));
1784             data += sizeof( aint );
1785             break;
1786
1787         case DIRPBIT_ACCESS :
1788             accessmode( upath, &ma, dir , st);
1789
1790             *data++ = ma.ma_user;
1791             *data++ = ma.ma_world;
1792             *data++ = ma.ma_group;
1793             *data++ = ma.ma_owner;
1794             break;
1795
1796             /* Client has requested the ProDOS information block.
1797                Just pass back the same basic block for all
1798                directories. <shirsch@ibm.net> */
1799         case DIRPBIT_PDINFO :                     
1800             if (afp_version >= 30) { /* UTF8 name */
1801                 utf8 = kTextEncodingUTF8;
1802                 if (dir->d_m_name) /* root of parent can have a null name */
1803                     utf_nameoff = data;
1804                 else
1805                     memset(data, 0, sizeof(u_int16_t));
1806                 data += sizeof( u_int16_t );
1807                 aint = 0;
1808                 memcpy(data, &aint, sizeof( aint ));
1809                 data += sizeof( aint );
1810             }
1811             else { /* ProDOS Info Block */
1812                 *data++ = 0x0f;
1813                 *data++ = 0;
1814                 ashort = htons( 0x0200 );
1815                 memcpy( data, &ashort, sizeof( ashort ));
1816                 data += sizeof( ashort );
1817                 memset( data, 0, sizeof( ashort ));
1818                 data += sizeof( ashort );
1819             }
1820             break;
1821
1822         case DIRPBIT_UNIXPR :
1823             aint = htonl(st->st_uid);
1824             memcpy( data, &aint, sizeof( aint ));
1825             data += sizeof( aint );
1826             aint = htonl(st->st_gid);
1827             memcpy( data, &aint, sizeof( aint ));
1828             data += sizeof( aint );
1829         
1830             aint = st->st_mode;
1831             aint = htonl ( aint & ~S_ISGID );  /* Remove SGID, OSX doesn't like it ... */
1832             memcpy( data, &aint, sizeof( aint ));
1833             data += sizeof( aint );
1834
1835             accessmode( upath, &ma, dir , st);
1836
1837             *data++ = ma.ma_user;
1838             *data++ = ma.ma_world;
1839             *data++ = ma.ma_group;
1840             *data++ = ma.ma_owner;
1841             break;
1842             
1843         default :
1844             if ( isad ) {
1845                 ad_close_metadata( &ad );
1846             }
1847             return( AFPERR_BITMAP );
1848         }
1849         bitmap = bitmap>>1;
1850         bit++;
1851     }
1852     if ( l_nameoff ) {
1853         ashort = htons( data - buf );
1854         memcpy( l_nameoff, &ashort, sizeof( ashort ));
1855         data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, 0);
1856     }
1857     if ( utf_nameoff ) {
1858         ashort = htons( data - buf );
1859         memcpy( utf_nameoff, &ashort, sizeof( ashort ));
1860         data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, utf8);
1861     }
1862     if ( isad ) {
1863         ad_close_metadata( &ad );
1864     }
1865     *buflen = data - buf;
1866     return( AFP_OK );
1867 }
1868
1869 /* ----------------------------- */
1870 int path_error(struct path *path, int error)
1871 {
1872 /* - a dir with access error
1873  * - no error it's a file
1874  * - file not found
1875  */
1876     if (path_isadir(path))
1877         return afp_errno;
1878     if (path->st_valid && path->st_errno)
1879         return error;
1880     return AFPERR_BADTYPE ;
1881 }
1882
1883 /* ----------------------------- */
1884 int afp_setdirparams(obj, ibuf, ibuflen, rbuf, rbuflen )
1885 AFPObj  *obj;
1886 char    *ibuf, *rbuf _U_;
1887 int     ibuflen _U_, *rbuflen;
1888 {
1889     struct vol  *vol;
1890     struct dir  *dir;
1891     struct path *path;
1892     u_int16_t   vid, bitmap;
1893     u_int32_t   did;
1894     int         rc;
1895
1896     *rbuflen = 0;
1897     ibuf += 2;
1898     memcpy( &vid, ibuf, sizeof( vid ));
1899     ibuf += sizeof( vid );
1900
1901     if (NULL == ( vol = getvolbyvid( vid )) ) {
1902         return( AFPERR_PARAM );
1903     }
1904
1905     if (vol->v_flags & AFPVOL_RO)
1906         return AFPERR_VLOCK;
1907
1908     memcpy( &did, ibuf, sizeof( did ));
1909     ibuf += sizeof( int );
1910
1911     if (NULL == ( dir = dirlookup( vol, did )) ) {
1912         return afp_errno;
1913     }
1914
1915     memcpy( &bitmap, ibuf, sizeof( bitmap ));
1916     bitmap = ntohs( bitmap );
1917     ibuf += sizeof( bitmap );
1918
1919     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1920         return get_afp_errno(AFPERR_NOOBJ); 
1921     }
1922
1923     if ( *path->m_name != '\0' ) {
1924         rc = path_error(path, AFPERR_NOOBJ);
1925         /* maybe we are trying to set perms back */
1926         if (rc != AFPERR_ACCESS)
1927             return rc;
1928     }
1929
1930     /*
1931      * If ibuf is odd, make it even.
1932      */
1933     if ((u_long)ibuf & 1 ) {
1934         ibuf++;
1935     }
1936
1937     if (AFP_OK == ( rc = setdirparams(vol, path, bitmap, ibuf )) ) {
1938         setvoltime(obj, vol );
1939     }
1940     return( rc );
1941 }
1942
1943 /*
1944  * cf AFP3.0.pdf page 244 for change_mdate and change_parent_mdate logic  
1945  *
1946  * assume path == '\0' eg. it's a directory in canonical form
1947 */
1948
1949 struct path Cur_Path = {
1950     0,
1951     "",  /* mac name */
1952     ".", /* unix name */
1953     0,   /* id */
1954     NULL,/* struct dir */
1955     0,   /* stat is not set */
1956 };
1957
1958 /* ------------------ */
1959 static int set_dir_errors(struct path *path, const char *where, int err)
1960 {
1961     switch ( err ) {
1962     case EPERM :
1963     case EACCES :
1964         return AFPERR_ACCESS;
1965     case EROFS :
1966         return AFPERR_VLOCK;
1967     }
1968     LOG(log_error, logtype_afpd, "setdirparam(%s): %s: %s", fullpathname(path->u_name), where, strerror(err) );
1969     return AFPERR_PARAM;
1970 }
1971  
1972 /* ------------------ */
1973 int setdirparams(const struct vol *vol, 
1974                  struct path *path, u_int16_t d_bitmap, char *buf )
1975 {
1976     struct maccess      ma;
1977     struct adouble      ad;
1978     struct utimbuf      ut;
1979     struct timeval      tv;
1980
1981     char                *upath;
1982     struct dir          *dir;
1983     int                 bit, isad = 1;
1984     int                 cdate, bdate;
1985     int                 owner, group;
1986     u_int16_t           ashort, bshort;
1987     int                 err = AFP_OK;
1988     int                 change_mdate = 0;
1989     int                 change_parent_mdate = 0;
1990     int                 newdate = 0;
1991     u_int16_t           bitmap = d_bitmap;
1992     u_char              finder_buf[32];
1993     u_int32_t           upriv;
1994     mode_t              mpriv = 0;        
1995     u_int16_t           upriv_bit = 0;
1996
1997     bit = 0;
1998     upath = path->u_name;
1999     dir   = path->d_dir;
2000     while ( bitmap != 0 ) {
2001         while (( bitmap & 1 ) == 0 ) {
2002             bitmap = bitmap>>1;
2003             bit++;
2004         }
2005
2006         switch( bit ) {
2007         case DIRPBIT_ATTR :
2008             change_mdate = 1;
2009             memcpy( &ashort, buf, sizeof( ashort ));
2010             buf += sizeof( ashort );
2011             break;
2012         case DIRPBIT_CDATE :
2013             change_mdate = 1;
2014             memcpy(&cdate, buf, sizeof(cdate));
2015             buf += sizeof( cdate );
2016             break;
2017         case DIRPBIT_MDATE :
2018             memcpy(&newdate, buf, sizeof(newdate));
2019             buf += sizeof( newdate );
2020             break;
2021         case DIRPBIT_BDATE :
2022             change_mdate = 1;
2023             memcpy(&bdate, buf, sizeof(bdate));
2024             buf += sizeof( bdate );
2025             break;
2026         case DIRPBIT_FINFO :
2027             change_mdate = 1;
2028             memcpy( finder_buf, buf, 32 );
2029             buf += 32;
2030             break;
2031         case DIRPBIT_UID :      /* What kind of loser mounts as root? */
2032             change_parent_mdate = 1;
2033             memcpy( &owner, buf, sizeof(owner));
2034             buf += sizeof( owner );
2035             break;
2036         case DIRPBIT_GID :
2037             change_parent_mdate = 1;
2038             memcpy( &group, buf, sizeof( group ));
2039             buf += sizeof( group );
2040             break;
2041         case DIRPBIT_ACCESS :
2042             change_mdate = 1;
2043             change_parent_mdate = 1;
2044             ma.ma_user = *buf++;
2045             ma.ma_world = *buf++;
2046             ma.ma_group = *buf++;
2047             ma.ma_owner = *buf++;
2048             mpriv = mtoumode( &ma ) | vol->v_dperm;
2049             if (dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2050                 err = set_dir_errors(path, "setdirmode", errno);
2051                 bitmap = 0;
2052             }
2053             break;
2054         /* Ignore what the client thinks we should do to the
2055            ProDOS information block.  Skip over the data and
2056            report nothing amiss. <shirsch@ibm.net> */
2057         case DIRPBIT_PDINFO :
2058             if (afp_version < 30) {
2059                 buf += 6;
2060             }
2061             else {
2062                 err = AFPERR_BITMAP;
2063                 bitmap = 0;
2064             }
2065             break;
2066         case DIRPBIT_UNIXPR :
2067             if (vol_unix_priv(vol)) {
2068                 memcpy( &owner, buf, sizeof(owner)); /* FIXME need to change owner too? */
2069                 buf += sizeof( owner );
2070                 memcpy( &group, buf, sizeof( group ));
2071                 buf += sizeof( group );
2072
2073                 change_mdate = 1;
2074                 change_parent_mdate = 1;
2075                 memcpy( &upriv, buf, sizeof( upriv ));
2076                 buf += sizeof( upriv );
2077                 upriv = ntohl (upriv) | vol->v_dperm;
2078                 if (dir_rx_set(upriv)) {
2079                     /* maybe we are trying to set perms back */
2080                     if ( setdirunixmode(vol, upath, upriv) < 0 ) {
2081                         bitmap = 0;
2082                         err = set_dir_errors(path, "setdirunixmode", errno);
2083                     }
2084                 }
2085                 else {
2086                     /* do it later */
2087                     upriv_bit = 1;
2088                 }
2089                 break;
2090             }
2091             /* fall through */
2092         default :
2093             err = AFPERR_BITMAP;
2094             bitmap = 0;
2095             break;
2096         }
2097
2098         bitmap = bitmap>>1;
2099         bit++;
2100     }
2101     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2102
2103     if (ad_open_metadata( upath, vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad) < 0) {
2104         /*
2105          * Check to see what we're trying to set.  If it's anything
2106          * but ACCESS, UID, or GID, give an error.  If it's any of those
2107          * three, we don't need the ad to be open, so just continue.
2108          *
2109          * note: we also don't need to worry about mdate. also, be quiet
2110          *       if we're using the noadouble option.
2111          */
2112         if (!vol_noadouble(vol) && (d_bitmap &
2113                                     ~((1<<DIRPBIT_ACCESS)|(1<<DIRPBIT_UNIXPR)|
2114                                       (1<<DIRPBIT_UID)|(1<<DIRPBIT_GID)|
2115                                       (1<<DIRPBIT_MDATE)|(1<<DIRPBIT_PDINFO)))) {
2116             return AFPERR_ACCESS;
2117         }
2118
2119         isad = 0;
2120     } else {
2121         /*
2122          * Check to see if a create was necessary. If it was, we'll want
2123          * to set our name, etc.
2124          */
2125         if ( (ad_get_HF_flags( &ad ) & O_CREAT)) {
2126             ad_setname(&ad, curdir->d_m_name);
2127         }
2128     }
2129
2130     bit = 0;
2131     bitmap = d_bitmap;
2132     while ( bitmap != 0 ) {
2133         while (( bitmap & 1 ) == 0 ) {
2134             bitmap = bitmap>>1;
2135             bit++;
2136         }
2137
2138         switch( bit ) {
2139         case DIRPBIT_ATTR :
2140             if (isad) {
2141                 ad_getattr(&ad, &bshort);
2142                 if ((bshort & htons(ATTRBIT_INVISIBLE)) != 
2143                     (ashort & htons(ATTRBIT_INVISIBLE) & htons(ATTRBIT_SETCLR)) )
2144                     change_parent_mdate = 1;
2145                 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
2146                     bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
2147                 } else {
2148                     bshort &= ~ashort;
2149                 }
2150                 ad_setattr(&ad, bshort);
2151             }
2152             break;
2153         case DIRPBIT_CDATE :
2154             if (isad) {
2155                 ad_setdate(&ad, AD_DATE_CREATE, cdate);
2156             }
2157             break;
2158         case DIRPBIT_MDATE :
2159             break;
2160         case DIRPBIT_BDATE :
2161             if (isad) {
2162                 ad_setdate(&ad, AD_DATE_BACKUP, bdate);
2163             }
2164             break;
2165         case DIRPBIT_FINFO :
2166             if (isad) {
2167                 /* Fixes #2802236 */
2168                 u_int16_t *fflags = (u_int16_t *)(finder_buf + FINDERINFO_FRFLAGOFF);
2169                 *fflags &= htons(~FINDERINFO_ISHARED);
2170                 /* #2802236 end */
2171                 if (  dir->d_did == DIRDID_ROOT ) {
2172                     /*
2173                      * Alright, we admit it, this is *really* sick!
2174                      * The 4 bytes that we don't copy, when we're dealing
2175                      * with the root of a volume, are the directory's
2176                      * location information. This eliminates that annoying
2177                      * behavior one sees when mounting above another mount
2178                      * point.
2179                      */
2180                     memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 10 );
2181                     memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, finder_buf + 14, 18 );
2182                 } else {
2183                     memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 32 );
2184                 }
2185             }
2186             break;
2187         case DIRPBIT_UID :      /* What kind of loser mounts as root? */
2188             if ( (dir->d_did == DIRDID_ROOT) &&
2189                     (setdeskowner( ntohl(owner), -1 ) < 0)) {
2190                 err = set_dir_errors(path, "setdeskowner", errno);
2191                 if (isad && err == AFPERR_PARAM) {
2192                     err = AFP_OK; /* ???*/
2193                 }
2194                 else {
2195                     goto setdirparam_done;
2196                 }
2197             }
2198             if ( setdirowner(vol, upath, ntohl(owner), -1 ) < 0 ) {
2199                 err = set_dir_errors(path, "setdirowner", errno);
2200                 goto setdirparam_done;
2201             }
2202             break;
2203         case DIRPBIT_GID :
2204             if (dir->d_did == DIRDID_ROOT)
2205                 setdeskowner( -1, ntohl(group) ); 
2206             if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2207                 err = set_dir_errors(path, "setdirowner", errno);
2208                 goto setdirparam_done;
2209             }
2210             break;
2211         case DIRPBIT_ACCESS :
2212             if (dir->d_did == DIRDID_ROOT) {
2213                 setdeskmode(mpriv);
2214                 if (!dir_rx_set(mpriv)) {
2215                     /* we can't remove read and search for owner on volume root */
2216                     err = AFPERR_ACCESS;
2217                     goto setdirparam_done;
2218                 }
2219             }
2220
2221             if (!dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2222                 err = set_dir_errors(path, "setdirmode", errno);
2223                 goto setdirparam_done;
2224             }
2225             break;
2226         case DIRPBIT_PDINFO :
2227             if (afp_version >= 30) {
2228                 err = AFPERR_BITMAP;
2229                 goto setdirparam_done;
2230             }
2231             break;
2232         case DIRPBIT_UNIXPR :
2233             if (vol_unix_priv(vol)) {
2234                 if (dir->d_did == DIRDID_ROOT) {
2235                     if (!dir_rx_set(upriv)) {
2236                         /* we can't remove read and search for owner on volume root */
2237                         err = AFPERR_ACCESS;
2238                         goto setdirparam_done;
2239                     }
2240                     setdeskowner( -1, ntohl(group) ); 
2241                     setdeskmode( upriv );
2242                 }
2243                 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2244                     err = set_dir_errors(path, "setdirowner", errno);
2245                     goto setdirparam_done;
2246                 }
2247
2248                 if ( upriv_bit && setdirunixmode(vol, upath, upriv) < 0 ) {
2249                     err = set_dir_errors(path, "setdirunixmode", errno);
2250                     goto setdirparam_done;
2251                 }
2252             }
2253             else {
2254                 err = AFPERR_BITMAP;
2255                 goto setdirparam_done;
2256             }
2257             break;
2258         default :
2259             err = AFPERR_BITMAP;
2260             goto setdirparam_done;
2261             break;
2262         }
2263
2264         bitmap = bitmap>>1;
2265         bit++;
2266     }
2267
2268 setdirparam_done:
2269     if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
2270        newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2271     }
2272     if (newdate) {
2273        if (isad)
2274           ad_setdate(&ad, AD_DATE_MODIFY, newdate);
2275        ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
2276        utime(upath, &ut);
2277     }
2278
2279     if ( isad ) {
2280         if (path->st_valid && !path->st_errno) {
2281             struct stat *st = &path->st;
2282
2283             if (dir && dir->d_parent) {
2284                 ad_setid(&ad, st->st_dev, st->st_ino,  dir->d_did, dir->d_parent->d_did, vol->v_stamp);
2285             }
2286         }
2287         ad_flush( &ad);
2288         ad_close_metadata( &ad);
2289     }
2290
2291     if (change_parent_mdate && dir->d_did != DIRDID_ROOT
2292             && gettimeofday(&tv, NULL) == 0) {
2293        if (!movecwd(vol, dir->d_parent)) {
2294            newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2295            /* be careful with bitmap because now dir is null */
2296            bitmap = 1<<DIRPBIT_MDATE;
2297            setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
2298            /* should we reset curdir ?*/
2299        }
2300     }
2301
2302     return err;
2303 }
2304
2305 int afp_syncdir(obj, ibuf, ibuflen, rbuf, rbuflen )
2306 AFPObj  *obj _U_;
2307 char    *ibuf, *rbuf _U_;
2308 int     ibuflen _U_, *rbuflen;
2309 {
2310 #ifdef HAVE_DIRFD
2311     DIR                  *dp;
2312 #endif
2313     int                  dfd;
2314     struct vol           *vol;
2315     struct dir           *dir;
2316     u_int32_t            did;
2317     u_int16_t            vid;
2318
2319     *rbuflen = 0;
2320     ibuf += 2;
2321
2322     memcpy( &vid, ibuf, sizeof( vid ));
2323     ibuf += sizeof( vid );
2324     if (NULL == (vol = getvolbyvid( vid )) ) {
2325         return( AFPERR_PARAM );
2326     }
2327
2328     memcpy( &did, ibuf, sizeof( did ));
2329     ibuf += sizeof( did );
2330
2331     /* 
2332      * Here's the deal:
2333      * if it's CNID 2 our only choice to meet the specs is call sync.
2334      * For any other CNID just sync that dir. To my knowledge the
2335      * intended use of FPSyncDir is to sync the volume so all we're
2336      * ever going to see here is probably CNID 2. Anyway, we' prepared.
2337     */
2338
2339     if ( ntohl(did) == 2 ) {
2340         sync();
2341     } else {
2342         if (NULL == ( dir = dirlookup( vol, did )) ) {
2343             return afp_errno; /* was AFPERR_NOOBJ */
2344         }
2345         
2346         if (movecwd( vol, dir ) < 0 )
2347             return ( AFPERR_NOOBJ ); 
2348         
2349         /*
2350          * Assuming only OSens that have dirfd also may require fsyncing directories
2351          * in order to flush metadata e.g. Linux.
2352          */
2353     
2354 #ifdef HAVE_DIRFD
2355         if (NULL == ( dp = opendir( "." )) ) {
2356             switch( errno ) {
2357             case ENOENT :
2358                 return( AFPERR_NOOBJ );
2359             case EACCES :
2360             return( AFPERR_ACCESS );
2361             default :
2362                 return( AFPERR_PARAM );
2363             }
2364         }
2365         
2366         LOG(log_debug, logtype_afpd, "afp_syncdir: dir: '%s'", dir->d_u_name);
2367         
2368         dfd = dirfd( dp );
2369         if ( fsync ( dfd ) < 0 )
2370             LOG(log_error, logtype_afpd, "afp_syncdir(%s):  %s",
2371                 dir->d_u_name, strerror(errno) );
2372         closedir(dp); /* closes dfd too */
2373 #endif
2374         
2375         if ( -1 == (dfd = open(vol->ad_path(".", ADFLAGS_DIR), O_RDWR))) {
2376             switch( errno ) {
2377             case ENOENT:
2378                 return( AFPERR_NOOBJ );
2379         case EACCES:
2380             return( AFPERR_ACCESS );
2381             default:
2382                 return( AFPERR_PARAM );
2383             }        
2384         }
2385         
2386         LOG(log_debug, logtype_afpd, "afp_syncdir: ad-file: '%s'",
2387             vol->ad_path(".", ADFLAGS_DIR) );
2388         
2389         if ( fsync(dfd) < 0 )
2390             LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2391                 vol->ad_path(dir->d_u_name, ADFLAGS_DIR), strerror(errno) );
2392         close(dfd);
2393     }
2394
2395     return ( AFP_OK );
2396 }
2397
2398 int afp_createdir(obj, ibuf, ibuflen, rbuf, rbuflen )
2399 AFPObj  *obj;
2400 char    *ibuf, *rbuf;
2401 int     ibuflen _U_, *rbuflen;
2402 {
2403     struct adouble      ad;
2404     struct vol          *vol;
2405     struct dir          *dir;
2406     char                *upath;
2407     struct path         *s_path;
2408     u_int32_t           did;
2409     u_int16_t           vid;
2410     int                 err;
2411     
2412     *rbuflen = 0;
2413     ibuf += 2;
2414
2415     memcpy( &vid, ibuf, sizeof( vid ));
2416     ibuf += sizeof( vid );
2417     if (NULL == ( vol = getvolbyvid( vid )) ) {
2418         return( AFPERR_PARAM );
2419     }
2420
2421     if (vol->v_flags & AFPVOL_RO)
2422         return AFPERR_VLOCK;
2423
2424     memcpy( &did, ibuf, sizeof( did ));
2425     ibuf += sizeof( did );
2426     if (NULL == ( dir = dirlookup( vol, did )) ) {
2427         return afp_errno; /* was AFPERR_NOOBJ */
2428     }
2429     /* for concurrent access we need to be sure we are not in the
2430      * folder we want to create...
2431     */
2432     movecwd(vol, dir);
2433     
2434     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
2435         return get_afp_errno(AFPERR_PARAM);
2436     }
2437     /* cname was able to move curdir to it! */
2438     if (*s_path->m_name == '\0')
2439         return AFPERR_EXIST;
2440
2441     upath = s_path->u_name;
2442
2443     if (AFP_OK != (err = netatalk_mkdir( upath))) {
2444         return err;
2445     }
2446
2447     if (of_stat(s_path) < 0) {
2448         return AFPERR_MISC;
2449     }
2450     curdir->offcnt++;
2451     if ((dir = adddir( vol, curdir, s_path)) == NULL) {
2452         return AFPERR_MISC;
2453     }
2454
2455     if ( movecwd( vol, dir ) < 0 ) {
2456         return( AFPERR_PARAM );
2457     }
2458
2459     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2460     if (ad_open_metadata( ".", vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad ) < 0)  {
2461         if (vol_noadouble(vol))
2462             goto createdir_done;
2463         return( AFPERR_ACCESS );
2464     }
2465     ad_setname(&ad, s_path->m_name);
2466     ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
2467
2468     ad_flush( &ad);
2469     ad_close_metadata( &ad);
2470
2471 createdir_done:
2472 #ifdef HAVE_NFSv4_ACLS
2473     /* FIXME: are we really inside the created dir? */
2474     addir_inherit_acl(vol);
2475 #endif
2476
2477     memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
2478     *rbuflen = sizeof( u_int32_t );
2479     setvoltime(obj, vol );
2480     return( AFP_OK );
2481 }
2482
2483 /*
2484  * dst       new unix filename (not a pathname)
2485  * newname   new mac name
2486  * newparent curdir
2487  *
2488 */
2489 int renamedir(vol, src, dst, dir, newparent, newname)
2490 const struct vol *vol;
2491 char    *src, *dst, *newname;
2492 struct dir      *dir, *newparent;
2493 {
2494     struct adouble      ad;
2495     struct dir          *parent;
2496     char                *buf;
2497     int                 len, err;
2498         
2499     /* existence check moved to afp_moveandrename */
2500     if ( unix_rename( src, dst ) < 0 ) {
2501         switch ( errno ) {
2502         case ENOENT :
2503             return( AFPERR_NOOBJ );
2504         case EACCES :
2505             return( AFPERR_ACCESS );
2506         case EROFS:
2507             return AFPERR_VLOCK;
2508         case EINVAL:
2509             /* tried to move directory into a subdirectory of itself */
2510             return AFPERR_CANTMOVE;
2511         case EXDEV:
2512             /* this needs to copy and delete. bleah. that means we have
2513              * to deal with entire directory hierarchies. */
2514             if ((err = copydir(vol, src, dst)) < 0) {
2515                 deletedir(dst);
2516                 return err;
2517             }
2518             if ((err = deletedir(src)) < 0)
2519                 return err;
2520             break;
2521         default :
2522             return( AFPERR_PARAM );
2523         }
2524     }
2525
2526     vol->vfs->rf_renamedir(vol, src, dst);
2527
2528     len = strlen( newname );
2529     /* rename() succeeded so we need to update our tree even if we can't open
2530      * metadata
2531     */
2532     
2533     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2534
2535     if (!ad_open_metadata( dst, ADFLAGS_DIR, 0, &ad)) {
2536         ad_setname(&ad, newname);
2537         ad_flush( &ad);
2538         ad_close_metadata( &ad);
2539     }
2540
2541     dir_hash_del(vol, dir);
2542     if (dir->d_m_name == dir->d_u_name)
2543         dir->d_u_name = NULL;
2544
2545     if ((buf = (char *) realloc( dir->d_m_name, len + 1 )) == NULL ) {
2546         LOG(log_error, logtype_afpd, "renamedir: realloc mac name: %s", strerror(errno) );
2547         /* FIXME : fatal ? */
2548         return AFPERR_MISC;
2549     }
2550     dir->d_m_name = buf;
2551     strcpy( dir->d_m_name, newname );
2552
2553     if (newname == dst) {
2554         free(dir->d_u_name);
2555         dir->d_u_name = dir->d_m_name;
2556     }
2557     else {
2558         if ((buf = (char *) realloc( dir->d_u_name, strlen(dst) + 1 )) == NULL ) {
2559             LOG(log_error, logtype_afpd, "renamedir: realloc unix name: %s", strerror(errno) );
2560             return AFPERR_MISC;
2561         }
2562         dir->d_u_name = buf;
2563         strcpy( dir->d_u_name, dst );
2564     }
2565
2566     if (dir->d_m_name_ucs2)
2567         free(dir->d_m_name_ucs2);
2568
2569     dir->d_m_name_ucs2 = NULL;
2570     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))
2571         dir->d_m_name_ucs2 = NULL;
2572
2573     if (( parent = dir->d_parent ) == NULL ) {
2574         return( AFP_OK );
2575     }
2576     if ( parent == newparent ) {
2577         hash_alloc_insert(vol->v_hash, dir, dir);
2578         return( AFP_OK );
2579     }
2580
2581     /* detach from old parent and add to new one. */
2582     dirchildremove(parent, dir);
2583     dir->d_parent = newparent;
2584     dirchildadd(vol, newparent, dir);
2585     return( AFP_OK );
2586 }
2587
2588 /* delete an empty directory */
2589 int deletecurdir( vol)
2590 const struct vol        *vol;
2591 {
2592     struct dirent *de;
2593     struct stat st;
2594     struct dir  *fdir;
2595     DIR *dp;
2596     struct adouble      ad;
2597     u_int16_t           ashort;
2598     int err;
2599
2600     if ( curdir->d_parent == NULL ) {
2601         return( AFPERR_ACCESS );
2602     }
2603
2604     fdir = curdir;
2605
2606     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2607     if ( ad_metadata( ".", ADFLAGS_DIR, &ad) == 0 ) {
2608
2609         ad_getattr(&ad, &ashort);
2610         ad_close( &ad, ADFLAGS_HF );
2611         if ((ashort & htons(ATTRBIT_NODELETE))) {
2612             return  AFPERR_OLOCK;
2613         }
2614     }
2615     err = vol->vfs->rf_deletecurdir(vol);
2616     if (err) {
2617         return err;
2618     }
2619
2620     /* now get rid of dangling symlinks */
2621     if ((dp = opendir("."))) {
2622         while ((de = readdir(dp))) {
2623             /* skip this and previous directory */
2624             if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
2625                 continue;
2626
2627             /* bail if it's not a symlink */
2628             if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
2629                 closedir(dp);
2630                 return AFPERR_DIRNEMPT;
2631             }
2632
2633             if ((err = netatalk_unlink(de->d_name))) {
2634                 closedir(dp);
2635                 return err;
2636             }
2637         }
2638     }
2639
2640     if ( movecwd( vol, curdir->d_parent ) < 0 ) {
2641         err = afp_errno;
2642         goto delete_done;
2643     }
2644
2645     if ( !(err = netatalk_rmdir(fdir->d_u_name))) {
2646         dirchildremove(curdir, fdir);
2647         cnid_delete(vol->v_cdb, fdir->d_did);
2648         dir_remove( vol, fdir );
2649         err = AFP_OK;
2650     }
2651 delete_done:
2652     if (dp) {
2653         /* inode is used as key for cnid.
2654          * Close the descriptor only after cnid_delete
2655          * has been called. 
2656         */
2657         closedir(dp);
2658     }
2659     return err;
2660 }
2661
2662 int afp_mapid(obj, ibuf, ibuflen, rbuf, rbuflen )
2663 AFPObj  *obj;
2664 char    *ibuf, *rbuf;
2665 int     ibuflen _U_, *rbuflen;
2666 {
2667     struct passwd       *pw;
2668     struct group        *gr;
2669     char                *name;
2670     u_int32_t           id;
2671     int                 len, sfunc;
2672     int         utf8 = 0;
2673     uuidtype_t          type;
2674
2675     ibuf++;
2676     sfunc = (unsigned char) *ibuf++;
2677     *rbuflen = 0;
2678
2679
2680     if (sfunc >= 3 && sfunc <= 6) {
2681         if (afp_version < 30) {
2682             return( AFPERR_PARAM );
2683         }
2684         utf8 = 1;
2685     }
2686
2687         switch ( sfunc ) {
2688         case 1 :
2689         case 3 :/* unicode */
2690         memcpy( &id, ibuf, sizeof( id ));
2691         id = ntohl(id);
2692         if ( id != 0 ) {
2693             if (( pw = getpwuid( id )) == NULL ) {
2694                 return( AFPERR_NOITEM );
2695             }
2696             len = convert_string_allocate( obj->options.unixcharset, ((!utf8)?obj->options.maccharset:CH_UTF8_MAC),
2697                                             pw->pw_name, strlen(pw->pw_name), &name);
2698         } else {
2699             len = 0;
2700             name = NULL;
2701         }
2702             break;
2703         case 2 :
2704         case 4 : /* unicode */
2705         memcpy( &id, ibuf, sizeof( id ));
2706         id = ntohl(id);
2707         if ( id != 0 ) {
2708             if (NULL == ( gr = (struct group *)getgrgid( id ))) {
2709                 return( AFPERR_NOITEM );
2710             }
2711             len = convert_string_allocate( obj->options.unixcharset, (!utf8)?obj->options.maccharset:CH_UTF8_MAC,
2712                                             gr->gr_name, strlen(gr->gr_name), &name);
2713         } else {
2714             len = 0;
2715             name = NULL;
2716         }
2717             break;
2718 #ifdef HAVE_NFSv4_ACLS
2719     case 5 : /* UUID -> username */
2720     case 6 : /* UUID -> groupname */
2721         if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2722             return AFPERR_PARAM;
2723         LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request");
2724         len = getnamefromuuid( ibuf, &name, &type);
2725         if (len != 0)           /* its a error code, not len */
2726             return AFPERR_NOITEM;
2727         if (type == UUID_USER) {
2728             if (( pw = getpwnam( name )) == NULL )
2729                 return( AFPERR_NOITEM );
2730             LOG(log_debug, logtype_afpd, "afp_mapid: name:%s -> uid:%d", name, pw->pw_uid);
2731             id = htonl(UUID_USER);
2732             memcpy( rbuf, &id, sizeof( id ));
2733             id = htonl( pw->pw_uid);
2734             rbuf += sizeof( id );
2735             memcpy( rbuf, &id, sizeof( id ));
2736             rbuf += sizeof( id );
2737             *rbuflen = 2 * sizeof( id );
2738         } else {                /* type == UUID_GROUP */
2739             if (( gr = getgrnam( name )) == NULL )
2740                 return( AFPERR_NOITEM );
2741             LOG(log_debug, logtype_afpd, "afp_mapid: group:%s -> gid:%d", name, gr->gr_gid);
2742             id = htonl(UUID_GROUP);
2743             memcpy( rbuf, &id, sizeof( id ));
2744             rbuf += sizeof( id );
2745             id = htonl( gr->gr_gid);
2746             memcpy( rbuf, &id, sizeof( id ));
2747             rbuf += sizeof( id );
2748             *rbuflen = 2 * sizeof( id );
2749         }
2750         break;
2751 #endif
2752         default :
2753             return( AFPERR_PARAM );
2754         }
2755
2756         if (name)
2757             len = strlen( name );
2758         
2759     if (utf8) {
2760         u_int16_t tp = htons(len);
2761         memcpy(rbuf, &tp, sizeof(tp));
2762         rbuf += sizeof(tp);
2763         *rbuflen += 2;
2764     }
2765     else {
2766         *rbuf++ = len;
2767         *rbuflen += 1;
2768     }
2769     if ( len > 0 ) {
2770         memcpy( rbuf, name, len );
2771     }
2772     *rbuflen += len;
2773     if (name)
2774         free(name);
2775     return( AFP_OK );
2776 }
2777
2778 int afp_mapname(obj, ibuf, ibuflen, rbuf, rbuflen )
2779 AFPObj  *obj _U_;
2780 char    *ibuf, *rbuf;
2781 int     ibuflen _U_, *rbuflen;
2782 {
2783     struct passwd       *pw;
2784     struct group        *gr;
2785     int             len, sfunc;
2786     u_int32_t       id;
2787     u_int16_t       ulen;
2788
2789     ibuf++;
2790     sfunc = (unsigned char) *ibuf++;
2791     *rbuflen = 0;
2792     LOG(log_debug, logtype_afpd, "afp_mapname: sfunc: %d, afp_version: %d", sfunc, afp_version);
2793     switch ( sfunc ) {
2794     case 1 : 
2795     case 2 : /* unicode */
2796         if (afp_version < 30) {
2797             return( AFPERR_PARAM );
2798         }
2799         memcpy(&ulen, ibuf, sizeof(ulen));
2800         len = ntohs(ulen);
2801         ibuf += 2;
2802         LOG(log_debug, logtype_afpd, "afp_mapname: alive");
2803         break;
2804     case 3 :
2805     case 4 :
2806         len = (unsigned char) *ibuf++;
2807         break;
2808 #ifdef HAVE_NFSv4_ACLS
2809     case 5 : /* username -> UUID  */
2810     case 6 : /* groupname -> UUID */
2811         if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2812             return AFPERR_PARAM;
2813         memcpy(&ulen, ibuf, sizeof(ulen));
2814         len = ntohs(ulen);
2815         ibuf += 2;
2816         break;
2817 #endif
2818     default :
2819         return( AFPERR_PARAM );
2820     }
2821
2822     ibuf[ len ] = '\0';
2823
2824     if ( len == 0 )
2825         return AFPERR_PARAM;
2826     else {
2827         switch ( sfunc ) {
2828         case 1 : /* unicode */
2829         case 3 :
2830             if (NULL == ( pw = (struct passwd *)getpwnam( ibuf )) ) {
2831                 return( AFPERR_NOITEM );
2832             }
2833             id = pw->pw_uid;
2834             id = htonl(id);
2835             memcpy( rbuf, &id, sizeof( id ));
2836             *rbuflen = sizeof( id );
2837             break;
2838
2839         case 2 : /* unicode */
2840         case 4 :
2841             LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s",ibuf);
2842             if (NULL == ( gr = (struct group *)getgrnam( ibuf ))) {
2843                 return( AFPERR_NOITEM );
2844             }
2845             id = gr->gr_gid;
2846             LOG(log_debug, logtype_afpd, "afp_mapname: gettgrnam for name: %s -> id: %d",ibuf, id);
2847     id = htonl(id);
2848     memcpy( rbuf, &id, sizeof( id ));
2849     *rbuflen = sizeof( id );
2850             break;
2851 #ifdef HAVE_NFSv4_ACLS
2852         case 5 :                /* username -> UUID */
2853             LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);    
2854             if (0 != getuuidfromname(ibuf, UUID_USER, rbuf))
2855                 return AFPERR_NOITEM;
2856             *rbuflen = UUID_BINSIZE;
2857             break;
2858         case 6 :                /* groupname -> UUID */
2859             LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);    
2860             if (0 != getuuidfromname(ibuf, UUID_GROUP, rbuf))
2861                 return AFPERR_NOITEM;
2862             *rbuflen = UUID_BINSIZE;
2863             break;
2864 #endif
2865         }
2866     }
2867     return( AFP_OK );
2868 }
2869
2870 /* ------------------------------------
2871   variable DID support 
2872 */
2873 int afp_closedir(obj, ibuf, ibuflen, rbuf, rbuflen )
2874 AFPObj  *obj _U_;
2875 char    *ibuf _U_, *rbuf _U_;
2876 int     ibuflen _U_, *rbuflen;
2877 {
2878 #if 0
2879     struct vol   *vol;
2880     struct dir   *dir;
2881     u_int16_t    vid;
2882     u_int32_t    did;
2883 #endif /* 0 */
2884
2885     *rbuflen = 0;
2886
2887     /* do nothing as dids are static for the life of the process. */
2888 #if 0
2889     ibuf += 2;
2890
2891     memcpy(&vid,  ibuf, sizeof( vid ));
2892     ibuf += sizeof( vid );
2893     if (( vol = getvolbyvid( vid )) == NULL ) {
2894         return( AFPERR_PARAM );
2895     }
2896
2897     memcpy( &did, ibuf, sizeof( did ));
2898     ibuf += sizeof( did );
2899     if (( dir = dirlookup( vol, did )) == NULL ) {
2900         return( AFPERR_PARAM );
2901     }
2902
2903     /* dir_remove -- deletedid */
2904 #endif /* 0 */
2905
2906     return AFP_OK;
2907 }
2908
2909 /* did creation gets done automatically 
2910  * there's a pb again with case but move it to cname
2911 */
2912 int afp_opendir(obj, ibuf, ibuflen, rbuf, rbuflen )
2913 AFPObj  *obj _U_;
2914 char    *ibuf, *rbuf;
2915 int     ibuflen  _U_, *rbuflen;
2916 {
2917     struct vol          *vol;
2918     struct dir          *parentdir;
2919     struct path         *path;
2920     u_int32_t           did;
2921     u_int16_t           vid;
2922
2923     *rbuflen = 0;
2924     ibuf += 2;
2925
2926     memcpy(&vid, ibuf, sizeof(vid));
2927     ibuf += sizeof( vid );
2928
2929     if (NULL == ( vol = getvolbyvid( vid )) ) {
2930         return( AFPERR_PARAM );
2931     }
2932
2933     memcpy(&did, ibuf, sizeof(did));
2934     ibuf += sizeof(did);
2935
2936     if (NULL == ( parentdir = dirlookup( vol, did )) ) {
2937         return afp_errno;
2938     }
2939
2940     if (NULL == ( path = cname( vol, parentdir, &ibuf )) ) {
2941         return get_afp_errno(AFPERR_PARAM);
2942     }
2943
2944     if ( *path->m_name != '\0' ) {
2945         return path_error(path, AFPERR_NOOBJ);
2946     }
2947
2948     if ( !path->st_valid && of_stat(path ) < 0 ) {
2949         return( AFPERR_NOOBJ );
2950     }
2951     if ( path->st_errno ) {
2952         return( AFPERR_NOOBJ );
2953     }
2954
2955     memcpy(rbuf, &curdir->d_did, sizeof(curdir->d_did));
2956     *rbuflen = sizeof(curdir->d_did);
2957     return AFP_OK;
2958 }