summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrian Kendall <brian@briankendall.net>2022-04-01 12:28:38 -0400
committerBrian Kendall <brian@briankendall.net>2022-04-01 12:28:38 -0400
commita1fd187e7aba6447dae71730e24bc84341def1d4 (patch)
tree239d8183525ba872a285a3cfeaea7ed9cac46ab3
parenta116ca61b1b1e0bdcbca416bd82d72252d583c7a (diff)
downloadunionfs-fuse-git-a1fd187e7aba6447dae71730e24bc84341def1d4.tar.gz
Create directories as needed when renaming with preserve_branch
-rw-r--r--src/cow.c83
-rw-r--r--src/cow.h4
-rw-r--r--src/findbranch.c4
-rw-r--r--src/fuse_ops.c13
-rw-r--r--src/general.c95
-rw-r--r--src/general.h2
-rwxr-xr-xtest_all.py21
7 files changed, 131 insertions, 91 deletions
diff --git a/src/cow.c b/src/cow.c
index c4b7f8f..dcee6f9 100644
--- a/src/cow.c
+++ b/src/cow.c
@@ -28,95 +28,28 @@
/**
- * Actually create the directory here.
- */
-static int do_create(const char *path, int nbranch_ro, int nbranch_rw) {
- DBG("%s\n", path);
-
- char dirp[PATHLEN_MAX]; // dir path to create
- sprintf(dirp, "%s%s", uopt.branches[nbranch_rw].path, path);
-
- struct stat buf;
- int res = stat(dirp, &buf);
- if (res != -1) RETURN(0); // already exists
-
- if (nbranch_ro == nbranch_rw) {
- // special case nbranch_ro = nbranch_rw, this is if we a create
- // unionfs meta directories, so not directly on cow operations
- buf.st_mode = S_IRWXU | S_IRWXG;
- } else {
- // data from the ro-branch
- char o_dirp[PATHLEN_MAX]; // the pathname we want to copy
- sprintf(o_dirp, "%s%s", uopt.branches[nbranch_ro].path, path);
- res = stat(o_dirp, &buf);
- if (res == -1) RETURN(1); // lower level branch removed in the mean time?
- }
-
- res = mkdir(dirp, buf.st_mode);
- if (res == -1) {
- USYSLOG(LOG_DAEMON, "Creating %s failed: \n", dirp);
- RETURN(1);
- }
-
- if (nbranch_ro == nbranch_rw) RETURN(0); // the special case again
-
- if (setfile(dirp, &buf)) RETURN(1); // directory already removed by another process?
-
- // TODO: time, but its values are modified by the next dir/file creation steps?
-
- RETURN(0);
-}
-
-/**
* l_nbranch (lower nbranch than nbranch) is write protected, create the dir path on
* nbranch for an other COW operation.
*/
-int path_create(const char *path, int nbranch_ro, int nbranch_rw) {
+int path_create_cow(const char *path, int nbranch_ro, int nbranch_rw) {
DBG("%s\n", path);
if (!uopt.cow_enabled) RETURN(0);
- char p[PATHLEN_MAX];
- if (BUILD_PATH(p, uopt.branches[nbranch_rw].path, path)) RETURN(-ENAMETOOLONG);
-
- struct stat st;
- if (!stat(p, &st)) {
- // path does already exists, no need to create it
- RETURN(0);
- }
-
- char *walk = (char *)path;
-
- // first slashes, e.g. we have path = /dir1/dir2/, will set walk = dir1/dir2/
- while (*walk == '/') walk++;
-
- do {
- // walk over the directory name, walk will now be /dir2
- while (*walk != '\0' && *walk != '/') walk++;
-
- // +1 due to \0, which gets added automatically
- snprintf(p, (walk - path) + 1, "%s", path); // walk - path = strlen(/dir1)
- int res = do_create(p, nbranch_ro, nbranch_rw);
- if (res) RETURN(res); // creating the directory failed
-
- // as above the do loop, walk over the next slashes, walk = dir2/
- while (*walk == '/') walk++;
- } while (*walk != '\0');
-
- RETURN(0);
+ return path_create(path, nbranch_ro, nbranch_rw);
}
/**
- * Same as path_create(), but ignore the last segment in path,
+ * Same as path_create_cow(), but ignore the last segment in path,
* i.e. it might be a filename.
*/
-int path_create_cutlast(const char *path, int nbranch_ro, int nbranch_rw) {
+int path_create_cutlast_cow(const char *path, int nbranch_ro, int nbranch_rw) {
DBG("%s\n", path);
char *dname = u_dirname(path);
if (dname == NULL)
RETURN(-ENOMEM);
- int ret = path_create(dname, nbranch_ro, nbranch_rw);
+ int ret = path_create_cow(dname, nbranch_ro, nbranch_rw);
free(dname);
RETURN(ret);
@@ -129,7 +62,7 @@ int cow_cp(const char *path, int branch_ro, int branch_rw, bool copy_dir) {
DBG("%s\n", path);
// create the path to the file
- path_create_cutlast(path, branch_ro, branch_rw);
+ path_create_cutlast_cow(path, branch_ro, branch_rw);
char from[PATHLEN_MAX], to[PATHLEN_MAX];
if (BUILD_PATH(from, uopt.branches[branch_ro].path, path))
@@ -163,7 +96,7 @@ int cow_cp(const char *path, int branch_ro, int branch_rw, bool copy_dir) {
if (copy_dir) {
res = copy_directory(path, branch_ro, branch_rw);
} else {
- res = path_create(path, branch_ro, branch_rw);
+ res = path_create_cow(path, branch_ro, branch_rw);
}
break;
case S_IFBLK:
@@ -190,7 +123,7 @@ int copy_directory(const char *path, int branch_ro, int branch_rw) {
DBG("%s\n", path);
/* create the directory on the destination branch */
- int res = path_create(path, branch_ro, branch_rw);
+ int res = path_create_cow(path, branch_ro, branch_rw);
if (res != 0) {
RETURN(res);
}
diff --git a/src/cow.h b/src/cow.h
index 04d6dfb..389224a 100644
--- a/src/cow.h
+++ b/src/cow.h
@@ -10,8 +10,8 @@
#include <sys/stat.h>
int cow_cp(const char *path, int branch_ro, int branch_rw, bool copy_dir);
-int path_create(const char *path, int nbranch_ro, int nbranch_rw);
-int path_create_cutlast(const char *path, int nbranch_ro, int nbranch_rw);
+int path_create_cow(const char *path, int nbranch_ro, int nbranch_rw);
+int path_create_cutlast_cow(const char *path, int nbranch_ro, int nbranch_rw);
int copy_directory(const char *path, int branch_ro, int branch_rw);
#endif
diff --git a/src/findbranch.c b/src/findbranch.c
index af526ea..f61668d 100644
--- a/src/findbranch.c
+++ b/src/findbranch.c
@@ -205,7 +205,7 @@ int __find_rw_branch_cutlast(const char *path, int rw_hint) {
goto out;
}
- if (path_create(dname, branch, branch_rw) == 0) branch = branch_rw; // path successfully copied
+ if (path_create_cow(dname, branch, branch_rw) == 0) branch = branch_rw; // path successfully copied
out:
free(dname);
@@ -230,7 +230,7 @@ int find_rw_branch_cow(const char *path) {
* copy-on-write
* Find path in a union branch and if this branch is read-only,
* copy the file to a read-write branch.
- * NOTE: Don't call this to copy directories. Use path_create() for that!
+ * NOTE: Don't call this to copy directories. Use path_create_cow() for that!
* It will definitely fail, when a ro-branch is on top of a rw-branch
* and a directory is to be copied from ro- to rw-branch.
*/
diff --git a/src/fuse_ops.c b/src/fuse_ops.c
index 394c81b..adec4e5 100644
--- a/src/fuse_ops.c
+++ b/src/fuse_ops.c
@@ -482,6 +482,7 @@ static int unionfs_rename(const char *from, const char *to, unsigned int flags)
DBG("from %s to %s\n", from, to);
+ int res;
bool is_dir = false; // is 'from' a file or directory
int j = find_rw_branch_cutlast(to);
@@ -492,15 +493,14 @@ static int unionfs_rename(const char *from, const char *to, unsigned int flags)
if (uopt.preserve_branch && uopt.branches[i].rw) {
if (branch_contains_file_or_parent_dir(i, to)) {
- DBG("...from branch contains target directory, no need to change branches\n");
+ DBG("preserving branch\n");
j = i;
} else {
- DBG("...creating target directory in from branch, no need to change branches\n");
-
- if (path_create_cutlast(to, j, i) == 0) {
+ DBG("preserving branch and creating directories to do so\n");
+ res = path_create_cutlast(to, j, i);
+
+ if (res == 0) {
j = i;
- } else {
- DBG("...failed to create path in from branch\n");
}
}
}
@@ -526,7 +526,6 @@ static int unionfs_rename(const char *from, const char *to, unsigned int flags)
else if (ftype == IS_DIR)
is_dir = true;
- int res;
if (!uopt.branches[i].rw) {
// since original file is on a read-only branch, we copied the from file to a writable branch,
// but since we will rename from, we also need to hide the from file on the read-only branch
diff --git a/src/general.c b/src/general.c
index 779bde5..47ab062 100644
--- a/src/general.c
+++ b/src/general.c
@@ -27,6 +27,7 @@
#include "opts.h"
#include "string.h"
#include "cow.h"
+#include "cow_utils.h"
#include "findbranch.h"
#include "general.h"
#include "debug.h"
@@ -143,7 +144,7 @@ static int do_create_whiteout(const char *path, int branch_rw, enum whiteout mod
// p MUST be without path to branch prefix here! 2 x branch_rw is correct here!
// this creates e.g. branch/.unionfs/some_directory
- path_create_cutlast(metapath, branch_rw, branch_rw);
+ path_create_cutlast_cow(metapath, branch_rw, branch_rw);
char p[PATHLEN_MAX];
if (BUILD_PATH(p, uopt.branches[branch_rw].path, metapath)) RETURN(-1);
@@ -213,3 +214,95 @@ int set_owner(const char *path) {
}
RETURN(0);
}
+
+/**
+ * Actually create the directory here.
+ */
+static int do_create(const char *path, int nbranch_ro, int nbranch_rw) {
+ DBG("%s\n", path);
+
+ char dirp[PATHLEN_MAX]; // dir path to create
+ sprintf(dirp, "%s%s", uopt.branches[nbranch_rw].path, path);
+
+ struct stat buf;
+ int res = stat(dirp, &buf);
+ if (res != -1) RETURN(0); // already exists
+
+ if (nbranch_ro == nbranch_rw) {
+ // special case nbranch_ro = nbranch_rw, this is if we a create
+ // unionfs meta directories, so not directly on cow operations
+ buf.st_mode = S_IRWXU | S_IRWXG;
+ } else {
+ // data from the ro-branch
+ char o_dirp[PATHLEN_MAX]; // the pathname we want to copy
+ sprintf(o_dirp, "%s%s", uopt.branches[nbranch_ro].path, path);
+ res = stat(o_dirp, &buf);
+ if (res == -1) RETURN(1); // lower level branch removed in the mean time?
+ }
+
+ res = mkdir(dirp, buf.st_mode);
+ if (res == -1) {
+ USYSLOG(LOG_DAEMON, "Creating %s failed: \n", dirp);
+ RETURN(1);
+ }
+
+ if (nbranch_ro == nbranch_rw) RETURN(0); // the special case again
+
+ if (setfile(dirp, &buf)) RETURN(1); // directory already removed by another process?
+
+ // TODO: time, but its values are modified by the next dir/file creation steps?
+
+ RETURN(0);
+}
+
+/**
+ * create the dir path on nbranch_rw matching same path on nbranch_ro
+ */
+int path_create(const char *path, int nbranch_ro, int nbranch_rw) {
+ DBG("%s\n", path);
+
+ char p[PATHLEN_MAX];
+ if (BUILD_PATH(p, uopt.branches[nbranch_rw].path, path)) RETURN(-ENAMETOOLONG);
+
+ struct stat st;
+ if (!stat(p, &st)) {
+ // path does already exists, no need to create it
+ RETURN(0);
+ }
+
+ char *walk = (char *)path;
+
+ // first slashes, e.g. we have path = /dir1/dir2/, will set walk = dir1/dir2/
+ while (*walk == '/') walk++;
+
+ do {
+ // walk over the directory name, walk will now be /dir2
+ while (*walk != '\0' && *walk != '/') walk++;
+
+ // +1 due to \0, which gets added automatically
+ snprintf(p, (walk - path) + 1, "%s", path); // walk - path = strlen(/dir1)
+ int res = do_create(p, nbranch_ro, nbranch_rw);
+ if (res) RETURN(res); // creating the directory failed
+
+ // as above the do loop, walk over the next slashes, walk = dir2/
+ while (*walk == '/') walk++;
+ } while (*walk != '\0');
+
+ RETURN(0);
+}
+
+/**
+ * Same as path_create(), but ignore the last segment in path,
+ * i.e. it might be a filename.
+ */
+int path_create_cutlast(const char *path, int nbranch_ro, int nbranch_rw) {
+ DBG("%s\n", path);
+
+ char *dname = u_dirname(path);
+ if (dname == NULL)
+ RETURN(-ENOMEM);
+ int ret = path_create(dname, nbranch_ro, nbranch_rw);
+ free(dname);
+
+ RETURN(ret);
+} \ No newline at end of file
diff --git a/src/general.h b/src/general.h
index 33a67c6..996a168 100644
--- a/src/general.h
+++ b/src/general.h
@@ -27,6 +27,8 @@ int hide_dir(const char *path, int branch_rw);
filetype_t path_is_dir (const char *path);
int maybe_whiteout(const char *path, int branch_rw, enum whiteout mode);
int set_owner(const char *path);
+int path_create(const char *path, int nbranch_ro, int nbranch_rw);
+int path_create_cutlast(const char *path, int nbranch_ro, int nbranch_rw);
#endif
diff --git a/test_all.py b/test_all.py
index 742e423..8999ea8 100755
--- a/test_all.py
+++ b/test_all.py
@@ -516,8 +516,8 @@ class UnionFS_RW_RW_PreserveBranch_TestCase(Common, unittest.TestCase):
def setUp(self):
super().setUp()
self.mount('%s -o preserve_branch rw1=rw:rw2=rw union' % self.unionfs_path)
-
- def test_move_from_branch2_to_common(self):
+
+ def test_move_from_branch_to_common(self):
write_to_file('rw2/rw2_dir/rw2_file2', 'something')
self.assertTrue(os.access('union/rw2_dir/rw2_file2', os.F_OK))
self.assertFalse(os.access('union/common_dir/rw2_file2', os.F_OK))
@@ -527,16 +527,29 @@ class UnionFS_RW_RW_PreserveBranch_TestCase(Common, unittest.TestCase):
self.assertFalse(os.access('union/rw2_dir/rw2_file2', os.F_OK))
self.assertTrue(os.access('rw2/common_dir/rw2_file2', os.F_OK))
self.assertTrue(os.access('union/common_dir/rw2_file2', os.F_OK))
-
+
def test_move_from_branch1_to_branch2(self):
self.assertTrue(os.access('rw1/rw1_dir/rw1_file', os.F_OK))
+ self.assertTrue(os.access('union/rw1_dir/rw1_file', os.F_OK))
self.assertFalse(os.access('rw2/rw2_dir/rw1_file', os.F_OK))
+ self.assertFalse(os.access('rw1/rw2_dir/rw1_file', os.F_OK))
self.assertFalse(os.access('union/rw2_dir/rw1_file', os.F_OK))
os.rename('union/rw1_dir/rw1_file', 'union/rw2_dir/rw1_file')
self.assertFalse(os.access('rw1/rw1_dir/rw1_file', os.F_OK))
- self.assertTrue(os.access('rw2/rw2_dir/rw1_file', os.F_OK))
+ self.assertTrue(os.access('rw1/rw2_dir/rw1_file', os.F_OK))
self.assertTrue(os.access('union/rw2_dir/rw1_file', os.F_OK))
+ def test_move_from_branch2_to_branch1(self):
+ self.assertTrue(os.access('rw2/rw2_dir/rw2_file', os.F_OK))
+ self.assertTrue(os.access('union/rw2_dir/rw2_file', os.F_OK))
+ self.assertFalse(os.access('rw1/rw1_dir/rw2_file', os.F_OK))
+ self.assertFalse(os.access('rw2/rw1_dir/rw2_file', os.F_OK))
+ self.assertFalse(os.access('union/rw1_dir/rw2_file', os.F_OK))
+ os.rename('union/rw2_dir/rw2_file', 'union/rw1_dir/rw2_file')
+ self.assertFalse(os.access('rw2/rw2_dir/rw2_file', os.F_OK))
+ self.assertTrue(os.access('rw2/rw1_dir/rw2_file', os.F_OK))
+ self.assertTrue(os.access('union/rw1_dir/rw2_file', os.F_OK))
+
if __name__ == '__main__':
unittest.main()