v0.4.67: NFSv3 alongside SMB (in-process via nfs3_client crate)
NFS is back — done right this time. v0.4.67 ships a pure-Rust NFSv3
client (`nfs3_client` 0.9 from the xetdata/Vaiz crate family) running
in-process inside the openpxe binary. No `mount.nfs`, no kernel
modules, no `CAP_SYS_ADMIN`, no subprocess. Works in every container
that the v0.4.65 SMB path works in (Unraid included).
The v0.4.65 SMB path stays as-is. Operators get both protocols
side-by-side and pick whichever their NAS prefers — or use both
together. NFSv3 has one architectural advantage over the SMB
userspace path: HTTP Range requests work for NFS-sourced ISOs
because NFSv3 READ3 takes an explicit offset. SMB-sourced ISOs still
return 416 for ranges (smbclient CLI can't seek mid-stream).
## What's new
- `crates/iso-store/src/nfs_share.rs` — `NfsShareManager` mirroring
`SmbShareManager` structurally. Lists ISOs via READDIR3+LOOKUP3+
GETATTR3, streams files via READ3 in 64 KiB chunks piped to axum
body streams. Uses `connect_from_privileged_port(false)` because
the openpxe binary runs as uid 10001 — most modern NFS servers
allow that; a server that demands privileged ports needs
`insecure` in /etc/exports, and the hint translation calls that
out specifically.
- `IsoSource::Nfs { share_id, relative_path }` variant alongside the
existing `Smb`. `IsoStore::iso_path_for` returns None for both;
the HTTP handler dispatches to the right share manager.
- `/api/nfs-shares` CRUD + scan endpoints, parallel to
`/api/smb-shares`. `POST` body: `{ server, export, port? }`.
- `nfs` terminal command back (this time as in-process, not kernel
mount): `list | add <srv>:<export> [port] | remove | scan`. The
v0.4.64 `nfs` command name pointing at kernel mount is moot
history — same name, completely different mechanism.
- Storage tab: a new NFS shares card sits directly below the SMB
shares card. The form is simpler (no auth fields) since NFSv3
uses AUTH_SYS and access is gated server-side by client IP.
- Dashboard "Images available" tile sums SMB + NFS reachable shares
into a generic "N remote shares" line.
## What's the same
- The structured `{error, stderr, hint}` JSON shape on failures
matches the SMB API exactly, so the UI's error banner renders
identically.
- Hint translation: NFS3ERR_ACCES → "exports list", NFS3ERR_NOENT →
"export path doesn't exist", `mount denied` → "/etc/exports may
need `insecure`", timeouts → "check IP/port/firewall".
- Persistence: `<work_dir>/nfs_shares.json`. No conflict with the
long-dead v0.4.64 `nfs.json`.
## Why nfs3_client
User picked it: pure-Rust matches the architecture, NFSv3 covers the
real-world cases, AUTH_SYS keeps the UI simple. The crate is at
0.9.0, MIT/Unlicense, rust-version 1.88 (we're on 1.95). Tokio
feature flag enabled. Image size unchanged at compile time — single
musl static binary, no extra OS packages.
## Tests
160 passing (was 150 in v0.4.66, +10):
- nfs_share parser: stable share ids, server normalization (smb://,
cifs://, \\, // all stripped).
- hint_for(): NFS3ERR_ACCES, NFS3ERR_NOENT, mount denied, unknown.
- status_label() covers the common nfsstat3 codes.
- HTTP integration: nfs-shares list starts empty, missing server
rejected, export without leading slash rejected.
`cargo clippy --workspace --all-targets -- -D warnings` clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
9f66c269c4
commit
3f9d8568f0
Generated
+42
-8
@@ -1102,6 +1102,37 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nfs3_client"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ac227029a6127f3474b4fb61cbf2d3dddbe9424ad50c24fada68b633687a532"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"nfs3_types",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nfs3_macros"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06d8fb377e6efeb91911a8ed962a69615535e167e675025a2be8fa109751426a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.117",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nfs3_types"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5d5ab1a9fdfeab2f03b36b6300dea4ea3806e6a057cb479628b41d65e356b94"
|
||||
dependencies = [
|
||||
"nfs3_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.50.3"
|
||||
@@ -1140,7 +1171,7 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "openpxe"
|
||||
version = "0.4.66"
|
||||
version = "0.4.67"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -1162,7 +1193,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openpxe-core"
|
||||
version = "0.4.66"
|
||||
version = "0.4.67"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bcrypt",
|
||||
@@ -1181,7 +1212,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openpxe-dhcp-proxy"
|
||||
version = "0.4.66"
|
||||
version = "0.4.67"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -1195,7 +1226,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openpxe-http-api"
|
||||
version = "0.4.66"
|
||||
version = "0.4.67"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"axum",
|
||||
@@ -1226,7 +1257,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openpxe-ipxe-assets"
|
||||
version = "0.4.66"
|
||||
version = "0.4.67"
|
||||
dependencies = [
|
||||
"openpxe-core",
|
||||
"rust-embed",
|
||||
@@ -1236,14 +1267,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openpxe-iso-store"
|
||||
version = "0.4.66"
|
||||
version = "0.4.67"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bcrypt",
|
||||
"bytes",
|
||||
"futures",
|
||||
"hex",
|
||||
"image",
|
||||
"libc",
|
||||
"nfs3_client",
|
||||
"nfs3_types",
|
||||
"openpxe-core",
|
||||
"parking_lot",
|
||||
"serde",
|
||||
@@ -1260,7 +1294,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openpxe-tftp"
|
||||
version = "0.4.66"
|
||||
version = "0.4.67"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -1274,7 +1308,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openpxe-webui"
|
||||
version = "0.4.66"
|
||||
version = "0.4.67"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
|
||||
Reference in New Issue
Block a user