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