From: Erez Zadok <[email protected]>
Fixes, updates, and better documentation for the file-copyup functionality.
Include two additional utility functions useful for copyup code callers.
Parent directory copyup updates: create_parents now takes a string name
instead of the whole dentry.
Signed-off-by: Erez Zadok <[email protected]>
Signed-off-by: Josef 'Jeff' Sipek <[email protected]>
---
fs/unionfs/commonfops.c | 135 +++++++++++--------
fs/unionfs/copyup.c | 348 +++++++++++++++++++++++++++-------------------
fs/unionfs/inode.c | 33 ++++--
fs/unionfs/rename.c | 26 +++--
fs/unionfs/subr.c | 4 +-
fs/unionfs/union.h | 11 +-
fs/unionfs/unlink.c | 2 +
7 files changed, 338 insertions(+), 221 deletions(-)
diff --git a/fs/unionfs/commonfops.c b/fs/unionfs/commonfops.c
index edb52c0..64bd0bd 100644
--- a/fs/unionfs/commonfops.c
+++ b/fs/unionfs/commonfops.c
@@ -31,7 +31,6 @@ static int copyup_deleted_file(struct file *file, struct dentry *dentry,
const int countersize = sizeof(counter) * 2;
const int nlen = sizeof(".unionfs") + i_inosize + countersize - 1;
char name[nlen + 1];
-
int err;
struct dentry *tmp_dentry = NULL;
struct dentry *lower_dentry;
@@ -42,7 +41,6 @@ static int copyup_deleted_file(struct file *file, struct dentry *dentry,
sprintf(name, ".unionfs%*.*lx",
i_inosize, i_inosize, lower_dentry->d_inode->i_ino);
-retry:
/*
* Loop, looking for an unused temp name to copyup to.
*
@@ -52,6 +50,7 @@ retry:
* the name exists in the dest branch, but it'd be nice to catch it
* sooner than later.
*/
+retry:
tmp_dentry = NULL;
do {
char *suffix = name + nlen - countersize;
@@ -73,14 +72,20 @@ retry:
dput(tmp_dentry);
err = copyup_named_file(dentry->d_parent->d_inode, file, name, bstart,
- bindex, file->f_dentry->d_inode->i_size);
- if (err == -EEXIST)
- goto retry;
- else if (err)
+ bindex, file->f_path.dentry->d_inode->i_size);
+ if (err) {
+ if (err == -EEXIST)
+ goto retry;
goto out;
+ }
/* bring it to the same state as an unlinked file */
lower_dentry = unionfs_lower_dentry_idx(dentry, dbstart(dentry));
+ if (!unionfs_lower_inode_idx(dentry->d_inode, bindex)) {
+ atomic_inc(&lower_dentry->d_inode->i_count);
+ unionfs_set_lower_inode_idx(dentry->d_inode, bindex,
+ lower_dentry->d_inode);
+ }
lower_dir_dentry = lock_parent(lower_dentry);
err = vfs_unlink(lower_dir_dentry->d_inode, lower_dentry);
unlock_dir(lower_dir_dentry);
@@ -96,48 +101,52 @@ out:
static void cleanup_file(struct file *file)
{
int bindex, bstart, bend;
- struct file **lf;
- struct super_block *sb = file->f_dentry->d_sb;
+ struct file **lower_files;
+ struct file *lower_file;
+ struct super_block *sb = file->f_path.dentry->d_sb;
- lf = UNIONFS_F(file)->lower_files;
+ lower_files = UNIONFS_F(file)->lower_files;
bstart = fbstart(file);
bend = fbend(file);
for (bindex = bstart; bindex <= bend; bindex++) {
- if (unionfs_lower_file_idx(file, bindex)) {
- /*
- * Find new index of matching branch with an open
- * file, since branches could have been added or
- * deleted causing the one with open files to shift.
- */
- int i; /* holds (possibly) updated branch index */
- int old_bid;
-
- old_bid = UNIONFS_F(file)->saved_branch_ids[bindex];
- i = branch_id_to_idx(sb, old_bid);
- if (i < 0)
- printk(KERN_ERR "unionfs: no superblock for "
- "file %p\n", file);
- else {
- /* decrement count of open files */
- branchput(sb, i);
- /*
- * fput will perform an mntput for us on the
- * correct branch. Although we're using the
- * file's old branch configuration, bindex,
- * which is the old index, correctly points
- * to the right branch in the file's branch
- * list. In other words, we're going to
- * mntput the correct branch even if
- * branches have been added/removed.
- */
- fput(unionfs_lower_file_idx(file, bindex));
- }
+ int i; /* holds (possibly) updated branch index */
+ int old_bid;
+
+ lower_file = unionfs_lower_file_idx(file, bindex);
+ if (!lower_file)
+ continue;
+
+ /*
+ * Find new index of matching branch with an open
+ * file, since branches could have been added or
+ * deleted causing the one with open files to shift.
+ */
+ old_bid = UNIONFS_F(file)->saved_branch_ids[bindex];
+ i = branch_id_to_idx(sb, old_bid);
+ if (i < 0) {
+ printk(KERN_ERR "unionfs: no superblock for "
+ "file %p\n", file);
+ continue;
}
+
+ /* decrement count of open files */
+ branchput(sb, i);
+ /*
+ * fput will perform an mntput for us on the correct branch.
+ * Although we're using the file's old branch configuration,
+ * bindex, which is the old index, correctly points to the
+ * right branch in the file's branch list. In other words,
+ * we're going to mntput the correct branch even if branches
+ * have been added/removed.
+ */
+ fput(lower_file);
+ UNIONFS_F(file)->lower_files[bindex] = NULL;
+ UNIONFS_F(file)->saved_branch_ids[bindex] = -1;
}
UNIONFS_F(file)->lower_files = NULL;
- kfree(lf);
+ kfree(lower_files);
kfree(UNIONFS_F(file)->saved_branch_ids);
/* set to NULL because caller needs to know if to kfree on error */
UNIONFS_F(file)->saved_branch_ids = NULL;
@@ -209,7 +218,6 @@ static int open_highest_file(struct file *file, int willwrite)
dget(lower_dentry);
unionfs_mntget(dentry, bstart);
- branchget(sb, bstart);
lower_file = dentry_open(lower_dentry,
unionfs_lower_mnt_idx(dentry, bstart),
file->f_flags);
@@ -217,6 +225,7 @@ static int open_highest_file(struct file *file, int willwrite)
err = PTR_ERR(lower_file);
goto out;
}
+ branchget(sb, bstart);
unionfs_set_lower_file(file, lower_file);
/* Fix up the position. */
lower_file->f_pos = file->f_pos;
@@ -227,19 +236,20 @@ out:
}
/* perform a delayed copyup of a read-write file on a read-only branch */
-static int do_delayed_copyup(struct file *file, struct dentry *dentry)
+static int do_delayed_copyup(struct file *file)
{
int bindex, bstart, bend, err = 0;
+ struct dentry *dentry = file->f_path.dentry;
struct inode *parent_inode = dentry->d_parent->d_inode;
- loff_t inode_size = file->f_dentry->d_inode->i_size;
+ loff_t inode_size = dentry->d_inode->i_size;
bstart = fbstart(file);
bend = fbend(file);
- BUG_ON(!S_ISREG(file->f_dentry->d_inode->i_mode));
+ BUG_ON(!S_ISREG(dentry->d_inode->i_mode));
for (bindex = bstart - 1; bindex >= 0; bindex--) {
- if (!d_deleted(file->f_dentry))
+ if (!d_deleted(dentry))
err = copyup_file(parent_inode, file, bstart,
bindex, inode_size);
else
@@ -249,17 +259,34 @@ static int do_delayed_copyup(struct file *file, struct dentry *dentry)
if (!err)
break;
}
- if (!err && (bstart > fbstart(file))) {
- bend = fbend(file);
- for (bindex = bstart; bindex <= bend; bindex++) {
- if (unionfs_lower_file_idx(file, bindex)) {
- branchput(dentry->d_sb, bindex);
- fput(unionfs_lower_file_idx(file, bindex));
- unionfs_set_lower_file_idx(file, bindex, NULL);
- }
+ if (err || (bstart <= fbstart(file)))
+ goto out;
+ bend = fbend(file);
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ if (unionfs_lower_file_idx(file, bindex)) {
+ branchput(dentry->d_sb, bindex);
+ fput(unionfs_lower_file_idx(file, bindex));
+ unionfs_set_lower_file_idx(file, bindex, NULL);
+ }
+ if (unionfs_lower_mnt_idx(dentry, bindex)) {
+ unionfs_mntput(dentry, bindex);
+ unionfs_set_lower_mnt_idx(dentry, bindex, NULL);
+ }
+ if (unionfs_lower_dentry_idx(dentry, bindex)) {
+ BUG_ON(!dentry->d_inode);
+ iput(unionfs_lower_inode_idx(dentry->d_inode, bindex));
+ unionfs_set_lower_inode_idx(dentry->d_inode, bindex,
+ NULL);
+ dput(unionfs_lower_dentry_idx(dentry, bindex));
+ unionfs_set_lower_dentry_idx(dentry, bindex, NULL);
}
- fbend(file) = bend;
}
+ /* for reg file, we only open it "once" */
+ fbend(file) = fbstart(file);
+ set_dbend(dentry, dbstart(dentry));
+ ibend(dentry->d_inode) = ibstart(dentry->d_inode);
+
+out:
return err;
}
@@ -348,7 +375,7 @@ int unionfs_file_revalidate(struct file *file, int willwrite)
is_robranch(dentry)) {
printk(KERN_DEBUG "unionfs: Doing delayed copyup of a "
"read-write file on a read-only branch.\n");
- err = do_delayed_copyup(file, dentry);
+ err = do_delayed_copyup(file);
}
out:
diff --git a/fs/unionfs/copyup.c b/fs/unionfs/copyup.c
index 410ce07..9c476cd 100644
--- a/fs/unionfs/copyup.c
+++ b/fs/unionfs/copyup.c
@@ -23,15 +23,6 @@
* Documentation/filesystems/unionfs/concepts.txt
*/
-/* forward definitions */
-static int copyup_named_dentry(struct inode *dir, struct dentry *dentry,
- int bstart, int new_bindex, const char *name,
- int namelen, struct file **copyup_file,
- loff_t len);
-static struct dentry *create_parents_named(struct inode *dir,
- struct dentry *dentry,
- const char *name, int bindex);
-
#ifdef CONFIG_UNION_FS_XATTR
/* copyup all extended attrs for a given dentry */
static int copyup_xattrs(struct dentry *old_lower_dentry,
@@ -101,7 +92,14 @@ out:
}
#endif /* CONFIG_UNION_FS_XATTR */
-/* Determine the mode based on the copyup flags, and the existing dentry. */
+/*
+ * Determine the mode based on the copyup flags, and the existing dentry.
+ *
+ * Handle file systems which may not support certain options. For example
+ * jffs2 doesn't allow one to chmod a symlink. So we ignore such harmless
+ * errors, rather than propagating them up, which results in copyup errors
+ * and errors returned back to users.
+ */
static int copyup_permissions(struct super_block *sb,
struct dentry *old_lower_dentry,
struct dentry *new_lower_dentry)
@@ -113,30 +111,31 @@ static int copyup_permissions(struct super_block *sb,
newattrs.ia_atime = i->i_atime;
newattrs.ia_mtime = i->i_mtime;
newattrs.ia_ctime = i->i_ctime;
-
newattrs.ia_gid = i->i_gid;
newattrs.ia_uid = i->i_uid;
-
- newattrs.ia_mode = i->i_mode;
-
newattrs.ia_valid = ATTR_CTIME | ATTR_ATIME | ATTR_MTIME |
ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_FORCE |
- ATTR_GID | ATTR_UID | ATTR_MODE;
+ ATTR_GID | ATTR_UID;
+ err = notify_change(new_lower_dentry, &newattrs);
+ if (err)
+ goto out;
+ /* now try to change the mode and ignore EOPNOTSUPP on symlinks */
+ newattrs.ia_mode = i->i_mode;
+ newattrs.ia_valid = ATTR_MODE | ATTR_FORCE;
err = notify_change(new_lower_dentry, &newattrs);
+ if (err == -EOPNOTSUPP &&
+ S_ISLNK(new_lower_dentry->d_inode->i_mode)) {
+ printk(KERN_WARNING
+ "unionfs: changing \"%s\" symlink mode unsupported\n",
+ new_lower_dentry->d_name.name);
+ err = 0;
+ }
+out:
return err;
}
-int copyup_dentry(struct inode *dir, struct dentry *dentry,
- int bstart, int new_bindex,
- struct file **copyup_file, loff_t len)
-{
- return copyup_named_dentry(dir, dentry, bstart, new_bindex,
- dentry->d_name.name,
- dentry->d_name.len, copyup_file, len);
-}
-
/*
* create the new device/file/directory - use copyup_permission to copyup
* times, and mode
@@ -202,6 +201,7 @@ static int __copyup_reg_data(struct dentry *dentry,
struct super_block *sb = dentry->d_sb;
struct file *input_file;
struct file *output_file;
+ struct vfsmount *output_mnt;
mm_segment_t old_fs;
char *buf = NULL;
ssize_t read_bytes, write_bytes;
@@ -211,6 +211,7 @@ static int __copyup_reg_data(struct dentry *dentry,
/* open old file */
unionfs_mntget(dentry, old_bindex);
branchget(sb, old_bindex);
+ /* dentry_open calls dput and mntput if it returns an error */
input_file = dentry_open(old_lower_dentry,
unionfs_lower_mnt_idx(dentry, old_bindex),
O_RDONLY | O_LARGEFILE);
@@ -226,10 +227,9 @@ static int __copyup_reg_data(struct dentry *dentry,
/* open new file */
dget(new_lower_dentry);
- unionfs_mntget(dentry, new_bindex);
+ output_mnt = unionfs_mntget(sb->s_root, new_bindex);
branchget(sb, new_bindex);
- output_file = dentry_open(new_lower_dentry,
- unionfs_lower_mnt_idx(dentry, new_bindex),
+ output_file = dentry_open(new_lower_dentry, output_mnt,
O_RDWR | O_LARGEFILE);
if (IS_ERR(output_file)) {
err = PTR_ERR(output_file);
@@ -331,11 +331,21 @@ static void __clear(struct dentry *dentry, struct dentry *old_lower_dentry,
dput(old_lower_dentry);
}
-/* copy up a dentry to a file of specified name */
-static int copyup_named_dentry(struct inode *dir, struct dentry *dentry,
- int bstart, int new_bindex, const char *name,
- int namelen, struct file **copyup_file,
- loff_t len)
+/*
+ * Copy up a dentry to a file of specified name.
+ *
+ * @dir: used to pull the ->i_sb to access other branches
+ * @dentry: the non-negative dentry whose lower_inode we should copy
+ * @bstart: the branch of the lower_inode to copy from
+ * @new_bindex: the branch to create the new file in
+ * @name: the name of the file to create
+ * @namelen: length of @name
+ * @copyup_file: the "struct file" to return (optional)
+ * @len: how many bytes to copy-up?
+ */
+int copyup_dentry(struct inode *dir, struct dentry *dentry, int bstart,
+ int new_bindex, const char *name, int namelen,
+ struct file **copyup_file, loff_t len)
{
struct dentry *new_lower_dentry;
struct dentry *old_lower_dentry = NULL;
@@ -363,8 +373,7 @@ static int copyup_named_dentry(struct inode *dir, struct dentry *dentry,
goto out;
/* Create the directory structure above this dentry. */
- new_lower_dentry =
- create_parents_named(dir, dentry, name, new_bindex);
+ new_lower_dentry = create_parents(dir, dentry, name, new_bindex);
if (IS_ERR(new_lower_dentry)) {
err = PTR_ERR(new_lower_dentry);
goto out;
@@ -479,6 +488,25 @@ out_free:
dput(old_lower_dentry);
kfree(symbuf);
+ if (err)
+ goto out;
+ if (!S_ISDIR(dentry->d_inode->i_mode)) {
+ unionfs_postcopyup_release(dentry);
+ if (!unionfs_lower_inode(dentry->d_inode)) {
+ /*
+ * If we got here, then we copied up to an
+ * unlinked-open file, whose name is .unionfsXXXXX.
+ */
+ struct inode *inode = new_lower_dentry->d_inode;
+ atomic_inc(&inode->i_count);
+ unionfs_set_lower_inode_idx(dentry->d_inode,
+ ibstart(dentry->d_inode),
+ inode);
+ }
+ }
+ unionfs_postcopyup_setmnt(dentry);
+ /* sync inode times from copied-up inode to our inode */
+ unionfs_copy_attr_times(dentry->d_inode);
out:
return err;
}
@@ -494,9 +522,8 @@ int copyup_named_file(struct inode *dir, struct file *file, char *name,
int err = 0;
struct file *output_file = NULL;
- err = copyup_named_dentry(dir, file->f_dentry, bstart,
- new_bindex, name, strlen(name), &output_file,
- len);
+ err = copyup_dentry(dir, file->f_path.dentry, bstart, new_bindex,
+ name, strlen(name), &output_file, len);
if (!err) {
fbstart(file) = new_bindex;
unionfs_set_lower_file_idx(file, new_bindex, output_file);
@@ -514,8 +541,10 @@ int copyup_file(struct inode *dir, struct file *file, int bstart,
{
int err = 0;
struct file *output_file = NULL;
+ struct dentry *dentry = file->f_path.dentry;
- err = copyup_dentry(dir, file->f_dentry, bstart, new_bindex,
+ err = copyup_dentry(dir, dentry, bstart, new_bindex,
+ dentry->d_name.name, dentry->d_name.len,
&output_file, len);
if (!err) {
fbstart(file) = new_bindex;
@@ -525,17 +554,6 @@ int copyup_file(struct inode *dir, struct file *file, int bstart,
return err;
}
-/*
- * This function replicates the directory structure up-to given dentry in the
- * bindex branch. Can create directory structure recursively to the right
- * also.
- */
-struct dentry *create_parents(struct inode *dir, struct dentry *dentry,
- int bindex)
-{
- return create_parents_named(dir, dentry, dentry->d_name.name, bindex);
-}
-
/* purge a dentry's lower-branch states (dput/mntput, etc.) */
static void __cleanup_dentry(struct dentry *dentry, int bindex,
int old_bstart, int old_bend)
@@ -615,9 +633,8 @@ static void __set_dentry(struct dentry *upper, struct dentry *lower,
* This function replicates the directory structure up-to given dentry
* in the bindex branch.
*/
-static struct dentry *create_parents_named(struct inode *dir,
- struct dentry *dentry,
- const char *name, int bindex)
+struct dentry *create_parents(struct inode *dir, struct dentry *dentry,
+ const char *name, int bindex)
{
int err;
struct dentry *child_dentry;
@@ -626,10 +643,8 @@ static struct dentry *create_parents_named(struct inode *dir,
struct dentry *lower_dentry = NULL;
const char *childname;
unsigned int childnamelen;
-
int nr_dentry;
int count = 0;
-
int old_bstart;
int old_bend;
struct dentry **path = NULL;
@@ -681,7 +696,8 @@ static struct dentry *create_parents_named(struct inode *dir,
void *p;
nr_dentry *= 2;
- p = krealloc(path, nr_dentry * sizeof(struct dentry *), GFP_KERNEL);
+ p = krealloc(path, nr_dentry * sizeof(struct dentry *),
+ GFP_KERNEL);
if (!p) {
lower_dentry = ERR_PTR(-ENOMEM);
goto out;
@@ -697,106 +713,100 @@ static struct dentry *create_parents_named(struct inode *dir,
sb = dentry->d_sb;
/*
- * This is basically while(child_dentry != dentry). This loop is
- * horrible to follow and should be replaced with cleaner code.
+ * This code goes between the begin/end labels and basically
+ * emulates a while(child_dentry != dentry), only cleaner and
+ * shorter than what would be a much longer while loop.
*/
- while (1) {
- /* get lower parent dir in the current branch */
- lower_parent_dentry =
- unionfs_lower_dentry_idx(parent_dentry, bindex);
- unionfs_unlock_dentry(parent_dentry);
-
- /* init the values to lookup */
- childname = child_dentry->d_name.name;
- childnamelen = child_dentry->d_name.len;
-
- if (child_dentry != dentry) {
- /* lookup child in the underlying file system */
- lower_dentry =
- lookup_one_len(childname, lower_parent_dentry,
- childnamelen);
- if (IS_ERR(lower_dentry))
- goto out;
- } else {
+begin:
+ /* get lower parent dir in the current branch */
+ lower_parent_dentry = unionfs_lower_dentry_idx(parent_dentry, bindex);
+ unionfs_unlock_dentry(parent_dentry);
+
+ /* init the values to lookup */
+ childname = child_dentry->d_name.name;
+ childnamelen = child_dentry->d_name.len;
+
+ if (child_dentry != dentry) {
+ /* lookup child in the underlying file system */
+ lower_dentry = lookup_one_len(childname, lower_parent_dentry,
+ childnamelen);
+ if (IS_ERR(lower_dentry))
+ goto out;
+ } else {
+ /*
+ * Is the name a whiteout of the child name ? lookup the
+ * whiteout child in the underlying file system
+ */
+ lower_dentry = lookup_one_len(name, lower_parent_dentry,
+ strlen(name));
+ if (IS_ERR(lower_dentry))
+ goto out;
- /*
- * is the name a whiteout of the child name ?
- * lookup the whiteout child in the underlying file
- * system
- */
- lower_dentry =
- lookup_one_len(name, lower_parent_dentry,
- strlen(name));
- if (IS_ERR(lower_dentry))
- goto out;
+ /* Replace the current dentry (if any) with the new one */
+ dput(unionfs_lower_dentry_idx(dentry, bindex));
+ unionfs_set_lower_dentry_idx(dentry, bindex,
+ lower_dentry);
- /*
- * Replace the current dentry (if any) with the new
- * one.
- */
- dput(unionfs_lower_dentry_idx(dentry, bindex));
- unionfs_set_lower_dentry_idx(dentry, bindex,
- lower_dentry);
+ __cleanup_dentry(dentry, bindex, old_bstart, old_bend);
+ goto out;
+ }
- __cleanup_dentry(dentry, bindex, old_bstart, old_bend);
- break;
- }
+ if (lower_dentry->d_inode) {
+ /*
+ * since this already exists we dput to avoid
+ * multiple references on the same dentry
+ */
+ dput(lower_dentry);
+ } else {
+ struct sioq_args args;
+
+ /* it's a negative dentry, create a new dir */
+ lower_parent_dentry = lock_parent(lower_dentry);
+
+ args.mkdir.parent = lower_parent_dentry->d_inode;
+ args.mkdir.dentry = lower_dentry;
+ args.mkdir.mode = child_dentry->d_inode->i_mode;
+
+ run_sioq(__unionfs_mkdir, &args);
+ err = args.err;
- if (lower_dentry->d_inode) {
+ if (!err)
+ err = copyup_permissions(dir->i_sb, child_dentry,
+ lower_dentry);
+ unlock_dir(lower_parent_dentry);
+ if (err) {
+ struct inode *inode = lower_dentry->d_inode;
/*
- * since this already exists we dput to avoid
- * multiple references on the same dentry
+ * If we get here, it means that we created a new
+ * dentry+inode, but copying permissions failed.
+ * Therefore, we should delete this inode and dput
+ * the dentry so as not to leave cruft behind.
*/
+ if (lower_dentry->d_op && lower_dentry->d_op->d_iput)
+ lower_dentry->d_op->d_iput(lower_dentry,
+ inode);
+ else
+ iput(inode);
+ lower_dentry->d_inode = NULL;
dput(lower_dentry);
- } else {
- struct sioq_args args;
-
- /* its a negative dentry, create a new dir */
- lower_parent_dentry = lock_parent(lower_dentry);
-
- args.mkdir.parent = lower_parent_dentry->d_inode;
- args.mkdir.dentry = lower_dentry;
- args.mkdir.mode = child_dentry->d_inode->i_mode;
-
- run_sioq(__unionfs_mkdir, &args);
- err = args.err;
-
- if (!err)
- err = copyup_permissions(dir->i_sb,
- child_dentry,
- lower_dentry);
- unlock_dir(lower_parent_dentry);
- if (err) {
- struct inode *inode = lower_dentry->d_inode;
- /*
- * If we get here, it means that we created a new
- * dentry+inode, but copying permissions failed.
- * Therefore, we should delete this inode and dput
- * the dentry so as not to leave cruft behind.
- *
- * XXX: call dentry_iput() instead, but then we have
- * to export that symbol.
- */
- if (lower_dentry->d_op && lower_dentry->d_op->d_iput)
- lower_dentry->d_op->d_iput(lower_dentry,
- inode);
- else
- iput(inode);
- lower_dentry->d_inode = NULL;
-
- dput(lower_dentry);
- lower_dentry = ERR_PTR(err);
- goto out;
- }
-
+ lower_dentry = ERR_PTR(err);
+ goto out;
}
- __set_inode(child_dentry, lower_dentry, bindex);
- __set_dentry(child_dentry, lower_dentry, bindex);
-
- parent_dentry = child_dentry;
- child_dentry = path[--count];
}
+
+ __set_inode(child_dentry, lower_dentry, bindex);
+ __set_dentry(child_dentry, lower_dentry, bindex);
+ /*
+ * update times of this dentry, but also the parent, because if
+ * we changed, the parent may have changed too.
+ */
+ unionfs_copy_attr_times(parent_dentry->d_inode);
+ unionfs_copy_attr_times(child_dentry->d_inode);
+
+ parent_dentry = child_dentry;
+ child_dentry = path[--count];
+ goto begin;
out:
/* cleanup any leftover locks from the do/while loop above */
if (IS_ERR(lower_dentry))
@@ -805,3 +815,53 @@ out:
kfree(path);
return lower_dentry;
}
+
+/*
+ * Post-copyup helper to ensure we have valid mnts: set lower mnt of
+ * dentry+parents to the first parent node that has an mnt.
+ */
+void unionfs_postcopyup_setmnt(struct dentry *dentry)
+{
+ struct dentry *parent, *hasone;
+ int bindex = dbstart(dentry);
+
+ if (unionfs_lower_mnt_idx(dentry, bindex))
+ return;
+ hasone = dentry->d_parent;
+ /* this loop should stop at root dentry */
+ while (!unionfs_lower_mnt_idx(hasone, bindex))
+ hasone = hasone->d_parent;
+ parent = dentry;
+ while (!unionfs_lower_mnt_idx(parent, bindex)) {
+ unionfs_set_lower_mnt_idx(parent, bindex,
+ unionfs_mntget(hasone, bindex));
+ parent = parent->d_parent;
+ }
+}
+
+/*
+ * Post-copyup helper to release all non-directory source objects of a
+ * copied-up file. Regular files should have only one lower object.
+ */
+void unionfs_postcopyup_release(struct dentry *dentry)
+{
+ int bindex;
+
+ BUG_ON(S_ISDIR(dentry->d_inode->i_mode));
+ for (bindex=dbstart(dentry)+1; bindex<=dbend(dentry); bindex++) {
+ if (unionfs_lower_mnt_idx(dentry, bindex)) {
+ unionfs_mntput(dentry, bindex);
+ unionfs_set_lower_mnt_idx(dentry, bindex, NULL);
+ }
+ if (unionfs_lower_dentry_idx(dentry, bindex)) {
+ dput(unionfs_lower_dentry_idx(dentry, bindex));
+ unionfs_set_lower_dentry_idx(dentry, bindex, NULL);
+ iput(unionfs_lower_inode_idx(dentry->d_inode, bindex));
+ unionfs_set_lower_inode_idx(dentry->d_inode, bindex,
+ NULL);
+ }
+ }
+ bindex = dbstart(dentry);
+ set_dbend(dentry, bindex);
+ ibend(dentry->d_inode) = ibstart(dentry->d_inode) = bindex;
+}
diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
index 4789fd5..6cec564 100644
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -167,7 +167,9 @@ static int unionfs_create(struct inode *parent, struct dentry *dentry,
* because lookup passed as a negative unionfs dentry
* pointing to a lone negative underlying dentry.
*/
- lower_dentry = create_parents(parent, dentry, bindex);
+ lower_dentry = create_parents(parent, dentry,
+ dentry->d_name.name,
+ bindex);
if (!lower_dentry || IS_ERR(lower_dentry)) {
if (IS_ERR(lower_dentry))
err = PTR_ERR(lower_dentry);
@@ -321,8 +323,9 @@ static int unionfs_link(struct dentry *old_dentry, struct inode *dir,
}
if (dbstart(old_dentry) != dbstart(new_dentry)) {
- lower_new_dentry =
- create_parents(dir, new_dentry, dbstart(old_dentry));
+ lower_new_dentry = create_parents(dir, new_dentry,
+ new_dentry->d_name.name,
+ dbstart(old_dentry));
err = PTR_ERR(lower_new_dentry);
if (IS_COPYUP_ERR(err))
goto docopyup;
@@ -347,11 +350,13 @@ docopyup:
for (bindex = old_bstart - 1; bindex >= 0; bindex--) {
err = copyup_dentry(old_dentry->d_parent->d_inode,
old_dentry, old_bstart,
- bindex, NULL,
+ bindex, old_dentry->d_name.name,
+ old_dentry->d_name.len, NULL,
old_dentry->d_inode->i_size);
if (!err) {
lower_new_dentry =
create_parents(dir, new_dentry,
+ new_dentry->d_name.name,
bindex);
lower_old_dentry =
unionfs_lower_dentry(old_dentry);
@@ -388,6 +393,8 @@ out:
d_drop(new_dentry);
kfree(name);
+ if (!err)
+ unionfs_postcopyup_setmnt(new_dentry);
unionfs_unlock_dentry(new_dentry);
unionfs_unlock_dentry(old_dentry);
@@ -488,7 +495,9 @@ static int unionfs_symlink(struct inode *dir, struct dentry *dentry,
* unionfs dentry pointing to a lone negative
* underlying dentry
*/
- lower_dentry = create_parents(dir, dentry, bindex);
+ lower_dentry = create_parents(dir, dentry,
+ dentry->d_name.name,
+ bindex);
if (!lower_dentry || IS_ERR(lower_dentry)) {
if (IS_ERR(lower_dentry))
err = PTR_ERR(lower_dentry);
@@ -621,7 +630,9 @@ static int unionfs_mkdir(struct inode *parent, struct dentry *dentry, int mode)
lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
if (!lower_dentry) {
- lower_dentry = create_parents(parent, dentry, bindex);
+ lower_dentry = create_parents(parent, dentry,
+ dentry->d_name.name,
+ bindex);
if (!lower_dentry || IS_ERR(lower_dentry)) {
printk(KERN_DEBUG "unionfs: lower dentry "
" NULL for bindex = %d\n", bindex);
@@ -759,7 +770,9 @@ static int unionfs_mknod(struct inode *dir, struct dentry *dentry, int mode,
lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
if (!lower_dentry) {
- lower_dentry = create_parents(dir, dentry, bindex);
+ lower_dentry = create_parents(dir, dentry,
+ dentry->d_name.name,
+ bindex);
if (IS_ERR(lower_dentry)) {
printk(KERN_DEBUG "unionfs: failed to create "
"parents on %d, err = %ld\n",
@@ -1068,8 +1081,10 @@ static int unionfs_setattr(struct dentry *dentry, struct iattr *ia)
if (ia->ia_valid & ATTR_SIZE)
size = ia->ia_size;
err = copyup_dentry(dentry->d_parent->d_inode,
- dentry, bstart, i, NULL,
- size);
+ dentry, bstart, i,
+ dentry->d_name.name,
+ dentry->d_name.len,
+ NULL, size);
if (!err) {
copyup = 1;
diff --git a/fs/unionfs/rename.c b/fs/unionfs/rename.c
index f8ea37a..acf829a 100644
--- a/fs/unionfs/rename.c
+++ b/fs/unionfs/rename.c
@@ -37,7 +37,8 @@ static int __unionfs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (!lower_new_dentry) {
lower_new_dentry =
create_parents(new_dentry->d_parent->d_inode,
- new_dentry, bindex);
+ new_dentry, new_dentry->d_name.name,
+ bindex);
if (IS_ERR(lower_new_dentry)) {
printk(KERN_DEBUG "unionfs: error creating directory "
"tree for rename, bindex = %d, err = %ld\n",
@@ -226,15 +227,18 @@ static int do_unionfs_rename(struct inode *old_dir,
*/
err = copyup_dentry(old_dentry->d_parent->d_inode,
old_dentry, old_bstart, bindex,
+ old_dentry->d_name.name,
+ old_dentry->d_name.len,
NULL, old_dentry->d_inode->i_size);
- if (!err) {
- dput(wh_old);
- bwh_old = bindex;
- err = __unionfs_rename(old_dir, old_dentry,
- new_dir, new_dentry,
- bindex, &wh_old);
- break;
- }
+ /* if copyup failed, try next branch to the left */
+ if (err)
+ continue;
+ dput(wh_old);
+ bwh_old = bindex;
+ err = __unionfs_rename(old_dir, old_dentry,
+ new_dir, new_dentry,
+ bindex, &wh_old);
+ break;
}
}
@@ -468,8 +472,12 @@ out:
*/
if (S_ISDIR(old_dentry->d_inode->i_mode))
atomic_dec(&UNIONFS_D(old_dentry)->generation);
+ else
+ unionfs_postcopyup_release(old_dentry);
if (new_dentry->d_inode &&
!S_ISDIR(new_dentry->d_inode->i_mode)) {
+ unionfs_postcopyup_release(new_dentry);
+ unionfs_postcopyup_setmnt(new_dentry);
if (!unionfs_lower_inode(new_dentry->d_inode)) {
/*
* If we get here, it means that no copyup
diff --git a/fs/unionfs/subr.c b/fs/unionfs/subr.c
index 3cebc17..5db9e62 100644
--- a/fs/unionfs/subr.c
+++ b/fs/unionfs/subr.c
@@ -56,7 +56,9 @@ int create_whiteout(struct dentry *dentry, int start)
* this dentry.
*/
lower_dentry = create_parents(dentry->d_inode,
- dentry, bindex);
+ dentry,
+ dentry->d_name.name,
+ bindex);
if (!lower_dentry || IS_ERR(lower_dentry)) {
printk(KERN_DEBUG "unionfs: create_parents "
"failed for bindex = %d\n", bindex);
diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h
index a9f9b97..f8a9cd2 100644
--- a/fs/unionfs/union.h
+++ b/fs/unionfs/union.h
@@ -255,7 +255,7 @@ extern void update_bstart(struct dentry *dentry);
/* replicates the directory structure up to given dentry in given branch */
extern struct dentry *create_parents(struct inode *dir, struct dentry *dentry,
- int bindex);
+ const char *name, int bindex);
extern int make_dir_opaque(struct dentry *dir, int bindex);
/* partial lookup */
@@ -275,9 +275,12 @@ extern int copyup_named_file(struct inode *dir, struct file *file,
char *name, int bstart, int new_bindex,
loff_t len);
/* copies a dentry from dbstart to newbindex branch */
-extern int copyup_dentry(struct inode *dir, struct dentry *dentry, int bstart,
- int new_bindex, struct file **copyup_file,
- loff_t len);
+extern int copyup_dentry(struct inode *dir, struct dentry *dentry,
+ int bstart, int new_bindex, const char *name,
+ int namelen, struct file **copyup_file, loff_t len);
+/* helper functions for post-copyup actions */
+extern void unionfs_postcopyup_setmnt(struct dentry *dentry);
+extern void unionfs_postcopyup_release(struct dentry *dentry);
extern int remove_whiteouts(struct dentry *dentry,
struct dentry *lower_dentry, int bindex);
diff --git a/fs/unionfs/unlink.c b/fs/unionfs/unlink.c
index 4e1c8f3..1e5bfce 100644
--- a/fs/unionfs/unlink.c
+++ b/fs/unionfs/unlink.c
@@ -88,6 +88,8 @@ int unionfs_unlink(struct inode *dir, struct dentry *dentry)
err = unionfs_unlink_whiteout(dir, dentry);
/* call d_drop so the system "forgets" about us */
if (!err) {
+ if (!S_ISDIR(dentry->d_inode->i_mode))
+ unionfs_postcopyup_release(dentry);
d_drop(dentry);
/*
* if unlink/whiteout succeeded, parent dir mtime has
--
1.5.2.2.238.g7cbf2f2
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[Index of Archives]
[Kernel Newbies]
[Netfilter]
[Bugtraq]
[Photo]
[Stuff]
[Gimp]
[Yosemite News]
[MIPS Linux]
[ARM Linux]
[Linux Security]
[Linux RAID]
[Video 4 Linux]
[Linux for the blind]
[Linux Resources]