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