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