]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/directory.c
0bbfade20bbf355e0707ef8d206239e180f8355f
[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
6 #ifdef HAVE_CONFIG_H
7 #include "config.h"
8 #endif /* HAVE_CONFIG_H */
9
10 #include <string.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <stdbool.h>
14 #include <grp.h>
15 #include <pwd.h>
16 #include <sys/param.h>
17 #include <sys/stat.h>
18 #include <errno.h>
19 #include <utime.h>
20 #include <assert.h>
21
22 #include <atalk/adouble.h>
23 #include <atalk/vfs.h>
24 #include <atalk/afp.h>
25 #include <atalk/util.h>
26 #include <atalk/cnid.h>
27 #include <atalk/logger.h>
28 #include <atalk/uuid.h>
29 #include <atalk/unix.h>
30 #include <atalk/bstrlib.h>
31 #include <atalk/bstradd.h>
32 #include <atalk/errchk.h>
33 #include <atalk/globals.h>
34 #include <atalk/fce_api.h>
35 #include <atalk/netatalk_conf.h>
36
37 #include "directory.h"
38 #include "dircache.h"
39 #include "desktop.h"
40 #include "volume.h"
41 #include "fork.h"
42 #include "file.h"
43 #include "filedir.h"
44 #include "unix.h"
45 #include "mangle.h"
46 #include "hash.h"
47
48 /*
49  * FIXMEs, loose ends after the dircache rewrite:
50  * o merge dircache_search_by_name and dir_add ??
51  * o case-insensitivity is gone from cname
52  */
53
54
55 /*******************************************************************************************
56  * Globals
57  ******************************************************************************************/
58
59 int         afp_errno;
60 /* As long as directory.c hasn't got its own init call, this get initialized in dircache_init */
61 struct dir rootParent  = {
62     NULL, NULL, NULL, NULL,          /* path, d_m_name, d_u_name, d_m_name_ucs2 */
63     NULL, 0, 0,                      /* qidx_node, ctime, d_flags */
64     0, 0, 0, 0                       /* pdid, did, offcnt, d_vid */
65 };
66 struct dir  *curdir = &rootParent;
67 struct path Cur_Path = {
68     0,
69     "",  /* mac name */
70     ".", /* unix name */
71     0,   /* id */
72     NULL,/* struct dir * */
73     0,   /* stat is not set */
74     0,   /* errno */
75     {0} /* struct stat */
76 };
77
78 /*
79  * dir_remove queues struct dirs to be freed here. We can't just delete them immeidately
80  * eg in dircache_search_by_id, because a caller somewhere up the stack might be
81  * referencing it.
82  * So instead:
83  * - we mark it as invalid by setting d_did to CNID_INVALID (ie 0)
84  * - queue it in "invalid_dircache_entries" queue
85  * - which is finally freed at the end of every AFP func in afp_dsi.c.
86  */
87 q_t *invalid_dircache_entries;
88
89
90 /*******************************************************************************************
91  * Locals
92  ******************************************************************************************/
93
94
95 /* -------------------------
96    appledouble mkdir afp error code.
97 */
98 static int netatalk_mkdir(const struct vol *vol, const char *name)
99 {
100     int ret;
101     struct stat st;
102
103     if (vol->v_flags & AFPVOL_UNIX_PRIV) {
104         if (lstat(".", &st) < 0)
105             return AFPERR_MISC;
106         int mode = (DIRBITS & (~S_ISGID & st.st_mode)) | (0777 & ~vol->v_umask);
107         LOG(log_maxdebug, logtype_afpd, "netatalk_mkdir(\"%s\") {parent mode: %04o, vol umask: %04o}",
108             name, st.st_mode, vol->v_umask);
109
110         ret = mkdir(name, mode);
111     } else {
112         ret = ad_mkdir(name, DIRBITS | 0777);
113     }
114
115     if (ret < 0) {
116         switch ( errno ) {
117         case ENOENT :
118             return( AFPERR_NOOBJ );
119         case EROFS :
120             return( AFPERR_VLOCK );
121         case EPERM:
122         case EACCES :
123             return( AFPERR_ACCESS );
124         case EEXIST :
125             return( AFPERR_EXIST );
126         case ENOSPC :
127         case EDQUOT :
128             return( AFPERR_DFULL );
129         default :
130             return( AFPERR_PARAM );
131         }
132     }
133     return AFP_OK;
134 }
135
136 /* ------------------- */
137 static int deletedir(const struct vol *vol, int dirfd, char *dir)
138 {
139     char path[MAXPATHLEN + 1];
140     DIR *dp;
141     struct dirent   *de;
142     struct stat st;
143     size_t len;
144     int err = AFP_OK;
145     size_t remain;
146
147     if ((len = strlen(dir)) +2 > sizeof(path))
148         return AFPERR_PARAM;
149
150     /* already gone */
151     if ((dp = opendirat(dirfd, dir)) == NULL)
152         return AFP_OK;
153
154     strcpy(path, dir);
155     strcat(path, "/");
156     len++;
157     remain = sizeof(path) -len -1;
158     while ((de = readdir(dp)) && err == AFP_OK) {
159         /* skip this and previous directory */
160         if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
161             continue;
162
163         if (strlen(de->d_name) > remain) {
164             err = AFPERR_PARAM;
165             break;
166         }
167         strcpy(path + len, de->d_name);
168         if (ostatat(dirfd, path, &st, vol_syml_opt(vol))) {
169             continue;
170         }
171         if (S_ISDIR(st.st_mode)) {
172             err = deletedir(vol, dirfd, path);
173         } else {
174             err = netatalk_unlinkat(dirfd, path);
175         }
176     }
177     closedir(dp);
178
179     /* okay. the directory is empty. delete it. note: we already got rid
180        of .AppleDouble.  */
181     if (err == AFP_OK) {
182         err = netatalk_rmdir(dirfd, dir);
183     }
184     return err;
185 }
186
187 /* do a recursive copy. */
188 static int copydir(struct vol *vol, struct dir *ddir, int dirfd, char *src, char *dst)
189 {
190     char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1];
191     DIR *dp;
192     struct dirent   *de;
193     struct stat st;
194     struct utimbuf      ut;
195     size_t slen, dlen;
196     size_t srem, drem;
197     int err;
198
199     /* doesn't exist or the path is too long. */
200     if (((slen = strlen(src)) > sizeof(spath) - 2) ||
201         ((dlen = strlen(dst)) > sizeof(dpath) - 2) ||
202         ((dp = opendirat(dirfd, src)) == NULL))
203         return AFPERR_PARAM;
204
205     /* try to create the destination directory */
206     if (AFP_OK != (err = netatalk_mkdir(vol, dst)) ) {
207         closedir(dp);
208         return err;
209     }
210
211     /* set things up to copy */
212     strcpy(spath, src);
213     strcat(spath, "/");
214     slen++;
215     srem = sizeof(spath) - slen -1;
216
217     strcpy(dpath, dst);
218     strcat(dpath, "/");
219     dlen++;
220     drem = sizeof(dpath) - dlen -1;
221
222     err = AFP_OK;
223     while ((de = readdir(dp))) {
224         /* skip this and previous directory */
225         if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
226             continue;
227
228         if (strlen(de->d_name) > srem) {
229             err = AFPERR_PARAM;
230             break;
231         }
232         strcpy(spath + slen, de->d_name);
233
234         if (ostatat(dirfd, spath, &st, vol_syml_opt(vol)) == 0) {
235             if (strlen(de->d_name) > drem) {
236                 err = AFPERR_PARAM;
237                 break;
238             }
239             strcpy(dpath + dlen, de->d_name);
240
241             if (S_ISDIR(st.st_mode)) {
242                 if (AFP_OK != (err = copydir(vol, ddir, dirfd, spath, dpath)))
243                     goto copydir_done;
244             } else if (AFP_OK != (err = copyfile(vol, vol, ddir, dirfd, spath, dpath, NULL, NULL))) {
245                 goto copydir_done;
246
247             } else {
248                 /* keep the same time stamp. */
249                 ut.actime = ut.modtime = st.st_mtime;
250                 utime(dpath, &ut);
251             }
252         }
253     }
254
255     /* keep the same time stamp. */
256     if (ostatat(dirfd, src, &st, vol_syml_opt(vol)) == 0) {
257         ut.actime = ut.modtime = st.st_mtime;
258         utime(dst, &ut);
259     }
260
261 copydir_done:
262     closedir(dp);
263     return err;
264 }
265
266 /* ---------------------
267  * is our cached offspring count valid?
268  */
269 static int diroffcnt(struct dir *dir, struct stat *st)
270 {
271     return st->st_ctime == dir->d_ctime;
272 }
273
274 /* --------------------- */
275 static int invisible_dots(const struct vol *vol, const char *name)
276 {
277     return vol_inv_dots(vol) && *name  == '.' && strcmp(name, ".") && strcmp(name, "..");
278 }
279
280 /* ------------------ */
281 static int set_dir_errors(struct path *path, const char *where, int err)
282 {
283     switch ( err ) {
284     case EPERM :
285     case EACCES :
286         return AFPERR_ACCESS;
287     case EROFS :
288         return AFPERR_VLOCK;
289     }
290     LOG(log_error, logtype_afpd, "setdirparam(%s): %s: %s", fullpathname(path->u_name), where, strerror(err) );
291     return AFPERR_PARAM;
292 }
293
294 /*!
295  * @brief Convert name in client encoding to server encoding
296  *
297  * Convert ret->m_name to ret->u_name from client encoding to server encoding.
298  * This only gets called from cname().
299  *
300  * @returns 0 on success, -1 on error
301  *
302  * @note If the passed ret->m_name is mangled, we'll demangle it
303  */
304 static int cname_mtouname(const struct vol *vol, struct dir *dir, struct path *ret, int toUTF8)
305 {
306     static char temp[ MAXPATHLEN + 1];
307     char *t;
308     cnid_t fileid = 0;
309
310     if (vol->v_obj->afp_version >= 30) {
311         if (toUTF8) {
312             if (dir->d_did == DIRDID_ROOT_PARENT) {
313                 /*
314                  * With uft8 volume name is utf8-mac, but requested path may be a mangled longname. See #2611981.
315                  * So we compare it with the longname from the current volume and if they match
316                  * we overwrite the requested path with the utf8 volume name so that the following
317                  * strcmp can match.
318                  */
319                 ucs2_to_charset(vol->v_maccharset, vol->v_macname, temp, AFPVOL_MACNAMELEN + 1);
320                 if (strcasecmp(ret->m_name, temp) == 0)
321                     ucs2_to_charset(CH_UTF8_MAC, vol->v_u8mname, ret->m_name, AFPVOL_U8MNAMELEN);
322             } else {
323                 /* toUTF8 */
324                 if (mtoUTF8(vol, ret->m_name, strlen(ret->m_name), temp, MAXPATHLEN) == (size_t)-1) {
325                     afp_errno = AFPERR_PARAM;
326                     return -1;
327                 }
328                 strcpy(ret->m_name, temp);
329             }
330         }
331
332         /* check for OS X mangled filename :( */
333         t = demangle_osx(vol, ret->m_name, dir->d_did, &fileid);
334
335         if (curdir == NULL) {
336             /* demangle_osx() calls dirlookup() which might have clobbered curdir */
337             movecwd(vol, dir);
338         }
339
340         LOG(log_maxdebug, logtype_afpd, "cname_mtouname('%s',did:%u) {demangled:'%s', fileid:%u}",
341             ret->m_name, ntohl(dir->d_did), t, ntohl(fileid));
342
343         if (t != ret->m_name) {
344             ret->u_name = t;
345             /* duplicate work but we can't reuse all convert_char we did in demangle_osx
346              * flags weren't the same
347              */
348             if ( (t = utompath(vol, ret->u_name, fileid, utf8_encoding(vol->v_obj))) ) {
349                 /* at last got our view of mac name */
350                 strcpy(ret->m_name, t);
351             }
352         }
353     } /* afp_version >= 30 */
354
355     /* If we haven't got it by now, get it */
356     if (ret->u_name == NULL) {
357         if ((ret->u_name = mtoupath(vol, ret->m_name, dir->d_did, utf8_encoding(vol->v_obj))) == NULL) {
358             afp_errno = AFPERR_PARAM;
359             return -1;
360         }
361     }
362
363     return 0;
364 }
365
366 /*!
367  * @brief Build struct path from struct dir
368  *
369  * The final movecwd in cname failed, possibly with EPERM or ENOENT. We:
370  * 1. move cwd into parent dir (we're often already there, but not always)
371  * 2. set struct path to the dirname
372  * 3. in case of
373  *    AFPERR_ACCESS: the dir is there, we just cant chdir into it
374  *    AFPERR_NOOBJ: the dir was there when we stated it in cname, so we have a race
375  *                  4. indicate there's no dir for this path
376  *                  5. remove the dir
377  */
378 static struct path *path_from_dir(struct vol *vol, struct dir *dir, struct path *ret)
379 {
380     if (dir->d_did == DIRDID_ROOT_PARENT || dir->d_did == DIRDID_ROOT)
381         return NULL;
382
383     switch (afp_errno) {
384
385     case AFPERR_ACCESS:
386         if (movecwd( vol, dirlookup(vol, dir->d_pdid)) < 0 ) /* 1 */
387             return NULL;
388
389         memcpy(ret->m_name, cfrombstr(dir->d_m_name), blength(dir->d_m_name) + 1); /* 3 */
390         if (dir->d_m_name == dir->d_u_name) {
391             ret->u_name = ret->m_name;
392         } else {
393             ret->u_name =  ret->m_name + blength(dir->d_m_name) + 1;
394             memcpy(ret->u_name, cfrombstr(dir->d_u_name), blength(dir->d_u_name) + 1);
395         }
396
397         ret->d_dir = dir;
398
399         LOG(log_debug, logtype_afpd, "cname('%s') {path-from-dir: AFPERR_ACCESS. curdir:'%s', path:'%s'}",
400             cfrombstr(dir->d_fullpath),
401             cfrombstr(curdir->d_fullpath),
402             ret->u_name);
403
404         return ret;
405
406     case AFPERR_NOOBJ:
407         if (movecwd(vol, dirlookup(vol, dir->d_pdid)) < 0 ) /* 1 */
408             return NULL;
409
410         memcpy(ret->m_name, cfrombstr(dir->d_m_name), blength(dir->d_m_name) + 1);
411         if (dir->d_m_name == dir->d_u_name) {
412             ret->u_name = ret->m_name;
413         } else {
414             ret->u_name =  ret->m_name + blength(dir->d_m_name) + 1;
415             memcpy(ret->u_name, cfrombstr(dir->d_u_name), blength(dir->d_u_name) + 1);
416         }
417
418         ret->d_dir = NULL;      /* 4 */
419         dir_remove(vol, dir);   /* 5 */
420         return ret;
421
422     default:
423         return NULL;
424     }
425
426     /* DEADC0DE: never get here */
427     return NULL;
428 }
429
430
431 /*********************************************************************************************
432  * Interface
433  ********************************************************************************************/
434
435 int get_afp_errno(const int param)
436 {
437     if (afp_errno != AFPERR_DID1)
438         return afp_errno;
439     return param;
440 }
441
442 /*!
443  * Resolve struct dir for an absolute path
444  *
445  * Given a path like "/Volumes/volume/dir/subdir" in a volume "/Volumes/volume" return
446  * a pointer to struct dir of "subdir".
447  * 1. Remove volue path from absolute path
448  * 2. start path
449  * 3. loop through all elements of the remaining path from 1.
450  * 4. we only allow dirs
451  * 5. search dircache
452  * 6. if not found in the dircache query the CNID database for the DID
453  * 7. and use dirlookup to resolve the DID to a it's struct dir *
454  *
455  * @param vol   (r) volume the path is in, must be known
456  * @param path  (r) absoule path
457  *
458  * @returns pointer to struct dir or NULL on error
459  */
460 struct dir *dirlookup_bypath(const struct vol *vol, const char *path)
461 {
462     EC_INIT;
463
464     struct dir *dir = NULL;
465     cnid_t cnid, did;
466     bstring rpath = NULL;
467     bstring statpath = NULL;
468     struct bstrList *l = NULL;
469     struct stat st;
470
471     cnid = htonl(2);
472     dir = vol->v_root;
473
474     LOG(log_debug, logtype_afpd, "dirlookup_bypath(\"%s\")", path);
475
476     if (strcmp(vol->v_path, path) == 0)
477         return dir;
478
479     EC_NULL(rpath = rel_path_in_vol(path, vol->v_path)); /* 1. */
480
481     LOG(log_debug, logtype_afpd, "dirlookup_bypath: rpath: \"%s\"", cfrombstr(rpath));
482
483     EC_NULL(statpath = bfromcstr(vol->v_path));          /* 2. */
484
485     l = bsplit(rpath, '/');
486     for (int i = 0; i < l->qty ; i++) {                  /* 3. */
487         did = cnid;
488         EC_ZERO(bcatcstr(statpath, "/"));
489         EC_ZERO(bconcat(statpath, l->entry[i]));
490
491         LOG(log_debug, logtype_afpd, "dirlookup_bypath: statpath: \"%s\"", cfrombstr(statpath));
492
493         EC_ZERO_LOGSTR(lstat(cfrombstr(statpath), &st),
494                        "lstat(rpath: %s, elem: %s): %s: %s",
495                        cfrombstr(rpath), cfrombstr(l->entry[i]),
496                        cfrombstr(statpath), strerror(errno));
497
498         if (!(S_ISDIR(st.st_mode)))                      /* 4. */
499             EC_FAIL;
500
501         if ((dir = dircache_search_by_name(vol,          /* 5. */
502                                            dir,
503                                            cfrombstr(l->entry[i]),
504                                            blength(l->entry[i]))) == NULL) {
505
506             AFP_CNID_START("cnid_add");
507             cnid = cnid_add(vol->v_cdb,             /* 6. */
508                             &st,
509                             did,
510                             cfrombstr(l->entry[i]),
511                             blength(l->entry[i]),
512                             0);
513             AFP_CNID_DONE();
514             if (cnid == CNID_INVALID)
515                 EC_FAIL;
516
517             if ((dir = dirlookup(vol, cnid)) == NULL) /* 7. */
518                 EC_FAIL;
519         }
520     }
521
522 EC_CLEANUP:
523     bdestroy(rpath);
524     bstrListDestroy(l);
525     bdestroy(statpath);
526     if (ret != 0)
527         return NULL;
528
529     LOG(log_debug, logtype_afpd, "dirlookup_bypath: result: \"%s\"",
530         cfrombstr(dir->d_fullpath));
531
532     return dir;
533 }
534
535 /*!
536  * @brief Resolve a DID
537  *
538  * Resolve a DID, allocate a struct dir for it
539  * 1. Check for special CNIDs 0 (invalid), 1 and 2.
540  * 2a. Check if the DID is in the cache.
541  * 2b. Check if it's really a dir  because we cache files too.
542  * 3. If it's not in the cache resolve it via the database.
543  * 4. Build complete server-side path to the dir.
544  * 5. Check if it exists and is a directory.
545  * 6. Create the struct dir and populate it.
546  * 7. Add it to the cache.
547  *
548  * @param vol   (r) pointer to struct vol
549  * @param did   (r) DID to resolve
550  *
551  * @returns pointer to struct dir
552  */
553 struct dir *dirlookup(const struct vol *vol, cnid_t did)
554 {
555     static char  buffer[12 + MAXPATHLEN + 1];
556     struct stat  st;
557     struct dir   *ret = NULL, *pdir;
558     bstring      fullpath = NULL;
559     char         *upath = NULL, *mpath;
560     cnid_t       cnid, pdid;
561     size_t       maxpath;
562     int          buflen = 12 + MAXPATHLEN + 1;
563     int          utf8;
564     int          err = 0;
565
566     LOG(log_debug, logtype_afpd, "dirlookup(did: %u): START", ntohl(did));
567
568     /* check for did 0, 1 and 2 */
569     if (did == 0 || vol == NULL) { /* 1 */
570         afp_errno = AFPERR_PARAM;
571         ret = NULL;
572         goto exit;
573     } else if (did == DIRDID_ROOT_PARENT) {
574         rootParent.d_vid = vol->v_vid;
575         ret = &rootParent;
576         goto exit;
577     } else if (did == DIRDID_ROOT) {
578         ret = vol->v_root;
579         goto exit;
580     }
581
582     /* Search the cache */
583     if ((ret = dircache_search_by_did(vol, did)) != NULL) { /* 2a */
584         if (ret->d_flags & DIRF_ISFILE) {                   /* 2b */
585             afp_errno = AFPERR_BADTYPE;
586             ret = NULL;
587             goto exit;
588         }
589         if (lstat(cfrombstr(ret->d_fullpath), &st) != 0) {
590             LOG(log_debug, logtype_afpd, "dirlookup(did: %u, path: \"%s\"): lstat: %s",
591                 ntohl(did), cfrombstr(ret->d_fullpath), strerror(errno));
592             switch (errno) {
593             case ENOENT:
594             case ENOTDIR:
595                 /* It's not there anymore, so remove it */
596                 LOG(log_debug, logtype_afpd, "dirlookup(did: %u): calling dir_remove", ntohl(did));
597                 dir_remove(vol, ret);
598                 afp_errno = AFPERR_NOOBJ;
599                 ret = NULL;
600                 goto exit;
601             default:
602                 ret = ret;
603                 goto exit;
604             }
605             /* DEADC0DE */
606             ret = NULL;
607             goto exit;            
608         }
609         ret = ret;
610         goto exit;
611     }
612
613     utf8 = utf8_encoding(vol->v_obj);
614     maxpath = (utf8) ? MAXPATHLEN - 7 : 255;
615
616     /* Get it from the database */
617     cnid = did;
618     LOG(log_debug, logtype_afpd, "dirlookup(did: %u): querying CNID database", ntohl(did));
619
620     AFP_CNID_START("cnid_resolve");
621     upath = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen);
622     AFP_CNID_DONE();
623     if (upath == NULL) {
624         afp_errno = AFPERR_NOOBJ;
625         err = 1;
626         goto exit;
627     }
628     if ((upath = strdup(upath)) == NULL) { /* 3 */
629         afp_errno = AFPERR_NOOBJ;
630         err = 1;
631         goto exit;
632     }
633     pdid = cnid;
634
635     /*
636      * Recurse up the tree, terminates in dirlookup when either
637      * - DIRDID_ROOT is hit
638      * - a cached entry is found
639      */
640     LOG(log_debug, logtype_afpd, "dirlookup(did: %u): recursion for did: %u",
641         ntohl(did), ntohl(pdid));
642     if ((pdir = dirlookup(vol, pdid)) == NULL) {
643         err = 1;
644         goto exit;
645     }
646
647     /* build the fullpath */
648     if ((fullpath = bstrcpy(pdir->d_fullpath)) == NULL
649         || bconchar(fullpath, '/') != BSTR_OK
650         || bcatcstr(fullpath, upath) != BSTR_OK) {
651         err = 1;
652         goto exit;
653     }
654
655     /* stat it and check if it's a dir */
656     LOG(log_debug, logtype_afpd, "dirlookup(did: %u): stating \"%s\"",
657         ntohl(did), cfrombstr(fullpath));
658
659     if (ostat(cfrombstr(fullpath), &st, vol_syml_opt(vol)) != 0) { /* 5a */
660         switch (errno) {
661         case ENOENT:
662             afp_errno = AFPERR_NOOBJ;
663             err = 1;
664             goto exit;
665         case EPERM:
666             afp_errno = AFPERR_ACCESS;
667             err = 1;
668             goto exit;
669         default:
670             afp_errno = AFPERR_MISC;
671             err = 1;
672             goto exit;
673         }
674     } else {
675         if ( ! S_ISDIR(st.st_mode)) { /* 5b */
676             afp_errno = AFPERR_BADTYPE;
677             err = 1;
678             goto exit;
679         }
680     }
681
682     /* Get macname from unix name */
683     if ( (mpath = utompath(vol, upath, did, utf8)) == NULL ) {
684         afp_errno = AFPERR_NOOBJ;
685         err = 1;
686         goto exit;
687     }
688
689     /* Create struct dir */
690     if ((ret = dir_new(mpath, upath, vol, pdid, did, fullpath, &st)) == NULL) { /* 6 */
691         LOG(log_error, logtype_afpd, "dirlookup(did: %u) {%s, %s}: %s", ntohl(did), mpath, upath, strerror(errno));
692         err = 1;
693         goto exit;
694     }
695     
696     /* Add it to the cache only if it's a dir */
697     if (dircache_add(vol, ret) != 0) { /* 7 */
698         err = 1;
699         goto exit;
700     }
701
702 exit:
703     if (upath) free(upath);
704     if (err) {
705         LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {exit_error: %s}",
706             ntohl(did), AfpErr2name(afp_errno));
707         if (fullpath)
708             bdestroy(fullpath);
709         if (ret) {
710             dir_free(ret);
711             ret = NULL;
712         }
713     }
714     if (ret)
715         LOG(log_debug, logtype_afpd, "dirlookup(did: %u): RESULT: pdid: %u, path: \"%s\"",
716             ntohl(ret->d_did), ntohl(ret->d_pdid), cfrombstr(ret->d_fullpath));
717
718     return ret;
719 }
720
721 #define ENUMVETO "./../Network Trash Folder/TheVolumeSettingsFolder/TheFindByContentFolder/:2eDS_Store/Contents/Desktop Folder/Trash/Benutzer/"
722
723 /*!
724  * @brief Construct struct dir
725  *
726  * Construct struct dir from parameters.
727  *
728  * @param m_name   (r) directory name in UTF8-dec
729  * @param u_name   (r) directory name in server side encoding
730  * @param vol      (r) pointer to struct vol
731  * @param pdid     (r) Parent CNID
732  * @param did      (r) CNID
733  * @param path     (r) Full unix path to object
734  * @param st       (r) struct stat of object
735  *
736  * @returns pointer to new struct dir or NULL on error
737  *
738  * @note Most of the time mac name and unix name are the same.
739  */
740 struct dir *dir_new(const char *m_name,
741                     const char *u_name,
742                     const struct vol *vol,
743                     cnid_t pdid,
744                     cnid_t did,
745                     bstring path,
746                     struct stat *st)
747 {
748     struct dir *dir;
749
750     dir = (struct dir *) calloc(1, sizeof( struct dir ));
751     if (!dir)
752         return NULL;
753
754     if ((dir->d_m_name = bfromcstr(m_name)) == NULL) {
755         free(dir);
756         return NULL;
757     }
758
759     if (convert_string_allocate( (utf8_encoding(vol->v_obj)) ? CH_UTF8_MAC : vol->v_maccharset,
760                                  CH_UCS2,
761                                  m_name,
762                                  -1, (char **)&dir->d_m_name_ucs2) == (size_t)-1 ) {
763         LOG(log_error, logtype_afpd, "dir_new(did: %u) {%s, %s}: couldn't set UCS2 name", ntohl(did), m_name, u_name);
764         dir->d_m_name_ucs2 = NULL;
765     }
766
767     if (m_name == u_name || !strcmp(m_name, u_name)) {
768         dir->d_u_name = dir->d_m_name;
769     }
770     else if ((dir->d_u_name = bfromcstr(u_name)) == NULL) {
771         bdestroy(dir->d_m_name);
772         free(dir);
773         return NULL;
774     }
775
776     dir->d_did = did;
777     dir->d_pdid = pdid;
778     dir->d_vid = vol->v_vid;
779     dir->d_fullpath = path;
780     dir->dcache_ctime = st->st_ctime;
781     dir->dcache_ino = st->st_ino;
782     if (!S_ISDIR(st->st_mode))
783         dir->d_flags = DIRF_ISFILE;
784     dir->d_rights_cache = 0xffffffff;
785     return dir;
786 }
787
788 /*!
789  * @brief Free a struct dir and all its members
790  *
791  * @param (rw) pointer to struct dir
792  */
793 void dir_free(struct dir *dir)
794 {
795     if (dir->d_u_name != dir->d_m_name) {
796         bdestroy(dir->d_u_name);
797     }
798     if (dir->d_m_name_ucs2)
799         free(dir->d_m_name_ucs2);
800     bdestroy(dir->d_m_name);
801     bdestroy(dir->d_fullpath);
802     free(dir);
803 }
804
805 /*!
806  * @brief Create struct dir from struct path
807  *
808  * Create a new struct dir from struct path. Then add it to the cache.
809  *
810  * 1. Open adouble file, get CNID from it.
811  * 2. Search the database, hinting with the CNID from (1).
812  * 3. Build fullpath and create struct dir.
813  * 4. Add it to the cache.
814  *
815  * @param vol   (r) pointer to struct vol, possibly modified in callee
816  * @param dir   (r) pointer to parrent directory
817  * @param path  (rw) pointer to struct path with valid path->u_name
818  * @param len   (r) strlen of path->u_name
819  *
820  * @returns Pointer to new struct dir or NULL on error.
821  *
822  * @note Function also assigns path->m_name from path->u_name.
823  */
824 struct dir *dir_add(struct vol *vol, const struct dir *dir, struct path *path, int len)
825 {
826     int err = 0;
827     struct dir  *cdir = NULL;
828     cnid_t      id;
829     struct adouble  ad;
830     struct adouble *adp = NULL;
831     bstring fullpath = NULL;
832
833     AFP_ASSERT(vol);
834     AFP_ASSERT(dir);
835     AFP_ASSERT(path);
836     AFP_ASSERT(len > 0);
837
838     if ((cdir = dircache_search_by_name(vol, dir, path->u_name, strlen(path->u_name))) != NULL) {
839         /* there's a stray entry in the dircache */
840         LOG(log_debug, logtype_afpd, "dir_add(did:%u,'%s/%s'): {stray cache entry: did:%u,'%s', removing}",
841             ntohl(dir->d_did), cfrombstr(dir->d_fullpath), path->u_name,
842             ntohl(cdir->d_did), cfrombstr(dir->d_fullpath));
843         if (dir_remove(vol, cdir) != 0) {
844             dircache_dump();
845             AFP_PANIC("dir_add");
846         }
847     }
848
849     /* get_id needs adp for reading CNID from adouble file */
850     ad_init(&ad, vol);
851     if ((ad_open(&ad, path->u_name, ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_RDONLY)) == 0) /* 1 */
852         adp = &ad;
853
854     /* Get CNID */
855     if ((id = get_id(vol, adp, &path->st, dir->d_did, path->u_name, len)) == 0) { /* 2 */
856         err = 1;
857         goto exit;
858     }
859
860     if (adp)
861         ad_close(adp, ADFLAGS_HF);
862
863     /* Get macname from unixname */
864     if (path->m_name == NULL) {
865         if ((path->m_name = utompath(vol, path->u_name, id, utf8_encoding(vol->v_obj))) == NULL) {
866             LOG(log_error, logtype_afpd, "dir_add(\"%s\"): can't assign macname", path->u_name);
867             err = 2;
868             goto exit;
869         }
870     }
871
872     /* Build fullpath */
873     if ( ((fullpath = bstrcpy(dir->d_fullpath)) == NULL) /* 3 */
874          || (bconchar(fullpath, '/') != BSTR_OK)
875          || (bcatcstr(fullpath, path->u_name)) != BSTR_OK) {
876         LOG(log_error, logtype_afpd, "dir_add: fullpath: %s", strerror(errno) );
877         err = 3;
878         goto exit;
879     }
880
881     /* Allocate and initialize struct dir */
882     if ((cdir = dir_new(path->m_name,
883                         path->u_name,
884                         vol,
885                         dir->d_did,
886                         id,
887                         fullpath,
888                         &path->st)) == NULL) { /* 3 */
889         err = 4;
890         goto exit;
891     }
892
893     if ((dircache_add(vol, cdir)) != 0) { /* 4 */
894         LOG(log_error, logtype_afpd, "dir_add: fatal dircache error: %s", cfrombstr(fullpath));
895         exit(EXITERR_SYS);
896     }
897
898 exit:
899     if (err != 0) {
900         LOG(log_debug, logtype_afpd, "dir_add('%s/%s'): error: %u",
901             cfrombstr(dir->d_u_name), path->u_name, err);
902
903         if (adp)
904             ad_close(adp, ADFLAGS_HF);
905         if (!cdir && fullpath)
906             bdestroy(fullpath);
907         if (cdir)
908             dir_free(cdir);
909         cdir = NULL;
910     } else {
911         /* no error */
912         LOG(log_debug, logtype_afpd, "dir_add(did:%u,'%s/%s'): {cached: %u,'%s'}",
913             ntohl(dir->d_did), cfrombstr(dir->d_fullpath), path->u_name,
914             ntohl(cdir->d_did), cfrombstr(cdir->d_fullpath));
915     }
916
917     return(cdir);
918 }
919
920 /*!
921  * Free the queue with invalid struct dirs
922  *
923  * This gets called at the end of every AFP func.
924  */
925 void dir_free_invalid_q(void)
926 {
927     struct dir *dir;
928     while ((dir = (struct dir *)dequeue(invalid_dircache_entries)))
929         dir_free(dir);
930 }
931
932 /*!
933  * @brief Remove a dir from a cache and queue it for freeing
934  *
935  * 1. Check if the dir is locked or has opened forks
936  * 2. Remove it from the cache
937  * 3. Queue it for removal
938  * 4. If it's a request to remove curdir, mark curdir as invalid
939  * 5. Mark it as invalid
940  *
941  * @param (r) pointer to struct vol
942  * @param (rw) pointer to struct dir
943  */
944 int dir_remove(const struct vol *vol, struct dir *dir)
945 {
946     AFP_ASSERT(vol);
947     AFP_ASSERT(dir);
948
949     if (dir->d_did == DIRDID_ROOT_PARENT || dir->d_did == DIRDID_ROOT)
950         return 0;
951
952     LOG(log_debug, logtype_afpd, "dir_remove(did:%u,'%s'): {removing}",
953         ntohl(dir->d_did), cfrombstr(dir->d_u_name));
954
955     dircache_remove(vol, dir, DIRCACHE | DIDNAME_INDEX | QUEUE_INDEX); /* 2 */
956     enqueue(invalid_dircache_entries, dir); /* 3 */
957
958     if (curdir == dir)                      /* 4 */
959         curdir = NULL;
960
961     dir->d_did = CNID_INVALID;              /* 5 */
962
963     return 0;
964 }
965
966 #if 0 /* unused */
967 /*!
968  * @brief Modify a struct dir, adjust cache
969  *
970  * Any value that is 0 or NULL is not changed. If new_uname is NULL it is set to new_mname.
971  * If given new_uname == new_mname, new_uname will point to new_mname.
972  *
973  * @param vol       (r) pointer to struct vol
974  * @param dir       (rw) pointer to struct dir
975  * @param pdid      (r) new parent DID
976  * @param did       (r) new DID
977  * @param new_mname (r) new mac-name
978  * @param new_uname (r) new unix-name
979  * @param pdir_fullpath (r) new fullpath of parent dir
980  */
981 int dir_modify(const struct vol *vol,
982                struct dir *dir,
983                cnid_t pdid,
984                cnid_t did,
985                const char *new_mname,
986                const char *new_uname,
987                bstring pdir_fullpath)
988 {
989     int ret = 0;
990
991     /* Remove it from the cache */
992     dircache_remove(vol, dir, DIRCACHE | DIDNAME_INDEX | QUEUE_INDEX);
993
994     if (pdid)
995         dir->d_pdid = pdid;
996     if (did)
997         dir->d_did = did;
998
999     if (new_mname) {
1000         /* free uname if it's not the same as mname */
1001         if (dir->d_m_name != dir->d_u_name)
1002             bdestroy(dir->d_u_name);
1003
1004         if (new_uname == NULL)
1005             new_uname = new_mname;
1006
1007         /* assign new name */
1008         if ((bassigncstr(dir->d_m_name, new_mname)) != BSTR_OK) {
1009             LOG(log_error, logtype_afpd, "dir_modify: bassigncstr: %s", strerror(errno) );
1010             return -1;
1011         }
1012
1013         if (new_mname == new_uname || (strcmp(new_mname, new_uname) == 0)) {
1014             dir->d_u_name = dir->d_m_name;
1015         } else {
1016             if ((dir->d_u_name = bfromcstr(new_uname)) == NULL) {
1017                 LOG(log_error, logtype_afpd, "dir_modify: bassigncstr: %s", strerror(errno) );
1018                 return -1;
1019             }
1020         }
1021     }
1022
1023     if (pdir_fullpath) {
1024         if (bassign(dir->d_fullpath, pdir_fullpath) != BSTR_OK)
1025             return -1;
1026         if (bcatcstr(dir->d_fullpath, "/") != BSTR_OK)
1027             return -1;
1028         if (bcatcstr(dir->d_fullpath, new_uname) != BSTR_OK)
1029             return -1;
1030     }
1031
1032     if (dir->d_m_name_ucs2)
1033         free(dir->d_m_name_ucs2);
1034     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))
1035         dir->d_m_name_ucs2 = NULL;
1036
1037     /* Re-add it to the cache */
1038     if ((dircache_add(vol, dir)) != 0) {
1039         dircache_dump();
1040         AFP_PANIC("dir_modify");
1041     }
1042
1043     return ret;
1044 }
1045 #endif
1046
1047 /*!
1048  * @brief Resolve a catalog node name path
1049  *
1050  * 1. Evaluate path type
1051  * 2. Move to start dir, if we cant, it might eg because of EACCES, build
1052  *    path from dirname, so eg getdirparams has sth it can chew on. curdir
1053  *    is dir parent then. All this is done in path_from_dir().
1054  * 3. Parse next cnode name in path, cases:
1055  * 4.   single "\0" -> do nothing
1056  * 5.   two or more consecutive "\0" -> chdir("..") one or more times
1057  * 6.   cnode name -> copy it to path.m_name
1058  * 7. Get unix name from mac name
1059  * 8. Special handling of request with did 1
1060  * 9. stat the cnode name
1061  * 10. If it's not there, it's probably an afp_createfile|dir,
1062  *     return with curdir = dir parent, struct path = dirname
1063  * 11. If it's there and it's a file, it must should be the last element of the requested
1064  *     path. Return with curdir = cnode name parent dir, struct path = filename
1065  * 12. Treat symlinks like files, dont follow them
1066  * 13. If it's a dir:
1067  * 14. Search the dircache for it
1068  * 15. If it's not in the cache, create a struct dir for it and add it to the cache
1069  * 16. chdir into the dir and
1070  * 17. set m_name to the mac equivalent of "."
1071  * 18. goto 3
1072  */
1073 struct path *cname(struct vol *vol, struct dir *dir, char **cpath)
1074 {
1075     static char        path[ MAXPATHLEN + 1];
1076     static struct path ret;
1077
1078     struct dir  *cdir;
1079     char        *data, *p;
1080     int         len;
1081     uint32_t   hint;
1082     uint16_t   len16;
1083     int         size = 0;
1084     int         toUTF8 = 0;
1085
1086     LOG(log_maxdebug, logtype_afpd, "came('%s'): {start}", cfrombstr(dir->d_fullpath));
1087
1088     data = *cpath;
1089     afp_errno = AFPERR_NOOBJ;
1090     memset(&ret, 0, sizeof(ret));
1091
1092     switch (ret.m_type = *data) { /* 1 */
1093     case 2:
1094         data++;
1095         len = (unsigned char) *data++;
1096         size = 2;
1097         if (vol->v_obj->afp_version >= 30) {
1098             ret.m_type = 3;
1099             toUTF8 = 1;
1100         }
1101         break;
1102     case 3:
1103         if (vol->v_obj->afp_version >= 30) {
1104             data++;
1105             memcpy(&hint, data, sizeof(hint));
1106             hint = ntohl(hint);
1107             data += sizeof(hint);
1108
1109             memcpy(&len16, data, sizeof(len16));
1110             len = ntohs(len16);
1111             data += 2;
1112             size = 7;
1113             break;
1114         }
1115         /* else it's an error */
1116     default:
1117         afp_errno = AFPERR_PARAM;
1118         return( NULL );
1119     }
1120     *cpath += len + size;
1121
1122     path[0] = 0;
1123     ret.m_name = path;
1124
1125     if (movecwd(vol, dir) < 0 ) {
1126         LOG(log_debug, logtype_afpd, "cname(did:%u): failed to chdir to '%s'",
1127             ntohl(dir->d_did), cfrombstr(dir->d_fullpath));
1128         if (len == 0)
1129             return path_from_dir(vol, dir, &ret);
1130         else
1131             return NULL;
1132     }
1133
1134     while (len) {         /* 3 */
1135         if (*data == 0) { /* 4 or 5 */
1136             data++;
1137             len--;
1138             while (len > 0 && *data == 0) { /* 5 */
1139                 /* chdir to parrent dir */
1140                 if ((dir = dirlookup(vol, dir->d_pdid)) == NULL)
1141                     return NULL;
1142                 if (movecwd( vol, dir ) < 0 ) {
1143                     dir_remove(vol, dir);
1144                     return NULL;
1145                 }
1146                 data++;
1147                 len--;
1148             }
1149             continue;
1150         }
1151
1152         /* 6*/
1153         for ( p = path; *data != 0 && len > 0; len-- ) {
1154             *p++ = *data++;
1155             if (p > &path[UTF8FILELEN_EARLY]) {   /* FIXME safeguard, limit of early Mac OS X */
1156                 afp_errno = AFPERR_PARAM;
1157                 return NULL;
1158             }
1159         }
1160         *p = 0;            /* Terminate string */
1161         ret.u_name = NULL;
1162
1163         if (cname_mtouname(vol, dir, &ret, toUTF8) != 0) { /* 7 */
1164             LOG(log_error, logtype_afpd, "cname('%s'): error from cname_mtouname", path);
1165             return NULL;
1166         }
1167
1168         LOG(log_maxdebug, logtype_afpd, "came('%s'): {node: '%s}", cfrombstr(dir->d_fullpath), ret.u_name);
1169
1170         /* Prevent access to our special folders like .AppleDouble */
1171         if (check_name(vol, ret.u_name)) {
1172             /* the name is illegal */
1173             LOG(log_info, logtype_afpd, "cname: illegal path: '%s'", ret.u_name);
1174             afp_errno = AFPERR_PARAM;
1175             return NULL;
1176         }
1177
1178         if (dir->d_did == DIRDID_ROOT_PARENT) { /* 8 */
1179             /*
1180              * Special case: CNID 1
1181              * root parent (did 1) has one child: the volume. Requests for did=1 with
1182              * some <name> must check against the volume name.
1183              */
1184             if ((strcmp(cfrombstr(vol->v_root->d_m_name), ret.m_name)) == 0)
1185                 cdir = vol->v_root;
1186             else
1187                 return NULL;
1188         } else {
1189             /*
1190              * CNID != 1, eg. most of the times we take this way.
1191              * Now check if current path-part is a file or dir:
1192              * o if it's dir we have to step into it
1193              * o if it's a file we expect it to be the last part of the requested path
1194              *   and thus call continue which should terminate the while loop because
1195              *   len = 0. Ok?
1196              */
1197             if (of_stat(vol, &ret) != 0) { /* 9 */
1198                 /*
1199                  * ret.u_name doesn't exist, might be afp_createfile|dir
1200                  * that means it should have been the last part
1201                  */
1202                 if (len > 0) {
1203                     /* it wasn't the last part, so we have a bogus path request */
1204                     afp_errno = AFPERR_NOOBJ;
1205                     return NULL;
1206                 }
1207                 /*
1208                  * this will terminate clean in while (1) because len == 0,
1209                  * probably afp_createfile|dir
1210                  */
1211                 LOG(log_maxdebug, logtype_afpd, "came('%s'): {leave-cnode ENOENT (possile create request): '%s'}",
1212                     cfrombstr(dir->d_fullpath), ret.u_name);
1213                 continue; /* 10 */
1214             }
1215
1216             switch (ret.st.st_mode & S_IFMT) {
1217             case S_IFREG: /* 11 */
1218                 LOG(log_debug, logtype_afpd, "came('%s'): {file: '%s'}",
1219                     cfrombstr(dir->d_fullpath), ret.u_name);
1220                 if (len > 0) {
1221                     /* it wasn't the last part, so we have a bogus path request */
1222                     afp_errno = AFPERR_PARAM;
1223                     return NULL;
1224                 }
1225                 continue; /* continues while loop */
1226             case S_IFLNK: /* 12 */
1227                 LOG(log_debug, logtype_afpd, "came('%s'): {link: '%s'}",
1228                     cfrombstr(dir->d_fullpath), ret.u_name);
1229                 if (len > 0) {
1230                     LOG(log_warning, logtype_afpd, "came('%s'): {symlinked dir: '%s'}",
1231                         cfrombstr(dir->d_fullpath), ret.u_name);
1232                     afp_errno = AFPERR_PARAM;
1233                     return NULL;
1234                 }
1235                 continue; /* continues while loop */
1236             case S_IFDIR: /* 13 */
1237                 break;
1238             default:
1239                 LOG(log_info, logtype_afpd, "cname: special file: '%s'", ret.u_name);
1240                 afp_errno = AFPERR_NODIR;
1241                 return NULL;
1242             }
1243
1244             /* Search the cache */
1245             int unamelen = strlen(ret.u_name);
1246             cdir = dircache_search_by_name(vol, dir, ret.u_name, unamelen); /* 14 */
1247             if (cdir == NULL) {
1248                 /* Not in cache, create one */
1249                 if ((cdir = dir_add(vol, dir, &ret, unamelen)) == NULL) { /* 15 */
1250                     LOG(log_error, logtype_afpd, "cname(did:%u, name:'%s', cwd:'%s'): failed to add dir",
1251                         ntohl(dir->d_did), ret.u_name, getcwdpath());
1252                     return NULL;
1253                 }
1254             }
1255         } /* if/else cnid==1 */
1256
1257         /* Now chdir to the evaluated dir */
1258         if (movecwd( vol, cdir ) < 0 ) { /* 16 */
1259             LOG(log_debug, logtype_afpd, "cname(cwd:'%s'): failed to chdir to new subdir '%s': %s",
1260                 cfrombstr(curdir->d_fullpath), cfrombstr(cdir->d_fullpath), strerror(errno));
1261             if (len == 0)
1262                 return path_from_dir(vol, cdir, &ret);
1263             else
1264                 return NULL;
1265         }
1266         dir = cdir;
1267         ret.m_name[0] = 0;      /* 17, so we later know last token was a dir */
1268     } /* while (len) */
1269
1270     if (curdir->d_did == DIRDID_ROOT_PARENT) {
1271         afp_errno = AFPERR_DID1;
1272         return NULL;
1273     }
1274
1275     if (ret.m_name[0] == 0) {
1276         /* Last part was a dir */
1277         ret.u_name = mtoupath(vol, ret.m_name, 0, 1); /* Force "." into a useable static buffer */
1278         ret.d_dir = dir;
1279     }
1280
1281     LOG(log_debug, logtype_afpd, "came('%s') {end: curdir:'%s', path:'%s'}",
1282         cfrombstr(dir->d_fullpath),
1283         cfrombstr(curdir->d_fullpath),
1284         ret.u_name);
1285
1286     return &ret;
1287 }
1288
1289 /*
1290  * @brief chdir() to dir
1291  *
1292  * @param vol   (r) pointer to struct vol
1293  * @param dir   (r) pointer to struct dir
1294  *
1295  * @returns 0 on success, -1 on error with afp_errno set appropiately
1296  */
1297 int movecwd(const struct vol *vol, struct dir *dir)
1298 {
1299     int ret;
1300
1301     AFP_ASSERT(vol);
1302     AFP_ASSERT(dir);
1303
1304     LOG(log_maxdebug, logtype_afpd, "movecwd: from: curdir:\"%s\", cwd:\"%s\"",
1305         curdir ? cfrombstr(curdir->d_fullpath) : "INVALID", getcwdpath());
1306
1307     if (dir->d_did == DIRDID_ROOT_PARENT) {
1308         curdir = &rootParent;
1309         return 0;
1310     }
1311
1312     LOG(log_debug, logtype_afpd, "movecwd(to: did: %u, \"%s\")",
1313         ntohl(dir->d_did), cfrombstr(dir->d_fullpath));
1314
1315     if ((ret = ochdir(cfrombstr(dir->d_fullpath), vol_syml_opt(vol))) != 0 ) {
1316         LOG(log_debug, logtype_afpd, "movecwd(\"%s\"): %s",
1317             cfrombstr(dir->d_fullpath), strerror(errno));
1318         if (ret == 1) {
1319             /* p is a symlink or getcwd failed */
1320             afp_errno = AFPERR_BADTYPE;
1321
1322             if (chdir(vol->v_path ) < 0) {
1323                 LOG(log_error, logtype_afpd, "can't chdir back'%s': %s", vol->v_path, strerror(errno));
1324                 /* XXX what do we do here? */
1325             }
1326             curdir = vol->v_root;
1327             return -1;
1328         }
1329
1330         switch (errno) {
1331         case EACCES:
1332         case EPERM:
1333             afp_errno = AFPERR_ACCESS;
1334             break;
1335         default:
1336             afp_errno = AFPERR_NOOBJ;
1337         }
1338         return( -1 );
1339     }
1340
1341     curdir = dir;
1342     return( 0 );
1343 }
1344
1345 /*
1346  * We can't use unix file's perm to support Apple's inherited protection modes.
1347  * If we aren't the file's owner we can't change its perms when moving it and smb
1348  * nfs,... don't even try.
1349  */
1350 int check_access(const AFPObj *obj, struct vol *vol, char *path, int mode)
1351 {
1352     struct maccess ma;
1353     char *p;
1354
1355     p = ad_dir(path);
1356     if (!p)
1357         return -1;
1358
1359     accessmode(obj, vol, p, &ma, curdir, NULL);
1360     if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1361         return -1;
1362     if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1363         return -1;
1364
1365     return 0;
1366 }
1367
1368 /* --------------------- */
1369 int file_access(const AFPObj *obj, struct vol *vol, struct path *path, int mode)
1370 {
1371     struct maccess ma;
1372
1373     accessmode(obj, vol, path->u_name, &ma, curdir, &path->st);
1374
1375     LOG(log_debug, logtype_afpd, "file_access(\"%s\"): mapped user mode: 0x%02x",
1376         path->u_name, ma.ma_user);
1377
1378     if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE)) {
1379         LOG(log_debug, logtype_afpd, "file_access(\"%s\"): write access denied", path->u_name);
1380         return -1;
1381     }
1382     if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD)) {
1383         LOG(log_debug, logtype_afpd, "file_access(\"%s\"): read access denied", path->u_name);
1384         return -1;
1385     }
1386     return 0;
1387
1388 }
1389
1390 /* --------------------- */
1391 void setdiroffcnt(struct dir *dir, struct stat *st,  uint32_t count)
1392 {
1393     dir->d_offcnt = count;
1394     dir->d_ctime = st->st_ctime;
1395     dir->d_flags &= ~DIRF_CNID;
1396 }
1397
1398
1399 /* ---------------------
1400  * is our cached also for reenumerate id?
1401  */
1402 int dirreenumerate(struct dir *dir, struct stat *st)
1403 {
1404     return st->st_ctime == dir->d_ctime && (dir->d_flags & DIRF_CNID);
1405 }
1406
1407 /* ------------------------------
1408    (".", curdir)
1409    (name, dir) with curdir:name == dir, from afp_enumerate
1410 */
1411
1412 int getdirparams(const AFPObj *obj,
1413                  const struct vol *vol,
1414                  uint16_t bitmap, struct path *s_path,
1415                  struct dir *dir,
1416                  char *buf, size_t *buflen )
1417 {
1418     struct maccess  ma;
1419     struct adouble  ad;
1420     char        *data, *l_nameoff = NULL, *utf_nameoff = NULL;
1421     int         bit = 0, isad = 0;
1422     uint32_t           aint;
1423     uint16_t       ashort;
1424     int                 ret;
1425     uint32_t           utf8 = 0;
1426     cnid_t              pdid;
1427     struct stat *st = &s_path->st;
1428     char *upath = s_path->u_name;
1429
1430     if ((bitmap & ((1 << DIRPBIT_ATTR)  |
1431                    (1 << DIRPBIT_CDATE) |
1432                    (1 << DIRPBIT_MDATE) |
1433                    (1 << DIRPBIT_BDATE) |
1434                    (1 << DIRPBIT_FINFO)))) {
1435
1436         ad_init(&ad, vol);
1437         if ( !ad_metadata( upath, ADFLAGS_DIR, &ad) ) {
1438             isad = 1;
1439             if (ad.ad_mdp->adf_flags & O_CREAT) {
1440                 /* We just created it */
1441                 if (s_path->m_name == NULL) {
1442                     if ((s_path->m_name = utompath(vol,
1443                                                    upath,
1444                                                    dir->d_did,
1445                                                    utf8_encoding(obj))) == NULL) {
1446                         LOG(log_error, logtype_afpd,
1447                             "getdirparams(\"%s\"): can't assign macname",
1448                             cfrombstr(dir->d_fullpath));
1449                         return AFPERR_MISC;
1450                     }
1451                 }
1452                 ad_setname(&ad, s_path->m_name);
1453                 ad_setid( &ad,
1454                           s_path->st.st_dev,
1455                           s_path->st.st_ino,
1456                           dir->d_did,
1457                           dir->d_pdid,
1458                           vol->v_stamp);
1459                 ad_flush( &ad);
1460             }
1461         }
1462     }
1463
1464     pdid = dir->d_pdid;
1465
1466     data = buf;
1467     while ( bitmap != 0 ) {
1468         while (( bitmap & 1 ) == 0 ) {
1469             bitmap = bitmap>>1;
1470             bit++;
1471         }
1472
1473         switch ( bit ) {
1474         case DIRPBIT_ATTR :
1475             if ( isad ) {
1476                 ad_getattr(&ad, &ashort);
1477             } else if (invisible_dots(vol, cfrombstr(dir->d_u_name))) {
1478                 ashort = htons(ATTRBIT_INVISIBLE);
1479             } else
1480                 ashort = 0;
1481             ashort &= ~htons(vol->v_ignattr);
1482             memcpy( data, &ashort, sizeof( ashort ));
1483             data += sizeof( ashort );
1484             break;
1485
1486         case DIRPBIT_PDID :
1487             memcpy( data, &pdid, sizeof( pdid ));
1488             data += sizeof( pdid );
1489             LOG(log_debug, logtype_afpd, "metadata('%s'):     Parent DID: %u",
1490                 s_path->u_name, ntohl(pdid));
1491             break;
1492
1493         case DIRPBIT_CDATE :
1494             if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
1495                 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1496             memcpy( data, &aint, sizeof( aint ));
1497             data += sizeof( aint );
1498             break;
1499
1500         case DIRPBIT_MDATE :
1501             aint = AD_DATE_FROM_UNIX(st->st_mtime);
1502             memcpy( data, &aint, sizeof( aint ));
1503             data += sizeof( aint );
1504             break;
1505
1506         case DIRPBIT_BDATE :
1507             if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
1508                 aint = AD_DATE_START;
1509             memcpy( data, &aint, sizeof( aint ));
1510             data += sizeof( aint );
1511             break;
1512
1513         case DIRPBIT_FINFO :
1514             if ( isad ) {
1515                 memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
1516             } else { /* no appledouble */
1517                 memset( data, 0, 32 );
1518                 /* dot files are by default visible */
1519                 if (invisible_dots(vol, cfrombstr(dir->d_u_name))) {
1520                     ashort = htons(FINDERINFO_INVISIBLE);
1521                     memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
1522                 }
1523             }
1524             data += 32;
1525             break;
1526
1527         case DIRPBIT_LNAME :
1528             if (dir->d_m_name) /* root of parent can have a null name */
1529                 l_nameoff = data;
1530             else
1531                 memset(data, 0, sizeof(uint16_t));
1532             data += sizeof( uint16_t );
1533             break;
1534
1535         case DIRPBIT_SNAME :
1536             memset(data, 0, sizeof(uint16_t));
1537             data += sizeof( uint16_t );
1538             break;
1539
1540         case DIRPBIT_DID :
1541             memcpy( data, &dir->d_did, sizeof( aint ));
1542             data += sizeof( aint );
1543             LOG(log_debug, logtype_afpd, "metadata('%s'):            DID: %u",
1544                 s_path->u_name, ntohl(dir->d_did));
1545             break;
1546
1547         case DIRPBIT_OFFCNT :
1548             ashort = 0;
1549             /* this needs to handle current directory access rights */
1550             if (diroffcnt(dir, st)) {
1551                 ashort = (dir->d_offcnt > 0xffff)?0xffff:dir->d_offcnt;
1552             }
1553             else if ((ret = for_each_dirent(vol, upath, NULL,NULL)) >= 0) {
1554                 setdiroffcnt(dir, st,  ret);
1555                 ashort = (dir->d_offcnt > 0xffff)?0xffff:dir->d_offcnt;
1556             }
1557             ashort = htons( ashort );
1558             memcpy( data, &ashort, sizeof( ashort ));
1559             data += sizeof( ashort );
1560             break;
1561
1562         case DIRPBIT_UID :
1563             aint = htonl(st->st_uid);
1564             memcpy( data, &aint, sizeof( aint ));
1565             data += sizeof( aint );
1566             break;
1567
1568         case DIRPBIT_GID :
1569             aint = htonl(st->st_gid);
1570             memcpy( data, &aint, sizeof( aint ));
1571             data += sizeof( aint );
1572             break;
1573
1574         case DIRPBIT_ACCESS :
1575             accessmode(obj, vol, upath, &ma, dir , st);
1576
1577             *data++ = ma.ma_user;
1578             *data++ = ma.ma_world;
1579             *data++ = ma.ma_group;
1580             *data++ = ma.ma_owner;
1581             break;
1582
1583             /* Client has requested the ProDOS information block.
1584                Just pass back the same basic block for all
1585                directories. <shirsch@ibm.net> */
1586         case DIRPBIT_PDINFO :
1587             if (obj->afp_version >= 30) { /* UTF8 name */
1588                 utf8 = kTextEncodingUTF8;
1589                 if (dir->d_m_name) /* root of parent can have a null name */
1590                     utf_nameoff = data;
1591                 else
1592                     memset(data, 0, sizeof(uint16_t));
1593                 data += sizeof( uint16_t );
1594                 aint = 0;
1595                 memcpy(data, &aint, sizeof( aint ));
1596                 data += sizeof( aint );
1597             }
1598             else { /* ProDOS Info Block */
1599                 *data++ = 0x0f;
1600                 *data++ = 0;
1601                 ashort = htons( 0x0200 );
1602                 memcpy( data, &ashort, sizeof( ashort ));
1603                 data += sizeof( ashort );
1604                 memset( data, 0, sizeof( ashort ));
1605                 data += sizeof( ashort );
1606             }
1607             break;
1608
1609         case DIRPBIT_UNIXPR :
1610             /* accessmode may change st_mode with ACLs */
1611             accessmode(obj, vol, upath, &ma, dir, st);
1612
1613             aint = htonl(st->st_uid);
1614             memcpy( data, &aint, sizeof( aint ));
1615             data += sizeof( aint );
1616             aint = htonl(st->st_gid);
1617             memcpy( data, &aint, sizeof( aint ));
1618             data += sizeof( aint );
1619
1620             aint = st->st_mode;
1621             aint = htonl ( aint & ~S_ISGID );  /* Remove SGID, OSX doesn't like it ... */
1622             memcpy( data, &aint, sizeof( aint ));
1623             data += sizeof( aint );
1624
1625             *data++ = ma.ma_user;
1626             *data++ = ma.ma_world;
1627             *data++ = ma.ma_group;
1628             *data++ = ma.ma_owner;
1629             break;
1630
1631         default :
1632             if ( isad ) {
1633                 ad_close(&ad, ADFLAGS_HF);
1634             }
1635             return( AFPERR_BITMAP );
1636         }
1637         bitmap = bitmap>>1;
1638         bit++;
1639     }
1640     if ( l_nameoff ) {
1641         ashort = htons( data - buf );
1642         memcpy( l_nameoff, &ashort, sizeof( ashort ));
1643         data = set_name(vol, data, pdid, cfrombstr(dir->d_m_name), dir->d_did, 0);
1644     }
1645     if ( utf_nameoff ) {
1646         ashort = htons( data - buf );
1647         memcpy( utf_nameoff, &ashort, sizeof( ashort ));
1648         data = set_name(vol, data, pdid, cfrombstr(dir->d_m_name), dir->d_did, utf8);
1649     }
1650     if ( isad ) {
1651         ad_close(&ad, ADFLAGS_HF);
1652     }
1653     *buflen = data - buf;
1654     return( AFP_OK );
1655 }
1656
1657 /* ----------------------------- */
1658 int path_error(struct path *path, int error)
1659 {
1660 /* - a dir with access error
1661  * - no error it's a file
1662  * - file not found
1663  */
1664     if (path_isadir(path))
1665         return afp_errno;
1666     if (path->st_valid && path->st_errno)
1667         return error;
1668     return AFPERR_BADTYPE ;
1669 }
1670
1671 /* ----------------------------- */
1672 int afp_setdirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1673 {
1674     struct vol  *vol;
1675     struct dir  *dir;
1676     struct path *path;
1677     uint16_t   vid, bitmap;
1678     uint32_t   did;
1679     int     rc;
1680
1681     *rbuflen = 0;
1682     ibuf += 2;
1683     memcpy( &vid, ibuf, sizeof( vid ));
1684     ibuf += sizeof( vid );
1685
1686     if (NULL == ( vol = getvolbyvid( vid )) ) {
1687         return( AFPERR_PARAM );
1688     }
1689
1690     if (vol->v_flags & AFPVOL_RO)
1691         return AFPERR_VLOCK;
1692
1693     memcpy( &did, ibuf, sizeof( did ));
1694     ibuf += sizeof( int );
1695
1696     if (NULL == ( dir = dirlookup( vol, did )) ) {
1697         return afp_errno;
1698     }
1699
1700     memcpy( &bitmap, ibuf, sizeof( bitmap ));
1701     bitmap = ntohs( bitmap );
1702     ibuf += sizeof( bitmap );
1703
1704     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1705         return get_afp_errno(AFPERR_NOOBJ);
1706     }
1707
1708     if ( *path->m_name != '\0' ) {
1709         rc = path_error(path, AFPERR_NOOBJ);
1710         /* maybe we are trying to set perms back */
1711         if (rc != AFPERR_ACCESS)
1712             return rc;
1713     }
1714
1715     /*
1716      * If ibuf is odd, make it even.
1717      */
1718     if ((u_long)ibuf & 1 ) {
1719         ibuf++;
1720     }
1721
1722     if (AFP_OK == ( rc = setdirparams(vol, path, bitmap, ibuf )) ) {
1723         setvoltime(obj, vol );
1724     }
1725     return( rc );
1726 }
1727
1728 /*
1729  * assume path == '\0' eg. it's a directory in canonical form
1730  */
1731 int setdirparams(struct vol *vol, struct path *path, uint16_t d_bitmap, char *buf )
1732 {
1733     struct maccess  ma;
1734     struct adouble  ad;
1735     struct utimbuf      ut;
1736     struct timeval      tv;
1737
1738     char                *upath;
1739     struct dir          *dir;
1740     int         bit, isad = 0;
1741     int                 cdate, bdate;
1742     int                 owner, group;
1743     uint16_t       ashort, bshort, oshort;
1744     int                 err = AFP_OK;
1745     int                 change_mdate = 0;
1746     int                 change_parent_mdate = 0;
1747     int                 newdate = 0;
1748     uint16_t           bitmap = d_bitmap;
1749     u_char              finder_buf[32];
1750     uint32_t       upriv;
1751     mode_t              mpriv = 0;
1752     bool                set_upriv = false, set_maccess = false;
1753
1754     LOG(log_debug, logtype_afpd, "setdirparams(\"%s\", bitmap: %02x)", path->u_name, bitmap);
1755
1756     bit = 0;
1757     upath = path->u_name;
1758     dir   = path->d_dir;
1759     while ( bitmap != 0 ) {
1760         while (( bitmap & 1 ) == 0 ) {
1761             bitmap = bitmap>>1;
1762             bit++;
1763         }
1764
1765         switch( bit ) {
1766         case DIRPBIT_ATTR :
1767             change_mdate = 1;
1768             memcpy( &ashort, buf, sizeof( ashort ));
1769             buf += sizeof( ashort );
1770             break;
1771         case DIRPBIT_CDATE :
1772             change_mdate = 1;
1773             memcpy(&cdate, buf, sizeof(cdate));
1774             buf += sizeof( cdate );
1775             break;
1776         case DIRPBIT_MDATE :
1777             memcpy(&newdate, buf, sizeof(newdate));
1778             buf += sizeof( newdate );
1779             break;
1780         case DIRPBIT_BDATE :
1781             change_mdate = 1;
1782             memcpy(&bdate, buf, sizeof(bdate));
1783             buf += sizeof( bdate );
1784             break;
1785         case DIRPBIT_FINFO :
1786             change_mdate = 1;
1787             memcpy( finder_buf, buf, 32 );
1788             buf += 32;
1789             break;
1790         case DIRPBIT_UID :  /* What kind of loser mounts as root? */
1791             memcpy( &owner, buf, sizeof(owner));
1792             buf += sizeof( owner );
1793             break;
1794         case DIRPBIT_GID :
1795             memcpy( &group, buf, sizeof( group ));
1796             buf += sizeof( group );
1797             break;
1798         case DIRPBIT_ACCESS :
1799             set_maccess = true;
1800             change_mdate = 1;
1801             ma.ma_user = *buf++;
1802             ma.ma_world = *buf++;
1803             ma.ma_group = *buf++;
1804             ma.ma_owner = *buf++;
1805             mpriv = mtoumode( &ma ) | vol->v_dperm;
1806             break;
1807             /* Ignore what the client thinks we should do to the
1808                ProDOS information block.  Skip over the data and
1809                report nothing amiss. <shirsch@ibm.net> */
1810         case DIRPBIT_PDINFO :
1811             if (vol->v_obj->afp_version < 30) {
1812                 buf += 6;
1813             }
1814             else {
1815                 err = AFPERR_BITMAP;
1816                 bitmap = 0;
1817             }
1818             break;
1819         case DIRPBIT_UNIXPR :
1820             if (vol_unix_priv(vol)) {
1821                 set_upriv = true;
1822                 memcpy( &owner, buf, sizeof(owner)); /* FIXME need to change owner too? */
1823                 buf += sizeof( owner );
1824                 memcpy( &group, buf, sizeof( group ));
1825                 buf += sizeof( group );
1826
1827                 change_mdate = 1;
1828                 memcpy( &upriv, buf, sizeof( upriv ));
1829                 buf += sizeof( upriv );
1830                 upriv = ntohl (upriv) | vol->v_dperm;
1831                 break;
1832             }
1833             /* fall through */
1834         default :
1835             err = AFPERR_BITMAP;
1836             bitmap = 0;
1837             break;
1838         }
1839
1840         bitmap = bitmap>>1;
1841         bit++;
1842     }
1843
1844     if (d_bitmap & ((1<<DIRPBIT_ATTR) | (1<<DIRPBIT_CDATE) | (1<<DIRPBIT_BDATE) | (1<<DIRPBIT_FINFO))) {
1845         ad_init(&ad, vol);
1846         if (ad_open(&ad, upath, ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_CREATE | ADFLAGS_RDWR, 0777) != 0) {
1847             LOG(log_debug, logtype_afpd, "setdirparams(\"%s\", bitmap: %02x): need adouble", path->u_name, d_bitmap);
1848             return AFPERR_ACCESS;
1849         }
1850         if ((ad_get_MD_flags(&ad) & O_CREAT)) {
1851             ad_setname(&ad, cfrombstr(curdir->d_m_name));
1852         }
1853         isad = 1;
1854     }
1855
1856     bit = 0;
1857     bitmap = d_bitmap;
1858     while ( bitmap != 0 ) {
1859         while (( bitmap & 1 ) == 0 ) {
1860             bitmap = bitmap>>1;
1861             bit++;
1862         }
1863
1864         switch( bit ) {
1865         case DIRPBIT_ATTR :
1866             if (isad) {
1867                 ad_getattr(&ad, &bshort);
1868                 oshort = bshort;
1869                 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1870                     ashort &= ~htons(vol->v_ignattr);
1871                     bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1872                 } else {
1873                     bshort &= ~ashort;
1874                 }
1875                 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1876                     change_parent_mdate = 1;
1877                 ad_setattr(&ad, bshort);
1878             }
1879             break;
1880         case DIRPBIT_CDATE :
1881             if (isad) {
1882                 ad_setdate(&ad, AD_DATE_CREATE, cdate);
1883             }
1884             break;
1885         case DIRPBIT_MDATE :
1886             break;
1887         case DIRPBIT_BDATE :
1888             if (isad) {
1889                 ad_setdate(&ad, AD_DATE_BACKUP, bdate);
1890             }
1891             break;
1892         case DIRPBIT_FINFO :
1893             if (isad) {
1894                 /* Fixes #2802236 */
1895                 uint16_t fflags;
1896                 memcpy(&fflags, finder_buf + FINDERINFO_FRFLAGOFF, sizeof(uint16_t));
1897                 fflags &= htons(~FINDERINFO_ISHARED);
1898                 memcpy(finder_buf + FINDERINFO_FRFLAGOFF, &fflags, sizeof(uint16_t));
1899                 /* #2802236 end */
1900
1901                 if (  dir->d_did == DIRDID_ROOT ) {
1902                     /*
1903                      * Alright, we admit it, this is *really* sick!
1904                      * The 4 bytes that we don't copy, when we're dealing
1905                      * with the root of a volume, are the directory's
1906                      * location information. This eliminates that annoying
1907                      * behavior one sees when mounting above another mount
1908                      * point.
1909                      */
1910                     memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 10 );
1911                     memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, finder_buf + 14, 18 );
1912                 } else {
1913                     memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 32 );
1914                 }
1915             }
1916             break;
1917         case DIRPBIT_UID :  /* What kind of loser mounts as root? */
1918             if ( (dir->d_did == DIRDID_ROOT) &&
1919                  (setdeskowner(vol, ntohl(owner), -1 ) < 0)) {
1920                 err = set_dir_errors(path, "setdeskowner", errno);
1921                 if (isad && err == AFPERR_PARAM) {
1922                     err = AFP_OK; /* ???*/
1923                 }
1924                 else {
1925                     goto setdirparam_done;
1926                 }
1927             }
1928             if ( setdirowner(vol, upath, ntohl(owner), -1 ) < 0 ) {
1929                 err = set_dir_errors(path, "setdirowner", errno);
1930                 goto setdirparam_done;
1931             }
1932             break;
1933         case DIRPBIT_GID :
1934             if (dir->d_did == DIRDID_ROOT)
1935                 setdeskowner(vol, -1, ntohl(group) );
1936             if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
1937                 err = set_dir_errors(path, "setdirowner", errno);
1938                 goto setdirparam_done;
1939             }
1940             break;
1941         case DIRPBIT_ACCESS :
1942             break;
1943         case DIRPBIT_PDINFO :
1944             if (vol->v_obj->afp_version >= 30) {
1945                 err = AFPERR_BITMAP;
1946                 goto setdirparam_done;
1947             }
1948             break;
1949         case DIRPBIT_UNIXPR :
1950             if (!vol_unix_priv(vol)) {
1951                 err = AFPERR_BITMAP;
1952                 goto setdirparam_done;
1953             }
1954             break;
1955         default :
1956             err = AFPERR_BITMAP;
1957             goto setdirparam_done;
1958             break;
1959         }
1960
1961         bitmap = bitmap>>1;
1962         bit++;
1963     }
1964
1965 setdirparam_done:
1966     if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1967         newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1968     }
1969     if (newdate) {
1970         if (isad)
1971             ad_setdate(&ad, AD_DATE_MODIFY, newdate);
1972         ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1973         utime(upath, &ut);
1974     }
1975
1976     if (isad) {
1977         if (path->st_valid && !path->st_errno) {
1978             struct stat *st = &path->st;
1979             if (dir && dir->d_pdid) {
1980                 ad_setid(&ad, st->st_dev, st->st_ino,  dir->d_did, dir->d_pdid, vol->v_stamp);
1981             }
1982         }
1983         if (ad_flush(&ad) != 0) {
1984             switch (errno) {
1985             case EACCES:
1986                 err = AFPERR_ACCESS;
1987                 break;
1988             default:
1989                 err = AFPERR_MISC;
1990                 break;
1991            }
1992         }
1993         ad_close(&ad, ADFLAGS_HF);
1994     }
1995
1996     if (err == AFP_OK) {
1997         if (set_maccess == true) {
1998             if (dir->d_did == DIRDID_ROOT) {
1999                 setdeskmode(vol, mpriv);
2000                 if (!dir_rx_set(mpriv)) {
2001                     /* we can't remove read and search for owner on volume root */
2002                     err = AFPERR_ACCESS;
2003                     goto setprivdone;
2004                 }
2005             }
2006             if (setdirunixmode(vol, upath, mpriv) < 0) {
2007                 LOG(log_info, logtype_afpd, "setdirparams(\"%s\"): setdirunixmode: %s",
2008                     fullpathname(upath), strerror(errno));
2009                 err = set_dir_errors(path, "setdirmode", errno);
2010             }
2011         }
2012         if ((set_upriv == true) && vol_unix_priv(vol)) {
2013             if (dir->d_did == DIRDID_ROOT) {
2014                 if (!dir_rx_set(upriv)) {
2015                     /* we can't remove read and search for owner on volume root */
2016                     err = AFPERR_ACCESS;
2017                     goto setprivdone;
2018                 }
2019                 setdeskowner(vol, -1, ntohl(group));
2020                 setdeskmode(vol, upriv);
2021             }
2022
2023             if (setdirowner(vol, upath, -1, ntohl(group)) < 0) {
2024                 LOG(log_info, logtype_afpd, "setdirparams(\"%s\"): setdirowner: %s",
2025                     fullpathname(upath), strerror(errno));
2026                 err = set_dir_errors(path, "setdirowner", errno);
2027                 goto setprivdone;
2028             }
2029
2030             if (setdirunixmode(vol, upath, upriv) < 0) {
2031                 LOG(log_info, logtype_afpd, "setdirparams(\"%s\"): setdirunixmode: %s",
2032                     fullpathname(upath), strerror(errno));
2033                 err = set_dir_errors(path, "setdirunixmode", errno);
2034             }
2035         }
2036     }
2037
2038 setprivdone:
2039     if (change_parent_mdate && dir->d_did != DIRDID_ROOT
2040         && gettimeofday(&tv, NULL) == 0) {
2041         if (movecwd(vol, dirlookup(vol, dir->d_pdid)) == 0) {
2042             newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2043             /* be careful with bitmap because now dir is null */
2044             bitmap = 1<<DIRPBIT_MDATE;
2045             setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
2046             /* should we reset curdir ?*/
2047         }
2048     }
2049     return err;
2050 }
2051
2052 int afp_syncdir(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2053 {
2054 #ifdef HAVE_DIRFD
2055     DIR                  *dp;
2056 #endif
2057     int                  dfd;
2058     struct vol           *vol;
2059     struct dir           *dir;
2060     uint32_t            did;
2061     uint16_t            vid;
2062
2063     *rbuflen = 0;
2064     ibuf += 2;
2065
2066     memcpy( &vid, ibuf, sizeof( vid ));
2067     ibuf += sizeof( vid );
2068     if (NULL == (vol = getvolbyvid( vid )) ) {
2069         return( AFPERR_PARAM );
2070     }
2071
2072     memcpy( &did, ibuf, sizeof( did ));
2073     ibuf += sizeof( did );
2074
2075     /*
2076      * Here's the deal:
2077      * if it's CNID 2 our only choice to meet the specs is call sync.
2078      * For any other CNID just sync that dir. To my knowledge the
2079      * intended use of FPSyncDir is to sync the volume so all we're
2080      * ever going to see here is probably CNID 2. Anyway, we' prepared.
2081      */
2082
2083     if ( ntohl(did) == 2 ) {
2084         sync();
2085     } else {
2086         if (NULL == ( dir = dirlookup( vol, did )) ) {
2087             return afp_errno; /* was AFPERR_NOOBJ */
2088         }
2089
2090         if (movecwd( vol, dir ) < 0 )
2091             return ( AFPERR_NOOBJ );
2092
2093         /*
2094          * Assuming only OSens that have dirfd also may require fsyncing directories
2095          * in order to flush metadata e.g. Linux.
2096          */
2097
2098 #ifdef HAVE_DIRFD
2099         if (NULL == ( dp = opendir( "." )) ) {
2100             switch( errno ) {
2101             case ENOENT :
2102                 return( AFPERR_NOOBJ );
2103             case EACCES :
2104                 return( AFPERR_ACCESS );
2105             default :
2106                 return( AFPERR_PARAM );
2107             }
2108         }
2109
2110         LOG(log_debug, logtype_afpd, "afp_syncdir: dir: '%s'", dir->d_u_name);
2111
2112         dfd = dirfd( dp );
2113         if ( fsync ( dfd ) < 0 )
2114             LOG(log_error, logtype_afpd, "afp_syncdir(%s):  %s",
2115                 dir->d_u_name, strerror(errno) );
2116         closedir(dp); /* closes dfd too */
2117 #endif
2118
2119         if ( -1 == (dfd = open(vol->ad_path(".", ADFLAGS_DIR), O_RDWR))) {
2120             switch( errno ) {
2121             case ENOENT:
2122                 return( AFPERR_NOOBJ );
2123             case EACCES:
2124                 return( AFPERR_ACCESS );
2125             default:
2126                 return( AFPERR_PARAM );
2127             }
2128         }
2129
2130         LOG(log_debug, logtype_afpd, "afp_syncdir: ad-file: '%s'",
2131             vol->ad_path(".", ADFLAGS_DIR) );
2132
2133         if ( fsync(dfd) < 0 )
2134             LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2135                 vol->ad_path(cfrombstr(dir->d_u_name), ADFLAGS_DIR), strerror(errno) );
2136         close(dfd);
2137     }
2138
2139     return ( AFP_OK );
2140 }
2141
2142 int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2143 {
2144     struct adouble  ad;
2145     struct vol      *vol;
2146     struct dir      *dir;
2147     char        *upath;
2148     struct path         *s_path;
2149     uint32_t       did;
2150     uint16_t       vid;
2151     int                 err;
2152
2153     *rbuflen = 0;
2154     ibuf += 2;
2155
2156     memcpy( &vid, ibuf, sizeof( vid ));
2157     ibuf += sizeof( vid );
2158     if (NULL == ( vol = getvolbyvid( vid )) ) {
2159         return( AFPERR_PARAM );
2160     }
2161
2162     if (vol->v_flags & AFPVOL_RO)
2163         return AFPERR_VLOCK;
2164
2165     memcpy( &did, ibuf, sizeof( did ));
2166     ibuf += sizeof( did );
2167     if (NULL == ( dir = dirlookup( vol, did )) ) {
2168         return afp_errno; /* was AFPERR_NOOBJ */
2169     }
2170     /* for concurrent access we need to be sure we are not in the
2171      * folder we want to create...
2172      */
2173     movecwd(vol, dir);
2174
2175     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
2176         return get_afp_errno(AFPERR_PARAM);
2177     }
2178     /* cname was able to move curdir to it! */
2179     if (*s_path->m_name == '\0')
2180         return AFPERR_EXIST;
2181
2182     upath = s_path->u_name;
2183
2184     if (AFP_OK != (err = netatalk_mkdir(vol, upath))) {
2185         return err;
2186     }
2187
2188     if (of_stat(vol, s_path) < 0) {
2189         return AFPERR_MISC;
2190     }
2191
2192     curdir->d_offcnt++;
2193
2194     if ((dir = dir_add(vol, curdir, s_path, strlen(s_path->u_name))) == NULL) {
2195         return AFPERR_MISC;
2196     }
2197
2198     if ( movecwd( vol, dir ) < 0 ) {
2199         return( AFPERR_PARAM );
2200     }
2201
2202     ad_init(&ad, vol);
2203     if (ad_open(&ad, ".", ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_CREATE | ADFLAGS_RDWR, 0777) < 0)  {
2204         return( AFPERR_ACCESS );
2205     }
2206     ad_setname(&ad, s_path->m_name);
2207     ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
2208
2209     fce_register(FCE_DIR_CREATE, bdata(curdir->d_fullpath), NULL, fce_dir);
2210
2211     ad_flush(&ad);
2212     ad_close(&ad, ADFLAGS_HF);
2213
2214     memcpy( rbuf, &dir->d_did, sizeof( uint32_t ));
2215     *rbuflen = sizeof( uint32_t );
2216     setvoltime(obj, vol );
2217     return( AFP_OK );
2218 }
2219
2220 /*
2221  * dst       new unix filename (not a pathname)
2222  * newname   new mac name
2223  * newparent curdir
2224  * dirfd     -1 means ignore dirfd (or use AT_FDCWD), otherwise src is relative to dirfd
2225  */
2226 int renamedir(struct vol *vol,
2227               int dirfd,
2228               char *src,
2229               char *dst,
2230               struct dir *dir,
2231               struct dir *newparent,
2232               char *newname)
2233 {
2234     struct adouble  ad;
2235     int             err;
2236
2237     /* existence check moved to afp_moveandrename */
2238     if ( unix_rename(dirfd, src, -1, dst ) < 0 ) {
2239         switch ( errno ) {
2240         case ENOENT :
2241             return( AFPERR_NOOBJ );
2242         case EACCES :
2243             return( AFPERR_ACCESS );
2244         case EROFS:
2245             return AFPERR_VLOCK;
2246         case EINVAL:
2247             /* tried to move directory into a subdirectory of itself */
2248             return AFPERR_CANTMOVE;
2249         case EXDEV:
2250             /* this needs to copy and delete. bleah. that means we have
2251              * to deal with entire directory hierarchies. */
2252             if ((err = copydir(vol, newparent, dirfd, src, dst)) < 0) {
2253                 deletedir(vol, -1, dst);
2254                 return err;
2255             }
2256             if ((err = deletedir(vol, dirfd, src)) < 0)
2257                 return err;
2258             break;
2259         default :
2260             return( AFPERR_PARAM );
2261         }
2262     }
2263
2264     vol->vfs->vfs_renamedir(vol, dirfd, src, dst);
2265
2266     ad_init(&ad, vol);
2267
2268     if (ad_open(&ad, dst, ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_RDWR) == 0) {
2269         ad_setname(&ad, newname);
2270         ad_flush(&ad);
2271         ad_close(&ad, ADFLAGS_HF);
2272     }
2273
2274     return( AFP_OK );
2275 }
2276
2277 /* delete an empty directory */
2278 int deletecurdir(struct vol *vol)
2279 {
2280     struct dirent *de;
2281     struct stat st;
2282     struct dir  *fdir, *pdir;
2283     DIR *dp;
2284     struct adouble  ad;
2285     uint16_t       ashort;
2286     int err;
2287
2288     if ((pdir = dirlookup(vol, curdir->d_pdid)) == NULL) {
2289         return( AFPERR_ACCESS );
2290     }
2291
2292     fdir = curdir;
2293
2294     ad_init(&ad, vol);
2295     /* we never want to create a resource fork here, we are going to delete it */
2296     if ( ad_metadata( ".", ADFLAGS_DIR, &ad) == 0 ) {
2297
2298         ad_getattr(&ad, &ashort);
2299         ad_close(&ad, ADFLAGS_HF);
2300         if (!(vol->v_ignattr & ATTRBIT_NODELETE) && (ashort & htons(ATTRBIT_NODELETE))) {
2301             return  AFPERR_OLOCK;
2302         }
2303     }
2304     err = vol->vfs->vfs_deletecurdir(vol);
2305     if (err) {
2306         LOG(log_error, logtype_afpd, "deletecurdir: error deleting .AppleDouble in \"%s\"",
2307             cfrombstr(curdir->d_fullpath));
2308         return err;
2309     }
2310
2311     /* now get rid of dangling symlinks */
2312     if ((dp = opendir("."))) {
2313         while ((de = readdir(dp))) {
2314             /* skip this and previous directory */
2315             if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
2316                 continue;
2317
2318             /* bail if it's not a symlink */
2319             if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
2320                 LOG(log_error, logtype_afpd, "deletecurdir(\"%s\"): not empty",
2321                     bdata(curdir->d_fullpath));
2322                 closedir(dp);
2323                 return AFPERR_DIRNEMPT;
2324             }
2325
2326             if ((err = netatalk_unlink(de->d_name))) {
2327                 closedir(dp);
2328                 return err;
2329             }
2330         }
2331     }
2332
2333     if (movecwd(vol, pdir) < 0) {
2334         err = afp_errno;
2335         goto delete_done;
2336     }
2337
2338     LOG(log_debug, logtype_afpd, "deletecurdir: moved to \"%s\"",
2339         cfrombstr(curdir->d_fullpath));
2340
2341     err = netatalk_rmdir_all_errors(-1, cfrombstr(fdir->d_u_name));
2342     if ( err ==  AFP_OK || err == AFPERR_NOOBJ) {
2343         AFP_CNID_START("cnid_delete");
2344         cnid_delete(vol->v_cdb, fdir->d_did);
2345         AFP_CNID_DONE();
2346         dir_remove( vol, fdir );
2347     } else {
2348         LOG(log_error, logtype_afpd, "deletecurdir(\"%s\"): netatalk_rmdir_all_errors error",
2349             cfrombstr(curdir->d_fullpath));
2350     }
2351
2352 delete_done:
2353     if (dp) {
2354         /* inode is used as key for cnid.
2355          * Close the descriptor only after cnid_delete
2356          * has been called.
2357          */
2358         closedir(dp);
2359     }
2360     return err;
2361 }
2362
2363 int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2364 {
2365     struct passwd   *pw;
2366     struct group    *gr;
2367     char        *name;
2368     uint32_t           id;
2369     int         len, sfunc;
2370     int         utf8 = 0;
2371
2372     ibuf++;
2373     sfunc = (unsigned char) *ibuf++;
2374     *rbuflen = 0;
2375
2376     if (sfunc >= 3 && sfunc <= 6) {
2377         if (obj->afp_version < 30) {
2378             return( AFPERR_PARAM );
2379         }
2380         utf8 = 1;
2381     }
2382
2383     switch ( sfunc ) {
2384     case 1 :
2385     case 3 :/* unicode */
2386         memcpy( &id, ibuf, sizeof( id ));
2387         id = ntohl(id);
2388         if ( id != 0 ) {
2389             if (( pw = getpwuid( id )) == NULL ) {
2390                 return( AFPERR_NOITEM );
2391             }
2392             len = convert_string_allocate( obj->options.unixcharset, ((!utf8)?obj->options.maccharset:CH_UTF8_MAC),
2393                                            pw->pw_name, -1, &name);
2394         } else {
2395             len = 0;
2396             name = NULL;
2397         }
2398         break;
2399     case 2 :
2400     case 4 : /* unicode */
2401         memcpy( &id, ibuf, sizeof( id ));
2402         id = ntohl(id);
2403         if ( id != 0 ) {
2404             if (NULL == ( gr = (struct group *)getgrgid( id ))) {
2405                 return( AFPERR_NOITEM );
2406             }
2407             len = convert_string_allocate( obj->options.unixcharset, (!utf8)?obj->options.maccharset:CH_UTF8_MAC,
2408                                            gr->gr_name, -1, &name);
2409         } else {
2410             len = 0;
2411             name = NULL;
2412         }
2413         break;
2414
2415     case 5 : /* UUID -> username */
2416     case 6 : /* UUID -> groupname */
2417         if ((obj->afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2418             return AFPERR_PARAM;
2419         LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request");
2420         uuidtype_t type;
2421         len = getnamefromuuid((unsigned char*) ibuf, &name, &type);
2422         if (len != 0)       /* its a error code, not len */
2423             return AFPERR_NOITEM;
2424         switch (type) {
2425         case UUID_USER:
2426             if (( pw = getpwnam( name )) == NULL )
2427                 return( AFPERR_NOITEM );
2428             LOG(log_debug, logtype_afpd, "afp_mapid: name:%s -> uid:%d", name, pw->pw_uid);
2429             id = htonl(UUID_USER);
2430             memcpy( rbuf, &id, sizeof( id ));
2431             id = htonl( pw->pw_uid);
2432             rbuf += sizeof( id );
2433             memcpy( rbuf, &id, sizeof( id ));
2434             rbuf += sizeof( id );
2435             *rbuflen = 2 * sizeof( id );
2436             break;
2437         case UUID_GROUP:
2438             if (( gr = getgrnam( name )) == NULL )
2439                 return( AFPERR_NOITEM );
2440             LOG(log_debug, logtype_afpd, "afp_mapid: group:%s -> gid:%d", name, gr->gr_gid);
2441             id = htonl(UUID_GROUP);
2442             memcpy( rbuf, &id, sizeof( id ));
2443             rbuf += sizeof( id );
2444             id = htonl( gr->gr_gid);
2445             memcpy( rbuf, &id, sizeof( id ));
2446             rbuf += sizeof( id );
2447             *rbuflen = 2 * sizeof( id );
2448             break;
2449         default:
2450             return AFPERR_MISC;
2451         }
2452         break;
2453
2454     default :
2455         return( AFPERR_PARAM );
2456     }
2457
2458     if (name)
2459         len = strlen( name );
2460
2461     if (utf8) {
2462         uint16_t tp = htons(len);
2463         memcpy(rbuf, &tp, sizeof(tp));
2464         rbuf += sizeof(tp);
2465         *rbuflen += 2;
2466     }
2467     else {
2468         *rbuf++ = len;
2469         *rbuflen += 1;
2470     }
2471     if ( len > 0 ) {
2472         memcpy( rbuf, name, len );
2473     }
2474     *rbuflen += len;
2475     if (name)
2476         free(name);
2477     return( AFP_OK );
2478 }
2479
2480 int afp_mapname(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2481 {
2482     struct passwd   *pw;
2483     struct group    *gr;
2484     int             len, sfunc;
2485     uint32_t       id;
2486     uint16_t       ulen;
2487
2488     ibuf++;
2489     sfunc = (unsigned char) *ibuf++;
2490     *rbuflen = 0;
2491     LOG(log_debug, logtype_afpd, "afp_mapname: sfunc: %d", sfunc);
2492     switch ( sfunc ) {
2493     case 1 :
2494     case 2 : /* unicode */
2495         if (obj->afp_version < 30) {
2496             return( AFPERR_PARAM );
2497         }
2498         memcpy(&ulen, ibuf, sizeof(ulen));
2499         len = ntohs(ulen);
2500         ibuf += 2;
2501         LOG(log_debug, logtype_afpd, "afp_mapname: alive");
2502         break;
2503     case 3 :
2504     case 4 :
2505         len = (unsigned char) *ibuf++;
2506         break;
2507     case 5 : /* username -> UUID  */
2508     case 6 : /* groupname -> UUID */
2509         if ((obj->afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2510             return AFPERR_PARAM;
2511         memcpy(&ulen, ibuf, sizeof(ulen));
2512         len = ntohs(ulen);
2513         ibuf += 2;
2514         break;
2515     default :
2516         return( AFPERR_PARAM );
2517     }
2518
2519     ibuf[ len ] = '\0';
2520
2521     if ( len == 0 )
2522         return AFPERR_PARAM;
2523     else {
2524         switch ( sfunc ) {
2525         case 1 : /* unicode */
2526         case 3 :
2527             if (NULL == ( pw = (struct passwd *)getpwnam( ibuf )) ) {
2528                 return( AFPERR_NOITEM );
2529             }
2530             id = pw->pw_uid;
2531             id = htonl(id);
2532             memcpy( rbuf, &id, sizeof( id ));
2533             *rbuflen = sizeof( id );
2534             break;
2535
2536         case 2 : /* unicode */
2537         case 4 :
2538             LOG(log_debug, logtype_afpd, "afp_mapname: getgrnam for name: %s",ibuf);
2539             if (NULL == ( gr = (struct group *)getgrnam( ibuf ))) {
2540                 return( AFPERR_NOITEM );
2541             }
2542             id = gr->gr_gid;
2543             LOG(log_debug, logtype_afpd, "afp_mapname: getgrnam for name: %s -> id: %d",ibuf, id);
2544             id = htonl(id);
2545             memcpy( rbuf, &id, sizeof( id ));
2546             *rbuflen = sizeof( id );
2547             break;
2548         case 5 :        /* username -> UUID */
2549             LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2550             if (0 != getuuidfromname(ibuf, UUID_USER, (unsigned char *)rbuf))
2551                 return AFPERR_NOITEM;
2552             *rbuflen = UUID_BINSIZE;
2553             break;
2554         case 6 :        /* groupname -> UUID */
2555             LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2556             if (0 != getuuidfromname(ibuf, UUID_GROUP, (unsigned char *)rbuf))
2557                 return AFPERR_NOITEM;
2558             *rbuflen = UUID_BINSIZE;
2559             break;
2560         }
2561     }
2562     return( AFP_OK );
2563 }
2564
2565 /* ------------------------------------
2566    variable DID support
2567 */
2568 int afp_closedir(AFPObj *obj _U_, char *ibuf _U_, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2569 {
2570 #if 0
2571     struct vol   *vol;
2572     struct dir   *dir;
2573     uint16_t    vid;
2574     uint32_t    did;
2575 #endif /* 0 */
2576
2577     *rbuflen = 0;
2578
2579     /* do nothing as dids are static for the life of the process. */
2580 #if 0
2581     ibuf += 2;
2582
2583     memcpy(&vid,  ibuf, sizeof( vid ));
2584     ibuf += sizeof( vid );
2585     if (( vol = getvolbyvid( vid )) == NULL ) {
2586         return( AFPERR_PARAM );
2587     }
2588
2589     memcpy( &did, ibuf, sizeof( did ));
2590     ibuf += sizeof( did );
2591     if (( dir = dirlookup( vol, did )) == NULL ) {
2592         return( AFPERR_PARAM );
2593     }
2594
2595     /* dir_remove -- deletedid */
2596 #endif /* 0 */
2597
2598     return AFP_OK;
2599 }
2600
2601 /* did creation gets done automatically
2602  * there's a pb again with case but move it to cname
2603  */
2604 int afp_opendir(AFPObj *obj _U_, char *ibuf, size_t ibuflen  _U_, char *rbuf, size_t *rbuflen)
2605 {
2606     struct vol      *vol;
2607     struct dir      *parentdir;
2608     struct path     *path;
2609     uint32_t       did;
2610     uint16_t       vid;
2611
2612     *rbuflen = 0;
2613     ibuf += 2;
2614
2615     memcpy(&vid, ibuf, sizeof(vid));
2616     ibuf += sizeof( vid );
2617
2618     if (NULL == ( vol = getvolbyvid( vid )) ) {
2619         return( AFPERR_PARAM );
2620     }
2621
2622     memcpy(&did, ibuf, sizeof(did));
2623     ibuf += sizeof(did);
2624
2625     if (NULL == ( parentdir = dirlookup( vol, did )) ) {
2626         return afp_errno;
2627     }
2628
2629     if (NULL == ( path = cname( vol, parentdir, &ibuf )) ) {
2630         return get_afp_errno(AFPERR_PARAM);
2631     }
2632
2633     if ( *path->m_name != '\0' ) {
2634         return path_error(path, AFPERR_NOOBJ);
2635     }
2636
2637     if ( !path->st_valid && of_stat(vol, path) < 0 ) {
2638         return( AFPERR_NOOBJ );
2639     }
2640     if ( path->st_errno ) {
2641         return( AFPERR_NOOBJ );
2642     }
2643
2644     memcpy(rbuf, &curdir->d_did, sizeof(curdir->d_did));
2645     *rbuflen = sizeof(curdir->d_did);
2646     return AFP_OK;
2647 }