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