aboutsummaryrefslogtreecommitdiff
path: root/sys/fs
diff options
context:
space:
mode:
Diffstat (limited to 'sys/fs')
-rw-r--r--sys/fs/nfs/nfs_commonkrpc.c1
-rw-r--r--sys/fs/nfs/nfs_commonsubs.c45
-rw-r--r--sys/fs/nfs/nfs_var.h17
-rw-r--r--sys/fs/nfs/nfsport.h7
-rw-r--r--sys/fs/nfs/nfsproto.h13
-rw-r--r--sys/fs/nfsclient/nfs_clcomsubs.c2
-rw-r--r--sys/fs/nfsclient/nfs_clport.c13
-rw-r--r--sys/fs/nfsclient/nfs_clrpcops.c237
-rw-r--r--sys/fs/nfsclient/nfs_clstate.c2
-rw-r--r--sys/fs/nfsclient/nfs_clvfsops.c10
-rw-r--r--sys/fs/nfsclient/nfs_clvnops.c139
-rw-r--r--sys/fs/nfsclient/nfsmount.h1
-rw-r--r--sys/fs/nfsserver/nfs_nfsdport.c45
-rw-r--r--sys/fs/nfsserver/nfs_nfsdserv.c216
-rw-r--r--sys/fs/nfsserver/nfs_nfsdsocket.c2
-rw-r--r--sys/fs/nfsserver/nfs_nfsdstate.c2
16 files changed, 660 insertions, 92 deletions
diff --git a/sys/fs/nfs/nfs_commonkrpc.c b/sys/fs/nfs/nfs_commonkrpc.c
index 0ae3b94bef89..1e4e8506790f 100644
--- a/sys/fs/nfs/nfs_commonkrpc.c
+++ b/sys/fs/nfs/nfs_commonkrpc.c
@@ -239,6 +239,7 @@ static bool nfscl_use_gss[NFSV42_NPROCS] = {
true,
true,
true,
+ true,
};
/*
diff --git a/sys/fs/nfs/nfs_commonsubs.c b/sys/fs/nfs/nfs_commonsubs.c
index 67e33193ecec..7f5b29ca2085 100644
--- a/sys/fs/nfs/nfs_commonsubs.c
+++ b/sys/fs/nfs/nfs_commonsubs.c
@@ -187,7 +187,7 @@ struct nfsv4_opflag nfsv4_opflag[NFSV42_NOPS] = {
{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* Read Plus */
{ 0, 1, 0, 0, LK_SHARED, 1, 0 }, /* Seek */
{ 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* Write Same */
- { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* Clone */
+ { 2, 1, 1, 0, LK_SHARED, 1, 0 }, /* Clone */
{ 0, 1, 0, 0, LK_SHARED, 1, 1 }, /* Getxattr */
{ 0, 1, 1, 1, LK_EXCLUSIVE, 1, 1 }, /* Setxattr */
{ 0, 1, 0, 0, LK_SHARED, 1, 1 }, /* Listxattrs */
@@ -219,7 +219,7 @@ NFSD_VNET_DEFINE_STATIC(u_char *, nfsrv_dnsname) = NULL;
static int nfs_bigreply[NFSV42_NPROCS] = { 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 1, 0, 0, 0, 0, 0, 0 };
+ 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 };
/* local functions */
static int nfsrv_skipace(struct nfsrv_descript *nd, int *acesizep);
@@ -310,6 +310,7 @@ static struct {
{ NFSV4OP_LAYOUTERROR, 1, "LayoutError", 11, },
{ NFSV4OP_VERIFY, 3, "AppendWrite", 11, },
{ NFSV4OP_OPENATTR, 3, "OpenAttr", 8, },
+ { NFSV4OP_SAVEFH, 5, "Clone", 5, },
};
/*
@@ -319,7 +320,7 @@ static int nfs_bigrequest[NFSV42_NPROCS] = {
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
- 0, 1, 0
+ 0, 1, 0, 0
};
/*
@@ -648,7 +649,7 @@ nfscl_fillsattr(struct nfsrv_descript *nd, struct vattr *vap,
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMECREATE);
(void) nfsv4_fillattr(nd, vp->v_mount, vp, NULL, vap, NULL, 0,
&attrbits, NULL, NULL, 0, 0, 0, 0, (uint64_t)0, NULL,
- false, false, false);
+ false, false, false, 0);
break;
}
}
@@ -1302,7 +1303,7 @@ nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
struct nfsv3_pathconf *pc, struct statfs *sbp, struct nfsstatfs *sfp,
struct nfsfsinfo *fsp, NFSACL_T *aclp, int compare, int *retcmpp,
u_int32_t *leasep, u_int32_t *rderrp, bool *has_namedattrp,
- NFSPROC_T *p, struct ucred *cred)
+ uint32_t *clone_blksizep, NFSPROC_T *p, struct ucred *cred)
{
u_int32_t *tl;
int i = 0, j, k, l = 0, m, bitpos, attrsum = 0;
@@ -1437,6 +1438,13 @@ nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
NFSCLRBIT_ATTRBIT(&checkattrbits,
NFSATTRBIT_SYSTEM);
}
+ /* Some filesystems do not support block cloning */
+ if (vp == NULL || VOP_PATHCONF(vp,
+ _PC_CLONE_BLKSIZE, &has_pathconf) != 0)
+ has_pathconf = 0;
+ if (has_pathconf == 0)
+ NFSCLRBIT_ATTRBIT(&checkattrbits,
+ NFSATTRBIT_CLONEBLKSIZE);
if (!NFSEQUAL_ATTRBIT(&retattrbits, &checkattrbits)
|| retnotsup)
*retcmpp = NFSERR_NOTSAME;
@@ -2374,6 +2382,23 @@ nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
if (compare && !(*retcmpp) && i != nfs_srvmaxio)
*retcmpp = NFSERR_NOTSAME;
break;
+ case NFSATTRBIT_CLONEBLKSIZE:
+ NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
+ if (compare) {
+ if (!(*retcmpp)) {
+ if (vp == NULL || VOP_PATHCONF(vp,
+ _PC_CLONE_BLKSIZE, &has_pathconf)
+ != 0)
+ has_pathconf = 0;
+ if (has_pathconf !=
+ fxdr_unsigned(uint32_t, *tl))
+ *retcmpp = NFSERR_NOTSAME;
+ }
+ } else if (clone_blksizep != NULL) {
+ *clone_blksizep = fxdr_unsigned(uint32_t, *tl);
+ }
+ attrsum += NFSX_UNSIGNED;
+ break;
case NFSATTRBIT_CHANGEATTRTYPE:
NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
if (compare) {
@@ -2648,7 +2673,7 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
nfsattrbit_t *attrbitp, struct ucred *cred, NFSPROC_T *p, int isdgram,
int reterr, int supports_nfsv4acls, int at_root, uint64_t mounted_on_fileno,
struct statfs *pnfssf, bool xattrsupp, bool has_hiddensystem,
- bool has_namedattr)
+ bool has_namedattr, uint32_t clone_blksize)
{
int bitpos, retnum = 0;
u_int32_t *tl;
@@ -2771,6 +2796,9 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_HIDDEN);
NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_SYSTEM);
}
+ if (clone_blksize == 0)
+ NFSCLRBIT_ATTRBIT(&attrbits,
+ NFSATTRBIT_CLONEBLKSIZE);
retnum += nfsrv_putattrbit(nd, &attrbits);
break;
case NFSATTRBIT_TYPE:
@@ -3249,6 +3277,11 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
}
retnum += NFSX_UNSIGNED;
break;
+ case NFSATTRBIT_CLONEBLKSIZE:
+ NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
+ *tl = txdr_unsigned(clone_blksize);
+ retnum += NFSX_UNSIGNED;
+ break;
default:
printf("EEK! Bad V4 attribute bitpos=%d\n", bitpos);
}
diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h
index 54f60a753c50..61083ecf2d66 100644
--- a/sys/fs/nfs/nfs_var.h
+++ b/sys/fs/nfs/nfs_var.h
@@ -286,6 +286,8 @@ int nfsrvd_deallocate(struct nfsrv_descript *, int,
vnode_t, struct nfsexstuff *);
int nfsrvd_copy_file_range(struct nfsrv_descript *, int,
vnode_t, vnode_t, struct nfsexstuff *, struct nfsexstuff *);
+int nfsrvd_clone(struct nfsrv_descript *, int,
+ vnode_t, vnode_t, struct nfsexstuff *, struct nfsexstuff *);
int nfsrvd_seek(struct nfsrv_descript *, int,
vnode_t, struct nfsexstuff *);
int nfsrvd_getxattr(struct nfsrv_descript *, int,
@@ -341,7 +343,8 @@ int nfsv4_loadattr(struct nfsrv_descript *, vnode_t,
struct nfsvattr *, struct nfsfh **, fhandle_t *, int,
struct nfsv3_pathconf *, struct statfs *, struct nfsstatfs *,
struct nfsfsinfo *, NFSACL_T *,
- int, int *, u_int32_t *, u_int32_t *, bool *, NFSPROC_T *, struct ucred *);
+ int, int *, u_int32_t *, u_int32_t *, bool *, uint32_t *, NFSPROC_T *,
+ struct ucred *);
int nfsv4_lock(struct nfsv4lock *, int, int *, struct mtx *, struct mount *);
void nfsv4_unlock(struct nfsv4lock *, int);
void nfsv4_relref(struct nfsv4lock *);
@@ -397,7 +400,7 @@ void nfsrv_wcc(struct nfsrv_descript *, int, struct nfsvattr *, int,
int nfsv4_fillattr(struct nfsrv_descript *, struct mount *, vnode_t, NFSACL_T *,
struct vattr *, fhandle_t *, int, nfsattrbit_t *, struct ucred *,
NFSPROC_T *, int, int, int, int, uint64_t, struct statfs *, bool, bool,
- bool);
+ bool, uint32_t);
void nfsrv_fillattr(struct nfsrv_descript *, struct nfsvattr *);
struct mbuf *nfsrv_adj(struct mbuf *, int, int);
void nfsrv_postopattr(struct nfsrv_descript *, int, struct nfsvattr *);
@@ -517,10 +520,10 @@ int nfsrpc_lock(struct nfsrv_descript *, struct nfsmount *, vnode_t,
u_int8_t *, int, struct nfscllockowner *, int, int, u_int64_t,
u_int64_t, short, struct ucred *, NFSPROC_T *, int);
int nfsrpc_statfs(vnode_t, struct nfsstatfs *, struct nfsfsinfo *, uint32_t *,
- struct ucred *, NFSPROC_T *, struct nfsvattr *, int *);
+ uint32_t *, struct ucred *, NFSPROC_T *, struct nfsvattr *, int *);
int nfsrpc_fsinfo(vnode_t, struct nfsfsinfo *, struct ucred *,
NFSPROC_T *, struct nfsvattr *, int *);
-int nfsrpc_pathconf(vnode_t, struct nfsv3_pathconf *, bool *,
+int nfsrpc_pathconf(vnode_t, struct nfsv3_pathconf *, bool *, uint32_t *,
struct ucred *, NFSPROC_T *, struct nfsvattr *, int *);
int nfsrpc_renew(struct nfsclclient *, struct nfsclds *, struct ucred *,
NFSPROC_T *);
@@ -562,6 +565,8 @@ int nfsrpc_deallocate(vnode_t, off_t, off_t, struct nfsvattr *, int *,
int nfsrpc_copy_file_range(vnode_t, off_t *, vnode_t, off_t *, size_t *,
unsigned int, int *, struct nfsvattr *, int *, struct nfsvattr *,
struct ucred *, bool, bool *);
+int nfsrpc_clone(vnode_t, off_t *, vnode_t, off_t *, size_t *, bool,
+ int *, struct nfsvattr *, int *, struct nfsvattr *, struct ucred *);
int nfsrpc_seek(vnode_t, off_t *, bool *, int, struct ucred *,
struct nfsvattr *, int *);
int nfsrpc_getextattr(vnode_t, const char *, struct uio *, ssize_t *,
@@ -668,7 +673,7 @@ int nfscl_nget(mount_t, vnode_t, struct nfsfh *,
NFSPROC_T *nfscl_getparent(NFSPROC_T *);
void nfscl_start_renewthread(struct nfsclclient *);
void nfscl_loadsbinfo(struct nfsmount *, struct nfsstatfs *, void *);
-void nfscl_loadfsinfo (struct nfsmount *, struct nfsfsinfo *);
+void nfscl_loadfsinfo(struct nfsmount *, struct nfsfsinfo *, uint32_t);
void nfscl_delegreturn(struct nfscldeleg *, int, struct nfsmount *,
struct ucred *, NFSPROC_T *);
void nfsrvd_cbinit(int);
@@ -737,7 +742,7 @@ int nfsvno_updfilerev(vnode_t, struct nfsvattr *, struct nfsrv_descript *,
int nfsvno_fillattr(struct nfsrv_descript *, struct mount *, vnode_t,
struct nfsvattr *, fhandle_t *, int, nfsattrbit_t *,
struct ucred *, NFSPROC_T *, int, int, int, int, uint64_t, bool, bool,
- bool);
+ bool, uint32_t);
int nfsrv_sattr(struct nfsrv_descript *, vnode_t, struct nfsvattr *, nfsattrbit_t *,
NFSACL_T *, NFSPROC_T *);
int nfsv4_sattr(struct nfsrv_descript *, vnode_t, struct nfsvattr *, nfsattrbit_t *,
diff --git a/sys/fs/nfs/nfsport.h b/sys/fs/nfs/nfsport.h
index c30b46261df0..bd6107187966 100644
--- a/sys/fs/nfs/nfsport.h
+++ b/sys/fs/nfs/nfsport.h
@@ -442,10 +442,13 @@
/* Do a NFSv4 Openattr. */
#define NFSPROC_OPENATTR 70
+/* Do a NFSv4.2 Clone. */
+#define NFSPROC_CLONE 71
+
/*
* Must be defined as one higher than the last NFSv4.2 Proc# above.
*/
-#define NFSV42_NPROCS 71
+#define NFSV42_NPROCS 72
/* Value of NFSV42_NPROCS for old nfsstats structure. (Always 69) */
#define NFSV42_OLDNPROCS 69
@@ -477,7 +480,7 @@ struct nfsstatsv1 {
uint64_t readlink_bios;
uint64_t biocache_readdirs;
uint64_t readdir_bios;
- uint64_t rpccnt[NFSV42_NPROCS + 9];
+ uint64_t rpccnt[NFSV42_NPROCS + 8];
uint64_t rpcretries;
uint64_t srvrpccnt[NFSV42_NOPS + NFSV4OP_FAKENOPS + 15];
uint64_t srvlayouts;
diff --git a/sys/fs/nfs/nfsproto.h b/sys/fs/nfs/nfsproto.h
index cb5a80e8df73..d628108bdc1a 100644
--- a/sys/fs/nfs/nfsproto.h
+++ b/sys/fs/nfs/nfsproto.h
@@ -411,10 +411,13 @@
/* Do a NFSv4 Openattr. */
#define NFSPROC_OPENATTR 70
+/* Do a NFSv4.2 Clone. */
+#define NFSPROC_CLONE 71
+
/*
* Must be defined as one higher than the last NFSv4.2 Proc# above.
*/
-#define NFSV42_NPROCS 71
+#define NFSV42_NPROCS 72
/* Value of NFSV42_NPROCS for old nfsstats structure. (Always 69) */
#define NFSV42_OLDNPROCS 69
@@ -1194,6 +1197,7 @@ struct nfsv3_sattr {
NFSATTRBM_LAYOUTBLKSIZE | \
NFSATTRBM_LAYOUTALIGNMENT | \
NFSATTRBM_SUPPATTREXCLCREAT | \
+ NFSATTRBM_CLONEBLKSIZE | \
NFSATTRBM_CHANGEATTRTYPE | \
NFSATTRBM_XATTRSUPPORT)
@@ -1242,7 +1246,8 @@ struct nfsv3_sattr {
* NFSATTRBIT_NFSV42 - Attributes only supported by NFSv4.2.
*/
#define NFSATTRBIT_NFSV42_2 \
- (NFSATTRBM_CHANGEATTRTYPE | \
+ (NFSATTRBM_CLONEBLKSIZE | \
+ NFSATTRBM_CHANGEATTRTYPE | \
NFSATTRBM_XATTRSUPPORT | \
NFSATTRBM_MODEUMASK)
@@ -1415,7 +1420,7 @@ struct nfsv3_sattr {
/*
* NFSGETATTRBIT_STATFS2 - bits 64<->95
*/
-#define NFSGETATTRBIT_STATFS2 0
+#define NFSGETATTRBIT_STATFS2 (NFSATTRBM_CLONEBLKSIZE)
/*
* Set of attributes for the equivalent of an nfsv3 pathconf rpc.
@@ -1438,7 +1443,7 @@ struct nfsv3_sattr {
/*
* NFSGETATTRBIT_PATHCONF2 - bits 64<->95
*/
-#define NFSGETATTRBIT_PATHCONF2 0
+#define NFSGETATTRBIT_PATHCONF2 (NFSATTRBM_CLONEBLKSIZE)
/*
* Sets of attributes required by readdir and readdirplus.
diff --git a/sys/fs/nfsclient/nfs_clcomsubs.c b/sys/fs/nfsclient/nfs_clcomsubs.c
index bca0bdcd0df1..05963074e53d 100644
--- a/sys/fs/nfsclient/nfs_clcomsubs.c
+++ b/sys/fs/nfsclient/nfs_clcomsubs.c
@@ -272,7 +272,7 @@ nfsm_loadattr(struct nfsrv_descript *nd, struct nfsvattr *nap)
if (nd->nd_flag & ND_NFSV4) {
error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL,
NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL,
- NULL);
+ NULL, NULL);
} else if (nd->nd_flag & ND_NFSV3) {
NFSM_DISSECT(fp, struct nfs_fattr *, NFSX_V3FATTR);
nap->na_type = nfsv34tov_type(fp->fa_type);
diff --git a/sys/fs/nfsclient/nfs_clport.c b/sys/fs/nfsclient/nfs_clport.c
index b25d967982a1..704aeeeabdf2 100644
--- a/sys/fs/nfsclient/nfs_clport.c
+++ b/sys/fs/nfsclient/nfs_clport.c
@@ -828,7 +828,7 @@ nfscl_wcc_data(struct nfsrv_descript *nd, struct vnode *vp,
== (ND_NFSV4 | ND_V4WCCATTR)) {
error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
- NULL, NULL, NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL);
if (error)
return (error);
/*
@@ -963,7 +963,8 @@ nfscl_loadsbinfo(struct nfsmount *nmp, struct nfsstatfs *sfp, void *statfs)
* Use the fsinfo stuff to update the mount point.
*/
void
-nfscl_loadfsinfo(struct nfsmount *nmp, struct nfsfsinfo *fsp)
+nfscl_loadfsinfo(struct nfsmount *nmp, struct nfsfsinfo *fsp,
+ uint32_t clone_blksize)
{
if ((nmp->nm_wsize == 0 || fsp->fs_wtpref < nmp->nm_wsize) &&
@@ -1003,6 +1004,14 @@ nfscl_loadfsinfo(struct nfsmount *nmp, struct nfsfsinfo *fsp)
fsp->fs_maxfilesize < nmp->nm_maxfilesize)
nmp->nm_maxfilesize = fsp->fs_maxfilesize;
nmp->nm_mountp->mnt_stat.f_iosize = newnfs_iosize(nmp);
+
+ /*
+ * Although ZFS reports a clone_blksize of 16Mbytes,
+ * 128Kbytes usually works, so set it to that.
+ */
+ if (clone_blksize > 128 * 1024)
+ clone_blksize = 128 * 1024;
+ nmp->nm_cloneblksize = clone_blksize;
nmp->nm_state |= NFSSTA_GOTFSINFO;
}
diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c
index 920fcf7b8c61..4ec621de2eff 100644
--- a/sys/fs/nfsclient/nfs_clrpcops.c
+++ b/sys/fs/nfsclient/nfs_clrpcops.c
@@ -225,6 +225,9 @@ static int nfsrpc_layoutgetres(struct nfsmount *, vnode_t, uint8_t *,
static int nfsrpc_copyrpc(vnode_t, off_t, vnode_t, off_t, size_t *,
nfsv4stateid_t *, nfsv4stateid_t *, struct nfsvattr *, int *,
struct nfsvattr *, int *, bool, int *, struct ucred *, NFSPROC_T *);
+static int nfsrpc_clonerpc(vnode_t, off_t, vnode_t, off_t, size_t *, bool,
+ nfsv4stateid_t *, nfsv4stateid_t *, struct nfsvattr *, int *,
+ struct nfsvattr *, int *, struct ucred *, NFSPROC_T *);
static int nfsrpc_seekrpc(vnode_t, off_t *, nfsv4stateid_t *, bool *,
int, struct nfsvattr *, int *, struct ucred *);
static struct mbuf *nfsm_split(struct mbuf *, uint64_t);
@@ -696,7 +699,7 @@ nfsrpc_openrpc(struct nfsmount *nmp, vnode_t vp, u_int8_t *nfhp, int fhlen,
("nfsrpc_openrpc: Getattr repstat"));
error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
- NULL, NULL, NULL, NULL, p, cred);
+ NULL, NULL, NULL, NULL, NULL, p, cred);
if (error)
goto nfsmout;
}
@@ -1355,7 +1358,7 @@ nfsrpc_getattrnovp(struct nfsmount *nmp, u_int8_t *fhp, int fhlen, int syscred,
if ((nd->nd_flag & ND_NFSV4) != 0)
error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0,
NULL, NULL, NULL, NULL, NULL, 0, NULL, leasep, NULL,
- NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL);
else
error = nfsm_loadattr(nd, nap);
} else
@@ -3597,7 +3600,7 @@ nfsrpc_readdir(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
nfsva.na_mntonfileno = UINT64_MAX;
error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
- NULL, NULL, NULL, NULL, p, cred);
+ NULL, NULL, NULL, NULL, NULL, p, cred);
if (error) {
dotdotfileid = dotfileid;
} else if (gotmnton) {
@@ -3847,7 +3850,7 @@ nfsrpc_readdir(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
nfsva.na_mntonfileno = UINT64_MAX;
error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
- NULL, NULL, &rderr, NULL, p, cred);
+ NULL, NULL, &rderr, NULL, NULL, p, cred);
if (error)
goto nfsmout;
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
@@ -4072,7 +4075,7 @@ nfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
nfsva.na_mntonfileno = UINT64_MAX;
error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
- NULL, NULL, NULL, NULL, p, cred);
+ NULL, NULL, NULL, NULL, NULL, p, cred);
if (error) {
dotdotfileid = dotfileid;
} else if (gotmnton) {
@@ -4346,7 +4349,7 @@ nfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep,
nfsva.na_mntonfileno = 0xffffffff;
error = nfsv4_loadattr(nd, NULL, &nfsva, &nfhp,
NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
- NULL, NULL, &rderr, NULL, p, cred);
+ NULL, NULL, &rderr, NULL, NULL, p, cred);
if (error)
goto nfsmout;
}
@@ -4981,8 +4984,8 @@ nfsmout:
*/
int
nfsrpc_statfs(vnode_t vp, struct nfsstatfs *sbp, struct nfsfsinfo *fsp,
- uint32_t *leasep, struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap,
- int *attrflagp)
+ uint32_t *leasep, uint32_t *cloneblksizep, struct ucred *cred, NFSPROC_T *p,
+ struct nfsvattr *nap, int *attrflagp)
{
u_int32_t *tl = NULL;
struct nfsrv_descript nfsd, *nd = &nfsd;
@@ -4991,6 +4994,8 @@ nfsrpc_statfs(vnode_t vp, struct nfsstatfs *sbp, struct nfsfsinfo *fsp,
int error;
*attrflagp = 0;
+ if (cloneblksizep != NULL)
+ *cloneblksizep = 0;
nmp = VFSTONFS(vp->v_mount);
if (NFSHASNFSV4(nmp)) {
/*
@@ -5009,7 +5014,7 @@ nfsrpc_statfs(vnode_t vp, struct nfsstatfs *sbp, struct nfsfsinfo *fsp,
if (nd->nd_repstat == 0) {
error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0,
NULL, NULL, sbp, fsp, NULL, 0, NULL, leasep, NULL,
- NULL, p, cred);
+ NULL, cloneblksizep, p, cred);
if (!error) {
nmp->nm_fsid[0] = nap->na_filesid[0];
nmp->nm_fsid[1] = nap->na_filesid[1];
@@ -5063,7 +5068,8 @@ nfsmout:
*/
int
nfsrpc_pathconf(vnode_t vp, struct nfsv3_pathconf *pc, bool *has_namedattrp,
- struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp)
+ uint32_t *clone_blksizep, struct ucred *cred, NFSPROC_T *p,
+ struct nfsvattr *nap, int *attrflagp)
{
struct nfsrv_descript nfsd, *nd = &nfsd;
struct nfsmount *nmp;
@@ -5074,6 +5080,7 @@ nfsrpc_pathconf(vnode_t vp, struct nfsv3_pathconf *pc, bool *has_namedattrp,
*has_namedattrp = false;
*attrflagp = 0;
+ *clone_blksizep = 0;
nmp = VFSTONFS(vp->v_mount);
if (NFSHASNFSV4(nmp)) {
np = VTONFS(vp);
@@ -5100,7 +5107,7 @@ nfsrpc_pathconf(vnode_t vp, struct nfsv3_pathconf *pc, bool *has_namedattrp,
if (nd->nd_repstat == 0) {
error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0,
pc, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL,
- has_namedattrp, p, cred);
+ has_namedattrp, clone_blksizep, p, cred);
if (!error)
*attrflagp = 1;
} else {
@@ -5395,7 +5402,8 @@ nfsrpc_getacl(vnode_t vp, struct ucred *cred, NFSPROC_T *p, struct acl *aclp)
return (error);
if (!nd->nd_repstat)
error = nfsv4_loadattr(nd, vp, NULL, NULL, NULL, 0, NULL,
- NULL, NULL, NULL, aclp, 0, NULL, NULL, NULL, NULL, p, cred);
+ NULL, NULL, NULL, aclp, 0, NULL, NULL, NULL, NULL, NULL, p,
+ cred);
else
error = nd->nd_repstat;
m_freem(nd->nd_mrep);
@@ -5437,7 +5445,7 @@ nfsrpc_setaclrpc(vnode_t vp, struct ucred *cred, NFSPROC_T *p,
NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_ACL);
(void) nfsv4_fillattr(nd, vp->v_mount, vp, aclp, NULL, NULL, 0,
&attrbits, NULL, NULL, 0, 0, 0, 0, (uint64_t)0, NULL, false, false,
- false);
+ false, 0);
error = nfscl_request(nd, vp, p, cred);
if (error)
return (error);
@@ -8496,7 +8504,7 @@ nfsrpc_openlayoutrpc(struct nfsmount *nmp, vnode_t vp, u_int8_t *nfhp,
if (*++tl == 0) {
error = nfsv4_loadattr(nd, NULL, &nfsva, NULL,
NULL, 0, NULL, NULL, NULL, NULL, NULL, 0,
- NULL, NULL, NULL, NULL, p, cred);
+ NULL, NULL, NULL, NULL, NULL, p, cred);
if (error != 0)
goto nfsmout;
if (ndp != NULL) {
@@ -9168,6 +9176,199 @@ nfsmout:
}
/*
+ * nfs clone operation.
+ */
+int
+nfsrpc_clone(vnode_t invp, off_t *inoffp, vnode_t outvp,
+ off_t *outoffp, size_t *lenp, bool toeof, int *inattrflagp,
+ struct nfsvattr *innap, int *outattrflagp, struct nfsvattr *outnap,
+ struct ucred *cred)
+{
+ int error, expireret = 0, retrycnt;
+ uint32_t clidrev = 0;
+ struct nfsmount *nmp = VFSTONFS(invp->v_mount);
+ struct nfsfh *innfhp = NULL, *outnfhp = NULL;
+ nfsv4stateid_t instateid, outstateid;
+ void *inlckp, *outlckp;
+
+ if (nmp->nm_clp != NULL)
+ clidrev = nmp->nm_clp->nfsc_clientidrev;
+ innfhp = VTONFS(invp)->n_fhp;
+ outnfhp = VTONFS(outvp)->n_fhp;
+ retrycnt = 0;
+ do {
+ /* Get both stateids. */
+ inlckp = NULL;
+ nfscl_getstateid(invp, innfhp->nfh_fh, innfhp->nfh_len,
+ NFSV4OPEN_ACCESSREAD, 0, NULL, curthread, &instateid,
+ &inlckp);
+ outlckp = NULL;
+ nfscl_getstateid(outvp, outnfhp->nfh_fh, outnfhp->nfh_len,
+ NFSV4OPEN_ACCESSWRITE, 0, NULL, curthread, &outstateid,
+ &outlckp);
+
+ error = nfsrpc_clonerpc(invp, *inoffp, outvp, *outoffp, lenp,
+ toeof, &instateid, &outstateid, innap, inattrflagp, outnap,
+ outattrflagp, cred, curthread);
+ if (error == 0) {
+ *inoffp += *lenp;
+ *outoffp += *lenp;
+ } else if (error == NFSERR_STALESTATEID)
+ nfscl_initiate_recovery(nmp->nm_clp);
+ if (inlckp != NULL)
+ nfscl_lockderef(inlckp);
+ if (outlckp != NULL)
+ nfscl_lockderef(outlckp);
+ if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID ||
+ error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY ||
+ error == NFSERR_OLDSTATEID || error == NFSERR_BADSESSION) {
+ (void) nfs_catnap(PZERO, error, "nfs_cfr");
+ } else if ((error == NFSERR_EXPIRED || (!NFSHASINT(nmp) &&
+ error == NFSERR_BADSTATEID)) && clidrev != 0) {
+ expireret = nfscl_hasexpired(nmp->nm_clp, clidrev,
+ curthread);
+ } else if (error == NFSERR_BADSTATEID && NFSHASINT(nmp)) {
+ error = EIO;
+ }
+ retrycnt++;
+ } while (error == NFSERR_GRACE || error == NFSERR_DELAY ||
+ error == NFSERR_STALESTATEID || error == NFSERR_BADSESSION ||
+ error == NFSERR_STALEDONTRECOVER ||
+ (error == NFSERR_OLDSTATEID && retrycnt < 20) ||
+ ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) &&
+ expireret == 0 && clidrev != 0 && retrycnt < 4));
+ if (error != 0 && (retrycnt >= 4 ||
+ error == NFSERR_STALESTATEID || error == NFSERR_BADSESSION ||
+ error == NFSERR_STALEDONTRECOVER))
+ error = EIO;
+ return (error);
+}
+
+/*
+ * The clone RPC.
+ */
+static int
+nfsrpc_clonerpc(vnode_t invp, off_t inoff, vnode_t outvp, off_t outoff,
+ size_t *lenp, bool toeof, nfsv4stateid_t *instateidp,
+ nfsv4stateid_t *outstateidp, struct nfsvattr *innap, int *inattrflagp,
+ struct nfsvattr *outnap, int *outattrflagp, struct ucred *cred,
+ NFSPROC_T *p)
+{
+ uint32_t *tl, *opcntp;
+ int error;
+ struct nfsrv_descript nfsd;
+ struct nfsrv_descript *nd = &nfsd;
+ struct nfsmount *nmp;
+ nfsattrbit_t attrbits;
+ struct vattr va;
+ uint64_t len;
+
+ nmp = VFSTONFS(invp->v_mount);
+ *inattrflagp = *outattrflagp = 0;
+ len = *lenp;
+ if (len == 0)
+ return (0);
+ if (toeof)
+ len = 0;
+ nfscl_reqstart(nd, NFSPROC_CLONE, nmp, VTONFS(invp)->n_fhp->nfh_fh,
+ VTONFS(invp)->n_fhp->nfh_len, &opcntp, NULL, 0, 0, cred);
+ /*
+ * First do a Setattr of atime to the server's clock
+ * time. The FreeBSD "collective" was of the opinion
+ * that setting atime was necessary for this syscall.
+ * Do the Setattr before the Clone, so that it can be
+ * handled well if the server replies NFSERR_DELAY to
+ * the Setattr operation.
+ */
+ if ((nmp->nm_mountp->mnt_flag & MNT_NOATIME) == 0) {
+ NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
+ *tl = txdr_unsigned(NFSV4OP_SETATTR);
+ nfsm_stateidtom(nd, instateidp, NFSSTATEID_PUTSTATEID);
+ VATTR_NULL(&va);
+ va.va_atime.tv_sec = va.va_atime.tv_nsec = 0;
+ va.va_vaflags = VA_UTIMES_NULL;
+ nfscl_fillsattr(nd, &va, invp, 0, 0);
+ /* Bump opcnt from 7 to 8. */
+ *opcntp = txdr_unsigned(8);
+ }
+
+ /* Now Getattr the invp attributes. */
+ NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
+ *tl = txdr_unsigned(NFSV4OP_GETATTR);
+ NFSGETATTR_ATTRBIT(&attrbits);
+ nfsrv_putattrbit(nd, &attrbits);
+
+ /* Set outvp. */
+ NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
+ *tl = txdr_unsigned(NFSV4OP_PUTFH);
+ (void)nfsm_fhtom(nmp, nd, VTONFS(outvp)->n_fhp->nfh_fh,
+ VTONFS(outvp)->n_fhp->nfh_len, 0);
+
+ /* Do the Clone. */
+ NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED);
+ *tl = txdr_unsigned(NFSV4OP_CLONE);
+ nfsm_stateidtom(nd, instateidp, NFSSTATEID_PUTSTATEID);
+ nfsm_stateidtom(nd, outstateidp, NFSSTATEID_PUTSTATEID);
+ NFSM_BUILD(tl, uint32_t *, 3 * NFSX_HYPER + NFSX_UNSIGNED);
+ txdr_hyper(inoff, tl); tl += 2;
+ txdr_hyper(outoff, tl); tl += 2;
+ txdr_hyper(len, tl); tl += 2;
+
+ /* Get the outvp attributes. */
+ *tl = txdr_unsigned(NFSV4OP_GETATTR);
+ NFSWRITEGETATTR_ATTRBIT(&attrbits);
+ nfsrv_putattrbit(nd, &attrbits);
+
+ error = nfscl_request(nd, invp, p, cred);
+ if (error != 0)
+ return (error);
+ /* Skip over the Setattr reply. */
+ if ((nd->nd_flag & ND_NOMOREDATA) == 0 &&
+ (nmp->nm_mountp->mnt_flag & MNT_NOATIME) == 0) {
+ NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+ if (*(tl + 1) == 0) {
+ error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
+ if (error != 0)
+ goto nfsmout;
+ } else
+ nd->nd_flag |= ND_NOMOREDATA;
+ }
+ if ((nd->nd_flag & ND_NOMOREDATA) == 0) {
+ /* Get the input file's attributes. */
+ NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+ if (*(tl + 1) == 0) {
+ error = nfsm_loadattr(nd, innap);
+ if (error != 0)
+ goto nfsmout;
+ *inattrflagp = 1;
+ } else
+ nd->nd_flag |= ND_NOMOREDATA;
+ }
+ /* Skip over return stat for PutFH. */
+ if ((nd->nd_flag & ND_NOMOREDATA) == 0) {
+ NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+ if (*++tl != 0)
+ nd->nd_flag |= ND_NOMOREDATA;
+ }
+ /* Skip over return stat for Clone. */
+ if ((nd->nd_flag & ND_NOMOREDATA) == 0)
+ NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+ if (nd->nd_repstat == 0) {
+ NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+ error = nfsm_loadattr(nd, outnap);
+ if (error == 0)
+ *outattrflagp = NFS_LATTR_NOSHRINK;
+ } else {
+ *lenp = 0;
+ }
+ if (error == 0)
+ error = nd->nd_repstat;
+nfsmout:
+ m_freem(nd->nd_mrep);
+ return (error);
+}
+
+/*
* Seek operation.
*/
int
@@ -9724,13 +9925,13 @@ nfscl_statfs(struct vnode *vp, struct ucred *cred, NFSPROC_T *td)
struct nfsstatfs sb;
struct mount *mp;
struct nfsmount *nmp;
- uint32_t lease;
+ uint32_t clone_blksize, lease;
int attrflag, error;
mp = vp->v_mount;
nmp = VFSTONFS(mp);
- error = nfsrpc_statfs(vp, &sb, &fs, &lease, cred, td, &nfsva,
- &attrflag);
+ error = nfsrpc_statfs(vp, &sb, &fs, &lease, &clone_blksize, cred, td,
+ &nfsva, &attrflag);
if (attrflag != 0)
(void) nfscl_loadattrcache(&vp, &nfsva, NULL, 0, 1);
if (error == 0) {
@@ -9739,7 +9940,7 @@ nfscl_statfs(struct vnode *vp, struct ucred *cred, NFSPROC_T *td)
nmp->nm_clp->nfsc_renew = NFSCL_RENEW(lease);
NFSUNLOCKCLSTATE();
mtx_lock(&nmp->nm_mtx);
- nfscl_loadfsinfo(nmp, &fs);
+ nfscl_loadfsinfo(nmp, &fs, clone_blksize);
nfscl_loadsbinfo(nmp, &sb, &mp->mnt_stat);
mp->mnt_stat.f_iosize = newnfs_iosize(nmp);
mtx_unlock(&nmp->nm_mtx);
diff --git a/sys/fs/nfsclient/nfs_clstate.c b/sys/fs/nfsclient/nfs_clstate.c
index 99a781640c53..aa9d01fc4632 100644
--- a/sys/fs/nfsclient/nfs_clstate.c
+++ b/sys/fs/nfsclient/nfs_clstate.c
@@ -3701,7 +3701,7 @@ nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p)
if (!error)
(void) nfsv4_fillattr(nd, NULL, NULL, NULL, &va,
NULL, 0, &rattrbits, NULL, p, 0, 0, 0, 0,
- (uint64_t)0, NULL, false, false, false);
+ (uint64_t)0, NULL, false, false, false, 0);
break;
case NFSV4OP_CBRECALL:
NFSCL_DEBUG(4, "cbrecall\n");
diff --git a/sys/fs/nfsclient/nfs_clvfsops.c b/sys/fs/nfsclient/nfs_clvfsops.c
index 0bd05c03885b..5ea7eab07632 100644
--- a/sys/fs/nfsclient/nfs_clvfsops.c
+++ b/sys/fs/nfsclient/nfs_clvfsops.c
@@ -292,8 +292,10 @@ nfs_statfs(struct mount *mp, struct statfs *sbp)
int error = 0, attrflag, gotfsinfo = 0, ret;
struct nfsnode *np;
char *fakefh;
+ uint32_t clone_blksize;
td = curthread;
+ clone_blksize = 0;
error = vfs_busy(mp, MBF_NOWAIT);
if (error)
@@ -337,8 +339,8 @@ nfs_statfs(struct mount *mp, struct statfs *sbp)
} else
mtx_unlock(&nmp->nm_mtx);
if (!error)
- error = nfsrpc_statfs(vp, &sb, &fs, NULL, td->td_ucred, td,
- &nfsva, &attrflag);
+ error = nfsrpc_statfs(vp, &sb, &fs, NULL, &clone_blksize,
+ td->td_ucred, td, &nfsva, &attrflag);
if ((nmp->nm_privflag & NFSMNTP_FAKEROOTFH) != 0 &&
error == NFSERR_WRONGSEC) {
/* Cannot get new stats, so return what is in mnt_stat. */
@@ -375,7 +377,7 @@ nfs_statfs(struct mount *mp, struct statfs *sbp)
if (!error) {
mtx_lock(&nmp->nm_mtx);
if (gotfsinfo || (nmp->nm_flag & NFSMNT_NFSV4))
- nfscl_loadfsinfo(nmp, &fs);
+ nfscl_loadfsinfo(nmp, &fs, clone_blksize);
nfscl_loadsbinfo(nmp, &sb, sbp);
sbp->f_iosize = newnfs_iosize(nmp);
mtx_unlock(&nmp->nm_mtx);
@@ -408,7 +410,7 @@ ncl_fsinfo(struct nfsmount *nmp, struct vnode *vp, struct ucred *cred,
if (attrflag)
(void) nfscl_loadattrcache(&vp, &nfsva, NULL, 0, 1);
mtx_lock(&nmp->nm_mtx);
- nfscl_loadfsinfo(nmp, &fs);
+ nfscl_loadfsinfo(nmp, &fs, 0);
mtx_unlock(&nmp->nm_mtx);
}
return (error);
diff --git a/sys/fs/nfsclient/nfs_clvnops.c b/sys/fs/nfsclient/nfs_clvnops.c
index fa451887e73e..6824ee6ef13d 100644
--- a/sys/fs/nfsclient/nfs_clvnops.c
+++ b/sys/fs/nfsclient/nfs_clvnops.c
@@ -4027,31 +4027,51 @@ nfs_copy_file_range(struct vop_copy_file_range_args *ap)
struct vattr va, *vap;
struct uio io;
struct nfsmount *nmp;
+ struct nfsnode *np;
size_t len, len2;
ssize_t r;
int error, inattrflag, outattrflag, ret, ret2, invp_lock;
off_t inoff, outoff;
- bool consecutive, must_commit, tryoutcred;
+ bool consecutive, must_commit, onevp, toeof, tryclone, tryoutcred;
+ bool mustclone;
/*
* NFSv4.2 Copy is not permitted for infile == outfile.
+ * The NFSv4.2 Clone operation does work on non-overlapping
+ * byte ranges in the same file, but only if offsets
+ * (and len if not to EOF) are aligned properly.
* TODO: copy_file_range() between multiple NFS mountpoints
+ * --> This is not possible now, since each mount appears to
+ * the NFSv4.n server as a separate client.
*/
- if (invp == outvp || invp->v_mount != outvp->v_mount) {
+ if ((invp == outvp && (ap->a_flags & COPY_FILE_RANGE_CLONE) == 0) ||
+ (invp != outvp && invp->v_mount != outvp->v_mount)) {
generic_copy:
return (ENOSYS);
}
-
- invp_lock = LK_SHARED;
+ if (invp == outvp) {
+ onevp = true;
+ invp_lock = LK_EXCLUSIVE;
+ } else {
+ onevp = false;
+ invp_lock = LK_SHARED;
+ }
+ mustclone = false;
+ if (onevp || (ap->a_flags & COPY_FILE_RANGE_CLONE) != 0)
+ mustclone = true;
relock:
+ inoff = *ap->a_inoffp;
+ outoff = *ap->a_outoffp;
- /* Lock both vnodes, avoiding risk of deadlock. */
+ /* Lock vnode(s), avoiding risk of deadlock. */
do {
mp = NULL;
error = vn_start_write(outvp, &mp, V_WAIT);
if (error == 0) {
error = vn_lock(outvp, LK_EXCLUSIVE);
if (error == 0) {
+ if (onevp)
+ break;
error = vn_lock(invp, invp_lock | LK_NOWAIT);
if (error == 0)
break;
@@ -4071,16 +4091,24 @@ relock:
return (error);
/*
- * More reasons to avoid nfs copy: not NFSv4.2, or explicitly
- * disabled.
+ * More reasons to avoid nfs copy/clone: not NFSv4.2, explicitly
+ * disabled or requires cloning and unable to clone.
+ * Only clone if the clone_blksize attribute is supported
+ * and the clone_blksize is greater than 0.
+ * Alignment of offsets and length will be checked later.
*/
nmp = VFSTONFS(invp->v_mount);
+ np = VTONFS(invp);
mtx_lock(&nmp->nm_mtx);
+ if ((nmp->nm_privflag & NFSMNTP_NOCOPY) != 0)
+ mustclone = true;
if (!NFSHASNFSV4(nmp) || nmp->nm_minorvers < NFSV42_MINORVERSION ||
- (nmp->nm_privflag & NFSMNTP_NOCOPY) != 0) {
+ (mustclone && (!NFSISSET_ATTRBIT(&np->n_vattr.na_suppattr,
+ NFSATTRBIT_CLONEBLKSIZE) || nmp->nm_cloneblksize == 0))) {
mtx_unlock(&nmp->nm_mtx);
VOP_UNLOCK(invp);
- VOP_UNLOCK(outvp);
+ if (!onevp)
+ VOP_UNLOCK(outvp); /* For onevp, same as invp. */
if (mp != NULL)
vn_finished_write(mp);
goto generic_copy;
@@ -4111,6 +4139,8 @@ relock:
invp_obj = invp->v_object;
if (invp_obj != NULL && vm_object_mightbedirty(invp_obj)) {
if (invp_lock != LK_EXCLUSIVE) {
+ KASSERT(!onevp, ("nfs_copy_file_range: "
+ "invp_lock LK_SHARED for onevp"));
invp_lock = LK_EXCLUSIVE;
VOP_UNLOCK(invp);
VOP_UNLOCK(outvp);
@@ -4134,10 +4164,10 @@ relock:
else
consecutive = false;
mtx_unlock(&nmp->nm_mtx);
- inoff = *ap->a_inoffp;
- outoff = *ap->a_outoffp;
tryoutcred = true;
must_commit = false;
+ toeof = false;
+
if (error == 0) {
vap = &VTONFS(invp)->n_vattr.na_vattr;
error = VOP_GETATTR(invp, vap, ap->a_incred);
@@ -4169,29 +4199,63 @@ relock:
if (error == 0 && ret != 0)
error = ret;
}
- } else if (inoff + len > vap->va_size)
+ } else if (inoff + len >= vap->va_size) {
+ toeof = true;
*ap->a_lenp = len = vap->va_size - inoff;
+ }
} else
error = 0;
}
/*
+ * For cloning, the offsets must be clone blksize aligned and
+ * the len must be blksize aligned unless it goes to EOF on
+ * the input file.
+ */
+ tryclone = false;
+ if (len > 0) {
+ if (error == 0 && NFSISSET_ATTRBIT(&np->n_vattr.na_suppattr,
+ NFSATTRBIT_CLONEBLKSIZE) && nmp->nm_cloneblksize != 0 &&
+ (inoff % nmp->nm_cloneblksize) == 0 &&
+ (outoff % nmp->nm_cloneblksize) == 0 &&
+ (toeof || (len % nmp->nm_cloneblksize) == 0))
+ tryclone = true;
+ else if (mustclone)
+ error = ENOSYS;
+ }
+
+ /*
* len will be set to 0 upon a successful Copy RPC.
- * As such, this only loops when the Copy RPC needs to be retried.
+ * As such, this only loops when the Copy/Clone RPC needs to be retried.
*/
while (len > 0 && error == 0) {
inattrflag = outattrflag = 0;
len2 = len;
- if (tryoutcred)
- error = nfsrpc_copy_file_range(invp, ap->a_inoffp,
- outvp, ap->a_outoffp, &len2, ap->a_flags,
- &inattrflag, &innfsva, &outattrflag, &outnfsva,
- ap->a_outcred, consecutive, &must_commit);
- else
- error = nfsrpc_copy_file_range(invp, ap->a_inoffp,
- outvp, ap->a_outoffp, &len2, ap->a_flags,
- &inattrflag, &innfsva, &outattrflag, &outnfsva,
- ap->a_incred, consecutive, &must_commit);
+ if (tryclone) {
+ if (tryoutcred)
+ error = nfsrpc_clone(invp, ap->a_inoffp, outvp,
+ ap->a_outoffp, &len2, toeof, &inattrflag,
+ &innfsva, &outattrflag, &outnfsva,
+ ap->a_outcred);
+ else
+ error = nfsrpc_clone(invp, ap->a_inoffp, outvp,
+ ap->a_outoffp, &len2, toeof, &inattrflag,
+ &innfsva, &outattrflag, &outnfsva,
+ ap->a_incred);
+ } else {
+ if (tryoutcred)
+ error = nfsrpc_copy_file_range(invp,
+ ap->a_inoffp, outvp, ap->a_outoffp, &len2,
+ ap->a_flags, &inattrflag, &innfsva,
+ &outattrflag, &outnfsva,
+ ap->a_outcred, consecutive, &must_commit);
+ else
+ error = nfsrpc_copy_file_range(invp,
+ ap->a_inoffp, outvp, ap->a_outoffp, &len2,
+ ap->a_flags, &inattrflag, &innfsva,
+ &outattrflag, &outnfsva,
+ ap->a_incred, consecutive, &must_commit);
+ }
if (inattrflag != 0)
ret = nfscl_loadattrcache(&invp, &innfsva, NULL, 0, 1);
if (outattrflag != 0)
@@ -4230,6 +4294,13 @@ relock:
/* Try again with incred. */
tryoutcred = false;
error = 0;
+ } else if (tryclone && error != 0) {
+ if (mustclone) {
+ error = ENOSYS;
+ } else {
+ tryclone = false;
+ error = 0;
+ }
}
if (error == NFSERR_STALEWRITEVERF) {
/*
@@ -4243,11 +4314,12 @@ relock:
}
}
VOP_UNLOCK(invp);
- VOP_UNLOCK(outvp);
+ if (!onevp)
+ VOP_UNLOCK(outvp); /* For onevp, same as invp. */
if (mp != NULL)
vn_finished_write(mp);
if (error == NFSERR_NOTSUPP || error == NFSERR_OFFLOADNOREQS ||
- error == NFSERR_ACCES) {
+ error == NFSERR_ACCES || error == ENOSYS) {
/*
* Unlike the NFSv4.2 Copy, vn_generic_copy_file_range() can
* use a_incred for the read and a_outcred for the write, so
@@ -4255,7 +4327,7 @@ relock:
* For NFSERR_NOTSUPP and NFSERR_OFFLOADNOREQS, the Copy can
* never succeed, so disable it.
*/
- if (error != NFSERR_ACCES) {
+ if (error != NFSERR_ACCES && error != ENOSYS) {
/* Can never do Copy on this mount. */
mtx_lock(&nmp->nm_mtx);
nmp->nm_privflag |= NFSMNTP_NOCOPY;
@@ -4596,6 +4668,7 @@ nfs_pathconf(struct vop_pathconf_args *ap)
struct nfsmount *nmp;
struct thread *td = curthread;
off_t off;
+ uint32_t clone_blksize;
bool eof, has_namedattr, named_enabled;
int attrflag, error;
struct nfsnode *np;
@@ -4604,19 +4677,22 @@ nfs_pathconf(struct vop_pathconf_args *ap)
np = VTONFS(vp);
named_enabled = false;
has_namedattr = false;
+ clone_blksize = 0;
if ((NFS_ISV34(vp) && (ap->a_name == _PC_LINK_MAX ||
ap->a_name == _PC_NAME_MAX || ap->a_name == _PC_CHOWN_RESTRICTED ||
ap->a_name == _PC_NO_TRUNC)) ||
(NFS_ISV4(vp) && (ap->a_name == _PC_ACL_NFS4 ||
- ap->a_name == _PC_HAS_NAMEDATTR))) {
+ ap->a_name == _PC_HAS_NAMEDATTR ||
+ ap->a_name == _PC_CLONE_BLKSIZE))) {
/*
* Since only the above 4 a_names are returned by the NFSv3
* Pathconf RPC, there is no point in doing it for others.
* For NFSv4, the Pathconf RPC (actually a Getattr Op.) can
- * be used for _PC_ACL_NFS4 and _PC_HAS_NAMEDATTR as well.
+ * be used for _PC_ACL_NFS4, _PC_HAS_NAMEDATTR and
+ * _PC_CLONE_BLKSIZE as well.
*/
- error = nfsrpc_pathconf(vp, &pc, &has_namedattr, td->td_ucred,
- td, &nfsva, &attrflag);
+ error = nfsrpc_pathconf(vp, &pc, &has_namedattr, &clone_blksize,
+ td->td_ucred, td, &nfsva, &attrflag);
if (attrflag != 0)
(void) nfscl_loadattrcache(&vp, &nfsva, NULL, 0, 1);
if (error != 0)
@@ -4771,6 +4847,9 @@ nfs_pathconf(struct vop_pathconf_args *ap)
else
*ap->a_retval = 0;
break;
+ case _PC_CLONE_BLKSIZE:
+ *ap->a_retval = clone_blksize;
+ break;
default:
error = vop_stdpathconf(ap);
diff --git a/sys/fs/nfsclient/nfsmount.h b/sys/fs/nfsclient/nfsmount.h
index 37b84a015dab..ef876dd30e59 100644
--- a/sys/fs/nfsclient/nfsmount.h
+++ b/sys/fs/nfsclient/nfsmount.h
@@ -87,6 +87,7 @@ struct nfsmount {
/* unclipped, wraps to 0 */
struct __rpc_client *nm_aconn[NFS_MAXNCONN - 1]; /* Additional nconn */
/* Locked via nm_sockreq.nr_mtx */
+ uint32_t nm_cloneblksize; /* Block cloning alignment */
u_int16_t nm_krbnamelen; /* Krb5 host principal, if any */
u_int16_t nm_dirpathlen; /* and mount dirpath, for V4 */
u_int16_t nm_srvkrbnamelen; /* and the server's target name */
diff --git a/sys/fs/nfsserver/nfs_nfsdport.c b/sys/fs/nfsserver/nfs_nfsdport.c
index 8c427c66c156..496cac263fa0 100644
--- a/sys/fs/nfsserver/nfs_nfsdport.c
+++ b/sys/fs/nfsserver/nfs_nfsdport.c
@@ -2113,7 +2113,8 @@ nfsvno_fillattr(struct nfsrv_descript *nd, struct mount *mp, struct vnode *vp,
struct nfsvattr *nvap, fhandle_t *fhp, int rderror, nfsattrbit_t *attrbitp,
struct ucred *cred, struct thread *p, int isdgram, int reterr,
int supports_nfsv4acls, int at_root, uint64_t mounted_on_fileno,
- bool xattrsupp, bool has_hiddensystem, bool has_namedattr)
+ bool xattrsupp, bool has_hiddensystem, bool has_namedattr,
+ uint32_t clone_blksize)
{
struct statfs *sf;
int error;
@@ -2130,9 +2131,11 @@ nfsvno_fillattr(struct nfsrv_descript *nd, struct mount *mp, struct vnode *vp,
sf = NULL;
}
}
+
error = nfsv4_fillattr(nd, mp, vp, NULL, &nvap->na_vattr, fhp, rderror,
attrbitp, cred, p, isdgram, reterr, supports_nfsv4acls, at_root,
- mounted_on_fileno, sf, xattrsupp, has_hiddensystem, has_namedattr);
+ mounted_on_fileno, sf, xattrsupp, has_hiddensystem, has_namedattr,
+ clone_blksize);
free(sf, M_TEMP);
NFSEXITCODE2(0, nd);
return (error);
@@ -2441,7 +2444,7 @@ nfsrvd_readdirplus(struct nfsrv_descript *nd, int isdgram,
struct vnode *vp, struct nfsexstuff *exp)
{
struct dirent *dp;
- u_int32_t *tl;
+ uint32_t clone_blksize, *tl;
int dirlen;
char *cpos, *cend, *rbuf;
struct vnode *nvp;
@@ -2943,6 +2946,7 @@ again:
xattrsupp = false;
has_hiddensystem = false;
has_namedattr = false;
+ clone_blksize = 0;
if (nvp != NULL) {
supports_nfsv4acls =
nfs_supportsnfsv4acls(nvp);
@@ -2966,6 +2970,11 @@ again:
&pathval) != 0)
pathval = 0;
has_namedattr = pathval > 0;
+ pathval = 0;
+ if (VOP_PATHCONF(nvp, _PC_CLONE_BLKSIZE,
+ &pathval) != 0)
+ pathval = 0;
+ clone_blksize = pathval;
NFSVOPUNLOCK(nvp);
} else
supports_nfsv4acls = 0;
@@ -2986,14 +2995,16 @@ again:
nd->nd_cred, p, isdgram, 0,
supports_nfsv4acls, at_root,
mounted_on_fileno, xattrsupp,
- has_hiddensystem, has_namedattr);
+ has_hiddensystem, has_namedattr,
+ clone_blksize);
} else {
dirlen += nfsvno_fillattr(nd, new_mp,
nvp, nvap, &nfh, r, &attrbits,
nd->nd_cred, p, isdgram, 0,
supports_nfsv4acls, at_root,
mounted_on_fileno, xattrsupp,
- has_hiddensystem, has_namedattr);
+ has_hiddensystem, has_namedattr,
+ clone_blksize);
}
if (nvp != NULL)
vrele(nvp);
@@ -5690,7 +5701,8 @@ nfsrv_writedsdorpc(struct nfsmount *nmp, fhandle_t *fhp, off_t off, int len,
if ((nd->nd_flag & (ND_NOMOREDATA | ND_NFSV4 | ND_V4WCCATTR)) ==
(ND_NFSV4 | ND_V4WCCATTR)) {
error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL, NULL,
- NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL);
NFSD_DEBUG(4, "nfsrv_writedsdorpc: wcc attr=%d\n", error);
if (error != 0)
goto nfsmout;
@@ -5721,7 +5733,8 @@ nfsrv_writedsdorpc(struct nfsmount *nmp, fhandle_t *fhp, off_t off, int len,
if (error == 0) {
NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL, NULL,
- NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL);
}
NFSD_DEBUG(4, "nfsrv_writedsdorpc: aft loadattr=%d\n", error);
nfsmout:
@@ -5887,7 +5900,8 @@ nfsrv_allocatedsdorpc(struct nfsmount *nmp, fhandle_t *fhp, off_t off,
if (nd->nd_repstat == 0) {
NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL, NULL,
- NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL);
} else
error = nd->nd_repstat;
NFSD_DEBUG(4, "nfsrv_allocatedsdorpc: aft loadattr=%d\n", error);
@@ -6054,7 +6068,8 @@ nfsrv_deallocatedsdorpc(struct nfsmount *nmp, fhandle_t *fhp, off_t off,
if ((nd->nd_flag & (ND_NOMOREDATA | ND_NFSV4 | ND_V4WCCATTR)) ==
(ND_NFSV4 | ND_V4WCCATTR)) {
error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL, NULL,
- NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL);
NFSD_DEBUG(4, "nfsrv_deallocatedsdorpc: wcc attr=%d\n", error);
if (error != 0)
goto nfsmout;
@@ -6068,7 +6083,8 @@ nfsrv_deallocatedsdorpc(struct nfsmount *nmp, fhandle_t *fhp, off_t off,
if (nd->nd_repstat == 0) {
NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL, NULL,
- NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL);
} else
error = nd->nd_repstat;
NFSD_DEBUG(4, "nfsrv_deallocatedsdorpc: aft loadattr=%d\n", error);
@@ -6216,7 +6232,8 @@ nfsrv_setattrdsdorpc(fhandle_t *fhp, struct ucred *cred, NFSPROC_T *p,
if ((nd->nd_flag & (ND_NOMOREDATA | ND_NFSV4 | ND_V4WCCATTR)) ==
(ND_NFSV4 | ND_V4WCCATTR)) {
error = nfsv4_loadattr(nd, NULL, dsnap, NULL, NULL, 0, NULL,
- NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL);
NFSD_DEBUG(4, "nfsrv_setattrdsdorpc: wcc attr=%d\n", error);
if (error != 0)
goto nfsmout;
@@ -6241,7 +6258,7 @@ nfsrv_setattrdsdorpc(fhandle_t *fhp, struct ucred *cred, NFSPROC_T *p,
NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
error = nfsv4_loadattr(nd, NULL, dsnap, NULL, NULL, 0, NULL,
NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL,
- NULL);
+ NULL, NULL);
}
NFSD_DEBUG(4, "nfsrv_setattrdsdorpc: aft setattr loadattr=%d\n", error);
nfsmout:
@@ -6386,7 +6403,7 @@ nfsrv_setacldsdorpc(fhandle_t *fhp, struct ucred *cred, NFSPROC_T *p,
* the same type (VREG).
*/
nfsv4_fillattr(nd, NULL, vp, aclp, NULL, NULL, 0, &attrbits, NULL,
- NULL, 0, 0, 0, 0, 0, NULL, false, false, false);
+ NULL, 0, 0, 0, 0, 0, NULL, false, false, false, 0);
error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
if (error != 0) {
@@ -6530,7 +6547,7 @@ nfsrv_getattrdsrpc(fhandle_t *fhp, struct ucred *cred, NFSPROC_T *p,
if (nd->nd_repstat == 0) {
error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0,
NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL,
- NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL);
/*
* We can only save the updated values in the extended
* attribute if the vp is exclusively locked.
diff --git a/sys/fs/nfsserver/nfs_nfsdserv.c b/sys/fs/nfsserver/nfs_nfsdserv.c
index 9eebcda548c6..a881968b03be 100644
--- a/sys/fs/nfsserver/nfs_nfsdserv.c
+++ b/sys/fs/nfsserver/nfs_nfsdserv.c
@@ -253,6 +253,7 @@ nfsrvd_getattr(struct nfsrv_descript *nd, int isdgram,
size_t atsiz;
long pathval;
bool has_hiddensystem, has_namedattr, xattrsupp;
+ uint32_t clone_blksize;
if (nd->nd_repstat)
goto out;
@@ -330,6 +331,11 @@ nfsrvd_getattr(struct nfsrv_descript *nd, int isdgram,
&pathval) != 0)
pathval = 0;
has_namedattr = pathval > 0;
+ pathval = 0;
+ if (VOP_PATHCONF(vp, _PC_CLONE_BLKSIZE,
+ &pathval) != 0)
+ pathval = 0;
+ clone_blksize = pathval;
mp = vp->v_mount;
if (nfsrv_enable_crossmntpt != 0 &&
vp->v_type == VDIR &&
@@ -365,7 +371,7 @@ nfsrvd_getattr(struct nfsrv_descript *nd, int isdgram,
isdgram, 1, supports_nfsv4acls,
at_root, mounted_on_fileno,
xattrsupp, has_hiddensystem,
- has_namedattr);
+ has_namedattr, clone_blksize);
vfs_unbusy(mp);
}
vrele(vp);
@@ -4347,7 +4353,7 @@ nfsrvd_verify(struct nfsrv_descript *nd, int isdgram,
if (!nd->nd_repstat) {
nfsvno_getfs(&fs, isdgram);
error = nfsv4_loadattr(nd, vp, &nva, NULL, &fh, fhsize, NULL,
- sf, NULL, &fs, NULL, 1, &ret, NULL, NULL, NULL, p,
+ sf, NULL, &fs, NULL, 1, &ret, NULL, NULL, NULL, NULL, p,
nd->nd_cred);
if (!error) {
if (nd->nd_procnum == NFSV4OP_NVERIFY) {
@@ -6011,6 +6017,212 @@ nfsmout:
}
/*
+ * nfs clone service
+ */
+int
+nfsrvd_clone(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t vp, vnode_t tovp, struct nfsexstuff *exp, struct nfsexstuff *toexp)
+{
+ uint32_t *tl;
+ struct nfsvattr at;
+ int error = 0, ret;
+ off_t inoff, outoff;
+ uint64_t len;
+ size_t xfer;
+ struct nfsstate inst, outst, *instp = &inst, *outstp = &outst;
+ struct nfslock inlo, outlo, *inlop = &inlo, *outlop = &outlo;
+ nfsquad_t clientid;
+ nfsv4stateid_t stateid;
+ nfsattrbit_t attrbits;
+ void *rl_rcookie, *rl_wcookie;
+ long pathval;
+
+ rl_rcookie = rl_wcookie = NULL;
+ pathval = 0;
+ if (nfsrv_maxcopyrange == 0 || nfsrv_devidcnt > 0 ||
+ VOP_PATHCONF(vp, _PC_CLONE_BLKSIZE, &pathval) != 0 ||
+ pathval == 0) {
+ /*
+ * For a pNFS server, reply NFSERR_NOTSUPP so that the client
+ * will not do the clone and will do I/O on the DS(s).
+ * If vfs.nfsd.maxcopyrange set to 0, disable Clone.
+ */
+ nd->nd_repstat = NFSERR_NOTSUPP;
+ goto nfsmout;
+ }
+ NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_STATEID + 3 * NFSX_HYPER);
+ instp->ls_flags = (NFSLCK_CHECK | NFSLCK_READACCESS);
+ inlop->lo_flags = NFSLCK_READ;
+ instp->ls_ownerlen = 0;
+ instp->ls_op = NULL;
+ instp->ls_uid = nd->nd_cred->cr_uid;
+ instp->ls_stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
+ clientid.lval[0] = instp->ls_stateid.other[0] = *tl++;
+ clientid.lval[1] = instp->ls_stateid.other[1] = *tl++;
+ if ((nd->nd_flag & ND_IMPLIEDCLID) != 0)
+ clientid.qval = nd->nd_clientid.qval;
+ instp->ls_stateid.other[2] = *tl++;
+ outstp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
+ outlop->lo_flags = NFSLCK_WRITE;
+ outstp->ls_ownerlen = 0;
+ outstp->ls_op = NULL;
+ outstp->ls_uid = nd->nd_cred->cr_uid;
+ outstp->ls_stateid.seqid = fxdr_unsigned(uint32_t, *tl++);
+ outstp->ls_stateid.other[0] = *tl++;
+ outstp->ls_stateid.other[1] = *tl++;
+ outstp->ls_stateid.other[2] = *tl++;
+ inoff = fxdr_hyper(tl); tl += 2;
+ inlop->lo_first = inoff;
+ outoff = fxdr_hyper(tl); tl += 2;
+ outlop->lo_first = outoff;
+ len = fxdr_hyper(tl);
+ if (len == 0) {
+ /* len == 0 means to EOF. */
+ inlop->lo_end = OFF_MAX;
+ outlop->lo_end = OFF_MAX;
+ } else {
+ inlop->lo_end = inlop->lo_first + len;
+ outlop->lo_end = outlop->lo_first + len;
+ }
+
+ if ((inoff > OFF_MAX || outoff > OFF_MAX ||
+ inlop->lo_end > OFF_MAX || outlop->lo_end > OFF_MAX ||
+ inlop->lo_end < inlop->lo_first || outlop->lo_end <
+ outlop->lo_first))
+ nd->nd_repstat = NFSERR_INVAL;
+
+ if (nd->nd_repstat == 0 && vp->v_type != VREG)
+ nd->nd_repstat = NFSERR_WRONGTYPE;
+
+ /* Check permissions for the input file. */
+ NFSZERO_ATTRBIT(&attrbits);
+ NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
+ ret = nfsvno_getattr(vp, &at, nd, curthread, 1, &attrbits);
+ if (nd->nd_repstat == 0)
+ nd->nd_repstat = ret;
+ if (nd->nd_repstat == 0 && (at.na_uid != nd->nd_cred->cr_uid ||
+ NFSVNO_EXSTRICTACCESS(exp)))
+ nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred, exp,
+ curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
+ NULL);
+ if (nd->nd_repstat == 0)
+ nd->nd_repstat = nfsrv_lockctrl(vp, &instp, &inlop, NULL,
+ clientid, &stateid, exp, nd, curthread);
+ if (vp != tovp) {
+ NFSVOPUNLOCK(vp);
+ if (nd->nd_repstat != 0)
+ goto out;
+
+ error = NFSVOPLOCK(tovp, LK_SHARED);
+ if (error != 0)
+ goto out;
+ pathval = 0;
+ if (VOP_PATHCONF(tovp, _PC_CLONE_BLKSIZE, &pathval) != 0 ||
+ pathval == 0)
+ nd->nd_repstat = NFSERR_NOTSUPP;
+ else if (tovp->v_type != VREG)
+ nd->nd_repstat = NFSERR_WRONGTYPE;
+ }
+
+ /* For the output file, we only need the Owner attribute. */
+ ret = nfsvno_getattr(tovp, &at, nd, curthread, 1, &attrbits);
+ if (nd->nd_repstat == 0)
+ nd->nd_repstat = ret;
+ if (nd->nd_repstat == 0 && (at.na_uid != nd->nd_cred->cr_uid ||
+ NFSVNO_EXSTRICTACCESS(exp)))
+ nd->nd_repstat = nfsvno_accchk(tovp, VWRITE, nd->nd_cred, toexp,
+ curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED,
+ NULL);
+ if (nd->nd_repstat == 0)
+ nd->nd_repstat = nfsrv_lockctrl(tovp, &outstp, &outlop, NULL,
+ clientid, &stateid, toexp, nd, curthread);
+ NFSVOPUNLOCK(tovp);
+
+ /* Range lock the byte ranges for both invp and outvp. */
+ if (nd->nd_repstat == 0) {
+ for (;;) {
+ if (len == 0)
+ rl_wcookie = vn_rangelock_wlock(tovp, outoff,
+ OFF_MAX);
+ else
+ rl_wcookie = vn_rangelock_wlock(tovp, outoff,
+ outoff + len);
+ if (vp != tovp) {
+ if (len == 0)
+ rl_rcookie = vn_rangelock_tryrlock(vp,
+ inoff, OFF_MAX);
+ else
+ rl_rcookie = vn_rangelock_tryrlock(vp,
+ inoff, inoff + len);
+ if (rl_rcookie != NULL)
+ break;
+ } else {
+ rl_rcookie = NULL;
+ break;
+ }
+ vn_rangelock_unlock(tovp, rl_wcookie);
+ if (len == 0)
+ rl_rcookie = vn_rangelock_rlock(vp, inoff,
+ OFF_MAX);
+ else
+ rl_rcookie = vn_rangelock_rlock(vp, inoff,
+ inoff + len);
+ vn_rangelock_unlock(vp, rl_rcookie);
+ }
+
+ error = NFSVOPLOCK(vp, LK_SHARED);
+ if (error == 0) {
+ ret = nfsvno_getattr(vp, &at, nd, curthread, 1, NULL);
+ if (ret == 0) {
+ /*
+ * Since invp is range locked, na_size should
+ * not change.
+ */
+ if (len == 0 && at.na_size > inoff)
+ len = SSIZE_MAX; /* To EOF. */
+ else if (inoff + len > at.na_size)
+ nd->nd_repstat = NFSERR_INVAL;
+ }
+ NFSVOPUNLOCK(vp);
+ if (ret != 0 && nd->nd_repstat == 0)
+ nd->nd_repstat = ret;
+ } else if (nd->nd_repstat == 0)
+ nd->nd_repstat = error;
+ }
+
+ /*
+ * Do the actual copy to an upper limit of vfs.nfsd.maxcopyrange.
+ * This size limit can be set to limit the time a copy RPC will
+ * take.
+ */
+ xfer = len;
+ if (nd->nd_repstat == 0) {
+ nd->nd_repstat = vn_copy_file_range(vp, &inoff, tovp, &outoff,
+ &xfer, COPY_FILE_RANGE_CLONE, nd->nd_cred, nd->nd_cred,
+ NULL);
+ if (nd->nd_repstat == ENOSYS)
+ nd->nd_repstat = NFSERR_INVAL;
+ }
+
+ /* Unlock the ranges. */
+ if (rl_rcookie != NULL)
+ vn_rangelock_unlock(vp, rl_rcookie);
+ if (rl_wcookie != NULL)
+ vn_rangelock_unlock(tovp, rl_wcookie);
+
+out:
+ vrele(vp);
+ vrele(tovp);
+ NFSEXITCODE2(error, nd);
+ return (error);
+nfsmout:
+ vput(vp);
+ vrele(tovp);
+ NFSEXITCODE2(error, nd);
+ return (error);
+}
+
+/*
* nfs seek service
*/
int
diff --git a/sys/fs/nfsserver/nfs_nfsdsocket.c b/sys/fs/nfsserver/nfs_nfsdsocket.c
index d6832b4f74be..636c4735a131 100644
--- a/sys/fs/nfsserver/nfs_nfsdsocket.c
+++ b/sys/fs/nfsserver/nfs_nfsdsocket.c
@@ -371,7 +371,7 @@ int (*nfsrv4_ops2[NFSV42_NOPS])(struct nfsrv_descript *,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
- (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
+ nfsrvd_clone,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
(int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0,
diff --git a/sys/fs/nfsserver/nfs_nfsdstate.c b/sys/fs/nfsserver/nfs_nfsdstate.c
index 2e27817389dd..36e3ce7b7142 100644
--- a/sys/fs/nfsserver/nfs_nfsdstate.c
+++ b/sys/fs/nfsserver/nfs_nfsdstate.c
@@ -4675,7 +4675,7 @@ errout:
} else if (error == 0 && procnum == NFSV4OP_CBGETATTR)
error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0,
NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL,
- NULL, p, NULL);
+ NULL, NULL, p, NULL);
m_freem(nd->nd_mrep);
}
NFSLOCKSTATE();