From: Erez Zadok <[email protected]>
Update unionfs_interpose to handle spliced dentries, which is important for
NFS exporting.
Signed-off-by: Erez Zadok <[email protected]>
Signed-off-by: Josef 'Jeff' Sipek <[email protected]>
---
fs/unionfs/inode.c | 40 +++++++++++----
fs/unionfs/lookup.c | 22 +++++++-
fs/unionfs/main.c | 138 +++++++++++++++++++++++++++++++++------------------
fs/unionfs/union.h | 4 +-
4 files changed, 141 insertions(+), 63 deletions(-)
diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
index 6cec564..a219a40 100644
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -152,7 +152,12 @@ static int unionfs_create(struct inode *parent, struct dentry *dentry,
wh_dentry);
wh_dentry = NULL;
- err = unionfs_interpose(dentry, parent->i_sb, 0);
+ /*
+ * Only INTERPOSE_LOOKUP can return a value other
+ * than 0 on err.
+ */
+ err = PTR_ERR(unionfs_interpose(dentry,
+ parent->i_sb, 0));
goto out;
}
}
@@ -194,11 +199,14 @@ static int unionfs_create(struct inode *parent, struct dentry *dentry,
if (!IS_COPYUP_ERR(err))
break;
} else {
- err = unionfs_interpose(dentry, parent->i_sb, 0);
+ /*
+ * Only INTERPOSE_LOOKUP can return a value other
+ * than 0 on err.
+ */
+ err = PTR_ERR(unionfs_interpose(dentry,
+ parent->i_sb, 0));
if (!err) {
- fsstack_copy_attr_times(parent,
- lower_parent_dentry->
- d_inode);
+ unionfs_copy_attr_times(parent);
fsstack_copy_inode_size(parent,
lower_parent_dentry->
d_inode);
@@ -527,7 +535,12 @@ static int unionfs_symlink(struct inode *dir, struct dentry *dentry,
if (!IS_COPYUP_ERR(err))
break;
} else {
- err = unionfs_interpose(dentry, dir->i_sb, 0);
+ /*
+ * Only INTERPOSE_LOOKUP can return a value other
+ * than 0 on err.
+ */
+ err = PTR_ERR(unionfs_interpose(dentry,
+ dir->i_sb, 0));
if (!err) {
fsstack_copy_attr_times(dir,
lower_dir_dentry->
@@ -664,10 +677,13 @@ static int unionfs_mkdir(struct inode *parent, struct dentry *dentry, int mode)
}
set_dbend(dentry, bindex);
- err = unionfs_interpose(dentry, parent->i_sb, 0);
+ /*
+ * Only INTERPOSE_LOOKUP can return a value other than 0 on
+ * err.
+ */
+ err = PTR_ERR(unionfs_interpose(dentry, parent->i_sb, 0));
if (!err) {
- fsstack_copy_attr_times(parent,
- lower_parent_dentry->d_inode);
+ unionfs_copy_attr_times(parent);
fsstack_copy_inode_size(parent,
lower_parent_dentry->d_inode);
@@ -795,7 +811,11 @@ static int unionfs_mknod(struct inode *dir, struct dentry *dentry, int mode,
break;
}
- err = unionfs_interpose(dentry, dir->i_sb, 0);
+ /*
+ * Only INTERPOSE_LOOKUP can return a value other than 0 on
+ * err.
+ */
+ err = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0));
if (!err) {
fsstack_copy_attr_times(dir,
lower_parent_dentry->d_inode);
diff --git a/fs/unionfs/lookup.c b/fs/unionfs/lookup.c
index 61ee50d..e4e8470 100644
--- a/fs/unionfs/lookup.c
+++ b/fs/unionfs/lookup.c
@@ -72,7 +72,12 @@ out:
return err;
}
-/* main (and complex) driver function for Unionfs's lookup */
+/*
+ * Main (and complex) driver function for Unionfs's lookup
+ *
+ * Returns: NULL (ok), ERR_PTR if an error occurred, or a non-null non-error
+ * PTR if d_splice returned a different dentry.
+ */
struct dentry *unionfs_lookup_backend(struct dentry *dentry,
struct nameidata *nd, int lookupmode)
{
@@ -81,6 +86,7 @@ struct dentry *unionfs_lookup_backend(struct dentry *dentry,
struct dentry *wh_lower_dentry = NULL;
struct dentry *lower_dir_dentry = NULL;
struct dentry *parent_dentry = NULL;
+ struct dentry *d_interposed = NULL;
int bindex, bstart, bend, bopaque;
int dentry_count = 0; /* Number of positive dentries. */
int first_dentry_offset = -1; /* -1 is uninitialized */
@@ -90,7 +96,6 @@ struct dentry *unionfs_lookup_backend(struct dentry *dentry,
int locked_parent = 0;
int locked_child = 0;
int allocated_new_info = 0;
-
int opaque;
char *whname = NULL;
const char *name;
@@ -370,7 +375,16 @@ out_positive:
bend = dbend(dentry);
}
- err = unionfs_interpose(dentry, dentry->d_sb, lookupmode);
+ /*
+ * Interpose can return a dentry if d_splice returned a different
+ * dentry.
+ */
+ d_interposed = unionfs_interpose(dentry, dentry->d_sb, lookupmode);
+ if (IS_ERR(d_interposed))
+ err = PTR_ERR(d_interposed);
+ else if (d_interposed)
+ dentry = d_interposed;
+
if (err)
goto out_drop;
@@ -406,6 +420,8 @@ out:
dput(parent_dentry);
if (locked_child || (err && allocated_new_info))
unionfs_unlock_dentry(dentry);
+ if (!err && d_interposed)
+ return d_interposed;
return ERR_PTR(err);
}
diff --git a/fs/unionfs/main.c b/fs/unionfs/main.c
index 689a8fa..bc5c105 100644
--- a/fs/unionfs/main.c
+++ b/fs/unionfs/main.c
@@ -20,20 +20,73 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
+static void unionfs_fill_inode(struct dentry *dentry,
+ struct inode *inode)
+{
+ struct inode *lower_inode;
+ struct dentry *lower_dentry;
+ int bindex, bstart, bend;
+
+ bstart = dbstart(dentry);
+ bend = dbend(dentry);
+
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
+ if (!lower_dentry) {
+ unionfs_set_lower_inode_idx(inode, bindex, NULL);
+ continue;
+ }
+
+ /* Initialize the lower inode to the new lower inode. */
+ if (!lower_dentry->d_inode)
+ continue;
+
+ unionfs_set_lower_inode_idx(inode, bindex,
+ igrab(lower_dentry->d_inode));
+ }
+
+ ibstart(inode) = dbstart(dentry);
+ ibend(inode) = dbend(dentry);
+
+ /* Use attributes from the first branch. */
+ lower_inode = unionfs_lower_inode(inode);
+
+ /* Use different set of inode ops for symlinks & directories */
+ if (S_ISLNK(lower_inode->i_mode))
+ inode->i_op = &unionfs_symlink_iops;
+ else if (S_ISDIR(lower_inode->i_mode))
+ inode->i_op = &unionfs_dir_iops;
+
+ /* Use different set of file ops for directories */
+ if (S_ISDIR(lower_inode->i_mode))
+ inode->i_fop = &unionfs_dir_fops;
+
+ /* properly initialize special inodes */
+ if (S_ISBLK(lower_inode->i_mode) || S_ISCHR(lower_inode->i_mode) ||
+ S_ISFIFO(lower_inode->i_mode) || S_ISSOCK(lower_inode->i_mode))
+ init_special_inode(inode, lower_inode->i_mode,
+ lower_inode->i_rdev);
+
+ /* all well, copy inode attributes */
+ unionfs_copy_attr_all(inode, lower_inode);
+ fsstack_copy_inode_size(inode, lower_inode);
+}
+
/*
* Connect a unionfs inode dentry/inode with several lower ones. This is
* the classic stackable file system "vnode interposition" action.
*
* @sb: unionfs's super_block
*/
-int unionfs_interpose(struct dentry *dentry, struct super_block *sb, int flag)
+struct dentry *unionfs_interpose(struct dentry *dentry, struct super_block *sb,
+ int flag)
{
- struct inode *lower_inode;
- struct dentry *lower_dentry;
int err = 0;
struct inode *inode;
int is_negative_dentry = 1;
int bindex, bstart, bend;
+ int need_fill_inode = 1;
+ struct dentry *spliced = NULL;
verify_locked(dentry);
@@ -80,51 +133,12 @@ int unionfs_interpose(struct dentry *dentry, struct super_block *sb, int flag)
err = -EACCES;
goto out;
}
-
if (atomic_read(&inode->i_count) > 1)
goto skip;
}
- for (bindex = bstart; bindex <= bend; bindex++) {
- lower_dentry = unionfs_lower_dentry_idx(dentry, bindex);
- if (!lower_dentry) {
- unionfs_set_lower_inode_idx(inode, bindex, NULL);
- continue;
- }
-
- /* Initialize the lower inode to the new lower inode. */
- if (!lower_dentry->d_inode)
- continue;
-
- unionfs_set_lower_inode_idx(inode, bindex,
- igrab(lower_dentry->d_inode));
- }
-
- ibstart(inode) = dbstart(dentry);
- ibend(inode) = dbend(dentry);
-
- /* Use attributes from the first branch. */
- lower_inode = unionfs_lower_inode(inode);
-
- /* Use different set of inode ops for symlinks & directories */
- if (S_ISLNK(lower_inode->i_mode))
- inode->i_op = &unionfs_symlink_iops;
- else if (S_ISDIR(lower_inode->i_mode))
- inode->i_op = &unionfs_dir_iops;
-
- /* Use different set of file ops for directories */
- if (S_ISDIR(lower_inode->i_mode))
- inode->i_fop = &unionfs_dir_fops;
-
- /* properly initialize special inodes */
- if (S_ISBLK(lower_inode->i_mode) || S_ISCHR(lower_inode->i_mode) ||
- S_ISFIFO(lower_inode->i_mode) || S_ISSOCK(lower_inode->i_mode))
- init_special_inode(inode, lower_inode->i_mode,
- lower_inode->i_rdev);
-
- /* all well, copy inode attributes */
- unionfs_copy_attr_all(inode, lower_inode);
- fsstack_copy_inode_size(inode, lower_inode);
+ need_fill_inode = 0;
+ unionfs_fill_inode(dentry, inode);
skip:
/* only (our) lookup wants to do a d_add */
@@ -134,7 +148,28 @@ skip:
d_instantiate(dentry, inode);
break;
case INTERPOSE_LOOKUP:
- err = PTR_ERR(d_splice_alias(inode, dentry));
+ spliced = d_splice_alias(inode, dentry);
+ if (IS_ERR(spliced))
+ err = PTR_ERR(spliced);
+ else if (spliced && spliced != dentry) {
+ /*
+ * d_splice can return a dentry if it was
+ * disconnected and had to be moved. We must ensure
+ * that the private data of the new dentry is
+ * correct and that the inode info was filled
+ * properly. Finally we must return this new
+ * dentry.
+ */
+ spliced->d_op = &unionfs_dops;
+ spliced->d_fsdata = dentry->d_fsdata;
+ dentry->d_fsdata = NULL;
+ dentry = spliced;
+ if (need_fill_inode) {
+ need_fill_inode = 0;
+ unionfs_fill_inode(dentry, inode);
+ }
+ goto out_spliced;
+ }
break;
case INTERPOSE_REVAL:
/* Do nothing. */
@@ -143,9 +178,13 @@ skip:
printk(KERN_ERR "unionfs: invalid interpose flag passed!");
BUG();
}
+ goto out;
+out_spliced:
+ if (!err)
+ return spliced;
out:
- return err;
+ return ERR_PTR(err);
}
/* like interpose above, but for an already existing dentry */
@@ -623,8 +662,11 @@ static int unionfs_read_super(struct super_block *sb, void *raw_data,
/* Set the generation number to one, since this is for the mount. */
atomic_set(&UNIONFS_D(sb->s_root)->generation, 1);
- /* call interpose to create the upper level inode */
- err = unionfs_interpose(sb->s_root, sb, 0);
+ /*
+ * Call interpose to create the upper level inode. Only
+ * INTERPOSE_LOOKUP can return a value other than 0 on err.
+ */
+ err = PTR_ERR(unionfs_interpose(sb->s_root, sb, 0));
unionfs_unlock_dentry(sb->s_root);
if (!err)
goto out;
diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h
index ec33155..0fa8ae0 100644
--- a/fs/unionfs/union.h
+++ b/fs/unionfs/union.h
@@ -333,8 +333,8 @@ extern int is_newer_lower(const struct dentry *dentry);
#define INTERPOSE_REVAL_NEG 3
#define INTERPOSE_PARTIAL 4
-extern int unionfs_interpose(struct dentry *this_dentry,
- struct super_block *sb, int flag);
+extern struct dentry *unionfs_interpose(struct dentry *this_dentry,
+ struct super_block *sb, int flag);
#ifdef CONFIG_UNION_FS_XATTR
/* Extended attribute functions. */
--
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]