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