// Copyright (c) 2026 Petr BalvĂ­n (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) }