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