]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/directory.c
fce: FCE version 2 with new event types and new config options
[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             if (vol->v_obj->options.flags & OPTION_VETOMSG) {
1176                 bstring message = bformat("Attempt to access vetoed file or directory \"%s\" in directory \"%s\"",
1177                                           ret.u_name, bdata(dir->d_u_name));
1178                 if (setmessage(bdata(message)) == 0)
1179                     /* Client may make multiple attempts, only send the message the first time */
1180                     kill(getpid(), SIGUSR2);
1181                 bdestroy(message);
1182             }
1183             return NULL;
1184         }
1185
1186         if (dir->d_did == DIRDID_ROOT_PARENT) { /* 8 */
1187             /*
1188              * Special case: CNID 1
1189              * root parent (did 1) has one child: the volume. Requests for did=1 with
1190              * some <name> must check against the volume name.
1191              */
1192             if ((strcmp(cfrombstr(vol->v_root->d_m_name), ret.m_name)) == 0)
1193                 cdir = vol->v_root;
1194             else
1195                 return NULL;
1196         } else {
1197             /*
1198              * CNID != 1, eg. most of the times we take this way.
1199              * Now check if current path-part is a file or dir:
1200              * o if it's dir we have to step into it
1201              * o if it's a file we expect it to be the last part of the requested path
1202              *   and thus call continue which should terminate the while loop because
1203              *   len = 0. Ok?
1204              */
1205             if (of_stat(vol, &ret) != 0) { /* 9 */
1206                 /*
1207                  * ret.u_name doesn't exist, might be afp_createfile|dir
1208                  * that means it should have been the last part
1209                  */
1210                 if (len > 0) {
1211                     /* it wasn't the last part, so we have a bogus path request */
1212                     afp_errno = AFPERR_NOOBJ;
1213                     return NULL;
1214                 }
1215                 /*
1216                  * this will terminate clean in while (1) because len == 0,
1217                  * probably afp_createfile|dir
1218                  */
1219                 LOG(log_maxdebug, logtype_afpd, "came('%s'): {leave-cnode ENOENT (possile create request): '%s'}",
1220                     cfrombstr(dir->d_fullpath), ret.u_name);
1221                 continue; /* 10 */
1222             }
1223
1224             switch (ret.st.st_mode & S_IFMT) {
1225             case S_IFREG: /* 11 */
1226                 LOG(log_debug, logtype_afpd, "came('%s'): {file: '%s'}",
1227                     cfrombstr(dir->d_fullpath), ret.u_name);
1228                 if (len > 0) {
1229                     /* it wasn't the last part, so we have a bogus path request */
1230                     afp_errno = AFPERR_PARAM;
1231                     return NULL;
1232                 }
1233                 continue; /* continues while loop */
1234             case S_IFLNK: /* 12 */
1235                 LOG(log_debug, logtype_afpd, "came('%s'): {link: '%s'}",
1236                     cfrombstr(dir->d_fullpath), ret.u_name);
1237                 if (len > 0) {
1238                     LOG(log_warning, logtype_afpd, "came('%s'): {symlinked dir: '%s'}",
1239                         cfrombstr(dir->d_fullpath), ret.u_name);
1240                     afp_errno = AFPERR_PARAM;
1241                     return NULL;
1242                 }
1243                 continue; /* continues while loop */
1244             case S_IFDIR: /* 13 */
1245                 break;
1246             default:
1247                 LOG(log_info, logtype_afpd, "cname: special file: '%s'", ret.u_name);
1248                 afp_errno = AFPERR_NODIR;
1249                 return NULL;
1250             }
1251
1252             /* Search the cache */
1253             int unamelen = strlen(ret.u_name);
1254             cdir = dircache_search_by_name(vol, dir, ret.u_name, unamelen); /* 14 */
1255             if (cdir == NULL) {
1256                 /* Not in cache, create one */
1257                 if ((cdir = dir_add(vol, dir, &ret, unamelen)) == NULL) { /* 15 */
1258                     LOG(log_error, logtype_afpd, "cname(did:%u, name:'%s', cwd:'%s'): failed to add dir",
1259                         ntohl(dir->d_did), ret.u_name, getcwdpath());
1260                     return NULL;
1261                 }
1262             }
1263         } /* if/else cnid==1 */
1264
1265         /* Now chdir to the evaluated dir */
1266         if (movecwd( vol, cdir ) < 0 ) { /* 16 */
1267             LOG(log_debug, logtype_afpd, "cname(cwd:'%s'): failed to chdir to new subdir '%s': %s",
1268                 cfrombstr(curdir->d_fullpath), cfrombstr(cdir->d_fullpath), strerror(errno));
1269             if (len == 0)
1270                 return path_from_dir(vol, cdir, &ret);
1271             else
1272                 return NULL;
1273         }
1274         dir = cdir;
1275         ret.m_name[0] = 0;      /* 17, so we later know last token was a dir */
1276     } /* while (len) */
1277
1278     if (curdir->d_did == DIRDID_ROOT_PARENT) {
1279         afp_errno = AFPERR_DID1;
1280         return NULL;
1281     }
1282
1283     if (ret.m_name[0] == 0) {
1284         /* Last part was a dir */
1285         ret.u_name = mtoupath(vol, ret.m_name, 0, 1); /* Force "." into a useable static buffer */
1286         ret.d_dir = dir;
1287     }
1288
1289     LOG(log_debug, logtype_afpd, "came('%s') {end: curdir:'%s', path:'%s'}",
1290         cfrombstr(dir->d_fullpath),
1291         cfrombstr(curdir->d_fullpath),
1292         ret.u_name);
1293
1294     return &ret;
1295 }
1296
1297 /*
1298  * @brief chdir() to dir
1299  *
1300  * @param vol   (r) pointer to struct vol
1301  * @param dir   (r) pointer to struct dir
1302  *
1303  * @returns 0 on success, -1 on error with afp_errno set appropiately
1304  */
1305 int movecwd(const struct vol *vol, struct dir *dir)
1306 {
1307     int ret;
1308
1309     AFP_ASSERT(vol);
1310     AFP_ASSERT(dir);
1311
1312     LOG(log_maxdebug, logtype_afpd, "movecwd: from: curdir:\"%s\", cwd:\"%s\"",
1313         curdir ? cfrombstr(curdir->d_fullpath) : "INVALID", getcwdpath());
1314
1315     if (dir->d_did == DIRDID_ROOT_PARENT) {
1316         curdir = &rootParent;
1317         return 0;
1318     }
1319
1320     LOG(log_debug, logtype_afpd, "movecwd(to: did: %u, \"%s\")",
1321         ntohl(dir->d_did), cfrombstr(dir->d_fullpath));
1322
1323     if ((ret = ochdir(cfrombstr(dir->d_fullpath), vol_syml_opt(vol))) != 0 ) {
1324         LOG(log_debug, logtype_afpd, "movecwd(\"%s\"): %s",
1325             cfrombstr(dir->d_fullpath), strerror(errno));
1326         if (ret == 1) {
1327             /* p is a symlink or getcwd failed */
1328             afp_errno = AFPERR_BADTYPE;
1329
1330             if (chdir(vol->v_path ) < 0) {
1331                 LOG(log_error, logtype_afpd, "can't chdir back'%s': %s", vol->v_path, strerror(errno));
1332                 /* XXX what do we do here? */
1333             }
1334             curdir = vol->v_root;
1335             return -1;
1336         }
1337
1338         switch (errno) {
1339         case EACCES:
1340         case EPERM:
1341             afp_errno = AFPERR_ACCESS;
1342             break;
1343         default:
1344             afp_errno = AFPERR_NOOBJ;
1345         }
1346         return( -1 );
1347     }
1348
1349     curdir = dir;
1350     return( 0 );
1351 }
1352
1353 /*
1354  * We can't use unix file's perm to support Apple's inherited protection modes.
1355  * If we aren't the file's owner we can't change its perms when moving it and smb
1356  * nfs,... don't even try.
1357  */
1358 int check_access(const AFPObj *obj, struct vol *vol, char *path, int mode)
1359 {
1360     struct maccess ma;
1361     char *p;
1362
1363     p = ad_dir(path);
1364     if (!p)
1365         return -1;
1366
1367     accessmode(obj, vol, p, &ma, curdir, NULL);
1368     if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE))
1369         return -1;
1370     if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD))
1371         return -1;
1372
1373     return 0;
1374 }
1375
1376 /* --------------------- */
1377 int file_access(const AFPObj *obj, struct vol *vol, struct path *path, int mode)
1378 {
1379     struct maccess ma;
1380
1381     accessmode(obj, vol, path->u_name, &ma, curdir, &path->st);
1382
1383     LOG(log_debug, logtype_afpd, "file_access(\"%s\"): mapped user mode: 0x%02x",
1384         path->u_name, ma.ma_user);
1385
1386     if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE)) {
1387         LOG(log_debug, logtype_afpd, "file_access(\"%s\"): write access denied", path->u_name);
1388         return -1;
1389     }
1390     if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD)) {
1391         LOG(log_debug, logtype_afpd, "file_access(\"%s\"): read access denied", path->u_name);
1392         return -1;
1393     }
1394     return 0;
1395
1396 }
1397
1398 /* --------------------- */
1399 void setdiroffcnt(struct dir *dir, struct stat *st,  uint32_t count)
1400 {
1401     dir->d_offcnt = count;
1402     dir->d_ctime = st->st_ctime;
1403     dir->d_flags &= ~DIRF_CNID;
1404 }
1405
1406
1407 /* ---------------------
1408  * is our cached also for reenumerate id?
1409  */
1410 int dirreenumerate(struct dir *dir, struct stat *st)
1411 {
1412     return st->st_ctime == dir->d_ctime && (dir->d_flags & DIRF_CNID);
1413 }
1414
1415 /* ------------------------------
1416    (".", curdir)
1417    (name, dir) with curdir:name == dir, from afp_enumerate
1418 */
1419
1420 int getdirparams(const AFPObj *obj,
1421                  const struct vol *vol,
1422                  uint16_t bitmap, struct path *s_path,
1423                  struct dir *dir,
1424                  char *buf, size_t *buflen )
1425 {
1426     struct maccess  ma;
1427     struct adouble  ad;
1428     char        *data, *l_nameoff = NULL, *utf_nameoff = NULL;
1429     int         bit = 0, isad = 0;
1430     uint32_t           aint;
1431     uint16_t       ashort;
1432     int                 ret;
1433     uint32_t           utf8 = 0;
1434     cnid_t              pdid;
1435     struct stat *st = &s_path->st;
1436     char *upath = s_path->u_name;
1437
1438     if ((bitmap & ((1 << DIRPBIT_ATTR)  |
1439                    (1 << DIRPBIT_CDATE) |
1440                    (1 << DIRPBIT_MDATE) |
1441                    (1 << DIRPBIT_BDATE) |
1442                    (1 << DIRPBIT_FINFO)))) {
1443
1444         ad_init(&ad, vol);
1445         if ( !ad_metadata( upath, ADFLAGS_DIR, &ad) ) {
1446             isad = 1;
1447             if (ad.ad_mdp->adf_flags & O_CREAT) {
1448                 /* We just created it */
1449                 if (s_path->m_name == NULL) {
1450                     if ((s_path->m_name = utompath(vol,
1451                                                    upath,
1452                                                    dir->d_did,
1453                                                    utf8_encoding(obj))) == NULL) {
1454                         LOG(log_error, logtype_afpd,
1455                             "getdirparams(\"%s\"): can't assign macname",
1456                             cfrombstr(dir->d_fullpath));
1457                         return AFPERR_MISC;
1458                     }
1459                 }
1460                 ad_setname(&ad, s_path->m_name);
1461                 ad_setid( &ad,
1462                           s_path->st.st_dev,
1463                           s_path->st.st_ino,
1464                           dir->d_did,
1465                           dir->d_pdid,
1466                           vol->v_stamp);
1467                 ad_flush( &ad);
1468             }
1469         }
1470     }
1471
1472     pdid = dir->d_pdid;
1473
1474     data = buf;
1475     while ( bitmap != 0 ) {
1476         while (( bitmap & 1 ) == 0 ) {
1477             bitmap = bitmap>>1;
1478             bit++;
1479         }
1480
1481         switch ( bit ) {
1482         case DIRPBIT_ATTR :
1483             if ( isad ) {
1484                 ad_getattr(&ad, &ashort);
1485             } else if (invisible_dots(vol, cfrombstr(dir->d_u_name))) {
1486                 ashort = htons(ATTRBIT_INVISIBLE);
1487             } else
1488                 ashort = 0;
1489             ashort &= ~htons(vol->v_ignattr);
1490             memcpy( data, &ashort, sizeof( ashort ));
1491             data += sizeof( ashort );
1492             break;
1493
1494         case DIRPBIT_PDID :
1495             memcpy( data, &pdid, sizeof( pdid ));
1496             data += sizeof( pdid );
1497             LOG(log_debug, logtype_afpd, "metadata('%s'):     Parent DID: %u",
1498                 s_path->u_name, ntohl(pdid));
1499             break;
1500
1501         case DIRPBIT_CDATE :
1502             if (!isad || (ad_getdate(&ad, AD_DATE_CREATE, &aint) < 0))
1503                 aint = AD_DATE_FROM_UNIX(st->st_mtime);
1504             memcpy( data, &aint, sizeof( aint ));
1505             data += sizeof( aint );
1506             break;
1507
1508         case DIRPBIT_MDATE :
1509             aint = AD_DATE_FROM_UNIX(st->st_mtime);
1510             memcpy( data, &aint, sizeof( aint ));
1511             data += sizeof( aint );
1512             break;
1513
1514         case DIRPBIT_BDATE :
1515             if (!isad || (ad_getdate(&ad, AD_DATE_BACKUP, &aint) < 0))
1516                 aint = AD_DATE_START;
1517             memcpy( data, &aint, sizeof( aint ));
1518             data += sizeof( aint );
1519             break;
1520
1521         case DIRPBIT_FINFO :
1522             if ( isad ) {
1523                 memcpy( data, ad_entry( &ad, ADEID_FINDERI ), 32 );
1524             } else { /* no appledouble */
1525                 memset( data, 0, 32 );
1526                 /* dot files are by default visible */
1527                 if (invisible_dots(vol, cfrombstr(dir->d_u_name))) {
1528                     ashort = htons(FINDERINFO_INVISIBLE);
1529                     memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
1530                 }
1531             }
1532             data += 32;
1533             break;
1534
1535         case DIRPBIT_LNAME :
1536             if (dir->d_m_name) /* root of parent can have a null name */
1537                 l_nameoff = data;
1538             else
1539                 memset(data, 0, sizeof(uint16_t));
1540             data += sizeof( uint16_t );
1541             break;
1542
1543         case DIRPBIT_SNAME :
1544             memset(data, 0, sizeof(uint16_t));
1545             data += sizeof( uint16_t );
1546             break;
1547
1548         case DIRPBIT_DID :
1549             memcpy( data, &dir->d_did, sizeof( aint ));
1550             data += sizeof( aint );
1551             LOG(log_debug, logtype_afpd, "metadata('%s'):            DID: %u",
1552                 s_path->u_name, ntohl(dir->d_did));
1553             break;
1554
1555         case DIRPBIT_OFFCNT :
1556             ashort = 0;
1557             /* this needs to handle current directory access rights */
1558             if (diroffcnt(dir, st)) {
1559                 ashort = (dir->d_offcnt > 0xffff)?0xffff:dir->d_offcnt;
1560             }
1561             else if ((ret = for_each_dirent(vol, upath, NULL,NULL)) >= 0) {
1562                 setdiroffcnt(dir, st,  ret);
1563                 ashort = (dir->d_offcnt > 0xffff)?0xffff:dir->d_offcnt;
1564             }
1565             ashort = htons( ashort );
1566             memcpy( data, &ashort, sizeof( ashort ));
1567             data += sizeof( ashort );
1568             break;
1569
1570         case DIRPBIT_UID :
1571             aint = htonl(st->st_uid);
1572             memcpy( data, &aint, sizeof( aint ));
1573             data += sizeof( aint );
1574             break;
1575
1576         case DIRPBIT_GID :
1577             aint = htonl(st->st_gid);
1578             memcpy( data, &aint, sizeof( aint ));
1579             data += sizeof( aint );
1580             break;
1581
1582         case DIRPBIT_ACCESS :
1583             accessmode(obj, vol, upath, &ma, dir , st);
1584
1585             *data++ = ma.ma_user;
1586             *data++ = ma.ma_world;
1587             *data++ = ma.ma_group;
1588             *data++ = ma.ma_owner;
1589             break;
1590
1591             /* Client has requested the ProDOS information block.
1592                Just pass back the same basic block for all
1593                directories. <shirsch@ibm.net> */
1594         case DIRPBIT_PDINFO :
1595             if (obj->afp_version >= 30) { /* UTF8 name */
1596                 utf8 = kTextEncodingUTF8;
1597                 if (dir->d_m_name) /* root of parent can have a null name */
1598                     utf_nameoff = data;
1599                 else
1600                     memset(data, 0, sizeof(uint16_t));
1601                 data += sizeof( uint16_t );
1602                 aint = 0;
1603                 memcpy(data, &aint, sizeof( aint ));
1604                 data += sizeof( aint );
1605             }
1606             else { /* ProDOS Info Block */
1607                 *data++ = 0x0f;
1608                 *data++ = 0;
1609                 ashort = htons( 0x0200 );
1610                 memcpy( data, &ashort, sizeof( ashort ));
1611                 data += sizeof( ashort );
1612                 memset( data, 0, sizeof( ashort ));
1613                 data += sizeof( ashort );
1614             }
1615             break;
1616
1617         case DIRPBIT_UNIXPR :
1618             /* accessmode may change st_mode with ACLs */
1619             accessmode(obj, vol, upath, &ma, dir, st);
1620
1621             aint = htonl(st->st_uid);
1622             memcpy( data, &aint, sizeof( aint ));
1623             data += sizeof( aint );
1624             aint = htonl(st->st_gid);
1625             memcpy( data, &aint, sizeof( aint ));
1626             data += sizeof( aint );
1627
1628             aint = st->st_mode;
1629             aint = htonl ( aint & ~S_ISGID );  /* Remove SGID, OSX doesn't like it ... */
1630             memcpy( data, &aint, sizeof( aint ));
1631             data += sizeof( aint );
1632
1633             *data++ = ma.ma_user;
1634             *data++ = ma.ma_world;
1635             *data++ = ma.ma_group;
1636             *data++ = ma.ma_owner;
1637             break;
1638
1639         default :
1640             if ( isad ) {
1641                 ad_close(&ad, ADFLAGS_HF);
1642             }
1643             return( AFPERR_BITMAP );
1644         }
1645         bitmap = bitmap>>1;
1646         bit++;
1647     }
1648     if ( l_nameoff ) {
1649         ashort = htons( data - buf );
1650         memcpy( l_nameoff, &ashort, sizeof( ashort ));
1651         data = set_name(vol, data, pdid, cfrombstr(dir->d_m_name), dir->d_did, 0);
1652     }
1653     if ( utf_nameoff ) {
1654         ashort = htons( data - buf );
1655         memcpy( utf_nameoff, &ashort, sizeof( ashort ));
1656         data = set_name(vol, data, pdid, cfrombstr(dir->d_m_name), dir->d_did, utf8);
1657     }
1658     if ( isad ) {
1659         ad_close(&ad, ADFLAGS_HF);
1660     }
1661     *buflen = data - buf;
1662     return( AFP_OK );
1663 }
1664
1665 /* ----------------------------- */
1666 int path_error(struct path *path, int error)
1667 {
1668 /* - a dir with access error
1669  * - no error it's a file
1670  * - file not found
1671  */
1672     if (path_isadir(path))
1673         return afp_errno;
1674     if (path->st_valid && path->st_errno)
1675         return error;
1676     return AFPERR_BADTYPE ;
1677 }
1678
1679 /* ----------------------------- */
1680 int afp_setdirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1681 {
1682     struct vol  *vol;
1683     struct dir  *dir;
1684     struct path *path;
1685     uint16_t   vid, bitmap;
1686     uint32_t   did;
1687     int     rc;
1688
1689     *rbuflen = 0;
1690     ibuf += 2;
1691     memcpy( &vid, ibuf, sizeof( vid ));
1692     ibuf += sizeof( vid );
1693
1694     if (NULL == ( vol = getvolbyvid( vid )) ) {
1695         return( AFPERR_PARAM );
1696     }
1697
1698     if (vol->v_flags & AFPVOL_RO)
1699         return AFPERR_VLOCK;
1700
1701     memcpy( &did, ibuf, sizeof( did ));
1702     ibuf += sizeof( int );
1703
1704     if (NULL == ( dir = dirlookup( vol, did )) ) {
1705         return afp_errno;
1706     }
1707
1708     memcpy( &bitmap, ibuf, sizeof( bitmap ));
1709     bitmap = ntohs( bitmap );
1710     ibuf += sizeof( bitmap );
1711
1712     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1713         return get_afp_errno(AFPERR_NOOBJ);
1714     }
1715
1716     if ( *path->m_name != '\0' ) {
1717         rc = path_error(path, AFPERR_NOOBJ);
1718         /* maybe we are trying to set perms back */
1719         if (rc != AFPERR_ACCESS)
1720             return rc;
1721     }
1722
1723     /*
1724      * If ibuf is odd, make it even.
1725      */
1726     if ((u_long)ibuf & 1 ) {
1727         ibuf++;
1728     }
1729
1730     if (AFP_OK == ( rc = setdirparams(vol, path, bitmap, ibuf )) ) {
1731         setvoltime(obj, vol );
1732     }
1733     return( rc );
1734 }
1735
1736 /*
1737  * assume path == '\0' eg. it's a directory in canonical form
1738  */
1739 int setdirparams(struct vol *vol, struct path *path, uint16_t d_bitmap, char *buf )
1740 {
1741     struct maccess  ma;
1742     struct adouble  ad;
1743     struct utimbuf      ut;
1744     struct timeval      tv;
1745
1746     char                *upath;
1747     struct dir          *dir;
1748     int         bit, isad = 0;
1749     int                 cdate, bdate;
1750     int                 owner, group;
1751     uint16_t       ashort, bshort, oshort;
1752     int                 err = AFP_OK;
1753     int                 change_mdate = 0;
1754     int                 change_parent_mdate = 0;
1755     int                 newdate = 0;
1756     uint16_t           bitmap = d_bitmap;
1757     u_char              finder_buf[32];
1758     uint32_t       upriv;
1759     mode_t              mpriv = 0;
1760     bool                set_upriv = false, set_maccess = false;
1761
1762     LOG(log_debug, logtype_afpd, "setdirparams(\"%s\", bitmap: %02x)", path->u_name, bitmap);
1763
1764     bit = 0;
1765     upath = path->u_name;
1766     dir   = path->d_dir;
1767     while ( bitmap != 0 ) {
1768         while (( bitmap & 1 ) == 0 ) {
1769             bitmap = bitmap>>1;
1770             bit++;
1771         }
1772
1773         switch( bit ) {
1774         case DIRPBIT_ATTR :
1775             change_mdate = 1;
1776             memcpy( &ashort, buf, sizeof( ashort ));
1777             buf += sizeof( ashort );
1778             break;
1779         case DIRPBIT_CDATE :
1780             change_mdate = 1;
1781             memcpy(&cdate, buf, sizeof(cdate));
1782             buf += sizeof( cdate );
1783             break;
1784         case DIRPBIT_MDATE :
1785             memcpy(&newdate, buf, sizeof(newdate));
1786             buf += sizeof( newdate );
1787             break;
1788         case DIRPBIT_BDATE :
1789             change_mdate = 1;
1790             memcpy(&bdate, buf, sizeof(bdate));
1791             buf += sizeof( bdate );
1792             break;
1793         case DIRPBIT_FINFO :
1794             change_mdate = 1;
1795             memcpy( finder_buf, buf, 32 );
1796             buf += 32;
1797             break;
1798         case DIRPBIT_UID :  /* What kind of loser mounts as root? */
1799             memcpy( &owner, buf, sizeof(owner));
1800             buf += sizeof( owner );
1801             break;
1802         case DIRPBIT_GID :
1803             memcpy( &group, buf, sizeof( group ));
1804             buf += sizeof( group );
1805             break;
1806         case DIRPBIT_ACCESS :
1807             set_maccess = true;
1808             change_mdate = 1;
1809             ma.ma_user = *buf++;
1810             ma.ma_world = *buf++;
1811             ma.ma_group = *buf++;
1812             ma.ma_owner = *buf++;
1813             mpriv = mtoumode( &ma ) | vol->v_dperm;
1814             break;
1815             /* Ignore what the client thinks we should do to the
1816                ProDOS information block.  Skip over the data and
1817                report nothing amiss. <shirsch@ibm.net> */
1818         case DIRPBIT_PDINFO :
1819             if (vol->v_obj->afp_version < 30) {
1820                 buf += 6;
1821             }
1822             else {
1823                 err = AFPERR_BITMAP;
1824                 bitmap = 0;
1825             }
1826             break;
1827         case DIRPBIT_UNIXPR :
1828             if (vol_unix_priv(vol)) {
1829                 set_upriv = true;
1830                 memcpy( &owner, buf, sizeof(owner)); /* FIXME need to change owner too? */
1831                 buf += sizeof( owner );
1832                 memcpy( &group, buf, sizeof( group ));
1833                 buf += sizeof( group );
1834
1835                 change_mdate = 1;
1836                 memcpy( &upriv, buf, sizeof( upriv ));
1837                 buf += sizeof( upriv );
1838                 upriv = ntohl (upriv) | vol->v_dperm;
1839                 break;
1840             }
1841             /* fall through */
1842         default :
1843             err = AFPERR_BITMAP;
1844             bitmap = 0;
1845             break;
1846         }
1847
1848         bitmap = bitmap>>1;
1849         bit++;
1850     }
1851
1852     if (d_bitmap & ((1<<DIRPBIT_ATTR) | (1<<DIRPBIT_CDATE) | (1<<DIRPBIT_BDATE) | (1<<DIRPBIT_FINFO))) {
1853         ad_init(&ad, vol);
1854         if (ad_open(&ad, upath, ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_CREATE | ADFLAGS_RDWR, 0777) != 0) {
1855             LOG(log_debug, logtype_afpd, "setdirparams(\"%s\", bitmap: %02x): need adouble", path->u_name, d_bitmap);
1856             return AFPERR_ACCESS;
1857         }
1858         if ((ad_get_MD_flags(&ad) & O_CREAT)) {
1859             ad_setname(&ad, cfrombstr(curdir->d_m_name));
1860         }
1861         isad = 1;
1862     }
1863
1864     bit = 0;
1865     bitmap = d_bitmap;
1866     while ( bitmap != 0 ) {
1867         while (( bitmap & 1 ) == 0 ) {
1868             bitmap = bitmap>>1;
1869             bit++;
1870         }
1871
1872         switch( bit ) {
1873         case DIRPBIT_ATTR :
1874             if (isad) {
1875                 ad_getattr(&ad, &bshort);
1876                 oshort = bshort;
1877                 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1878                     ashort &= ~htons(vol->v_ignattr);
1879                     bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1880                 } else {
1881                     bshort &= ~ashort;
1882                 }
1883                 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1884                     change_parent_mdate = 1;
1885                 ad_setattr(&ad, bshort);
1886             }
1887             break;
1888         case DIRPBIT_CDATE :
1889             if (isad) {
1890                 ad_setdate(&ad, AD_DATE_CREATE, cdate);
1891             }
1892             break;
1893         case DIRPBIT_MDATE :
1894             break;
1895         case DIRPBIT_BDATE :
1896             if (isad) {
1897                 ad_setdate(&ad, AD_DATE_BACKUP, bdate);
1898             }
1899             break;
1900         case DIRPBIT_FINFO :
1901             if (isad) {
1902                 /* Fixes #2802236 */
1903                 uint16_t fflags;
1904                 memcpy(&fflags, finder_buf + FINDERINFO_FRFLAGOFF, sizeof(uint16_t));
1905                 fflags &= htons(~FINDERINFO_ISHARED);
1906                 memcpy(finder_buf + FINDERINFO_FRFLAGOFF, &fflags, sizeof(uint16_t));
1907                 /* #2802236 end */
1908
1909                 if (  dir->d_did == DIRDID_ROOT ) {
1910                     /*
1911                      * Alright, we admit it, this is *really* sick!
1912                      * The 4 bytes that we don't copy, when we're dealing
1913                      * with the root of a volume, are the directory's
1914                      * location information. This eliminates that annoying
1915                      * behavior one sees when mounting above another mount
1916                      * point.
1917                      */
1918                     memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 10 );
1919                     memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, finder_buf + 14, 18 );
1920                 } else {
1921                     memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 32 );
1922                 }
1923             }
1924             break;
1925         case DIRPBIT_UID :  /* What kind of loser mounts as root? */
1926             if ( (dir->d_did == DIRDID_ROOT) &&
1927                  (setdeskowner(vol, ntohl(owner), -1 ) < 0)) {
1928                 err = set_dir_errors(path, "setdeskowner", errno);
1929                 if (isad && err == AFPERR_PARAM) {
1930                     err = AFP_OK; /* ???*/
1931                 }
1932                 else {
1933                     goto setdirparam_done;
1934                 }
1935             }
1936             if ( setdirowner(vol, upath, ntohl(owner), -1 ) < 0 ) {
1937                 err = set_dir_errors(path, "setdirowner", errno);
1938                 goto setdirparam_done;
1939             }
1940             break;
1941         case DIRPBIT_GID :
1942             if (dir->d_did == DIRDID_ROOT)
1943                 setdeskowner(vol, -1, ntohl(group) );
1944             if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
1945                 err = set_dir_errors(path, "setdirowner", errno);
1946                 goto setdirparam_done;
1947             }
1948             break;
1949         case DIRPBIT_ACCESS :
1950             break;
1951         case DIRPBIT_PDINFO :
1952             if (vol->v_obj->afp_version >= 30) {
1953                 err = AFPERR_BITMAP;
1954                 goto setdirparam_done;
1955             }
1956             break;
1957         case DIRPBIT_UNIXPR :
1958             if (!vol_unix_priv(vol)) {
1959                 err = AFPERR_BITMAP;
1960                 goto setdirparam_done;
1961             }
1962             break;
1963         default :
1964             err = AFPERR_BITMAP;
1965             goto setdirparam_done;
1966             break;
1967         }
1968
1969         bitmap = bitmap>>1;
1970         bit++;
1971     }
1972
1973 setdirparam_done:
1974     if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1975         newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1976     }
1977     if (newdate) {
1978         if (isad)
1979             ad_setdate(&ad, AD_DATE_MODIFY, newdate);
1980         ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1981         utime(upath, &ut);
1982     }
1983
1984     if (isad) {
1985         if (path->st_valid && !path->st_errno) {
1986             struct stat *st = &path->st;
1987             if (dir && dir->d_pdid) {
1988                 ad_setid(&ad, st->st_dev, st->st_ino,  dir->d_did, dir->d_pdid, vol->v_stamp);
1989             }
1990         }
1991         if (ad_flush(&ad) != 0) {
1992             switch (errno) {
1993             case EACCES:
1994                 err = AFPERR_ACCESS;
1995                 break;
1996             default:
1997                 err = AFPERR_MISC;
1998                 break;
1999            }
2000         }
2001         ad_close(&ad, ADFLAGS_HF);
2002     }
2003
2004     if (err == AFP_OK) {
2005         if (set_maccess == true) {
2006             if (dir->d_did == DIRDID_ROOT) {
2007                 setdeskmode(vol, mpriv);
2008                 if (!dir_rx_set(mpriv)) {
2009                     /* we can't remove read and search for owner on volume root */
2010                     err = AFPERR_ACCESS;
2011                     goto setprivdone;
2012                 }
2013             }
2014             if (setdirunixmode(vol, upath, mpriv) < 0) {
2015                 LOG(log_info, logtype_afpd, "setdirparams(\"%s\"): setdirunixmode: %s",
2016                     fullpathname(upath), strerror(errno));
2017                 err = set_dir_errors(path, "setdirmode", errno);
2018             }
2019         }
2020         if ((set_upriv == true) && vol_unix_priv(vol)) {
2021             if (dir->d_did == DIRDID_ROOT) {
2022                 if (!dir_rx_set(upriv)) {
2023                     /* we can't remove read and search for owner on volume root */
2024                     err = AFPERR_ACCESS;
2025                     goto setprivdone;
2026                 }
2027                 setdeskowner(vol, -1, ntohl(group));
2028                 setdeskmode(vol, upriv);
2029             }
2030
2031             if (setdirowner(vol, upath, -1, ntohl(group)) < 0) {
2032                 LOG(log_info, logtype_afpd, "setdirparams(\"%s\"): setdirowner: %s",
2033                     fullpathname(upath), strerror(errno));
2034                 err = set_dir_errors(path, "setdirowner", errno);
2035                 goto setprivdone;
2036             }
2037
2038             if (setdirunixmode(vol, upath, upriv) < 0) {
2039                 LOG(log_info, logtype_afpd, "setdirparams(\"%s\"): setdirunixmode: %s",
2040                     fullpathname(upath), strerror(errno));
2041                 err = set_dir_errors(path, "setdirunixmode", errno);
2042             }
2043         }
2044     }
2045
2046 setprivdone:
2047     if (change_parent_mdate && dir->d_did != DIRDID_ROOT
2048         && gettimeofday(&tv, NULL) == 0) {
2049         if (movecwd(vol, dirlookup(vol, dir->d_pdid)) == 0) {
2050             newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2051             /* be careful with bitmap because now dir is null */
2052             bitmap = 1<<DIRPBIT_MDATE;
2053             setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
2054             /* should we reset curdir ?*/
2055         }
2056     }
2057     return err;
2058 }
2059
2060 int afp_syncdir(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2061 {
2062 #ifdef HAVE_DIRFD
2063     DIR                  *dp;
2064 #endif
2065     int                  dfd;
2066     struct vol           *vol;
2067     struct dir           *dir;
2068     uint32_t            did;
2069     uint16_t            vid;
2070
2071     *rbuflen = 0;
2072     ibuf += 2;
2073
2074     memcpy( &vid, ibuf, sizeof( vid ));
2075     ibuf += sizeof( vid );
2076     if (NULL == (vol = getvolbyvid( vid )) ) {
2077         return( AFPERR_PARAM );
2078     }
2079
2080     memcpy( &did, ibuf, sizeof( did ));
2081     ibuf += sizeof( did );
2082
2083     /*
2084      * Here's the deal:
2085      * if it's CNID 2 our only choice to meet the specs is call sync.
2086      * For any other CNID just sync that dir. To my knowledge the
2087      * intended use of FPSyncDir is to sync the volume so all we're
2088      * ever going to see here is probably CNID 2. Anyway, we' prepared.
2089      */
2090
2091     if ( ntohl(did) == 2 ) {
2092         sync();
2093     } else {
2094         if (NULL == ( dir = dirlookup( vol, did )) ) {
2095             return afp_errno; /* was AFPERR_NOOBJ */
2096         }
2097
2098         if (movecwd( vol, dir ) < 0 )
2099             return ( AFPERR_NOOBJ );
2100
2101         /*
2102          * Assuming only OSens that have dirfd also may require fsyncing directories
2103          * in order to flush metadata e.g. Linux.
2104          */
2105
2106 #ifdef HAVE_DIRFD
2107         if (NULL == ( dp = opendir( "." )) ) {
2108             switch( errno ) {
2109             case ENOENT :
2110                 return( AFPERR_NOOBJ );
2111             case EACCES :
2112                 return( AFPERR_ACCESS );
2113             default :
2114                 return( AFPERR_PARAM );
2115             }
2116         }
2117
2118         LOG(log_debug, logtype_afpd, "afp_syncdir: dir: '%s'", dir->d_u_name);
2119
2120         dfd = dirfd( dp );
2121         if ( fsync ( dfd ) < 0 )
2122             LOG(log_error, logtype_afpd, "afp_syncdir(%s):  %s",
2123                 dir->d_u_name, strerror(errno) );
2124         closedir(dp); /* closes dfd too */
2125 #endif
2126
2127         if ( -1 == (dfd = open(vol->ad_path(".", ADFLAGS_DIR), O_RDWR))) {
2128             switch( errno ) {
2129             case ENOENT:
2130                 return( AFPERR_NOOBJ );
2131             case EACCES:
2132                 return( AFPERR_ACCESS );
2133             default:
2134                 return( AFPERR_PARAM );
2135             }
2136         }
2137
2138         LOG(log_debug, logtype_afpd, "afp_syncdir: ad-file: '%s'",
2139             vol->ad_path(".", ADFLAGS_DIR) );
2140
2141         if ( fsync(dfd) < 0 )
2142             LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2143                 vol->ad_path(cfrombstr(dir->d_u_name), ADFLAGS_DIR), strerror(errno) );
2144         close(dfd);
2145     }
2146
2147     return ( AFP_OK );
2148 }
2149
2150 int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2151 {
2152     struct adouble  ad;
2153     struct vol      *vol;
2154     struct dir      *dir;
2155     char        *upath;
2156     struct path         *s_path;
2157     uint32_t       did;
2158     uint16_t       vid;
2159     int                 err;
2160
2161     *rbuflen = 0;
2162     ibuf += 2;
2163
2164     memcpy( &vid, ibuf, sizeof( vid ));
2165     ibuf += sizeof( vid );
2166     if (NULL == ( vol = getvolbyvid( vid )) ) {
2167         return( AFPERR_PARAM );
2168     }
2169
2170     if (vol->v_flags & AFPVOL_RO)
2171         return AFPERR_VLOCK;
2172
2173     memcpy( &did, ibuf, sizeof( did ));
2174     ibuf += sizeof( did );
2175     if (NULL == ( dir = dirlookup( vol, did )) ) {
2176         return afp_errno; /* was AFPERR_NOOBJ */
2177     }
2178     /* for concurrent access we need to be sure we are not in the
2179      * folder we want to create...
2180      */
2181     movecwd(vol, dir);
2182
2183     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
2184         return get_afp_errno(AFPERR_PARAM);
2185     }
2186     /* cname was able to move curdir to it! */
2187     if (*s_path->m_name == '\0')
2188         return AFPERR_EXIST;
2189
2190     upath = s_path->u_name;
2191
2192     if (AFP_OK != (err = netatalk_mkdir(vol, upath))) {
2193         return err;
2194     }
2195
2196     if (of_stat(vol, s_path) < 0) {
2197         return AFPERR_MISC;
2198     }
2199
2200     curdir->d_offcnt++;
2201
2202     if ((dir = dir_add(vol, curdir, s_path, strlen(s_path->u_name))) == NULL) {
2203         return AFPERR_MISC;
2204     }
2205
2206     if ( movecwd( vol, dir ) < 0 ) {
2207         return( AFPERR_PARAM );
2208     }
2209
2210     ad_init(&ad, vol);
2211     if (ad_open(&ad, ".", ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_CREATE | ADFLAGS_RDWR, 0777) < 0)  {
2212         return( AFPERR_ACCESS );
2213     }
2214     ad_setname(&ad, s_path->m_name);
2215     ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
2216
2217     fce_register(obj, FCE_DIR_CREATE, bdata(curdir->d_fullpath), NULL);
2218
2219     ad_flush(&ad);
2220     ad_close(&ad, ADFLAGS_HF);
2221
2222     memcpy( rbuf, &dir->d_did, sizeof( uint32_t ));
2223     *rbuflen = sizeof( uint32_t );
2224     setvoltime(obj, vol );
2225     return( AFP_OK );
2226 }
2227
2228 /*
2229  * dst       new unix filename (not a pathname)
2230  * newname   new mac name
2231  * newparent curdir
2232  * dirfd     -1 means ignore dirfd (or use AT_FDCWD), otherwise src is relative to dirfd
2233  */
2234 int renamedir(struct vol *vol,
2235               int dirfd,
2236               char *src,
2237               char *dst,
2238               struct dir *dir,
2239               struct dir *newparent,
2240               char *newname)
2241 {
2242     struct adouble  ad;
2243     int             err;
2244
2245     /* existence check moved to afp_moveandrename */
2246     if ( unix_rename(dirfd, src, -1, dst ) < 0 ) {
2247         switch ( errno ) {
2248         case ENOENT :
2249             return( AFPERR_NOOBJ );
2250         case EACCES :
2251             return( AFPERR_ACCESS );
2252         case EROFS:
2253             return AFPERR_VLOCK;
2254         case EINVAL:
2255             /* tried to move directory into a subdirectory of itself */
2256             return AFPERR_CANTMOVE;
2257         case EXDEV:
2258             /* this needs to copy and delete. bleah. that means we have
2259              * to deal with entire directory hierarchies. */
2260             if ((err = copydir(vol, newparent, dirfd, src, dst)) < 0) {
2261                 deletedir(vol, -1, dst);
2262                 return err;
2263             }
2264             if ((err = deletedir(vol, dirfd, src)) < 0)
2265                 return err;
2266             break;
2267         default :
2268             return( AFPERR_PARAM );
2269         }
2270     }
2271
2272     vol->vfs->vfs_renamedir(vol, dirfd, src, dst);
2273
2274     ad_init(&ad, vol);
2275
2276     if (ad_open(&ad, dst, ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_RDWR) == 0) {
2277         ad_setname(&ad, newname);
2278         ad_flush(&ad);
2279         ad_close(&ad, ADFLAGS_HF);
2280     }
2281
2282     return( AFP_OK );
2283 }
2284
2285 /* delete an empty directory */
2286 int deletecurdir(struct vol *vol)
2287 {
2288     struct dirent *de;
2289     struct stat st;
2290     struct dir  *fdir, *pdir;
2291     struct adouble  ad;
2292     uint16_t       ashort;
2293     int err;
2294
2295     if ((pdir = dirlookup(vol, curdir->d_pdid)) == NULL) {
2296         return( AFPERR_ACCESS );
2297     }
2298
2299     fdir = curdir;
2300
2301     ad_init(&ad, vol);
2302     /* we never want to create a resource fork here, we are going to delete it */
2303     if ( ad_metadata( ".", ADFLAGS_DIR, &ad) == 0 ) {
2304
2305         ad_getattr(&ad, &ashort);
2306         ad_close(&ad, ADFLAGS_HF);
2307         if (!(vol->v_ignattr & ATTRBIT_NODELETE) && (ashort & htons(ATTRBIT_NODELETE))) {
2308             return  AFPERR_OLOCK;
2309         }
2310     }
2311     err = vol->vfs->vfs_deletecurdir(vol);
2312     if (err) {
2313         LOG(log_error, logtype_afpd, "deletecurdir: error deleting AppleDouble files in \"%s\"",
2314             cfrombstr(curdir->d_fullpath));
2315         return err;
2316     }
2317
2318     if (movecwd(vol, pdir) < 0) {
2319         err = afp_errno;
2320         goto delete_done;
2321     }
2322
2323     LOG(log_debug, logtype_afpd, "deletecurdir: moved to \"%s\"",
2324         cfrombstr(curdir->d_fullpath));
2325
2326     err = netatalk_rmdir_all_errors(-1, cfrombstr(fdir->d_u_name));
2327
2328     switch (err) {
2329     case AFP_OK:
2330     case AFPERR_NOOBJ:
2331         break;
2332     case AFPERR_DIRNEMPT:
2333         if (delete_vetoed_files(vol, bdata(fdir->d_u_name), false) != 0)
2334             goto delete_done;
2335         err = AFP_OK;
2336         break;
2337     default:
2338         LOG(log_error, logtype_afpd, "deletecurdir(\"%s\"): netatalk_rmdir_all_errors error",
2339             cfrombstr(curdir->d_fullpath));
2340         goto delete_done;
2341     }
2342
2343     AFP_CNID_START("cnid_delete");
2344     cnid_delete(vol->v_cdb, fdir->d_did);
2345     AFP_CNID_DONE();
2346
2347     dir_remove( vol, fdir );
2348
2349 delete_done:
2350     return err;
2351 }
2352
2353 int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2354 {
2355     struct passwd   *pw;
2356     struct group    *gr;
2357     char        *name;
2358     uint32_t           id;
2359     int         len, sfunc;
2360     int         utf8 = 0;
2361
2362     ibuf++;
2363     sfunc = (unsigned char) *ibuf++;
2364     *rbuflen = 0;
2365
2366     if (sfunc >= 3 && sfunc <= 6) {
2367         if (obj->afp_version < 30) {
2368             return( AFPERR_PARAM );
2369         }
2370         utf8 = 1;
2371     }
2372
2373     switch ( sfunc ) {
2374     case 1 :
2375     case 3 :/* unicode */
2376         memcpy( &id, ibuf, sizeof( id ));
2377         id = ntohl(id);
2378         if ( id != 0 ) {
2379             if (( pw = getpwuid( id )) == NULL ) {
2380                 return( AFPERR_NOITEM );
2381             }
2382             len = convert_string_allocate( obj->options.unixcharset, ((!utf8)?obj->options.maccharset:CH_UTF8_MAC),
2383                                            pw->pw_name, -1, &name);
2384         } else {
2385             len = 0;
2386             name = NULL;
2387         }
2388         break;
2389     case 2 :
2390     case 4 : /* unicode */
2391         memcpy( &id, ibuf, sizeof( id ));
2392         id = ntohl(id);
2393         if ( id != 0 ) {
2394             if (NULL == ( gr = (struct group *)getgrgid( id ))) {
2395                 return( AFPERR_NOITEM );
2396             }
2397             len = convert_string_allocate( obj->options.unixcharset, (!utf8)?obj->options.maccharset:CH_UTF8_MAC,
2398                                            gr->gr_name, -1, &name);
2399         } else {
2400             len = 0;
2401             name = NULL;
2402         }
2403         break;
2404
2405     case 5 : /* UUID -> username */
2406     case 6 : /* UUID -> groupname */
2407         if ((obj->afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2408             return AFPERR_PARAM;
2409         LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request");
2410         uuidtype_t type;
2411         len = getnamefromuuid((unsigned char*) ibuf, &name, &type);
2412         if (len != 0)       /* its a error code, not len */
2413             return AFPERR_NOITEM;
2414         switch (type) {
2415         case UUID_USER:
2416             if (( pw = getpwnam( name )) == NULL )
2417                 return( AFPERR_NOITEM );
2418             LOG(log_debug, logtype_afpd, "afp_mapid: name:%s -> uid:%d", name, pw->pw_uid);
2419             id = htonl(UUID_USER);
2420             memcpy( rbuf, &id, sizeof( id ));
2421             id = htonl( pw->pw_uid);
2422             rbuf += sizeof( id );
2423             memcpy( rbuf, &id, sizeof( id ));
2424             rbuf += sizeof( id );
2425             *rbuflen = 2 * sizeof( id );
2426             break;
2427         case UUID_GROUP:
2428             if (( gr = getgrnam( name )) == NULL )
2429                 return( AFPERR_NOITEM );
2430             LOG(log_debug, logtype_afpd, "afp_mapid: group:%s -> gid:%d", name, gr->gr_gid);
2431             id = htonl(UUID_GROUP);
2432             memcpy( rbuf, &id, sizeof( id ));
2433             rbuf += sizeof( id );
2434             id = htonl( gr->gr_gid);
2435             memcpy( rbuf, &id, sizeof( id ));
2436             rbuf += sizeof( id );
2437             *rbuflen = 2 * sizeof( id );
2438             break;
2439         default:
2440             return AFPERR_MISC;
2441         }
2442         break;
2443
2444     default :
2445         return( AFPERR_PARAM );
2446     }
2447
2448     if (name)
2449         len = strlen( name );
2450
2451     if (utf8) {
2452         uint16_t tp = htons(len);
2453         memcpy(rbuf, &tp, sizeof(tp));
2454         rbuf += sizeof(tp);
2455         *rbuflen += 2;
2456     }
2457     else {
2458         *rbuf++ = len;
2459         *rbuflen += 1;
2460     }
2461     if ( len > 0 ) {
2462         memcpy( rbuf, name, len );
2463     }
2464     *rbuflen += len;
2465     if (name)
2466         free(name);
2467     return( AFP_OK );
2468 }
2469
2470 int afp_mapname(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2471 {
2472     struct passwd   *pw;
2473     struct group    *gr;
2474     int             len, sfunc;
2475     uint32_t       id;
2476     uint16_t       ulen;
2477
2478     ibuf++;
2479     sfunc = (unsigned char) *ibuf++;
2480     *rbuflen = 0;
2481     LOG(log_debug, logtype_afpd, "afp_mapname: sfunc: %d", sfunc);
2482     switch ( sfunc ) {
2483     case 1 :
2484     case 2 : /* unicode */
2485         if (obj->afp_version < 30) {
2486             return( AFPERR_PARAM );
2487         }
2488         memcpy(&ulen, ibuf, sizeof(ulen));
2489         len = ntohs(ulen);
2490         ibuf += 2;
2491         LOG(log_debug, logtype_afpd, "afp_mapname: alive");
2492         break;
2493     case 3 :
2494     case 4 :
2495         len = (unsigned char) *ibuf++;
2496         break;
2497     case 5 : /* username -> UUID  */
2498     case 6 : /* groupname -> UUID */
2499         if ((obj->afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2500             return AFPERR_PARAM;
2501         memcpy(&ulen, ibuf, sizeof(ulen));
2502         len = ntohs(ulen);
2503         ibuf += 2;
2504         break;
2505     default :
2506         return( AFPERR_PARAM );
2507     }
2508
2509     ibuf[ len ] = '\0';
2510
2511     if ( len == 0 )
2512         return AFPERR_PARAM;
2513     else {
2514         switch ( sfunc ) {
2515         case 1 : /* unicode */
2516         case 3 :
2517             if (NULL == ( pw = (struct passwd *)getpwnam( ibuf )) ) {
2518                 return( AFPERR_NOITEM );
2519             }
2520             id = pw->pw_uid;
2521             id = htonl(id);
2522             memcpy( rbuf, &id, sizeof( id ));
2523             *rbuflen = sizeof( id );
2524             break;
2525
2526         case 2 : /* unicode */
2527         case 4 :
2528             LOG(log_debug, logtype_afpd, "afp_mapname: getgrnam for name: %s",ibuf);
2529             if (NULL == ( gr = (struct group *)getgrnam( ibuf ))) {
2530                 return( AFPERR_NOITEM );
2531             }
2532             id = gr->gr_gid;
2533             LOG(log_debug, logtype_afpd, "afp_mapname: getgrnam for name: %s -> id: %d",ibuf, id);
2534             id = htonl(id);
2535             memcpy( rbuf, &id, sizeof( id ));
2536             *rbuflen = sizeof( id );
2537             break;
2538         case 5 :        /* username -> UUID */
2539             LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2540             if (0 != getuuidfromname(ibuf, UUID_USER, (unsigned char *)rbuf))
2541                 return AFPERR_NOITEM;
2542             *rbuflen = UUID_BINSIZE;
2543             break;
2544         case 6 :        /* groupname -> UUID */
2545             LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2546             if (0 != getuuidfromname(ibuf, UUID_GROUP, (unsigned char *)rbuf))
2547                 return AFPERR_NOITEM;
2548             *rbuflen = UUID_BINSIZE;
2549             break;
2550         }
2551     }
2552     return( AFP_OK );
2553 }
2554
2555 /* ------------------------------------
2556    variable DID support
2557 */
2558 int afp_closedir(AFPObj *obj _U_, char *ibuf _U_, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2559 {
2560 #if 0
2561     struct vol   *vol;
2562     struct dir   *dir;
2563     uint16_t    vid;
2564     uint32_t    did;
2565 #endif /* 0 */
2566
2567     *rbuflen = 0;
2568
2569     /* do nothing as dids are static for the life of the process. */
2570 #if 0
2571     ibuf += 2;
2572
2573     memcpy(&vid,  ibuf, sizeof( vid ));
2574     ibuf += sizeof( vid );
2575     if (( vol = getvolbyvid( vid )) == NULL ) {
2576         return( AFPERR_PARAM );
2577     }
2578
2579     memcpy( &did, ibuf, sizeof( did ));
2580     ibuf += sizeof( did );
2581     if (( dir = dirlookup( vol, did )) == NULL ) {
2582         return( AFPERR_PARAM );
2583     }
2584
2585     /* dir_remove -- deletedid */
2586 #endif /* 0 */
2587
2588     return AFP_OK;
2589 }
2590
2591 /* did creation gets done automatically
2592  * there's a pb again with case but move it to cname
2593  */
2594 int afp_opendir(AFPObj *obj _U_, char *ibuf, size_t ibuflen  _U_, char *rbuf, size_t *rbuflen)
2595 {
2596     struct vol      *vol;
2597     struct dir      *parentdir;
2598     struct path     *path;
2599     uint32_t       did;
2600     uint16_t       vid;
2601
2602     *rbuflen = 0;
2603     ibuf += 2;
2604
2605     memcpy(&vid, ibuf, sizeof(vid));
2606     ibuf += sizeof( vid );
2607
2608     if (NULL == ( vol = getvolbyvid( vid )) ) {
2609         return( AFPERR_PARAM );
2610     }
2611
2612     memcpy(&did, ibuf, sizeof(did));
2613     ibuf += sizeof(did);
2614
2615     if (NULL == ( parentdir = dirlookup( vol, did )) ) {
2616         return afp_errno;
2617     }
2618
2619     if (NULL == ( path = cname( vol, parentdir, &ibuf )) ) {
2620         return get_afp_errno(AFPERR_PARAM);
2621     }
2622
2623     if ( *path->m_name != '\0' ) {
2624         return path_error(path, AFPERR_NOOBJ);
2625     }
2626
2627     if ( !path->st_valid && of_stat(vol, path) < 0 ) {
2628         return( AFPERR_NOOBJ );
2629     }
2630     if ( path->st_errno ) {
2631         return( AFPERR_NOOBJ );
2632     }
2633
2634     memcpy(rbuf, &curdir->d_did, sizeof(curdir->d_did));
2635     *rbuflen = sizeof(curdir->d_did);
2636     return AFP_OK;
2637 }