feat: full NFSv4.2 server and client in pure Go
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
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
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user