summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/overlayfs/dir.c59
-rw-r--r--fs/overlayfs/inode.c12
-rw-r--r--fs/overlayfs/overlayfs.h1
3 files changed, 60 insertions, 12 deletions
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index e8c7df070fed..f480b1a2cd2e 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -229,24 +229,54 @@ static int ovl_set_opaque(struct dentry *dentry, struct dentry *upperdentry)
return ovl_set_opaque_xerr(dentry, upperdentry, -EIO);
}
-/* Common operations required to be done after creation of file on upper */
-static void ovl_instantiate(struct dentry *dentry, struct inode *inode,
- struct dentry *newdentry, bool hardlink)
+/*
+ * Common operations required to be done after creation of file on upper.
+ * If @hardlink is false, then @inode is a pre-allocated inode, we may or
+ * may not use to instantiate the new dentry.
+ */
+static int ovl_instantiate(struct dentry *dentry, struct inode *inode,
+ struct dentry *newdentry, bool hardlink)
{
+ struct ovl_inode_params oip = {
+ .upperdentry = newdentry,
+ .newinode = inode,
+ };
+
ovl_dentry_version_inc(dentry->d_parent, false);
ovl_dentry_set_upper_alias(dentry);
if (!hardlink) {
- ovl_inode_update(inode, newdentry);
- ovl_copyattr(newdentry->d_inode, inode);
+ /*
+ * ovl_obtain_alias() can be called after ovl_create_real()
+ * and before we get here, so we may get an inode from cache
+ * with the same real upperdentry that is not the inode we
+ * pre-allocated. In this case we will use the cached inode
+ * to instantiate the new dentry.
+ *
+ * XXX: if we ever use ovl_obtain_alias() to decode directory
+ * file handles, need to use ovl_get_inode_locked() and
+ * d_instantiate_new() here to prevent from creating two
+ * hashed directory inode aliases.
+ */
+ inode = ovl_get_inode(dentry->d_sb, &oip);
+ if (WARN_ON(IS_ERR(inode)))
+ return PTR_ERR(inode);
} else {
WARN_ON(ovl_inode_real(inode) != d_inode(newdentry));
dput(newdentry);
inc_nlink(inode);
}
+
d_instantiate(dentry, inode);
+ if (inode != oip.newinode) {
+ pr_warn_ratelimited("overlayfs: newly created inode found in cache (%pd2)\n",
+ dentry);
+ }
+
/* Force lookup of new upper hardlink to find its lower */
if (hardlink)
d_drop(dentry);
+
+ return 0;
}
static bool ovl_type_merge(struct dentry *dentry)
@@ -285,11 +315,17 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
ovl_set_opaque(dentry, newdentry);
}
- ovl_instantiate(dentry, inode, newdentry, !!attr->hardlink);
- err = 0;
+ err = ovl_instantiate(dentry, inode, newdentry, !!attr->hardlink);
+ if (err)
+ goto out_cleanup;
out_unlock:
inode_unlock(udir);
return err;
+
+out_cleanup:
+ ovl_cleanup(udir, newdentry);
+ dput(newdentry);
+ goto out_unlock;
}
static struct dentry *ovl_clear_empty(struct dentry *dentry,
@@ -474,8 +510,9 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
if (err)
goto out_cleanup;
}
- ovl_instantiate(dentry, inode, newdentry, hardlink);
- err = 0;
+ err = ovl_instantiate(dentry, inode, newdentry, hardlink);
+ if (err)
+ goto out_cleanup;
out_dput:
dput(upper);
out_unlock:
@@ -558,6 +595,7 @@ static int ovl_create_object(struct dentry *dentry, int mode, dev_t rdev,
if (err)
goto out;
+ /* Preallocate inode to be used by ovl_get_inode() */
err = -ENOMEM;
inode = ovl_new_inode(dentry->d_sb, mode, rdev);
if (!inode)
@@ -567,7 +605,8 @@ static int ovl_create_object(struct dentry *dentry, int mode, dev_t rdev,
attr.mode = inode->i_mode;
err = ovl_create_or_link(dentry, inode, &attr, false);
- if (err)
+ /* Did we end up using the preallocated inode? */
+ if (inode != d_inode(dentry))
iput(inode);
out_drop_write:
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c
index 2b9e8370500c..1db5b3b458a1 100644
--- a/fs/overlayfs/inode.c
+++ b/fs/overlayfs/inode.c
@@ -749,6 +749,15 @@ static bool ovl_hash_bylower(struct super_block *sb, struct dentry *upper,
return true;
}
+static struct inode *ovl_iget5(struct super_block *sb, struct inode *newinode,
+ struct inode *key)
+{
+ return newinode ? inode_insert5(newinode, (unsigned long) key,
+ ovl_inode_test, ovl_inode_set, key) :
+ iget5_locked(sb, (unsigned long) key,
+ ovl_inode_test, ovl_inode_set, key);
+}
+
struct inode *ovl_get_inode(struct super_block *sb,
struct ovl_inode_params *oip)
{
@@ -776,8 +785,7 @@ struct inode *ovl_get_inode(struct super_block *sb,
upperdentry);
unsigned int nlink = is_dir ? 1 : realinode->i_nlink;
- inode = iget5_locked(sb, (unsigned long) key, ovl_inode_test,
- ovl_inode_set, key);
+ inode = ovl_iget5(sb, oip->newinode, key);
if (!inode)
goto out_nomem;
if (!(inode->i_state & I_NEW)) {
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index b8a0160742b2..3c5e9f18b0d9 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -329,6 +329,7 @@ int ovl_update_time(struct inode *inode, struct timespec *ts, int flags);
bool ovl_is_private_xattr(const char *name);
struct ovl_inode_params {
+ struct inode *newinode;
struct dentry *upperdentry;
struct ovl_path *lowerpath;
struct dentry *index;