summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/internal.h1
-rw-r--r--fs/libfs.c29
-rw-r--r--fs/nsfs.c16
-rw-r--r--fs/pidfs.c13
4 files changed, 31 insertions, 28 deletions
diff --git a/fs/internal.h b/fs/internal.h
index a34531bcad6e..b0c843c3fa3c 100644
--- a/fs/internal.h
+++ b/fs/internal.h
@@ -314,3 +314,4 @@ int path_from_stashed(struct dentry **stashed, unsigned long ino,
struct vfsmount *mnt, const struct file_operations *fops,
const struct inode_operations *iops, void *data,
struct path *path);
+void stashed_dentry_prune(struct dentry *dentry);
diff --git a/fs/libfs.c b/fs/libfs.c
index 7617e1bc6e5b..472f21bd0325 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -1988,7 +1988,8 @@ static inline struct dentry *get_stashed_dentry(struct dentry *stashed)
return dentry;
}
-static struct dentry *prepare_anon_dentry(unsigned long ino,
+static struct dentry *prepare_anon_dentry(struct dentry **stashed,
+ unsigned long ino,
struct super_block *sb,
const struct file_operations *fops,
const struct inode_operations *iops,
@@ -2019,6 +2020,9 @@ static struct dentry *prepare_anon_dentry(unsigned long ino,
inode->i_private = data;
simple_inode_init_ts(inode);
+ /* Store address of location where dentry's supposed to be stashed. */
+ dentry->d_fsdata = stashed;
+
/* @data is now owned by the fs */
d_instantiate(dentry, inode);
return dentry;
@@ -2081,7 +2085,7 @@ int path_from_stashed(struct dentry **stashed, unsigned long ino,
goto out_path;
/* Allocate a new dentry. */
- dentry = prepare_anon_dentry(ino, mnt->mnt_sb, fops, iops, data);
+ dentry = prepare_anon_dentry(stashed, ino, mnt->mnt_sb, fops, iops, data);
if (IS_ERR(dentry))
return PTR_ERR(dentry);
@@ -2092,6 +2096,27 @@ int path_from_stashed(struct dentry **stashed, unsigned long ino,
ret = 1;
out_path:
+ WARN_ON_ONCE(path->dentry->d_fsdata != stashed);
+ WARN_ON_ONCE(d_inode(path->dentry)->i_private != data);
path->mnt = mntget(mnt);
return ret;
}
+
+void stashed_dentry_prune(struct dentry *dentry)
+{
+ struct dentry **stashed = dentry->d_fsdata;
+ struct inode *inode = d_inode(dentry);
+
+ if (WARN_ON_ONCE(!stashed))
+ return;
+
+ if (!inode)
+ return;
+
+ /*
+ * Only replace our own @dentry as someone else might've
+ * already cleared out @dentry and stashed their own
+ * dentry in there.
+ */
+ cmpxchg(stashed, dentry, NULL);
+}
diff --git a/fs/nsfs.c b/fs/nsfs.c
index 3a36bb62353c..2ce229af34e9 100644
--- a/fs/nsfs.c
+++ b/fs/nsfs.c
@@ -34,22 +34,10 @@ static char *ns_dname(struct dentry *dentry, char *buffer, int buflen)
ns_ops->name, inode->i_ino);
}
-static void ns_prune_dentry(struct dentry *dentry)
-{
- struct inode *inode;
-
- inode = d_inode(dentry);
- if (inode) {
- struct ns_common *ns = inode->i_private;
- cmpxchg(&ns->stashed, dentry, NULL);
- }
-}
-
-const struct dentry_operations ns_dentry_operations =
-{
- .d_prune = ns_prune_dentry,
+const struct dentry_operations ns_dentry_operations = {
.d_delete = always_delete_dentry,
.d_dname = ns_dname,
+ .d_prune = stashed_dentry_prune,
};
static void nsfs_evict(struct inode *inode)
diff --git a/fs/pidfs.c b/fs/pidfs.c
index 5f33c820b7f8..d38b7a184994 100644
--- a/fs/pidfs.c
+++ b/fs/pidfs.c
@@ -187,21 +187,10 @@ static char *pidfs_dname(struct dentry *dentry, char *buffer, int buflen)
d_inode(dentry)->i_ino);
}
-static void pidfs_prune_dentry(struct dentry *dentry)
-{
- struct inode *inode;
-
- inode = d_inode(dentry);
- if (inode) {
- struct pid *pid = inode->i_private;
- cmpxchg(&pid->stashed, dentry, NULL);
- }
-}
-
static const struct dentry_operations pidfs_dentry_operations = {
.d_delete = always_delete_dentry,
.d_dname = pidfs_dname,
- .d_prune = pidfs_prune_dentry,
+ .d_prune = stashed_dentry_prune,
};
static int pidfs_init_fs_context(struct fs_context *fc)