forked from TrueCloudLab/distribution
Refactor layerReader into fileReader
This change separates out the remote file reader functionality from layer reprsentation data. More importantly, issues with seeking have been fixed and thoroughly tested.
This commit is contained in:
parent
74279723c0
commit
3f479b62b4
8 changed files with 344 additions and 191 deletions
|
@ -1,10 +1,6 @@
|
|||
package storage
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker-registry/digest"
|
||||
|
@ -13,22 +9,11 @@ import (
|
|||
// layerReadSeeker implements Layer and provides facilities for reading and
|
||||
// seeking.
|
||||
type layerReader struct {
|
||||
layerStore *layerStore
|
||||
rc io.ReadCloser
|
||||
brd *bufio.Reader
|
||||
fileReader
|
||||
|
||||
name string // repo name of this layer
|
||||
digest digest.Digest
|
||||
path string
|
||||
createdAt time.Time
|
||||
|
||||
// offset is the current read offset
|
||||
offset int64
|
||||
|
||||
// size is the total layer size, if available.
|
||||
size int64
|
||||
|
||||
closedErr error // terminal error, if set, reader is closed
|
||||
}
|
||||
|
||||
var _ Layer = &layerReader{}
|
||||
|
@ -44,131 +29,3 @@ func (lrs *layerReader) Digest() digest.Digest {
|
|||
func (lrs *layerReader) CreatedAt() time.Time {
|
||||
return lrs.createdAt
|
||||
}
|
||||
|
||||
func (lrs *layerReader) Read(p []byte) (n int, err error) {
|
||||
if err := lrs.closed(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
rd, err := lrs.reader()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
n, err = rd.Read(p)
|
||||
lrs.offset += int64(n)
|
||||
|
||||
// Simulate io.EOR error if we reach filesize.
|
||||
if err == nil && lrs.offset >= lrs.size {
|
||||
err = io.EOF
|
||||
}
|
||||
|
||||
// TODO(stevvooe): More error checking is required here. If the reader
|
||||
// times out for some reason, we should reset the reader so we re-open the
|
||||
// connection.
|
||||
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (lrs *layerReader) Seek(offset int64, whence int) (int64, error) {
|
||||
if err := lrs.closed(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var err error
|
||||
newOffset := lrs.offset
|
||||
|
||||
switch whence {
|
||||
case os.SEEK_CUR:
|
||||
newOffset += int64(whence)
|
||||
case os.SEEK_END:
|
||||
newOffset = lrs.size + int64(whence)
|
||||
case os.SEEK_SET:
|
||||
newOffset = int64(whence)
|
||||
}
|
||||
|
||||
if newOffset < 0 {
|
||||
err = fmt.Errorf("cannot seek to negative position")
|
||||
} else if newOffset >= lrs.size {
|
||||
err = fmt.Errorf("cannot seek passed end of layer")
|
||||
} else {
|
||||
if lrs.offset != newOffset {
|
||||
lrs.resetReader()
|
||||
}
|
||||
|
||||
// No problems, set the offset.
|
||||
lrs.offset = newOffset
|
||||
}
|
||||
|
||||
return lrs.offset, err
|
||||
}
|
||||
|
||||
// Close the layer. Should be called when the resource is no longer needed.
|
||||
func (lrs *layerReader) Close() error {
|
||||
if lrs.closedErr != nil {
|
||||
return lrs.closedErr
|
||||
}
|
||||
// TODO(sday): Must export this error.
|
||||
lrs.closedErr = fmt.Errorf("layer closed")
|
||||
|
||||
// close and release reader chain
|
||||
if lrs.rc != nil {
|
||||
lrs.rc.Close()
|
||||
lrs.rc = nil
|
||||
}
|
||||
lrs.brd = nil
|
||||
|
||||
return lrs.closedErr
|
||||
}
|
||||
|
||||
// reader prepares the current reader at the lrs offset, ensuring its buffered
|
||||
// and ready to go.
|
||||
func (lrs *layerReader) reader() (io.Reader, error) {
|
||||
if err := lrs.closed(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if lrs.rc != nil {
|
||||
return lrs.brd, nil
|
||||
}
|
||||
|
||||
// If we don't have a reader, open one up.
|
||||
rc, err := lrs.layerStore.driver.ReadStream(lrs.path, uint64(lrs.offset))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lrs.rc = rc
|
||||
|
||||
if lrs.brd == nil {
|
||||
// TODO(stevvooe): Set an optimal buffer size here. We'll have to
|
||||
// understand the latency characteristics of the underlying network to
|
||||
// set this correctly, so we may want to leave it to the driver. For
|
||||
// out of process drivers, we'll have to optimize this buffer size for
|
||||
// local communication.
|
||||
lrs.brd = bufio.NewReader(lrs.rc)
|
||||
} else {
|
||||
lrs.brd.Reset(lrs.rc)
|
||||
}
|
||||
|
||||
return lrs.brd, nil
|
||||
}
|
||||
|
||||
// resetReader resets the reader, forcing the read method to open up a new
|
||||
// connection and rebuild the buffered reader. This should be called when the
|
||||
// offset and the reader will become out of sync, such as during a seek
|
||||
// operation.
|
||||
func (lrs *layerReader) resetReader() {
|
||||
if err := lrs.closed(); err != nil {
|
||||
return
|
||||
}
|
||||
if lrs.rc != nil {
|
||||
lrs.rc.Close()
|
||||
lrs.rc = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (lrs *layerReader) closed() error {
|
||||
return lrs.closedErr
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue