summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/overlayfs/dir.c37
-rw-r--r--fs/overlayfs/overlayfs.h8
-rw-r--r--fs/overlayfs/super.c1
3 files changed, 27 insertions, 19 deletions
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c
index c4feb89ad1e3..8c0a3d876fef 100644
--- a/fs/overlayfs/dir.c
+++ b/fs/overlayfs/dir.c
@@ -159,7 +159,8 @@ kill_whiteout:
}
struct dentry *ovl_create_real(struct ovl_fs *ofs, struct dentry *parent,
- struct dentry *newdentry, struct ovl_cattr *attr)
+ struct dentry *newdentry, struct qstr *qname,
+ struct ovl_cattr *attr)
{
struct inode *dir = parent->d_inode;
int err;
@@ -221,19 +222,30 @@ struct dentry *ovl_create_real(struct ovl_fs *ofs, struct dentry *parent,
struct dentry *d;
/*
* Some filesystems (i.e. casefolded) may return an unhashed
- * negative dentry from the ovl_lookup_upper() call before
+ * negative dentry from the ovl_start_creating_upper() call before
* ovl_create_real().
* In that case, lookup again after making the newdentry
* positive, so ovl_create_upper() always returns a hashed
- * positive dentry.
+ * positive dentry. We lookup using qname which should be
+ * the same name as newentry, but is certain not to change.
+ * As we have to drop the lock before the lookup a race
+ * could result in a lookup failure. In that case we return
+ * an error.
*/
- d = ovl_lookup_upper(ofs, newdentry->d_name.name, parent,
- newdentry->d_name.len);
- dput(newdentry);
- if (IS_ERR_OR_NULL(d))
+ end_creating_keep(newdentry);
+ d = ovl_start_creating_upper(ofs, parent, qname);
+
+ if (IS_ERR_OR_NULL(d)) {
err = d ? PTR_ERR(d) : -ENOENT;
- else
+ } else if (d->d_inode != newdentry->d_inode) {
+ err = -EIO;
+ } else {
+ dput(newdentry);
return d;
+ }
+ end_creating(d);
+ dput(newdentry);
+ return ERR_PTR(err);
}
out:
if (err) {
@@ -252,7 +264,7 @@ struct dentry *ovl_create_temp(struct ovl_fs *ofs, struct dentry *workdir,
ret = ovl_start_creating_temp(ofs, workdir, name);
if (IS_ERR(ret))
return ret;
- ret = ovl_create_real(ofs, workdir, ret, attr);
+ ret = ovl_create_real(ofs, workdir, ret, &QSTR(name), attr);
return end_creating_keep(ret);
}
@@ -352,14 +364,15 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
struct dentry *newdentry;
+ struct qstr qname = QSTR_LEN(dentry->d_name.name,
+ dentry->d_name.len);
int err;
newdentry = ovl_start_creating_upper(ofs, upperdir,
- &QSTR_LEN(dentry->d_name.name,
- dentry->d_name.len));
+ &qname);
if (IS_ERR(newdentry))
return PTR_ERR(newdentry);
- newdentry = ovl_create_real(ofs, upperdir, newdentry, attr);
+ newdentry = ovl_create_real(ofs, upperdir, newdentry, &qname, attr);
if (IS_ERR(newdentry))
return PTR_ERR(newdentry);
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index cad2055ebf18..714a1cec3709 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -406,13 +406,6 @@ static inline struct file *ovl_do_tmpfile(struct ovl_fs *ofs,
return file;
}
-static inline struct dentry *ovl_lookup_upper(struct ovl_fs *ofs,
- const char *name,
- struct dentry *base, int len)
-{
- return lookup_one(ovl_upper_mnt_idmap(ofs), &QSTR_LEN(name, len), base);
-}
-
static inline struct dentry *ovl_lookup_upper_unlocked(struct ovl_fs *ofs,
const char *name,
struct dentry *base,
@@ -888,6 +881,7 @@ struct ovl_cattr {
struct dentry *ovl_create_real(struct ovl_fs *ofs,
struct dentry *parent, struct dentry *newdentry,
+ struct qstr *qname,
struct ovl_cattr *attr);
int ovl_cleanup(struct ovl_fs *ofs, struct dentry *workdir, struct dentry *dentry);
#define OVL_TEMPNAME_SIZE 20
diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index d4c12feec039..109643930b9f 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -634,6 +634,7 @@ static struct dentry *ovl_lookup_or_create(struct ovl_fs *ofs,
if (!IS_ERR(child)) {
if (!child->d_inode)
child = ovl_create_real(ofs, parent, child,
+ &QSTR(name),
OVL_CATTR(mode));
end_creating_keep(child);
}