Files
nfs/internal/nfs4/migration.go
T

157 lines
3.9 KiB
Go
Raw Normal View History

// 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)
}