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,89 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package nfsfs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// ErrReadOnly marks an operation a read only export refuses, NFS4ERR_ROFS
|
||||
// on the wire.
|
||||
var ErrReadOnly = errors.New("nfsfs: the export is read only")
|
||||
|
||||
// A roFS wraps an export and hides its mutating halves. The Writer half
|
||||
// is gone, so the dispatcher answers NFS4ERR_ROFS for every operation
|
||||
// that would change anything through it. The optional halves keep their
|
||||
// reads and refuse their writes with ErrReadOnly, so an export served
|
||||
// read only still reports its extended attributes, its holes and its
|
||||
// named attributes, and refuses to touch them.
|
||||
type roFS struct{ FS }
|
||||
|
||||
// ReadOnly serves fs read only: the reads of every half pass through, the
|
||||
// writes of the mutating halves answer ErrReadOnly, and the mutating
|
||||
// Writer half disappears from the type.
|
||||
func ReadOnly(fs FS) FS {
|
||||
return roFS{fs}
|
||||
}
|
||||
|
||||
// GetXattr reads one named attribute of the object.
|
||||
func (ro roFS) GetXattr(h Handle, name string, max int) ([]byte, error) {
|
||||
x, ok := ro.FS.(XattrFS)
|
||||
if !ok {
|
||||
return nil, ErrXattrNotSupp
|
||||
}
|
||||
return x.GetXattr(h, name, max)
|
||||
}
|
||||
|
||||
// ListXattr names the attributes of the object.
|
||||
func (ro roFS) ListXattr(h Handle, max int) ([]string, error) {
|
||||
x, ok := ro.FS.(XattrFS)
|
||||
if !ok {
|
||||
return nil, ErrXattrNotSupp
|
||||
}
|
||||
return x.ListXattr(h, max)
|
||||
}
|
||||
|
||||
// SetXattr refuses a write on a read only export.
|
||||
func (ro roFS) SetXattr(h Handle, name string, value []byte, mode uint32) error {
|
||||
return ErrReadOnly
|
||||
}
|
||||
|
||||
// RemoveXattr refuses a write on a read only export.
|
||||
func (ro roFS) RemoveXattr(h Handle, name string) error {
|
||||
return ErrReadOnly
|
||||
}
|
||||
|
||||
// SeekHole reports the first hole at or after the offset.
|
||||
func (ro roFS) SeekHole(h Handle, offset int64) (int64, bool, error) {
|
||||
s, ok := ro.FS.(interface {
|
||||
SeekHole(Handle, int64) (int64, bool, error)
|
||||
SeekData(Handle, int64) (int64, bool, error)
|
||||
})
|
||||
if !ok {
|
||||
return 0, false, ErrNoSparse
|
||||
}
|
||||
return s.SeekHole(h, offset)
|
||||
}
|
||||
|
||||
// SeekData reports the first data byte at or after the offset.
|
||||
func (ro roFS) SeekData(h Handle, offset int64) (int64, bool, error) {
|
||||
s, ok := ro.FS.(interface {
|
||||
SeekHole(Handle, int64) (int64, bool, error)
|
||||
SeekData(Handle, int64) (int64, bool, error)
|
||||
})
|
||||
if !ok {
|
||||
return 0, false, ErrNoSparse
|
||||
}
|
||||
return s.SeekData(h, offset)
|
||||
}
|
||||
|
||||
// Allocate refuses a space reservation on a read only export.
|
||||
func (ro roFS) Allocate(h Handle, offset, length int64) error {
|
||||
return ErrReadOnly
|
||||
}
|
||||
|
||||
// Deallocate refuses a hole punch on a read only export.
|
||||
func (ro roFS) Deallocate(h Handle, offset, length int64) error {
|
||||
return ErrReadOnly
|
||||
}
|
||||
Reference in New Issue
Block a user