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 02/13] server41tests: test COPY with non-zero offsets
Date: Thu, 09 Jul 2026 15:02:30 -0400 [thread overview]
Message-ID: <20260709-copy-v1-2-849bf581d7cb@kernel.org> (raw)
In-Reply-To: <20260709-copy-v1-0-849bf581d7cb@kernel.org>
Add testCopyWithOffset (COPY2) which copies 4KB from source offset
1024 to destination offset 512, then reads back the destination to
verify the data landed at the correct position.
Signed-off-by: Jeff Layton <jlayton@kernel.org>
---
CLAUDE.md | 100 +++++++++++++++++++++
...ts-add-a-test-for-rename-within-a-dir-wit.patch | 55 ++++++++++++
nfs4.1/server41tests/st_copy.py | 25 ++++++
3 files changed, 180 insertions(+)
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 000000000000..91a499884990
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,100 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## What is pynfs?
+
+pynfs is an NFS protocol conformance testing framework written in Python. It tests NFSv4.0 and NFSv4.1/4.2 servers by sending crafted NFS COMPOUND operations over RPC and verifying responses. Tests require a live NFS server with an exported filesystem.
+
+## Build
+
+```bash
+# Install dependencies (Fedora)
+yum install krb5-devel python3-devel swig python3-gssapi python3-ply
+pip install xdrlib3
+
+# Build (generates XDR code from .x files -- must be done before running tests)
+./setup.py build
+```
+
+Build order matters: xdr → rpc → nfs4.1 → nfs4.0. The top-level setup.py handles this.
+
+## Running Tests
+
+Tests use a **custom test framework** (not pytest). They require a live NFS server.
+
+```bash
+cd nfs4.1
+
+# First run: create test directory tree on server
+./testserver.py SERVER:/export --maketree -v all
+
+# Run all tests
+./testserver.py SERVER:/export -v all
+
+# Run a single test by code
+./testserver.py SERVER:/export OPEN1
+
+# Run a flag group
+./testserver.py SERVER:/export dirdeleg
+
+# Exclude tests: prefix with "no"
+./testserver.py SERVER:/export all nodirdeleg
+./testserver.py SERVER:/export dirdeleg noDIRDELEG3
+
+# Run with dependencies auto-resolved
+./testserver.py SERVER:/export --rundeps DIRDELEG5
+
+# Skip initial tree cleanup (faster for tests that don't need it)
+./testserver.py SERVER:/export --noinit EXID1
+```
+
+NFSv4.0 tests work the same way from the `nfs4.0/` directory.
+
+## Architecture
+
+- **`xdr/`** — XDR parser/code generator (`xdrgen.py` uses PLY). Generates `*_const.py`, `*_type.py`, `*_pack.py` from `.x` definition files. These generated files are gitignored.
+- **`rpc/`** — Shared RPC client library with AUTH_SYS and RPCSEC_GSS support.
+- **`nfs4.1/`** — NFSv4.1/4.2 test suite and utilities. Active development happens here.
+- **`nfs4.0/`** — NFSv4.0 test suite. Older codebase with its own copy of rpc/testmod in `lib/`.
+- **`nfs4.1/testmod.py`** — The custom test framework engine (discovery, dependencies, execution).
+- **`nfs4.1/nfs4client.py`** — NFS4 client implementation built on RPC.
+- **`nfs4.1/nfs_ops.py`** — Operation factory (`op.open()`, `op.putfh()`, `op.rename()`, etc.).
+
+## Writing Tests (NFSv4.1)
+
+Tests live in `nfs4.1/server41tests/st_*.py`. Each test is a function:
+
+```python
+def testMyFeature(t, env):
+ """Describe what this test verifies
+
+ FLAGS: myfeature all
+ CODE: MYFEAT1
+ DEPEND: MYFEAT0
+ """
+ sess = env.c1.new_client_session(env.testname(t))
+ res = sess.compound([op.putrootfh()])
+ check(res)
+```
+
+Key conventions:
+- Function name starts with `test`, takes `(t, env)`.
+- **CODE** (required): unique identifier like `OPEN1`, `DIRDELEG5`.
+- **FLAGS**: space-separated group tags. Include `all` for general tests.
+- **DEPEND**: test codes that must pass first.
+- **VERS**: minor version range (e.g., `1-2`).
+- `check(res, NFS4_OK)` is the primary assertion — compares compound result status.
+- `t.fail("msg")` signals failure; `t.pass_warn("msg")` for warnings; `t.fail_support("msg")` for unsupported features.
+- New test modules must be added to `server41tests/__init__.py`'s `__all__` list.
+- Environment helpers in `server41tests/environment.py`: `create_file()`, `open_file()`, `close_file()`, `clean_dir()`, `do_readdir()`, etc.
+
+## Contributing
+
+Patches go to linux-nfs@vger.kernel.org via `git format-patch` / `git send-email`. Commits should be signed off (`git commit -s`). Follow Linux kernel patch submission conventions.
+
+## Notes
+
+- The NFS server under test must allow high-port connections (`insecure` export option on Linux).
+- `use_local.py` in each package manipulates `sys.path` so tests can run directly from the source tree without installation.
+- Test results are not authoritative protocol statements — consult the RFCs if a server fails a test.
diff --git a/nfs4.1/0001-server41tests-add-a-test-for-rename-within-a-dir-wit.patch b/nfs4.1/0001-server41tests-add-a-test-for-rename-within-a-dir-wit.patch
new file mode 100644
index 000000000000..7e855592da85
--- /dev/null
+++ b/nfs4.1/0001-server41tests-add-a-test-for-rename-within-a-dir-wit.patch
@@ -0,0 +1,55 @@
+From 0ce942390b36e00547ea9ea56afdc449a9a50699 Mon Sep 17 00:00:00 2001
+From: Jeff Layton <jlayton@kernel.org>
+Date: Wed, 28 May 2025 12:10:22 -0400
+Subject: [PATCH] server41tests: add a test for rename within a dir with a
+ delegation
+
+Signed-off-by: Jeff Layton <jlayton@kernel.org>
+---
+ nfs4.1/server41tests/st_dir_deleg.py | 33 ++++++++++++++++++++++++++++
+ 1 file changed, 33 insertions(+)
+
+diff --git a/nfs4.1/server41tests/st_dir_deleg.py b/nfs4.1/server41tests/st_dir_deleg.py
+index 1fbeb6e0efb9..505e46680197 100644
+--- a/nfs4.1/server41tests/st_dir_deleg.py
++++ b/nfs4.1/server41tests/st_dir_deleg.py
+@@ -102,3 +102,36 @@ def testDirDelegRemove(t, env):
+
+ if (not completed):
+ fail("I didn't receive a CB_NOTIFY from the server!")
++
++def testDirDelegRename(t, env):
++ """Create a dir_deleg that accepts notification of RENAME events
++
++ FLAGS: dirdeleg all
++ CODE: DIRDELEG2
++ """
++ c = env.c1
++ recall = threading.Event()
++ notify = threading.Event()
++ sess1, fh, deleg = _getDirDeleg(t, env, [NOTIFY4_RENAME_ENTRY], recall, notify)
++
++ claim = open_claim4(CLAIM_NULL, env.testname(t))
++ owner = open_owner4(0, b"owner")
++ how = openflag4(OPEN4_CREATE, createhow4(GUARDED4, {FATTR4_SIZE:0}))
++ open_op = [ op.putfh(fh), op.open(0, OPEN4_SHARE_ACCESS_WRITE,
++ OPEN4_SHARE_DENY_NONE, owner, how, claim) ]
++ res = sess1.compound(open_op)
++ check(res)
++
++ sess2 = c.new_client_session(b"%s_2" % env.testname(t))
++ topdir = c.homedir + [t.code.encode('utf8')]
++ oldpath = b"%s/%s" % (topdir, env.testname(t))
++ newpath = b"%s_2" % oldpath)
++ res = rename_obj(sess2, oldpath, newpath)
++ check(res)
++
++ completed = notify.wait(5)
++ ops = [ op.putfh(fh), op.delegreturn(deleg) ]
++ res = sess1.compound(ops)
++
++ if (not completed):
++ fail("I didn't receive a CB_NOTIFY from the server!")
+--
+2.49.0
+
diff --git a/nfs4.1/server41tests/st_copy.py b/nfs4.1/server41tests/st_copy.py
index c7e48adf6fbe..bfc64bbe1584 100644
--- a/nfs4.1/server41tests/st_copy.py
+++ b/nfs4.1/server41tests/st_copy.py
@@ -97,6 +97,31 @@ def testSyncCopy(t, env):
_verify_data(sess, dst_fh, dst_stateid, data)
+def testCopyWithOffset(t, env):
+ """copy with non-zero source and destination offsets
+
+ FLAGS: copy
+ CODE: COPY2
+ """
+ sess = env.c1.new_client_session(env.testname(t))
+ src_fh, src_stateid = _create_and_open(sess, env.testname(t))
+ data = b"\x00" * 1024 + b"B" * 4096 + b"\x00" * 1024
+ _write_data(sess, src_fh, src_stateid, data)
+
+ dst_fh, dst_stateid = _create_and_open(sess, env.testname(t) + b"_dst")
+
+ res = _do_copy(sess, src_fh, src_stateid, dst_fh, dst_stateid,
+ src_offset=1024, dst_offset=512, count=4096, synchronous=1)
+ check(res)
+ cr = res.resarray[-1]
+ if cr.cr_response.wr_count != 4096:
+ fail("Expected to copy 4096 bytes, got %d" % cr.cr_response.wr_count)
+
+ res = read_file(sess, dst_fh, 512, 4096, dst_stateid)
+ check(res)
+ if res.data != b"B" * 4096:
+ fail("Destination data at offset 512 does not match expected content")
+
def testZeroLengthCopy(t, env):
"""test that zero-length copy copies to EOF
--
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 ` Jeff Layton [this message]
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 ` [PATCH pynfs 11/13] server41tests: test COPY_NOTIFY Jeff Layton
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-2-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.