diff options
Diffstat (limited to 'fs/overlayfs/dir.c')
| -rw-r--r-- | fs/overlayfs/dir.c | 37 |
1 files changed, 25 insertions, 12 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); |
