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,156 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// The file system location attributes of RFC 5661 section 11.10: the
|
||||
// fs_locations and fs_locations_info attributes that migration and
|
||||
// referrals hand to the client.
|
||||
|
||||
package nfs4
|
||||
|
||||
import (
|
||||
"sourcedock.dev/petrbalvin/nfs/internal/xdr"
|
||||
)
|
||||
|
||||
// The attribute numbers of the location family.
|
||||
const (
|
||||
AttrFsLocations = 24 // fs_locations4
|
||||
AttrFsLocationsInfo = 67 // fs_locations_info4
|
||||
)
|
||||
|
||||
// The general flags of fli_flags, RFC 5661 section 11.10.1.
|
||||
const (
|
||||
Fsli4ifVarSub = 0x1
|
||||
)
|
||||
|
||||
// The general flags of the fls_info GFLAGS byte.
|
||||
const (
|
||||
Fsli4gfWritable = 0x01
|
||||
Fsli4gfCurReq = 0x02
|
||||
Fsli4gfAbsent = 0x04
|
||||
Fsli4gfGoing = 0x08
|
||||
Fsli4gfSplit = 0x10
|
||||
)
|
||||
|
||||
// An FsLocation is one fs_location4: the servers that share a root
|
||||
// path.
|
||||
type FsLocation struct {
|
||||
Servers []string
|
||||
RootPath []string
|
||||
}
|
||||
|
||||
// An FsServer is one fs_locations_server4 replica entry: the currency
|
||||
// hint, the opaque info bytes and the server name.
|
||||
type FsServer struct {
|
||||
Currency int32
|
||||
Info []byte
|
||||
Server string
|
||||
}
|
||||
|
||||
// An FsItem is one fs_locations_item4: the replicas of one root path.
|
||||
type FsItem struct {
|
||||
Entries []FsServer
|
||||
RootPath []string
|
||||
}
|
||||
|
||||
// AppendPathname encodes a pathname4: an array of components.
|
||||
func AppendPathname(b []byte, components []string) []byte {
|
||||
b = xdr.AppendUint32(b, uint32(len(components)))
|
||||
for _, c := range components {
|
||||
b = xdr.AppendString(b, c)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// AppendFsLocations encodes the fs_locations4 attribute.
|
||||
func AppendFsLocations(b []byte, fsRoot []string, locations []FsLocation) []byte {
|
||||
b = AppendPathname(b, fsRoot)
|
||||
b = xdr.AppendUint32(b, uint32(len(locations)))
|
||||
for _, l := range locations {
|
||||
b = xdr.AppendUint32(b, uint32(len(l.Servers)))
|
||||
for _, s := range l.Servers {
|
||||
b = xdr.AppendString(b, s)
|
||||
}
|
||||
b = AppendPathname(b, l.RootPath)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// AppendFsLocationsInfo encodes the fs_locations_info4 attribute: the
|
||||
// flags, the validity window, the root and the replica items.
|
||||
func AppendFsLocationsInfo(b []byte, flags uint32, validFor int32, fsRoot []string, items []FsItem) []byte {
|
||||
b = xdr.AppendUint32(b, flags)
|
||||
b = xdr.AppendInt32(b, validFor)
|
||||
b = AppendPathname(b, fsRoot)
|
||||
b = xdr.AppendUint32(b, uint32(len(items)))
|
||||
for _, item := range items {
|
||||
b = xdr.AppendUint32(b, uint32(len(item.Entries)))
|
||||
for _, e := range item.Entries {
|
||||
b = xdr.AppendInt32(b, e.Currency)
|
||||
b = xdr.AppendVarOpaque(b, e.Info)
|
||||
b = xdr.AppendString(b, e.Server)
|
||||
}
|
||||
b = AppendPathname(b, item.RootPath)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// DecodeFsLocations reads the fs_locations4 attribute back.
|
||||
func DecodeFsLocations(payload []byte) (fsRoot []string, locations []FsLocation, err error) {
|
||||
d := xdr.NewDecoder(payload)
|
||||
if fsRoot, err = decodePathname(d); err != nil {
|
||||
return
|
||||
}
|
||||
var n uint32
|
||||
if n, err = d.Uint32(); err != nil {
|
||||
return
|
||||
}
|
||||
for i := uint32(0); i < n; i++ {
|
||||
var l FsLocation
|
||||
var count uint32
|
||||
if count, err = d.Uint32(); err != nil {
|
||||
return
|
||||
}
|
||||
for j := uint32(0); j < count; j++ {
|
||||
var s string
|
||||
if s, err = d.String(); err != nil {
|
||||
return
|
||||
}
|
||||
l.Servers = append(l.Servers, s)
|
||||
}
|
||||
if l.RootPath, err = decodePathname(d); err != nil {
|
||||
return
|
||||
}
|
||||
locations = append(locations, l)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func decodePathname(d *xdr.Decoder) ([]string, error) {
|
||||
n, err := d.Uint32()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var out []string
|
||||
for range n {
|
||||
s, err := d.String()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, s)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// AppendOpenattrArgs encodes the OPENATTR argop: the created flag.
|
||||
func AppendOpenattrArgs(b []byte, created bool) []byte {
|
||||
b = xdr.AppendUint32(b, OpOpenattr)
|
||||
return xdr.AppendBool(b, created)
|
||||
}
|
||||
|
||||
// AppendReadArgs encodes the READ argop.
|
||||
func AppendReadArgs(b []byte, st Stateid, offset uint64, count uint32) []byte {
|
||||
b = xdr.AppendUint32(b, OpRead)
|
||||
b = st.Append(b)
|
||||
b = xdr.AppendUint64(b, offset)
|
||||
return xdr.AppendUint32(b, count)
|
||||
}
|
||||
Reference in New Issue
Block a user