// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: MIT package nfs4 import ( "bytes" "testing" "sourcedock.dev/petrbalvin/nfs/internal/xdr" ) func TestOps42Shapes(t *testing.T) { t.Parallel() st := Stateid{2, 'S'} // SEEK args and res. seek := AppendSeekArgs(nil, st, 8, ContentHole) d := xdr.NewDecoder(seek[4:]) if raw, err := d.Raw(16); err != nil || !bytes.Equal(raw, st[:]) { t.Fatalf("stateid % x: %v", raw, err) } if off, err := d.Uint64(); err != nil || off != 8 { t.Fatalf("offset %d: %v", off, err) } if what, err := d.Uint32(); err != nil || what != ContentHole { t.Fatalf("what %d: %v", what, err) } res := AppendSeekRes(nil, true, 4096) if n, err := resBodyLen(OpSeek, ErrOK, res); err != nil || n != len(res) { t.Fatalf("seek res measured %d of %d: %v", n, len(res), err) } // COPY res measures through the write response and requirements. verifier := [8]byte{1, 2, 3, 4, 5, 6, 7, 8} copyRes := AppendCopyRes(nil, st, false, 128, NfsSyncFileSync, verifier, true, true) if n, err := resBodyLen(OpCopy, ErrOK, copyRes); err != nil || n != len(copyRes) { t.Fatalf("copy res measured %d of %d: %v", n, len(copyRes), err) } // READ_PLUS res with one data segment. rp := AppendReadPlusDataRes(nil, false, 0, []byte("data")) if n, err := resBodyLen(OpReadPlus, ErrOK, rp); err != nil || n != len(rp) { t.Fatalf("read plus res measured %d of %d: %v", n, len(rp), err) } rph := AppendReadPlusHoleRes(nil, true, 0, 4096) if n, err := resBodyLen(OpReadPlus, ErrOK, rph); err != nil || n != len(rph) { t.Fatalf("read plus hole res measured %d of %d: %v", n, len(rph), err) } // WRITE_SAME args decode through the adb fields. The block number // is a count4 on the wire, RFC 7862. ws := AppendWriteSameArgs(nil, st, StableFileSync, 8, 512, 2, 0, 0, 0, []byte("ab")) d = xdr.NewDecoder(ws[4:]) if raw, err := d.Raw(16); err != nil || !bytes.Equal(raw, st[:]) { t.Fatalf("stateid % x: %v", raw, err) } if stable, err := d.Uint32(); err != nil || stable != StableFileSync { t.Fatalf("stable %d: %v", stable, err) } if off, err := d.Uint64(); err != nil || off != 8 { t.Fatalf("offset %d: %v", off, err) } for _, want := range []uint64{512, 2, 0} { if v, err := d.Uint64(); err != nil || v != want { t.Fatalf("adb field %d: %v", v, err) } } if bn, err := d.Uint32(); err != nil || bn != 0 { t.Fatalf("adb block num %d: %v", bn, err) } if rp, err := d.Uint64(); err != nil || rp != 0 { t.Fatalf("adb rel off pattern %d: %v", rp, err) } if p, err := d.VarOpaque(); err != nil || string(p) != "ab" { t.Fatalf("pattern %q: %v", p, err) } wsr := AppendWriteSameRes(nil, 1024, NfsSyncFileSync, verifier) if n, err := resBodyLen(OpWriteSame, ErrOK, wsr); err != nil || n != len(wsr) { t.Fatalf("write same res measured %d of %d: %v", n, len(wsr), err) } // The small ops: allocate, io advise, offload status. al := AppendAllocateArgs(nil, st, 0, 100) d = xdr.NewDecoder(al[4:]) if _, err := d.Raw(16); err != nil { t.Fatal(err) } if _, err := d.Uint64(); err != nil { t.Fatal(err) } if l, err := d.Uint64(); err != nil || l != 100 { t.Fatalf("length %d: %v", l, err) } hints := AppendIoAdviseRes(nil, Bitmap{}.With(2)) if n, err := resBodyLen(OpIoAdvise, ErrOK, hints); err != nil || n != len(hints) { t.Fatalf("io advise res measured %d of %d: %v", n, len(hints), err) } os := AppendOffloadStatusRes(nil, 64, OffloadSuccess) if n, err := resBodyLen(OpOffloadStatus, ErrOK, os); err != nil || n != len(os) { t.Fatalf("offload status res measured %d of %d: %v", n, len(os), err) } clone := AppendCloneArgs(nil, st, st, 1, 2, 3) d = xdr.NewDecoder(clone[4:]) if _, err := d.Raw(16); err != nil { t.Fatal(err) } if _, err := d.Raw(16); err != nil { t.Fatal(err) } for _, want := range []uint64{1, 2, 3} { if v, err := d.Uint64(); err != nil || v != want { t.Fatalf("clone field %d: %v", v, err) } } } func TestOps42RemainingShapes(t *testing.T) { t.Parallel() st := Stateid{4, 'C'} // The remaining arg encoders: op code in front, fields in order. bodies := map[string][]byte{ "deallocate": AppendDeallocateArgs(nil, st, 1, 2), "ioadvise": AppendIoAdviseArgs(nil, st, 1, 2, Bitmap{}.With(1)), "copy": AppendCopyArgs(nil, st, st, 1, 2, 3, true, false, nil), "copynotify": AppendCopyNotifyArgs(nil, st, 3, "", NetAddr{Netid: "tcp", Uaddr: "1.2.3.4.8.1"}), "cancel": AppendOffloadCancelArgs(nil, st), "status": AppendOffloadStatusArgs(nil, st), "layerr": AppendLayoutErrorArgs(nil, 1, 2, st, layoutTestDevice, ErrBadStateid, OpRead), "laystats": AppendLayoutStatsArgs(nil, 1, 2, st, 1, 2, 3, 4, layoutTestDevice, 0, nil), "readplus": AppendReadPlusArgs(nil, st, 1, 512), } for _, body := range bodies { d := xdr.NewDecoder(body[4:]) if _, err := d.Raw(16); err != nil { t.Fatal(err) } } cn := AppendCopyNotifyRes(nil, 90, st, []CopySourceServer{{Type: 2, Name: "dst"}}) if n, err := resBodyLen(OpCopyNotify, ErrOK, cn); err != nil || n != len(cn) { t.Fatalf("copy notify res measured %d of %d: %v", n, len(cn), err) } // A COPY result whose write response carries the callback stateid // arm measures through the same shape. wr := AppendCopyRes(nil, st, true, 10, NfsSyncDataSync, [8]byte{}, false, true) if n, err := resBodyLen(OpCopy, ErrOK, wr); err != nil || n != len(wr) { t.Fatalf("copy res with callback measured %d of %d: %v", n, len(wr), err) } } var layoutTestDevice = [16]byte{1, 2, 3}