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