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