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
142 lines
3.7 KiB
Go
142 lines
3.7 KiB
Go
// 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
|
|
}
|