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,141 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// The handlers of the extended attribute family, RFC 8276. A backend
|
||||
// without the XattrFS half answers NOT_SUPP to all of them.
|
||||
|
||||
package nfs4server
|
||||
|
||||
import (
|
||||
"sourcedock.dev/petrbalvin/nfs/internal/nfs4"
|
||||
"sourcedock.dev/petrbalvin/nfs/internal/nfsfs"
|
||||
"sourcedock.dev/petrbalvin/nfs/internal/xdr"
|
||||
)
|
||||
|
||||
// xattrFS resolves the backend's xattr half, or nil when it has none.
|
||||
func (h *Handler) xattrFS() nfsfs.XattrFS {
|
||||
x, _ := h.FS.(nfsfs.XattrFS)
|
||||
return x
|
||||
}
|
||||
|
||||
// getXattrOp serves GETXATTR: one named value of the current object.
|
||||
// The argument is the name alone, RFC 8276.
|
||||
func (h *Handler) getXattrOp(d *xdr.Decoder, reg *fhreg) ([]byte, uint32, error) {
|
||||
name, err := d.String()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if !reg.haveCur {
|
||||
return nil, nfs4.ErrNoFileHandle, nil
|
||||
}
|
||||
x := h.xattrFS()
|
||||
if x == nil {
|
||||
return nil, nfs4.ErrNotSupp, nil
|
||||
}
|
||||
value, xerr := x.GetXattr(reg.cur, name, 0)
|
||||
if xerr == nfsfs.ErrNoXattr {
|
||||
return nil, nfs4.ErrNoXattr, nil
|
||||
}
|
||||
if xerr == nfsfs.ErrXattrNotSupp {
|
||||
return nil, nfs4.ErrNotSupp, nil
|
||||
}
|
||||
if xerr != nil {
|
||||
return nil, mapErr(xerr), nil
|
||||
}
|
||||
return nfs4.AppendGetXattrRes(nil, value), nfs4.ErrOK, nil
|
||||
}
|
||||
|
||||
// setXattrOp serves SETXATTR: it writes or creates or replaces one named
|
||||
// value of the current object. The argument order is the mode, the key
|
||||
// and the value, RFC 8276.
|
||||
func (h *Handler) setXattrOp(d *xdr.Decoder, reg *fhreg) ([]byte, uint32, error) {
|
||||
mode, err := d.Uint32()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
name, err := d.String()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
value, err := d.VarOpaque()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if !reg.haveCur {
|
||||
return nil, nfs4.ErrNoFileHandle, nil
|
||||
}
|
||||
x := h.xattrFS()
|
||||
if x == nil {
|
||||
return nil, nfs4.ErrNotSupp, nil
|
||||
}
|
||||
if err := x.SetXattr(reg.cur, name, value, mode); err != nil {
|
||||
if err == nfsfs.ErrXattrNotSupp {
|
||||
return nil, nfs4.ErrNotSupp, nil
|
||||
}
|
||||
return nil, mapErr(err), nil
|
||||
}
|
||||
return nil, nfs4.ErrOK, nil
|
||||
}
|
||||
|
||||
// listXattrOp serves LISTXATTR: the names of the current object's
|
||||
// attributes, one page bounded by the budget. The whole list answers in
|
||||
// one page with the end of list flag set, so a resume cookie never
|
||||
// names a remaining entry: cookie zero answers everything and any other
|
||||
// cookie answers an empty final page, RFC 8276 section 5.
|
||||
func (h *Handler) listXattrOp(d *xdr.Decoder, reg *fhreg) ([]byte, uint32, error) {
|
||||
cookie, err := d.Uint64()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
maxcount, err := d.Uint32()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if maxcount > 1<<20 {
|
||||
maxcount = 1 << 20
|
||||
}
|
||||
if !reg.haveCur {
|
||||
return nil, nfs4.ErrNoFileHandle, nil
|
||||
}
|
||||
x := h.xattrFS()
|
||||
if x == nil {
|
||||
return nil, nfs4.ErrNotSupp, nil
|
||||
}
|
||||
names := []string{}
|
||||
if cookie == 0 {
|
||||
names, err = x.ListXattr(reg.cur, int(maxcount))
|
||||
if err == nfsfs.ErrXattrNotSupp {
|
||||
return nil, nfs4.ErrNotSupp, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, mapErr(err), nil
|
||||
}
|
||||
}
|
||||
return nfs4.AppendListXattrRes(nil, cookie, names, true), nfs4.ErrOK, nil
|
||||
}
|
||||
|
||||
// removeXattrOp serves REMOVEXATTR: it deletes one named value of the
|
||||
// current object.
|
||||
func (h *Handler) removeXattrOp(d *xdr.Decoder, reg *fhreg) ([]byte, uint32, error) {
|
||||
name, err := d.String()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
if !reg.haveCur {
|
||||
return nil, nfs4.ErrNoFileHandle, nil
|
||||
}
|
||||
x := h.xattrFS()
|
||||
if x == nil {
|
||||
return nil, nfs4.ErrNotSupp, nil
|
||||
}
|
||||
if err := x.RemoveXattr(reg.cur, name); err != nil {
|
||||
if err == nfsfs.ErrNoXattr {
|
||||
return nil, nfs4.ErrNoXattr, nil
|
||||
}
|
||||
if err == nfsfs.ErrXattrNotSupp {
|
||||
return nil, nfs4.ErrNotSupp, nil
|
||||
}
|
||||
return nil, mapErr(err), nil
|
||||
}
|
||||
return nil, nfs4.ErrOK, nil
|
||||
}
|
||||
Reference in New Issue
Block a user