// Copyright (c) 2026 Petr BalvĂ­n (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) }