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
56 lines
1.7 KiB
Go
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)
|
|
}
|