]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - fs/nfs/callback_xdr.c
Merge tag 'driver-core-4.20-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux.git] / fs / nfs / callback_xdr.c
index a813979b5be0c7b6491d3ca0843f6c63945ac959..a87a562734077c221884edaf7962086af6e7a643 100644 (file)
@@ -38,6 +38,9 @@
 #define CB_OP_RECALLSLOT_RES_MAXSZ     (CB_OP_HDR_RES_MAXSZ)
 #define CB_OP_NOTIFY_LOCK_RES_MAXSZ    (CB_OP_HDR_RES_MAXSZ)
 #endif /* CONFIG_NFS_V4_1 */
+#ifdef CONFIG_NFS_V4_2
+#define CB_OP_OFFLOAD_RES_MAXSZ                (CB_OP_HDR_RES_MAXSZ)
+#endif /* CONFIG_NFS_V4_2 */
 
 #define NFSDBG_FACILITY NFSDBG_CALLBACK
 
@@ -527,7 +530,72 @@ static __be32 decode_notify_lock_args(struct svc_rqst *rqstp,
 }
 
 #endif /* CONFIG_NFS_V4_1 */
+#ifdef CONFIG_NFS_V4_2
+static __be32 decode_write_response(struct xdr_stream *xdr,
+                                       struct cb_offloadargs *args)
+{
+       __be32 *p;
+
+       /* skip the always zero field */
+       p = read_buf(xdr, 4);
+       if (unlikely(!p))
+               goto out;
+       p++;
+
+       /* decode count, stable_how, verifier */
+       p = xdr_inline_decode(xdr, 8 + 4);
+       if (unlikely(!p))
+               goto out;
+       p = xdr_decode_hyper(p, &args->wr_count);
+       args->wr_writeverf.committed = be32_to_cpup(p);
+       p = xdr_inline_decode(xdr, NFS4_VERIFIER_SIZE);
+       if (likely(p)) {
+               memcpy(&args->wr_writeverf.verifier.data[0], p,
+                       NFS4_VERIFIER_SIZE);
+               return 0;
+       }
+out:
+       return htonl(NFS4ERR_RESOURCE);
+}
+
+static __be32 decode_offload_args(struct svc_rqst *rqstp,
+                                       struct xdr_stream *xdr,
+                                       void *data)
+{
+       struct cb_offloadargs *args = data;
+       __be32 *p;
+       __be32 status;
+
+       /* decode fh */
+       status = decode_fh(xdr, &args->coa_fh);
+       if (unlikely(status != 0))
+               return status;
 
+       /* decode stateid */
+       status = decode_stateid(xdr, &args->coa_stateid);
+       if (unlikely(status != 0))
+               return status;
+
+       /* decode status */
+       p = read_buf(xdr, 4);
+       if (unlikely(!p))
+               goto out;
+       args->error = ntohl(*p++);
+       if (!args->error) {
+               status = decode_write_response(xdr, args);
+               if (unlikely(status != 0))
+                       return status;
+       } else {
+               p = xdr_inline_decode(xdr, 8);
+               if (unlikely(!p))
+                       goto out;
+               p = xdr_decode_hyper(p, &args->wr_count);
+       }
+       return 0;
+out:
+       return htonl(NFS4ERR_RESOURCE);
+}
+#endif /* CONFIG_NFS_V4_2 */
 static __be32 encode_string(struct xdr_stream *xdr, unsigned int len, const char *str)
 {
        if (unlikely(xdr_stream_encode_opaque(xdr, str, len) < 0))
@@ -773,7 +841,10 @@ preprocess_nfs42_op(int nop, unsigned int op_nr, struct callback_op **op)
        if (status != htonl(NFS4ERR_OP_ILLEGAL))
                return status;
 
-       if (op_nr == OP_CB_OFFLOAD)
+       if (op_nr == OP_CB_OFFLOAD) {
+               *op = &callback_ops[op_nr];
+               return htonl(NFS_OK);
+       } else
                return htonl(NFS4ERR_NOTSUPP);
        return htonl(NFS4ERR_OP_ILLEGAL);
 }
@@ -883,16 +954,21 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp)
 
        if (hdr_arg.minorversion == 0) {
                cps.clp = nfs4_find_client_ident(SVC_NET(rqstp), hdr_arg.cb_ident);
-               if (!cps.clp || !check_gss_callback_principal(cps.clp, rqstp))
+               if (!cps.clp || !check_gss_callback_principal(cps.clp, rqstp)) {
+                       if (cps.clp)
+                               nfs_put_client(cps.clp);
                        goto out_invalidcred;
+               }
        }
 
        cps.minorversion = hdr_arg.minorversion;
        hdr_res.taglen = hdr_arg.taglen;
        hdr_res.tag = hdr_arg.tag;
-       if (encode_compound_hdr_res(&xdr_out, &hdr_res) != 0)
+       if (encode_compound_hdr_res(&xdr_out, &hdr_res) != 0) {
+               if (cps.clp)
+                       nfs_put_client(cps.clp);
                return rpc_system_err;
-
+       }
        while (status == 0 && nops != hdr_arg.nops) {
                status = process_op(nops, rqstp, &xdr_in,
                                    rqstp->rq_argp, &xdr_out, rqstp->rq_resp,
@@ -969,6 +1045,13 @@ static struct callback_op callback_ops[] = {
                .res_maxsize = CB_OP_NOTIFY_LOCK_RES_MAXSZ,
        },
 #endif /* CONFIG_NFS_V4_1 */
+#ifdef CONFIG_NFS_V4_2
+       [OP_CB_OFFLOAD] = {
+               .process_op = nfs4_callback_offload,
+               .decode_args = decode_offload_args,
+               .res_maxsize = CB_OP_OFFLOAD_RES_MAXSZ,
+       },
+#endif /* CONFIG_NFS_V4_2 */
 };
 
 /*