Hi @mecsbecs, thank you for the feedback.
NFSv4
Since my previous update I have focused on refactoring & testing the NFSv4 support. The changes can be seen here: Commits · rrauch/nfsserve · GitHub
There is now a cleaner separation between v4.0 & v4.1, several missing commands have been added to increase compatibility (mainly for macOS) & more unit tests were added here: nfsserve/src/nfs4/state.rs at 8c7c3da49641ae99cebaac6969682426160361cb · rrauch/nfsserve · GitHub
Here is the result of running the unit tests:
running 28 tests
test nfs4::state::tests::bad_seqid_preserves_the_open_owner ... ok
test nfs4::state::tests::close_of_an_unknown_stateid_records_nothing ... ok
test nfs4::state::tests::client_owns_its_state ... ok
test nfs4::state::tests::close_tombstone_is_one_per_open_owner ... ok
test nfs4::state::tests::expiry_purges_everything_the_client_owned ... ok
test nfs4::state::tests::failed_op_does_not_desync_the_owner_seqid ... ok
test nfs4::state::tests::failed_op_still_consumes_the_owner_seqid ... ok
test nfs4::state::tests::open_confirm_marks_the_owner_and_replays ... ok
test nfs4::state::tests::open_confirm_rejects_a_stale_stateid_on_a_fresh_seqid ... ok
test nfs4::state::tests::open_downgrade_keeps_the_owner_seqid_level ... ok
test nfs4::state::tests::owner_seqid_detects_retransmits ... ok
test nfs4::state::tests::open_downgrade_retransmit_replays ... ok
test nfs4::state::tests::rejected_lock_books_the_open_owner_seqid ... ok
test nfs4::state::tests::rejected_owner_op_replays ... ok
test nfs4::state::tests::resolve_renews_the_lease ... ok
test nfs4::state::tests::repeat_open_upgrades_one_state ... ok
test nfs4::state::tests::setclientid_confirm_rejects_a_wrong_verifier_without_touching_state ... ok
test nfs4::state::tests::retained_errors_do_not_consume_the_owner_seqid ... ok
test nfs4::state::tests::setclientid_retry_keeps_state_and_only_a_confirmed_reboot_drops_it ... ok
test nfs4::state::tests::stale_stateid_seqid_is_rejected ... ok
test nfs4::state::tests::v40_close_retransmit_replays ... ok
test nfs4::state::tests::v41_cannot_open_confirm ... ok
test nfs4::state::tests::stateid_is_bound_to_its_client ... ok
test nfs4::state::tests::v41_close_releases_the_handle_despite_a_stale_wire_seqid ... ok
test nfs4::state::tests::v41_keeps_no_open_owner_state ... ok
test nfs4::state::tests::versions_do_not_share_an_owner_namespace ... ok
test nfs4::state::tests::v41_open_of_a_second_file_is_not_a_replay ... ok
test nfs4::state::tests::by_owner_ignores_a_pending_setclientid ... ok
test result: ok. 28 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Additionally, I have added a test script to run integration tests with common workloads on common NFS clients. I have run these tests on Linux (both using NFSv4.1 & NFSv4.0) & macOS (NFSV4.0 only):
Linux (NFSv4.0):
========================================================================
nfstest.py Linux 6.8.0-137-generic python 3.12.3
mountpoint : /tmp/foo/nfs
mount : localhost:/ on /tmp/foo/nfs type nfs4 (rw,relatime,vers=4.0,rsize=1048576,wsize=1048576,namlen=255,hard,proto=tcp,port=11111,timeo=600,retrans=2,sec=sys,clientaddr=127.0.0.1,local_lock=none,addr=127.0.0.1)
uid/gid : 1000/1000 umask 0002
scratch : nfs/nfstest.thinkpad.22748
========================================================================
[ PASS ] rw/write-read-verify 0.08s
[ PASS ] rw/append 0.03s
[ PASS ] rw/overwrite-at-offset 0.04s
[ PASS ] rw/reopen-o-trunc 0.03s
[ PASS ] rw/1MB-sha256 0.04s
[ PASS ] rw/bigfile-sha256 1.18s
[ PASS ] dir/mkdir-rmdir 0.03s
[ PASS ] dir/readdir-10 0.15s
[ PASS ] dir/readdir-100 1.69s
[ PASS ] dir/recursive-walk 0.48s
[ PASS ] ns/rename-same-dir 0.03s
[ PASS ] ns/rename-across-dirs 0.04s
[ PASS ] ns/rename-over-existing 0.04s
[ PASS ] ns/rename-dir 0.03s
[ PASS ] ns/unlink 0.03s
[ PASS ] ns/symlink-readlink 0.04s
[ PASS ] attr/size-after-close 0.08s
[ PASS ] attr/chmod 0.03s
[ PASS ] attr/mtime-changes-on-write 1.24s
[ PASS ] attr/truncate-grow-shrink 0.04s
[ PASS ] err/enoent 0.01s
[ PASS ] err/eexist 0.02s
[ PASS ] err/rmdir-nonempty-fails 0.03s
[ PASS ] err/eisdir-enotdir 0.03s
[ PASS ] coh/write-then-read-other-process 0.11s
[ PASS ] coh/truncate-then-stat 0.09s
[ PASS ] coh/create-then-readdir 0.08s
[ PASS ] coh/unlink-then-stat 0.09s
------------------------------------------------------------------------
28 passed, 0 failed, 0 skipped in 6.7s
Linux (NFSv4.1):
========================================================================
nfstest.py Linux 6.8.0-137-generic python 3.12.3
mountpoint : /tmp/foo/nfs
mount : localhost:/ on /tmp/foo/nfs type nfs4 (rw,relatime,vers=4.1,rsize=1047672,wsize=1047532,namlen=255,hard,proto=tcp,port=11111,timeo=600,retrans=2,sec=sys,clientaddr=127.0.0.1,local_lock=none,addr=127.0.0.1)
uid/gid : 1000/1000 umask 0002
scratch : nfs/nfstest.thinkpad.22792
========================================================================
[ PASS ] rw/write-read-verify 0.12s
[ PASS ] rw/append 0.03s
[ PASS ] rw/overwrite-at-offset 0.03s
[ PASS ] rw/reopen-o-trunc 0.03s
[ PASS ] rw/1MB-sha256 0.05s
[ PASS ] rw/bigfile-sha256 2.64s
[ PASS ] dir/mkdir-rmdir 0.04s
[ PASS ] dir/readdir-10 0.16s
[ PASS ] dir/readdir-100 1.85s
[ PASS ] dir/recursive-walk 0.36s
[ PASS ] ns/rename-same-dir 0.03s
[ PASS ] ns/rename-across-dirs 0.05s
[ PASS ] ns/rename-over-existing 0.05s
[ PASS ] ns/rename-dir 0.03s
[ PASS ] ns/unlink 0.03s
[ PASS ] ns/symlink-readlink 0.04s
[ PASS ] attr/size-after-close 0.08s
[ PASS ] attr/chmod 0.04s
[ PASS ] attr/mtime-changes-on-write 1.24s
[ PASS ] attr/truncate-grow-shrink 0.05s
[ PASS ] err/enoent 0.01s
[ PASS ] err/eexist 0.02s
[ PASS ] err/rmdir-nonempty-fails 0.03s
[ PASS ] err/eisdir-enotdir 0.03s
[ PASS ] coh/write-then-read-other-process 0.11s
[ PASS ] coh/truncate-then-stat 0.09s
[ PASS ] coh/create-then-readdir 0.08s
[ PASS ] coh/unlink-then-stat 0.08s
------------------------------------------------------------------------
28 passed, 0 failed, 0 skipped in 8.4s
macOS (NFSv4.0):
========================================================================
nfstest.py Darwin 22.1.0 python 3.9.6
mountpoint : /private/tmp/nfs
mount : 192.168.40.52:/ on /private/tmp/nfs (nfs, nodev, nosuid, mounted by foo)
uid/gid : 501/20 umask 0022
scratch : nfs/nfstest.Foos-Mac.1030
========================================================================
[ PASS ] rw/write-read-verify 0.34s
[ PASS ] rw/append 0.17s
[ PASS ] rw/overwrite-at-offset 0.22s
[ PASS ] rw/reopen-o-trunc 0.11s
[ PASS ] rw/1MB-sha256 0.17s
[ PASS ] rw/bigfile-sha256 12.63s
[ PASS ] dir/mkdir-rmdir 0.13s
[ PASS ] dir/readdir-10 0.36s
[ PASS ] dir/readdir-100 3.79s
[ PASS ] dir/recursive-walk 1.21s
[ PASS ] ns/rename-same-dir 0.08s
[ PASS ] ns/rename-across-dirs 0.12s
[ PASS ] ns/rename-over-existing 0.11s
[ PASS ] ns/rename-dir 0.10s
[ PASS ] ns/unlink 0.27s
[ PASS ] ns/symlink-readlink 0.12s
[ PASS ] attr/size-after-close 0.16s
[ PASS ] attr/chmod 0.09s
[ PASS ] attr/mtime-changes-on-write 1.30s
[ PASS ] attr/truncate-grow-shrink 0.11s
[ PASS ] err/enoent 0.02s
[ PASS ] err/eexist 0.05s
[ PASS ] err/rmdir-nonempty-fails 0.09s
[ PASS ] err/eisdir-enotdir 0.06s
[ PASS ] coh/write-then-read-other-process 0.32s
[ PASS ] coh/truncate-then-stat 0.16s
[ PASS ] coh/create-then-readdir 0.16s
[ PASS ] coh/unlink-then-stat 0.18s
------------------------------------------------------------------------
28 passed, 0 failed, 0 skipped in 25.8s
During the above work several issues were identified & fixed. At this point I cannot find any additional issues with the NFSv4 support & all tests pass reliably.
Release
I went ahead and pushed a release (0.5.0) to the projects Github. A corresponding docker image has been released along with it.
I believe the project has delivered the agreed-upon milestones and can be used as envisioned.