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