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