Skip to content

Commit 876c553

Browse files
jtlaytonchucklever
authored andcommitted
NFSD: verify the opened dentry after setting a delegation
Between opening a file and setting a delegation on it, someone could rename or unlink the dentry. If this happens, we do not want to grant a delegation on the open. On a CLAIM_NULL open, we're opening by filename, and we may (in the non-create case) or may not (in the create case) be holding i_rwsem when attempting to set a delegation. The latter case allows a race. After getting a lease, redo the lookup of the file being opened and validate that the resulting dentry matches the one in the open file description. To properly redo the lookup we need an rqst pointer to pass to nfsd_lookup_dentry(), so make sure that is available. Signed-off-by: Jeff Layton <[email protected]> Signed-off-by: NeilBrown <[email protected]> Signed-off-by: Chuck Lever <[email protected]>
1 parent bbf936e commit 876c553

File tree

3 files changed

+51
-5
lines changed

3 files changed

+51
-5
lines changed

fs/nfsd/nfs4proc.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,6 +547,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
547547
open->op_openowner);
548548

549549
open->op_filp = NULL;
550+
open->op_rqstp = rqstp;
550551

551552
/* This check required by spec. */
552553
if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL)

fs/nfsd/nfs4state.c

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5288,11 +5288,44 @@ static int nfsd4_check_conflicting_opens(struct nfs4_client *clp,
52885288
return 0;
52895289
}
52905290

5291+
/*
5292+
* It's possible that between opening the dentry and setting the delegation,
5293+
* that it has been renamed or unlinked. Redo the lookup to verify that this
5294+
* hasn't happened.
5295+
*/
5296+
static int
5297+
nfsd4_verify_deleg_dentry(struct nfsd4_open *open, struct nfs4_file *fp,
5298+
struct svc_fh *parent)
5299+
{
5300+
struct svc_export *exp;
5301+
struct dentry *child;
5302+
__be32 err;
5303+
5304+
/* parent may already be locked, and it may get unlocked by
5305+
* this call, but that is safe.
5306+
*/
5307+
err = nfsd_lookup_dentry(open->op_rqstp, parent,
5308+
open->op_fname, open->op_fnamelen,
5309+
&exp, &child);
5310+
5311+
if (err)
5312+
return -EAGAIN;
5313+
5314+
dput(child);
5315+
if (child != file_dentry(fp->fi_deleg_file->nf_file))
5316+
return -EAGAIN;
5317+
5318+
return 0;
5319+
}
5320+
52915321
static struct nfs4_delegation *
5292-
nfs4_set_delegation(struct nfs4_client *clp,
5293-
struct nfs4_file *fp, struct nfs4_clnt_odstate *odstate)
5322+
nfs4_set_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
5323+
struct svc_fh *parent)
52945324
{
52955325
int status = 0;
5326+
struct nfs4_client *clp = stp->st_stid.sc_client;
5327+
struct nfs4_file *fp = stp->st_stid.sc_file;
5328+
struct nfs4_clnt_odstate *odstate = stp->st_clnt_odstate;
52965329
struct nfs4_delegation *dp;
52975330
struct nfsd_file *nf;
52985331
struct file_lock *fl;
@@ -5347,6 +5380,13 @@ nfs4_set_delegation(struct nfs4_client *clp,
53475380
locks_free_lock(fl);
53485381
if (status)
53495382
goto out_clnt_odstate;
5383+
5384+
if (parent) {
5385+
status = nfsd4_verify_deleg_dentry(open, fp, parent);
5386+
if (status)
5387+
goto out_unlock;
5388+
}
5389+
53505390
status = nfsd4_check_conflicting_opens(clp, fp);
53515391
if (status)
53525392
goto out_unlock;
@@ -5402,11 +5442,13 @@ static void nfsd4_open_deleg_none_ext(struct nfsd4_open *open, int status)
54025442
* proper support for them.
54035443
*/
54045444
static void
5405-
nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp)
5445+
nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp,
5446+
struct svc_fh *currentfh)
54065447
{
54075448
struct nfs4_delegation *dp;
54085449
struct nfs4_openowner *oo = openowner(stp->st_stateowner);
54095450
struct nfs4_client *clp = stp->st_stid.sc_client;
5451+
struct svc_fh *parent = NULL;
54105452
int cb_up;
54115453
int status = 0;
54125454

@@ -5420,6 +5462,8 @@ nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp)
54205462
goto out_no_deleg;
54215463
break;
54225464
case NFS4_OPEN_CLAIM_NULL:
5465+
parent = currentfh;
5466+
fallthrough;
54235467
case NFS4_OPEN_CLAIM_FH:
54245468
/*
54255469
* Let's not give out any delegations till everyone's
@@ -5434,7 +5478,7 @@ nfs4_open_delegation(struct nfsd4_open *open, struct nfs4_ol_stateid *stp)
54345478
default:
54355479
goto out_no_deleg;
54365480
}
5437-
dp = nfs4_set_delegation(clp, stp->st_stid.sc_file, stp->st_clnt_odstate);
5481+
dp = nfs4_set_delegation(open, stp, parent);
54385482
if (IS_ERR(dp))
54395483
goto out_no_deleg;
54405484

@@ -5566,7 +5610,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
55665610
* Attempt to hand out a delegation. No error return, because the
55675611
* OPEN succeeds even if we fail.
55685612
*/
5569-
nfs4_open_delegation(open, stp);
5613+
nfs4_open_delegation(open, stp, &resp->cstate.current_fh);
55705614
nodeleg:
55715615
status = nfs_ok;
55725616
trace_nfsd_open(&stp->st_stid.sc_stateid);

fs/nfsd/xdr4.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ struct nfsd4_open {
279279
struct nfs4_clnt_odstate *op_odstate; /* used during processing */
280280
struct nfs4_acl *op_acl;
281281
struct xdr_netobj op_label;
282+
struct svc_rqst *op_rqstp;
282283
};
283284

284285
struct nfsd4_open_confirm {

0 commit comments

Comments
 (0)