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
285 lines
9.4 KiB
Go
285 lines
9.4 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
// The wire codecs of the NFSv4.2 operations, RFC 7862, with the shapes
|
|
// taken from the XDR of RFC 7863.
|
|
|
|
package nfs4
|
|
|
|
import (
|
|
"sourcedock.dev/petrbalvin/nfs/internal/xdr"
|
|
)
|
|
|
|
// Content types of the data_content4 enum, RFC 7862 section 8.2.
|
|
const (
|
|
ContentData = 0
|
|
ContentHole = 1
|
|
)
|
|
|
|
// Error codes the copy family answers with, RFC 7862 section 15.1.
|
|
const (
|
|
ErrOffloadDenied = 10091
|
|
ErrOffloadNoReqs = 10094
|
|
NfsSyncNothing = 0
|
|
NfsSyncDataSync = 1
|
|
NfsSyncFileSync = 2
|
|
OffloadNotReq = 0
|
|
OffloadSuccess = 1
|
|
OffloadDenied = 2
|
|
OffloadFatal = 3
|
|
OffloadNoFallback = 4
|
|
)
|
|
|
|
// AppendSeekArgs encodes the SEEK argop: the stateid, the offset and the
|
|
// content kind the client searches for.
|
|
func AppendSeekArgs(b []byte, st Stateid, offset uint64, what uint32) []byte {
|
|
b = xdr.AppendUint32(b, OpSeek)
|
|
b = st.Append(b)
|
|
b = xdr.AppendUint64(b, offset)
|
|
return xdr.AppendUint32(b, what)
|
|
}
|
|
|
|
// AppendSeekRes encodes a successful SEEK result: the end of file flag
|
|
// and the offset of the next content of the requested kind.
|
|
func AppendSeekRes(b []byte, eof bool, offset uint64) []byte {
|
|
b = xdr.AppendBool(b, eof)
|
|
return xdr.AppendUint64(b, offset)
|
|
}
|
|
|
|
// AppendAllocateArgs encodes the ALLOCATE argop: the reserved range.
|
|
func AppendAllocateArgs(b []byte, st Stateid, offset, length uint64) []byte {
|
|
b = xdr.AppendUint32(b, OpAllocate)
|
|
b = st.Append(b)
|
|
b = xdr.AppendUint64(b, offset)
|
|
return xdr.AppendUint64(b, length)
|
|
}
|
|
|
|
// AppendDeallocateArgs encodes the DEALLOCATE argop: the released range.
|
|
func AppendDeallocateArgs(b []byte, st Stateid, offset, length uint64) []byte {
|
|
b = xdr.AppendUint32(b, OpDeallocate)
|
|
b = st.Append(b)
|
|
b = xdr.AppendUint64(b, offset)
|
|
return xdr.AppendUint64(b, length)
|
|
}
|
|
|
|
// AppendIoAdviseArgs encodes the IO_ADVISE argop: the range and the hint
|
|
// bitmap.
|
|
func AppendIoAdviseArgs(b []byte, st Stateid, offset, length uint64, hints Bitmap) []byte {
|
|
b = xdr.AppendUint32(b, OpIoAdvise)
|
|
b = st.Append(b)
|
|
b = xdr.AppendUint64(b, offset)
|
|
b = xdr.AppendUint64(b, length)
|
|
return hints.AppendTo(b)
|
|
}
|
|
|
|
// AppendIoAdviseRes encodes a successful IO_ADVISE result: the hints the
|
|
// server accepted.
|
|
func AppendIoAdviseRes(b []byte, hints Bitmap) []byte {
|
|
return hints.AppendTo(b)
|
|
}
|
|
|
|
// A CopySourceServer is one netloc4 of the COPY argument list: the
|
|
// servers the source lives on.
|
|
type CopySourceServer struct {
|
|
Type uint32
|
|
Name string
|
|
Addr NetAddr
|
|
}
|
|
|
|
// AppendCopyArgs encodes the COPY argop: the source and destination
|
|
// stateids, the ranges and the source server list.
|
|
func AppendCopyArgs(b []byte, src, dst Stateid, srcOff, dstOff, count uint64, consecutive, synchronous bool, sources []CopySourceServer) []byte {
|
|
b = xdr.AppendUint32(b, OpCopy)
|
|
b = src.Append(b)
|
|
b = dst.Append(b)
|
|
b = xdr.AppendUint64(b, srcOff)
|
|
b = xdr.AppendUint64(b, dstOff)
|
|
b = xdr.AppendUint64(b, count)
|
|
b = xdr.AppendBool(b, consecutive)
|
|
b = xdr.AppendBool(b, synchronous)
|
|
b = xdr.AppendUint32(b, uint32(len(sources)))
|
|
for _, s := range sources {
|
|
b = xdr.AppendUint32(b, s.Type)
|
|
switch s.Type {
|
|
case 1, 2:
|
|
b = xdr.AppendString(b, s.Name)
|
|
case 3:
|
|
b = xdr.AppendString(b, s.Addr.Netid)
|
|
b = xdr.AppendString(b, s.Addr.Uaddr)
|
|
}
|
|
}
|
|
return b
|
|
}
|
|
|
|
// AppendWriteResponse encodes the write_response4 the copy family
|
|
// answers with.
|
|
func AppendWriteResponse(b []byte, callbackSt Stateid, callbackSet bool, count uint64, committed uint32, verifier [8]byte) []byte {
|
|
if callbackSet {
|
|
b = xdr.AppendUint32(b, 1)
|
|
b = callbackSt.Append(b)
|
|
} else {
|
|
b = xdr.AppendUint32(b, 0)
|
|
}
|
|
b = xdr.AppendUint64(b, count)
|
|
b = xdr.AppendUint32(b, committed)
|
|
return append(b, verifier[:]...)
|
|
}
|
|
|
|
// AppendCopyRes encodes a successful COPY result: the write response and
|
|
// the requirements the server met.
|
|
func AppendCopyRes(b []byte, callbackSt Stateid, callbackSet bool, count uint64, committed uint32, verifier [8]byte, consecutive, synchronous bool) []byte {
|
|
b = AppendWriteResponse(b, callbackSt, callbackSet, count, committed, verifier)
|
|
b = xdr.AppendBool(b, consecutive)
|
|
return xdr.AppendBool(b, synchronous)
|
|
}
|
|
|
|
// AppendCopyNotifyArgs encodes the COPY_NOTIFY argop: the source stateid
|
|
// and the destination the client copies to.
|
|
func AppendCopyNotifyArgs(b []byte, st Stateid, dstType uint32, dstName string, dstAddr NetAddr) []byte {
|
|
b = xdr.AppendUint32(b, OpCopyNotify)
|
|
b = st.Append(b)
|
|
b = xdr.AppendUint32(b, dstType)
|
|
switch dstType {
|
|
case 1, 2:
|
|
b = xdr.AppendString(b, dstName)
|
|
case 3:
|
|
b = xdr.AppendString(b, dstAddr.Netid)
|
|
b = xdr.AppendString(b, dstAddr.Uaddr)
|
|
}
|
|
return b
|
|
}
|
|
|
|
// AppendCopyNotifyRes encodes a successful COPY_NOTIFY result: the lease
|
|
// the authorization lives, the stateid naming it and the source servers.
|
|
func AppendCopyNotifyRes(b []byte, leaseSeconds int64, st Stateid, sources []CopySourceServer) []byte {
|
|
b = xdr.AppendInt64(b, leaseSeconds)
|
|
b = xdr.AppendUint32(b, 0)
|
|
b = st.Append(b)
|
|
b = xdr.AppendUint32(b, uint32(len(sources)))
|
|
for _, s := range sources {
|
|
b = xdr.AppendUint32(b, s.Type)
|
|
switch s.Type {
|
|
case 1, 2:
|
|
b = xdr.AppendString(b, s.Name)
|
|
case 3:
|
|
b = xdr.AppendString(b, s.Addr.Netid)
|
|
b = xdr.AppendString(b, s.Addr.Uaddr)
|
|
}
|
|
}
|
|
return b
|
|
}
|
|
|
|
// AppendOffloadCancelArgs encodes the OFFLOAD_CANCEL argop: the stateid
|
|
// of the copy to abandon.
|
|
func AppendOffloadCancelArgs(b []byte, st Stateid) []byte {
|
|
b = xdr.AppendUint32(b, OpOffloadCancel)
|
|
return st.Append(b)
|
|
}
|
|
|
|
// AppendOffloadStatusArgs encodes the OFFLOAD_STATUS argop: the stateid
|
|
// of the copy in flight.
|
|
func AppendOffloadStatusArgs(b []byte, st Stateid) []byte {
|
|
b = xdr.AppendUint32(b, OpOffloadStatus)
|
|
return st.Append(b)
|
|
}
|
|
|
|
// AppendOffloadStatusRes encodes a successful OFFLOAD_STATUS result: the
|
|
// bytes copied so far and the completion code.
|
|
func AppendOffloadStatusRes(b []byte, count uint64, complete uint32) []byte {
|
|
b = xdr.AppendUint64(b, count)
|
|
return xdr.AppendUint32(b, complete)
|
|
}
|
|
|
|
// AppendCloneArgs encodes the CLONE argop: the ranges of the clone. The
|
|
// saved file handle is the source, the current one the destination.
|
|
func AppendCloneArgs(b []byte, src, dst Stateid, srcOff, dstOff, count uint64) []byte {
|
|
b = xdr.AppendUint32(b, OpClone)
|
|
b = src.Append(b)
|
|
b = dst.Append(b)
|
|
b = xdr.AppendUint64(b, srcOff)
|
|
b = xdr.AppendUint64(b, dstOff)
|
|
return xdr.AppendUint64(b, count)
|
|
}
|
|
|
|
// AppendLayoutErrorArgs encodes the LAYOUTERROR argop: the failing range
|
|
// and one error per device.
|
|
func AppendLayoutErrorArgs(b []byte, offset, length uint64, st Stateid, device [16]byte, status, opnum uint32) []byte {
|
|
b = xdr.AppendUint32(b, OpLayoutError)
|
|
b = xdr.AppendUint64(b, offset)
|
|
b = xdr.AppendUint64(b, length)
|
|
b = st.Append(b)
|
|
b = xdr.AppendUint32(b, 1)
|
|
b = append(b, device[:]...)
|
|
b = xdr.AppendUint32(b, status)
|
|
return xdr.AppendUint32(b, opnum)
|
|
}
|
|
|
|
// AppendLayoutStatsArgs encodes the LAYOUTSTATS argop: the reported range
|
|
// and the IO counters of the data server.
|
|
func AppendLayoutStatsArgs(b []byte, offset, length uint64, st Stateid, readCount, readBytes, writeCount, writeBytes uint64, device [16]byte, updateType uint32, updateBody []byte) []byte {
|
|
b = xdr.AppendUint32(b, OpLayoutStats)
|
|
b = xdr.AppendUint64(b, offset)
|
|
b = xdr.AppendUint64(b, length)
|
|
b = st.Append(b)
|
|
b = xdr.AppendUint64(b, readCount)
|
|
b = xdr.AppendUint64(b, readBytes)
|
|
b = xdr.AppendUint64(b, writeCount)
|
|
b = xdr.AppendUint64(b, writeBytes)
|
|
b = append(b, device[:]...)
|
|
b = xdr.AppendUint32(b, updateType)
|
|
return xdr.AppendVarOpaque(b, updateBody)
|
|
}
|
|
|
|
// AppendReadPlusArgs encodes the READ_PLUS argop: the range to read.
|
|
func AppendReadPlusArgs(b []byte, st Stateid, offset uint64, count uint32) []byte {
|
|
b = xdr.AppendUint32(b, OpReadPlus)
|
|
b = st.Append(b)
|
|
b = xdr.AppendUint64(b, offset)
|
|
return xdr.AppendUint32(b, count)
|
|
}
|
|
|
|
// AppendReadPlusDataRes encodes a successful READ_PLUS result that
|
|
// carries one data segment.
|
|
func AppendReadPlusDataRes(b []byte, eof bool, offset uint64, data []byte) []byte {
|
|
b = xdr.AppendBool(b, eof)
|
|
b = xdr.AppendUint32(b, 1)
|
|
b = xdr.AppendUint32(b, ContentData)
|
|
b = xdr.AppendUint64(b, offset)
|
|
return xdr.AppendVarOpaque(b, data)
|
|
}
|
|
|
|
// AppendReadPlusHoleRes encodes a successful READ_PLUS result that
|
|
// reports a hole.
|
|
func AppendReadPlusHoleRes(b []byte, eof bool, offset, length uint64) []byte {
|
|
b = xdr.AppendBool(b, eof)
|
|
b = xdr.AppendUint32(b, 1)
|
|
b = xdr.AppendUint32(b, ContentHole)
|
|
b = xdr.AppendUint64(b, offset)
|
|
return xdr.AppendUint64(b, length)
|
|
}
|
|
|
|
// AppendWriteSameArgs encodes the WRITE_SAME argop: the stateid, the
|
|
// stability and the application data block to write. The block number
|
|
// is a count4 on the wire, RFC 7862 section 15.8.
|
|
func AppendWriteSameArgs(b []byte, st Stateid, stable uint32, offset, blockSize, blockCount, relOffBlocknum uint64, blockNum uint32, relOffPattern uint64, pattern []byte) []byte {
|
|
b = xdr.AppendUint32(b, OpWriteSame)
|
|
b = st.Append(b)
|
|
b = xdr.AppendUint32(b, stable)
|
|
b = xdr.AppendUint64(b, offset)
|
|
b = xdr.AppendUint64(b, blockSize)
|
|
b = xdr.AppendUint64(b, blockCount)
|
|
b = xdr.AppendUint64(b, relOffBlocknum)
|
|
b = xdr.AppendUint32(b, blockNum)
|
|
b = xdr.AppendUint64(b, relOffPattern)
|
|
return xdr.AppendVarOpaque(b, pattern)
|
|
}
|
|
|
|
// AppendWriteSameRes encodes a successful WRITE_SAME result: the write
|
|
// response alone.
|
|
func AppendWriteSameRes(b []byte, count uint64, committed uint32, verifier [8]byte) []byte {
|
|
b = xdr.AppendUint32(b, 0)
|
|
b = xdr.AppendUint64(b, count)
|
|
b = xdr.AppendUint32(b, committed)
|
|
return append(b, verifier[:]...)
|
|
}
|