You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
wincmd/ntfs/fragment/reader.go

84 lines
2.7 KiB
Go

/*
Package fragment contains a Reader which can read Fragments which may be scattered around a volume (and perhaps even
not in sequence). Typically these could be translated from MFT attribute DataRuns. To convert MFT attribute DataRuns
to Fragments for use in the fragment Reader, use mft.DataRunsToFragments().
Implementation notes
When the fragment Reader is near the end of a fragment and a Read() call requests more data than what is left in
the current fragment, the Reader will exhaust only the current fragment and return that data (which could be less
than len(p)). A next Read() call will then seek to the next fragment and continue reading there. When the last
fragment is exhausted by a Read(), it will return the remaining bytes read and a nil error. Any subsequent Read()
calls after that will return 0, io.EOF.
When accessing a new fragment, the Reader will seek using the absolute Length in the fragment from the start
of the contained io.ReadSeeker (using io.SeekStart).
*/
package fragment
import (
"fmt"
"io"
"os"
)
// Fragment contains an absolute Offset in bytes from the start of a volume and a Length of the fragment, also in bytes.
type Fragment struct {
Offset int64
Length int64
}
// A fragment Reader will read data from the fragments in order. When one fragment is depleted, it will seek to the
// position of the next fragment and continue reading from there, until all fragments have been exhausted. When the last
// fragment has been exhaused, each subsequent Read() will return io.EOF.
type Reader struct {
src io.ReadSeeker
fragments []Fragment
idx int
remaining int64
file *os.File
}
// NewReader initializes a new Reader from the io.ReaderSeeker and fragments and returns a pointer to. Note that
// fragments may not be sequential in order, so the io.ReadSeeker should support seeking backwards (or rather, from the
// start).
func NewReader(src io.ReadSeeker, fragments []Fragment) *Reader {
return &Reader{src: src, fragments: fragments, idx: -1, remaining: 0}
}
func (r *Reader) Read(p []byte) (n int, err error) {
if r.idx >= len(r.fragments) {
r.src.(*os.File).Close()
return 0, io.EOF
}
if len(p) == 0 {
return 0, nil
}
if r.remaining == 0 {
r.idx++
if r.idx >= len(r.fragments) {
return 0, io.EOF
}
next := r.fragments[r.idx]
r.remaining = next.Length
seeked, err := r.src.Seek(next.Offset, io.SeekStart)
if err != nil {
return 0, fmt.Errorf("unable to seek to next offset %d: %v", next.Offset, err)
}
if seeked != next.Offset {
return 0, fmt.Errorf("wanted to seek to %d but reached %d", next.Offset, seeked)
}
}
target := p
if int64(len(p)) > r.remaining {
target = p[:r.remaining]
}
n, err = io.ReadFull(r.src, target)
r.remaining -= int64(n)
return n, err
}