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