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