#include <atalk/unix.h>
#include <atalk/bstrlib.h>
#include <atalk/bstradd.h>
+#include <atalk/errchk.h>
#include "directory.h"
#include "dircache.h"
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(bcatcstr(statpath, "/"));
+ 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_CLEANUP:
+ bdestroy(rpath);
+ bstrListDestroy(l);
+ bdestroy(statpath);
+ if (ret != 0)
+ return NULL;
+
+ return dir;
+}
+
/*!
* @brief Resolve a DID
*
* @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)
{
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) { /* 2a */
if (ret->d_fullpath == NULL) { /* 2b */
afp_errno = AFPERR_BADTYPE;
- return NULL;
+ 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));
LOG(log_debug, logtype_afpd, "dirlookup(did: %u) {calling dir_remove()}", ntohl(did));
dir_remove(vol, ret);
afp_errno = AFPERR_NOOBJ;
- return NULL;
+ ret = NULL;
+ goto exit;
default:
- return ret;
+ ret = ret;
+ goto exit;
}
/* DEADC0DE */
- return NULL;
+ ret = NULL;
+ goto exit;
}
- return ret;
+ ret = ret;
+ goto exit;
}
utf8 = utf8_encoding();
/* Get it from the database */
cnid = did;
- if ( (upath = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen)) == NULL
- || (upath = strdup(upath)) == NULL) { /* 3 */
+ if ((upath = cnid_resolve(vol->v_cdb, &cnid, buffer, buflen)) == NULL) {
+ afp_errno = AFPERR_NOOBJ;
+ err = 1;
+ goto exit;
+ }
+ if ((upath = strdup(upath)) == NULL) { /* 3 */
afp_errno = AFPERR_NOOBJ;
err = 1;
goto exit;
* - 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;
}
/* 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), cfrombstr(ret->d_fullpath));
-
exit:
if (upath) free(upath);
if (err) {
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;
}
/* Get macname from unixname */
if (path->m_name == NULL) {
if ((path->m_name = utompath(vol, path->u_name, id, utf8_encoding())) == NULL) {
+ LOG(log_error, logtype_afpd, "dir_add(\"%s\"): can't assign macname", path->u_name);
err = 2;
goto exit;
}
goto exit;
}
- if ((dircache_add(cdir)) != 0) { /* 4 */
+ if ((dircache_add(vol, cdir)) != 0) { /* 4 */
LOG(log_error, logtype_afpd, "dir_add: fatal dircache error: %s", cfrombstr(fullpath));
exit(EXITERR_SYS);
}
* @brief Remove a dir from a cache and queue it for freeing
*
* 1. Check if the dir is locked or has opened forks
- * 2. If it's a request to remove curdir, just chdir to volume root
- * 3. Remove it from the cache
- * 4. Queue it for removal
+ * 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
if (dir->d_did == DIRDID_ROOT_PARENT || dir->d_did == DIRDID_ROOT)
return 0;
- if (dir->d_flags & DIRF_CACHELOCK) { /* 1 */
- LOG(log_warning, logtype_afpd, "dir_remove(did:%u,'%s'): dir is locked",
- ntohl(dir->d_did), cfrombstr(dir->d_u_name));
- return 0;
- }
-
- if (curdir == dir) { /* 2 */
- 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); /* 3 */
- enqueue(invalid_dircache_entries, dir); /* 4 */
+ 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, adjust cache
*
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();
AFP_PANIC("dir_modify");
}
return ret;
}
+#endif
/*!
* @brief Resolve a catalog node name path
AFP_ASSERT(vol);
AFP_ASSERT(dir);
- LOG(log_maxdebug, logtype_afpd, "movecwd(curdir:'%s', cwd:'%s')",
- cfrombstr(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')",
+ 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",
+ 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 */
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;
}
{
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 );
}
}
err = vol->vfs->vfs_deletecurdir(vol);
if (err) {
+ LOG(log_error, logtype_afpd, "deletecurdir: error deleting .AppleDouble in \"%s\"",
+ curdir->d_fullpath);
return err;
}
/* 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;
}
}
}
- if ( movecwd(vol, dirlookup(vol, curdir->d_pdid)) < 0 ) {
+ if (movecwd(vol, pdir) < 0) {
err = afp_errno;
goto delete_done;
}
+ 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.