NFSD: Implement the COPY call
authorAnna Schumaker <Anna.Schumaker@netapp.com>
Wed, 7 Sep 2016 19:57:30 +0000 (15:57 -0400)
committerJ. Bruce Fields <bfields@redhat.com>
Fri, 7 Oct 2016 18:54:25 +0000 (14:54 -0400)
I only implemented the sync version of this call, since it's the
easiest.  I can simply call vfs_copy_range() and have the vfs do the
right thing for the filesystem being exported.

Signed-off-by: Anna Schumaker <bjschuma@netapp.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
fs/nfsd/nfs4proc.c
fs/nfsd/nfs4xdr.c
fs/nfsd/vfs.c
fs/nfsd/vfs.h
fs/nfsd/xdr4.h

index 1fb222752b2b154d1c7171b6c1ea766a8472d09e..abb09b580389a5f81c3c0b04425582c21e6364ec 100644 (file)
@@ -1010,46 +1010,96 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 }
 
 static __be32
-nfsd4_clone(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
-               struct nfsd4_clone *clone)
+nfsd4_verify_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+                 stateid_t *src_stateid, struct file **src,
+                 stateid_t *dst_stateid, struct file **dst)
 {
-       struct file *src, *dst;
        __be32 status;
 
        status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->save_fh,
-                                           &clone->cl_src_stateid, RD_STATE,
-                                           &src, NULL);
+                                           src_stateid, RD_STATE, src, NULL);
        if (status) {
                dprintk("NFSD: %s: couldn't process src stateid!\n", __func__);
                goto out;
        }
 
        status = nfs4_preprocess_stateid_op(rqstp, cstate, &cstate->current_fh,
-                                           &clone->cl_dst_stateid, WR_STATE,
-                                           &dst, NULL);
+                                           dst_stateid, WR_STATE, dst, NULL);
        if (status) {
                dprintk("NFSD: %s: couldn't process dst stateid!\n", __func__);
                goto out_put_src;
        }
 
        /* fix up for NFS-specific error code */
-       if (!S_ISREG(file_inode(src)->i_mode) ||
-           !S_ISREG(file_inode(dst)->i_mode)) {
+       if (!S_ISREG(file_inode(*src)->i_mode) ||
+           !S_ISREG(file_inode(*dst)->i_mode)) {
                status = nfserr_wrong_type;
                goto out_put_dst;
        }
 
+out:
+       return status;
+out_put_dst:
+       fput(*dst);
+out_put_src:
+       fput(*src);
+       goto out;
+}
+
+static __be32
+nfsd4_clone(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+               struct nfsd4_clone *clone)
+{
+       struct file *src, *dst;
+       __be32 status;
+
+       status = nfsd4_verify_copy(rqstp, cstate, &clone->cl_src_stateid, &src,
+                                  &clone->cl_dst_stateid, &dst);
+       if (status)
+               goto out;
+
        status = nfsd4_clone_file_range(src, clone->cl_src_pos,
                        dst, clone->cl_dst_pos, clone->cl_count);
 
-out_put_dst:
        fput(dst);
-out_put_src:
        fput(src);
 out:
        return status;
 }
 
+static __be32
+nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+               struct nfsd4_copy *copy)
+{
+       struct file *src, *dst;
+       __be32 status;
+       ssize_t bytes;
+
+       status = nfsd4_verify_copy(rqstp, cstate, &copy->cp_src_stateid, &src,
+                                  &copy->cp_dst_stateid, &dst);
+       if (status)
+               goto out;
+
+       bytes = nfsd_copy_file_range(src, copy->cp_src_pos,
+                       dst, copy->cp_dst_pos, copy->cp_count);
+
+       if (bytes < 0)
+               status = nfserrno(bytes);
+       else {
+               copy->cp_res.wr_bytes_written = bytes;
+               copy->cp_res.wr_stable_how = NFS_UNSTABLE;
+               copy->cp_consecutive = 1;
+               copy->cp_synchronous = 1;
+               gen_boot_verifier(&copy->cp_res.wr_verifier, SVC_NET(rqstp));
+               status = nfs_ok;
+       }
+
+       fput(src);
+       fput(dst);
+out:
+       return status;
+}
+
 static __be32
 nfsd4_fallocate(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                struct nfsd4_fallocate *fallocate, int flags)
@@ -1966,6 +2016,18 @@ static inline u32 nfsd4_create_session_rsize(struct svc_rqst *rqstp, struct nfsd
                op_encode_channel_attrs_maxsz) * sizeof(__be32);
 }
 
+static inline u32 nfsd4_copy_rsize(struct svc_rqst *rqstp, struct nfsd4_op *op)
+{
+       return (op_encode_hdr_size +
+               1 /* wr_callback */ +
+               op_encode_stateid_maxsz /* wr_callback */ +
+               2 /* wr_count */ +
+               1 /* wr_committed */ +
+               op_encode_verifier_maxsz +
+               1 /* cr_consecutive */ +
+               1 /* cr_synchronous */) * sizeof(__be32);
+}
+
 #ifdef CONFIG_NFSD_PNFS
 /*
  * At this stage we don't really know what layout driver will handle the request,
@@ -2328,6 +2390,12 @@ static struct nfsd4_operation nfsd4_ops[] = {
                .op_name = "OP_CLONE",
                .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
        },
+       [OP_COPY] = {
+               .op_func = (nfsd4op_func)nfsd4_copy,
+               .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME,
+               .op_name = "OP_COPY",
+               .op_rsize_bop = (nfsd4op_rsize)nfsd4_copy_rsize,
+       },
        [OP_SEEK] = {
                .op_func = (nfsd4op_func)nfsd4_seek,
                .op_name = "OP_SEEK",
index 7f8232e32dc73ab5361b7bc3f6946e67c30dc77e..c2d2895a1ec1d222f8e3b9913f65f603164707cb 100644 (file)
@@ -1693,6 +1693,30 @@ nfsd4_decode_clone(struct nfsd4_compoundargs *argp, struct nfsd4_clone *clone)
        DECODE_TAIL;
 }
 
+static __be32
+nfsd4_decode_copy(struct nfsd4_compoundargs *argp, struct nfsd4_copy *copy)
+{
+       DECODE_HEAD;
+       unsigned int tmp;
+
+       status = nfsd4_decode_stateid(argp, &copy->cp_src_stateid);
+       if (status)
+               return status;
+       status = nfsd4_decode_stateid(argp, &copy->cp_dst_stateid);
+       if (status)
+               return status;
+
+       READ_BUF(8 + 8 + 8 + 4 + 4 + 4);
+       p = xdr_decode_hyper(p, &copy->cp_src_pos);
+       p = xdr_decode_hyper(p, &copy->cp_dst_pos);
+       p = xdr_decode_hyper(p, &copy->cp_count);
+       copy->cp_consecutive = be32_to_cpup(p++);
+       copy->cp_synchronous = be32_to_cpup(p++);
+       tmp = be32_to_cpup(p); /* Source server list not supported */
+
+       DECODE_TAIL;
+}
+
 static __be32
 nfsd4_decode_seek(struct nfsd4_compoundargs *argp, struct nfsd4_seek *seek)
 {
@@ -1793,7 +1817,7 @@ static nfsd4_dec nfsd4_dec_ops[] = {
 
        /* new operations for NFSv4.2 */
        [OP_ALLOCATE]           = (nfsd4_dec)nfsd4_decode_fallocate,
-       [OP_COPY]               = (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_COPY]               = (nfsd4_dec)nfsd4_decode_copy,
        [OP_COPY_NOTIFY]        = (nfsd4_dec)nfsd4_decode_notsupp,
        [OP_DEALLOCATE]         = (nfsd4_dec)nfsd4_decode_fallocate,
        [OP_IO_ADVISE]          = (nfsd4_dec)nfsd4_decode_notsupp,
@@ -4201,6 +4225,41 @@ nfsd4_encode_layoutreturn(struct nfsd4_compoundres *resp, __be32 nfserr,
 }
 #endif /* CONFIG_NFSD_PNFS */
 
+static __be32
+nfsd42_encode_write_res(struct nfsd4_compoundres *resp, struct nfsd42_write_res *write)
+{
+       __be32 *p;
+
+       p = xdr_reserve_space(&resp->xdr, 4 + 8 + 4 + NFS4_VERIFIER_SIZE);
+       if (!p)
+               return nfserr_resource;
+
+       *p++ = cpu_to_be32(0);
+       p = xdr_encode_hyper(p, write->wr_bytes_written);
+       *p++ = cpu_to_be32(write->wr_stable_how);
+       p = xdr_encode_opaque_fixed(p, write->wr_verifier.data,
+                                   NFS4_VERIFIER_SIZE);
+       return nfs_ok;
+}
+
+static __be32
+nfsd4_encode_copy(struct nfsd4_compoundres *resp, __be32 nfserr,
+                 struct nfsd4_copy *copy)
+{
+       __be32 *p;
+
+       if (!nfserr) {
+               nfserr = nfsd42_encode_write_res(resp, &copy->cp_res);
+               if (nfserr)
+                       return nfserr;
+
+               p = xdr_reserve_space(&resp->xdr, 4 + 4);
+               *p++ = cpu_to_be32(copy->cp_consecutive);
+               *p++ = cpu_to_be32(copy->cp_synchronous);
+       }
+       return nfserr;
+}
+
 static __be32
 nfsd4_encode_seek(struct nfsd4_compoundres *resp, __be32 nfserr,
                  struct nfsd4_seek *seek)
@@ -4300,7 +4359,7 @@ static nfsd4_enc nfsd4_enc_ops[] = {
 
        /* NFSv4.2 operations */
        [OP_ALLOCATE]           = (nfsd4_enc)nfsd4_encode_noop,
-       [OP_COPY]               = (nfsd4_enc)nfsd4_encode_noop,
+       [OP_COPY]               = (nfsd4_enc)nfsd4_encode_copy,
        [OP_COPY_NOTIFY]        = (nfsd4_enc)nfsd4_encode_noop,
        [OP_DEALLOCATE]         = (nfsd4_enc)nfsd4_encode_noop,
        [OP_IO_ADVISE]          = (nfsd4_enc)nfsd4_encode_noop,
index ff476e654b8f8044b84808b1c92c54055e4ca393..8ca642fe9b21f556d9ddc56079af83f9b8c095b3 100644 (file)
@@ -513,6 +513,22 @@ __be32 nfsd4_clone_file_range(struct file *src, u64 src_pos, struct file *dst,
                        count));
 }
 
+ssize_t nfsd_copy_file_range(struct file *src, u64 src_pos, struct file *dst,
+                            u64 dst_pos, u64 count)
+{
+
+       /*
+        * Limit copy to 4MB to prevent indefinitely blocking an nfsd
+        * thread and client rpc slot.  The choice of 4MB is somewhat
+        * arbitrary.  We might instead base this on r/wsize, or make it
+        * tunable, or use a time instead of a byte limit, or implement
+        * asynchronous copy.  In theory a client could also recognize a
+        * limit like this and pipeline multiple COPY requests.
+        */
+       count = min_t(u64, count, 1 << 22);
+       return vfs_copy_file_range(src, src_pos, dst, dst_pos, count, 0);
+}
+
 __be32 nfsd4_vfs_fallocate(struct svc_rqst *rqstp, struct svc_fh *fhp,
                           struct file *file, loff_t offset, loff_t len,
                           int flags)
index 3cbb1b33777b5219aef4116fb6e07fd83183a8a8..0bf9e7bf5800af3855e3d93aaec194dcbea93ba6 100644 (file)
@@ -96,6 +96,8 @@ __be32                nfsd_symlink(struct svc_rqst *, struct svc_fh *,
                                struct svc_fh *res);
 __be32         nfsd_link(struct svc_rqst *, struct svc_fh *,
                                char *, int, struct svc_fh *);
+ssize_t                nfsd_copy_file_range(struct file *, u64,
+                                    struct file *, u64, u64);
 __be32         nfsd_rename(struct svc_rqst *,
                                struct svc_fh *, char *, int,
                                struct svc_fh *, char *, int);
index beea0c5edc51436cb3525fa0f2cc58463f07a3e0..8fda4abdf3b12c358ffa43bef8870b5b2b057341 100644 (file)
@@ -503,6 +503,28 @@ struct nfsd4_clone {
        u64             cl_count;
 };
 
+struct nfsd42_write_res {
+       u64                     wr_bytes_written;
+       u32                     wr_stable_how;
+       nfs4_verifier           wr_verifier;
+};
+
+struct nfsd4_copy {
+       /* request */
+       stateid_t       cp_src_stateid;
+       stateid_t       cp_dst_stateid;
+       u64             cp_src_pos;
+       u64             cp_dst_pos;
+       u64             cp_count;
+
+       /* both */
+       bool            cp_consecutive;
+       bool            cp_synchronous;
+
+       /* response */
+       struct nfsd42_write_res cp_res;
+};
+
 struct nfsd4_seek {
        /* request */
        stateid_t       seek_stateid;
@@ -568,6 +590,7 @@ struct nfsd4_op {
                struct nfsd4_fallocate          allocate;
                struct nfsd4_fallocate          deallocate;
                struct nfsd4_clone              clone;
+               struct nfsd4_copy               copy;
                struct nfsd4_seek               seek;
        } u;
        struct nfs4_replay *                    replay;