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,139 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package nfs4
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"sourcedock.dev/petrbalvin/nfs/internal/xdr"
|
||||
)
|
||||
|
||||
func TestDirDelegationShapes(t *testing.T) {
|
||||
t.Parallel()
|
||||
st := Stateid{1, 'D', 'D', 'E'}
|
||||
|
||||
args := AppendGetDirDelegationArgs(nil, true, OfBits(NotifyAddEntry),
|
||||
NfsTime{Seconds: 5, Nseconds: 6}, NfsTime{Seconds: 7, Nseconds: 8},
|
||||
Bitmap{}, Bitmap{})
|
||||
d := xdr.NewDecoder(args[4:])
|
||||
if sig, err := d.Bool(); err != nil || !sig {
|
||||
t.Fatalf("signal %v: %v", sig, err)
|
||||
}
|
||||
if _, err := ReadBitmap(d); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, want := range []NfsTime{{Seconds: 5, Nseconds: 6}, {Seconds: 7, Nseconds: 8}} {
|
||||
s, err := d.Int64()
|
||||
if err != nil || s != want.Seconds {
|
||||
t.Fatalf("delay seconds %d: %v", s, err)
|
||||
}
|
||||
ns, err := d.Uint32()
|
||||
if err != nil || ns != want.Nseconds {
|
||||
t.Fatalf("delay nseconds %d: %v", ns, err)
|
||||
}
|
||||
}
|
||||
if _, err := ReadBitmap(d); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := ReadBitmap(d); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if d.Remaining() != 0 {
|
||||
t.Fatalf("%d bytes left in the dir delegation args", d.Remaining())
|
||||
}
|
||||
|
||||
ok := AppendGetDirDelegationResOK(nil, [8]byte{9}, st,
|
||||
OfBits(NotifyAddEntry), Bitmap{}, Bitmap{})
|
||||
n, err := resBodyLen(OpGetDirDelegation, ErrOK, ok)
|
||||
if err != nil || n != len(ok) {
|
||||
t.Fatalf("resok measured %d of %d: %v", n, len(ok), err)
|
||||
}
|
||||
un := AppendGetDirDelegationResUnavail(nil, true)
|
||||
if n, err := resBodyLen(OpGetDirDelegation, ErrOK, un); err != nil || n != len(un) {
|
||||
t.Fatalf("unavail measured %d of %d: %v", n, len(un), err)
|
||||
}
|
||||
d = xdr.NewDecoder(un)
|
||||
if s, err := d.Uint32(); err != nil || s != GddUnavail {
|
||||
t.Fatalf("unavail status %d: %v", s, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotifyShapes(t *testing.T) {
|
||||
t.Parallel()
|
||||
add := AppendNotifyAdd(nil, "entry", 0, true)
|
||||
d := xdr.NewDecoder(add)
|
||||
if n, err := d.Uint32(); err != nil || n != 0 {
|
||||
t.Fatalf("old entries %d: %v", n, err)
|
||||
}
|
||||
if name, err := d.String(); err != nil || name != "entry" {
|
||||
t.Fatalf("name %q: %v", name, err)
|
||||
}
|
||||
if _, err := ReadBitmap(d); err != nil { // empty fattr bitmap
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := d.VarOpaque(); err != nil { // empty fattr blob
|
||||
t.Fatal(err)
|
||||
}
|
||||
for range 2 {
|
||||
if _, err := d.Uint32(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if last, err := d.Bool(); err != nil || !last {
|
||||
t.Fatalf("last %v: %v", last, err)
|
||||
}
|
||||
if d.Remaining() != 0 {
|
||||
t.Fatalf("%d bytes left in the add body", d.Remaining())
|
||||
}
|
||||
|
||||
rem := AppendNotifyRemove(nil, "gone", 7)
|
||||
d = xdr.NewDecoder(rem)
|
||||
if name, err := d.String(); err != nil || name != "gone" {
|
||||
t.Fatalf("name %q: %v", name, err)
|
||||
}
|
||||
if _, err := ReadBitmap(d); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := d.VarOpaque(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if c, err := d.Uint64(); err != nil || c != 7 {
|
||||
t.Fatalf("cookie %d: %v", c, err)
|
||||
}
|
||||
|
||||
// CB_NOTIFY args round trip through the decoder the client uses.
|
||||
st := Stateid{2, 'N'}
|
||||
fh := []byte("dir-fh")
|
||||
changes := []Notify4{{Mask: OfBits(NotifyAddEntry), Vals: add}}
|
||||
cb := AppendCBNotifyArgs(nil, st, fh, changes)
|
||||
if n, err := resBodyLenCB(OpCBSequence, ErrOK, make([]byte, 32)); err != nil {
|
||||
t.Fatal(err)
|
||||
} else if n != 32 {
|
||||
t.Fatalf("cb sequence body %d", n)
|
||||
}
|
||||
got, err := DecodeCBNotifyArgs(xdr.NewDecoder(cb[4:]))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got.Stateid != st || !bytes.Equal(got.FH, fh) || len(got.Changes) != 1 {
|
||||
t.Fatalf("decoded %+v", got)
|
||||
}
|
||||
if !got.Changes[0].Mask.Has(NotifyAddEntry) {
|
||||
t.Fatalf("mask %v", got.Changes[0].Mask)
|
||||
}
|
||||
|
||||
// CB_NOTIFY_LOCK args round trip.
|
||||
nl := AppendCBNotifyLockArgs(nil, fh, 0x77, []byte("owner"))
|
||||
lock, err := DecodeCBNotifyLockArgs(xdr.NewDecoder(nl[4:]))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if lock.Clientid != 0x77 || string(lock.Owner) != "owner" || !bytes.Equal(lock.FH, fh) {
|
||||
t.Fatalf("decoded %+v", lock)
|
||||
}
|
||||
if n, err := resBodyLenCB(OpCBNotifyLock, ErrOK, nil); err != nil || n != 0 {
|
||||
t.Fatalf("notify lock res body %d: %v", n, err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user