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