Files
petrbalvin a9b8039ef7
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
feat: full NFSv4.2 server and client in pure Go
Assisted-by: GLM 5.3 Flash
2026-09-21 18:51:17 +02:00

56 lines
1.7 KiB
Go

// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: MIT
package nfsfs
import (
"errors"
"fmt"
"io"
)
// A ReadIntoer is the optional read half that fills a caller provided
// buffer instead of allocating its own: the reply path of a server reads
// straight into the buffer it is about to send. A backend that carries
// only Read keeps its allocating behaviour.
type ReadIntoer interface {
// ReadInto reads up to len(buf) bytes at the offset into buf and
// answers how many landed and whether the end of file was reached.
ReadInto(h Handle, off int64, buf []byte) (int, bool, error)
}
// ReadInto fills buf from the regular file the handle names. The
// descriptor comes from the cache or a fresh verified open, and the
// identity is revalidated before anything is read, exactly as Read.
func (l *Local) ReadInto(h Handle, off int64, buf []byte) (int, bool, error) {
f, release, err := l.dataFD(h, false)
if err != nil {
return 0, false, err
}
defer release()
n, rerr := f.ReadAt(buf, off)
switch {
case errors.Is(rerr, io.EOF):
return n, true, nil
case rerr != nil:
return n, false, fmt.Errorf("%w: %v", ErrIO, rerr)
}
// A full buffer proves the end of file only against the size; the
// descriptor's own stat answers it without a second path walk.
if fst, serr := f.Stat(); serr == nil {
return n, int64(off)+int64(n) >= fst.Size(), nil
}
return n, false, nil
}
// ReadInto forwards to the wrapped export: a read only export reads as
// its backend does.
func (ro roFS) ReadInto(h Handle, off int64, buf []byte) (int, bool, error) {
ri, ok := ro.FS.(ReadIntoer)
if !ok {
data, err := ro.Read(h, off, len(buf))
return len(data), false, err
}
return ri.ReadInto(h, off, buf)
}