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,133 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// Migration and referrals of RFC 5661 section 11: names the handler
|
||||
// marks as referrals resolve to stub handles whose GETATTR answers the
|
||||
// fs_locations and fs_locations_info attributes and whose other
|
||||
// operations answer NFS4ERR_MOVED.
|
||||
|
||||
package nfs4server
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"sourcedock.dev/petrbalvin/nfs/internal/nfs4"
|
||||
"sourcedock.dev/petrbalvin/nfs/internal/nfsfs"
|
||||
)
|
||||
|
||||
// A Referral names the file system the client is sent to: the replica
|
||||
// server list and the path of the referred root on those servers.
|
||||
type Referral struct {
|
||||
Servers []string
|
||||
RootPath []string
|
||||
}
|
||||
|
||||
// referralStore keeps the configured referrals by name and the stub
|
||||
// handles issued for them.
|
||||
type referralStore struct {
|
||||
mu sync.Mutex
|
||||
byRef map[string]Referral // by component name
|
||||
byKey map[string]nfsfs.Handle // by component name: the stub
|
||||
names map[string]string // by stub handle: the component
|
||||
next uint64
|
||||
}
|
||||
|
||||
func newReferralStore() *referralStore {
|
||||
return &referralStore{
|
||||
byRef: make(map[string]Referral),
|
||||
byKey: make(map[string]nfsfs.Handle),
|
||||
names: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
// put registers a referral and answers its stable stub handle.
|
||||
func (s *referralStore) put(name string, r Referral) nfsfs.Handle {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.byRef[name] = r
|
||||
if stub, ok := s.byKey[name]; ok {
|
||||
return stub
|
||||
}
|
||||
s.next++
|
||||
stub := nfsfs.Handle(append([]byte("REF/"), []byte(name)...))
|
||||
s.byKey[name] = stub
|
||||
s.names[string(stub)] = name
|
||||
return stub
|
||||
}
|
||||
|
||||
// byName resolves the referral configured for a component name.
|
||||
func (s *referralStore) byName(name string) (Referral, bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
r, ok := s.byRef[name]
|
||||
return r, ok
|
||||
}
|
||||
|
||||
// byHandle resolves the referral a stub handle names.
|
||||
func (s *referralStore) byHandle(fh nfsfs.Handle) (Referral, bool) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
name, ok := s.names[string(fh)]
|
||||
if !ok {
|
||||
return Referral{}, false
|
||||
}
|
||||
r, ok := s.byRef[name]
|
||||
return r, ok
|
||||
}
|
||||
|
||||
// referrals returns the referral store, made once per handler.
|
||||
func (h *Handler) referrals() *referralStore {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
if h.refSt == nil {
|
||||
h.refSt = newReferralStore()
|
||||
}
|
||||
return h.refSt
|
||||
}
|
||||
|
||||
// SetReferral registers a referral: LOOKUP of the component name lands
|
||||
// on a stub whose GETATTR answers the location attributes and whose
|
||||
// other operations answer NFS4ERR_MOVED.
|
||||
func (h *Handler) SetReferral(name string, r Referral) {
|
||||
h.referrals().put(name, r)
|
||||
}
|
||||
|
||||
// isReferralStub reports whether the handle names a referral stub.
|
||||
func (h *Handler) isReferralStub(fh nfsfs.Handle) bool {
|
||||
_, ok := h.referrals().byHandle(fh)
|
||||
return ok
|
||||
}
|
||||
|
||||
// stubAttrs builds the attributes of a referral stub: an absent
|
||||
// directory whose location attributes carry the referral.
|
||||
func (h *Handler) stubAttrs(fh nfsfs.Handle) nfs4.Attrs {
|
||||
r, _ := h.referrals().byHandle(fh)
|
||||
fsRoot := []string{""}
|
||||
a := nfs4.Attrs{
|
||||
Type: nfs4.NF4Dir,
|
||||
FHExpireType: nfs4.FH4Persistent,
|
||||
NamedAttr: false,
|
||||
UniqueHandles: true,
|
||||
}
|
||||
var servers []nfs4.FsServer
|
||||
for i, s := range r.Servers {
|
||||
currency := int32(0)
|
||||
info := []byte{rpcNoneInfo}
|
||||
if i == 0 {
|
||||
info[0] |= nfs4.Fsli4gfCurReq
|
||||
}
|
||||
info[0] |= nfs4.Fsli4gfWritable
|
||||
servers = append(servers, nfs4.FsServer{Currency: currency, Info: info, Server: s})
|
||||
}
|
||||
a.FsLocations = nfs4.AppendFsLocations(nil, fsRoot, []nfs4.FsLocation{{
|
||||
Servers: r.Servers,
|
||||
RootPath: r.RootPath,
|
||||
}})
|
||||
a.FsLocationsInfo = nfs4.AppendFsLocationsInfo(nil, 0, 60, fsRoot,
|
||||
[]nfs4.FsItem{{Entries: servers, RootPath: r.RootPath}})
|
||||
return a
|
||||
}
|
||||
|
||||
// The value of the fls_info class byte that marks this build's single
|
||||
// homogeneous replica set.
|
||||
const rpcNoneInfo = 0x00
|
||||
Reference in New Issue
Block a user