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