Skip to content

Commit 7ab96df

Browse files
neilbrownbrauner
authored andcommitted
VFS/nfsd/cachefiles/ovl: add start_creating() and end_creating()
start_creating() is similar to simple_start_creating() but is not so simple. It takes a qstr for the name, includes permission checking, and does NOT report an error if the name already exists, returning a positive dentry instead. This is currently used by nfsd, cachefiles, and overlayfs. end_creating() is called after the dentry has been used. end_creating() drops the reference to the dentry as it is generally no longer needed. This is exactly the first section of end_creating_path() so that function is changed to call the new end_creating() These calls help encapsulate locking rules so that directory locking can be changed. Occasionally this change means that the parent lock is held for a shorter period of time, for example in cachefiles_commit_tmpfile(). As this function now unlocks after an unlink and before the following lookup, it is possible that the lookup could again find a positive dentry, so a while loop is introduced there. In overlayfs the ovl_lookup_temp() function has ovl_tempname() split out to be used in ovl_start_creating_temp(). The other use of ovl_lookup_temp() is preparing for a rename. When rename handling is updated, ovl_lookup_temp() will be removed. Reviewed-by: Jeff Layton <jlayton@kernel.org> Reviewed-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: NeilBrown <neil@brown.name> Link: https://patch.msgid.link/20251113002050.676694-5-neilb@ownmail.net Tested-by: syzbot@syzkaller.appspotmail.com Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent 3661a78 commit 7ab96df

12 files changed

Lines changed: 215 additions & 160 deletions

File tree

fs/cachefiles/namei.c

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,11 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
9393
_enter(",,%s", dirname);
9494

9595
/* search the current directory for the element name */
96-
inode_lock_nested(d_inode(dir), I_MUTEX_PARENT);
9796

9897
retry:
9998
ret = cachefiles_inject_read_error();
10099
if (ret == 0)
101-
subdir = lookup_one(&nop_mnt_idmap, &QSTR(dirname), dir);
100+
subdir = start_creating(&nop_mnt_idmap, dir, &QSTR(dirname));
102101
else
103102
subdir = ERR_PTR(ret);
104103
trace_cachefiles_lookup(NULL, dir, subdir);
@@ -141,7 +140,7 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
141140
trace_cachefiles_mkdir(dir, subdir);
142141

143142
if (unlikely(d_unhashed(subdir) || d_is_negative(subdir))) {
144-
dput(subdir);
143+
end_creating(subdir, dir);
145144
goto retry;
146145
}
147146
ASSERT(d_backing_inode(subdir));
@@ -154,7 +153,8 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
154153

155154
/* Tell rmdir() it's not allowed to delete the subdir */
156155
inode_lock(d_inode(subdir));
157-
inode_unlock(d_inode(dir));
156+
dget(subdir);
157+
end_creating(subdir, dir);
158158

159159
if (!__cachefiles_mark_inode_in_use(NULL, d_inode(subdir))) {
160160
pr_notice("cachefiles: Inode already in use: %pd (B=%lx)\n",
@@ -196,14 +196,11 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
196196
return ERR_PTR(-EBUSY);
197197

198198
mkdir_error:
199-
inode_unlock(d_inode(dir));
200-
if (!IS_ERR(subdir))
201-
dput(subdir);
199+
end_creating(subdir, dir);
202200
pr_err("mkdir %s failed with error %d\n", dirname, ret);
203201
return ERR_PTR(ret);
204202

205203
lookup_error:
206-
inode_unlock(d_inode(dir));
207204
ret = PTR_ERR(subdir);
208205
pr_err("Lookup %s failed with error %d\n", dirname, ret);
209206
return ERR_PTR(ret);
@@ -679,36 +676,41 @@ bool cachefiles_commit_tmpfile(struct cachefiles_cache *cache,
679676

680677
_enter(",%pD", object->file);
681678

682-
inode_lock_nested(d_inode(fan), I_MUTEX_PARENT);
683679
ret = cachefiles_inject_read_error();
684680
if (ret == 0)
685-
dentry = lookup_one(&nop_mnt_idmap, &QSTR(object->d_name), fan);
681+
dentry = start_creating(&nop_mnt_idmap, fan, &QSTR(object->d_name));
686682
else
687683
dentry = ERR_PTR(ret);
688684
if (IS_ERR(dentry)) {
689685
trace_cachefiles_vfs_error(object, d_inode(fan), PTR_ERR(dentry),
690686
cachefiles_trace_lookup_error);
691687
_debug("lookup fail %ld", PTR_ERR(dentry));
692-
goto out_unlock;
688+
goto out;
693689
}
694690

695-
if (!d_is_negative(dentry)) {
691+
/*
692+
* This loop will only execute more than once if some other thread
693+
* races to create the object we are trying to create.
694+
*/
695+
while (!d_is_negative(dentry)) {
696696
ret = cachefiles_unlink(volume->cache, object, fan, dentry,
697697
FSCACHE_OBJECT_IS_STALE);
698698
if (ret < 0)
699-
goto out_dput;
699+
goto out_end;
700+
701+
end_creating(dentry, fan);
700702

701-
dput(dentry);
702703
ret = cachefiles_inject_read_error();
703704
if (ret == 0)
704-
dentry = lookup_one(&nop_mnt_idmap, &QSTR(object->d_name), fan);
705+
dentry = start_creating(&nop_mnt_idmap, fan,
706+
&QSTR(object->d_name));
705707
else
706708
dentry = ERR_PTR(ret);
707709
if (IS_ERR(dentry)) {
708710
trace_cachefiles_vfs_error(object, d_inode(fan), PTR_ERR(dentry),
709711
cachefiles_trace_lookup_error);
710712
_debug("lookup fail %ld", PTR_ERR(dentry));
711-
goto out_unlock;
713+
goto out;
712714
}
713715
}
714716

@@ -729,10 +731,9 @@ bool cachefiles_commit_tmpfile(struct cachefiles_cache *cache,
729731
success = true;
730732
}
731733

732-
out_dput:
733-
dput(dentry);
734-
out_unlock:
735-
inode_unlock(d_inode(fan));
734+
out_end:
735+
end_creating(dentry, fan);
736+
out:
736737
_leave(" = %u", success);
737738
return success;
738739
}

fs/namei.c

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3221,6 +3221,33 @@ struct dentry *lookup_noperm_positive_unlocked(struct qstr *name,
32213221
}
32223222
EXPORT_SYMBOL(lookup_noperm_positive_unlocked);
32233223

3224+
/**
3225+
* start_creating - prepare to create a given name with permission checking
3226+
* @idmap: idmap of the mount
3227+
* @parent: directory in which to prepare to create the name
3228+
* @name: the name to be created
3229+
*
3230+
* Locks are taken and a lookup is performed prior to creating
3231+
* an object in a directory. Permission checking (MAY_EXEC) is performed
3232+
* against @idmap.
3233+
*
3234+
* If the name already exists, a positive dentry is returned, so
3235+
* behaviour is similar to O_CREAT without O_EXCL, which doesn't fail
3236+
* with -EEXIST.
3237+
*
3238+
* Returns: a negative or positive dentry, or an error.
3239+
*/
3240+
struct dentry *start_creating(struct mnt_idmap *idmap, struct dentry *parent,
3241+
struct qstr *name)
3242+
{
3243+
int err = lookup_one_common(idmap, name, parent);
3244+
3245+
if (err)
3246+
return ERR_PTR(err);
3247+
return start_dirop(parent, name, LOOKUP_CREATE);
3248+
}
3249+
EXPORT_SYMBOL(start_creating);
3250+
32243251
#ifdef CONFIG_UNIX98_PTYS
32253252
int path_pts(struct path *path)
32263253
{
@@ -4306,13 +4333,7 @@ EXPORT_SYMBOL(start_creating_path);
43064333
*/
43074334
void end_creating_path(const struct path *path, struct dentry *dentry)
43084335
{
4309-
if (IS_ERR(dentry))
4310-
/* The parent is still locked despite the error from
4311-
* vfs_mkdir() - must unlock it.
4312-
*/
4313-
inode_unlock(path->dentry->d_inode);
4314-
else
4315-
end_dirop(dentry);
4336+
end_creating(dentry, path->dentry);
43164337
mnt_drop_write(path->mnt);
43174338
path_put(path);
43184339
}

fs/nfsd/nfs3proc.c

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -281,14 +281,11 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
281281
if (host_err)
282282
return nfserrno(host_err);
283283

284-
inode_lock_nested(inode, I_MUTEX_PARENT);
285-
286-
child = lookup_one(&nop_mnt_idmap,
287-
&QSTR_LEN(argp->name, argp->len),
288-
parent);
284+
child = start_creating(&nop_mnt_idmap, parent,
285+
&QSTR_LEN(argp->name, argp->len));
289286
if (IS_ERR(child)) {
290287
status = nfserrno(PTR_ERR(child));
291-
goto out;
288+
goto out_write;
292289
}
293290

294291
if (d_really_is_negative(child)) {
@@ -367,9 +364,8 @@ nfsd3_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
367364
status = nfsd_create_setattr(rqstp, fhp, resfhp, &attrs);
368365

369366
out:
370-
inode_unlock(inode);
371-
if (child && !IS_ERR(child))
372-
dput(child);
367+
end_creating(child, parent);
368+
out_write:
373369
fh_drop_write(fhp);
374370
return status;
375371
}

fs/nfsd/nfs4proc.c

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -264,14 +264,11 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
264264
if (is_create_with_attrs(open))
265265
nfsd4_acl_to_attr(NF4REG, open->op_acl, &attrs);
266266

267-
inode_lock_nested(inode, I_MUTEX_PARENT);
268-
269-
child = lookup_one(&nop_mnt_idmap,
270-
&QSTR_LEN(open->op_fname, open->op_fnamelen),
271-
parent);
267+
child = start_creating(&nop_mnt_idmap, parent,
268+
&QSTR_LEN(open->op_fname, open->op_fnamelen));
272269
if (IS_ERR(child)) {
273270
status = nfserrno(PTR_ERR(child));
274-
goto out;
271+
goto out_write;
275272
}
276273

277274
if (d_really_is_negative(child)) {
@@ -379,10 +376,9 @@ nfsd4_create_file(struct svc_rqst *rqstp, struct svc_fh *fhp,
379376
if (attrs.na_aclerr)
380377
open->op_bmval[0] &= ~FATTR4_WORD0_ACL;
381378
out:
382-
inode_unlock(inode);
379+
end_creating(child, parent);
383380
nfsd_attrs_free(&attrs);
384-
if (child && !IS_ERR(child))
385-
dput(child);
381+
out_write:
386382
fh_drop_write(fhp);
387383
return status;
388384
}

fs/nfsd/nfs4recover.c

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -195,13 +195,11 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
195195
goto out_creds;
196196

197197
dir = nn->rec_file->f_path.dentry;
198-
/* lock the parent */
199-
inode_lock(d_inode(dir));
200198

201-
dentry = lookup_one(&nop_mnt_idmap, &QSTR(dname), dir);
199+
dentry = start_creating(&nop_mnt_idmap, dir, &QSTR(dname));
202200
if (IS_ERR(dentry)) {
203201
status = PTR_ERR(dentry);
204-
goto out_unlock;
202+
goto out;
205203
}
206204
if (d_really_is_positive(dentry))
207205
/*
@@ -212,15 +210,13 @@ nfsd4_create_clid_dir(struct nfs4_client *clp)
212210
* In the 4.0 case, we should never get here; but we may
213211
* as well be forgiving and just succeed silently.
214212
*/
215-
goto out_put;
213+
goto out_end;
216214
dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(dir), dentry, S_IRWXU);
217215
if (IS_ERR(dentry))
218216
status = PTR_ERR(dentry);
219-
out_put:
220-
if (!status)
221-
dput(dentry);
222-
out_unlock:
223-
inode_unlock(d_inode(dir));
217+
out_end:
218+
end_creating(dentry, dir);
219+
out:
224220
if (status == 0) {
225221
if (nn->in_grace)
226222
__nfsd4_create_reclaim_record_grace(clp, dname,

fs/nfsd/nfsproc.c

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -306,18 +306,16 @@ nfsd_proc_create(struct svc_rqst *rqstp)
306306
goto done;
307307
}
308308

309-
inode_lock_nested(dirfhp->fh_dentry->d_inode, I_MUTEX_PARENT);
310-
dchild = lookup_one(&nop_mnt_idmap, &QSTR_LEN(argp->name, argp->len),
311-
dirfhp->fh_dentry);
309+
dchild = start_creating(&nop_mnt_idmap, dirfhp->fh_dentry,
310+
&QSTR_LEN(argp->name, argp->len));
312311
if (IS_ERR(dchild)) {
313312
resp->status = nfserrno(PTR_ERR(dchild));
314-
goto out_unlock;
313+
goto out_write;
315314
}
316315
fh_init(newfhp, NFS_FHSIZE);
317316
resp->status = fh_compose(newfhp, dirfhp->fh_export, dchild, dirfhp);
318317
if (!resp->status && d_really_is_negative(dchild))
319318
resp->status = nfserr_noent;
320-
dput(dchild);
321319
if (resp->status) {
322320
if (resp->status != nfserr_noent)
323321
goto out_unlock;
@@ -423,7 +421,8 @@ nfsd_proc_create(struct svc_rqst *rqstp)
423421
}
424422

425423
out_unlock:
426-
inode_unlock(dirfhp->fh_dentry->d_inode);
424+
end_creating(dchild, dirfhp->fh_dentry);
425+
out_write:
427426
fh_drop_write(dirfhp);
428427
done:
429428
fh_put(dirfhp);

0 commit comments

Comments
 (0)