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