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
244 lines
7.0 KiB
Go
244 lines
7.0 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package nfs4
|
|
|
|
import (
|
|
"slices"
|
|
|
|
"sourcedock.dev/petrbalvin/nfs/internal/xdr"
|
|
)
|
|
|
|
// Attribute numbers of the fattr4 table, RFC 8881 section 5. The numbers
|
|
// were verified against the XDR description of the standard rather than
|
|
// recalled: an off by one here shows up as garbage on the client.
|
|
const (
|
|
AttrSupportedAttrs = 0 // bitmap4
|
|
AttrType = 1 // enum
|
|
AttrFHExpireType = 2 // uint32
|
|
AttrChange = 3 // uint64
|
|
AttrSize = 4 // uint64
|
|
AttrLinkSupport = 5 // bool
|
|
AttrSymlinkSupport = 6 // bool
|
|
AttrNamedAttr = 7 // bool
|
|
AttrFSID = 8 // uint64, uint64
|
|
AttrUniqueHandles = 9 // bool
|
|
AttrLeaseTime = 10 // uint32
|
|
AttrRdattrError = 11 // enum
|
|
AttrFileHandle = 19 // opaque
|
|
AttrFileID = 20 // uint64
|
|
AttrMaxName = 29 // uint32
|
|
AttrMaxRead = 30 // uint64
|
|
AttrMaxWrite = 31 // uint64
|
|
AttrMode = 33 // uint32
|
|
AttrNumlinks = 35 // uint32
|
|
AttrOwner = 36 // string
|
|
AttrOwnerGroup = 37 // string
|
|
AttrRawDev = 41 // specdata
|
|
AttrSpaceUsed = 45 // uint64
|
|
AttrTimeAccessSet = 48 // settime4
|
|
AttrTimeModifySet = 54 // settime4
|
|
AttrTimeAccess = 47 // nfstime4
|
|
AttrTimeMetadata = 52 // nfstime4
|
|
AttrTimeModify = 53 // nfstime4
|
|
AttrMountedOnFileID = 55 // uint64
|
|
AttrSuppattrExclCreat = 75 // bitmap4
|
|
)
|
|
|
|
// SupportedAttrs is the attribute set this server advertises. A client asks
|
|
// only for attributes inside the set it read from here.
|
|
var SupportedAttrs = OfBits(
|
|
AttrSupportedAttrs,
|
|
AttrType,
|
|
AttrFHExpireType,
|
|
AttrChange,
|
|
AttrSize,
|
|
AttrLinkSupport,
|
|
AttrSymlinkSupport,
|
|
AttrNamedAttr,
|
|
AttrFSID,
|
|
AttrUniqueHandles,
|
|
AttrFileHandle,
|
|
AttrFileID,
|
|
AttrMaxName,
|
|
AttrMaxRead,
|
|
AttrMaxWrite,
|
|
AttrMode,
|
|
AttrNumlinks,
|
|
AttrOwner,
|
|
AttrOwnerGroup,
|
|
AttrSpaceUsed,
|
|
AttrTimeAccess,
|
|
AttrTimeMetadata,
|
|
AttrTimeModify,
|
|
AttrMountedOnFileID,
|
|
)
|
|
|
|
// ServerLimits carries the per export constants the advertised attributes
|
|
// report back.
|
|
type ServerLimits struct {
|
|
MaxName uint32
|
|
MaxRead uint64
|
|
MaxWrite uint64
|
|
}
|
|
|
|
// DefaultLimits are the limits the server advertises.
|
|
var DefaultLimits = ServerLimits{MaxName: 255, MaxRead: 1 << 22, MaxWrite: 1 << 22}
|
|
|
|
// Attrs holds the values the fattr4 builder emits. A zero field for an
|
|
// attribute the server does not have is simply not requested by a client
|
|
// that honoured the advertised set.
|
|
type Attrs struct {
|
|
Type uint32
|
|
FHExpireType uint32
|
|
Change uint64
|
|
Size uint64
|
|
LinkSupport bool
|
|
SymlinkSupport bool
|
|
NamedAttr bool
|
|
FSID [2]uint64
|
|
UniqueHandles bool
|
|
FileHandle []byte
|
|
FileID uint64
|
|
Mode uint32
|
|
Numlinks uint32
|
|
Owner string // decimal uid, RFC 8881 section 5.8
|
|
OwnerGroup string // decimal gid
|
|
SpaceUsed uint64
|
|
TimeAccess NfsTime
|
|
TimeMetadata NfsTime
|
|
TimeModify NfsTime
|
|
MountedOnFileID uint64
|
|
Limits ServerLimits
|
|
|
|
// FsLocations and FsLocationsInfo carry the pre encoded attributes
|
|
// for referral stubs; nil omits them.
|
|
FsLocations []byte
|
|
FsLocationsInfo []byte
|
|
}
|
|
|
|
// A NfsTime is an nfstime4: seconds and nanoseconds since the epoch.
|
|
type NfsTime struct {
|
|
Seconds int64
|
|
Nseconds uint32
|
|
}
|
|
|
|
// NfsTimeOf converts a Go time.
|
|
func NfsTimeOf(sec int64, nsec uint32) NfsTime { return NfsTime{sec, nsec} }
|
|
|
|
func appendNfsTime(b []byte, t NfsTime) []byte {
|
|
b = xdr.AppendInt64(b, t.Seconds)
|
|
return xdr.AppendUint32(b, t.Nseconds)
|
|
}
|
|
|
|
// AppendFattr emits an fattr4 for the requested attributes: the request
|
|
// bitmap filtered to what this server has, then the length prefixed list of
|
|
// values in numeric order, as the encoding requires.
|
|
func AppendFattr(b []byte, request Bitmap, a Attrs) []byte {
|
|
var body []byte
|
|
response := Bitmap{}
|
|
for _, n := range sortedBits(request) {
|
|
switch n {
|
|
case AttrSupportedAttrs:
|
|
body = SupportedAttrs.AppendTo(body)
|
|
response = response.With(n)
|
|
case AttrType:
|
|
body = xdr.AppendUint32(body, a.Type)
|
|
response = response.With(n)
|
|
case AttrFHExpireType:
|
|
body = xdr.AppendUint32(body, a.FHExpireType)
|
|
response = response.With(n)
|
|
case AttrChange:
|
|
body = xdr.AppendUint64(body, a.Change)
|
|
response = response.With(n)
|
|
case AttrSize:
|
|
body = xdr.AppendUint64(body, a.Size)
|
|
response = response.With(n)
|
|
case AttrLinkSupport:
|
|
body = xdr.AppendBool(body, a.LinkSupport)
|
|
response = response.With(n)
|
|
case AttrSymlinkSupport:
|
|
body = xdr.AppendBool(body, a.SymlinkSupport)
|
|
response = response.With(n)
|
|
case AttrNamedAttr:
|
|
body = xdr.AppendBool(body, a.NamedAttr)
|
|
response = response.With(n)
|
|
case AttrFSID:
|
|
body = xdr.AppendUint64(body, a.FSID[0])
|
|
body = xdr.AppendUint64(body, a.FSID[1])
|
|
response = response.With(n)
|
|
case AttrUniqueHandles:
|
|
body = xdr.AppendBool(body, a.UniqueHandles)
|
|
response = response.With(n)
|
|
case AttrFileHandle:
|
|
body = xdr.AppendVarOpaque(body, a.FileHandle)
|
|
response = response.With(n)
|
|
case AttrFileID:
|
|
body = xdr.AppendUint64(body, a.FileID)
|
|
response = response.With(n)
|
|
case AttrMaxName:
|
|
body = xdr.AppendUint32(body, a.Limits.MaxName)
|
|
response = response.With(n)
|
|
case AttrMaxRead:
|
|
body = xdr.AppendUint64(body, a.Limits.MaxRead)
|
|
response = response.With(n)
|
|
case AttrMaxWrite:
|
|
body = xdr.AppendUint64(body, a.Limits.MaxWrite)
|
|
response = response.With(n)
|
|
case AttrMode:
|
|
body = xdr.AppendUint32(body, a.Mode)
|
|
response = response.With(n)
|
|
case AttrNumlinks:
|
|
body = xdr.AppendUint32(body, a.Numlinks)
|
|
response = response.With(n)
|
|
case AttrOwner:
|
|
body = xdr.AppendString(body, a.Owner)
|
|
response = response.With(n)
|
|
case AttrOwnerGroup:
|
|
body = xdr.AppendString(body, a.OwnerGroup)
|
|
response = response.With(n)
|
|
case AttrSpaceUsed:
|
|
body = xdr.AppendUint64(body, a.SpaceUsed)
|
|
response = response.With(n)
|
|
case AttrTimeAccess:
|
|
body = appendNfsTime(body, a.TimeAccess)
|
|
response = response.With(n)
|
|
case AttrTimeMetadata:
|
|
body = appendNfsTime(body, a.TimeMetadata)
|
|
response = response.With(n)
|
|
case AttrTimeModify:
|
|
body = appendNfsTime(body, a.TimeModify)
|
|
response = response.With(n)
|
|
case AttrMountedOnFileID:
|
|
body = xdr.AppendUint64(body, a.MountedOnFileID)
|
|
response = response.With(n)
|
|
case AttrFsLocations:
|
|
if a.FsLocations != nil {
|
|
body = append(body, a.FsLocations...)
|
|
response = response.With(n)
|
|
}
|
|
case AttrFsLocationsInfo:
|
|
if a.FsLocationsInfo != nil {
|
|
body = append(body, a.FsLocationsInfo...)
|
|
response = response.With(n)
|
|
}
|
|
}
|
|
}
|
|
b = response.AppendTo(b)
|
|
return xdr.AppendVarOpaque(b, body)
|
|
}
|
|
|
|
// sortedBits lists the attribute numbers set in the bitmap, ascending.
|
|
func sortedBits(m Bitmap) []uint32 {
|
|
var out []uint32
|
|
for w, word := range m.Words() {
|
|
for bit := range 32 {
|
|
if word&(1<<bit) != 0 {
|
|
out = append(out, uint32(w*32+bit))
|
|
}
|
|
}
|
|
}
|
|
slices.Sort(out)
|
|
return out
|
|
}
|