Linux NFS development
 help / color / mirror / Atom feed
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


  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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox