192 lines
6.8 KiB
Go
192 lines
6.8 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|||
|
|
// SPDX-License-Identifier: MIT
|
||
|
|
|
||
|
|
// The layout type families beyond flexfiles, each with its wire body
|
||
|
|
// and the device address this build answers for it: files (RFC 5661
|
||
|
|
// section 13.3), block/volume (RFC 5663), objects (RFC 5664) and SCSI
|
||
|
|
// (RFC 8154). The metadata server of this build emulates every one of
|
||
|
|
// them over its own storage, so all bodies name the one device the
|
||
|
|
// server itself is.
|
||
|
|
|
||
|
|
package nfs4
|
||
|
|
|
||
|
|
import (
|
||
|
|
"sourcedock.dev/petrbalvin/nfs/internal/xdr"
|
||
|
|
)
|
||
|
|
|
||
|
|
// AppendFileLayoutBody encodes the nfsv4_1_file_layout4: the device,
|
||
|
|
// the stripe unit descriptor, the first stripe index, the pattern
|
||
|
|
// offset and the file handle list.
|
||
|
|
func AppendFileLayoutBody(b []byte, device [16]byte, util, firstStripe uint32, patternOffset uint64, fhs [][]byte) []byte {
|
||
|
|
b = append(b, device[:]...)
|
||
|
|
b = xdr.AppendUint32(b, util)
|
||
|
|
b = xdr.AppendUint32(b, firstStripe)
|
||
|
|
b = xdr.AppendUint64(b, patternOffset)
|
||
|
|
b = xdr.AppendUint32(b, uint32(len(fhs)))
|
||
|
|
for _, fh := range fhs {
|
||
|
|
b = xdr.AppendVarOpaque(b, fh)
|
||
|
|
}
|
||
|
|
return b
|
||
|
|
}
|
||
|
|
|
||
|
|
// AppendFileDeviceAddr encodes the nfsv4_1_file_layout_ds_addr4: the
|
||
|
|
// stripe indices over the multipath list of data server addresses.
|
||
|
|
func AppendFileDeviceAddr(b []byte, stripeIndices []uint32, addrs []NetAddr) []byte {
|
||
|
|
b = xdr.AppendUint32(b, uint32(len(stripeIndices)))
|
||
|
|
for _, i := range stripeIndices {
|
||
|
|
b = xdr.AppendUint32(b, i)
|
||
|
|
}
|
||
|
|
b = xdr.AppendUint32(b, uint32(len(addrs)))
|
||
|
|
for _, a := range addrs {
|
||
|
|
b = xdr.AppendString(b, a.Netid)
|
||
|
|
b = xdr.AppendString(b, a.Uaddr)
|
||
|
|
}
|
||
|
|
return b
|
||
|
|
}
|
||
|
|
|
||
|
|
// A BlockVolume is one pnfs_block_volume4 of the emulated device: a
|
||
|
|
// simple volume with the given base offset and block count.
|
||
|
|
type BlockVolume struct {
|
||
|
|
// Type is PNFS_BLOCK_VOLUME_SIMPLE (1).
|
||
|
|
BaseOffset uint64
|
||
|
|
BlockCount uint64
|
||
|
|
DeviceID [16]byte
|
||
|
|
}
|
||
|
|
|
||
|
|
// AppendBlockDeviceAddr encodes the pnfs_block_deviceaddr4 with one
|
||
|
|
// simple volume, RFC 5663 section 5.1.
|
||
|
|
func AppendBlockDeviceAddr(b []byte, v BlockVolume) []byte {
|
||
|
|
b = xdr.AppendUint32(b, 1) // one volume
|
||
|
|
b = xdr.AppendUint32(b, 1) // PNFS_BLOCK_VOLUME_SIMPLE
|
||
|
|
b = append(b, v.DeviceID[:]...)
|
||
|
|
b = xdr.AppendUint64(b, v.BaseOffset)
|
||
|
|
return xdr.AppendUint64(b, v.BlockCount)
|
||
|
|
}
|
||
|
|
|
||
|
|
// AppendScsiLayoutBody encodes the pnfs_scsi_layout4: one extent over
|
||
|
|
// the emulated volume, RFC 8154 section 5.2.
|
||
|
|
func AppendScsiLayoutBody(b []byte, device [16]byte, fileOffset, length, storageOffset uint64) []byte {
|
||
|
|
b = xdr.AppendUint32(b, 1) // one extent
|
||
|
|
b = append(b, device[:]...)
|
||
|
|
b = xdr.AppendUint64(b, fileOffset)
|
||
|
|
b = xdr.AppendUint64(b, length)
|
||
|
|
b = xdr.AppendUint64(b, storageOffset)
|
||
|
|
return xdr.AppendUint32(b, 0) // reserved
|
||
|
|
}
|
||
|
|
|
||
|
|
// An ObjectLayout is the emulated pnfs_osd_layout4: the map header and
|
||
|
|
// one component credential over the emulated OSD.
|
||
|
|
type ObjectLayout struct {
|
||
|
|
NumComponents uint32
|
||
|
|
StripeUnit uint64
|
||
|
|
GroupWidth uint32
|
||
|
|
GroupDepth uint32
|
||
|
|
RefTagSize uint32
|
||
|
|
}
|
||
|
|
|
||
|
|
// AppendObjectLayoutBody encodes a minimal pnfs_osd_layout4 for the
|
||
|
|
// emulated OSD, RFC 5664 section 3.3: the data map and one component.
|
||
|
|
func AppendObjectLayoutBody(b []byte, device [16]byte, l ObjectLayout) []byte {
|
||
|
|
// pnfs_osd_data_map4
|
||
|
|
b = xdr.AppendUint32(b, 0) // odi_odm_raid_algorithm: PNFS_OSD_RAID_0
|
||
|
|
b = xdr.AppendUint64(b, l.StripeUnit)
|
||
|
|
b = xdr.AppendUint32(b, l.NumComponents)
|
||
|
|
b = xdr.AppendUint32(b, l.GroupWidth)
|
||
|
|
b = xdr.AppendUint32(b, l.GroupDepth)
|
||
|
|
b = xdr.AppendUint32(b, l.RefTagSize)
|
||
|
|
// olo_comps_index and the single component
|
||
|
|
b = xdr.AppendUint32(b, 0)
|
||
|
|
// pnfs_osd_object_cred4: version, credential type, opaque cred
|
||
|
|
b = xdr.AppendUint32(b, 0)
|
||
|
|
b = xdr.AppendUint32(b, 0)
|
||
|
|
b = xdr.AppendVarOpaque(b, nil)
|
||
|
|
// object id: partition id + object id
|
||
|
|
b = xdr.AppendUint64(b, 0)
|
||
|
|
b = xdr.AppendUint64(b, 1)
|
||
|
|
_ = device
|
||
|
|
return b
|
||
|
|
}
|
||
|
|
|
||
|
|
// AppendGetDeviceListRes encodes the GETDEVICELIST4resok: the cookie,
|
||
|
|
// its verifier, the device ids and the end of list flag, RFC 5661
|
||
|
|
// section 18.41.
|
||
|
|
func AppendGetDeviceListRes(b []byte, cookie uint64, cookieverf [8]byte, devices [][16]byte, eof bool) []byte {
|
||
|
|
b = xdr.AppendUint64(b, cookie)
|
||
|
|
b = append(b, cookieverf[:]...)
|
||
|
|
b = xdr.AppendUint32(b, uint32(len(devices)))
|
||
|
|
for _, d := range devices {
|
||
|
|
b = append(b, d[:]...)
|
||
|
|
}
|
||
|
|
return xdr.AppendBool(b, eof)
|
||
|
|
}
|
||
|
|
|
||
|
|
// AppendGetDeviceListArgs encodes the GETDEVICELIST argop: the layout
|
||
|
|
// type, the device count budget and the resumption cookie, RFC 5661
|
||
|
|
// section 18.41.
|
||
|
|
func AppendGetDeviceListArgs(b []byte, layoutType, maxDevices uint32, cookie uint64, cookieverf [8]byte) []byte {
|
||
|
|
b = xdr.AppendUint32(b, OpGetDeviceList)
|
||
|
|
b = xdr.AppendUint32(b, layoutType)
|
||
|
|
b = xdr.AppendUint32(b, maxDevices)
|
||
|
|
b = xdr.AppendUint64(b, cookie)
|
||
|
|
return append(b, cookieverf[:]...)
|
||
|
|
}
|
||
|
|
|
||
|
|
// LayoutTypeFlexFilesV2 is the layout type of the flexible file layout
|
||
|
|
// version 2: the layout type number 0x6 of the pNFS Layout Types
|
||
|
|
// registry, defined by the expired Internet-Draft
|
||
|
|
// draft-haynes-nfsv4-flex-filesv2-00. The draft never became an RFC;
|
||
|
|
// the number is stable and Linux clients carry the same one.
|
||
|
|
const LayoutTypeFlexFilesV2 = 6
|
||
|
|
|
||
|
|
// A FlexDataServerV2 is one ffv2_data_server4 of the draft: like the
|
||
|
|
// version one entry but with an array of stateids, the file handles
|
||
|
|
// per supported version, and the RPC credential the data server needs
|
||
|
|
// for loose coupling.
|
||
|
|
type FlexDataServerV2 struct {
|
||
|
|
DeviceID [16]byte
|
||
|
|
Efficiency uint32
|
||
|
|
Stateids []Stateid
|
||
|
|
FHs [][]byte
|
||
|
|
User string
|
||
|
|
Group string
|
||
|
|
AuthFlavor uint32
|
||
|
|
AuthBody []byte
|
||
|
|
}
|
||
|
|
|
||
|
|
// A FlexMirrorV2 is one ffv2_mirror4: the data servers of one mirror.
|
||
|
|
type FlexMirrorV2 struct {
|
||
|
|
DataServers []FlexDataServerV2
|
||
|
|
}
|
||
|
|
|
||
|
|
// AppendFlexFileLayoutBodyV2 encodes the ffv2_layout4 of
|
||
|
|
// draft-haynes-nfsv4-flex-filesv2-00 section 3.1: the stripe unit, the
|
||
|
|
// mirrors with their data servers, the flags and the stats hint. The
|
||
|
|
// device address is the version one ff_device_addr4, whose version
|
||
|
|
// array must carry one entry per stateid and per file handle.
|
||
|
|
func AppendFlexFileLayoutBodyV2(b []byte, flags uint32, stripeUnit uint64, mirrors []FlexMirrorV2) []byte {
|
||
|
|
b = xdr.AppendUint64(b, stripeUnit)
|
||
|
|
b = xdr.AppendUint32(b, uint32(len(mirrors)))
|
||
|
|
for _, m := range mirrors {
|
||
|
|
b = xdr.AppendUint32(b, uint32(len(m.DataServers)))
|
||
|
|
for _, ds := range m.DataServers {
|
||
|
|
b = append(b, ds.DeviceID[:]...)
|
||
|
|
b = xdr.AppendUint32(b, ds.Efficiency)
|
||
|
|
b = xdr.AppendUint32(b, uint32(len(ds.Stateids)))
|
||
|
|
for _, st := range ds.Stateids {
|
||
|
|
b = st.Append(b)
|
||
|
|
}
|
||
|
|
b = xdr.AppendUint32(b, uint32(len(ds.FHs)))
|
||
|
|
for _, fh := range ds.FHs {
|
||
|
|
b = xdr.AppendVarOpaque(b, fh)
|
||
|
|
}
|
||
|
|
b = xdr.AppendString(b, ds.User)
|
||
|
|
b = xdr.AppendString(b, ds.Group)
|
||
|
|
// opaque_auth: the flavour and the credential body.
|
||
|
|
b = xdr.AppendUint32(b, ds.AuthFlavor)
|
||
|
|
b = xdr.AppendVarOpaque(b, ds.AuthBody)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
b = xdr.AppendUint32(b, flags)
|
||
|
|
return xdr.AppendUint32(b, 0) // stats collect hint
|
||
|
|
}
|