Files
nfs/internal/nfs4/dirdeleg.go
T

180 lines
5.3 KiB
Go
Raw Normal View History

// 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
}