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