]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/directory.c
if there's no server configured, errno is undefined
[netatalk.git] / etc / afpd / directory.c
1 /*
2  * $Id: directory.c,v 1.82 2005-09-28 09:43:04 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                 memcpy( &owner, buf, sizeof(owner)); /* FIXME need to change owner too? */
2039                 buf += sizeof( owner );
2040                 memcpy( &group, buf, sizeof( group ));
2041                 buf += sizeof( group );
2042
2043                 change_mdate = 1;
2044                 change_parent_mdate = 1;
2045                 memcpy( &upriv, buf, sizeof( upriv ));
2046                 buf += sizeof( upriv );
2047                 upriv = ntohl (upriv) | vol->v_perm;
2048                 if (dir_rx_set(upriv)) {
2049                     /* maybe we are trying to set perms back */
2050                     if ( setdirunixmode(vol, upath, upriv) < 0 ) {
2051                         bitmap = 0;
2052                         err = set_dir_errors(path, "setdirunixmode", errno);
2053                     }
2054                 }
2055                 else {
2056                     /* do it later */
2057                     upriv_bit = 1;
2058                 }
2059                 break;
2060             }
2061             /* fall through */
2062         default :
2063             err = AFPERR_BITMAP;
2064             bitmap = 0;
2065             break;
2066         }
2067
2068         bitmap = bitmap>>1;
2069         bit++;
2070     }
2071     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2072
2073     if (ad_open_metadata( upath, vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad) < 0) {
2074         /*
2075          * Check to see what we're trying to set.  If it's anything
2076          * but ACCESS, UID, or GID, give an error.  If it's any of those
2077          * three, we don't need the ad to be open, so just continue.
2078          *
2079          * note: we also don't need to worry about mdate. also, be quiet
2080          *       if we're using the noadouble option.
2081          */
2082         if (!vol_noadouble(vol) && (d_bitmap &
2083                                     ~((1<<DIRPBIT_ACCESS)|(1<<DIRPBIT_UNIXPR)|
2084                                       (1<<DIRPBIT_UID)|(1<<DIRPBIT_GID)|
2085                                       (1<<DIRPBIT_MDATE)|(1<<DIRPBIT_PDINFO)))) {
2086             return AFPERR_ACCESS;
2087         }
2088
2089         isad = 0;
2090     } else {
2091         /*
2092          * Check to see if a create was necessary. If it was, we'll want
2093          * to set our name, etc.
2094          */
2095         if ( (ad_get_HF_flags( &ad ) & O_CREAT)) {
2096             ad_setname(&ad, curdir->d_m_name);
2097         }
2098     }
2099
2100     bit = 0;
2101     bitmap = d_bitmap;
2102     while ( bitmap != 0 ) {
2103         while (( bitmap & 1 ) == 0 ) {
2104             bitmap = bitmap>>1;
2105             bit++;
2106         }
2107
2108         switch( bit ) {
2109         case DIRPBIT_ATTR :
2110             if (isad) {
2111                 ad_getattr(&ad, &bshort);
2112                 if ((bshort & htons(ATTRBIT_INVISIBLE)) != 
2113                     (ashort & htons(ATTRBIT_INVISIBLE) & htons(ATTRBIT_SETCLR)) )
2114                     change_parent_mdate = 1;
2115                 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
2116                     bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
2117                 } else {
2118                     bshort &= ~ashort;
2119                 }
2120                 ad_setattr(&ad, bshort);
2121             }
2122             break;
2123         case DIRPBIT_CDATE :
2124             if (isad) {
2125                 ad_setdate(&ad, AD_DATE_CREATE, cdate);
2126             }
2127             break;
2128         case DIRPBIT_MDATE :
2129             break;
2130         case DIRPBIT_BDATE :
2131             if (isad) {
2132                 ad_setdate(&ad, AD_DATE_BACKUP, bdate);
2133             }
2134             break;
2135         case DIRPBIT_FINFO :
2136             if (isad) {
2137                 if (  dir->d_did == DIRDID_ROOT ) {
2138                     /*
2139                      * Alright, we admit it, this is *really* sick!
2140                      * The 4 bytes that we don't copy, when we're dealing
2141                      * with the root of a volume, are the directory's
2142                      * location information. This eliminates that annoying
2143                      * behavior one sees when mounting above another mount
2144                      * point.
2145                      */
2146                     memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 10 );
2147                     memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, finder_buf + 14, 18 );
2148                 } else {
2149                     memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 32 );
2150                 }
2151             }
2152             break;
2153         case DIRPBIT_UID :      /* What kind of loser mounts as root? */
2154             if ( (dir->d_did == DIRDID_ROOT) &&
2155                     (setdeskowner( ntohl(owner), -1 ) < 0)) {
2156                 err = set_dir_errors(path, "setdeskowner", errno);
2157                 if (isad && err == AFPERR_PARAM) {
2158                     err = AFP_OK; /* ???*/
2159                 }
2160                 else {
2161                     goto setdirparam_done;
2162                 }
2163             }
2164             if ( setdirowner(vol, upath, ntohl(owner), -1 ) < 0 ) {
2165                 err = set_dir_errors(path, "setdirowner", errno);
2166                 goto setdirparam_done;
2167             }
2168             break;
2169         case DIRPBIT_GID :
2170             if (dir->d_did == DIRDID_ROOT)
2171                 setdeskowner( -1, ntohl(group) ); 
2172             if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2173                 err = set_dir_errors(path, "setdirowner", errno);
2174                 goto setdirparam_done;
2175             }
2176             break;
2177         case DIRPBIT_ACCESS :
2178             if (dir->d_did == DIRDID_ROOT) {
2179                 setdeskmode(mpriv);
2180                 if (!dir_rx_set(mpriv)) {
2181                     /* we can't remove read and search for owner on volume root */
2182                     err = AFPERR_ACCESS;
2183                     goto setdirparam_done;
2184                 }
2185             }
2186
2187             if (!dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2188                 err = set_dir_errors(path, "setdirmode", errno);
2189                 goto setdirparam_done;
2190             }
2191             break;
2192         case DIRPBIT_PDINFO :
2193             if (afp_version >= 30) {
2194                 err = AFPERR_BITMAP;
2195                 goto setdirparam_done;
2196             }
2197             break;
2198         case DIRPBIT_UNIXPR :
2199             if (vol_unix_priv(vol)) {
2200                 if (dir->d_did == DIRDID_ROOT) {
2201                     if (!dir_rx_set(upriv)) {
2202                         /* we can't remove read and search for owner on volume root */
2203                         err = AFPERR_ACCESS;
2204                         goto setdirparam_done;
2205                     }
2206                     setdeskowner( -1, ntohl(group) ); 
2207                     setdeskmode( upriv );
2208                 }
2209                 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2210                     err = set_dir_errors(path, "setdirowner", errno);
2211                     goto setdirparam_done;
2212                 }
2213
2214                 if ( upriv_bit && setdirunixmode(vol, upath, upriv) < 0 ) {
2215                     err = set_dir_errors(path, "setdirunixmode", errno);
2216                     goto setdirparam_done;
2217                 }
2218             }
2219             else {
2220                 err = AFPERR_BITMAP;
2221                 goto setdirparam_done;
2222             }
2223             break;
2224         default :
2225             err = AFPERR_BITMAP;
2226             goto setdirparam_done;
2227             break;
2228         }
2229
2230         bitmap = bitmap>>1;
2231         bit++;
2232     }
2233
2234 setdirparam_done:
2235     if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
2236        newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2237     }
2238     if (newdate) {
2239        if (isad)
2240           ad_setdate(&ad, AD_DATE_MODIFY, newdate);
2241        ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
2242        utime(upath, &ut);
2243     }
2244
2245     if ( isad ) {
2246         if (path->st_valid && !path->st_errno) {
2247             struct stat *st = &path->st;
2248
2249             if (dir && dir->d_parent) {
2250                 ad_setid(&ad, st->st_dev, st->st_ino,  dir->d_did, dir->d_parent->d_did, vol->v_stamp);
2251             }
2252         }
2253         ad_flush_metadata( &ad);
2254         ad_close_metadata( &ad);
2255     }
2256
2257     if (change_parent_mdate && dir->d_did != DIRDID_ROOT
2258             && gettimeofday(&tv, NULL) == 0) {
2259        if (!movecwd(vol, dir->d_parent)) {
2260            newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2261            /* be careful with bitmap because now dir is null */
2262            bitmap = 1<<DIRPBIT_MDATE;
2263            setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
2264            /* should we reset curdir ?*/
2265        }
2266     }
2267
2268     return err;
2269 }
2270
2271 int afp_createdir(obj, ibuf, ibuflen, rbuf, rbuflen )
2272 AFPObj  *obj;
2273 char    *ibuf, *rbuf;
2274 int     ibuflen _U_, *rbuflen;
2275 {
2276     struct adouble      ad;
2277     struct vol          *vol;
2278     struct dir          *dir;
2279     char                *upath;
2280     struct path         *s_path;
2281     u_int32_t           did;
2282     u_int16_t           vid;
2283     int                 err;
2284     
2285     *rbuflen = 0;
2286     ibuf += 2;
2287
2288     memcpy( &vid, ibuf, sizeof( vid ));
2289     ibuf += sizeof( vid );
2290     if (NULL == ( vol = getvolbyvid( vid )) ) {
2291         return( AFPERR_PARAM );
2292     }
2293
2294     if (vol->v_flags & AFPVOL_RO)
2295         return AFPERR_VLOCK;
2296
2297     memcpy( &did, ibuf, sizeof( did ));
2298     ibuf += sizeof( did );
2299     if (NULL == ( dir = dirlookup( vol, did )) ) {
2300         return afp_errno; /* was AFPERR_NOOBJ */
2301     }
2302     /* for concurrent access we need to be sure we are not in the
2303      * folder we want to create...
2304     */
2305     movecwd(vol, dir);
2306     
2307     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
2308         return get_afp_errno(AFPERR_PARAM);
2309     }
2310     /* cname was able to move curdir to it! */
2311     if (*s_path->m_name == '\0')
2312         return AFPERR_EXIST;
2313
2314     upath = s_path->u_name;
2315     if (0 != (err = check_name(vol, upath))) {
2316        return err;
2317     }
2318
2319     if (AFP_OK != (err = netatalk_mkdir( upath))) {
2320         return err;
2321     }
2322
2323     if (of_stat(s_path) < 0) {
2324         return AFPERR_MISC;
2325     }
2326     curdir->offcnt++;
2327     if ((dir = adddir( vol, curdir, s_path)) == NULL) {
2328         return AFPERR_MISC;
2329     }
2330
2331     if ( movecwd( vol, dir ) < 0 ) {
2332         return( AFPERR_PARAM );
2333     }
2334
2335     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2336     if (ad_open_metadata( ".", vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad ) < 0)  {
2337         if (vol_noadouble(vol))
2338             goto createdir_done;
2339         return( AFPERR_ACCESS );
2340     }
2341     ad_setname(&ad, s_path->m_name);
2342     ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
2343
2344     ad_flush_metadata( &ad);
2345     ad_close_metadata( &ad);
2346
2347 createdir_done:
2348     memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
2349     *rbuflen = sizeof( u_int32_t );
2350     setvoltime(obj, vol );
2351     return( AFP_OK );
2352 }
2353
2354 /*
2355  * dst       new unix filename (not a pathname)
2356  * newname   new mac name
2357  * newparent curdir
2358  *
2359 */
2360 int renamedir(vol, src, dst, dir, newparent, newname)
2361 const struct vol *vol;
2362 char    *src, *dst, *newname;
2363 struct dir      *dir, *newparent;
2364 {
2365     struct adouble      ad;
2366     struct dir          *parent;
2367     char                *buf;
2368     int                 len, err;
2369         
2370     /* existence check moved to afp_moveandrename */
2371     if ( unix_rename( src, dst ) < 0 ) {
2372         switch ( errno ) {
2373         case ENOENT :
2374             return( AFPERR_NOOBJ );
2375         case EACCES :
2376             return( AFPERR_ACCESS );
2377         case EROFS:
2378             return AFPERR_VLOCK;
2379         case EINVAL:
2380             /* tried to move directory into a subdirectory of itself */
2381             return AFPERR_CANTMOVE;
2382         case EXDEV:
2383             /* this needs to copy and delete. bleah. that means we have
2384              * to deal with entire directory hierarchies. */
2385             if ((err = copydir(vol, src, dst)) < 0) {
2386                 deletedir(dst);
2387                 return err;
2388             }
2389             if ((err = deletedir(src)) < 0)
2390                 return err;
2391             break;
2392         default :
2393             return( AFPERR_PARAM );
2394         }
2395     }
2396
2397     vol->vfs->rf_renamedir(vol, src, dst);
2398
2399     len = strlen( newname );
2400     /* rename() succeeded so we need to update our tree even if we can't open
2401      * metadata
2402     */
2403     
2404     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2405
2406     if (!ad_open_metadata( dst, ADFLAGS_DIR, 0, &ad)) {
2407         ad_setname(&ad, newname);
2408         ad_flush_metadata( &ad);
2409         ad_close_metadata( &ad);
2410     }
2411
2412     dir_hash_del(vol, dir);
2413     if (dir->d_m_name == dir->d_u_name)
2414         dir->d_u_name = NULL;
2415
2416     if ((buf = (char *) realloc( dir->d_m_name, len + 1 )) == NULL ) {
2417         LOG(log_error, logtype_afpd, "renamedir: realloc mac name: %s", strerror(errno) );
2418         /* FIXME : fatal ? */
2419         return AFPERR_MISC;
2420     }
2421     dir->d_m_name = buf;
2422     strcpy( dir->d_m_name, newname );
2423
2424     if (newname == dst) {
2425         free(dir->d_u_name);
2426         dir->d_u_name = dir->d_m_name;
2427     }
2428     else {
2429         if ((buf = (char *) realloc( dir->d_u_name, strlen(dst) + 1 )) == NULL ) {
2430             LOG(log_error, logtype_afpd, "renamedir: realloc unix name: %s", strerror(errno) );
2431             return AFPERR_MISC;
2432         }
2433         dir->d_u_name = buf;
2434         strcpy( dir->d_u_name, dst );
2435     }
2436
2437     if (dir->d_m_name_ucs2)
2438         free(dir->d_m_name_ucs2);
2439
2440     dir->d_m_name_ucs2 = NULL;
2441     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))
2442         dir->d_m_name_ucs2 = NULL;
2443
2444     if (( parent = dir->d_parent ) == NULL ) {
2445         return( AFP_OK );
2446     }
2447     if ( parent == newparent ) {
2448         hash_alloc_insert(vol->v_hash, dir, dir);
2449         return( AFP_OK );
2450     }
2451
2452     /* detach from old parent and add to new one. */
2453     dirchildremove(parent, dir);
2454     dir->d_parent = newparent;
2455     dirchildadd(vol, newparent, dir);
2456     return( AFP_OK );
2457 }
2458
2459 /* delete an empty directory */
2460 int deletecurdir( vol)
2461 const struct vol        *vol;
2462 {
2463     struct dirent *de;
2464     struct stat st;
2465     struct dir  *fdir;
2466     DIR *dp;
2467     struct adouble      ad;
2468     u_int16_t           ashort;
2469     int err;
2470
2471     if ( curdir->d_parent == NULL ) {
2472         return( AFPERR_ACCESS );
2473     }
2474
2475     fdir = curdir;
2476
2477     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2478     if ( ad_metadata( ".", ADFLAGS_DIR, &ad) == 0 ) {
2479
2480         ad_getattr(&ad, &ashort);
2481         ad_close( &ad, ADFLAGS_HF );
2482         if ((ashort & htons(ATTRBIT_NODELETE))) {
2483             return  AFPERR_OLOCK;
2484         }
2485     }
2486     err = vol->vfs->rf_deletecurdir(vol);
2487     if (err) {
2488         return err;
2489     }
2490
2491     /* now get rid of dangling symlinks */
2492     if ((dp = opendir("."))) {
2493         while ((de = readdir(dp))) {
2494             /* skip this and previous directory */
2495             if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
2496                 continue;
2497
2498             /* bail if it's not a symlink */
2499             if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
2500                 closedir(dp);
2501                 return AFPERR_DIRNEMPT;
2502             }
2503
2504             if ((err = netatalk_unlink(de->d_name))) {
2505                 closedir(dp);
2506                 return err;
2507             }
2508         }
2509     }
2510
2511     if ( movecwd( vol, curdir->d_parent ) < 0 ) {
2512         err = afp_errno;
2513         goto delete_done;
2514     }
2515
2516     if ( !(err = netatalk_rmdir(fdir->d_u_name))) {
2517         dirchildremove(curdir, fdir);
2518         cnid_delete(vol->v_cdb, fdir->d_did);
2519         dir_remove( vol, fdir );
2520         err = AFP_OK;
2521     }
2522 delete_done:
2523     if (dp) {
2524         /* inode is used as key for cnid.
2525          * Close the descriptor only after cnid_delete
2526          * has been called. 
2527         */
2528         closedir(dp);
2529     }
2530     return err;
2531 }
2532
2533 int afp_mapid(obj, ibuf, ibuflen, rbuf, rbuflen )
2534 AFPObj  *obj;
2535 char    *ibuf, *rbuf;
2536 int     ibuflen _U_, *rbuflen;
2537 {
2538     struct passwd       *pw;
2539     struct group        *gr;
2540     char                *name;
2541     u_int32_t           id;
2542     int                 len, sfunc;
2543     int         utf8 = 0;
2544     
2545     ibuf++;
2546     sfunc = (unsigned char) *ibuf++;
2547     memcpy( &id, ibuf, sizeof( id ));
2548
2549     id = ntohl(id);
2550     *rbuflen = 0;
2551
2552     if (sfunc == 3 || sfunc == 4) {
2553         if (afp_version < 30) {
2554             return( AFPERR_PARAM );
2555         }
2556         utf8 = 1;
2557     }
2558     if ( id != 0 ) {
2559         switch ( sfunc ) {
2560         case 1 :
2561         case 3 :/* unicode */
2562             if (( pw = getpwuid( id )) == NULL ) {
2563                 return( AFPERR_NOITEM );
2564             }
2565             len = convert_string_allocate( obj->options.unixcharset, ((!utf8)?obj->options.maccharset:CH_UTF8_MAC),
2566                                             pw->pw_name, strlen(pw->pw_name), &name);
2567             break;
2568
2569         case 2 :
2570         case 4 : /* unicode */
2571             if (NULL == ( gr = (struct group *)getgrgid( id ))) {
2572                 return( AFPERR_NOITEM );
2573             }
2574             len = convert_string_allocate( obj->options.unixcharset, (!utf8)?obj->options.maccharset:CH_UTF8_MAC,
2575                                             gr->gr_name, strlen(gr->gr_name), &name);
2576             break;
2577
2578         default :
2579             return( AFPERR_PARAM );
2580         }
2581         len = strlen( name );
2582
2583     } else {
2584         len = 0;
2585         name = NULL;
2586     }
2587     if (utf8) {
2588         u_int16_t tp = htons(len);
2589         memcpy(rbuf, &tp, sizeof(tp));
2590         rbuf += sizeof(tp);
2591         *rbuflen += 2;
2592     }
2593     else {
2594         *rbuf++ = len;
2595         *rbuflen += 1;
2596     }
2597     if ( len > 0 ) {
2598         memcpy( rbuf, name, len );
2599     }
2600     *rbuflen += len;
2601     if (name)
2602         free(name);
2603     return( AFP_OK );
2604 }
2605
2606 int afp_mapname(obj, ibuf, ibuflen, rbuf, rbuflen )
2607 AFPObj  *obj _U_;
2608 char    *ibuf, *rbuf;
2609 int     ibuflen _U_, *rbuflen;
2610 {
2611     struct passwd       *pw;
2612     struct group        *gr;
2613     int             len, sfunc;
2614     u_int32_t       id;
2615     u_int16_t       ulen;
2616
2617     ibuf++;
2618     sfunc = (unsigned char) *ibuf++;
2619     *rbuflen = 0;
2620     switch ( sfunc ) {
2621     case 1 : 
2622     case 2 : /* unicode */
2623         if (afp_version < 30) {
2624             return( AFPERR_PARAM );
2625         }
2626         memcpy(&ulen, ibuf, sizeof(ulen));
2627         len = ntohs(ulen);
2628         ibuf += 2;
2629         break;
2630     case 3 :
2631     case 4 :
2632         len = (unsigned char) *ibuf++;
2633         break;
2634     default :
2635         return( AFPERR_PARAM );
2636     }
2637
2638     ibuf[ len ] = '\0';
2639
2640     if ( len != 0 ) {
2641         switch ( sfunc ) {
2642         case 1 : /* unicode */
2643         case 3 :
2644             if (NULL == ( pw = (struct passwd *)getpwnam( ibuf )) ) {
2645                 return( AFPERR_NOITEM );
2646             }
2647             id = pw->pw_uid;
2648             break;
2649
2650         case 2 : /* unicode */
2651         case 4 :
2652             if (NULL == ( gr = (struct group *)getgrnam( ibuf ))) {
2653                 return( AFPERR_NOITEM );
2654             }
2655             id = gr->gr_gid;
2656             break;
2657         }
2658     } else {
2659         id = 0;
2660     }
2661     id = htonl(id);
2662     memcpy( rbuf, &id, sizeof( id ));
2663     *rbuflen = sizeof( id );
2664     return( AFP_OK );
2665 }
2666
2667 /* ------------------------------------
2668   variable DID support 
2669 */
2670 int afp_closedir(obj, ibuf, ibuflen, rbuf, rbuflen )
2671 AFPObj  *obj _U_;
2672 char    *ibuf _U_, *rbuf _U_;
2673 int     ibuflen _U_, *rbuflen;
2674 {
2675 #if 0
2676     struct vol   *vol;
2677     struct dir   *dir;
2678     u_int16_t    vid;
2679     u_int32_t    did;
2680 #endif /* 0 */
2681
2682     *rbuflen = 0;
2683
2684     /* do nothing as dids are static for the life of the process. */
2685 #if 0
2686     ibuf += 2;
2687
2688     memcpy(&vid,  ibuf, sizeof( vid ));
2689     ibuf += sizeof( vid );
2690     if (( vol = getvolbyvid( vid )) == NULL ) {
2691         return( AFPERR_PARAM );
2692     }
2693
2694     memcpy( &did, ibuf, sizeof( did ));
2695     ibuf += sizeof( did );
2696     if (( dir = dirlookup( vol, did )) == NULL ) {
2697         return( AFPERR_PARAM );
2698     }
2699
2700     /* dir_remove -- deletedid */
2701 #endif /* 0 */
2702
2703     return AFP_OK;
2704 }
2705
2706 /* did creation gets done automatically 
2707  * there's a pb again with case but move it to cname
2708 */
2709 int afp_opendir(obj, ibuf, ibuflen, rbuf, rbuflen )
2710 AFPObj  *obj _U_;
2711 char    *ibuf, *rbuf;
2712 int     ibuflen  _U_, *rbuflen;
2713 {
2714     struct vol          *vol;
2715     struct dir          *parentdir;
2716     struct path         *path;
2717     u_int32_t           did;
2718     u_int16_t           vid;
2719
2720     *rbuflen = 0;
2721     ibuf += 2;
2722
2723     memcpy(&vid, ibuf, sizeof(vid));
2724     ibuf += sizeof( vid );
2725
2726     if (NULL == ( vol = getvolbyvid( vid )) ) {
2727         return( AFPERR_PARAM );
2728     }
2729
2730     memcpy(&did, ibuf, sizeof(did));
2731     ibuf += sizeof(did);
2732
2733     if (NULL == ( parentdir = dirlookup( vol, did )) ) {
2734         return afp_errno;
2735     }
2736
2737     if (NULL == ( path = cname( vol, parentdir, &ibuf )) ) {
2738         return get_afp_errno(AFPERR_PARAM);
2739     }
2740
2741     if ( *path->m_name != '\0' ) {
2742         return path_error(path, AFPERR_NOOBJ);
2743     }
2744
2745     if ( !path->st_valid && of_stat(path ) < 0 ) {
2746         return( AFPERR_NOOBJ );
2747     }
2748     if ( path->st_errno ) {
2749         return( AFPERR_NOOBJ );
2750     }
2751
2752     memcpy(rbuf, &curdir->d_did, sizeof(curdir->d_did));
2753     *rbuflen = sizeof(curdir->d_did);
2754     return AFP_OK;
2755 }