diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nfsd/nfs4proc.c | 40 | ||||
-rw-r--r-- | fs/nfsd/nfs4state.c | 4 |
2 files changed, 36 insertions, 8 deletions
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index f7be7fabe62c..ec51936d2ce2 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c | |||
@@ -811,14 +811,15 @@ static inline void nfsd4_increment_op_stats(u32 opnum) | |||
811 | 811 | ||
812 | typedef __be32(*nfsd4op_func)(struct svc_rqst *, struct nfsd4_compound_state *, | 812 | typedef __be32(*nfsd4op_func)(struct svc_rqst *, struct nfsd4_compound_state *, |
813 | void *); | 813 | void *); |
814 | enum nfsd4_op_flags { | ||
815 | ALLOWED_WITHOUT_FH = 1 << 0, /* No current filehandle required */ | ||
816 | ALLOWED_ON_ABSENT_FS = 2 << 0, /* ops processed on absent fs */ | ||
817 | ALLOWED_AS_FIRST_OP = 3 << 0, /* ops reqired first in compound */ | ||
818 | }; | ||
814 | 819 | ||
815 | struct nfsd4_operation { | 820 | struct nfsd4_operation { |
816 | nfsd4op_func op_func; | 821 | nfsd4op_func op_func; |
817 | u32 op_flags; | 822 | u32 op_flags; |
818 | /* Most ops require a valid current filehandle; a few don't: */ | ||
819 | #define ALLOWED_WITHOUT_FH 1 | ||
820 | /* GETATTR and ops not listed as returning NFS4ERR_MOVED: */ | ||
821 | #define ALLOWED_ON_ABSENT_FS 2 | ||
822 | char *op_name; | 823 | char *op_name; |
823 | }; | 824 | }; |
824 | 825 | ||
@@ -827,6 +828,23 @@ static struct nfsd4_operation nfsd4_ops[]; | |||
827 | static const char *nfsd4_op_name(unsigned opnum); | 828 | static const char *nfsd4_op_name(unsigned opnum); |
828 | 829 | ||
829 | /* | 830 | /* |
831 | * Enforce NFSv4.1 COMPOUND ordering rules. | ||
832 | * | ||
833 | * TODO: | ||
834 | * - enforce NFS4ERR_NOT_ONLY_OP, | ||
835 | * - DESTROY_SESSION MUST be the final operation in the COMPOUND request. | ||
836 | */ | ||
837 | static bool nfs41_op_ordering_ok(struct nfsd4_compoundargs *args) | ||
838 | { | ||
839 | if (args->minorversion && args->opcnt > 0) { | ||
840 | struct nfsd4_op *op = &args->ops[0]; | ||
841 | return (op->status == nfserr_op_illegal) || | ||
842 | (nfsd4_ops[op->opnum].op_flags & ALLOWED_AS_FIRST_OP); | ||
843 | } | ||
844 | return true; | ||
845 | } | ||
846 | |||
847 | /* | ||
830 | * COMPOUND call. | 848 | * COMPOUND call. |
831 | */ | 849 | */ |
832 | static __be32 | 850 | static __be32 |
@@ -864,6 +882,12 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, | |||
864 | if (args->minorversion > NFSD_SUPPORTED_MINOR_VERSION) | 882 | if (args->minorversion > NFSD_SUPPORTED_MINOR_VERSION) |
865 | goto out; | 883 | goto out; |
866 | 884 | ||
885 | if (!nfs41_op_ordering_ok(args)) { | ||
886 | op = &args->ops[0]; | ||
887 | op->status = nfserr_sequence_pos; | ||
888 | goto encode_op; | ||
889 | } | ||
890 | |||
867 | status = nfs_ok; | 891 | status = nfs_ok; |
868 | while (!status && resp->opcnt < args->opcnt) { | 892 | while (!status && resp->opcnt < args->opcnt) { |
869 | op = &args->ops[resp->opcnt++]; | 893 | op = &args->ops[resp->opcnt++]; |
@@ -1105,22 +1129,22 @@ static struct nfsd4_operation nfsd4_ops[] = { | |||
1105 | /* NFSv4.1 operations */ | 1129 | /* NFSv4.1 operations */ |
1106 | [OP_EXCHANGE_ID] = { | 1130 | [OP_EXCHANGE_ID] = { |
1107 | .op_func = (nfsd4op_func)nfsd4_exchange_id, | 1131 | .op_func = (nfsd4op_func)nfsd4_exchange_id, |
1108 | .op_flags = ALLOWED_WITHOUT_FH, | 1132 | .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, |
1109 | .op_name = "OP_EXCHANGE_ID", | 1133 | .op_name = "OP_EXCHANGE_ID", |
1110 | }, | 1134 | }, |
1111 | [OP_CREATE_SESSION] = { | 1135 | [OP_CREATE_SESSION] = { |
1112 | .op_func = (nfsd4op_func)nfsd4_create_session, | 1136 | .op_func = (nfsd4op_func)nfsd4_create_session, |
1113 | .op_flags = ALLOWED_WITHOUT_FH, | 1137 | .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, |
1114 | .op_name = "OP_CREATE_SESSION", | 1138 | .op_name = "OP_CREATE_SESSION", |
1115 | }, | 1139 | }, |
1116 | [OP_DESTROY_SESSION] = { | 1140 | [OP_DESTROY_SESSION] = { |
1117 | .op_func = (nfsd4op_func)nfsd4_destroy_session, | 1141 | .op_func = (nfsd4op_func)nfsd4_destroy_session, |
1118 | .op_flags = ALLOWED_WITHOUT_FH, | 1142 | .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, |
1119 | .op_name = "OP_DESTROY_SESSION", | 1143 | .op_name = "OP_DESTROY_SESSION", |
1120 | }, | 1144 | }, |
1121 | [OP_SEQUENCE] = { | 1145 | [OP_SEQUENCE] = { |
1122 | .op_func = (nfsd4op_func)nfsd4_sequence, | 1146 | .op_func = (nfsd4op_func)nfsd4_sequence, |
1123 | .op_flags = ALLOWED_WITHOUT_FH, | 1147 | .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, |
1124 | .op_name = "OP_SEQUENCE", | 1148 | .op_name = "OP_SEQUENCE", |
1125 | }, | 1149 | }, |
1126 | }; | 1150 | }; |
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 24b6e0593184..9243dca3576c 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -1047,10 +1047,14 @@ nfsd4_sequence(struct svc_rqst *rqstp, | |||
1047 | struct nfsd4_compound_state *cstate, | 1047 | struct nfsd4_compound_state *cstate, |
1048 | struct nfsd4_sequence *seq) | 1048 | struct nfsd4_sequence *seq) |
1049 | { | 1049 | { |
1050 | struct nfsd4_compoundres *resp = rqstp->rq_resp; | ||
1050 | struct nfsd4_session *session; | 1051 | struct nfsd4_session *session; |
1051 | struct nfsd4_slot *slot; | 1052 | struct nfsd4_slot *slot; |
1052 | int status; | 1053 | int status; |
1053 | 1054 | ||
1055 | if (resp->opcnt != 1) | ||
1056 | return nfserr_sequence_pos; | ||
1057 | |||
1054 | spin_lock(&sessionid_lock); | 1058 | spin_lock(&sessionid_lock); |
1055 | status = nfserr_badsession; | 1059 | status = nfserr_badsession; |
1056 | session = find_in_sessionid_hashtbl(&seq->sessionid); | 1060 | session = find_in_sessionid_hashtbl(&seq->sessionid); |