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