Skip to content

Testing

This document is the protocol-conformance and interop test runbook for DittoFS contributors. It covers Windows VM setup, manual SMB and NFS validation checklists, and the WPTS and smbtorture conformance suites.

For Go unit/integration tests and the e2e test runner, see ./contributing.md.

For the end-user guide to connecting a Windows client, see ../guide/windows.md.


Last validated: 2026-02-28 (Windows 11 24H2)

Section titled “Option A: UTM (Apple Silicon, recommended)”

UTM is the recommended VM solution for Apple Silicon Macs running DittoFS.

  1. Download and install UTM from mac.getutm.app.
  2. Download the Windows 11 ARM ISO from Microsoft UUP dump or the official Microsoft site.
  3. Create a new VM:
    • Click “Create a New Virtual Machine” > “Virtualize”.
    • Select “Windows”.
    • Allocate at least 4 GB RAM and 2 CPU cores.
    • Allocate 64 GB disk (minimum).
    • Enable “Install drivers and SPICE tools”.
  4. Attach the Windows 11 ARM ISO and boot the VM.
  5. Complete the Windows 11 installation (skip network during OOBE if needed; use a local account).
  • VirtualBox: download from virtualbox.org, create a Windows 11 x64 VM with at least 4 GB RAM, enable EFI and TPM 2.0 emulation.
  • Hyper-V: available on Windows 10/11 Pro/Enterprise, enable via “Turn Windows features on or off”.

The Windows VM must be able to reach the DittoFS host on ports 12445 (SMB) and 12049 (NFS).

Network modeWhen to useConfiguration
BridgedVM and host on same LANVM gets its own IP from DHCP; use host’s LAN IP to connect
Host-onlyIsolated testingConfigure a host-only network adapter; use host-only gateway IP
Shared (NAT)Default in UTMForward ports 12445 (SMB) and 12049 (NFS) from host to guest

Verify connectivity from the Windows VM:

Terminal window
Test-NetConnection -ComputerName <host-ip> -Port 12445

Client for NFS (for NFS client testing):

  1. Open Settings > Apps > Optional Features > More Windows features.
  2. Expand Services for NFS, check Client for NFS, click OK and restart if prompted.

SMB client: installed and enabled by default on Windows 11; no additional setup needed.

Windows 11 24H2 blocks insecure guest logons by default. For testing DittoFS with guest/anonymous sessions:

Option 1 — Group Policy Editor (gpedit.msc):

  1. Press Win+R, type gpedit.msc, press Enter.
  2. Navigate to: Computer Configuration > Administrative Templates > Network > Lanman Workstation.
  3. Double-click “Enable insecure guest logons”, set to Enabled, click OK.
  4. Restart the Lanman Workstation service or reboot.

Option 2 — Registry (Windows 11 Home or scripted):

Terminal window
# Run as Administrator
Set-ItemProperty `
-Path "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" `
-Name "AllowInsecureGuestAuth" -Value 1 -Type DWord
Restart-Service LanmanWorkstation

Registry path: HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters\AllowInsecureGuestAuth = 1 (DWORD)

On the macOS/Linux host:

Terminal window
# 1. Build DittoFS
go build -o dfs cmd/dfs/main.go
go build -o dfsctl cmd/dfsctl/main.go
# 2. Initialize config (first time only)
./dfs config init
# 3. Start DittoFS
./dfs start
# 4. Create test stores
./dfsctl store metadata add --name test-meta --type memory
./dfsctl store block local add --name test-blocks --type memory
# 5. Create a test share
./dfsctl share create --name /smbbasic --metadata test-meta --local test-blocks
# 6. Create a test user
./dfsctl user create --username testuser # enter password when prompted
# 7. Grant permissions
./dfsctl share permission grant /smbbasic --user testuser --level read-write
# 8. Verify the SMB adapter is listening on port 12445
./dfsctl adapter list

Connect from the Windows VM:

Terminal window
rem Mapped drive
net use Z: \\<host-ip>\smbbasic /user:testuser <password>
rem UNC path
explorer \\<host-ip>\smbbasic

Optional software useful for testing:

SoftwarePurpose
VS CodeTest opening projects from SMB share, file editing, integrated terminal
Microsoft OfficeTest Word/Excel save/open cycles with file locking
7-Zip or WinRARTest archive extraction to/from share

Test both mapped drives (e.g., Z:) and UNC paths (e.g., \\host\smbbasic) for each category.

#TestMapped driveUNC pathNotes
C-01Connect with net use Z: \\host\smbbasic /user:testuser <pass>[ ] Pass / [ ] Fail / [ ] SkipN/A
C-02Browse via Explorer \\host\smbbasicN/A[ ] Pass / [ ] Fail / [ ] Skip
C-03Disconnect with net use Z: /delete[ ] Pass / [ ] Fail / [ ] SkipN/A
C-04Reconnect after disconnect[ ] Pass / [ ] Fail / [ ] Skip[ ] Pass / [ ] Fail / [ ] Skip
#TestMapped driveUNC pathNotes
E-01Create new text file (right-click > New > Text Document)[ ] Pass / [ ] Fail / [ ] Skip[ ] Pass / [ ] Fail / [ ] Skip
E-02Create new folder[ ] Pass / [ ] Fail / [ ] Skip[ ] Pass / [ ] Fail / [ ] Skip
E-03Rename file[ ] Pass / [ ] Fail / [ ] Skip[ ] Pass / [ ] Fail / [ ] Skip
E-04Rename folder[ ] Pass / [ ] Fail / [ ] Skip[ ] Pass / [ ] Fail / [ ] Skip
E-05Delete file (Delete key or right-click > Delete)[ ] Pass / [ ] Fail / [ ] Skip[ ] Pass / [ ] Fail / [ ] Skip
E-06Delete folder (empty)[ ] Pass / [ ] Fail / [ ] Skip[ ] Pass / [ ] Fail / [ ] Skip
E-07Delete folder (non-empty)[ ] Pass / [ ] Fail / [ ] Skip[ ] Pass / [ ] Fail / [ ] Skip
E-08Copy file (Ctrl+C / Ctrl+V)[ ] Pass / [ ] Fail / [ ] Skip[ ] Pass / [ ] Fail / [ ] Skip
E-09Move file (Ctrl+X / Ctrl+V)[ ] Pass / [ ] Fail / [ ] Skip[ ] Pass / [ ] Fail / [ ] Skip
E-10Drag-and-drop file within share[ ] Pass / [ ] Fail / [ ] Skip[ ] Pass / [ ] Fail / [ ] Skip
E-11Copy file from local disk to share[ ] Pass / [ ] Fail / [ ] Skip[ ] Pass / [ ] Fail / [ ] Skip
E-12Copy file from share to local disk[ ] Pass / [ ] Fail / [ ] Skip[ ] Pass / [ ] Fail / [ ] Skip
E-13View file Properties (General tab)[ ] Pass / [ ] Fail / [ ] Skip[ ] Pass / [ ] Fail / [ ] Skip
E-14View file Properties (Security tab)[ ] Pass / [ ] Fail / [ ] Skip[ ] Pass / [ ] Fail / [ ] SkipShould show Owner + DACL
#TestCommandResultNotes
CMD-01List directorydir Z:\[ ] Pass / [ ] Fail / [ ] Skip
CMD-02Display file contentstype Z:\testfile.txt[ ] Pass / [ ] Fail / [ ] Skip
CMD-03Copy filecopy Z:\file1.txt Z:\file2.txt[ ] Pass / [ ] Fail / [ ] Skip
CMD-04Move filemove Z:\file2.txt Z:\subfolder\[ ] Pass / [ ] Fail / [ ] Skip
CMD-05Rename fileren Z:\file1.txt newname.txt[ ] Pass / [ ] Fail / [ ] Skip
CMD-06Delete filedel Z:\newname.txt[ ] Pass / [ ] Fail / [ ] Skip
CMD-07Create directorymkdir Z:\testdir[ ] Pass / [ ] Fail / [ ] Skip
CMD-08Remove directoryrmdir Z:\testdir[ ] Pass / [ ] Fail / [ ] Skip
CMD-09View ACLsicacls Z:\[ ] Pass / [ ] Fail / [ ] SkipShould show Owner + DACL entries
CMD-10View attributesattrib Z:\testfile.txt[ ] Pass / [ ] Fail / [ ] Skip
CMD-11File system infofsutil fsinfo volumeinfo Z:\[ ] Pass / [ ] Fail / [ ] Skip
#TestCommandResultNotes
PS-01Get file infoGet-Item Z:\testfile.txt[ ] Pass / [ ] Fail / [ ] Skip
PS-02List directoryGet-ChildItem Z:\ -Recurse[ ] Pass / [ ] Fail / [ ] Skip
PS-03Create fileNew-Item Z:\pstest.txt -ItemType File -Value "hello"[ ] Pass / [ ] Fail / [ ] Skip
PS-04Create directoryNew-Item Z:\psdir -ItemType Directory[ ] Pass / [ ] Fail / [ ] Skip
PS-05Remove fileRemove-Item Z:\pstest.txt[ ] Pass / [ ] Fail / [ ] Skip
PS-06Remove directoryRemove-Item Z:\psdir -Recurse[ ] Pass / [ ] Fail / [ ] Skip
PS-07Copy fileCopy-Item Z:\file1.txt Z:\file_copy.txt[ ] Pass / [ ] Fail / [ ] Skip
PS-08Move fileMove-Item Z:\file_copy.txt Z:\moved.txt[ ] Pass / [ ] Fail / [ ] Skip
PS-09Get ACLGet-Acl Z:\testfile.txt[ ] Pass / [ ] Fail / [ ] SkipShould show Owner + Access rules
PS-10Set ACL$acl = Get-Acl Z:\testfile.txt; Set-Acl Z:\testfile.txt $acl[ ] Pass / [ ] Fail / [ ] SkipRound-trip test
PS-11Write contentSet-Content Z:\pstest.txt "new content"[ ] Pass / [ ] Fail / [ ] Skip
#TestStepsResultNotes
OFF-01Word: Create new documentOpen Word > Save As to Z:\ as .docx[ ] Pass / [ ] Fail / [ ] Skip
OFF-02Word: Reopen documentDouble-click the .docx on Z:\[ ] Pass / [ ] Fail / [ ] Skip
OFF-03Word: Edit and saveModify text, Ctrl+S[ ] Pass / [ ] Fail / [ ] Skip
OFF-04Excel: Create workbookOpen Excel > Add data + formulas > Save As .xlsx to Z:\[ ] Pass / [ ] Fail / [ ] Skip
OFF-05Excel: Reopen workbookDouble-click the .xlsx on Z:\[ ] Pass / [ ] Fail / [ ] Skip
OFF-06Excel: Verify formulasCheck that formulas compute correctly after reopen[ ] Pass / [ ] Fail / [ ] Skip
OFF-07Large file save (10 MB+)Create a large document with images, save to Z:\[ ] Pass / [ ] Fail / [ ] Skip
#TestStepsResultNotes
VS-01Open folder from shareFile > Open Folder > select Z:\ or \\host\smbbasic[ ] Pass / [ ] Fail / [ ] Skip
VS-02Create new fileFile > New File > save to share[ ] Pass / [ ] Fail / [ ] Skip
VS-03Edit and saveModify file content, Ctrl+S[ ] Pass / [ ] Fail / [ ] Skip
VS-04Search across filesCtrl+Shift+F, search for a string[ ] Pass / [ ] Fail / [ ] Skip
VS-05Integrated terminalOpen terminal, run dir on share path[ ] Pass / [ ] Fail / [ ] Skip
VS-06Git operations (if .git exists)git status, git log on share[ ] Pass / [ ] Fail / [ ] SkipBest-effort
#TestCommandResultNotes
FS-011 MB filefsutil file createnew Z:\test_1mb.bin 1048576[ ] Pass / [ ] Fail / [ ] Skip
FS-0210 MB filefsutil file createnew Z:\test_10mb.bin 10485760[ ] Pass / [ ] Fail / [ ] Skip
FS-0350 MB filefsutil file createnew Z:\test_50mb.bin 52428800[ ] Pass / [ ] Fail / [ ] Skip
FS-04100 MB filefsutil file createnew Z:\test_100mb.bin 104857600[ ] Pass / [ ] Fail / [ ] Skip
FS-05Read back 1 MBcopy Z:\test_1mb.bin NUL[ ] Pass / [ ] Fail / [ ] Skip
FS-06Read back 10 MBcopy Z:\test_10mb.bin NUL[ ] Pass / [ ] Fail / [ ] Skip
FS-07Read back 50 MBcopy Z:\test_50mb.bin NUL[ ] Pass / [ ] Fail / [ ] Skip
FS-08Read back 100 MBcopy Z:\test_100mb.bin NUL[ ] Pass / [ ] Fail / [ ] Skip
FS-09Verify integrityWrite known content, read back, compare[ ] Pass / [ ] Fail / [ ] SkipUse certutil -hashfile for SHA256

Windows NFS client support is best-effort. The Windows Services for NFS client has known limitations compared to Linux/macOS NFS clients.

#TestCommandResultNotes
NFS-01Mount NFS sharemount -o anon \\<host-ip>\export Z:[ ] Pass / [ ] Fail / [ ] SkipPort 12049
NFS-02List share contentsdir Z:\[ ] Pass / [ ] Fail / [ ] Skip
NFS-03Unmountumount Z:[ ] Pass / [ ] Fail / [ ] Skip
#TestCommandResultNotes
NFS-04Create fileecho test > Z:\nfstest.txt[ ] Pass / [ ] Fail / [ ] Skip
NFS-05Read filetype Z:\nfstest.txt[ ] Pass / [ ] Fail / [ ] Skip
NFS-06Create directorymkdir Z:\nfsdir[ ] Pass / [ ] Fail / [ ] Skip
NFS-07Delete filedel Z:\nfstest.txt[ ] Pass / [ ] Fail / [ ] Skip
NFS-08Delete directoryrmdir Z:\nfsdir[ ] Pass / [ ] Fail / [ ] Skip

Real NFSv3 NLM lock testing (network-namespace isolation)

Section titled “Real NFSv3 NLM lock testing (network-namespace isolation)”

DittoFS coordinates byte-range locks across NFSv3 NLM, NFSv4, and SMB through a single cross-protocol lock manager (pkg/metadata/lock/manager.go). Proving this end-to-end with a real kernel NFSv3 client — not the nolock mount option, which makes the kernel resolve locks locally and never contact the server — requires a specific harness. This section explains why, and how the e2e suite does it.

A kernel NFSv3 client discovers the server’s NLM (lockd) port by querying rpcbind on port 111 — a location fixed by RFC 1833 with no client-side override. But the kernel’s own lockd also registers program 100021 with the local rpcbind. The moment an NFSv3-with-locking mount activates on the same host as DittoFS, the kernel lockd reclaims the 100021 mapping, so the client sends its NLM requests to the local kernel lockd instead of to DittoFS. You cannot run both a userspace NLM server and a kernel NFSv3 client’s lockd against one shared rpcbind.

This is the same constraint NFS-Ganesha hits, and DittoFS copies Ganesha’s solution: isolate the client in a separate Linux network namespace.

server netns (sns) client netns (cns)
┌────────────────────────┐ ┌────────────────────────┐
│ dfs (NFSv3/v4 + SMB) │ │ kernel NFSv3 client │
│ rpcbind :111 (own) │◀──────▶│ rpcbind :111 (own) │
│ NLM 100021 → dfs port │ veth │ rpc.statd (NSM) │
│ │ │ mount -o nfsvers=3 │
└────────────────────────┘ │ (NO nolock) │
└────────────────────────┘

Both the server and the client run in their own network namespace, each with its own rpcbind and its own lockd/NSM port space, so the two 100021 registrations never collide. The server’s rpcbind is made private by launching dfs under unshare --mount with a fresh tmpfs on /run — otherwise it would share the host’s rpcbind socket. The client’s GETPORT for NLM queries the server namespace’s rpcbind across the veth and correctly resolves to the DittoFS port; SM_NOTIFY / GRANTED callbacks flow back over the same veth.

Gotchas mirrored from Ganesha (each is a real failure mode):

  • rpc.statd must run in the client netns. Without a reachable NSM, the first lock fails with SM_MON status 1NLM4_DENIED_NOLOCKS. The harness gates on a warm-up lock so a not-ready NSM can never be mistaken for a conflict.
  • Give each side its own rpcbind (the netns-native form of Ganesha’s multi-homed rpcbind -h fix). If you see “failed to contact remote rpcbind”, a shared/misbound rpcbind is the usual cause.
  • Use IP addresses, not hostnames, for the mount target and NSM — the two namespaces do not share DNS resolution.
  • DittoFS must be started with system-rpcbind registration and UDP enabled (below); UDP carries NSM/SM_NOTIFY.

The registration is opt-in (default off). The e2e harness turns it on at runtime:

PATCH /api/v1/adapters/nfs/settings
{ "portmapper_register_with_system": true, "udp_enabled": true }

Equivalent YAML / env: adapters.nfs.portmapper.register_with_system: true / DITTOFS_ADAPTERS_NFS_PORTMAPPER_REGISTER_WITH_SYSTEM=true. See issue #1503.

  • NLM ↔ SMB — an NFSv3 NLM byte-range lock conflicts with a CIFS byte-range lock on the same file, resolved by the server’s unified lock manager (both directions).
  • NLM ↔ NFSv4 — an NFSv3 NLM lock conflicts with an NFSv4 LOCK, both directions.
  • Registration reachability — rpcinfo -p shows DittoFS’s NLM (100021) mapping.

These tests need root, rpcbind, rpc.statd, and iproute2 (network namespaces), so they run only on Linux. On unsupported environments (macOS, no root, no rpcbind) they t.Skip rather than fail. In CI the e2e workflow installs rpcbind and the job runs privileged enough to create namespaces; locally:

Terminal window
cd test/e2e
sudo ./run-e2e.sh --test TestNLMAxisInterop

Framework code lives in test/e2e/framework/netns.go; the tests in test/e2e/nlm_axis_interop_test.go. The single-host reachability check (which cannot take a real lock, for the reason above) is TestNLMSystemRpcbindRegistration in test/e2e/nlm_locking_test.go.


DittoFS is validated against two industry-standard conformance test suites.

  • Suite: MS-SMB2 BVT (Build Verification Tests)
  • Known failures: see ../../test/smb-conformance/KNOWN_FAILURES.md
  • Run locally:
    Terminal window
    cd test/smb-conformance
    make test # Run WPTS BVT suite
    make test-quick # Quick run (memory profile only)
  • Suite: Full SMB2 test suite (smb2.*)
  • Image: quay.io/samba.org/samba-toolbox:v0.8
  • Known failures: see ../../test/smb-conformance/smbtorture/KNOWN_FAILURES.md
  • Run locally:
    Terminal window
    cd test/smb-conformance
    make smbtorture # Run full smbtorture suite
    make smbtorture-quick # Quick run (memory profile only)
Terminal window
cd test/smb-conformance
make test smbtorture # Run WPTS + smbtorture in sequence

Both test suites run in CI via .github/workflows/smb-conformance.yml on every PR touching SMB-related code.

Do not run two instances of the e2e or conformance suites concurrently — they share a Docker container name and will collide. Run them serially and docker rm -f between runs if needed.