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