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