From: Jeff Layton <jlayton@kernel.org>
To: Calum Mackay <calum.mackay@oracle.com>
Cc: Chuck Lever <cel@kernel.org>, NeilBrown <neil@brown.name>,
Olga Kornievskaia <okorniev@redhat.com>,
Dai Ngo <Dai.Ngo@oracle.com>, Tom Talpey <tom@talpey.com>,
linux-nfs@vger.kernel.org, Jeff Layton <jlayton@kernel.org>
Subject: [PATCH pynfs 11/13] server41tests: test COPY_NOTIFY
Date: Thu, 09 Jul 2026 15:02:39 -0400 [thread overview]
Message-ID: <20260709-copy-v1-11-849bf581d7cb@kernel.org> (raw)
In-Reply-To: <20260709-copy-v1-0-849bf581d7cb@kernel.org>
Add tests exercising the source-server half of NFSv4.2 inter-server copy
against a single server. A client requesting an inter-server copy first
sends COPY_NOTIFY to the source server to authorize the destination and
obtain a source stateid and netloc list.
CPNOTIFY1 issues a basic COPY_NOTIFY and checks that a stateid and a
non-empty source_server list are returned; CPNOTIFY2 uses a bad source
stateid (expecting NFS4ERR_BAD_STATEID); CPNOTIFY3 omits the current
filehandle (expecting NFS4ERR_NOFILEHANDLE). All three treat
NFS4ERR_NOTSUPP as unsupported so they are portable.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
nfs4.1/server41tests/st_copy.py | 115 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 114 insertions(+), 1 deletion(-)
diff --git a/nfs4.1/server41tests/st_copy.py b/nfs4.1/server41tests/st_copy.py
index 1a392f9377b8..7eb6e2c98c4d 100644
--- a/nfs4.1/server41tests/st_copy.py
+++ b/nfs4.1/server41tests/st_copy.py
@@ -1,3 +1,4 @@
+import socket
import time
from .st_create_session import create_session
@@ -7,7 +8,7 @@ from .environment import check, fail, create_file, open_file, close_file
from .environment import open_create_file_op, use_obj, write_file, read_file
from xdrdef.nfs4_type import open_owner4, openflag4, createhow4, open_claim4
from xdrdef.nfs4_type import creatverfattr, fattr4, stateid4, locker4, lock_owner4
-from xdrdef.nfs4_type import open_to_lock_owner4
+from xdrdef.nfs4_type import open_to_lock_owner4, netloc4, netaddr4
import nfs_ops
op = nfs_ops.NFS4ops()
@@ -43,6 +44,23 @@ def _bad_stateid():
# all-one READ-bypass special stateids) the server cannot recognize.
return stateid4(1, b'\xde\xad\xbe\xef' * 3)
+def _server_netloc(env):
+ """netloc4 (NL4_NETADDR) naming the server under test.
+
+ The Linux server only accepts the NL4_NETADDR form of netloc4 and
+ rejects NL4_NAME / NL4_URL at decode time with NFS4ERR_BADXDR, so build
+ a universal address (RFC 1833: h.h.h.h.p1.p2) from the server address
+ and port. For single-server COPY_NOTIFY tests this names the server
+ itself; we only need a netloc4 the source server will accept and record.
+ """
+ host, port = env.opts.server, env.opts.port
+ family, _, _, _, sockaddr = socket.getaddrinfo(
+ host, port, type=socket.SOCK_STREAM)[0]
+ ip = sockaddr[0]
+ uaddr = "%s.%d.%d" % (ip, (port >> 8) & 0xff, port & 0xff)
+ netid = b'tcp6' if family == socket.AF_INET6 else b'tcp'
+ return netloc4(NL4_NETADDR, nl_addr=netaddr4(netid, uaddr.encode('ascii')))
+
def _write_data(sess, fh, stateid, data, offset=0):
"""Write data in chunks bounded by the session's max request size."""
chunk = sess.fore_channel.maxrequestsize - 1024
@@ -323,3 +341,98 @@ def testCopyToSameFile(t, env):
check(res)
if res.data != data:
fail("Same-file copy: data at offset 8192 does not match source")
+
+def testCopyNotify(t, env):
+ """COPY_NOTIFY authorizes a destination server to copy from the source
+
+ A client wanting an inter-server copy first sends COPY_NOTIFY to the
+ source server (CURRENT_FH = source file) naming the destination server.
+ The source returns a stateid and a list of netlocs the destination
+ should use to reach it; the client then hands these to the destination
+ server's COPY. This exercises the source-server half against a single
+ server.
+
+ FLAGS: copy
+ CODE: CPNOTIFY1
+ VERS: 2-
+ """
+ sess = env.c1.new_client_session(env.testname(t))
+ src_fh, src_stateid = _create_and_open(sess, env.testname(t))
+ _write_data(sess, src_fh, src_stateid, b"copy notify test data")
+
+ ops = [op.putfh(src_fh),
+ op.copy_notify(src_stateid, _server_netloc(env))]
+ res = sess.compound(ops)
+ check(res, [NFS4_OK, NFS4ERR_NOTSUPP], msg="COPY_NOTIFY")
+ if res.status == NFS4ERR_NOTSUPP:
+ t.fail_support("Server does not support COPY_NOTIFY "
+ "(inter-server copy source)")
+
+ cnr = res.resarray[-1]
+ # The client needs a source stateid to give to the destination's COPY.
+ if cnr.cnr_stateid is None:
+ fail("COPY_NOTIFY did not return a source stateid")
+ # And at least one netloc telling the destination how to reach the source.
+ if not cnr.cnr_source_server:
+ fail("COPY_NOTIFY returned an empty cnr_source_server list")
+
+def testCopyNotifyBadStateid(t, env):
+ """COPY_NOTIFY with an invalid source stateid should fail
+
+ FLAGS: copy
+ CODE: CPNOTIFY2
+ VERS: 2-
+ """
+ sess = env.c1.new_client_session(env.testname(t))
+ src_fh, _src_stateid = _create_and_open(sess, env.testname(t))
+
+ ops = [op.putfh(src_fh),
+ op.copy_notify(_bad_stateid(), _server_netloc(env))]
+ res = sess.compound(ops)
+ if res.status == NFS4ERR_NOTSUPP:
+ t.fail_support("Server does not support COPY_NOTIFY "
+ "(inter-server copy source)")
+ check(res, NFS4ERR_BAD_STATEID, msg="COPY_NOTIFY with bad source stateid")
+
+def testCopyNotifyUnsupportedNetloc(t, env):
+ """COPY_NOTIFY with a name or URL netloc must not return NFS4ERR_BADXDR
+
+ NL4_NAME and NL4_URL are well-formed XDR, so a server that does not
+ support them must reject the operation with NFS4ERR_NOTSUPP, not
+ NFS4ERR_BADXDR (which is reserved for XDR that cannot be decoded). A
+ server that does support them may return NFS4_OK.
+
+ FLAGS: copy
+ CODE: CPNOTIFY4
+ VERS: 2-
+ """
+ sess = env.c1.new_client_session(env.testname(t))
+ src_fh, src_stateid = _create_and_open(sess, env.testname(t))
+
+ for name, nl in [("NL4_NAME", netloc4(NL4_NAME, nl_name=b"nfs.example.org")),
+ ("NL4_URL", netloc4(NL4_URL,
+ nl_url=b"nfs://nfs.example.org/"))]:
+ res = sess.compound([op.putfh(src_fh),
+ op.copy_notify(src_stateid, nl)])
+ if res.status == NFS4ERR_BADXDR:
+ fail("COPY_NOTIFY with a %s netloc returned NFS4ERR_BADXDR; a "
+ "well-formed but unsupported netloc should return "
+ "NFS4ERR_NOTSUPP" % name)
+ check(res, [NFS4_OK, NFS4ERR_NOTSUPP],
+ msg="COPY_NOTIFY with a %s netloc" % name)
+
+def testCopyNotifyNoFh(t, env):
+ """COPY_NOTIFY without a current filehandle should fail
+
+ FLAGS: copy
+ CODE: CPNOTIFY3
+ VERS: 2-
+ """
+ sess = env.c1.new_client_session(env.testname(t))
+
+ ops = [op.copy_notify(env.stateid0, _server_netloc(env))]
+ res = sess.compound(ops)
+ if res.status == NFS4ERR_NOTSUPP:
+ t.fail_support("Server does not support COPY_NOTIFY "
+ "(inter-server copy source)")
+ check(res, NFS4ERR_NOFILEHANDLE, msg="COPY_NOTIFY with no filehandle")
--
2.55.0
next prev parent reply other threads:[~2026-07-09 19:02 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-09 19:02 [PATCH pynfs 00/13] server41tests: add some tests for copy offload Jeff Layton
2026-07-09 19:02 ` [PATCH pynfs 01/13] server41tests: add helpers and basic synchronous COPY test Jeff Layton
2026-07-09 19:02 ` [PATCH pynfs 02/13] server41tests: test COPY with non-zero offsets Jeff Layton
2026-07-09 19:02 ` [PATCH pynfs 03/13] server41tests: test async COPY with OFFLOAD_STATUS polling Jeff Layton
2026-07-09 19:02 ` [PATCH pynfs 04/13] server41tests: test OFFLOAD_STATUS persists after async copy completes Jeff Layton
2026-07-09 19:02 ` [PATCH pynfs 05/13] server41tests: test OFFLOAD_CANCEL on async copy Jeff Layton
2026-07-09 19:02 ` [PATCH pynfs 06/13] server41tests: test COPY with bad source stateid Jeff Layton
2026-07-09 19:02 ` [PATCH pynfs 07/13] server41tests: test COPY with bad destination stateid Jeff Layton
2026-07-09 19:02 ` [PATCH pynfs 08/13] server41tests: test OFFLOAD_STATUS with fabricated stateid Jeff Layton
2026-07-09 19:02 ` [PATCH pynfs 09/13] server41tests: test COPY within same file Jeff Layton
2026-07-09 19:02 ` [PATCH pynfs 10/13] nfs4.1: fix COPY_NOTIFY args union arm name in XDR Jeff Layton
2026-07-09 19:02 ` Jeff Layton [this message]
2026-07-09 19:02 ` [PATCH pynfs 12/13] server41tests: support a second server for inter-server copy Jeff Layton
2026-07-09 19:02 ` [PATCH pynfs 13/13] server41tests: test inter-server COPY Jeff Layton
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260709-copy-v1-11-849bf581d7cb@kernel.org \
--to=jlayton@kernel.org \
--cc=Dai.Ngo@oracle.com \
--cc=calum.mackay@oracle.com \
--cc=cel@kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=neil@brown.name \
--cc=okorniev@redhat.com \
--cc=tom@talpey.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.