// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: MIT package nfsclient import ( "net" "os" "path/filepath" "testing" "sourcedock.dev/petrbalvin/nfs/internal/krb5" "sourcedock.dev/petrbalvin/nfs/internal/nfs4" "sourcedock.dev/petrbalvin/nfs/internal/nfs4server" "sourcedock.dev/petrbalvin/nfs/internal/nfsfs" "sourcedock.dev/petrbalvin/nfs/internal/rpc" "sourcedock.dev/petrbalvin/nfs/internal/server" "sourcedock.dev/petrbalvin/nfs/internal/xdr" ) // The named attribute high-level API over a protected session: OPENATTR, // create, write and read of one named attribute on a real file. func TestNamedAttrAPI(t *testing.T) { root := t.TempDir() if err := os.WriteFile(filepath.Join(root, "n.txt"), []byte("obj"), 0o644); err != nil { t.Fatal(err) } backend, err := nfsfs.NewLocal(root) if err != nil { t.Fatal(err) } key := make([]byte, 32) for i := range key { key[i] = byte(i + 11) } h := &nfs4server.Handler{FS: backend, ServerKey: key, ServiceName: "nfs"} ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatal(err) } srv := &server.Server{Handle: h.HandleConn} go srv.Serve(t.Context(), ln) defer ln.Close() cl, err := Dial(ln.Addr().String()) if err != nil { t.Fatal(err) } defer cl.Close() if err := cl.Establish("nattr"); err != nil { t.Fatalf("establish: %v", err) } if err := cl.EnableGSS(krb5.EtypeAES128, key, "EXAMPLE.ORG", "nfs", "petr@EXAMPLE.ORG", rpc.SvcIntegrity); err != nil { t.Fatalf("enable gss: %v", err) } // Open the file and write the named attribute through the API. res, bodies, err := cl.Compound("open", [][]byte{ nfs4.AppendPutRootfh(nil), nfs4.AppendLookup(nil, "n.txt"), nfs4.AppendGetfh(nil), }) if err != nil || res.Status != nfs4.ErrOK { t.Fatalf("open: %d %v", res.Status, err) } fh, ferr := xdr.NewDecoder(bodies[2]).VarOpaque() if ferr != nil { t.Fatal(ferr) } if err := cl.SetXattrNamed(fh, "user.note", []byte("hello note")); err != nil { t.Fatalf("set xattr: %v", err) } // Read it back through the OPENATTR path. got, err := cl.GetXattrNamed(fh, "user.note", 256) if err != nil { t.Fatalf("get xattr: %v", err) } if string(got) != "hello note" { t.Fatalf("value %q", got) } }