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