|
@@ -811,14 +811,15 @@ static inline void nfsd4_increment_op_stats(u32 opnum)
|
|
|
|
|
|
typedef __be32(*nfsd4op_func)(struct svc_rqst *, struct nfsd4_compound_state *,
|
|
typedef __be32(*nfsd4op_func)(struct svc_rqst *, struct nfsd4_compound_state *,
|
|
void *);
|
|
void *);
|
|
|
|
+enum nfsd4_op_flags {
|
|
|
|
+ ALLOWED_WITHOUT_FH = 1 << 0, /* No current filehandle required */
|
|
|
|
+ ALLOWED_ON_ABSENT_FS = 2 << 0, /* ops processed on absent fs */
|
|
|
|
+ ALLOWED_AS_FIRST_OP = 3 << 0, /* ops reqired first in compound */
|
|
|
|
+};
|
|
|
|
|
|
struct nfsd4_operation {
|
|
struct nfsd4_operation {
|
|
nfsd4op_func op_func;
|
|
nfsd4op_func op_func;
|
|
u32 op_flags;
|
|
u32 op_flags;
|
|
-/* Most ops require a valid current filehandle; a few don't: */
|
|
|
|
-#define ALLOWED_WITHOUT_FH 1
|
|
|
|
-/* GETATTR and ops not listed as returning NFS4ERR_MOVED: */
|
|
|
|
-#define ALLOWED_ON_ABSENT_FS 2
|
|
|
|
char *op_name;
|
|
char *op_name;
|
|
};
|
|
};
|
|
|
|
|
|
@@ -826,6 +827,23 @@ static struct nfsd4_operation nfsd4_ops[];
|
|
|
|
|
|
static const char *nfsd4_op_name(unsigned opnum);
|
|
static const char *nfsd4_op_name(unsigned opnum);
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Enforce NFSv4.1 COMPOUND ordering rules.
|
|
|
|
+ *
|
|
|
|
+ * TODO:
|
|
|
|
+ * - enforce NFS4ERR_NOT_ONLY_OP,
|
|
|
|
+ * - DESTROY_SESSION MUST be the final operation in the COMPOUND request.
|
|
|
|
+ */
|
|
|
|
+static bool nfs41_op_ordering_ok(struct nfsd4_compoundargs *args)
|
|
|
|
+{
|
|
|
|
+ if (args->minorversion && args->opcnt > 0) {
|
|
|
|
+ struct nfsd4_op *op = &args->ops[0];
|
|
|
|
+ return (op->status == nfserr_op_illegal) ||
|
|
|
|
+ (nfsd4_ops[op->opnum].op_flags & ALLOWED_AS_FIRST_OP);
|
|
|
|
+ }
|
|
|
|
+ return true;
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* COMPOUND call.
|
|
* COMPOUND call.
|
|
*/
|
|
*/
|
|
@@ -864,6 +882,12 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
|
|
if (args->minorversion > NFSD_SUPPORTED_MINOR_VERSION)
|
|
if (args->minorversion > NFSD_SUPPORTED_MINOR_VERSION)
|
|
goto out;
|
|
goto out;
|
|
|
|
|
|
|
|
+ if (!nfs41_op_ordering_ok(args)) {
|
|
|
|
+ op = &args->ops[0];
|
|
|
|
+ op->status = nfserr_sequence_pos;
|
|
|
|
+ goto encode_op;
|
|
|
|
+ }
|
|
|
|
+
|
|
status = nfs_ok;
|
|
status = nfs_ok;
|
|
while (!status && resp->opcnt < args->opcnt) {
|
|
while (!status && resp->opcnt < args->opcnt) {
|
|
op = &args->ops[resp->opcnt++];
|
|
op = &args->ops[resp->opcnt++];
|
|
@@ -1105,22 +1129,22 @@ static struct nfsd4_operation nfsd4_ops[] = {
|
|
/* NFSv4.1 operations */
|
|
/* NFSv4.1 operations */
|
|
[OP_EXCHANGE_ID] = {
|
|
[OP_EXCHANGE_ID] = {
|
|
.op_func = (nfsd4op_func)nfsd4_exchange_id,
|
|
.op_func = (nfsd4op_func)nfsd4_exchange_id,
|
|
- .op_flags = ALLOWED_WITHOUT_FH,
|
|
|
|
|
|
+ .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
|
|
.op_name = "OP_EXCHANGE_ID",
|
|
.op_name = "OP_EXCHANGE_ID",
|
|
},
|
|
},
|
|
[OP_CREATE_SESSION] = {
|
|
[OP_CREATE_SESSION] = {
|
|
.op_func = (nfsd4op_func)nfsd4_create_session,
|
|
.op_func = (nfsd4op_func)nfsd4_create_session,
|
|
- .op_flags = ALLOWED_WITHOUT_FH,
|
|
|
|
|
|
+ .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
|
|
.op_name = "OP_CREATE_SESSION",
|
|
.op_name = "OP_CREATE_SESSION",
|
|
},
|
|
},
|
|
[OP_DESTROY_SESSION] = {
|
|
[OP_DESTROY_SESSION] = {
|
|
.op_func = (nfsd4op_func)nfsd4_destroy_session,
|
|
.op_func = (nfsd4op_func)nfsd4_destroy_session,
|
|
- .op_flags = ALLOWED_WITHOUT_FH,
|
|
|
|
|
|
+ .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
|
|
.op_name = "OP_DESTROY_SESSION",
|
|
.op_name = "OP_DESTROY_SESSION",
|
|
},
|
|
},
|
|
[OP_SEQUENCE] = {
|
|
[OP_SEQUENCE] = {
|
|
.op_func = (nfsd4op_func)nfsd4_sequence,
|
|
.op_func = (nfsd4op_func)nfsd4_sequence,
|
|
- .op_flags = ALLOWED_WITHOUT_FH,
|
|
|
|
|
|
+ .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
|
|
.op_name = "OP_SEQUENCE",
|
|
.op_name = "OP_SEQUENCE",
|
|
},
|
|
},
|
|
};
|
|
};
|