feat: full NFSv4.2 server and client in pure Go
Test / test (push) Successful in 2m4s
Release / gates (push) Successful in 2m5s
Release / build (amd64, freebsd) (push) Successful in 1m27s
Release / build (amd64, linux) (push) Successful in 1m22s
Release / build (amd64, netbsd) (push) Successful in 1m19s
Release / build (amd64, openbsd) (push) Successful in 1m20s
Release / build (arm64, darwin) (push) Successful in 1m21s
Release / build (arm64, freebsd) (push) Successful in 1m26s
Release / build (arm64, linux) (push) Successful in 1m25s
Release / build (arm64, netbsd) (push) Successful in 1m31s
Release / build (arm64, openbsd) (push) Successful in 1m27s
Release / build (loong64, linux) (push) Successful in 1m37s
Release / build (riscv64, linux) (push) Successful in 1m21s
Release / release (push) Successful in 40s
Test / test (push) Successful in 2m4s
Release / gates (push) Successful in 2m5s
Release / build (amd64, freebsd) (push) Successful in 1m27s
Release / build (amd64, linux) (push) Successful in 1m22s
Release / build (amd64, netbsd) (push) Successful in 1m19s
Release / build (amd64, openbsd) (push) Successful in 1m20s
Release / build (arm64, darwin) (push) Successful in 1m21s
Release / build (arm64, freebsd) (push) Successful in 1m26s
Release / build (arm64, linux) (push) Successful in 1m25s
Release / build (arm64, netbsd) (push) Successful in 1m31s
Release / build (arm64, openbsd) (push) Successful in 1m27s
Release / build (loong64, linux) (push) Successful in 1m37s
Release / build (riscv64, linux) (push) Successful in 1m21s
Release / release (push) Successful in 40s
Assisted-by: GLM 5.3 Flash
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package nfsfs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// The create and write error wrappers map the errno families the
|
||||
// protocol knows: a missing parent is NoEnt, a permission problem is
|
||||
// Permission, a collision is Exist, and the rest stay raw.
|
||||
func TestWrapErrFamilies(t *testing.T) {
|
||||
// The wrapper level: each mapped errno.
|
||||
cases := []struct {
|
||||
err error
|
||||
want error
|
||||
}{
|
||||
{nil, nil},
|
||||
{&os.PathError{Op: "open", Err: syscall.EACCES}, ErrPermission},
|
||||
{&os.PathError{Op: "open", Err: syscall.EPERM}, ErrPermission},
|
||||
{&os.PathError{Op: "open", Err: syscall.EEXIST}, ErrExist},
|
||||
{&os.PathError{Op: "open", Err: syscall.EISDIR}, ErrIsDir},
|
||||
{&os.PathError{Op: "open", Err: syscall.ENOTDIR}, ErrNotDir},
|
||||
{&os.PathError{Op: "write", Err: syscall.ENOSPC}, ErrNoSpace},
|
||||
{&os.PathError{Op: "write", Err: syscall.EIO}, ErrIO},
|
||||
}
|
||||
for _, c := range cases {
|
||||
if got := wrapCreateErr(c.err); !errors.Is(got, c.want) {
|
||||
t.Fatalf("create wrap of %v: %v, want %v", c.err, got, c.want)
|
||||
}
|
||||
}
|
||||
// The write wrapper answers a vanished path with stale, not noent.
|
||||
if got := wrapWriteErr(&os.PathError{Op: "open", Err: syscall.ENOENT}); !errors.Is(got, ErrStale) {
|
||||
t.Fatalf("write wrap of a vanished path: %v, want stale", got)
|
||||
}
|
||||
// An errno outside the families lands in the io error family with
|
||||
// its cause kept in the text.
|
||||
if got := wrapCreateErr(&os.PathError{Op: "open", Err: syscall.EDQUOT}); !errors.Is(got, ErrIO) {
|
||||
t.Fatalf("unmapped errno: %v, want the io error family", got)
|
||||
}
|
||||
}
|
||||
|
||||
// A hard link to a missing target and one under a file both answer
|
||||
// their own sentinels.
|
||||
func TestLinkErrors(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
l, err := NewLocal(root)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
rh, err := l.Root()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, _, err := l.Link(nfsfsMissingHandle(), rh, "x"); err == nil {
|
||||
t.Fatal("a link to a missing target succeeded")
|
||||
}
|
||||
target := filepath.Join(root, "t.txt")
|
||||
if err := os.WriteFile(target, []byte("t"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
th, _, err := l.Lookup(rh, "t.txt")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, _, err := l.Link(th, nfsfsMissingHandle(), "x"); err == nil {
|
||||
t.Fatal("a link under a missing directory succeeded")
|
||||
}
|
||||
}
|
||||
|
||||
// nfsfsMissingHandle builds a handle that resolves to nothing.
|
||||
func nfsfsMissingHandle() Handle {
|
||||
return Handle("nfs\x02\x00\x00\x00stale-handle-bytes")
|
||||
}
|
||||
Reference in New Issue
Block a user