# Test Case: FreeBSD NFSv3 Export Basic Functionality **Test ID**: TC131-nfs-export-basic **Category**: NFS Export **Priority**: Critical **Regression**: EROFS `VOP_VPTOFH` / `VFS_FHTOVP` / export updates ## Objective Verify that FreeBSD 15 mountd can install an export on a read-only EROFS mount, that nfsd can resolve EROFS file handles, and that regular files, directories, symlinks, and FIFOs are visible through a real NFSv3 client. ## Preconditions - FreeBSD 15 amd64 server/client, optionally the same host over loopback. - Root privilege for module, md, NFS service, `getfh`, and `fhopen` operations. - `erofs.ko`, `test-nfs.erofs`, and `tests/nfs_fh_tool.c` available in the guest. - The fixture contains `basic/regular.txt`, `basic/subdir`, `basic/link-to-regular`, and `basic/test.fifo`. ## Procedure 1. Build the module and helper: ```sh ./build.sh cc -O2 -Wall -Wextra -std=c17 tests/nfs_fh_tool.c -o nfs_fh_tool ``` 2. Load and mount EROFS on a fixed md unit: ```sh kldload ./erofs.ko mdconfig -a -t vnode -f test-nfs.erofs -u 42 mkdir -p /mnt/repo22-erofs mount -t erofs -o ro /dev/md42 /mnt/repo22-erofs mount -v | grep /mnt/repo22-erofs ``` The line must show `read-only` and must not show `NFS exported` before mountd processes `/etc/exports`. 3. Configure the real FreeBSD mountd/nfsd flow: ```sh cp -p /etc/exports /tmp/exports.before 2>/dev/null || true printf '%s\n' \ '/mnt/repo22-erofs -ro -maproot=root -network 127.0.0.0 -mask 255.0.0.0' \ > /etc/exports service rpcbind onestart service mountd onestart service nfsd onestart service mountd onereload rpcinfo -p 127.0.0.1 showmount -e 127.0.0.1 mount -v | grep /mnt/repo22-erofs ``` After reload, the EROFS mount line must show `NFS exported`. The flag is installed by the generic FreeBSD export layer after the filesystem accepts the export-only `MNT_UPDATE`; EROFS must not set it during the initial mount. 4. Mount the export through NFSv3 and confirm the negotiated options: ```sh mkdir -p /mnt/repo22-nfs mount_nfs -o nfsv3,tcp,rdirplus 127.0.0.1:/mnt/repo22-erofs \ /mnt/repo22-nfs nfsstat -m ``` 5. Verify all required vnode types and read-only behavior: ```sh cmp /mnt/repo22-nfs/basic/regular.txt \ /mnt/repo22-erofs/basic/regular.txt test -d /mnt/repo22-nfs/basic/subdir test "$(readlink /mnt/repo22-nfs/basic/link-to-regular)" = regular.txt test -p /mnt/repo22-nfs/basic/test.fifo test "$(stat -f %i /mnt/repo22-nfs/basic/regular.txt)" = \ "$(stat -f %i /mnt/repo22-erofs/basic/regular.txt)" ! touch /mnt/repo22-nfs/write-must-fail ``` 6. Exercise both shared- and exclusive-lock file-handle resolution locally: ```sh ./nfs_fh_tool capture /mnt/repo22-erofs/basic/regular.txt regular.fh ./nfs_fh_tool stat regular.fh ./nfs_fh_tool cat regular.fh regular.out cmp regular.out /mnt/repo22-erofs/basic/regular.txt ``` `fhstat` requests a shared lock and `fhopen` requests an exclusive lock. 7. Confirm that NFSv3 used READDIRPLUS and did not issue writes: ```sh nfsstat -c nfsstat -s ``` ## Expected Results - mountd accepts the EROFS export-only update. - `showmount -e` lists the EROFS path and `mount -v` shows `NFS exported` only after mountd reloads exports. - NFSv3 regular reads, directory traversal, symlink lookup, FIFO metadata, and inode numbers match the direct EROFS mount. - Writes fail with `EROFS`/`Read-only file system`. - `fhstat` and `fhopen` both resolve the same 64-bit EROFS NID. - NFS statistics show READDIRPLUS traffic and zero successful write RPCs. ## Cleanup Always remove clients before stopping the loopback server: ```sh umount /mnt/repo22-nfs : > /etc/exports service mountd onereload service nfsd onestop service mountd onestop service rpcbind onestop umount /mnt/repo22-erofs mdconfig -d -u 42 kldunload erofs rm -f /etc/exports ``` Restore a pre-existing `/etc/exports` instead of removing it when applicable. ## Notes - The EROFS FreeBSD file-handle payload is 16 bytes: `len`, `pad`, `nid_hi`, `nid_lo`, and a nonzero superblock-seeded per-inode generation matching `va_gen`. - Do not use Linux `exportfs`; FreeBSD mountd installs exports through a mount update carrying the `export` option. - Throughput is recorded for information only; correctness has no fixed MB/s threshold.