summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBernd Schubert <bernd.schubert@fastmail.fm>2010-06-01 22:15:40 +0200
committerBernd Schubert <bernd.schubert@fastmail.fm>2010-06-01 22:15:40 +0200
commit2c7b6aca8165aafbc38e71b106ef6339948f236c (patch)
treeddfc2226d24af672c55f8f65fcb2b6167df4579e
parentc4620de7157eed358b1f88679eb820fa7f7f25b3 (diff)
downloadunionfs-fuse-2c7b6aca8165aafbc38e71b106ef6339948f236c.tar.gz
Fix hard link issues if we have several rw branches. Debian Bug#509516.
COW functions always use the highest level rw branch, but if we have several rw branches and the file to link from is located on a lower rw branch, cow functions decided to use the highest rw branch to link the file to. However, if those branches are different filesystems, that is not allowed How to reproduce: ro/destdir rw_low/fromdir/testfile /tmp/rw_hi/ (/tmp is a different filesystem) union/ unionfs-fuse -d -o cow /tmp/rw_hi=RW:rw_low=RW:ro=RO union/ link union/fromdir/testfile union/destdir/testfile ==> link: cannot create link `union/destdir/testfile' to `union/fromdir/testfile': Invalid cross-device link This patch fixed that issue.
-rw-r--r--src/findbranch.c18
-rw-r--r--src/findbranch.h1
-rw-r--r--src/unionfs.c5
3 files changed, 20 insertions, 4 deletions
diff --git a/src/findbranch.c b/src/findbranch.c
index 145edf6..9581c3a 100644
--- a/src/findbranch.c
+++ b/src/findbranch.c
@@ -107,8 +107,10 @@ int find_rorw_branch(const char *path) {
/**
* Find a writable branch. If file does not exist, we check for
* the parent directory.
+ * @path - the path to find or to copy (with last element cut off)
+ * @ rw_hint - the rw branch to copy to, set to -1 to autodetect it
*/
-int find_rw_branch_cutlast(const char *path) {
+int __find_rw_branch_cutlast(const char *path, int rw_hint) {
DBG_IN();
int branch = find_rw_branch_cow(path);
@@ -136,8 +138,12 @@ int find_rw_branch_cutlast(const char *path) {
goto out;
}
+ int branch_rw;
// since it is a directory, any rw-branch is fine
- int branch_rw = find_lowest_rw_branch(uopt.nbranches);
+ if (rw_hint == -1)
+ branch_rw = find_lowest_rw_branch(uopt.nbranches);
+ else
+ branch_rw = rw_hint;
// no writable branch found, we must return an error
if (branch_rw < 0) {
@@ -155,6 +161,14 @@ out:
}
/**
+ * Call __find_rw_branch_cutlast()
+ */
+int find_rw_branch_cutlast(const char *path) {
+ int rw_hint = -1; // autodetect rw_branch
+ return __find_rw_branch_cutlast(path, rw_hint);
+}
+
+/**
* copy-on-write
* Find path in a union branch and if this branch is read-only,
* copy the file to a read-write branch.
diff --git a/src/findbranch.h b/src/findbranch.h
index 6378d86..56cfc08 100644
--- a/src/findbranch.h
+++ b/src/findbranch.h
@@ -15,6 +15,7 @@ typedef enum searchflag {
int find_rorw_branch(const char *path);
int find_lowest_rw_branch(int branch_ro);
int find_rw_branch_cutlast(const char *path);
+int __find_rw_branch_cutlast(const char *path, int rw_hint);
int find_rw_branch_cow(const char *path);
#endif
diff --git a/src/unionfs.c b/src/unionfs.c
index cd58a83..091f00d 100644
--- a/src/unionfs.c
+++ b/src/unionfs.c
@@ -241,10 +241,11 @@ static int unionfs_link(const char *from, const char *to) {
int i = find_rw_branch_cow(from);
if (i == -1) return -errno;
- // FIXME, we actually MUST COW to i
- int j = find_rw_branch_cutlast(to);
+ int j = __find_rw_branch_cutlast(to, i);
if (j == -1) return -errno;
+ DBG("from branch: %d to branch: %d\n", i, j);
+
char f[PATHLEN_MAX], t[PATHLEN_MAX];
if (BUILD_PATH(f, uopt.branches[i].path, from)) return -ENAMETOOLONG;
if (BUILD_PATH(t, uopt.branches[j].path, to)) return -ENAMETOOLONG;