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