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