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.
84 lines
2.7 KiB
Go
84 lines
2.7 KiB
Go
3 years ago
|
/*
|
||
|
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
|
||
|
}
|