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