mirror of
https://github.com/openharmony/third_party_libfuse.git
synced 2026-07-16 09:04:28 -04:00
rename: perform user mode dir loop check when not done in kernel
Linux performs the dir loop check (rename(a, a/b/c)
or rename(a/b/c, a), etc.) in kernel. Unfortunately
other systems do not perform this check (e.g. FreeBSD).
This results in a deadlock in get_path2, because libfuse
did not expect to handle such cases.
We add a check_dir_loop function that performs the dir
loop check in user mode and enable it on systems that
need it.
This commit is contained in:
committed by
Nikolaus Rath
parent
88da32d665
commit
d105fafabb
+59
-3
@@ -1248,6 +1248,49 @@ static int get_path_wrlock(struct fuse *f, fuse_ino_t nodeid, const char *name,
|
||||
return get_path_common(f, nodeid, name, path, wnode);
|
||||
}
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
#define CHECK_DIR_LOOP true
|
||||
#else
|
||||
#define CHECK_DIR_LOOP false
|
||||
#endif
|
||||
|
||||
static int check_dir_loop(struct fuse *f,
|
||||
fuse_ino_t nodeid1, const char *name1,
|
||||
fuse_ino_t nodeid2, const char *name2)
|
||||
{
|
||||
struct node *node, *node1, *node2;
|
||||
fuse_ino_t id1, id2;
|
||||
|
||||
node1 = lookup_node(f, nodeid1, name1);
|
||||
id1 = node1 ? node1->nodeid : nodeid1;
|
||||
|
||||
node2 = lookup_node(f, nodeid2, name2);
|
||||
id2 = node2 ? node2->nodeid : nodeid2;
|
||||
|
||||
for (node = get_node(f, id2); node->nodeid != FUSE_ROOT_ID;
|
||||
node = node->parent) {
|
||||
if (node->name == NULL || node->parent == NULL)
|
||||
break;
|
||||
|
||||
if (node->nodeid != id2 && node->nodeid == id1)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (node2)
|
||||
{
|
||||
for (node = get_node(f, id1); node->nodeid != FUSE_ROOT_ID;
|
||||
node = node->parent) {
|
||||
if (node->name == NULL || node->parent == NULL)
|
||||
break;
|
||||
|
||||
if (node->nodeid != id1 && node->nodeid == id2)
|
||||
return -ENOTEMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int try_get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
|
||||
fuse_ino_t nodeid2, const char *name2,
|
||||
char **path1, char **path2,
|
||||
@@ -1272,11 +1315,20 @@ static int try_get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
|
||||
static int get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
|
||||
fuse_ino_t nodeid2, const char *name2,
|
||||
char **path1, char **path2,
|
||||
struct node **wnode1, struct node **wnode2)
|
||||
struct node **wnode1, struct node **wnode2,
|
||||
bool dir_loop)
|
||||
{
|
||||
int err;
|
||||
|
||||
pthread_mutex_lock(&f->lock);
|
||||
|
||||
if (dir_loop)
|
||||
{
|
||||
err = check_dir_loop(f, nodeid1, name1, nodeid2, name2);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
err = try_get_path2(f, nodeid1, name1, nodeid2, name2,
|
||||
path1, path2, wnode1, wnode2);
|
||||
if (err == -EAGAIN) {
|
||||
@@ -1297,6 +1349,8 @@ static int get_path2(struct fuse *f, fuse_ino_t nodeid1, const char *name1,
|
||||
debug_path(f, "DEQUEUE PATH1", nodeid1, name1, !!wnode1);
|
||||
debug_path(f, " PATH2", nodeid2, name2, !!wnode2);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
pthread_mutex_unlock(&f->lock);
|
||||
|
||||
return err;
|
||||
@@ -2965,7 +3019,8 @@ static void fuse_lib_rename(fuse_req_t req, fuse_ino_t olddir,
|
||||
int err;
|
||||
|
||||
err = get_path2(f, olddir, oldname, newdir, newname,
|
||||
&oldpath, &newpath, &wnode1, &wnode2);
|
||||
&oldpath, &newpath, &wnode1, &wnode2,
|
||||
CHECK_DIR_LOOP);
|
||||
if (!err) {
|
||||
struct fuse_intr_data d;
|
||||
err = 0;
|
||||
@@ -3001,7 +3056,8 @@ static void fuse_lib_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent,
|
||||
int err;
|
||||
|
||||
err = get_path2(f, ino, NULL, newparent, newname,
|
||||
&oldpath, &newpath, NULL, NULL);
|
||||
&oldpath, &newpath, NULL, NULL,
|
||||
false);
|
||||
if (!err) {
|
||||
struct fuse_intr_data d;
|
||||
|
||||
|
||||
@@ -1282,6 +1282,221 @@ static int test_rename_dir(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int test_rename_dir_loop(void)
|
||||
{
|
||||
#define PATH(p) (snprintf(path, sizeof path, "%s/%s", testdir, p), path)
|
||||
#define PATH2(p) (snprintf(path2, sizeof path2, "%s/%s", testdir, p), path2)
|
||||
|
||||
char path[1024], path2[1024];
|
||||
int err = 0;
|
||||
int res;
|
||||
|
||||
start_test("rename dir loop");
|
||||
|
||||
res = create_dir(testdir, testdir_files);
|
||||
if (res == -1)
|
||||
return -1;
|
||||
|
||||
res = mkdir(PATH("a"), 0755);
|
||||
if (res == -1) {
|
||||
PERROR("mkdir");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = rename(PATH("a"), PATH2("a"));
|
||||
if (res == -1) {
|
||||
PERROR("rename");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
res = rename(PATH("a"), PATH2("a/b"));
|
||||
if (res == 0 || errno != EINVAL) {
|
||||
PERROR("rename");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = mkdir(PATH("a/b"), 0755);
|
||||
if (res == -1) {
|
||||
PERROR("mkdir");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = mkdir(PATH("a/b/c"), 0755);
|
||||
if (res == -1) {
|
||||
PERROR("mkdir");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
res = rename(PATH("a"), PATH2("a/b/c"));
|
||||
if (res == 0 || errno != EINVAL) {
|
||||
PERROR("rename");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
res = rename(PATH("a"), PATH2("a/b/c/a"));
|
||||
if (res == 0 || errno != EINVAL) {
|
||||
PERROR("rename");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
res = rename(PATH("a/b/c"), PATH2("a"));
|
||||
if (res == 0 || errno != ENOTEMPTY) {
|
||||
PERROR("rename");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = open(PATH("a/foo"), O_CREAT, 0644);
|
||||
if (res == -1) {
|
||||
PERROR("open");
|
||||
goto fail;
|
||||
}
|
||||
close(res);
|
||||
|
||||
res = rename(PATH("a/foo"), PATH2("a/bar"));
|
||||
if (res == -1) {
|
||||
PERROR("rename");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = rename(PATH("a/bar"), PATH2("a/foo"));
|
||||
if (res == -1) {
|
||||
PERROR("rename");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = rename(PATH("a/foo"), PATH2("a/b/bar"));
|
||||
if (res == -1) {
|
||||
PERROR("rename");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = rename(PATH("a/b/bar"), PATH2("a/foo"));
|
||||
if (res == -1) {
|
||||
PERROR("rename");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = rename(PATH("a/foo"), PATH2("a/b/c/bar"));
|
||||
if (res == -1) {
|
||||
PERROR("rename");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = rename(PATH("a/b/c/bar"), PATH2("a/foo"));
|
||||
if (res == -1) {
|
||||
PERROR("rename");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = open(PATH("a/bar"), O_CREAT, 0644);
|
||||
if (res == -1) {
|
||||
PERROR("open");
|
||||
goto fail;
|
||||
}
|
||||
close(res);
|
||||
|
||||
res = rename(PATH("a/foo"), PATH2("a/bar"));
|
||||
if (res == -1) {
|
||||
PERROR("rename");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
unlink(PATH("a/bar"));
|
||||
|
||||
res = rename(PATH("a/b"), PATH2("a/d"));
|
||||
if (res == -1) {
|
||||
PERROR("rename");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = rename(PATH("a/d"), PATH2("a/b"));
|
||||
if (res == -1) {
|
||||
PERROR("rename");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = mkdir(PATH("a/d"), 0755);
|
||||
if (res == -1) {
|
||||
PERROR("mkdir");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = rename(PATH("a/b"), PATH2("a/d"));
|
||||
if (res == -1) {
|
||||
PERROR("rename");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = rename(PATH("a/d"), PATH2("a/b"));
|
||||
if (res == -1) {
|
||||
PERROR("rename");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = mkdir(PATH("a/d"), 0755);
|
||||
if (res == -1) {
|
||||
PERROR("mkdir");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
res = mkdir(PATH("a/d/e"), 0755);
|
||||
if (res == -1) {
|
||||
PERROR("mkdir");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
res = rename(PATH("a/b"), PATH2("a/d"));
|
||||
if (res == 0 || errno != ENOTEMPTY) {
|
||||
PERROR("rename");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rmdir(PATH("a/d/e"));
|
||||
rmdir(PATH("a/d"));
|
||||
|
||||
rmdir(PATH("a/b/c"));
|
||||
rmdir(PATH("a/b"));
|
||||
rmdir(PATH("a"));
|
||||
|
||||
err += cleanup_dir(testdir, testdir_files, 0);
|
||||
res = rmdir(testdir);
|
||||
if (res == -1) {
|
||||
PERROR("rmdir");
|
||||
goto fail;
|
||||
}
|
||||
res = check_nonexist(testdir);
|
||||
if (res == -1)
|
||||
return -1;
|
||||
if (err)
|
||||
return -1;
|
||||
|
||||
success();
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
unlink(PATH("a/bar"));
|
||||
|
||||
rmdir(PATH("a/d/e"));
|
||||
rmdir(PATH("a/d"));
|
||||
|
||||
rmdir(PATH("a/b/c"));
|
||||
rmdir(PATH("a/b"));
|
||||
rmdir(PATH("a"));
|
||||
|
||||
cleanup_dir(testdir, testdir_files, 1);
|
||||
rmdir(testdir);
|
||||
|
||||
return -1;
|
||||
|
||||
#undef PATH2
|
||||
#undef PATH
|
||||
}
|
||||
|
||||
#ifndef __FreeBSD__
|
||||
static int test_mkfifo(void)
|
||||
{
|
||||
@@ -1461,6 +1676,7 @@ int main(int argc, char *argv[])
|
||||
err += test_mkdir();
|
||||
err += test_rename_file();
|
||||
err += test_rename_dir();
|
||||
err += test_rename_dir_loop();
|
||||
err += test_utime();
|
||||
err += test_truncate(0);
|
||||
err += test_truncate(testdatalen / 2);
|
||||
|
||||
Reference in New Issue
Block a user