add mft read method
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user