diff options
| author | J. Bruce Fields <bfields@citi.umich.edu> | 2010-04-21 12:27:19 -0400 |
|---|---|---|
| committer | J. Bruce Fields <bfields@citi.umich.edu> | 2010-04-22 11:35:14 -0400 |
| commit | 5771635592267758e7dc5647f2a0088aa6244159 (patch) | |
| tree | 71ffa3a85fe311d5eef591ba69e0bbe7d0b17edc | |
| parent | 4b21d0defcc9680da8a694e92d5fe8eb668c2c0b (diff) | |
nfsd4: complete enforcement of 4.1 op ordering
Enforce the rules about compound op ordering.
Motivated by implementing RECLAIM_COMPLETE, for which the client is
implicit in the current session, so it is important to ensure a
succesful SEQUENCE proceeds the RECLAIM_COMPLETE.
Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
| -rw-r--r-- | fs/nfsd/nfs4proc.c | 44 | ||||
| -rw-r--r-- | fs/nfsd/nfs4state.c | 13 |
2 files changed, 43 insertions, 14 deletions
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 37514c469846..e147dbcb7ef7 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c | |||
| @@ -968,20 +968,36 @@ static struct nfsd4_operation nfsd4_ops[]; | |||
| 968 | static const char *nfsd4_op_name(unsigned opnum); | 968 | static const char *nfsd4_op_name(unsigned opnum); |
| 969 | 969 | ||
| 970 | /* | 970 | /* |
| 971 | * Enforce NFSv4.1 COMPOUND ordering rules. | 971 | * Enforce NFSv4.1 COMPOUND ordering rules: |
| 972 | * | 972 | * |
| 973 | * TODO: | 973 | * Also note, enforced elsewhere: |
| 974 | * - enforce NFS4ERR_NOT_ONLY_OP, | 974 | * - SEQUENCE other than as first op results in |
| 975 | * - DESTROY_SESSION MUST be the final operation in the COMPOUND request. | 975 | * NFS4ERR_SEQUENCE_POS. (Enforced in nfsd4_sequence().) |
| 976 | * - BIND_CONN_TO_SESSION must be the only op in its compound | ||
| 977 | * (Will be enforced in nfsd4_bind_conn_to_session().) | ||
| 978 | * - DESTROY_SESSION must be the final operation in a compound, if | ||
| 979 | * sessionid's in SEQUENCE and DESTROY_SESSION are the same. | ||
| 980 | * (Enforced in nfsd4_destroy_session().) | ||
| 976 | */ | 981 | */ |
| 977 | static bool nfs41_op_ordering_ok(struct nfsd4_compoundargs *args) | 982 | static __be32 nfs41_check_op_ordering(struct nfsd4_compoundargs *args) |
| 978 | { | 983 | { |
| 979 | if (args->minorversion && args->opcnt > 0) { | 984 | struct nfsd4_op *op = &args->ops[0]; |
| 980 | struct nfsd4_op *op = &args->ops[0]; | 985 | |
| 981 | return (op->status == nfserr_op_illegal) || | 986 | /* These ordering requirements don't apply to NFSv4.0: */ |
| 982 | (nfsd4_ops[op->opnum].op_flags & ALLOWED_AS_FIRST_OP); | 987 | if (args->minorversion == 0) |
| 983 | } | 988 | return nfs_ok; |
| 984 | return true; | 989 | /* This is weird, but OK, not our problem: */ |
| 990 | if (args->opcnt == 0) | ||
| 991 | return nfs_ok; | ||
| 992 | if (op->status == nfserr_op_illegal) | ||
| 993 | return nfs_ok; | ||
| 994 | if (!(nfsd4_ops[op->opnum].op_flags & ALLOWED_AS_FIRST_OP)) | ||
| 995 | return nfserr_op_not_in_session; | ||
| 996 | if (op->opnum == OP_SEQUENCE) | ||
| 997 | return nfs_ok; | ||
| 998 | if (args->opcnt != 1) | ||
| 999 | return nfserr_not_only_op; | ||
| 1000 | return nfs_ok; | ||
| 985 | } | 1001 | } |
| 986 | 1002 | ||
| 987 | /* | 1003 | /* |
| @@ -1023,13 +1039,13 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, | |||
| 1023 | if (args->minorversion > nfsd_supported_minorversion) | 1039 | if (args->minorversion > nfsd_supported_minorversion) |
| 1024 | goto out; | 1040 | goto out; |
| 1025 | 1041 | ||
| 1026 | if (!nfs41_op_ordering_ok(args)) { | 1042 | status = nfs41_check_op_ordering(args); |
| 1043 | if (status) { | ||
| 1027 | op = &args->ops[0]; | 1044 | op = &args->ops[0]; |
| 1028 | op->status = nfserr_sequence_pos; | 1045 | op->status = status; |
| 1029 | goto encode_op; | 1046 | goto encode_op; |
| 1030 | } | 1047 | } |
| 1031 | 1048 | ||
| 1032 | status = nfs_ok; | ||
| 1033 | while (!status && resp->opcnt < args->opcnt) { | 1049 | while (!status && resp->opcnt < args->opcnt) { |
| 1034 | op = &args->ops[resp->opcnt++]; | 1050 | op = &args->ops[resp->opcnt++]; |
| 1035 | 1051 | ||
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 4300d9ffe95f..bba9fff49cfe 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
| @@ -1343,6 +1343,14 @@ out: | |||
| 1343 | return status; | 1343 | return status; |
| 1344 | } | 1344 | } |
| 1345 | 1345 | ||
| 1346 | static bool nfsd4_last_compound_op(struct svc_rqst *rqstp) | ||
| 1347 | { | ||
| 1348 | struct nfsd4_compoundres *resp = rqstp->rq_resp; | ||
| 1349 | struct nfsd4_compoundargs *argp = rqstp->rq_argp; | ||
| 1350 | |||
| 1351 | return argp->opcnt == resp->opcnt; | ||
| 1352 | } | ||
| 1353 | |||
| 1346 | __be32 | 1354 | __be32 |
| 1347 | nfsd4_destroy_session(struct svc_rqst *r, | 1355 | nfsd4_destroy_session(struct svc_rqst *r, |
| 1348 | struct nfsd4_compound_state *cstate, | 1356 | struct nfsd4_compound_state *cstate, |
| @@ -1358,6 +1366,11 @@ nfsd4_destroy_session(struct svc_rqst *r, | |||
| 1358 | * - Do we need to clear any callback info from previous session? | 1366 | * - Do we need to clear any callback info from previous session? |
| 1359 | */ | 1367 | */ |
| 1360 | 1368 | ||
| 1369 | if (!memcmp(&sessionid->sessionid, &cstate->session->se_sessionid, | ||
| 1370 | sizeof(struct nfs4_sessionid))) { | ||
| 1371 | if (!nfsd4_last_compound_op(r)) | ||
| 1372 | return nfserr_not_only_op; | ||
| 1373 | } | ||
| 1361 | dump_sessionid(__func__, &sessionid->sessionid); | 1374 | dump_sessionid(__func__, &sessionid->sessionid); |
| 1362 | spin_lock(&sessionid_lock); | 1375 | spin_lock(&sessionid_lock); |
| 1363 | ses = find_in_sessionid_hashtbl(&sessionid->sessionid); | 1376 | ses = find_in_sessionid_hashtbl(&sessionid->sessionid); |
