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