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
180 lines
5.3 KiB
Go
180 lines
5.3 KiB
Go
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
// Directory delegations and the notification family: the wire codecs of
|
|
// GET_DIR_DELEGATION (RFC 8881 section 18.39), CB_NOTIFY and
|
|
// CB_NOTIFY_LOCK (RFC 8881 section 20), with the shapes taken from the
|
|
// XDR of RFC 7863.
|
|
|
|
package nfs4
|
|
|
|
import (
|
|
"sourcedock.dev/petrbalvin/nfs/internal/xdr"
|
|
)
|
|
|
|
// Notification types of the notify_type4 enum.
|
|
const (
|
|
NotifyChangeChildAttrs = 0
|
|
NotifyChangeDirAttrs = 1
|
|
NotifyRemoveEntry = 2
|
|
NotifyAddEntry = 3
|
|
NotifyRenameEntry = 4
|
|
NotifyChangeCookieVerf = 5
|
|
)
|
|
|
|
// Statuses of the gddrnf4_status union.
|
|
const (
|
|
GddOK = 0
|
|
GddUnavail = 1
|
|
)
|
|
|
|
// A Notify4 is one notify4: the kind of the change and the encoded
|
|
// change body.
|
|
type Notify4 struct {
|
|
Mask Bitmap
|
|
Vals []byte
|
|
}
|
|
|
|
// A CBNotify is the decoded CB_NOTIFY4args.
|
|
type CBNotify struct {
|
|
Stateid Stateid
|
|
FH []byte
|
|
Changes []Notify4
|
|
}
|
|
|
|
// A CBNotifyLock is the decoded CB_NOTIFY_LOCK4args.
|
|
type CBNotifyLock struct {
|
|
FH []byte
|
|
Clientid uint64
|
|
Owner []byte
|
|
}
|
|
|
|
// AppendGetDirDelegationArgs encodes the GET_DIR_DELEGATION4args: the
|
|
// signal flag, the notification types, the attr delay hints and the
|
|
// requested attribute bitmaps.
|
|
func AppendGetDirDelegationArgs(b []byte, signalAvail bool, notifyTypes Bitmap, childDelay, dirDelay NfsTime, childAttrs, dirAttrs Bitmap) []byte {
|
|
b = xdr.AppendUint32(b, OpGetDirDelegation)
|
|
b = xdr.AppendBool(b, signalAvail)
|
|
b = notifyTypes.AppendTo(b)
|
|
b = appendNfsTime(b, childDelay)
|
|
b = appendNfsTime(b, dirDelay)
|
|
b = childAttrs.AppendTo(b)
|
|
return dirAttrs.AppendTo(b)
|
|
}
|
|
|
|
// AppendGetDirDelegationResOK encodes the delegation granted: the
|
|
// verifier, the stateid, the supported notifications and the attribute
|
|
// bitmaps the server promises.
|
|
func AppendGetDirDelegationResOK(b []byte, cookieverf [8]byte, st Stateid, notification, childAttrs, dirAttrs Bitmap) []byte {
|
|
b = xdr.AppendUint32(b, GddOK)
|
|
b = append(b, cookieverf[:]...)
|
|
b = st.Append(b)
|
|
b = notification.AppendTo(b)
|
|
b = childAttrs.AppendTo(b)
|
|
return dirAttrs.AppendTo(b)
|
|
}
|
|
|
|
// AppendGetDirDelegationResUnavail encodes the no delegation answer: the
|
|
// server promises to signal when one becomes available.
|
|
func AppendGetDirDelegationResUnavail(b []byte, willSignal bool) []byte {
|
|
b = xdr.AppendUint32(b, GddUnavail)
|
|
return xdr.AppendBool(b, willSignal)
|
|
}
|
|
|
|
// appendNotifyEntry encodes one notify_entry4: the name and the full
|
|
// attribute list, which this build carries empty.
|
|
func appendNotifyEntry(b []byte, name string) []byte {
|
|
b = xdr.AppendString(b, name)
|
|
return AppendFattr(b, Bitmap{}, Attrs{})
|
|
}
|
|
|
|
// AppendNotifyAdd encodes the notify_add4 body: the optional removed
|
|
// source of a rename, the new entry, its cookie, the optional previous
|
|
// entry and the end of page flag.
|
|
func AppendNotifyAdd(b []byte, name string, cookie uint64, lastEntry bool) []byte {
|
|
b = xdr.AppendUint32(b, 0) // nad_old_entry<1>: none
|
|
b = appendNotifyEntry(b, name)
|
|
b = xdr.AppendUint32(b, 0) // nad_new_entry_cookie<1>: none
|
|
b = xdr.AppendUint32(b, 0) // nad_prev_entry<1>: none
|
|
return xdr.AppendBool(b, lastEntry)
|
|
}
|
|
|
|
// AppendNotifyRemove encodes the notify_remove4 body: the removed entry
|
|
// and the cookie the client resumes past it from.
|
|
func AppendNotifyRemove(b []byte, name string, cookie uint64) []byte {
|
|
b = appendNotifyEntry(b, name)
|
|
return xdr.AppendUint64(b, cookie)
|
|
}
|
|
|
|
// AppendNotify4 encodes one notify4: the mask and the encoded body.
|
|
func AppendNotify4(b []byte, mask Bitmap, vals []byte) []byte {
|
|
b = mask.AppendTo(b)
|
|
return xdr.AppendVarOpaque(b, vals)
|
|
}
|
|
|
|
// AppendCBNotifyArgs encodes the CB_NOTIFY4args: the delegation stateid,
|
|
// the directory and the changes.
|
|
func AppendCBNotifyArgs(b []byte, st Stateid, fh []byte, changes []Notify4) []byte {
|
|
b = xdr.AppendUint32(b, OpCBNotify)
|
|
b = st.Append(b)
|
|
b = xdr.AppendVarOpaque(b, fh)
|
|
b = xdr.AppendUint32(b, uint32(len(changes)))
|
|
for _, c := range changes {
|
|
b = AppendNotify4(b, c.Mask, c.Vals)
|
|
}
|
|
return b
|
|
}
|
|
|
|
// DecodeCBNotifyArgs decodes the CB_NOTIFY4args the client receives.
|
|
func DecodeCBNotifyArgs(d *xdr.Decoder) (CBNotify, error) {
|
|
var out CBNotify
|
|
raw, err := d.Raw(16)
|
|
if err != nil {
|
|
return out, err
|
|
}
|
|
copy(out.Stateid[:], raw)
|
|
if out.FH, err = d.VarOpaque(); err != nil {
|
|
return out, err
|
|
}
|
|
n, err := d.Uint32()
|
|
if err != nil {
|
|
return out, err
|
|
}
|
|
for range n {
|
|
var c Notify4
|
|
if c.Mask, err = ReadBitmap(d); err != nil {
|
|
return out, err
|
|
}
|
|
if c.Vals, err = d.VarOpaque(); err != nil {
|
|
return out, err
|
|
}
|
|
out.Changes = append(out.Changes, c)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// AppendCBNotifyLockArgs encodes the CB_NOTIFY_LOCK4args: the file and
|
|
// the lock owner whose denied lock may be available again.
|
|
func AppendCBNotifyLockArgs(b []byte, fh []byte, clientid uint64, owner []byte) []byte {
|
|
b = xdr.AppendUint32(b, OpCBNotifyLock)
|
|
b = xdr.AppendVarOpaque(b, fh)
|
|
b = xdr.AppendUint64(b, clientid)
|
|
return xdr.AppendVarOpaque(b, owner)
|
|
}
|
|
|
|
// DecodeCBNotifyLockArgs decodes the CB_NOTIFY_LOCK4args the client
|
|
// receives.
|
|
func DecodeCBNotifyLockArgs(d *xdr.Decoder) (CBNotifyLock, error) {
|
|
var out CBNotifyLock
|
|
fh, err := d.VarOpaque()
|
|
if err != nil {
|
|
return out, err
|
|
}
|
|
out.FH = fh
|
|
if out.Clientid, err = d.Uint64(); err != nil {
|
|
return out, err
|
|
}
|
|
out.Owner, err = d.VarOpaque()
|
|
return out, err
|
|
}
|