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,94 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package nfs4
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"sourcedock.dev/petrbalvin/nfs/internal/xdr"
|
||||
)
|
||||
|
||||
// maxBitmapWords is the word count limit of a bitmap4. The protocol caps
|
||||
// the array at 100 words; the attribute table today ends far below that.
|
||||
const maxBitmapWords = 100
|
||||
|
||||
// A Bitmap is a bitmap4: a compact set of attribute numbers.
|
||||
type Bitmap []uint32
|
||||
|
||||
// OfBits returns a Bitmap carrying exactly the given attribute numbers.
|
||||
func OfBits(bits ...uint32) Bitmap {
|
||||
if len(bits) == 0 {
|
||||
return Bitmap{}
|
||||
}
|
||||
var words Bitmap
|
||||
for _, n := range bits {
|
||||
w := int(n / 32)
|
||||
for len(words) <= w {
|
||||
words = append(words, 0)
|
||||
}
|
||||
words[w] |= 1 << (n % 32)
|
||||
}
|
||||
return words
|
||||
}
|
||||
|
||||
// Has reports whether the attribute number n is set.
|
||||
func (m Bitmap) Has(n uint32) bool {
|
||||
w := int(n / 32)
|
||||
return w < len(m) && m[w]&(1<<(n%32)) != 0
|
||||
}
|
||||
|
||||
// With returns a bitmap carrying n in addition to what was already set.
|
||||
// Words the original bitmap already held are never dropped, even when the
|
||||
// new bit lands in a lower word.
|
||||
func (m Bitmap) With(n uint32) Bitmap {
|
||||
w := int(n / 32)
|
||||
out := make(Bitmap, max(w+1, len(m)))
|
||||
copy(out, m)
|
||||
out[w] |= 1 << (n % 32)
|
||||
return out
|
||||
}
|
||||
|
||||
// AppendTo appends the bitmap in wire form: a count of words followed by
|
||||
// the words, with trailing zero words trimmed.
|
||||
func (m Bitmap) AppendTo(b []byte) []byte {
|
||||
trimmed := m
|
||||
for len(trimmed) > 0 && trimmed[len(trimmed)-1] == 0 {
|
||||
trimmed = trimmed[:len(trimmed)-1]
|
||||
}
|
||||
b = xdr.AppendUint32(b, uint32(len(trimmed)))
|
||||
for _, w := range trimmed {
|
||||
b = xdr.AppendUint32(b, w)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// ReadBitmap reads a bitmap4. A bitmap beyond the word limit is refused
|
||||
// before any of it is buffered.
|
||||
func ReadBitmap(d *xdr.Decoder) (Bitmap, error) {
|
||||
n, err := d.Uint32()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n > maxBitmapWords {
|
||||
return nil, xdr.ErrBadLength
|
||||
}
|
||||
m := make(Bitmap, 0, n)
|
||||
for range n {
|
||||
w, err := d.Uint32()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m = append(m, w)
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Words reports the words of the bitmap, without trailing zero words.
|
||||
func (m Bitmap) Words() []uint32 {
|
||||
trimmed := slices.Clone(m)
|
||||
for len(trimmed) > 0 && trimmed[len(trimmed)-1] == 0 {
|
||||
trimmed = trimmed[:len(trimmed)-1]
|
||||
}
|
||||
return trimmed
|
||||
}
|
||||
Reference in New Issue
Block a user