]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/directory.c
add a SFM compatible format for adouble
[netatalk.git] / etc / afpd / directory.c
1 /*
2  * $Id: directory.c,v 1.84 2006-09-29 09:39:16 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 static int invisible_dots(const struct vol *vol, const char *name)
1606
1607   return vol_inv_dots(vol) && *name  == '.' && strcmp(name, ".") && strcmp(name, "..");
1608 }
1609
1610 /* ------------------------------ 
1611    (".", curdir)
1612    (name, dir) with curdir:name == dir, from afp_enumerate
1613 */
1614
1615 int getdirparams(const struct vol *vol,
1616                  u_int16_t bitmap, struct path *s_path,
1617                  struct dir *dir, 
1618                  char *buf, int *buflen )
1619 {
1620     struct maccess      ma;
1621     struct adouble      ad;
1622     char                *data, *l_nameoff = NULL, *utf_nameoff = NULL;
1623     int                 bit = 0, isad = 0;
1624     u_int32_t           aint;
1625     u_int16_t           ashort;
1626     int                 ret;
1627     u_int32_t           utf8 = 0;
1628     cnid_t              pdid;
1629     struct stat *st = &s_path->st;
1630     char *upath = s_path->u_name;
1631     
1632     if ((bitmap & ((1 << DIRPBIT_ATTR)  |
1633                    (1 << DIRPBIT_CDATE) |
1634                    (1 << DIRPBIT_MDATE) |
1635                    (1 << DIRPBIT_BDATE) |
1636                    (1 << DIRPBIT_FINFO)))) {
1637
1638         ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1639         if ( !ad_metadata( upath, ADFLAGS_DIR, &ad) ) {
1640             isad = 1;
1641         }
1642     }
1643     
1644     if ( dir->d_did == DIRDID_ROOT) {
1645         pdid = DIRDID_ROOT_PARENT;
1646     } else if (dir->d_did == DIRDID_ROOT_PARENT) {
1647         pdid = 0;
1648     } else {
1649         pdid = dir->d_parent->d_did;
1650     }
1651     
1652     data = buf;
1653     while ( bitmap != 0 ) {
1654         while (( bitmap & 1 ) == 0 ) {
1655             bitmap = bitmap>>1;
1656             bit++;
1657         }
1658
1659         switch ( bit ) {
1660         case DIRPBIT_ATTR :
1661             if ( isad ) {
1662                 ad_getattr(&ad, &ashort);
1663             } else if (invisible_dots(vol, dir->d_u_name)) {
1664                 ashort = htons(ATTRBIT_INVISIBLE);
1665             } else
1666                 ashort = 0;
1667             ashort |= htons(ATTRBIT_SHARED);
1668             memcpy( data, &ashort, sizeof( ashort ));
1669             data += sizeof( ashort );
1670             break;
1671
1672         case DIRPBIT_PDID :
1673             memcpy( data, &pdid, sizeof( pdid ));
1674             data += sizeof( pdid );
1675             break;
1676
1677         case DIRPBIT_CDATE :
1678             if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
1679                 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1680             memcpy( data, &aint, sizeof( aint ));
1681             data += sizeof( aint );
1682             break;
1683
1684         case DIRPBIT_MDATE :
1685             aint = AD_DATE_FROM_UNIX(st->st_mtime);
1686             memcpy( data, &aint, sizeof( aint ));
1687             data += sizeof( aint );
1688             break;
1689
1690         case DIRPBIT_BDATE :
1691             if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
1692                 aint = AD_DATE_START;
1693             memcpy( data, &aint, sizeof( aint ));
1694             data += sizeof( aint );
1695             break;
1696
1697         case DIRPBIT_FINFO :
1698             if ( isad ) {
1699                 memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
1700             } else { /* no appledouble */
1701                 memset( data, 0, 32 );
1702                 /* set default view -- this also gets done in ad_open() */
1703                 ashort = htons(FINDERINFO_CLOSEDVIEW);
1704                 memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort));
1705
1706                 /* dot files are by default visible */
1707                 if (invisible_dots(vol, dir->d_u_name)) {
1708                     ashort = htons(FINDERINFO_INVISIBLE);
1709                     memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
1710                 }
1711             }
1712             data += 32;
1713             break;
1714
1715         case DIRPBIT_LNAME :
1716             if (dir->d_m_name) /* root of parent can have a null name */
1717                 l_nameoff = data;
1718             else
1719                 memset(data, 0, sizeof(u_int16_t));
1720             data += sizeof( u_int16_t );
1721             break;
1722
1723         case DIRPBIT_SNAME :
1724             memset(data, 0, sizeof(u_int16_t));
1725             data += sizeof( u_int16_t );
1726             break;
1727
1728         case DIRPBIT_DID :
1729             memcpy( data, &dir->d_did, sizeof( aint ));
1730             data += sizeof( aint );
1731             break;
1732
1733         case DIRPBIT_OFFCNT :
1734             ashort = 0;
1735             /* this needs to handle current directory access rights */
1736             if (diroffcnt(dir, st)) {
1737                 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1738             }
1739             else if ((ret = for_each_dirent(vol, upath, NULL,NULL)) >= 0) {
1740                 setdiroffcnt(dir, st,  ret);
1741                 ashort = (dir->offcnt > 0xffff)?0xffff:dir->offcnt;
1742             }
1743             ashort = htons( ashort );
1744             memcpy( data, &ashort, sizeof( ashort ));
1745             data += sizeof( ashort );
1746             break;
1747
1748         case DIRPBIT_UID :
1749             aint = htonl(st->st_uid);
1750             memcpy( data, &aint, sizeof( aint ));
1751             data += sizeof( aint );
1752             break;
1753
1754         case DIRPBIT_GID :
1755             aint = htonl(st->st_gid);
1756             memcpy( data, &aint, sizeof( aint ));
1757             data += sizeof( aint );
1758             break;
1759
1760         case DIRPBIT_ACCESS :
1761             accessmode( upath, &ma, dir , st);
1762
1763             *data++ = ma.ma_user;
1764             *data++ = ma.ma_world;
1765             *data++ = ma.ma_group;
1766             *data++ = ma.ma_owner;
1767             break;
1768
1769             /* Client has requested the ProDOS information block.
1770                Just pass back the same basic block for all
1771                directories. <shirsch@ibm.net> */
1772         case DIRPBIT_PDINFO :                     
1773             if (afp_version >= 30) { /* UTF8 name */
1774                 utf8 = kTextEncodingUTF8;
1775                 if (dir->d_m_name) /* root of parent can have a null name */
1776                     utf_nameoff = data;
1777                 else
1778                     memset(data, 0, sizeof(u_int16_t));
1779                 data += sizeof( u_int16_t );
1780                 aint = 0;
1781                 memcpy(data, &aint, sizeof( aint ));
1782                 data += sizeof( aint );
1783             }
1784             else { /* ProDOS Info Block */
1785                 *data++ = 0x0f;
1786                 *data++ = 0;
1787                 ashort = htons( 0x0200 );
1788                 memcpy( data, &ashort, sizeof( ashort ));
1789                 data += sizeof( ashort );
1790                 memset( data, 0, sizeof( ashort ));
1791                 data += sizeof( ashort );
1792             }
1793             break;
1794
1795         case DIRPBIT_UNIXPR :
1796             aint = htonl(st->st_uid);
1797             memcpy( data, &aint, sizeof( aint ));
1798             data += sizeof( aint );
1799             aint = htonl(st->st_gid);
1800             memcpy( data, &aint, sizeof( aint ));
1801             data += sizeof( aint );
1802         
1803             aint = st->st_mode;
1804             aint = htonl ( aint & ~S_ISGID );  /* Remove SGID, OSX doesn't like it ... */
1805             memcpy( data, &aint, sizeof( aint ));
1806             data += sizeof( aint );
1807
1808             accessmode( upath, &ma, dir , st);
1809
1810             *data++ = ma.ma_user;
1811             *data++ = ma.ma_world;
1812             *data++ = ma.ma_group;
1813             *data++ = ma.ma_owner;
1814             break;
1815             
1816         default :
1817             if ( isad ) {
1818                 ad_close_metadata( &ad );
1819             }
1820             return( AFPERR_BITMAP );
1821         }
1822         bitmap = bitmap>>1;
1823         bit++;
1824     }
1825     if ( l_nameoff ) {
1826         ashort = htons( data - buf );
1827         memcpy( l_nameoff, &ashort, sizeof( ashort ));
1828         data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, 0);
1829     }
1830     if ( utf_nameoff ) {
1831         ashort = htons( data - buf );
1832         memcpy( utf_nameoff, &ashort, sizeof( ashort ));
1833         data = set_name(vol, data, pdid, dir->d_m_name, dir->d_did, utf8);
1834     }
1835     if ( isad ) {
1836         ad_close_metadata( &ad );
1837     }
1838     *buflen = data - buf;
1839     return( AFP_OK );
1840 }
1841
1842 /* ----------------------------- */
1843 int path_error(struct path *path, int error)
1844 {
1845 /* - a dir with access error
1846  * - no error it's a file
1847  * - file not found
1848  */
1849     if (path_isadir(path))
1850         return afp_errno;
1851     if (path->st_valid && path->st_errno)
1852         return error;
1853     return AFPERR_BADTYPE ;
1854 }
1855
1856 /* ----------------------------- */
1857 int afp_setdirparams(obj, ibuf, ibuflen, rbuf, rbuflen )
1858 AFPObj  *obj;
1859 char    *ibuf, *rbuf _U_;
1860 int     ibuflen _U_, *rbuflen;
1861 {
1862     struct vol  *vol;
1863     struct dir  *dir;
1864     struct path *path;
1865     u_int16_t   vid, bitmap;
1866     u_int32_t   did;
1867     int         rc;
1868
1869     *rbuflen = 0;
1870     ibuf += 2;
1871     memcpy( &vid, ibuf, sizeof( vid ));
1872     ibuf += sizeof( vid );
1873
1874     if (NULL == ( vol = getvolbyvid( vid )) ) {
1875         return( AFPERR_PARAM );
1876     }
1877
1878     if (vol->v_flags & AFPVOL_RO)
1879         return AFPERR_VLOCK;
1880
1881     memcpy( &did, ibuf, sizeof( did ));
1882     ibuf += sizeof( int );
1883
1884     if (NULL == ( dir = dirlookup( vol, did )) ) {
1885         return afp_errno;
1886     }
1887
1888     memcpy( &bitmap, ibuf, sizeof( bitmap ));
1889     bitmap = ntohs( bitmap );
1890     ibuf += sizeof( bitmap );
1891
1892     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1893         return get_afp_errno(AFPERR_NOOBJ); 
1894     }
1895
1896     if ( *path->m_name != '\0' ) {
1897         rc = path_error(path, AFPERR_NOOBJ);
1898         /* maybe we are trying to set perms back */
1899         if (rc != AFPERR_ACCESS)
1900             return rc;
1901     }
1902
1903     /*
1904      * If ibuf is odd, make it even.
1905      */
1906     if ((u_long)ibuf & 1 ) {
1907         ibuf++;
1908     }
1909
1910     if (AFP_OK == ( rc = setdirparams(vol, path, bitmap, ibuf )) ) {
1911         setvoltime(obj, vol );
1912     }
1913     return( rc );
1914 }
1915
1916 /*
1917  * cf AFP3.0.pdf page 244 for change_mdate and change_parent_mdate logic  
1918  *
1919  * assume path == '\0' eg. it's a directory in canonical form
1920 */
1921
1922 struct path Cur_Path = {
1923     0,
1924     "",  /* mac name */
1925     ".", /* unix name */
1926     0,   /* id */
1927     NULL,/* struct dir */
1928     0,   /* stat is not set */
1929 };
1930
1931 /* ------------------ */
1932 static int set_dir_errors(struct path *path, const char *where, int err)
1933 {
1934     switch ( err ) {
1935     case EPERM :
1936     case EACCES :
1937         return AFPERR_ACCESS;
1938     case EROFS :
1939         return AFPERR_VLOCK;
1940     }
1941     LOG(log_error, logtype_afpd, "setdirparam(%s): %s: %s", fullpathname(path->u_name), where, strerror(err) );
1942     return AFPERR_PARAM;
1943 }
1944  
1945 /* ------------------ */
1946 int setdirparams(const struct vol *vol, 
1947                  struct path *path, u_int16_t d_bitmap, char *buf )
1948 {
1949     struct maccess      ma;
1950     struct adouble      ad;
1951     struct utimbuf      ut;
1952     struct timeval      tv;
1953
1954     char                *upath;
1955     struct dir          *dir;
1956     int                 bit, aint, isad = 1;
1957     int                 cdate, bdate;
1958     int                 owner, group;
1959     u_int16_t           ashort, bshort;
1960     int                 err = AFP_OK;
1961     int                 change_mdate = 0;
1962     int                 change_parent_mdate = 0;
1963     int                 newdate = 0;
1964     u_int16_t           bitmap = d_bitmap;
1965     u_char              finder_buf[32];
1966     u_int32_t           upriv;
1967     mode_t              mpriv = 0;        
1968     u_int16_t           upriv_bit = 0;
1969
1970     bit = 0;
1971     upath = path->u_name;
1972     dir   = path->d_dir;
1973     while ( bitmap != 0 ) {
1974         while (( bitmap & 1 ) == 0 ) {
1975             bitmap = bitmap>>1;
1976             bit++;
1977         }
1978
1979         switch( bit ) {
1980         case DIRPBIT_ATTR :
1981             change_mdate = 1;
1982             memcpy( &ashort, buf, sizeof( ashort ));
1983             buf += sizeof( ashort );
1984             break;
1985         case DIRPBIT_CDATE :
1986             change_mdate = 1;
1987             memcpy(&cdate, buf, sizeof(cdate));
1988             buf += sizeof( cdate );
1989             break;
1990         case DIRPBIT_MDATE :
1991             memcpy(&newdate, buf, sizeof(newdate));
1992             buf += sizeof( newdate );
1993             break;
1994         case DIRPBIT_BDATE :
1995             change_mdate = 1;
1996             memcpy(&bdate, buf, sizeof(bdate));
1997             buf += sizeof( bdate );
1998             break;
1999         case DIRPBIT_FINFO :
2000             change_mdate = 1;
2001             memcpy( finder_buf, buf, 32 );
2002             buf += 32;
2003             break;
2004         case DIRPBIT_UID :      /* What kind of loser mounts as root? */
2005             change_parent_mdate = 1;
2006             memcpy( &owner, buf, sizeof(owner));
2007             buf += sizeof( owner );
2008             break;
2009         case DIRPBIT_GID :
2010             change_parent_mdate = 1;
2011             memcpy( &group, buf, sizeof( group ));
2012             buf += sizeof( group );
2013             break;
2014         case DIRPBIT_ACCESS :
2015             change_mdate = 1;
2016             change_parent_mdate = 1;
2017             ma.ma_user = *buf++;
2018             ma.ma_world = *buf++;
2019             ma.ma_group = *buf++;
2020             ma.ma_owner = *buf++;
2021             mpriv = mtoumode( &ma ) | vol->v_perm;
2022             if (dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2023                 err = set_dir_errors(path, "setdirmode", errno);
2024                 bitmap = 0;
2025             }
2026             break;
2027         /* Ignore what the client thinks we should do to the
2028            ProDOS information block.  Skip over the data and
2029            report nothing amiss. <shirsch@ibm.net> */
2030         case DIRPBIT_PDINFO :
2031             if (afp_version < 30) {
2032                 buf += 6;
2033             }
2034             else {
2035                 err = AFPERR_BITMAP;
2036                 bitmap = 0;
2037             }
2038             break;
2039         case DIRPBIT_UNIXPR :
2040             if (vol_unix_priv(vol)) {
2041                 memcpy( &owner, buf, sizeof(owner)); /* FIXME need to change owner too? */
2042                 buf += sizeof( owner );
2043                 memcpy( &group, buf, sizeof( group ));
2044                 buf += sizeof( group );
2045
2046                 change_mdate = 1;
2047                 change_parent_mdate = 1;
2048                 memcpy( &upriv, buf, sizeof( upriv ));
2049                 buf += sizeof( upriv );
2050                 upriv = ntohl (upriv) | vol->v_perm;
2051                 if (dir_rx_set(upriv)) {
2052                     /* maybe we are trying to set perms back */
2053                     if ( setdirunixmode(vol, upath, upriv) < 0 ) {
2054                         bitmap = 0;
2055                         err = set_dir_errors(path, "setdirunixmode", errno);
2056                     }
2057                 }
2058                 else {
2059                     /* do it later */
2060                     upriv_bit = 1;
2061                 }
2062                 break;
2063             }
2064             /* fall through */
2065         default :
2066             err = AFPERR_BITMAP;
2067             bitmap = 0;
2068             break;
2069         }
2070
2071         bitmap = bitmap>>1;
2072         bit++;
2073     }
2074     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2075
2076     if (ad_open_metadata( upath, vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad) < 0) {
2077         /*
2078          * Check to see what we're trying to set.  If it's anything
2079          * but ACCESS, UID, or GID, give an error.  If it's any of those
2080          * three, we don't need the ad to be open, so just continue.
2081          *
2082          * note: we also don't need to worry about mdate. also, be quiet
2083          *       if we're using the noadouble option.
2084          */
2085         if (!vol_noadouble(vol) && (d_bitmap &
2086                                     ~((1<<DIRPBIT_ACCESS)|(1<<DIRPBIT_UNIXPR)|
2087                                       (1<<DIRPBIT_UID)|(1<<DIRPBIT_GID)|
2088                                       (1<<DIRPBIT_MDATE)|(1<<DIRPBIT_PDINFO)))) {
2089             return AFPERR_ACCESS;
2090         }
2091
2092         isad = 0;
2093     } else {
2094         /*
2095          * Check to see if a create was necessary. If it was, we'll want
2096          * to set our name, etc.
2097          */
2098         if ( (ad_get_HF_flags( &ad ) & O_CREAT)) {
2099             ad_setname(&ad, curdir->d_m_name);
2100         }
2101     }
2102
2103     bit = 0;
2104     bitmap = d_bitmap;
2105     while ( bitmap != 0 ) {
2106         while (( bitmap & 1 ) == 0 ) {
2107             bitmap = bitmap>>1;
2108             bit++;
2109         }
2110
2111         switch( bit ) {
2112         case DIRPBIT_ATTR :
2113             if (isad) {
2114                 ad_getattr(&ad, &bshort);
2115                 if ((bshort & htons(ATTRBIT_INVISIBLE)) != 
2116                     (ashort & htons(ATTRBIT_INVISIBLE) & htons(ATTRBIT_SETCLR)) )
2117                     change_parent_mdate = 1;
2118                 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
2119                     bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
2120                 } else {
2121                     bshort &= ~ashort;
2122                 }
2123                 ad_setattr(&ad, bshort);
2124             }
2125             break;
2126         case DIRPBIT_CDATE :
2127             if (isad) {
2128                 ad_setdate(&ad, AD_DATE_CREATE, cdate);
2129             }
2130             break;
2131         case DIRPBIT_MDATE :
2132             break;
2133         case DIRPBIT_BDATE :
2134             if (isad) {
2135                 ad_setdate(&ad, AD_DATE_BACKUP, bdate);
2136             }
2137             break;
2138         case DIRPBIT_FINFO :
2139             if (isad) {
2140                 if (  dir->d_did == DIRDID_ROOT ) {
2141                     /*
2142                      * Alright, we admit it, this is *really* sick!
2143                      * The 4 bytes that we don't copy, when we're dealing
2144                      * with the root of a volume, are the directory's
2145                      * location information. This eliminates that annoying
2146                      * behavior one sees when mounting above another mount
2147                      * point.
2148                      */
2149                     memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 10 );
2150                     memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, finder_buf + 14, 18 );
2151                 } else {
2152                     memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 32 );
2153                 }
2154             }
2155             break;
2156         case DIRPBIT_UID :      /* What kind of loser mounts as root? */
2157             if ( (dir->d_did == DIRDID_ROOT) &&
2158                     (setdeskowner( ntohl(owner), -1 ) < 0)) {
2159                 err = set_dir_errors(path, "setdeskowner", errno);
2160                 if (isad && err == AFPERR_PARAM) {
2161                     err = AFP_OK; /* ???*/
2162                 }
2163                 else {
2164                     goto setdirparam_done;
2165                 }
2166             }
2167             if ( setdirowner(vol, upath, ntohl(owner), -1 ) < 0 ) {
2168                 err = set_dir_errors(path, "setdirowner", errno);
2169                 goto setdirparam_done;
2170             }
2171             break;
2172         case DIRPBIT_GID :
2173             if (dir->d_did == DIRDID_ROOT)
2174                 setdeskowner( -1, ntohl(group) ); 
2175             if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2176                 err = set_dir_errors(path, "setdirowner", errno);
2177                 goto setdirparam_done;
2178             }
2179             break;
2180         case DIRPBIT_ACCESS :
2181             if (dir->d_did == DIRDID_ROOT) {
2182                 setdeskmode(mpriv);
2183                 if (!dir_rx_set(mpriv)) {
2184                     /* we can't remove read and search for owner on volume root */
2185                     err = AFPERR_ACCESS;
2186                     goto setdirparam_done;
2187                 }
2188             }
2189
2190             if (!dir_rx_set(mpriv) && setdirmode( vol, upath, mpriv) < 0 ) {
2191                 err = set_dir_errors(path, "setdirmode", errno);
2192                 goto setdirparam_done;
2193             }
2194             break;
2195         case DIRPBIT_PDINFO :
2196             if (afp_version >= 30) {
2197                 err = AFPERR_BITMAP;
2198                 goto setdirparam_done;
2199             }
2200             break;
2201         case DIRPBIT_UNIXPR :
2202             if (vol_unix_priv(vol)) {
2203                 if (dir->d_did == DIRDID_ROOT) {
2204                     if (!dir_rx_set(upriv)) {
2205                         /* we can't remove read and search for owner on volume root */
2206                         err = AFPERR_ACCESS;
2207                         goto setdirparam_done;
2208                     }
2209                     setdeskowner( -1, ntohl(group) ); 
2210                     setdeskmode( upriv );
2211                 }
2212                 if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
2213                     err = set_dir_errors(path, "setdirowner", errno);
2214                     goto setdirparam_done;
2215                 }
2216
2217                 if ( upriv_bit && setdirunixmode(vol, upath, upriv) < 0 ) {
2218                     err = set_dir_errors(path, "setdirunixmode", errno);
2219                     goto setdirparam_done;
2220                 }
2221             }
2222             else {
2223                 err = AFPERR_BITMAP;
2224                 goto setdirparam_done;
2225             }
2226             break;
2227         default :
2228             err = AFPERR_BITMAP;
2229             goto setdirparam_done;
2230             break;
2231         }
2232
2233         bitmap = bitmap>>1;
2234         bit++;
2235     }
2236
2237 setdirparam_done:
2238     if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
2239        newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2240     }
2241     if (newdate) {
2242        if (isad)
2243           ad_setdate(&ad, AD_DATE_MODIFY, newdate);
2244        ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
2245        utime(upath, &ut);
2246     }
2247
2248     if ( isad ) {
2249         if (path->st_valid && !path->st_errno) {
2250             struct stat *st = &path->st;
2251
2252             if (dir && dir->d_parent) {
2253                 ad_setid(&ad, st->st_dev, st->st_ino,  dir->d_did, dir->d_parent->d_did, vol->v_stamp);
2254             }
2255         }
2256         ad_flush( &ad);
2257         ad_close_metadata( &ad);
2258     }
2259
2260     if (change_parent_mdate && dir->d_did != DIRDID_ROOT
2261             && gettimeofday(&tv, NULL) == 0) {
2262        if (!movecwd(vol, dir->d_parent)) {
2263            newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2264            /* be careful with bitmap because now dir is null */
2265            bitmap = 1<<DIRPBIT_MDATE;
2266            setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
2267            /* should we reset curdir ?*/
2268        }
2269     }
2270
2271     return err;
2272 }
2273
2274 int afp_createdir(obj, ibuf, ibuflen, rbuf, rbuflen )
2275 AFPObj  *obj;
2276 char    *ibuf, *rbuf;
2277 int     ibuflen _U_, *rbuflen;
2278 {
2279     struct adouble      ad;
2280     struct vol          *vol;
2281     struct dir          *dir;
2282     char                *upath;
2283     struct path         *s_path;
2284     u_int32_t           did;
2285     u_int16_t           vid;
2286     int                 err;
2287     
2288     *rbuflen = 0;
2289     ibuf += 2;
2290
2291     memcpy( &vid, ibuf, sizeof( vid ));
2292     ibuf += sizeof( vid );
2293     if (NULL == ( vol = getvolbyvid( vid )) ) {
2294         return( AFPERR_PARAM );
2295     }
2296
2297     if (vol->v_flags & AFPVOL_RO)
2298         return AFPERR_VLOCK;
2299
2300     memcpy( &did, ibuf, sizeof( did ));
2301     ibuf += sizeof( did );
2302     if (NULL == ( dir = dirlookup( vol, did )) ) {
2303         return afp_errno; /* was AFPERR_NOOBJ */
2304     }
2305     /* for concurrent access we need to be sure we are not in the
2306      * folder we want to create...
2307     */
2308     movecwd(vol, dir);
2309     
2310     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
2311         return get_afp_errno(AFPERR_PARAM);
2312     }
2313     /* cname was able to move curdir to it! */
2314     if (*s_path->m_name == '\0')
2315         return AFPERR_EXIST;
2316
2317     upath = s_path->u_name;
2318     if (0 != (err = check_name(vol, upath))) {
2319        return err;
2320     }
2321
2322     if (AFP_OK != (err = netatalk_mkdir( upath))) {
2323         return err;
2324     }
2325
2326     if (of_stat(s_path) < 0) {
2327         return AFPERR_MISC;
2328     }
2329     curdir->offcnt++;
2330     if ((dir = adddir( vol, curdir, s_path)) == NULL) {
2331         return AFPERR_MISC;
2332     }
2333
2334     if ( movecwd( vol, dir ) < 0 ) {
2335         return( AFPERR_PARAM );
2336     }
2337
2338     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2339     if (ad_open_metadata( ".", vol_noadouble(vol)|ADFLAGS_DIR, O_CREAT, &ad ) < 0)  {
2340         if (vol_noadouble(vol))
2341             goto createdir_done;
2342         return( AFPERR_ACCESS );
2343     }
2344     ad_setname(&ad, s_path->m_name);
2345     ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
2346
2347     ad_flush( &ad);
2348     ad_close_metadata( &ad);
2349
2350 createdir_done:
2351     memcpy( rbuf, &dir->d_did, sizeof( u_int32_t ));
2352     *rbuflen = sizeof( u_int32_t );
2353     setvoltime(obj, vol );
2354     return( AFP_OK );
2355 }
2356
2357 /*
2358  * dst       new unix filename (not a pathname)
2359  * newname   new mac name
2360  * newparent curdir
2361  *
2362 */
2363 int renamedir(vol, src, dst, dir, newparent, newname)
2364 const struct vol *vol;
2365 char    *src, *dst, *newname;
2366 struct dir      *dir, *newparent;
2367 {
2368     struct adouble      ad;
2369     struct dir          *parent;
2370     char                *buf;
2371     int                 len, err;
2372         
2373     /* existence check moved to afp_moveandrename */
2374     if ( unix_rename( src, dst ) < 0 ) {
2375         switch ( errno ) {
2376         case ENOENT :
2377             return( AFPERR_NOOBJ );
2378         case EACCES :
2379             return( AFPERR_ACCESS );
2380         case EROFS:
2381             return AFPERR_VLOCK;
2382         case EINVAL:
2383             /* tried to move directory into a subdirectory of itself */
2384             return AFPERR_CANTMOVE;
2385         case EXDEV:
2386             /* this needs to copy and delete. bleah. that means we have
2387              * to deal with entire directory hierarchies. */
2388             if ((err = copydir(vol, src, dst)) < 0) {
2389                 deletedir(dst);
2390                 return err;
2391             }
2392             if ((err = deletedir(src)) < 0)
2393                 return err;
2394             break;
2395         default :
2396             return( AFPERR_PARAM );
2397         }
2398     }
2399
2400     vol->vfs->rf_renamedir(vol, src, dst);
2401
2402     len = strlen( newname );
2403     /* rename() succeeded so we need to update our tree even if we can't open
2404      * metadata
2405     */
2406     
2407     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2408
2409     if (!ad_open_metadata( dst, ADFLAGS_DIR, 0, &ad)) {
2410         ad_setname(&ad, newname);
2411         ad_flush( &ad);
2412         ad_close_metadata( &ad);
2413     }
2414
2415     dir_hash_del(vol, dir);
2416     if (dir->d_m_name == dir->d_u_name)
2417         dir->d_u_name = NULL;
2418
2419     if ((buf = (char *) realloc( dir->d_m_name, len + 1 )) == NULL ) {
2420         LOG(log_error, logtype_afpd, "renamedir: realloc mac name: %s", strerror(errno) );
2421         /* FIXME : fatal ? */
2422         return AFPERR_MISC;
2423     }
2424     dir->d_m_name = buf;
2425     strcpy( dir->d_m_name, newname );
2426
2427     if (newname == dst) {
2428         free(dir->d_u_name);
2429         dir->d_u_name = dir->d_m_name;
2430     }
2431     else {
2432         if ((buf = (char *) realloc( dir->d_u_name, strlen(dst) + 1 )) == NULL ) {
2433             LOG(log_error, logtype_afpd, "renamedir: realloc unix name: %s", strerror(errno) );
2434             return AFPERR_MISC;
2435         }
2436         dir->d_u_name = buf;
2437         strcpy( dir->d_u_name, dst );
2438     }
2439
2440     if (dir->d_m_name_ucs2)
2441         free(dir->d_m_name_ucs2);
2442
2443     dir->d_m_name_ucs2 = NULL;
2444     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))
2445         dir->d_m_name_ucs2 = NULL;
2446
2447     if (( parent = dir->d_parent ) == NULL ) {
2448         return( AFP_OK );
2449     }
2450     if ( parent == newparent ) {
2451         hash_alloc_insert(vol->v_hash, dir, dir);
2452         return( AFP_OK );
2453     }
2454
2455     /* detach from old parent and add to new one. */
2456     dirchildremove(parent, dir);
2457     dir->d_parent = newparent;
2458     dirchildadd(vol, newparent, dir);
2459     return( AFP_OK );
2460 }
2461
2462 /* delete an empty directory */
2463 int deletecurdir( vol)
2464 const struct vol        *vol;
2465 {
2466     struct dirent *de;
2467     struct stat st;
2468     struct dir  *fdir;
2469     DIR *dp;
2470     struct adouble      ad;
2471     u_int16_t           ashort;
2472     int err;
2473
2474     if ( curdir->d_parent == NULL ) {
2475         return( AFPERR_ACCESS );
2476     }
2477
2478     fdir = curdir;
2479
2480     ad_init(&ad, vol->v_adouble, vol->v_ad_options);
2481     if ( ad_metadata( ".", ADFLAGS_DIR, &ad) == 0 ) {
2482
2483         ad_getattr(&ad, &ashort);
2484         ad_close( &ad, ADFLAGS_HF );
2485         if ((ashort & htons(ATTRBIT_NODELETE))) {
2486             return  AFPERR_OLOCK;
2487         }
2488     }
2489     err = vol->vfs->rf_deletecurdir(vol);
2490     if (err) {
2491         return err;
2492     }
2493
2494     /* now get rid of dangling symlinks */
2495     if ((dp = opendir("."))) {
2496         while ((de = readdir(dp))) {
2497             /* skip this and previous directory */
2498             if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
2499                 continue;
2500
2501             /* bail if it's not a symlink */
2502             if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
2503                 closedir(dp);
2504                 return AFPERR_DIRNEMPT;
2505             }
2506
2507             if ((err = netatalk_unlink(de->d_name))) {
2508                 closedir(dp);
2509                 return err;
2510             }
2511         }
2512     }
2513
2514     if ( movecwd( vol, curdir->d_parent ) < 0 ) {
2515         err = afp_errno;
2516         goto delete_done;
2517     }
2518
2519     if ( !(err = netatalk_rmdir(fdir->d_u_name))) {
2520         dirchildremove(curdir, fdir);
2521         cnid_delete(vol->v_cdb, fdir->d_did);
2522         dir_remove( vol, fdir );
2523         err = AFP_OK;
2524     }
2525 delete_done:
2526     if (dp) {
2527         /* inode is used as key for cnid.
2528          * Close the descriptor only after cnid_delete
2529          * has been called. 
2530         */
2531         closedir(dp);
2532     }
2533     return err;
2534 }
2535
2536 int afp_mapid(obj, ibuf, ibuflen, rbuf, rbuflen )
2537 AFPObj  *obj;
2538 char    *ibuf, *rbuf;
2539 int     ibuflen _U_, *rbuflen;
2540 {
2541     struct passwd       *pw;
2542     struct group        *gr;
2543     char                *name;
2544     u_int32_t           id;
2545     int                 len, sfunc;
2546     int         utf8 = 0;
2547     
2548     ibuf++;
2549     sfunc = (unsigned char) *ibuf++;
2550     memcpy( &id, ibuf, sizeof( id ));
2551
2552     id = ntohl(id);
2553     *rbuflen = 0;
2554
2555     if (sfunc == 3 || sfunc == 4) {
2556         if (afp_version < 30) {
2557             return( AFPERR_PARAM );
2558         }
2559         utf8 = 1;
2560     }
2561     if ( id != 0 ) {
2562         switch ( sfunc ) {
2563         case 1 :
2564         case 3 :/* unicode */
2565             if (( pw = getpwuid( id )) == NULL ) {
2566                 return( AFPERR_NOITEM );
2567             }
2568             len = convert_string_allocate( obj->options.unixcharset, ((!utf8)?obj->options.maccharset:CH_UTF8_MAC),
2569                                             pw->pw_name, strlen(pw->pw_name), &name);
2570             break;
2571
2572         case 2 :
2573         case 4 : /* unicode */
2574             if (NULL == ( gr = (struct group *)getgrgid( id ))) {
2575                 return( AFPERR_NOITEM );
2576             }
2577             len = convert_string_allocate( obj->options.unixcharset, (!utf8)?obj->options.maccharset:CH_UTF8_MAC,
2578                                             gr->gr_name, strlen(gr->gr_name), &name);
2579             break;
2580
2581         default :
2582             return( AFPERR_PARAM );
2583         }
2584         len = strlen( name );
2585
2586     } else {
2587         len = 0;
2588         name = NULL;
2589     }
2590     if (utf8) {
2591         u_int16_t tp = htons(len);
2592         memcpy(rbuf, &tp, sizeof(tp));
2593         rbuf += sizeof(tp);
2594         *rbuflen += 2;
2595     }
2596     else {
2597         *rbuf++ = len;
2598         *rbuflen += 1;
2599     }
2600     if ( len > 0 ) {
2601         memcpy( rbuf, name, len );
2602     }
2603     *rbuflen += len;
2604     if (name)
2605         free(name);
2606     return( AFP_OK );
2607 }
2608
2609 int afp_mapname(obj, ibuf, ibuflen, rbuf, rbuflen )
2610 AFPObj  *obj _U_;
2611 char    *ibuf, *rbuf;
2612 int     ibuflen _U_, *rbuflen;
2613 {
2614     struct passwd       *pw;
2615     struct group        *gr;
2616     int             len, sfunc;
2617     u_int32_t       id;
2618     u_int16_t       ulen;
2619
2620     ibuf++;
2621     sfunc = (unsigned char) *ibuf++;
2622     *rbuflen = 0;
2623     switch ( sfunc ) {
2624     case 1 : 
2625     case 2 : /* unicode */
2626         if (afp_version < 30) {
2627             return( AFPERR_PARAM );
2628         }
2629         memcpy(&ulen, ibuf, sizeof(ulen));
2630         len = ntohs(ulen);
2631         ibuf += 2;
2632         break;
2633     case 3 :
2634     case 4 :
2635         len = (unsigned char) *ibuf++;
2636         break;
2637     default :
2638         return( AFPERR_PARAM );
2639     }
2640
2641     ibuf[ len ] = '\0';
2642
2643     if ( len != 0 ) {
2644         switch ( sfunc ) {
2645         case 1 : /* unicode */
2646         case 3 :
2647             if (NULL == ( pw = (struct passwd *)getpwnam( ibuf )) ) {
2648                 return( AFPERR_NOITEM );
2649             }
2650             id = pw->pw_uid;
2651             break;
2652
2653         case 2 : /* unicode */
2654         case 4 :
2655             if (NULL == ( gr = (struct group *)getgrnam( ibuf ))) {
2656                 return( AFPERR_NOITEM );
2657             }
2658             id = gr->gr_gid;
2659             break;
2660         }
2661     } else {
2662         id = 0;
2663     }
2664     id = htonl(id);
2665     memcpy( rbuf, &id, sizeof( id ));
2666     *rbuflen = sizeof( id );
2667     return( AFP_OK );
2668 }
2669
2670 /* ------------------------------------
2671   variable DID support 
2672 */
2673 int afp_closedir(obj, ibuf, ibuflen, rbuf, rbuflen )
2674 AFPObj  *obj _U_;
2675 char    *ibuf _U_, *rbuf _U_;
2676 int     ibuflen _U_, *rbuflen;
2677 {
2678 #if 0
2679     struct vol   *vol;
2680     struct dir   *dir;
2681     u_int16_t    vid;
2682     u_int32_t    did;
2683 #endif /* 0 */
2684
2685     *rbuflen = 0;
2686
2687     /* do nothing as dids are static for the life of the process. */
2688 #if 0
2689     ibuf += 2;
2690
2691     memcpy(&vid,  ibuf, sizeof( vid ));
2692     ibuf += sizeof( vid );
2693     if (( vol = getvolbyvid( vid )) == NULL ) {
2694         return( AFPERR_PARAM );
2695     }
2696
2697     memcpy( &did, ibuf, sizeof( did ));
2698     ibuf += sizeof( did );
2699     if (( dir = dirlookup( vol, did )) == NULL ) {
2700         return( AFPERR_PARAM );
2701     }
2702
2703     /* dir_remove -- deletedid */
2704 #endif /* 0 */
2705
2706     return AFP_OK;
2707 }
2708
2709 /* did creation gets done automatically 
2710  * there's a pb again with case but move it to cname
2711 */
2712 int afp_opendir(obj, ibuf, ibuflen, rbuf, rbuflen )
2713 AFPObj  *obj _U_;
2714 char    *ibuf, *rbuf;
2715 int     ibuflen  _U_, *rbuflen;
2716 {
2717     struct vol          *vol;
2718     struct dir          *parentdir;
2719     struct path         *path;
2720     u_int32_t           did;
2721     u_int16_t           vid;
2722
2723     *rbuflen = 0;
2724     ibuf += 2;
2725
2726     memcpy(&vid, ibuf, sizeof(vid));
2727     ibuf += sizeof( vid );
2728
2729     if (NULL == ( vol = getvolbyvid( vid )) ) {
2730         return( AFPERR_PARAM );
2731     }
2732
2733     memcpy(&did, ibuf, sizeof(did));
2734     ibuf += sizeof(did);
2735
2736     if (NULL == ( parentdir = dirlookup( vol, did )) ) {
2737         return afp_errno;
2738     }
2739
2740     if (NULL == ( path = cname( vol, parentdir, &ibuf )) ) {
2741         return get_afp_errno(AFPERR_PARAM);
2742     }
2743
2744     if ( *path->m_name != '\0' ) {
2745         return path_error(path, AFPERR_NOOBJ);
2746     }
2747
2748     if ( !path->st_valid && of_stat(path ) < 0 ) {
2749         return( AFPERR_NOOBJ );
2750     }
2751     if ( path->st_errno ) {
2752         return( AFPERR_NOOBJ );
2753     }
2754
2755     memcpy(rbuf, &curdir->d_did, sizeof(curdir->d_did));
2756     *rbuflen = sizeof(curdir->d_did);
2757     return AFP_OK;
2758 }