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 /fs/nfsd | |
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>
Diffstat (limited to 'fs/nfsd')
-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); |