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