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