On 07/22/2013 02:05 PM, J. Bruce Fields wrote: > On Fri, Jul 19, 2013 at 05:03:47PM -0400, bjschuma@xxxxxxxxxx wrote: >> From: Bryan Schumaker <bjschuma@xxxxxxxxxx> >> >> 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: Bryan Schumaker <bjschuma@xxxxxxxxxx> >> --- >> fs/nfsd/nfs4proc.c | 75 ++++++++++++++++++++++++++++--- >> fs/nfsd/nfs4state.c | 4 +- >> fs/nfsd/nfs4xdr.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++- >> fs/nfsd/state.h | 2 +- >> fs/nfsd/vfs.c | 9 ++++ >> fs/nfsd/vfs.h | 1 + >> fs/nfsd/xdr4.h | 24 ++++++++++ >> include/linux/nfs4.h | 3 ++ >> 8 files changed, 230 insertions(+), 9 deletions(-) >> >> diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c >> index a7cee86..d4584ea 100644 >> --- a/fs/nfsd/nfs4proc.c >> +++ b/fs/nfsd/nfs4proc.c >> @@ -780,8 +780,9 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, >> >> nfs4_lock_state(); >> /* check stateid */ >> - if ((status = nfs4_preprocess_stateid_op(SVC_NET(rqstp), >> - cstate, &read->rd_stateid, >> + if ((status = nfs4_preprocess_stateid_op(SVC_NET(rqstp), cstate, >> + &cstate->current_fh, >> + &read->rd_stateid, >> RD_STATE, &read->rd_filp))) { >> dprintk("NFSD: nfsd4_read: couldn't process stateid!\n"); >> goto out; >> @@ -931,7 +932,7 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, >> if (setattr->sa_iattr.ia_valid & ATTR_SIZE) { >> nfs4_lock_state(); >> status = nfs4_preprocess_stateid_op(SVC_NET(rqstp), cstate, >> - &setattr->sa_stateid, WR_STATE, NULL); >> + &cstate->current_fh, &setattr->sa_stateid, WR_STATE, NULL); >> nfs4_unlock_state(); >> if (status) { >> dprintk("NFSD: nfsd4_setattr: couldn't process stateid!\n"); >> @@ -999,8 +1000,9 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, >> return nfserr_inval; >> >> nfs4_lock_state(); >> - status = nfs4_preprocess_stateid_op(SVC_NET(rqstp), >> - cstate, stateid, WR_STATE, &filp); >> + status = nfs4_preprocess_stateid_op(SVC_NET(rqstp), cstate, >> + &cstate->current_fh, stateid, >> + WR_STATE, &filp); >> if (status) { >> nfs4_unlock_state(); >> dprintk("NFSD: nfsd4_write: couldn't process stateid!\n"); >> @@ -1028,6 +1030,63 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, >> return status; >> } >> >> +static __be32 >> +nfsd4_verify_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, >> + struct nfsd4_copy *copy, struct file **src, struct file **dst) >> +{ >> + __be32 status; >> + /* only support copying data to an existing file */ >> + if (!cstate->current_fh.fh_dentry || !cstate->save_fh.fh_dentry) >> + return nfserr_nofilehandle; >> + >> + nfs4_lock_state(); >> + /* check stateids */ >> + if ((status = nfs4_preprocess_stateid_op(SVC_NET(rqstp), cstate, >> + &cstate->save_fh, >> + ©->cp_src_stateid, >> + RD_STATE, src))){ >> + dprintk("NFSD: nfsd4_copy: couldn't process src stateid!\n"); >> + goto out; >> + } >> + >> + if ((status = nfs4_preprocess_stateid_op(SVC_NET(rqstp), cstate, >> + &cstate->current_fh, >> + ©->cp_dst_stateid, >> + WR_STATE, dst))){ >> + dprintk("NFSD: nfsd4_copy: couldn't process dst stateid!\n"); >> + goto out; >> + } >> + >> +out: >> + nfs4_unlock_state(); >> + return status; >> +} >> + >> +static __be32 >> +nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, >> + struct nfsd4_copy *copy) >> +{ >> + __be32 status; >> + struct file *src = NULL, *dst = NULL; >> + >> + status = nfsd4_verify_copy(rqstp, cstate, copy, &src, &dst); >> + if (status) >> + return status; >> + >> + status = nfsd_copy_range(src, copy->cp_src_pos, >> + dst, copy->cp_dst_pos, >> + copy->cp_count); >> + >> + if (status == nfs_ok) { >> + copy->cp_res.wr_stateid = NULL; >> + copy->cp_res.wr_bytes_written = copy->cp_count; >> + copy->cp_res.wr_stable_how = NFS_FILE_SYNC; >> + gen_boot_verifier(©->cp_res.wr_verifier, SVC_NET(rqstp)); >> + } >> + >> + return status; >> +} >> + >> /* This routine never returns NFS_OK! If there are no other errors, it >> * will return NFSERR_SAME or NFSERR_NOT_SAME depending on whether the >> * attributes matched. VERIFY is implemented by mapping NFSERR_SAME >> @@ -1840,6 +1899,12 @@ static struct nfsd4_operation nfsd4_ops[] = { >> .op_get_currentstateid = (stateid_getter)nfsd4_get_freestateid, >> .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, >> }, >> + >> + /* NFSv4.2 operations */ >> + [OP_COPY] = { >> + .op_func = (nfsd4op_func)nfsd4_copy, >> + .op_name = "OP_COPY", > > This needs more fields filled in. Probably take the OP_WRITE entry as a > starting point. I'll look, thanks for the tip! > >> + } >> }; >> >> #ifdef NFSD_DEBUG >> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c >> index 280acef..c4e270e 100644 >> --- a/fs/nfsd/nfs4state.c >> +++ b/fs/nfsd/nfs4state.c >> @@ -3613,12 +3613,12 @@ static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, >> */ >> __be32 >> nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate, >> - stateid_t *stateid, int flags, struct file **filpp) >> + struct svc_fh *current_fh, stateid_t *stateid, >> + int flags, struct file **filpp) >> { >> struct nfs4_stid *s; >> struct nfs4_ol_stateid *stp = NULL; >> struct nfs4_delegation *dp = NULL; >> - struct svc_fh *current_fh = &cstate->current_fh; >> struct inode *ino = current_fh->fh_dentry->d_inode; >> struct nfsd_net *nn = net_generic(net, nfsd_net_id); >> __be32 status; >> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c >> index 0c0f3ea9..8f84e9e 100644 >> --- a/fs/nfsd/nfs4xdr.c >> +++ b/fs/nfsd/nfs4xdr.c >> @@ -1485,6 +1485,26 @@ static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, str >> } >> >> static __be32 >> +nfsd4_decode_copy(struct nfsd4_compoundargs *argp, struct nfsd4_copy *copy) >> +{ >> + DECODE_HEAD; >> + >> + status = nfsd4_decode_stateid(argp, ©->cp_src_stateid); >> + if (status) >> + return status; >> + status = nfsd4_decode_stateid(argp, ©->cp_dst_stateid); >> + if (status) >> + return status; >> + >> + READ_BUF(24); >> + READ64(copy->cp_src_pos); >> + READ64(copy->cp_dst_pos); >> + READ64(copy->cp_count); >> + >> + DECODE_TAIL; >> +} >> + >> +static __be32 >> nfsd4_decode_noop(struct nfsd4_compoundargs *argp, void *p) >> { >> return nfs_ok; >> @@ -1599,6 +1619,70 @@ static nfsd4_dec nfsd41_dec_ops[] = { >> [OP_RECLAIM_COMPLETE] = (nfsd4_dec)nfsd4_decode_reclaim_complete, >> }; >> >> +static nfsd4_dec nfsd42_dec_ops[] = { >> + [OP_ACCESS] = (nfsd4_dec)nfsd4_decode_access, >> + [OP_CLOSE] = (nfsd4_dec)nfsd4_decode_close, >> + [OP_COMMIT] = (nfsd4_dec)nfsd4_decode_commit, >> + [OP_CREATE] = (nfsd4_dec)nfsd4_decode_create, >> + [OP_DELEGPURGE] = (nfsd4_dec)nfsd4_decode_notsupp, >> + [OP_DELEGRETURN] = (nfsd4_dec)nfsd4_decode_delegreturn, >> + [OP_GETATTR] = (nfsd4_dec)nfsd4_decode_getattr, >> + [OP_GETFH] = (nfsd4_dec)nfsd4_decode_noop, >> + [OP_LINK] = (nfsd4_dec)nfsd4_decode_link, >> + [OP_LOCK] = (nfsd4_dec)nfsd4_decode_lock, >> + [OP_LOCKT] = (nfsd4_dec)nfsd4_decode_lockt, >> + [OP_LOCKU] = (nfsd4_dec)nfsd4_decode_locku, >> + [OP_LOOKUP] = (nfsd4_dec)nfsd4_decode_lookup, >> + [OP_LOOKUPP] = (nfsd4_dec)nfsd4_decode_noop, >> + [OP_NVERIFY] = (nfsd4_dec)nfsd4_decode_verify, >> + [OP_OPEN] = (nfsd4_dec)nfsd4_decode_open, >> + [OP_OPENATTR] = (nfsd4_dec)nfsd4_decode_notsupp, >> + [OP_OPEN_CONFIRM] = (nfsd4_dec)nfsd4_decode_notsupp, >> + [OP_OPEN_DOWNGRADE] = (nfsd4_dec)nfsd4_decode_open_downgrade, >> + [OP_PUTFH] = (nfsd4_dec)nfsd4_decode_putfh, >> + [OP_PUTPUBFH] = (nfsd4_dec)nfsd4_decode_notsupp, >> + [OP_PUTROOTFH] = (nfsd4_dec)nfsd4_decode_noop, >> + [OP_READ] = (nfsd4_dec)nfsd4_decode_read, >> + [OP_READDIR] = (nfsd4_dec)nfsd4_decode_readdir, >> + [OP_READLINK] = (nfsd4_dec)nfsd4_decode_noop, >> + [OP_REMOVE] = (nfsd4_dec)nfsd4_decode_remove, >> + [OP_RENAME] = (nfsd4_dec)nfsd4_decode_rename, >> + [OP_RENEW] = (nfsd4_dec)nfsd4_decode_notsupp, >> + [OP_RESTOREFH] = (nfsd4_dec)nfsd4_decode_noop, >> + [OP_SAVEFH] = (nfsd4_dec)nfsd4_decode_noop, >> + [OP_SECINFO] = (nfsd4_dec)nfsd4_decode_secinfo, >> + [OP_SETATTR] = (nfsd4_dec)nfsd4_decode_setattr, >> + [OP_SETCLIENTID] = (nfsd4_dec)nfsd4_decode_notsupp, >> + [OP_SETCLIENTID_CONFIRM]= (nfsd4_dec)nfsd4_decode_notsupp, >> + [OP_VERIFY] = (nfsd4_dec)nfsd4_decode_verify, >> + [OP_WRITE] = (nfsd4_dec)nfsd4_decode_write, >> + [OP_RELEASE_LOCKOWNER] = (nfsd4_dec)nfsd4_decode_notsupp, >> + >> + /* new operations for NFSv4.1 */ >> + [OP_BACKCHANNEL_CTL] = (nfsd4_dec)nfsd4_decode_backchannel_ctl, >> + [OP_BIND_CONN_TO_SESSION]= (nfsd4_dec)nfsd4_decode_bind_conn_to_session, >> + [OP_EXCHANGE_ID] = (nfsd4_dec)nfsd4_decode_exchange_id, >> + [OP_CREATE_SESSION] = (nfsd4_dec)nfsd4_decode_create_session, >> + [OP_DESTROY_SESSION] = (nfsd4_dec)nfsd4_decode_destroy_session, >> + [OP_FREE_STATEID] = (nfsd4_dec)nfsd4_decode_free_stateid, >> + [OP_GET_DIR_DELEGATION] = (nfsd4_dec)nfsd4_decode_notsupp, >> + [OP_GETDEVICEINFO] = (nfsd4_dec)nfsd4_decode_notsupp, >> + [OP_GETDEVICELIST] = (nfsd4_dec)nfsd4_decode_notsupp, >> + [OP_LAYOUTCOMMIT] = (nfsd4_dec)nfsd4_decode_notsupp, >> + [OP_LAYOUTGET] = (nfsd4_dec)nfsd4_decode_notsupp, >> + [OP_LAYOUTRETURN] = (nfsd4_dec)nfsd4_decode_notsupp, >> + [OP_SECINFO_NO_NAME] = (nfsd4_dec)nfsd4_decode_secinfo_no_name, >> + [OP_SEQUENCE] = (nfsd4_dec)nfsd4_decode_sequence, >> + [OP_SET_SSV] = (nfsd4_dec)nfsd4_decode_notsupp, >> + [OP_TEST_STATEID] = (nfsd4_dec)nfsd4_decode_test_stateid, >> + [OP_WANT_DELEGATION] = (nfsd4_dec)nfsd4_decode_notsupp, >> + [OP_DESTROY_CLIENTID] = (nfsd4_dec)nfsd4_decode_destroy_clientid, >> + [OP_RECLAIM_COMPLETE] = (nfsd4_dec)nfsd4_decode_reclaim_complete, >> + >> + /* new operations for NFS v4.2 */ >> + [OP_COPY] = (nfsd4_dec)nfsd4_decode_copy, >> +}; >> + >> struct nfsd4_minorversion_ops { >> nfsd4_dec *decoders; >> int nops; >> @@ -1607,7 +1691,7 @@ struct nfsd4_minorversion_ops { >> static struct nfsd4_minorversion_ops nfsd4_minorversion[] = { >> [0] = { nfsd4_dec_ops, ARRAY_SIZE(nfsd4_dec_ops) }, >> [1] = { nfsd41_dec_ops, ARRAY_SIZE(nfsd41_dec_ops) }, >> - [2] = { nfsd41_dec_ops, ARRAY_SIZE(nfsd41_dec_ops) }, >> + [2] = { nfsd42_dec_ops, ARRAY_SIZE(nfsd42_dec_ops) }, >> }; >> >> static __be32 >> @@ -3518,6 +3602,38 @@ nfsd4_encode_test_stateid(struct nfsd4_compoundres *resp, __be32 nfserr, >> return nfserr; >> } >> >> +static void >> +nfsd42_encode_write_res(struct nfsd4_compoundres *resp, struct nfsd42_write_res *write) >> +{ >> + __be32 *p; >> + >> + RESERVE_SPACE(4); >> + >> + if (write->wr_stateid == NULL) { >> + WRITE32(0); >> + ADJUST_ARGS(); >> + } else { >> + WRITE32(1); >> + ADJUST_ARGS(); >> + nfsd4_encode_stateid(resp, write->wr_stateid); >> + } >> + >> + RESERVE_SPACE(12 + NFS4_VERIFIER_SIZE); >> + WRITE64(write->wr_bytes_written); >> + WRITE32(write->wr_stable_how); >> + WRITEMEM(write->wr_verifier.data, NFS4_VERIFIER_SIZE); >> + ADJUST_ARGS(); > > If I'm reading > http://tools.ietf.org/html/draft-ietf-nfsv4-minorversion2-19 14.1.2 > correctly.... This should be just an offset in the error case, right? Yeah, I think it should be... I'll fix that up once I figure out where I can find an offset! > > Also, may as well share code in the succesful case with > nfsd4_encode_write(). Okay, I'll see what I can do. Thanks! - Bryan > > --b. > >> +} >> + >> +static __be32 >> +nfsd4_encode_copy(struct nfsd4_compoundres *resp, __be32 nfserr, >> + struct nfsd4_copy *copy) >> +{ >> + if (!nfserr) >> + nfsd42_encode_write_res(resp, ©->cp_res); >> + return nfserr; >> +} >> + >> static __be32 >> nfsd4_encode_noop(struct nfsd4_compoundres *resp, __be32 nfserr, void *p) >> { >> @@ -3590,6 +3706,9 @@ static nfsd4_enc nfsd4_enc_ops[] = { >> [OP_WANT_DELEGATION] = (nfsd4_enc)nfsd4_encode_noop, >> [OP_DESTROY_CLIENTID] = (nfsd4_enc)nfsd4_encode_noop, >> [OP_RECLAIM_COMPLETE] = (nfsd4_enc)nfsd4_encode_noop, >> + >> + /* NFSv4.2 operations */ >> + [OP_COPY] = (nfsd4_enc)nfsd4_encode_copy, >> }; >> >> /* >> diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h >> index 424d8f5..2478805 100644 >> --- a/fs/nfsd/state.h >> +++ b/fs/nfsd/state.h >> @@ -455,7 +455,7 @@ struct nfsd4_compound_state; >> struct nfsd_net; >> >> extern __be32 nfs4_preprocess_stateid_op(struct net *net, >> - struct nfsd4_compound_state *cstate, >> + struct nfsd4_compound_state *cstate, struct svc_fh *, >> stateid_t *stateid, int flags, struct file **filp); >> extern void nfs4_lock_state(void); >> extern void nfs4_unlock_state(void); >> diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c >> index 8ff6a00..d77958d 100644 >> --- a/fs/nfsd/vfs.c >> +++ b/fs/nfsd/vfs.c >> @@ -649,6 +649,15 @@ __be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp, >> } >> #endif >> >> +__be32 nfsd_copy_range(struct file *src, u64 src_pos, >> + struct file *dst, u64 dst_pos, >> + u64 count) >> +{ >> + int err = vfs_copy_range(src, src_pos, dst, dst_pos, count); >> + if (err < 0) >> + return nfserrno(err); >> + return vfs_fsync_range(dst, dst_pos, dst_pos + count, 0); >> +} >> #endif /* defined(CONFIG_NFSD_V4) */ >> >> #ifdef CONFIG_NFSD_V3 >> diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h >> index a4be2e3..0f26c3b 100644 >> --- a/fs/nfsd/vfs.h >> +++ b/fs/nfsd/vfs.h >> @@ -86,6 +86,7 @@ __be32 nfsd_symlink(struct svc_rqst *, struct svc_fh *, >> struct svc_fh *res, struct iattr *); >> __be32 nfsd_link(struct svc_rqst *, struct svc_fh *, >> char *, int, struct svc_fh *); >> +__be32 nfsd_copy_range(struct file *, u64, struct file *, u64, u64); >> __be32 nfsd_rename(struct svc_rqst *, >> struct svc_fh *, char *, int, >> struct svc_fh *, char *, int); >> diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h >> index b3ed644..55b9ef7 100644 >> --- a/fs/nfsd/xdr4.h >> +++ b/fs/nfsd/xdr4.h >> @@ -430,6 +430,27 @@ struct nfsd4_reclaim_complete { >> u32 rca_one_fs; >> }; >> >> +struct nfsd42_write_res { >> + stateid_t *wr_stateid; >> + 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; >> + >> + /* response */ >> + struct nfsd42_write_res cp_res; >> +}; >> + >> struct nfsd4_op { >> int opnum; >> __be32 status; >> @@ -475,6 +496,9 @@ struct nfsd4_op { >> struct nfsd4_reclaim_complete reclaim_complete; >> struct nfsd4_test_stateid test_stateid; >> struct nfsd4_free_stateid free_stateid; >> + >> + /* NFSv4.2 */ >> + struct nfsd4_copy copy; >> } u; >> struct nfs4_replay * replay; >> }; >> diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h >> index e36dee5..ebf60c6 100644 >> --- a/include/linux/nfs4.h >> +++ b/include/linux/nfs4.h >> @@ -110,6 +110,9 @@ enum nfs_opnum4 { >> OP_DESTROY_CLIENTID = 57, >> OP_RECLAIM_COMPLETE = 58, >> >> + /* nfs42 */ >> + OP_COPY = 59, >> + >> OP_ILLEGAL = 10044, >> }; >> >> -- >> 1.8.3.3 >> >> -- >> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in >> the body of a message to majordomo@xxxxxxxxxxxxxxx >> More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html