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
101 lines
3.5 KiB
Go
101 lines
3.5 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package nfs4
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"sourcedock.dev/petrbalvin/nfs/internal/xdr"
|
|
)
|
|
|
|
// The numbered session id: the prefix, the number and the client id
|
|
// halves, RFC 8881 section 18.36.
|
|
func TestMakeNumberedSessionID(t *testing.T) {
|
|
id := MakeNumberedSessionID([4]byte{9, 9, 9, 9}, 0x11223344, 0x5566778899aabbcc)
|
|
if id[0] != 9 || id[3] != 9 {
|
|
t.Fatalf("prefix % x", id[:4])
|
|
}
|
|
if id[4] != 0x11 || id[7] != 0x44 {
|
|
t.Fatalf("number % x", id[4:8])
|
|
}
|
|
if id.ClientIDOf() != 0x5566778899aabbcc {
|
|
t.Fatalf("clientid %x", id.ClientIDOf())
|
|
}
|
|
// Every number mints a distinct id.
|
|
if MakeNumberedSessionID([4]byte{}, 1, 5) == MakeNumberedSessionID([4]byte{}, 2, 5) {
|
|
t.Fatal("two numbers minted the same session id")
|
|
}
|
|
}
|
|
|
|
// NegotiateChannel clamps each value to the smaller of the request and
|
|
// the limit and leaves a zero request at zero.
|
|
func TestNegotiateChannel(t *testing.T) {
|
|
got := NegotiateChannel(
|
|
ChannelAttrs{MaxRequest: 1 << 30, MaxResponse: 1 << 10, MaxRespResourced: 1 << 10,
|
|
MaxOperations: 64, MaxRequests: 0},
|
|
ChannelAttrs{MaxRequest: 1 << 22, MaxResponse: 1 << 22, MaxRespResourced: 1 << 22,
|
|
MaxOperations: 16, MaxRequests: 8},
|
|
)
|
|
want := ChannelAttrs{MaxRequest: 1 << 22, MaxResponse: 1 << 10, MaxRespResourced: 1 << 10,
|
|
MaxOperations: 16, MaxRequests: 0}
|
|
if got.MaxRequest != want.MaxRequest || got.MaxResponse != want.MaxResponse ||
|
|
got.MaxRespResourced != want.MaxRespResourced || got.MaxOperations != want.MaxOperations ||
|
|
got.MaxRequests != want.MaxRequests {
|
|
t.Fatalf("negotiated %+v, want %+v", got, want)
|
|
}
|
|
}
|
|
|
|
// The EXCLUSIVE4_1 open args round trip: guarded create, verifier
|
|
// consumed, mode carried.
|
|
func TestOpenArgsExclusive41RoundTrip(t *testing.T) {
|
|
arg := AppendOpenArgsExclusive41(nil, 0x77, []byte("ex"),
|
|
[8]byte{1, 2, 3, 4, 5, 6, 7, 8}, 0o640, "ex41.txt")
|
|
d := xdr.NewDecoder(arg)
|
|
if op, err := d.Uint32(); err != nil || op != OpOpen {
|
|
t.Fatalf("op %d %v", op, err)
|
|
}
|
|
a, err := DecodeOpenArgs(d)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !a.Create || !a.Guarded || a.Truncate || a.Perm != 0o640 || a.Name != "ex41.txt" ||
|
|
a.ClientID != 0x77 || string(a.Owner) != "ex" {
|
|
t.Fatalf("args %+v", a)
|
|
}
|
|
if d.Remaining() != 0 {
|
|
t.Fatalf("%d bytes left", d.Remaining())
|
|
}
|
|
// The guarded form sets the flag the same way.
|
|
arg = AppendOpenArgs(nil, 1, []byte("o"), ShareAccessRead, 0, true, 0o600, "g.txt")
|
|
if _, err := DecodeOpenArgs(xdr.NewDecoder(arg[4:])); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
// The OPEN result measurement walks the write delegation with its space
|
|
// limit union and the permissions ace.
|
|
func TestResBodyLenOpenDelegations(t *testing.T) {
|
|
var st Stateid
|
|
st[0] = 1
|
|
write := AppendOpenResDeleg(nil, st, OpenDelegWrite, st)
|
|
if n, err := resBodyLen(OpOpen, ErrOK, write); err != nil || n != len(write) {
|
|
t.Fatalf("write deleg measured %d of %d: %v", n, len(write), err)
|
|
}
|
|
read := AppendOpenResDeleg(nil, st, OpenDelegRead, st)
|
|
if n, err := resBodyLen(OpOpen, ErrOK, read); err != nil || n != len(read) {
|
|
t.Fatalf("read deleg measured %d of %d: %v", n, len(read), err)
|
|
}
|
|
none := AppendOpenRes(nil, st)
|
|
if n, err := resBodyLen(OpOpen, ErrOK, none); err != nil || n != len(none) {
|
|
t.Fatalf("no deleg measured %d of %d: %v", n, len(none), err)
|
|
}
|
|
// A truncated body of any of the three fails instead of measuring
|
|
// past the payload.
|
|
for n := 0; n < len(write); n += 4 {
|
|
if _, err := resBodyLen(OpOpen, ErrOK, write[:n]); err == nil {
|
|
t.Fatalf("a %d byte prefix measured cleanly", n)
|
|
}
|
|
}
|
|
}
|