Files

56 lines
1.7 KiB
Go
Raw Permalink Normal View History

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