github.com/lusis/distribution@v2.0.1+incompatible/registry/storage/filereader.go (about)

     1  package storage
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"os"
    10  	"time"
    11  
    12  	storagedriver "github.com/docker/distribution/registry/storage/driver"
    13  )
    14  
    15  // TODO(stevvooe): Set an optimal buffer size here. We'll have to
    16  // understand the latency characteristics of the underlying network to
    17  // set this correctly, so we may want to leave it to the driver. For
    18  // out of process drivers, we'll have to optimize this buffer size for
    19  // local communication.
    20  const fileReaderBufferSize = 4 << 20
    21  
    22  // remoteFileReader provides a read seeker interface to files stored in
    23  // storagedriver. Used to implement part of layer interface and will be used
    24  // to implement read side of LayerUpload.
    25  type fileReader struct {
    26  	driver storagedriver.StorageDriver
    27  
    28  	// identifying fields
    29  	path    string
    30  	size    int64     // size is the total size, must be set.
    31  	modtime time.Time // TODO(stevvooe): This is not needed anymore.
    32  
    33  	// mutable fields
    34  	rc     io.ReadCloser // remote read closer
    35  	brd    *bufio.Reader // internal buffered io
    36  	offset int64         // offset is the current read offset
    37  	err    error         // terminal error, if set, reader is closed
    38  }
    39  
    40  // newFileReader initializes a file reader for the remote file. The read takes
    41  // on the offset and size at the time the reader is created. If the underlying
    42  // file changes, one must create a new fileReader.
    43  func newFileReader(driver storagedriver.StorageDriver, path string) (*fileReader, error) {
    44  	rd := &fileReader{
    45  		driver: driver,
    46  		path:   path,
    47  	}
    48  
    49  	// Grab the size of the layer file, ensuring existence.
    50  	if fi, err := driver.Stat(path); err != nil {
    51  		switch err := err.(type) {
    52  		case storagedriver.PathNotFoundError:
    53  			// NOTE(stevvooe): We really don't care if the file is not
    54  			// actually present for the reader. If the caller needs to know
    55  			// whether or not the file exists, they should issue a stat call
    56  			// on the path. There is still no guarantee, since the file may be
    57  			// gone by the time the reader is created. The only correct
    58  			// behavior is to return a reader that immediately returns EOF.
    59  		default:
    60  			// Any other error we want propagated up the stack.
    61  			return nil, err
    62  		}
    63  	} else {
    64  		if fi.IsDir() {
    65  			return nil, fmt.Errorf("cannot read a directory")
    66  		}
    67  
    68  		// Fill in file information
    69  		rd.size = fi.Size()
    70  		rd.modtime = fi.ModTime()
    71  	}
    72  
    73  	return rd, nil
    74  }
    75  
    76  func (fr *fileReader) Read(p []byte) (n int, err error) {
    77  	if fr.err != nil {
    78  		return 0, fr.err
    79  	}
    80  
    81  	rd, err := fr.reader()
    82  	if err != nil {
    83  		return 0, err
    84  	}
    85  
    86  	n, err = rd.Read(p)
    87  	fr.offset += int64(n)
    88  
    89  	// Simulate io.EOR error if we reach filesize.
    90  	if err == nil && fr.offset >= fr.size {
    91  		err = io.EOF
    92  	}
    93  
    94  	return n, err
    95  }
    96  
    97  func (fr *fileReader) Seek(offset int64, whence int) (int64, error) {
    98  	if fr.err != nil {
    99  		return 0, fr.err
   100  	}
   101  
   102  	var err error
   103  	newOffset := fr.offset
   104  
   105  	switch whence {
   106  	case os.SEEK_CUR:
   107  		newOffset += int64(offset)
   108  	case os.SEEK_END:
   109  		newOffset = fr.size + int64(offset)
   110  	case os.SEEK_SET:
   111  		newOffset = int64(offset)
   112  	}
   113  
   114  	if newOffset < 0 {
   115  		err = fmt.Errorf("cannot seek to negative position")
   116  	} else {
   117  		if fr.offset != newOffset {
   118  			fr.reset()
   119  		}
   120  
   121  		// No problems, set the offset.
   122  		fr.offset = newOffset
   123  	}
   124  
   125  	return fr.offset, err
   126  }
   127  
   128  func (fr *fileReader) Close() error {
   129  	return fr.closeWithErr(fmt.Errorf("fileReader: closed"))
   130  }
   131  
   132  // reader prepares the current reader at the lrs offset, ensuring its buffered
   133  // and ready to go.
   134  func (fr *fileReader) reader() (io.Reader, error) {
   135  	if fr.err != nil {
   136  		return nil, fr.err
   137  	}
   138  
   139  	if fr.rc != nil {
   140  		return fr.brd, nil
   141  	}
   142  
   143  	// If we don't have a reader, open one up.
   144  	rc, err := fr.driver.ReadStream(fr.path, fr.offset)
   145  	if err != nil {
   146  		switch err := err.(type) {
   147  		case storagedriver.PathNotFoundError:
   148  			// NOTE(stevvooe): If the path is not found, we simply return a
   149  			// reader that returns io.EOF. However, we do not set fr.rc,
   150  			// allowing future attempts at getting a reader to possibly
   151  			// succeed if the file turns up later.
   152  			return ioutil.NopCloser(bytes.NewReader([]byte{})), nil
   153  		default:
   154  			return nil, err
   155  		}
   156  	}
   157  
   158  	fr.rc = rc
   159  
   160  	if fr.brd == nil {
   161  		// TODO(stevvooe): Set an optimal buffer size here. We'll have to
   162  		// understand the latency characteristics of the underlying network to
   163  		// set this correctly, so we may want to leave it to the driver. For
   164  		// out of process drivers, we'll have to optimize this buffer size for
   165  		// local communication.
   166  		fr.brd = bufio.NewReaderSize(fr.rc, fileReaderBufferSize)
   167  	} else {
   168  		fr.brd.Reset(fr.rc)
   169  	}
   170  
   171  	return fr.brd, nil
   172  }
   173  
   174  // resetReader resets the reader, forcing the read method to open up a new
   175  // connection and rebuild the buffered reader. This should be called when the
   176  // offset and the reader will become out of sync, such as during a seek
   177  // operation.
   178  func (fr *fileReader) reset() {
   179  	if fr.err != nil {
   180  		return
   181  	}
   182  	if fr.rc != nil {
   183  		fr.rc.Close()
   184  		fr.rc = nil
   185  	}
   186  }
   187  
   188  func (fr *fileReader) closeWithErr(err error) error {
   189  	if fr.err != nil {
   190  		return fr.err
   191  	}
   192  
   193  	fr.err = err
   194  
   195  	// close and release reader chain
   196  	if fr.rc != nil {
   197  		fr.rc.Close()
   198  	}
   199  
   200  	fr.rc = nil
   201  	fr.brd = nil
   202  
   203  	return fr.err
   204  }