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