]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/directory.c
Change default FinderInfo for directories to be all 0
[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                 /* dot files are by default visible */
1518                 if (invisible_dots(vol, cfrombstr(dir->d_u_name))) {
1519                     ashort = htons(FINDERINFO_INVISIBLE);
1520                     memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
1521                 }
1522             }
1523             data += 32;
1524             break;
1525
1526         case DIRPBIT_LNAME :
1527             if (dir->d_m_name) /* root of parent can have a null name */
1528                 l_nameoff = data;
1529             else
1530                 memset(data, 0, sizeof(uint16_t));
1531             data += sizeof( uint16_t );
1532             break;
1533
1534         case DIRPBIT_SNAME :
1535             memset(data, 0, sizeof(uint16_t));
1536             data += sizeof( uint16_t );
1537             break;
1538
1539         case DIRPBIT_DID :
1540             memcpy( data, &dir->d_did, sizeof( aint ));
1541             data += sizeof( aint );
1542             LOG(log_debug, logtype_afpd, "metadata('%s'):            DID: %u",
1543                 s_path->u_name, ntohl(dir->d_did));
1544             break;
1545
1546         case DIRPBIT_OFFCNT :
1547             ashort = 0;
1548             /* this needs to handle current directory access rights */
1549             if (diroffcnt(dir, st)) {
1550                 ashort = (dir->d_offcnt > 0xffff)?0xffff:dir->d_offcnt;
1551             }
1552             else if ((ret = for_each_dirent(vol, upath, NULL,NULL)) >= 0) {
1553                 setdiroffcnt(dir, st,  ret);
1554                 ashort = (dir->d_offcnt > 0xffff)?0xffff:dir->d_offcnt;
1555             }
1556             ashort = htons( ashort );
1557             memcpy( data, &ashort, sizeof( ashort ));
1558             data += sizeof( ashort );
1559             break;
1560
1561         case DIRPBIT_UID :
1562             aint = htonl(st->st_uid);
1563             memcpy( data, &aint, sizeof( aint ));
1564             data += sizeof( aint );
1565             break;
1566
1567         case DIRPBIT_GID :
1568             aint = htonl(st->st_gid);
1569             memcpy( data, &aint, sizeof( aint ));
1570             data += sizeof( aint );
1571             break;
1572
1573         case DIRPBIT_ACCESS :
1574             accessmode(obj, vol, upath, &ma, dir , st);
1575
1576             *data++ = ma.ma_user;
1577             *data++ = ma.ma_world;
1578             *data++ = ma.ma_group;
1579             *data++ = ma.ma_owner;
1580             break;
1581
1582             /* Client has requested the ProDOS information block.
1583                Just pass back the same basic block for all
1584                directories. <shirsch@ibm.net> */
1585         case DIRPBIT_PDINFO :
1586             if (obj->afp_version >= 30) { /* UTF8 name */
1587                 utf8 = kTextEncodingUTF8;
1588                 if (dir->d_m_name) /* root of parent can have a null name */
1589                     utf_nameoff = data;
1590                 else
1591                     memset(data, 0, sizeof(uint16_t));
1592                 data += sizeof( uint16_t );
1593                 aint = 0;
1594                 memcpy(data, &aint, sizeof( aint ));
1595                 data += sizeof( aint );
1596             }
1597             else { /* ProDOS Info Block */
1598                 *data++ = 0x0f;
1599                 *data++ = 0;
1600                 ashort = htons( 0x0200 );
1601                 memcpy( data, &ashort, sizeof( ashort ));
1602                 data += sizeof( ashort );
1603                 memset( data, 0, sizeof( ashort ));
1604                 data += sizeof( ashort );
1605             }
1606             break;
1607
1608         case DIRPBIT_UNIXPR :
1609             /* accessmode may change st_mode with ACLs */
1610             accessmode(obj, vol, upath, &ma, dir, st);
1611
1612             aint = htonl(st->st_uid);
1613             memcpy( data, &aint, sizeof( aint ));
1614             data += sizeof( aint );
1615             aint = htonl(st->st_gid);
1616             memcpy( data, &aint, sizeof( aint ));
1617             data += sizeof( aint );
1618
1619             aint = st->st_mode;
1620             aint = htonl ( aint & ~S_ISGID );  /* Remove SGID, OSX doesn't like it ... */
1621             memcpy( data, &aint, sizeof( aint ));
1622             data += sizeof( aint );
1623
1624             *data++ = ma.ma_user;
1625             *data++ = ma.ma_world;
1626             *data++ = ma.ma_group;
1627             *data++ = ma.ma_owner;
1628             break;
1629
1630         default :
1631             if ( isad ) {
1632                 ad_close(&ad, ADFLAGS_HF);
1633             }
1634             return( AFPERR_BITMAP );
1635         }
1636         bitmap = bitmap>>1;
1637         bit++;
1638     }
1639     if ( l_nameoff ) {
1640         ashort = htons( data - buf );
1641         memcpy( l_nameoff, &ashort, sizeof( ashort ));
1642         data = set_name(vol, data, pdid, cfrombstr(dir->d_m_name), dir->d_did, 0);
1643     }
1644     if ( utf_nameoff ) {
1645         ashort = htons( data - buf );
1646         memcpy( utf_nameoff, &ashort, sizeof( ashort ));
1647         data = set_name(vol, data, pdid, cfrombstr(dir->d_m_name), dir->d_did, utf8);
1648     }
1649     if ( isad ) {
1650         ad_close(&ad, ADFLAGS_HF);
1651     }
1652     *buflen = data - buf;
1653     return( AFP_OK );
1654 }
1655
1656 /* ----------------------------- */
1657 int path_error(struct path *path, int error)
1658 {
1659 /* - a dir with access error
1660  * - no error it's a file
1661  * - file not found
1662  */
1663     if (path_isadir(path))
1664         return afp_errno;
1665     if (path->st_valid && path->st_errno)
1666         return error;
1667     return AFPERR_BADTYPE ;
1668 }
1669
1670 /* ----------------------------- */
1671 int afp_setdirparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1672 {
1673     struct vol  *vol;
1674     struct dir  *dir;
1675     struct path *path;
1676     uint16_t   vid, bitmap;
1677     uint32_t   did;
1678     int     rc;
1679
1680     *rbuflen = 0;
1681     ibuf += 2;
1682     memcpy( &vid, ibuf, sizeof( vid ));
1683     ibuf += sizeof( vid );
1684
1685     if (NULL == ( vol = getvolbyvid( vid )) ) {
1686         return( AFPERR_PARAM );
1687     }
1688
1689     if (vol->v_flags & AFPVOL_RO)
1690         return AFPERR_VLOCK;
1691
1692     memcpy( &did, ibuf, sizeof( did ));
1693     ibuf += sizeof( int );
1694
1695     if (NULL == ( dir = dirlookup( vol, did )) ) {
1696         return afp_errno;
1697     }
1698
1699     memcpy( &bitmap, ibuf, sizeof( bitmap ));
1700     bitmap = ntohs( bitmap );
1701     ibuf += sizeof( bitmap );
1702
1703     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1704         return get_afp_errno(AFPERR_NOOBJ);
1705     }
1706
1707     if ( *path->m_name != '\0' ) {
1708         rc = path_error(path, AFPERR_NOOBJ);
1709         /* maybe we are trying to set perms back */
1710         if (rc != AFPERR_ACCESS)
1711             return rc;
1712     }
1713
1714     /*
1715      * If ibuf is odd, make it even.
1716      */
1717     if ((u_long)ibuf & 1 ) {
1718         ibuf++;
1719     }
1720
1721     if (AFP_OK == ( rc = setdirparams(vol, path, bitmap, ibuf )) ) {
1722         setvoltime(obj, vol );
1723     }
1724     return( rc );
1725 }
1726
1727 /*
1728  * assume path == '\0' eg. it's a directory in canonical form
1729  */
1730 int setdirparams(struct vol *vol, struct path *path, uint16_t d_bitmap, char *buf )
1731 {
1732     struct maccess  ma;
1733     struct adouble  ad;
1734     struct utimbuf      ut;
1735     struct timeval      tv;
1736
1737     char                *upath;
1738     struct dir          *dir;
1739     int         bit, isad = 0;
1740     int                 cdate, bdate;
1741     int                 owner, group;
1742     uint16_t       ashort, bshort, oshort;
1743     int                 err = AFP_OK;
1744     int                 change_mdate = 0;
1745     int                 change_parent_mdate = 0;
1746     int                 newdate = 0;
1747     uint16_t           bitmap = d_bitmap;
1748     u_char              finder_buf[32];
1749     uint32_t       upriv;
1750     mode_t              mpriv = 0;
1751     bool                set_upriv = false, set_maccess = false;
1752
1753     LOG(log_debug, logtype_afpd, "setdirparams(\"%s\", bitmap: %02x)", path->u_name, bitmap);
1754
1755     bit = 0;
1756     upath = path->u_name;
1757     dir   = path->d_dir;
1758     while ( bitmap != 0 ) {
1759         while (( bitmap & 1 ) == 0 ) {
1760             bitmap = bitmap>>1;
1761             bit++;
1762         }
1763
1764         switch( bit ) {
1765         case DIRPBIT_ATTR :
1766             change_mdate = 1;
1767             memcpy( &ashort, buf, sizeof( ashort ));
1768             buf += sizeof( ashort );
1769             break;
1770         case DIRPBIT_CDATE :
1771             change_mdate = 1;
1772             memcpy(&cdate, buf, sizeof(cdate));
1773             buf += sizeof( cdate );
1774             break;
1775         case DIRPBIT_MDATE :
1776             memcpy(&newdate, buf, sizeof(newdate));
1777             buf += sizeof( newdate );
1778             break;
1779         case DIRPBIT_BDATE :
1780             change_mdate = 1;
1781             memcpy(&bdate, buf, sizeof(bdate));
1782             buf += sizeof( bdate );
1783             break;
1784         case DIRPBIT_FINFO :
1785             change_mdate = 1;
1786             memcpy( finder_buf, buf, 32 );
1787             buf += 32;
1788             break;
1789         case DIRPBIT_UID :  /* What kind of loser mounts as root? */
1790             memcpy( &owner, buf, sizeof(owner));
1791             buf += sizeof( owner );
1792             break;
1793         case DIRPBIT_GID :
1794             memcpy( &group, buf, sizeof( group ));
1795             buf += sizeof( group );
1796             break;
1797         case DIRPBIT_ACCESS :
1798             set_maccess = true;
1799             change_mdate = 1;
1800             ma.ma_user = *buf++;
1801             ma.ma_world = *buf++;
1802             ma.ma_group = *buf++;
1803             ma.ma_owner = *buf++;
1804             mpriv = mtoumode( &ma ) | vol->v_dperm;
1805             break;
1806             /* Ignore what the client thinks we should do to the
1807                ProDOS information block.  Skip over the data and
1808                report nothing amiss. <shirsch@ibm.net> */
1809         case DIRPBIT_PDINFO :
1810             if (vol->v_obj->afp_version < 30) {
1811                 buf += 6;
1812             }
1813             else {
1814                 err = AFPERR_BITMAP;
1815                 bitmap = 0;
1816             }
1817             break;
1818         case DIRPBIT_UNIXPR :
1819             if (vol_unix_priv(vol)) {
1820                 set_upriv = true;
1821                 memcpy( &owner, buf, sizeof(owner)); /* FIXME need to change owner too? */
1822                 buf += sizeof( owner );
1823                 memcpy( &group, buf, sizeof( group ));
1824                 buf += sizeof( group );
1825
1826                 change_mdate = 1;
1827                 memcpy( &upriv, buf, sizeof( upriv ));
1828                 buf += sizeof( upriv );
1829                 upriv = ntohl (upriv) | vol->v_dperm;
1830                 break;
1831             }
1832             /* fall through */
1833         default :
1834             err = AFPERR_BITMAP;
1835             bitmap = 0;
1836             break;
1837         }
1838
1839         bitmap = bitmap>>1;
1840         bit++;
1841     }
1842
1843     if (d_bitmap & ((1<<DIRPBIT_ATTR) | (1<<DIRPBIT_CDATE) | (1<<DIRPBIT_BDATE) | (1<<DIRPBIT_FINFO))) {
1844         ad_init(&ad, vol);
1845         if (ad_open(&ad, upath, ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_CREATE | ADFLAGS_RDWR, 0777) != 0) {
1846             LOG(log_debug, logtype_afpd, "setdirparams(\"%s\", bitmap: %02x): need adouble", path->u_name, d_bitmap);
1847             return AFPERR_ACCESS;
1848         }
1849         if ((ad_get_MD_flags(&ad) & O_CREAT)) {
1850             ad_setname(&ad, cfrombstr(curdir->d_m_name));
1851         }
1852         isad = 1;
1853     }
1854
1855     bit = 0;
1856     bitmap = d_bitmap;
1857     while ( bitmap != 0 ) {
1858         while (( bitmap & 1 ) == 0 ) {
1859             bitmap = bitmap>>1;
1860             bit++;
1861         }
1862
1863         switch( bit ) {
1864         case DIRPBIT_ATTR :
1865             if (isad) {
1866                 ad_getattr(&ad, &bshort);
1867                 oshort = bshort;
1868                 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1869                     bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1870                 } else {
1871                     bshort &= ~ashort;
1872                 }
1873                 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1874                     change_parent_mdate = 1;
1875                 ad_setattr(&ad, bshort);
1876             }
1877             break;
1878         case DIRPBIT_CDATE :
1879             if (isad) {
1880                 ad_setdate(&ad, AD_DATE_CREATE, cdate);
1881             }
1882             break;
1883         case DIRPBIT_MDATE :
1884             break;
1885         case DIRPBIT_BDATE :
1886             if (isad) {
1887                 ad_setdate(&ad, AD_DATE_BACKUP, bdate);
1888             }
1889             break;
1890         case DIRPBIT_FINFO :
1891             if (isad) {
1892                 /* Fixes #2802236 */
1893                 uint16_t fflags;
1894                 memcpy(&fflags, finder_buf + FINDERINFO_FRFLAGOFF, sizeof(uint16_t));
1895                 fflags &= htons(~FINDERINFO_ISHARED);
1896                 memcpy(finder_buf + FINDERINFO_FRFLAGOFF, &fflags, sizeof(uint16_t));
1897                 /* #2802236 end */
1898
1899                 if (  dir->d_did == DIRDID_ROOT ) {
1900                     /*
1901                      * Alright, we admit it, this is *really* sick!
1902                      * The 4 bytes that we don't copy, when we're dealing
1903                      * with the root of a volume, are the directory's
1904                      * location information. This eliminates that annoying
1905                      * behavior one sees when mounting above another mount
1906                      * point.
1907                      */
1908                     memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 10 );
1909                     memcpy( ad_entry( &ad, ADEID_FINDERI ) + 14, finder_buf + 14, 18 );
1910                 } else {
1911                     memcpy( ad_entry( &ad, ADEID_FINDERI ), finder_buf, 32 );
1912                 }
1913             }
1914             break;
1915         case DIRPBIT_UID :  /* What kind of loser mounts as root? */
1916             if ( (dir->d_did == DIRDID_ROOT) &&
1917                  (setdeskowner(vol, ntohl(owner), -1 ) < 0)) {
1918                 err = set_dir_errors(path, "setdeskowner", errno);
1919                 if (isad && err == AFPERR_PARAM) {
1920                     err = AFP_OK; /* ???*/
1921                 }
1922                 else {
1923                     goto setdirparam_done;
1924                 }
1925             }
1926             if ( setdirowner(vol, upath, ntohl(owner), -1 ) < 0 ) {
1927                 err = set_dir_errors(path, "setdirowner", errno);
1928                 goto setdirparam_done;
1929             }
1930             break;
1931         case DIRPBIT_GID :
1932             if (dir->d_did == DIRDID_ROOT)
1933                 setdeskowner(vol, -1, ntohl(group) );
1934             if ( setdirowner(vol, upath, -1, ntohl(group) ) < 0 ) {
1935                 err = set_dir_errors(path, "setdirowner", errno);
1936                 goto setdirparam_done;
1937             }
1938             break;
1939         case DIRPBIT_ACCESS :
1940             break;
1941         case DIRPBIT_PDINFO :
1942             if (vol->v_obj->afp_version >= 30) {
1943                 err = AFPERR_BITMAP;
1944                 goto setdirparam_done;
1945             }
1946             break;
1947         case DIRPBIT_UNIXPR :
1948             if (!vol_unix_priv(vol)) {
1949                 err = AFPERR_BITMAP;
1950                 goto setdirparam_done;
1951             }
1952             break;
1953         default :
1954             err = AFPERR_BITMAP;
1955             goto setdirparam_done;
1956             break;
1957         }
1958
1959         bitmap = bitmap>>1;
1960         bit++;
1961     }
1962
1963 setdirparam_done:
1964     if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1965         newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1966     }
1967     if (newdate) {
1968         if (isad)
1969             ad_setdate(&ad, AD_DATE_MODIFY, newdate);
1970         ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1971         utime(upath, &ut);
1972     }
1973
1974     if (isad) {
1975         if (path->st_valid && !path->st_errno) {
1976             struct stat *st = &path->st;
1977             if (dir && dir->d_pdid) {
1978                 ad_setid(&ad, st->st_dev, st->st_ino,  dir->d_did, dir->d_pdid, vol->v_stamp);
1979             }
1980         }
1981         if (ad_flush(&ad) != 0) {
1982             switch (errno) {
1983             case EACCES:
1984                 err = AFPERR_ACCESS;
1985                 break;
1986             default:
1987                 err = AFPERR_MISC;
1988                 break;
1989            }
1990         }
1991         ad_close(&ad, ADFLAGS_HF);
1992     }
1993
1994     if (err == AFP_OK) {
1995         if (set_maccess == true) {
1996             if (dir->d_did == DIRDID_ROOT) {
1997                 setdeskmode(vol, mpriv);
1998                 if (!dir_rx_set(mpriv)) {
1999                     /* we can't remove read and search for owner on volume root */
2000                     err = AFPERR_ACCESS;
2001                     goto setprivdone;
2002                 }
2003             }
2004             if (setdirunixmode(vol, upath, mpriv) < 0) {
2005                 LOG(log_info, logtype_afpd, "setdirparams(\"%s\"): setdirunixmode: %s",
2006                     fullpathname(upath), strerror(errno));
2007                 err = set_dir_errors(path, "setdirmode", errno);
2008             }
2009         }
2010         if ((set_upriv == true) && vol_unix_priv(vol)) {
2011             if (dir->d_did == DIRDID_ROOT) {
2012                 if (!dir_rx_set(upriv)) {
2013                     /* we can't remove read and search for owner on volume root */
2014                     err = AFPERR_ACCESS;
2015                     goto setprivdone;
2016                 }
2017                 setdeskowner(vol, -1, ntohl(group));
2018                 setdeskmode(vol, upriv);
2019             }
2020
2021             if (setdirowner(vol, upath, -1, ntohl(group)) < 0) {
2022                 LOG(log_info, logtype_afpd, "setdirparams(\"%s\"): setdirowner: %s",
2023                     fullpathname(upath), strerror(errno));
2024                 err = set_dir_errors(path, "setdirowner", errno);
2025                 goto setprivdone;
2026             }
2027
2028             if (setdirunixmode(vol, upath, upriv) < 0) {
2029                 LOG(log_info, logtype_afpd, "setdirparams(\"%s\"): setdirunixmode: %s",
2030                     fullpathname(upath), strerror(errno));
2031                 err = set_dir_errors(path, "setdirunixmode", errno);
2032             }
2033         }
2034     }
2035
2036 setprivdone:
2037     if (change_parent_mdate && dir->d_did != DIRDID_ROOT
2038         && gettimeofday(&tv, NULL) == 0) {
2039         if (movecwd(vol, dirlookup(vol, dir->d_pdid)) == 0) {
2040             newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
2041             /* be careful with bitmap because now dir is null */
2042             bitmap = 1<<DIRPBIT_MDATE;
2043             setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
2044             /* should we reset curdir ?*/
2045         }
2046     }
2047     return err;
2048 }
2049
2050 int afp_syncdir(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2051 {
2052 #ifdef HAVE_DIRFD
2053     DIR                  *dp;
2054 #endif
2055     int                  dfd;
2056     struct vol           *vol;
2057     struct dir           *dir;
2058     uint32_t            did;
2059     uint16_t            vid;
2060
2061     *rbuflen = 0;
2062     ibuf += 2;
2063
2064     memcpy( &vid, ibuf, sizeof( vid ));
2065     ibuf += sizeof( vid );
2066     if (NULL == (vol = getvolbyvid( vid )) ) {
2067         return( AFPERR_PARAM );
2068     }
2069
2070     memcpy( &did, ibuf, sizeof( did ));
2071     ibuf += sizeof( did );
2072
2073     /*
2074      * Here's the deal:
2075      * if it's CNID 2 our only choice to meet the specs is call sync.
2076      * For any other CNID just sync that dir. To my knowledge the
2077      * intended use of FPSyncDir is to sync the volume so all we're
2078      * ever going to see here is probably CNID 2. Anyway, we' prepared.
2079      */
2080
2081     if ( ntohl(did) == 2 ) {
2082         sync();
2083     } else {
2084         if (NULL == ( dir = dirlookup( vol, did )) ) {
2085             return afp_errno; /* was AFPERR_NOOBJ */
2086         }
2087
2088         if (movecwd( vol, dir ) < 0 )
2089             return ( AFPERR_NOOBJ );
2090
2091         /*
2092          * Assuming only OSens that have dirfd also may require fsyncing directories
2093          * in order to flush metadata e.g. Linux.
2094          */
2095
2096 #ifdef HAVE_DIRFD
2097         if (NULL == ( dp = opendir( "." )) ) {
2098             switch( errno ) {
2099             case ENOENT :
2100                 return( AFPERR_NOOBJ );
2101             case EACCES :
2102                 return( AFPERR_ACCESS );
2103             default :
2104                 return( AFPERR_PARAM );
2105             }
2106         }
2107
2108         LOG(log_debug, logtype_afpd, "afp_syncdir: dir: '%s'", dir->d_u_name);
2109
2110         dfd = dirfd( dp );
2111         if ( fsync ( dfd ) < 0 )
2112             LOG(log_error, logtype_afpd, "afp_syncdir(%s):  %s",
2113                 dir->d_u_name, strerror(errno) );
2114         closedir(dp); /* closes dfd too */
2115 #endif
2116
2117         if ( -1 == (dfd = open(vol->ad_path(".", ADFLAGS_DIR), O_RDWR))) {
2118             switch( errno ) {
2119             case ENOENT:
2120                 return( AFPERR_NOOBJ );
2121             case EACCES:
2122                 return( AFPERR_ACCESS );
2123             default:
2124                 return( AFPERR_PARAM );
2125             }
2126         }
2127
2128         LOG(log_debug, logtype_afpd, "afp_syncdir: ad-file: '%s'",
2129             vol->ad_path(".", ADFLAGS_DIR) );
2130
2131         if ( fsync(dfd) < 0 )
2132             LOG(log_error, logtype_afpd, "afp_syncdir(%s): %s",
2133                 vol->ad_path(cfrombstr(dir->d_u_name), ADFLAGS_DIR), strerror(errno) );
2134         close(dfd);
2135     }
2136
2137     return ( AFP_OK );
2138 }
2139
2140 int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2141 {
2142     struct adouble  ad;
2143     struct vol      *vol;
2144     struct dir      *dir;
2145     char        *upath;
2146     struct path         *s_path;
2147     uint32_t       did;
2148     uint16_t       vid;
2149     int                 err;
2150
2151     *rbuflen = 0;
2152     ibuf += 2;
2153
2154     memcpy( &vid, ibuf, sizeof( vid ));
2155     ibuf += sizeof( vid );
2156     if (NULL == ( vol = getvolbyvid( vid )) ) {
2157         return( AFPERR_PARAM );
2158     }
2159
2160     if (vol->v_flags & AFPVOL_RO)
2161         return AFPERR_VLOCK;
2162
2163     memcpy( &did, ibuf, sizeof( did ));
2164     ibuf += sizeof( did );
2165     if (NULL == ( dir = dirlookup( vol, did )) ) {
2166         return afp_errno; /* was AFPERR_NOOBJ */
2167     }
2168     /* for concurrent access we need to be sure we are not in the
2169      * folder we want to create...
2170      */
2171     movecwd(vol, dir);
2172
2173     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
2174         return get_afp_errno(AFPERR_PARAM);
2175     }
2176     /* cname was able to move curdir to it! */
2177     if (*s_path->m_name == '\0')
2178         return AFPERR_EXIST;
2179
2180     upath = s_path->u_name;
2181
2182     if (AFP_OK != (err = netatalk_mkdir(vol, upath))) {
2183         return err;
2184     }
2185
2186     if (of_stat(vol, s_path) < 0) {
2187         return AFPERR_MISC;
2188     }
2189
2190     curdir->d_offcnt++;
2191
2192     if ((dir = dir_add(vol, curdir, s_path, strlen(s_path->u_name))) == NULL) {
2193         return AFPERR_MISC;
2194     }
2195
2196     if ( movecwd( vol, dir ) < 0 ) {
2197         return( AFPERR_PARAM );
2198     }
2199
2200     ad_init(&ad, vol);
2201     if (ad_open(&ad, ".", ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_CREATE | ADFLAGS_RDWR, 0777) < 0)  {
2202         return( AFPERR_ACCESS );
2203     }
2204     ad_setname(&ad, s_path->m_name);
2205     ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
2206
2207     fce_register(FCE_DIR_CREATE, bdata(curdir->d_fullpath), NULL, fce_dir);
2208
2209     ad_flush(&ad);
2210     ad_close(&ad, ADFLAGS_HF);
2211
2212     memcpy( rbuf, &dir->d_did, sizeof( uint32_t ));
2213     *rbuflen = sizeof( uint32_t );
2214     setvoltime(obj, vol );
2215     return( AFP_OK );
2216 }
2217
2218 /*
2219  * dst       new unix filename (not a pathname)
2220  * newname   new mac name
2221  * newparent curdir
2222  * dirfd     -1 means ignore dirfd (or use AT_FDCWD), otherwise src is relative to dirfd
2223  */
2224 int renamedir(struct vol *vol,
2225               int dirfd,
2226               char *src,
2227               char *dst,
2228               struct dir *dir,
2229               struct dir *newparent,
2230               char *newname)
2231 {
2232     struct adouble  ad;
2233     int             err;
2234
2235     /* existence check moved to afp_moveandrename */
2236     if ( unix_rename(dirfd, src, -1, dst ) < 0 ) {
2237         switch ( errno ) {
2238         case ENOENT :
2239             return( AFPERR_NOOBJ );
2240         case EACCES :
2241             return( AFPERR_ACCESS );
2242         case EROFS:
2243             return AFPERR_VLOCK;
2244         case EINVAL:
2245             /* tried to move directory into a subdirectory of itself */
2246             return AFPERR_CANTMOVE;
2247         case EXDEV:
2248             /* this needs to copy and delete. bleah. that means we have
2249              * to deal with entire directory hierarchies. */
2250             if ((err = copydir(vol, newparent, dirfd, src, dst)) < 0) {
2251                 deletedir(vol, -1, dst);
2252                 return err;
2253             }
2254             if ((err = deletedir(vol, dirfd, src)) < 0)
2255                 return err;
2256             break;
2257         default :
2258             return( AFPERR_PARAM );
2259         }
2260     }
2261
2262     vol->vfs->vfs_renamedir(vol, dirfd, src, dst);
2263
2264     ad_init(&ad, vol);
2265
2266     if (ad_open(&ad, dst, ADFLAGS_HF | ADFLAGS_DIR | ADFLAGS_RDWR) == 0) {
2267         ad_setname(&ad, newname);
2268         ad_flush(&ad);
2269         ad_close(&ad, ADFLAGS_HF);
2270     }
2271
2272     return( AFP_OK );
2273 }
2274
2275 /* delete an empty directory */
2276 int deletecurdir(struct vol *vol)
2277 {
2278     struct dirent *de;
2279     struct stat st;
2280     struct dir  *fdir, *pdir;
2281     DIR *dp;
2282     struct adouble  ad;
2283     uint16_t       ashort;
2284     int err;
2285
2286     if ((pdir = dirlookup(vol, curdir->d_pdid)) == NULL) {
2287         return( AFPERR_ACCESS );
2288     }
2289
2290     fdir = curdir;
2291
2292     ad_init(&ad, vol);
2293     /* we never want to create a resource fork here, we are going to delete it */
2294     if ( ad_metadata( ".", ADFLAGS_DIR, &ad) == 0 ) {
2295
2296         ad_getattr(&ad, &ashort);
2297         ad_close(&ad, ADFLAGS_HF);
2298         if ((ashort & htons(ATTRBIT_NODELETE))) {
2299             return  AFPERR_OLOCK;
2300         }
2301     }
2302     err = vol->vfs->vfs_deletecurdir(vol);
2303     if (err) {
2304         LOG(log_error, logtype_afpd, "deletecurdir: error deleting .AppleDouble in \"%s\"",
2305             cfrombstr(curdir->d_fullpath));
2306         return err;
2307     }
2308
2309     /* now get rid of dangling symlinks */
2310     if ((dp = opendir("."))) {
2311         while ((de = readdir(dp))) {
2312             /* skip this and previous directory */
2313             if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
2314                 continue;
2315
2316             /* bail if it's not a symlink */
2317             if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) {
2318                 LOG(log_error, logtype_afpd, "deletecurdir(\"%s\"): not empty",
2319                     bdata(curdir->d_fullpath));
2320                 closedir(dp);
2321                 return AFPERR_DIRNEMPT;
2322             }
2323
2324             if ((err = netatalk_unlink(de->d_name))) {
2325                 closedir(dp);
2326                 return err;
2327             }
2328         }
2329     }
2330
2331     if (movecwd(vol, pdir) < 0) {
2332         err = afp_errno;
2333         goto delete_done;
2334     }
2335
2336     LOG(log_debug, logtype_afpd, "deletecurdir: moved to \"%s\"",
2337         cfrombstr(curdir->d_fullpath));
2338
2339     err = netatalk_rmdir_all_errors(-1, cfrombstr(fdir->d_u_name));
2340     if ( err ==  AFP_OK || err == AFPERR_NOOBJ) {
2341         AFP_CNID_START("cnid_delete");
2342         cnid_delete(vol->v_cdb, fdir->d_did);
2343         AFP_CNID_DONE();
2344         dir_remove( vol, fdir );
2345     } else {
2346         LOG(log_error, logtype_afpd, "deletecurdir(\"%s\"): netatalk_rmdir_all_errors error",
2347             cfrombstr(curdir->d_fullpath));
2348     }
2349
2350 delete_done:
2351     if (dp) {
2352         /* inode is used as key for cnid.
2353          * Close the descriptor only after cnid_delete
2354          * has been called.
2355          */
2356         closedir(dp);
2357     }
2358     return err;
2359 }
2360
2361 int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2362 {
2363     struct passwd   *pw;
2364     struct group    *gr;
2365     char        *name;
2366     uint32_t           id;
2367     int         len, sfunc;
2368     int         utf8 = 0;
2369
2370     ibuf++;
2371     sfunc = (unsigned char) *ibuf++;
2372     *rbuflen = 0;
2373
2374     if (sfunc >= 3 && sfunc <= 6) {
2375         if (obj->afp_version < 30) {
2376             return( AFPERR_PARAM );
2377         }
2378         utf8 = 1;
2379     }
2380
2381     switch ( sfunc ) {
2382     case 1 :
2383     case 3 :/* unicode */
2384         memcpy( &id, ibuf, sizeof( id ));
2385         id = ntohl(id);
2386         if ( id != 0 ) {
2387             if (( pw = getpwuid( id )) == NULL ) {
2388                 return( AFPERR_NOITEM );
2389             }
2390             len = convert_string_allocate( obj->options.unixcharset, ((!utf8)?obj->options.maccharset:CH_UTF8_MAC),
2391                                            pw->pw_name, -1, &name);
2392         } else {
2393             len = 0;
2394             name = NULL;
2395         }
2396         break;
2397     case 2 :
2398     case 4 : /* unicode */
2399         memcpy( &id, ibuf, sizeof( id ));
2400         id = ntohl(id);
2401         if ( id != 0 ) {
2402             if (NULL == ( gr = (struct group *)getgrgid( id ))) {
2403                 return( AFPERR_NOITEM );
2404             }
2405             len = convert_string_allocate( obj->options.unixcharset, (!utf8)?obj->options.maccharset:CH_UTF8_MAC,
2406                                            gr->gr_name, -1, &name);
2407         } else {
2408             len = 0;
2409             name = NULL;
2410         }
2411         break;
2412
2413     case 5 : /* UUID -> username */
2414     case 6 : /* UUID -> groupname */
2415         if ((obj->afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2416             return AFPERR_PARAM;
2417         LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request");
2418         uuidtype_t type;
2419         len = getnamefromuuid((unsigned char*) ibuf, &name, &type);
2420         if (len != 0)       /* its a error code, not len */
2421             return AFPERR_NOITEM;
2422         switch (type) {
2423         case UUID_USER:
2424             if (( pw = getpwnam( name )) == NULL )
2425                 return( AFPERR_NOITEM );
2426             LOG(log_debug, logtype_afpd, "afp_mapid: name:%s -> uid:%d", name, pw->pw_uid);
2427             id = htonl(UUID_USER);
2428             memcpy( rbuf, &id, sizeof( id ));
2429             id = htonl( pw->pw_uid);
2430             rbuf += sizeof( id );
2431             memcpy( rbuf, &id, sizeof( id ));
2432             rbuf += sizeof( id );
2433             *rbuflen = 2 * sizeof( id );
2434             break;
2435         case UUID_GROUP:
2436             if (( gr = getgrnam( name )) == NULL )
2437                 return( AFPERR_NOITEM );
2438             LOG(log_debug, logtype_afpd, "afp_mapid: group:%s -> gid:%d", name, gr->gr_gid);
2439             id = htonl(UUID_GROUP);
2440             memcpy( rbuf, &id, sizeof( id ));
2441             rbuf += sizeof( id );
2442             id = htonl( gr->gr_gid);
2443             memcpy( rbuf, &id, sizeof( id ));
2444             rbuf += sizeof( id );
2445             *rbuflen = 2 * sizeof( id );
2446             break;
2447         default:
2448             return AFPERR_MISC;
2449         }
2450         break;
2451
2452     default :
2453         return( AFPERR_PARAM );
2454     }
2455
2456     if (name)
2457         len = strlen( name );
2458
2459     if (utf8) {
2460         uint16_t tp = htons(len);
2461         memcpy(rbuf, &tp, sizeof(tp));
2462         rbuf += sizeof(tp);
2463         *rbuflen += 2;
2464     }
2465     else {
2466         *rbuf++ = len;
2467         *rbuflen += 1;
2468     }
2469     if ( len > 0 ) {
2470         memcpy( rbuf, name, len );
2471     }
2472     *rbuflen += len;
2473     if (name)
2474         free(name);
2475     return( AFP_OK );
2476 }
2477
2478 int afp_mapname(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
2479 {
2480     struct passwd   *pw;
2481     struct group    *gr;
2482     int             len, sfunc;
2483     uint32_t       id;
2484     uint16_t       ulen;
2485
2486     ibuf++;
2487     sfunc = (unsigned char) *ibuf++;
2488     *rbuflen = 0;
2489     LOG(log_debug, logtype_afpd, "afp_mapname: sfunc: %d", sfunc);
2490     switch ( sfunc ) {
2491     case 1 :
2492     case 2 : /* unicode */
2493         if (obj->afp_version < 30) {
2494             return( AFPERR_PARAM );
2495         }
2496         memcpy(&ulen, ibuf, sizeof(ulen));
2497         len = ntohs(ulen);
2498         ibuf += 2;
2499         LOG(log_debug, logtype_afpd, "afp_mapname: alive");
2500         break;
2501     case 3 :
2502     case 4 :
2503         len = (unsigned char) *ibuf++;
2504         break;
2505     case 5 : /* username -> UUID  */
2506     case 6 : /* groupname -> UUID */
2507         if ((obj->afp_version < 32) || !(obj->options.flags & OPTION_UUID ))
2508             return AFPERR_PARAM;
2509         memcpy(&ulen, ibuf, sizeof(ulen));
2510         len = ntohs(ulen);
2511         ibuf += 2;
2512         break;
2513     default :
2514         return( AFPERR_PARAM );
2515     }
2516
2517     ibuf[ len ] = '\0';
2518
2519     if ( len == 0 )
2520         return AFPERR_PARAM;
2521     else {
2522         switch ( sfunc ) {
2523         case 1 : /* unicode */
2524         case 3 :
2525             if (NULL == ( pw = (struct passwd *)getpwnam( ibuf )) ) {
2526                 return( AFPERR_NOITEM );
2527             }
2528             id = pw->pw_uid;
2529             id = htonl(id);
2530             memcpy( rbuf, &id, sizeof( id ));
2531             *rbuflen = sizeof( id );
2532             break;
2533
2534         case 2 : /* unicode */
2535         case 4 :
2536             LOG(log_debug, logtype_afpd, "afp_mapname: getgrnam for name: %s",ibuf);
2537             if (NULL == ( gr = (struct group *)getgrnam( ibuf ))) {
2538                 return( AFPERR_NOITEM );
2539             }
2540             id = gr->gr_gid;
2541             LOG(log_debug, logtype_afpd, "afp_mapname: getgrnam for name: %s -> id: %d",ibuf, id);
2542             id = htonl(id);
2543             memcpy( rbuf, &id, sizeof( id ));
2544             *rbuflen = sizeof( id );
2545             break;
2546         case 5 :        /* username -> UUID */
2547             LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2548             if (0 != getuuidfromname(ibuf, UUID_USER, (unsigned char *)rbuf))
2549                 return AFPERR_NOITEM;
2550             *rbuflen = UUID_BINSIZE;
2551             break;
2552         case 6 :        /* groupname -> UUID */
2553             LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf);
2554             if (0 != getuuidfromname(ibuf, UUID_GROUP, (unsigned char *)rbuf))
2555                 return AFPERR_NOITEM;
2556             *rbuflen = UUID_BINSIZE;
2557             break;
2558         }
2559     }
2560     return( AFP_OK );
2561 }
2562
2563 /* ------------------------------------
2564    variable DID support
2565 */
2566 int afp_closedir(AFPObj *obj _U_, char *ibuf _U_, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2567 {
2568 #if 0
2569     struct vol   *vol;
2570     struct dir   *dir;
2571     uint16_t    vid;
2572     uint32_t    did;
2573 #endif /* 0 */
2574
2575     *rbuflen = 0;
2576
2577     /* do nothing as dids are static for the life of the process. */
2578 #if 0
2579     ibuf += 2;
2580
2581     memcpy(&vid,  ibuf, sizeof( vid ));
2582     ibuf += sizeof( vid );
2583     if (( vol = getvolbyvid( vid )) == NULL ) {
2584         return( AFPERR_PARAM );
2585     }
2586
2587     memcpy( &did, ibuf, sizeof( did ));
2588     ibuf += sizeof( did );
2589     if (( dir = dirlookup( vol, did )) == NULL ) {
2590         return( AFPERR_PARAM );
2591     }
2592
2593     /* dir_remove -- deletedid */
2594 #endif /* 0 */
2595
2596     return AFP_OK;
2597 }
2598
2599 /* did creation gets done automatically
2600  * there's a pb again with case but move it to cname
2601  */
2602 int afp_opendir(AFPObj *obj _U_, char *ibuf, size_t ibuflen  _U_, char *rbuf, size_t *rbuflen)
2603 {
2604     struct vol      *vol;
2605     struct dir      *parentdir;
2606     struct path     *path;
2607     uint32_t       did;
2608     uint16_t       vid;
2609
2610     *rbuflen = 0;
2611     ibuf += 2;
2612
2613     memcpy(&vid, ibuf, sizeof(vid));
2614     ibuf += sizeof( vid );
2615
2616     if (NULL == ( vol = getvolbyvid( vid )) ) {
2617         return( AFPERR_PARAM );
2618     }
2619
2620     memcpy(&did, ibuf, sizeof(did));
2621     ibuf += sizeof(did);
2622
2623     if (NULL == ( parentdir = dirlookup( vol, did )) ) {
2624         return afp_errno;
2625     }
2626
2627     if (NULL == ( path = cname( vol, parentdir, &ibuf )) ) {
2628         return get_afp_errno(AFPERR_PARAM);
2629     }
2630
2631     if ( *path->m_name != '\0' ) {
2632         return path_error(path, AFPERR_NOOBJ);
2633     }
2634
2635     if ( !path->st_valid && of_stat(vol, path) < 0 ) {
2636         return( AFPERR_NOOBJ );
2637     }
2638     if ( path->st_errno ) {
2639         return( AFPERR_NOOBJ );
2640     }
2641
2642     memcpy(rbuf, &curdir->d_did, sizeof(curdir->d_did));
2643     *rbuflen = sizeof(curdir->d_did);
2644     return AFP_OK;
2645 }