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