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,85 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package nfsclient
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"sourcedock.dev/petrbalvin/nfs/internal/nfs4"
|
||||
"sourcedock.dev/petrbalvin/nfs/internal/rpc"
|
||||
)
|
||||
|
||||
// The accessors answer copies of what the callback demux recorded.
|
||||
func TestNotifiedAccessors(t *testing.T) {
|
||||
c := &Client{}
|
||||
if got := c.Notified(); len(got) != 0 {
|
||||
t.Fatal("notified before any delivery")
|
||||
}
|
||||
if got := c.NotifiedLocks(); len(got) != 0 {
|
||||
t.Fatal("locks notified before any delivery")
|
||||
}
|
||||
c.recMu.Lock()
|
||||
c.notified = append(c.notified, nfs4.CBNotify{FH: []byte("d")})
|
||||
c.notifiedLocks = append(c.notifiedLocks, nfs4.CBNotifyLock{Clientid: 5})
|
||||
c.recMu.Unlock()
|
||||
if n := c.Notified(); len(n) != 1 || string(n[0].FH) != "d" {
|
||||
t.Fatalf("notified %+v", n)
|
||||
}
|
||||
if n := c.NotifiedLocks(); len(n) != 1 || n[0].Clientid != 5 {
|
||||
t.Fatalf("locks %+v", n)
|
||||
}
|
||||
}
|
||||
|
||||
// The callback demux answers CB_NOTIFY and CB_NOTIFY_LOCK and records
|
||||
// them for the caller.
|
||||
func TestCBCompoundNotifies(t *testing.T) {
|
||||
// The demux answers every callback on the wire; a pipe with a
|
||||
// draining side stands in for the connection.
|
||||
a, b := net.Pipe()
|
||||
defer a.Close()
|
||||
defer b.Close()
|
||||
go func() {
|
||||
buf := make([]byte, 4096)
|
||||
for {
|
||||
if _, err := b.Read(buf); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
c := &Client{conn: a, cbProgram: 0x40000000}
|
||||
st := nfs4.Stateid{1, 'D', 'D'}
|
||||
|
||||
notify := nfs4.AppendCBNotifyArgs(nil, st, []byte("dir"),
|
||||
[]nfs4.Notify4{{Mask: nfs4.OfBits(nfs4.NotifyAddEntry),
|
||||
Vals: nfs4.AppendNotifyAdd(nil, "new", 0, true)}})
|
||||
lock := nfs4.AppendCBNotifyLockArgs(nil, []byte("f"), 9, []byte("owner"))
|
||||
|
||||
c.handleCBCall(1, buildCBCall(t, [][]byte{notify, lock}))
|
||||
notes := c.Notified()
|
||||
if len(notes) != 1 || !notes[0].Changes[0].Mask.Has(nfs4.NotifyAddEntry) {
|
||||
t.Fatalf("notified %+v", notes)
|
||||
}
|
||||
if n := c.NotifiedLocks(); len(n) != 1 || n[0].Clientid != 9 {
|
||||
t.Fatalf("locks %+v", n)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// buildCBCall frames one CB_COMPOUND call record the way the server
|
||||
// sends it.
|
||||
func buildCBCall(t *testing.T, ops [][]byte) []byte {
|
||||
t.Helper()
|
||||
args := nfs4.AppendCBCompoundArgs(nil, "cb", nfs4.MinorVersion, 0, ops)
|
||||
record, err := rpc.AppendCall(nil, rpc.Call{
|
||||
XID: 1, Program: nfs4.CBDefaultProgram, Version: nfs4.Version,
|
||||
Procedure: 1,
|
||||
Cred: rpc.Auth{Flavor: rpc.FlavorNone},
|
||||
Verifier: rpc.Auth{Flavor: rpc.FlavorNone},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return append(record, args...)
|
||||
}
|
||||
Reference in New Issue
Block a user