switch (open->op_claim_type) {
case NFS4_OPEN_CLAIM_DELEGATE_CUR:
- status = nfserr_inval;
- if (open->op_create)
- goto out;
- /* fall through */
case NFS4_OPEN_CLAIM_NULL:
/*
* (1) set CURRENT_FH to the file being opened,
struct file *filp = NULL;
u32 *p;
__be32 status = nfs_ok;
+ unsigned long cnt;
/* no need to check permission - this will be done in nfsd_write() */
return status;
}
- write->wr_bytes_written = write->wr_buflen;
+ cnt = write->wr_buflen;
write->wr_how_written = write->wr_stable_how;
p = (u32 *)write->wr_verifier.data;
*p++ = nfssvc_boot.tv_sec;
status = nfsd_write(rqstp, &cstate->current_fh, filp,
write->wr_offset, rqstp->rq_vec, write->wr_vlen,
- write->wr_buflen, &write->wr_how_written);
+ &cnt, &write->wr_how_written);
if (filp)
fput(filp);
+ write->wr_bytes_written = cnt;
+
if (status == nfserr_symlink)
status = nfserr_inval;
return status;
nfsdstats.nfs4_opcount[opnum]++;
}
-static void cstate_free(struct nfsd4_compound_state *cstate)
-{
- if (cstate == NULL)
- return;
- fh_put(&cstate->current_fh);
- fh_put(&cstate->save_fh);
- BUG_ON(cstate->replay_owner);
- kfree(cstate);
-}
-
-static struct nfsd4_compound_state *cstate_alloc(void)
-{
- struct nfsd4_compound_state *cstate;
-
- cstate = kmalloc(sizeof(struct nfsd4_compound_state), GFP_KERNEL);
- if (cstate == NULL)
- return NULL;
- fh_init(&cstate->current_fh, NFS4_FHSIZE);
- fh_init(&cstate->save_fh, NFS4_FHSIZE);
- cstate->replay_owner = NULL;
- return cstate;
-}
-
typedef __be32(*nfsd4op_func)(struct svc_rqst *, struct nfsd4_compound_state *,
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 {
nfsd4op_func op_func;
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;
};
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.
*/
static __be32
{
struct nfsd4_op *op;
struct nfsd4_operation *opdesc;
- struct nfsd4_compound_state *cstate = NULL;
+ struct nfsd4_compound_state *cstate = &resp->cstate;
int slack_bytes;
__be32 status;
resp->xbuf = &rqstp->rq_res;
- resp->p = rqstp->rq_res.head[0].iov_base + rqstp->rq_res.head[0].iov_len;
+ resp->p = rqstp->rq_res.head[0].iov_base +
+ rqstp->rq_res.head[0].iov_len;
resp->tagp = resp->p;
/* reserve space for: taglen, tag, and opcnt */
resp->p += 2 + XDR_QUADLEN(args->taglen);
resp->tag = args->tag;
resp->opcnt = 0;
resp->rqstp = rqstp;
+ resp->cstate.replay_owner = NULL;
+ fh_init(&resp->cstate.current_fh, NFS4_FHSIZE);
+ fh_init(&resp->cstate.save_fh, NFS4_FHSIZE);
+ /* Use the deferral mechanism only for NFSv4.0 compounds */
+ rqstp->rq_usedeferral = (args->minorversion == 0);
/*
* According to RFC3010, this takes precedence over all other errors.
if (args->minorversion > NFSD_SUPPORTED_MINOR_VERSION)
goto out;
- status = nfserr_resource;
- cstate = cstate_alloc();
- if (cstate == NULL)
- goto out;
+ if (!nfs41_op_ordering_ok(args)) {
+ op = &args->ops[0];
+ op->status = nfserr_sequence_pos;
+ goto encode_op;
+ }
status = nfs_ok;
while (!status && resp->opcnt < args->opcnt) {
BUG_ON(op->status == nfs_ok);
encode_op:
+ /* Only from SEQUENCE or CREATE_SESSION */
+ if (resp->cstate.status == nfserr_replay_cache) {
+ dprintk("%s NFS4.1 replay from cache\n", __func__);
+ status = op->status;
+ goto out;
+ }
if (op->status == nfserr_replay_me) {
op->replay = &cstate->replay_owner->so_replay;
nfsd4_encode_replay(resp, op);
nfsd4_increment_op_stats(op->opnum);
}
+ if (!rqstp->rq_usedeferral && status == nfserr_dropit) {
+ dprintk("%s Dropit - send NFS4ERR_DELAY\n", __func__);
+ status = nfserr_jukebox;
+ }
- cstate_free(cstate);
+ resp->cstate.status = status;
+ fh_put(&resp->cstate.current_fh);
+ fh_put(&resp->cstate.save_fh);
+ BUG_ON(resp->cstate.replay_owner);
out:
nfsd4_release_compoundargs(args);
+ /* Reset deferral mechanism for RPC deferrals */
+ rqstp->rq_usedeferral = 1;
dprintk("nfsv4 compound returned %d\n", ntohl(status));
return status;
}
-static struct nfsd4_operation nfsd4_ops[OP_RELEASE_LOCKOWNER+1] = {
+static struct nfsd4_operation nfsd4_ops[] = {
[OP_ACCESS] = {
.op_func = (nfsd4op_func)nfsd4_access,
.op_name = "OP_ACCESS",
.op_name = "OP_PUTFH",
},
[OP_PUTPUBFH] = {
- /* unsupported, just for future reference: */
+ .op_func = (nfsd4op_func)nfsd4_putrootfh,
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS,
.op_name = "OP_PUTPUBFH",
},
.op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS,
.op_name = "OP_RELEASE_LOCKOWNER",
},
+
+ /* NFSv4.1 operations */
+ [OP_EXCHANGE_ID] = {
+ .op_func = (nfsd4op_func)nfsd4_exchange_id,
+ .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
+ .op_name = "OP_EXCHANGE_ID",
+ },
+ [OP_CREATE_SESSION] = {
+ .op_func = (nfsd4op_func)nfsd4_create_session,
+ .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
+ .op_name = "OP_CREATE_SESSION",
+ },
+ [OP_DESTROY_SESSION] = {
+ .op_func = (nfsd4op_func)nfsd4_destroy_session,
+ .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
+ .op_name = "OP_DESTROY_SESSION",
+ },
+ [OP_SEQUENCE] = {
+ .op_func = (nfsd4op_func)nfsd4_sequence,
+ .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
+ .op_name = "OP_SEQUENCE",
+ },
};
static const char *nfsd4_op_name(unsigned opnum)