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