X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=etc%2Fafpd%2Fdirectory.c;h=467bc62d89e936c2cf69988db42f66b27ea3fe36;hb=6d60f95c388da5f8f901d67e0018cf4896e6c6dc;hp=db531cf78bee798f3813054e4e6c1da7654fa548;hpb=0c8cd9181b742fd59e974cc888c4775565e1f381;p=netatalk.git diff --git a/etc/afpd/directory.c b/etc/afpd/directory.c index db531cf7..488f900a 100644 --- a/etc/afpd/directory.c +++ b/etc/afpd/directory.c @@ -1,6 +1,4 @@ /* - * $Id: directory.c,v 1.131.2.4 2010-02-01 16:13:52 franklahm Exp $ - * * Copyright (c) 1990,1993 Regents of The University of Michigan. * All Rights Reserved. See COPYRIGHT. */ @@ -30,6 +28,7 @@ #include #include #include +#include #include "directory.h" #include "dircache.h" @@ -43,17 +42,10 @@ #include "mangle.h" #include "hash.h" -#ifdef HAVE_NFSv4_ACLS -extern void addir_inherit_acl(const struct vol *vol); -#endif - /* * FIXMEs, loose ends after the dircache rewrite: - * o case-insensitivity is gone - * o setdirparams doesn't change parent mdate anymore - * o catsearch doesn't work, see FIXMEs in catsearch.c - * o curdir per volume caching is gone - * o directory offspring count calculation probably broken + * o merge dircache_search_by_name and dir_add ?? + * o case-insensitivity is gone from cname */ @@ -62,9 +54,10 @@ extern void addir_inherit_acl(const struct vol *vol); ******************************************************************************************/ int afp_errno; +/* As long as directory.c hasn't got its own init call, this get initialized in dircache_init */ struct dir rootParent = { NULL, NULL, NULL, NULL, /* path, d_m_name, d_u_name, d_m_name_ucs2 */ - NULL, NULL, 0, 0, /* qidx_node, d_ofork, ctime, d_flags */ + NULL, 0, 0, /* qidx_node, ctime, d_flags */ 0, 0, 0, 0 /* pdid, did, offcnt, d_vid */ }; struct dir *curdir = &rootParent; @@ -73,10 +66,23 @@ struct path Cur_Path = { "", /* mac name */ ".", /* unix name */ 0, /* id */ - NULL,/* struct dir */ + NULL,/* struct dir * */ 0, /* stat is not set */ + 0, /* errno */ + {0} /* struct stat */ }; +/* + * dir_remove queues struct dirs to be freed here. We can't just delete them immeidately + * eg in dircache_search_by_id, because a caller somewhere up the stack might be + * referencing it. + * So instead: + * - we mark it as invalid by setting d_did to CNID_INVALID (ie 0) + * - queue it in "invalid_dircache_entries" queue + * - which is finally freed at the end of every AFP func in afp_dsi.c. + */ +q_t *invalid_dircache_entries; + /******************************************************************************************* * Locals @@ -86,9 +92,24 @@ struct path Cur_Path = { /* ------------------------- appledouble mkdir afp error code. */ -static int netatalk_mkdir(const char *name) +static int netatalk_mkdir(const struct vol *vol, const char *name) { - if (ad_mkdir(name, DIRBITS | 0777) < 0) { + int ret; + struct stat st; + + if (vol->v_flags & AFPVOL_UNIX_PRIV) { + if (lstat(".", &st) < 0) + return AFPERR_MISC; + int mode = (DIRBITS & (~S_ISGID & st.st_mode)) | (0777 & ~vol->v_umask); + LOG(log_maxdebug, logtype_afpd, "netatalk_mkdir(\"%s\") {parent mode: %04o, vol umask: %04o}", + name, st.st_mode, vol->v_umask); + + ret = mkdir(name, mode); + } else { + ret = ad_mkdir(name, DIRBITS | 0777); + } + + if (ret < 0) { switch ( errno ) { case ENOENT : return( AFPERR_NOOBJ ); @@ -110,7 +131,7 @@ static int netatalk_mkdir(const char *name) } /* ------------------- */ -static int deletedir(char *dir) +static int deletedir(int dirfd, char *dir) { char path[MAXPATHLEN + 1]; DIR *dp; @@ -124,7 +145,7 @@ static int deletedir(char *dir) return AFPERR_PARAM; /* already gone */ - if ((dp = opendir(dir)) == NULL) + if ((dp = opendirat(dirfd, dir)) == NULL) return AFP_OK; strcpy(path, dir); @@ -141,13 +162,13 @@ static int deletedir(char *dir) break; } strcpy(path + len, de->d_name); - if (stat(path, &st)) { + if (lstatat(dirfd, path, &st)) { continue; } if (S_ISDIR(st.st_mode)) { - err = deletedir(path); + err = deletedir(dirfd, path); } else { - err = netatalk_unlink(path); + err = netatalk_unlinkat(dirfd, path); } } closedir(dp); @@ -155,13 +176,13 @@ static int deletedir(char *dir) /* okay. the directory is empty. delete it. note: we already got rid of .AppleDouble. */ if (err == AFP_OK) { - err = netatalk_rmdir(dir); + err = netatalk_rmdir(dirfd, dir); } return err; } /* do a recursive copy. */ -static int copydir(const struct vol *vol, char *src, char *dst) +static int copydir(const struct vol *vol, int dirfd, char *src, char *dst) { char spath[MAXPATHLEN + 1], dpath[MAXPATHLEN + 1]; DIR *dp; @@ -175,11 +196,11 @@ static int copydir(const struct vol *vol, char *src, char *dst) /* doesn't exist or the path is too long. */ if (((slen = strlen(src)) > sizeof(spath) - 2) || ((dlen = strlen(dst)) > sizeof(dpath) - 2) || - ((dp = opendir(src)) == NULL)) + ((dp = opendirat(dirfd, src)) == NULL)) return AFPERR_PARAM; /* try to create the destination directory */ - if (AFP_OK != (err = netatalk_mkdir(dst)) ) { + if (AFP_OK != (err = netatalk_mkdir(vol, dst)) ) { closedir(dp); return err; } @@ -207,7 +228,7 @@ static int copydir(const struct vol *vol, char *src, char *dst) } strcpy(spath + slen, de->d_name); - if (stat(spath, &st) == 0) { + if (lstatat(dirfd, spath, &st) == 0) { if (strlen(de->d_name) > drem) { err = AFPERR_PARAM; break; @@ -215,9 +236,9 @@ static int copydir(const struct vol *vol, char *src, char *dst) strcpy(dpath + dlen, de->d_name); if (S_ISDIR(st.st_mode)) { - if (AFP_OK != (err = copydir(vol, spath, dpath))) + if (AFP_OK != (err = copydir(vol, dirfd, spath, dpath))) goto copydir_done; - } else if (AFP_OK != (err = copyfile(vol, vol, spath, dpath, NULL, NULL))) { + } else if (AFP_OK != (err = copyfile(vol, vol, dirfd, spath, dpath, NULL, NULL))) { goto copydir_done; } else { @@ -229,7 +250,7 @@ static int copydir(const struct vol *vol, char *src, char *dst) } /* keep the same time stamp. */ - if (stat(src, &st) == 0) { + if (lstatat(dirfd, src, &st) == 0) { ut.actime = ut.modtime = st.st_mtime; utime(dst, &ut); } @@ -281,7 +302,7 @@ static int cname_mtouname(const struct vol *vol, const struct dir *dir, struct p { static char temp[ MAXPATHLEN + 1]; char *t; - cnid_t fileid; + cnid_t fileid = 0; if (afp_version >= 30) { if (toUTF8) { @@ -338,61 +359,55 @@ static int cname_mtouname(const struct vol *vol, const struct dir *dir, struct p * * The final movecwd in cname failed, possibly with EPERM or ENOENT. We: * 1. move cwd into parent dir (we're often already there, but not always) + * 2. set struct path to the dirname + * 3. in case of + * AFPERR_ACCESS: the dir is there, we just cant chdir into it + * AFPERR_NOOBJ: the dir was there when we stated it in cname, so we have a race + * 4. indicate there's no dir for this path + * 5. remove the dir */ static struct path *path_from_dir(struct vol *vol, struct dir *dir, struct path *ret) { - /* - * it's tricky: movecwd failed some of dir path are not there anymore. - * FIXME: Is it true with other errors? - */ if (dir->d_did == DIRDID_ROOT_PARENT || dir->d_did == DIRDID_ROOT) return NULL; switch (afp_errno) { case AFPERR_ACCESS: - if (movecwd( vol, dirlookup(vol, dir->d_pdid)) < 0 ) + if (movecwd( vol, dirlookup(vol, dir->d_pdid)) < 0 ) /* 1 */ return NULL; - memcpy(ret->m_name, cfrombstring(dir->d_m_name), blength(dir->d_m_name) + 1); + memcpy(ret->m_name, cfrombstr(dir->d_m_name), blength(dir->d_m_name) + 1); /* 3 */ if (dir->d_m_name == dir->d_u_name) { ret->u_name = ret->m_name; } else { ret->u_name = ret->m_name + blength(dir->d_m_name) + 1; - memcpy(ret->u_name, cfrombstring(dir->d_u_name), blength(dir->d_u_name) + 1); + memcpy(ret->u_name, cfrombstr(dir->d_u_name), blength(dir->d_u_name) + 1); } ret->d_dir = dir; -#if 0 - ret->st_valid = 1; - ret->st_errno = EACCES; -#endif - LOG(log_debug, logtype_afpd, "cname(AFPERR_ACCESS:'%s') {path-from-dir: curdir:'%s', path:'%s'}", - cfrombstring(dir->d_fullpath), - cfrombstring(curdir->d_fullpath), + LOG(log_debug, logtype_afpd, "cname('%s') {path-from-dir: AFPERR_ACCESS. curdir:'%s', path:'%s'}", + cfrombstr(dir->d_fullpath), + cfrombstr(curdir->d_fullpath), ret->u_name); return ret; case AFPERR_NOOBJ: - if (movecwd(vol, dirlookup(vol, dir->d_pdid)) < 0 ) + if (movecwd(vol, dirlookup(vol, dir->d_pdid)) < 0 ) /* 1 */ return NULL; - memcpy(ret->m_name, cfrombstring(dir->d_m_name), blength(dir->d_m_name) + 1); + memcpy(ret->m_name, cfrombstr(dir->d_m_name), blength(dir->d_m_name) + 1); if (dir->d_m_name == dir->d_u_name) { ret->u_name = ret->m_name; } else { ret->u_name = ret->m_name + blength(dir->d_m_name) + 1; - memcpy(ret->u_name, cfrombstring(dir->d_u_name), blength(dir->d_u_name) + 1); + memcpy(ret->u_name, cfrombstr(dir->d_u_name), blength(dir->d_u_name) + 1); } -#if 0 - ret->st_valid = 1; - ret->st_errno = ENOENT; -#endif - ret->d_dir = NULL; - dir_remove(vol, dir); + ret->d_dir = NULL; /* 4 */ + dir_remove(vol, dir); /* 5 */ return ret; default: @@ -415,12 +430,91 @@ int get_afp_errno(const int param) return param; } +/*! + * Resolve struct dir for an absolute path + * + * Given a path like "/Volumes/volume/dir/subdir" in a volume "/Volumes/volume" return + * a pointer to struct dir of "subdir". + * 1. Remove volue path from absolute path + * 2. start path + * 3. loop through all elements of the remaining path from 1. + * 4. we only allow dirs + * 5. search dircache + * 6. if not found in the dircache query the CNID database for the DID + * 7. and use dirlookup to resolve the DID to a it's struct dir * + * + * @param vol (r) volume the path is in, must be known + * @param path (r) absoule path + * + * @returns pointer to struct dir or NULL on error + */ +struct dir *dirlookup_bypath(const struct vol *vol, const char *path) +{ + EC_INIT; + + struct dir *dir = NULL; + cnid_t cnid, did; + bstring rpath = NULL; + bstring statpath = NULL; + struct bstrList *l = NULL; + struct stat st; + + cnid = htonl(2); + dir = vol->v_root; + + EC_NULL(rpath = rel_path_in_vol(path, vol->v_path)); /* 1. */ + EC_NULL(statpath = bfromcstr(vol->v_path)); /* 2. */ + + l = bsplit(rpath, '/'); + for (int i = 0; i < l->qty ; i++) { /* 3. */ + did = cnid; + EC_ZERO(bconcat(statpath, l->entry[i])); + EC_ZERO_LOGSTR(lstat(cfrombstr(statpath), &st), + "lstat(rpath: %s, elem: %s): %s: %s", + cfrombstr(rpath), cfrombstr(l->entry[i]), + cfrombstr(statpath), strerror(errno)); + + if (!(S_ISDIR(st.st_mode))) /* 4. */ + EC_FAIL; + + if ((dir = dircache_search_by_name(vol, /* 5. */ + dir, + cfrombstr(l->entry[i]), + blength(l->entry[i]), + st.st_ctime)) == NULL) { + if ((cnid = cnid_add(vol->v_cdb, /* 6. */ + &st, + did, + cfrombstr(l->entry[i]), + blength(l->entry[i]), + 0)) == CNID_INVALID) { + EC_FAIL; + } + + if ((dir = dirlookup(vol, cnid)) == NULL) /* 7. */ + EC_FAIL; + } + + EC_ZERO(bcatcstr(statpath, "/")); + } + +EC_CLEANUP: + bdestroy(rpath); + bstrListDestroy(l); + bdestroy(statpath); + if (ret != 0) + return NULL; + + return dir; +} + /*! * @brief Resolve a DID * * Resolve a DID, allocate a struct dir for it * 1. Check for special CNIDs 0 (invalid), 1 and 2. - * 2. Check if the DID is in the cache. + * 2a. Check if the DID is in the cache. + * 2b. Check if it's really a dir (d_fullpath != NULL) because we cache files too. * 3. If it's not in the cache resolve it via the database. * 4. Build complete server-side path to the dir. * 5. Check if it exists and is a directory. @@ -431,105 +525,106 @@ int get_afp_errno(const int param) * @param did (r) DID to resolve * * @returns pointer to struct dir - * - * @note FIXME: OSX calls it with bogus id, ie file ID not folder ID, - * and we are really bad in this case. */ struct dir *dirlookup(const struct vol *vol, cnid_t did) { static char buffer[12 + MAXPATHLEN + 1]; - struct bstrList *pathlist = NULL; - bstring fullpath = NULL; struct stat st; - struct dir *ret = NULL; - char *upath, *mpath; + struct dir *ret = NULL, *pdir; + bstring fullpath = NULL; + char *upath = NULL, *mpath; cnid_t cnid, pdid; size_t maxpath; int buflen = 12 + MAXPATHLEN + 1; int utf8; int err = 0; - LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {start}", ntohl(did)); + LOG(log_debug, logtype_afpd, "dirlookup(did: %u)", ntohl(did)); /* check for did 0, 1 and 2 */ if (did == 0 || vol == NULL) { /* 1 */ afp_errno = AFPERR_PARAM; - return NULL; + ret = NULL; + goto exit; } else if (did == DIRDID_ROOT_PARENT) { rootParent.d_vid = vol->v_vid; - return (&rootParent); + ret = &rootParent; + goto exit; } else if (did == DIRDID_ROOT) { - return vol->v_root; + ret = vol->v_root; + goto exit; } /* Search the cache */ - if ((ret = dircache_search_by_did(vol, did)) != NULL) { /* 2 */ - return ret; + if ((ret = dircache_search_by_did(vol, did)) != NULL) { /* 2a */ + if (ret->d_fullpath == NULL) { /* 2b */ + afp_errno = AFPERR_BADTYPE; + ret = NULL; + goto exit; + } + if (lstat(cfrombstr(ret->d_fullpath), &st) != 0) { + LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {lstat: %s}", ntohl(did), strerror(errno)); + switch (errno) { + case ENOENT: + case ENOTDIR: + /* It's not there anymore, so remove it */ + LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {calling dir_remove()}", ntohl(did)); + dir_remove(vol, ret); + afp_errno = AFPERR_NOOBJ; + ret = NULL; + goto exit; + default: + ret = ret; + goto exit; + } + /* DEADC0DE */ + ret = NULL; + goto exit; + } + ret = ret; + goto exit; } utf8 = utf8_encoding(); maxpath = (utf8) ? MAXPATHLEN - 7 : 255; - /* Create list for path elements, request 16 list elements for now*/ - if ((pathlist = bstListCreateMin(16)) == NULL) { /* 4 */ - LOG(log_error, logtype_afpd, "dirlookup(did: %u): OOM: %s", ntohl(did), strerror(errno)); - return NULL; - } - /* Get it from the database */ cnid = did; - if ( (upath = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen)) == NULL ) { /* 3 */ + if ((upath = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen)) == NULL) { afp_errno = AFPERR_NOOBJ; err = 1; goto exit; } - pdid = cnid; - - /* construct path, copy already found uname to path element list*/ - if ((bstrListPush(pathlist, bfromcstr(upath))) != BSTR_OK) { /* 4 */ - afp_errno = AFPERR_MISC; + if ((upath = strdup(upath)) == NULL) { /* 3 */ + afp_errno = AFPERR_NOOBJ; err = 1; goto exit; } + pdid = cnid; - LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {%u, %s}", ntohl(did), ntohl(pdid), upath); - - /* The stuff that follows is for building the full path to the directory */ - - /* work upwards until we reach volume root */ - while (cnid != DIRDID_ROOT) { - /* construct path, copy already found uname to path element list*/ - if ((bstrListPush(pathlist, bfromcstr(upath))) != BSTR_OK) { /* 4 */ - afp_errno = AFPERR_MISC; - err = 1; - goto exit; - } - - /* next part */ - if ((upath = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen)) == NULL ) { /* 3 */ - afp_errno = AFPERR_NOOBJ; - err = 1; - goto exit; - } - } - - if ((bstrListPush(pathlist, bfromcstr(vol->v_path))) != BSTR_OK) { /* 4 */ - afp_errno = AFPERR_MISC; + /* + * Recurse up the tree, terminates in dirlookup when either + * - DIRDID_ROOT is hit + * - a cached entry is found + */ + LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {recursion for did: %u}", ntohl(pdid)); + if ((pdir = dirlookup(vol, pdid)) == NULL) { err = 1; goto exit; } - if ((fullpath = bjoinInv(pathlist, bfromcstr("/"))) == NULL) { /* 4 */ - afp_errno = AFPERR_MISC; + /* build the fullpath */ + if ((fullpath = bstrcpy(pdir->d_fullpath)) == NULL + || bconchar(fullpath, '/') != BSTR_OK + || bcatcstr(fullpath, upath) != BSTR_OK) { err = 1; goto exit; } - /* Finished building the fullpath */ /* stat it and check if it's a dir */ - LOG(log_debug, logtype_afpd, "dirlookup: {stating %s}", cfrombstring(fullpath)); + LOG(log_debug, logtype_afpd, "dirlookup: {stating %s}", cfrombstr(fullpath)); - if (stat(cfrombstring(fullpath), &st) != 0) { /* 5a */ + if (stat(cfrombstr(fullpath), &st) != 0) { /* 5a */ switch (errno) { case ENOENT: afp_errno = AFPERR_NOOBJ; @@ -560,25 +655,20 @@ struct dir *dirlookup(const struct vol *vol, cnid_t did) } /* Create struct dir */ - if ((ret = dir_new(mpath, upath, vol, pdid, did, fullpath)) == NULL) { /* 6 */ + if ((ret = dir_new(mpath, upath, vol, pdid, did, fullpath, st.st_ctime)) == NULL) { /* 6 */ LOG(log_error, logtype_afpd, "dirlookup(did: %u) {%s, %s}: %s", ntohl(did), mpath, upath, strerror(errno)); err = 1; goto exit; } - + /* Add it to the cache only if it's a dir */ - if (dircache_add(ret) != 0) { /* 7 */ + if (dircache_add(vol, ret) != 0) { /* 7 */ err = 1; goto exit; } - LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {end: did:%u, path:'%s'}", - ntohl(did), ntohl(pdid), cfrombstring(ret->d_fullpath)); - exit: - if (pathlist) - bstrListDestroy(pathlist); - + if (upath) free(upath); if (err) { LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {exit_error: %s}", ntohl(did), AfpErr2name(afp_errno)); @@ -589,6 +679,10 @@ exit: ret = NULL; } } + if (ret) + LOG(log_debug, logtype_afpd, "dirlookup(did: %u): pdid: %u, \"%s\"", + ntohl(ret->d_did), ntohl(ret->d_pdid), cfrombstr(ret->d_fullpath)); + return ret; } @@ -684,7 +778,8 @@ int caseenumerate(const struct vol *vol, struct path *path, struct dir *dir) * @param vol (r) pointer to struct vol * @param pdid (r) Parent CNID * @param did (r) CNID - * @param fullpath (r) Full unix path to dir + * @param path (r) Full unix path to dir or NULL for files + * @param ctime (r) st_ctime from stat * * @returns pointer to new struct dir or NULL on error * @@ -695,7 +790,8 @@ struct dir *dir_new(const char *m_name, const struct vol *vol, cnid_t pdid, cnid_t did, - bstring path) + bstring path, + time_t ctime) { struct dir *dir; @@ -729,6 +825,7 @@ struct dir *dir_new(const char *m_name, dir->d_pdid = pdid; dir->d_vid = vol->v_vid; dir->d_fullpath = path; + dir->ctime_dircache = ctime; return dir; } @@ -753,14 +850,13 @@ void dir_free(struct dir *dir) * @brief Create struct dir from struct path * * Create a new struct dir from struct path. Then add it to the cache. - * The caller must have assured that the dir is not already in the cache, - * cf the assertion. + * * 1. Open adouble file, get CNID from it. * 2. Search the database, hinting with the CNID from (1). * 3. Build fullpath and create struct dir. * 4. Add it to the cache. * - * @param vol (r) pointer to struct vol + * @param vol (r) pointer to struct vol, possibly modified in callee * @param dir (r) pointer to parrent directory * @param path (rw) pointer to struct path with valid path->u_name * @param len (r) strlen of path->u_name @@ -769,7 +865,7 @@ void dir_free(struct dir *dir) * * @note Function also assigns path->m_name from path->u_name. */ -struct dir *dir_add(const struct vol *vol, const struct dir *dir, struct path *path, int len) +struct dir *dir_add(struct vol *vol, const struct dir *dir, struct path *path, int len) { int err = 0; struct dir *cdir = NULL; @@ -778,15 +874,25 @@ struct dir *dir_add(const struct vol *vol, const struct dir *dir, struct path *p struct adouble *adp = NULL; bstring fullpath; - assert(vol); - assert(dir); - assert(path); - assert(len > 0); - assert(dircache_search_by_name(vol, dir->d_did, path->u_name, strlen(path->u_name)) == NULL); + AFP_ASSERT(vol); + AFP_ASSERT(dir); + AFP_ASSERT(path); + AFP_ASSERT(len > 0); + + if ((cdir = dircache_search_by_name(vol, dir, path->u_name, strlen(path->u_name), path->st.st_ctime)) != NULL) { + /* there's a stray entry in the dircache */ + LOG(log_debug, logtype_afpd, "dir_add(did:%u,'%s/%s'): {stray cache entry: did:%u,'%s', removing}", + ntohl(dir->d_did), cfrombstr(dir->d_fullpath), path->u_name, + ntohl(cdir->d_did), cfrombstr(dir->d_fullpath)); + if (dir_remove(vol, cdir) != 0) { + dircache_dump(); + AFP_PANIC("dir_add"); + } + } /* get_id needs adp for reading CNID from adouble file */ ad_init(&ad, vol->v_adouble, vol->v_ad_options); - if ((ad_open_metadata(path->u_name, ADFLAGS_DIR, 0, &ad)) == 0) /* 1 */ + if ((ad_open(&ad, path->u_name, ADFLAGS_HF | ADFLAGS_DIR)) == 0) /* 1 */ adp = &ad; /* Get CNID */ @@ -801,7 +907,8 @@ struct dir *dir_add(const struct vol *vol, const struct dir *dir, struct path *p /* Get macname from unixname */ if (path->m_name == NULL) { if ((path->m_name = utompath(vol, path->u_name, id, utf8_encoding())) == NULL) { - err = 1; + LOG(log_error, logtype_afpd, "dir_add(\"%s\"): can't assign macname", path->u_name); + err = 2; goto exit; } } @@ -811,23 +918,26 @@ struct dir *dir_add(const struct vol *vol, const struct dir *dir, struct path *p || (bconchar(fullpath, '/') != BSTR_OK) || (bcatcstr(fullpath, path->u_name)) != BSTR_OK) { LOG(log_error, logtype_afpd, "dir_add: fullpath: %s", strerror(errno) ); - err = 1; + err = 3; goto exit; } /* Allocate and initialize struct dir */ - if ((cdir = dir_new( path->m_name, path->u_name, vol, dir->d_did, id, fullpath)) == NULL) { /* 3 */ - err = 1; + if ((cdir = dir_new( path->m_name, path->u_name, vol, dir->d_did, id, fullpath, path->st.st_ctime)) == NULL) { /* 3 */ + err = 4; goto exit; } - if ((dircache_add(cdir)) != 0) { /* 4 */ - LOG(log_error, logtype_afpd, "dir_add: fatal dircache error: %s", cfrombstring(fullpath)); + if ((dircache_add(vol, cdir)) != 0) { /* 4 */ + LOG(log_error, logtype_afpd, "dir_add: fatal dircache error: %s", cfrombstr(fullpath)); exit(EXITERR_SYS); } exit: if (err != 0) { + LOG(log_debug, logtype_afpd, "dir_add('%s/%s'): error: %u", + cfrombstr(dir->d_u_name), path->u_name, err); + if (adp) ad_close_metadata(adp); if (!cdir && fullpath) @@ -835,39 +945,65 @@ exit: if (cdir) dir_free(cdir); cdir = NULL; + } else { + /* no error */ + LOG(log_debug, logtype_afpd, "dir_add(did:%u,'%s/%s'): {cached: %u,'%s'}", + ntohl(dir->d_did), cfrombstr(dir->d_fullpath), path->u_name, + ntohl(cdir->d_did), cfrombstr(cdir->d_fullpath)); } return(cdir); } /*! - * @brief Remove a dir from a cache and free it and any ressources with it + * Free the queue with invalid struct dirs + * + * This gets called at the end of every AFP func. + */ +void dir_free_invalid_q(void) +{ + struct dir *dir; + while (dir = (struct dir *)dequeue(invalid_dircache_entries)) + dir_free(dir); +} + +/*! + * @brief Remove a dir from a cache and queue it for freeing + * + * 1. Check if the dir is locked or has opened forks + * 2. Remove it from the cache + * 3. Queue it for removal + * 4. If it's a request to remove curdir, mark curdir as invalid + * 5. Mark it as invalid * * @param (r) pointer to struct vol * @param (rw) pointer to struct dir */ int dir_remove(const struct vol *vol, struct dir *dir) { - assert(vol); - assert(dir); + AFP_ASSERT(vol); + AFP_ASSERT(dir); if (dir->d_did == DIRDID_ROOT_PARENT || dir->d_did == DIRDID_ROOT) return 0; - if (curdir == dir) { - if (movecwd(vol, vol->v_root) < 0) { - LOG(log_error, logtype_afpd, "dir_remove: can't chdir to : %s", vol->v_root); - } - } + LOG(log_debug, logtype_afpd, "dir_remove(did:%u,'%s'): {removing}", + ntohl(dir->d_did), cfrombstr(dir->d_u_name)); - dircache_remove(vol, dir, DIRCACHE | DIDNAME_INDEX | QUEUE_INDEX); - dir_free(dir); + dircache_remove(vol, dir, DIRCACHE | DIDNAME_INDEX | QUEUE_INDEX); /* 2 */ + enqueue(invalid_dircache_entries, dir); /* 3 */ + + if (curdir == dir) /* 4 */ + curdir = NULL; + + dir->d_did = CNID_INVALID; /* 5 */ return 0; } +#if 0 /* unused */ /*! - * @brief Modify a struct dir, adust cache + * @brief Modify a struct dir, adjust cache * * Any value that is 0 or NULL is not changed. If new_uname is NULL it is set to new_mname. * If given new_uname == new_mname, new_uname will point to new_mname. @@ -878,6 +1014,7 @@ int dir_remove(const struct vol *vol, struct dir *dir) * @param did (r) new DID * @param new_mname (r) new mac-name * @param new_uname (r) new unix-name + * @param pdir_fullpath (r) new fullpath of parent dir */ int dir_modify(const struct vol *vol, struct dir *dir, @@ -915,7 +1052,7 @@ int dir_modify(const struct vol *vol, dir->d_u_name = dir->d_m_name; } else { if ((dir->d_u_name = bfromcstr(new_uname)) == NULL) { - LOG(log_error, logtype_afpd, "renamedir: bassigncstr: %s", strerror(errno) ); + LOG(log_error, logtype_afpd, "dir_modify: bassigncstr: %s", strerror(errno) ); return -1; } } @@ -936,50 +1073,41 @@ int dir_modify(const struct vol *vol, dir->d_m_name_ucs2 = NULL; /* Re-add it to the cache */ - if ((dircache_add(dir)) != 0) { + if ((dircache_add(vol, dir)) != 0) { dircache_dump(); - exit(EXITERR_SYS); + AFP_PANIC("dir_modify"); } return ret; } +#endif /*! * @brief Resolve a catalog node name path * - * If it's a filename: - * 1. compute unix name - * 2. stat the file, storing struct stat or errno in struct path - * 3. cwd (and curdir) is filename parent directory - * 4. return path with with filename - * - * If it's a dirname: - * 5. search the dircache - * 6. if not in the cache: - * 7. compute unix name - * 8. stat the dir, storing struct stat or errno in struct path - * 9. chdir dirname - * 10. if chdir failed, return path with dirname - * cwd is dir parent directory - * if chdir succeeded, add to dircache - * return path with "" and "." - * cwd is dirname - * 11. else in the cache: - * 12. stat the dir, storing struct stat or errno in struct path - * 13. chdir dirname - * 14. if chdir failed - * if ENOENT - * remove from cache - * if not last path part, return NULL - * else - return - if chdir error - dirname - curdir: dir parent directory - else - dirname: "" - curdir: dir -*/ + * 1. Evaluate path type + * 2. Move to start dir, if we cant, it might eg because of EACCES, build + * path from dirname, so eg getdirparams has sth it can chew on. curdir + * is dir parent then. All this is done in path_from_dir(). + * 3. Parse next cnode name in path, cases: + * 4. single "\0" -> do nothing + * 5. two or more consecutive "\0" -> chdir("..") one or more times + * 6. cnode name -> copy it to path.m_name + * 7. Get unix name from mac name + * 8. Special handling of request with did 1 + * 9. stat the cnode name + * 10. If it's not there, it's probably an afp_createfile|dir, + * return with curdir = dir parent, struct path = dirname + * 11. If it's there and it's a file, it must should be the last element of the requested + * path. Return with curdir = cnode name parent dir, struct path = filename + * 12. Treat symlinks like files, dont follow them + * 13. If it's a dir: + * 14. Search the dircache for it + * 15. If it's not in the cache, create a struct dir for it and add it to the cache + * 16. chdir into the dir and + * 17. set m_name to the mac equivalent of "." + * 18. goto 3 + */ struct path *cname(struct vol *vol, struct dir *dir, char **cpath) { static char path[ MAXPATHLEN + 1]; @@ -993,13 +1121,13 @@ struct path *cname(struct vol *vol, struct dir *dir, char **cpath) int size = 0; int toUTF8 = 0; - LOG(log_debug, logtype_afpd, "came('%s'): {start}", cfrombstring(dir->d_fullpath)); + LOG(log_maxdebug, logtype_afpd, "came('%s'): {start}", cfrombstr(dir->d_fullpath)); data = *cpath; afp_errno = AFPERR_NOOBJ; memset(&ret, 0, sizeof(ret)); - switch (ret.m_type = *data) { /* path type */ + switch (ret.m_type = *data) { /* 1 */ case 2: data++; len = (unsigned char) *data++; @@ -1034,26 +1162,18 @@ struct path *cname(struct vol *vol, struct dir *dir, char **cpath) if (movecwd(vol, dir) < 0 ) { LOG(log_debug, logtype_afpd, "cname(did:%u): failed to chdir to '%s'", - ntohl(dir->d_did), cfrombstring(dir->d_fullpath)); + ntohl(dir->d_did), cfrombstr(dir->d_fullpath)); if (len == 0) return path_from_dir(vol, dir, &ret); else return NULL; } - while (len) { - /* - * Three cases: - * 1. single 0 -> delimiter - * 2. additional 0 -> chdir(..) - * 3. a name - * a) name is a file, build struct path from it etc., exit while loop - * b) name is a dir, add it to the dircache, chdir to it, continue - */ - if (*data == 0) { /* case 1 or 2 */ + while (len) { /* 3 */ + if (*data == 0) { /* 4 or 5 */ data++; len--; - while (len > 0 && *data == 0) { /* case 2 */ + while (len > 0 && *data == 0) { /* 5 */ /* chdir to parrent dir */ if ((dir = dirlookup(vol, dir->d_pdid)) == NULL) return NULL; @@ -1067,10 +1187,10 @@ struct path *cname(struct vol *vol, struct dir *dir, char **cpath) continue; } - /* case 3: copy name from packet buffer to ret.m_name and process it */ + /* 6*/ for ( p = path; *data != 0 && len > 0; len-- ) { *p++ = *data++; - if (p > &path[ MAXPATHLEN]) { + if (p > &path[UTF8FILELEN_EARLY]) { /* FIXME safeguard, limit of early Mac OS X */ afp_errno = AFPERR_PARAM; return NULL; } @@ -1078,13 +1198,12 @@ struct path *cname(struct vol *vol, struct dir *dir, char **cpath) *p = 0; /* Terminate string */ ret.u_name = NULL; - /* Get u_name from m_name */ - if (cname_mtouname(vol, dir, &ret, toUTF8) != 0) { + if (cname_mtouname(vol, dir, &ret, toUTF8) != 0) { /* 7 */ LOG(log_error, logtype_afpd, "cname('%s'): error from cname_mtouname", path); return NULL; } - LOG(log_maxdebug, logtype_afpd, "came('%s'): {node: '%s}", cfrombstring(dir->d_fullpath), ret.u_name); + LOG(log_maxdebug, logtype_afpd, "came('%s'): {node: '%s}", cfrombstr(dir->d_fullpath), ret.u_name); /* Prevent access to our special folders like .AppleDouble */ if (check_name(vol, ret.u_name)) { @@ -1094,13 +1213,13 @@ struct path *cname(struct vol *vol, struct dir *dir, char **cpath) return NULL; } - if (dir->d_did == DIRDID_ROOT_PARENT) { + if (dir->d_did == DIRDID_ROOT_PARENT) { /* 8 */ /* * Special case: CNID 1 * root parent (did 1) has one child: the volume. Requests for did=1 with * some must check against the volume name. */ - if ((strcmp(cfrombstring(vol->v_root->d_m_name), ret.m_name)) == 0) + if ((strcmp(cfrombstr(vol->v_root->d_m_name), ret.m_name)) == 0) cdir = vol->v_root; else return NULL; @@ -1113,7 +1232,7 @@ struct path *cname(struct vol *vol, struct dir *dir, char **cpath) * and thus call continue which should terminate the while loop because * len = 0. Ok? */ - if (of_stat(&ret) != 0) { + if (of_stat(&ret) != 0) { /* 9 */ /* * ret.u_name doesn't exist, might be afp_createfile|dir * that means it should have been the last part @@ -1127,28 +1246,32 @@ struct path *cname(struct vol *vol, struct dir *dir, char **cpath) * this will terminate clean in while (1) because len == 0, * probably afp_createfile|dir */ - LOG(log_maxdebug, logtype_afpd, "came('%s'): {leave-cnode ENOENT (possile create request): '%s'}", cfrombstring(dir->d_fullpath), ret.u_name); - continue; + LOG(log_maxdebug, logtype_afpd, "came('%s'): {leave-cnode ENOENT (possile create request): '%s'}", + cfrombstr(dir->d_fullpath), ret.u_name); + continue; /* 10 */ } switch (ret.st.st_mode & S_IFMT) { - case S_IFREG: - LOG(log_debug, logtype_afpd, "came('%s'): {file: '%s'}", cfrombstring(dir->d_fullpath), ret.u_name); + case S_IFREG: /* 11 */ + LOG(log_debug, logtype_afpd, "came('%s'): {file: '%s'}", + cfrombstr(dir->d_fullpath), ret.u_name); if (len > 0) { /* it wasn't the last part, so we have a bogus path request */ afp_errno = AFPERR_PARAM; return NULL; } continue; /* continues while loop */ - case S_IFLNK: - LOG(log_debug, logtype_afpd, "came('%s'): {link: '%s'}", cfrombstring(dir->d_fullpath), ret.u_name); + case S_IFLNK: /* 12 */ + LOG(log_debug, logtype_afpd, "came('%s'): {link: '%s'}", + cfrombstr(dir->d_fullpath), ret.u_name); if (len > 0) { - LOG(log_warning, logtype_afpd, "came('%s'): {symlinked dir: '%s'}", cfrombstring(dir->d_fullpath), ret.u_name); + LOG(log_warning, logtype_afpd, "came('%s'): {symlinked dir: '%s'}", + cfrombstr(dir->d_fullpath), ret.u_name); afp_errno = AFPERR_PARAM; return NULL; } continue; /* continues while loop */ - case S_IFDIR: + case S_IFDIR: /* 13 */ break; default: LOG(log_info, logtype_afpd, "cname: special file: '%s'", ret.u_name); @@ -1158,10 +1281,10 @@ struct path *cname(struct vol *vol, struct dir *dir, char **cpath) /* Search the cache */ int unamelen = strlen(ret.u_name); - cdir = dircache_search_by_name(vol, dir->d_did, ret.u_name, unamelen); + cdir = dircache_search_by_name(vol, dir, ret.u_name, unamelen, ret.st.st_ctime); /* 14 */ if (cdir == NULL) { /* Not in cache, create one */ - if ((cdir = dir_add(vol, dir, &ret, unamelen)) == NULL) { + if ((cdir = dir_add(vol, dir, &ret, unamelen)) == NULL) { /* 15 */ LOG(log_error, logtype_afpd, "cname(did:%u, name:'%s', cwd:'%s'): failed to add dir", ntohl(dir->d_did), ret.u_name, getcwdpath()); return NULL; @@ -1170,16 +1293,16 @@ struct path *cname(struct vol *vol, struct dir *dir, char **cpath) } /* if/else cnid==1 */ /* Now chdir to the evaluated dir */ - if (movecwd( vol, cdir ) < 0 ) { + if (movecwd( vol, cdir ) < 0 ) { /* 16 */ LOG(log_debug, logtype_afpd, "cname(cwd:'%s'): failed to chdir to new subdir '%s': %s", - cfrombstring(curdir->d_fullpath), cfrombstring(cdir->d_fullpath), strerror(errno)); + cfrombstr(curdir->d_fullpath), cfrombstr(cdir->d_fullpath), strerror(errno)); if (len == 0) return path_from_dir(vol, cdir, &ret); else return NULL; } dir = cdir; - ret.m_name[0] = 0; /* so we later know last token was a dir */ + ret.m_name[0] = 0; /* 17, so we later know last token was a dir */ } /* while (len) */ if (curdir->d_did == DIRDID_ROOT_PARENT) { @@ -1194,8 +1317,8 @@ struct path *cname(struct vol *vol, struct dir *dir, char **cpath) } LOG(log_debug, logtype_afpd, "came('%s') {end: curdir:'%s', path:'%s'}", - cfrombstring(dir->d_fullpath), - cfrombstring(curdir->d_fullpath), + cfrombstr(dir->d_fullpath), + cfrombstr(curdir->d_fullpath), ret.u_name); return &ret; @@ -1211,25 +1334,37 @@ struct path *cname(struct vol *vol, struct dir *dir, char **cpath) */ int movecwd(const struct vol *vol, struct dir *dir) { - assert(vol); + int ret; - if (dir == NULL) - return -1; + AFP_ASSERT(vol); + AFP_ASSERT(dir); - LOG(log_maxdebug, logtype_afpd, "movecwd(curdir:'%s', cwd:'%s')", - cfrombstring(curdir->d_fullpath), getcwdpath()); + LOG(log_maxdebug, logtype_afpd, "movecwd: from: curdir:\"%s\", cwd:\"%s\"", + curdir ? cfrombstr(curdir->d_fullpath) : "INVALID", getcwdpath()); - if ( dir == curdir) - return( 0 ); if (dir->d_did == DIRDID_ROOT_PARENT) { curdir = &rootParent; return 0; } - LOG(log_debug, logtype_afpd, "movecwd(did:%u, '%s')", ntohl(dir->d_did), cfrombstring(dir->d_fullpath)); + LOG(log_debug, logtype_afpd, "movecwd(to: did: %u, \"%s\")", + ntohl(dir->d_did), cfrombstr(dir->d_fullpath)); + + if ((ret = lchdir(cfrombstr(dir->d_fullpath))) != 0 ) { + LOG(log_debug, logtype_afpd, "movecwd(\"%s\"): ret: %u, %s", + cfrombstr(dir->d_fullpath), ret, strerror(errno)); + if (ret == 1) { + /* p is a symlink or getcwd failed */ + afp_errno = AFPERR_BADTYPE; + + if (chdir(vol->v_path ) < 0) { + LOG(log_error, logtype_afpd, "can't chdir back'%s': %s", vol->v_path, strerror(errno)); + /* XXX what do we do here? */ + } + curdir = vol->v_root; + return -1; + } - if ( chdir(cfrombstring(dir->d_fullpath)) < 0 ) { - LOG(log_debug, logtype_afpd, "movecwd('%s'): %s", cfrombstring(dir->d_fullpath), strerror(errno)); switch (errno) { case EACCES: case EPERM: @@ -1277,10 +1412,18 @@ int file_access(struct path *path, int mode) struct maccess ma; accessmode(path->u_name, &ma, curdir, &path->st); - if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE)) + + LOG(log_debug, logtype_afpd, "file_access(\"%s\"): mapped user mode: 0x%02x", + path->u_name, ma.ma_user); + + if ((mode & OPENACC_WR) && !(ma.ma_user & AR_UWRITE)) { + LOG(log_debug, logtype_afpd, "file_access(\"%s\"): write access denied", path->u_name); return -1; - if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD)) + } + if ((mode & OPENACC_RD) && !(ma.ma_user & AR_UREAD)) { + LOG(log_debug, logtype_afpd, "file_access(\"%s\"): read access denied", path->u_name); return -1; + } return 0; } @@ -1331,20 +1474,8 @@ int getdirparams(const struct vol *vol, (1 << DIRPBIT_FINFO)))) { ad_init(&ad, vol->v_adouble, vol->v_ad_options); - if ( !ad_metadata( upath, ADFLAGS_CREATE|ADFLAGS_DIR, &ad) ) { + if ( !ad_metadata( upath, ADFLAGS_DIR, &ad) ) isad = 1; - if (ad.ad_md->adf_flags & O_CREAT) { - /* We just created it */ - ad_setname(&ad, s_path->m_name); - ad_setid( &ad, - s_path->st.st_dev, - s_path->st.st_ino, - dir->d_did, - dir->d_pdid, - vol->v_stamp); - ad_flush( &ad); - } - } } pdid = dir->d_pdid; @@ -1360,7 +1491,7 @@ int getdirparams(const struct vol *vol, case DIRPBIT_ATTR : if ( isad ) { ad_getattr(&ad, &ashort); - } else if (invisible_dots(vol, cfrombstring(dir->d_u_name))) { + } else if (invisible_dots(vol, cfrombstr(dir->d_u_name))) { ashort = htons(ATTRBIT_INVISIBLE); } else ashort = 0; @@ -1372,6 +1503,8 @@ int getdirparams(const struct vol *vol, case DIRPBIT_PDID : memcpy( data, &pdid, sizeof( pdid )); data += sizeof( pdid ); + LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u", + s_path->u_name, ntohl(pdid)); break; case DIRPBIT_CDATE : @@ -1404,7 +1537,7 @@ int getdirparams(const struct vol *vol, memcpy(data + FINDERINFO_FRVIEWOFF, &ashort, sizeof(ashort)); /* dot files are by default visible */ - if (invisible_dots(vol, cfrombstring(dir->d_u_name))) { + if (invisible_dots(vol, cfrombstr(dir->d_u_name))) { ashort = htons(FINDERINFO_INVISIBLE); memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort)); } @@ -1428,6 +1561,8 @@ int getdirparams(const struct vol *vol, case DIRPBIT_DID : memcpy( data, &dir->d_did, sizeof( aint )); data += sizeof( aint ); + LOG(log_debug, logtype_afpd, "metadata('%s'): DID: %u", + s_path->u_name, ntohl(dir->d_did)); break; case DIRPBIT_OFFCNT : @@ -1525,12 +1660,12 @@ int getdirparams(const struct vol *vol, if ( l_nameoff ) { ashort = htons( data - buf ); memcpy( l_nameoff, &ashort, sizeof( ashort )); - data = set_name(vol, data, pdid, cfrombstring(dir->d_m_name), dir->d_did, 0); + data = set_name(vol, data, pdid, cfrombstr(dir->d_m_name), dir->d_did, 0); } if ( utf_nameoff ) { ashort = htons( data - buf ); memcpy( utf_nameoff, &ashort, sizeof( ashort )); - data = set_name(vol, data, pdid, cfrombstring(dir->d_m_name), dir->d_did, utf8); + data = set_name(vol, data, pdid, cfrombstr(dir->d_m_name), dir->d_did, utf8); } if ( isad ) { ad_close_metadata( &ad ); @@ -1744,7 +1879,7 @@ int setdirparams(struct vol *vol, struct path *path, u_int16_t d_bitmap, char *b } ad_init(&ad, vol->v_adouble, vol->v_ad_options); - if (ad_open_metadata( upath, ADFLAGS_DIR, O_CREAT, &ad) < 0) { + if (ad_open(&ad, upath, ADFLAGS_HF | ADFLAGS_DIR, O_CREAT, 0777) != 0) { /* * Check to see what we're trying to set. If it's anything * but ACCESS, UID, or GID, give an error. If it's any of those @@ -1766,8 +1901,8 @@ int setdirparams(struct vol *vol, struct path *path, u_int16_t d_bitmap, char *b * Check to see if a create was necessary. If it was, we'll want * to set our name, etc. */ - if ( (ad_get_HF_flags( &ad ) & O_CREAT)) { - ad_setname(&ad, cfrombstring(curdir->d_m_name)); + if ( (ad_get_MD_flags( &ad ) & O_CREAT)) { + ad_setname(&ad, cfrombstr(curdir->d_m_name)); } } @@ -1932,10 +2067,9 @@ setdirparam_done: ad_close_metadata( &ad); } -#if 0 if (change_parent_mdate && dir->d_did != DIRDID_ROOT && gettimeofday(&tv, NULL) == 0) { - if (!movecwd(vol, dir->d_parent)) { + if (movecwd(vol, dirlookup(vol, dir->d_pdid)) == 0) { newdate = AD_DATE_FROM_UNIX(tv.tv_sec); /* be careful with bitmap because now dir is null */ bitmap = 1<ad_path(cfrombstring(dir->d_u_name), ADFLAGS_DIR), strerror(errno) ); + vol->ad_path(cfrombstr(dir->d_u_name), ADFLAGS_DIR), strerror(errno) ); close(dfd); } @@ -2079,7 +2213,7 @@ int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_ upath = s_path->u_name; - if (AFP_OK != (err = netatalk_mkdir( upath))) { + if (AFP_OK != (err = netatalk_mkdir(vol, upath))) { return err; } @@ -2098,7 +2232,7 @@ int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_ } ad_init(&ad, vol->v_adouble, vol->v_ad_options); - if (ad_open_metadata( ".", ADFLAGS_DIR, O_CREAT, &ad ) < 0) { + if (ad_open(&ad, ".", ADFLAGS_HF | ADFLAGS_DIR, O_CREAT, 0777) < 0) { if (vol_noadouble(vol)) goto createdir_done; return( AFPERR_ACCESS ); @@ -2110,11 +2244,6 @@ int afp_createdir(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_ ad_close_metadata( &ad); createdir_done: -#ifdef HAVE_NFSv4_ACLS - /* FIXME: are we really inside the created dir? */ - addir_inherit_acl(vol); -#endif - memcpy( rbuf, &dir->d_did, sizeof( u_int32_t )); *rbuflen = sizeof( u_int32_t ); setvoltime(obj, vol ); @@ -2125,8 +2254,12 @@ createdir_done: * dst new unix filename (not a pathname) * newname new mac name * newparent curdir + * dirfd -1 means ignore dirfd (or use AT_FDCWD), otherwise src is relative to dirfd */ -int renamedir(const struct vol *vol, char *src, char *dst, +int renamedir(const struct vol *vol, + int dirfd, + char *src, + char *dst, struct dir *dir, struct dir *newparent, char *newname) @@ -2135,7 +2268,7 @@ int renamedir(const struct vol *vol, char *src, char *dst, int err; /* existence check moved to afp_moveandrename */ - if ( unix_rename( src, dst ) < 0 ) { + if ( unix_rename(dirfd, src, -1, dst ) < 0 ) { switch ( errno ) { case ENOENT : return( AFPERR_NOOBJ ); @@ -2149,11 +2282,11 @@ int renamedir(const struct vol *vol, char *src, char *dst, case EXDEV: /* this needs to copy and delete. bleah. that means we have * to deal with entire directory hierarchies. */ - if ((err = copydir(vol, src, dst)) < 0) { - deletedir(dst); + if ((err = copydir(vol, dirfd, src, dst)) < 0) { + deletedir(-1, dst); return err; } - if ((err = deletedir(src)) < 0) + if ((err = deletedir(dirfd, src)) < 0) return err; break; default : @@ -2161,21 +2294,16 @@ int renamedir(const struct vol *vol, char *src, char *dst, } } - vol->vfs->vfs_renamedir(vol, src, dst); + vol->vfs->vfs_renamedir(vol, dirfd, src, dst); ad_init(&ad, vol->v_adouble, vol->v_ad_options); - if (!ad_open_metadata( dst, ADFLAGS_DIR, 0, &ad)) { + if (ad_open(&ad, dst, ADFLAGS_HF | ADFLAGS_DIR) == 0) { ad_setname(&ad, newname); ad_flush( &ad); ad_close_metadata( &ad); } - if (dir_modify(vol, dir, curdir->d_did, 0, newname, dst, curdir->d_fullpath) != 0) { - LOG(log_error, logtype_afpd, "renamedir: fatal error from dir_modify: %s -> %s", src, dst); - return AFPERR_MISC; - } - return( AFP_OK ); } @@ -2184,13 +2312,13 @@ int deletecurdir(struct vol *vol) { struct dirent *de; struct stat st; - struct dir *fdir; + struct dir *fdir, *pdir; DIR *dp; struct adouble ad; u_int16_t ashort; int err; - if ( dirlookup(vol, curdir->d_pdid) == NULL ) { + if ((pdir = dirlookup(vol, curdir->d_pdid)) == NULL) { return( AFPERR_ACCESS ); } @@ -2201,13 +2329,15 @@ int deletecurdir(struct vol *vol) if ( ad_metadata( ".", ADFLAGS_DIR, &ad) == 0 ) { ad_getattr(&ad, &ashort); - ad_close( &ad, ADFLAGS_HF ); + ad_close_metadata(&ad); if ((ashort & htons(ATTRBIT_NODELETE))) { return AFPERR_OLOCK; } } err = vol->vfs->vfs_deletecurdir(vol); if (err) { + LOG(log_error, logtype_afpd, "deletecurdir: error deleting .AppleDouble in \"%s\"", + curdir->d_fullpath); return err; } @@ -2220,6 +2350,8 @@ int deletecurdir(struct vol *vol) /* bail if it's not a symlink */ if ((lstat(de->d_name, &st) == 0) && !S_ISLNK(st.st_mode)) { + LOG(log_error, logtype_afpd, "deletecurdir(\"%s\"): not empty", + curdir->d_fullpath); closedir(dp); return AFPERR_DIRNEMPT; } @@ -2231,16 +2363,23 @@ int deletecurdir(struct vol *vol) } } - if ( movecwd(vol, dirlookup(vol, curdir->d_pdid)) < 0 ) { + if (movecwd(vol, pdir) < 0) { err = afp_errno; goto delete_done; } - err = netatalk_rmdir_all_errors(cfrombstring(fdir->d_u_name)); + LOG(log_debug, logtype_afpd, "deletecurdir: moved to \"%s\"", + cfrombstr(curdir->d_fullpath)); + + err = netatalk_rmdir_all_errors(-1, cfrombstr(fdir->d_u_name)); if ( err == AFP_OK || err == AFPERR_NOOBJ) { cnid_delete(vol->v_cdb, fdir->d_did); dir_remove( vol, fdir ); + } else { + LOG(log_error, logtype_afpd, "deletecurdir(\"%s\"): netatalk_rmdir_all_errors error", + curdir->d_fullpath); } + delete_done: if (dp) { /* inode is used as key for cnid. @@ -2265,7 +2404,6 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r sfunc = (unsigned char) *ibuf++; *rbuflen = 0; - if (sfunc >= 3 && sfunc <= 6) { if (afp_version < 30) { return( AFPERR_PARAM ); @@ -2304,17 +2442,18 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r name = NULL; } break; -#ifdef HAVE_NFSv4_ACLS + case 5 : /* UUID -> username */ case 6 : /* UUID -> groupname */ if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID )) return AFPERR_PARAM; LOG(log_debug, logtype_afpd, "afp_mapid: valid UUID request"); uuidtype_t type; - len = getnamefromuuid( ibuf, &name, &type); + len = getnamefromuuid((unsigned char*) ibuf, &name, &type); if (len != 0) /* its a error code, not len */ return AFPERR_NOITEM; - if (type == UUID_USER) { + switch (type) { + case UUID_USER: if (( pw = getpwnam( name )) == NULL ) return( AFPERR_NOITEM ); LOG(log_debug, logtype_afpd, "afp_mapid: name:%s -> uid:%d", name, pw->pw_uid); @@ -2325,7 +2464,8 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r memcpy( rbuf, &id, sizeof( id )); rbuf += sizeof( id ); *rbuflen = 2 * sizeof( id ); - } else { /* type == UUID_GROUP */ + break; + case UUID_GROUP: if (( gr = getgrnam( name )) == NULL ) return( AFPERR_NOITEM ); LOG(log_debug, logtype_afpd, "afp_mapid: group:%s -> gid:%d", name, gr->gr_gid); @@ -2336,9 +2476,15 @@ int afp_mapid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *r memcpy( rbuf, &id, sizeof( id )); rbuf += sizeof( id ); *rbuflen = 2 * sizeof( id ); + break; + case UUID_LOCAL: + free(name); + return (AFPERR_NOITEM); + default: + return AFPERR_MISC; } break; -#endif + default : return( AFPERR_PARAM ); } @@ -2392,7 +2538,6 @@ int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, siz case 4 : len = (unsigned char) *ibuf++; break; -#ifdef HAVE_NFSv4_ACLS case 5 : /* username -> UUID */ case 6 : /* groupname -> UUID */ if ((afp_version < 32) || !(obj->options.flags & OPTION_UUID )) @@ -2401,7 +2546,6 @@ int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, siz len = ntohs(ulen); ibuf += 2; break; -#endif default : return( AFPERR_PARAM ); } @@ -2435,20 +2579,18 @@ int afp_mapname(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, siz memcpy( rbuf, &id, sizeof( id )); *rbuflen = sizeof( id ); break; -#ifdef HAVE_NFSv4_ACLS case 5 : /* username -> UUID */ LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf); - if (0 != getuuidfromname(ibuf, UUID_USER, rbuf)) + if (0 != getuuidfromname(ibuf, UUID_USER, (unsigned char *)rbuf)) return AFPERR_NOITEM; *rbuflen = UUID_BINSIZE; break; case 6 : /* groupname -> UUID */ LOG(log_debug, logtype_afpd, "afp_mapname: name: %s",ibuf); - if (0 != getuuidfromname(ibuf, UUID_GROUP, rbuf)) + if (0 != getuuidfromname(ibuf, UUID_GROUP, (unsigned char *)rbuf)) return AFPERR_NOITEM; *rbuflen = UUID_BINSIZE; break; -#endif } } return( AFP_OK );