Skip to content

Commit 8348278

Browse files
neilbrownbrauner
authored andcommitted
ovl: change ovl_create_real() to get a new lock when re-opening created file.
When ovl_create_real() is used to create a file on the upper filesystem it needs to return the resulting dentry - positive and hashed. It is usually the case the that dentry passed to the create function (e.g. vfs_create()) will be suitable but this is not guaranteed. The filesystem may unhash that dentry forcing a repeat lookup next time the name is wanted. So ovl_create_real() must be (and is) aware of this and prepared to perform that lookup to get a hash positive dentry. This is currently done under that same directory lock that provided exclusion for the create. Proposed changes to locking will make this not possible - as the name, rather than the directory, will be locked. The new APIs provided for lookup and locking do not and cannot support this pattern. The lock isn't needed. ovl_create_real() can drop the lock and then get a new lock for the lookup - then check that the lookup returned the correct inode. In a well-behaved configuration where the upper filesystem is not being modified by a third party, this will always work reliably, and if there are separate modification it will fail cleanly. So change ovl_create_real() to drop the lock and call ovl_start_creating_upper() to find the correct dentry. Note that start_creating doesn't fail if the name already exists. The lookup previously used the name from newdentry which was guaranteed to be stable because the parent directory was locked. As we now drop the lock we lose that guarantee. As newdentry is unhashed it is unlikely for the name to change, but safest not to depend on that. So the expected name is now passed in to ovl_create_real() and that is used. This removes the only remaining use of ovl_lookup_upper, so it is removed. Reviewed-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: NeilBrown <neil@brown.name> Link: https://patch.msgid.link/20260224222542.3458677-13-neilb@ownmail.net Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent 85bb142 commit 8348278

3 files changed

Lines changed: 27 additions & 19 deletions

File tree

fs/overlayfs/dir.c

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,8 @@ int ovl_cleanup_and_whiteout(struct ovl_fs *ofs, struct dentry *dir,
159159
}
160160

161161
struct dentry *ovl_create_real(struct ovl_fs *ofs, struct dentry *parent,
162-
struct dentry *newdentry, struct ovl_cattr *attr)
162+
struct dentry *newdentry, struct qstr *qname,
163+
struct ovl_cattr *attr)
163164
{
164165
struct inode *dir = parent->d_inode;
165166
int err;
@@ -221,19 +222,30 @@ struct dentry *ovl_create_real(struct ovl_fs *ofs, struct dentry *parent,
221222
struct dentry *d;
222223
/*
223224
* Some filesystems (i.e. casefolded) may return an unhashed
224-
* negative dentry from the ovl_lookup_upper() call before
225+
* negative dentry from the ovl_start_creating_upper() call before
225226
* ovl_create_real().
226227
* In that case, lookup again after making the newdentry
227228
* positive, so ovl_create_upper() always returns a hashed
228-
* positive dentry.
229+
* positive dentry. We lookup using qname which should be
230+
* the same name as newentry, but is certain not to change.
231+
* As we have to drop the lock before the lookup a race
232+
* could result in a lookup failure. In that case we return
233+
* an error.
229234
*/
230-
d = ovl_lookup_upper(ofs, newdentry->d_name.name, parent,
231-
newdentry->d_name.len);
232-
dput(newdentry);
233-
if (IS_ERR_OR_NULL(d))
235+
end_creating_keep(newdentry);
236+
d = ovl_start_creating_upper(ofs, parent, qname);
237+
238+
if (IS_ERR_OR_NULL(d)) {
234239
err = d ? PTR_ERR(d) : -ENOENT;
235-
else
240+
} else if (d->d_inode != newdentry->d_inode) {
241+
err = -EIO;
242+
} else {
243+
dput(newdentry);
236244
return d;
245+
}
246+
end_creating(d);
247+
dput(newdentry);
248+
return ERR_PTR(err);
237249
}
238250
out:
239251
if (err) {
@@ -252,7 +264,7 @@ struct dentry *ovl_create_temp(struct ovl_fs *ofs, struct dentry *workdir,
252264
ret = ovl_start_creating_temp(ofs, workdir, name);
253265
if (IS_ERR(ret))
254266
return ret;
255-
ret = ovl_create_real(ofs, workdir, ret, attr);
267+
ret = ovl_create_real(ofs, workdir, ret, &QSTR(name), attr);
256268
return end_creating_keep(ret);
257269
}
258270

@@ -352,14 +364,15 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
352364
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
353365
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
354366
struct dentry *newdentry;
367+
struct qstr qname = QSTR_LEN(dentry->d_name.name,
368+
dentry->d_name.len);
355369
int err;
356370

357371
newdentry = ovl_start_creating_upper(ofs, upperdir,
358-
&QSTR_LEN(dentry->d_name.name,
359-
dentry->d_name.len));
372+
&qname);
360373
if (IS_ERR(newdentry))
361374
return PTR_ERR(newdentry);
362-
newdentry = ovl_create_real(ofs, upperdir, newdentry, attr);
375+
newdentry = ovl_create_real(ofs, upperdir, newdentry, &qname, attr);
363376
if (IS_ERR(newdentry))
364377
return PTR_ERR(newdentry);
365378

fs/overlayfs/overlayfs.h

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -406,13 +406,6 @@ static inline struct file *ovl_do_tmpfile(struct ovl_fs *ofs,
406406
return file;
407407
}
408408

409-
static inline struct dentry *ovl_lookup_upper(struct ovl_fs *ofs,
410-
const char *name,
411-
struct dentry *base, int len)
412-
{
413-
return lookup_one(ovl_upper_mnt_idmap(ofs), &QSTR_LEN(name, len), base);
414-
}
415-
416409
static inline struct dentry *ovl_lookup_upper_unlocked(struct ovl_fs *ofs,
417410
const char *name,
418411
struct dentry *base,
@@ -888,6 +881,7 @@ struct ovl_cattr {
888881

889882
struct dentry *ovl_create_real(struct ovl_fs *ofs,
890883
struct dentry *parent, struct dentry *newdentry,
884+
struct qstr *qname,
891885
struct ovl_cattr *attr);
892886
int ovl_cleanup(struct ovl_fs *ofs, struct dentry *workdir, struct dentry *dentry);
893887
#define OVL_TEMPNAME_SIZE 20

fs/overlayfs/super.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,7 @@ static struct dentry *ovl_lookup_or_create(struct ovl_fs *ofs,
634634
if (!IS_ERR(child)) {
635635
if (!child->d_inode)
636636
child = ovl_create_real(ofs, parent, child,
637+
&QSTR(name),
637638
OVL_CATTR(mode));
638639
end_creating_keep(child);
639640
}

0 commit comments

Comments
 (0)