github.com/mika/distribution@v2.2.2-0.20160108133430-a75790e3d8e0+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 11 "github.com/docker/distribution/context" 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 ctx context.Context 29 30 // identifying fields 31 path string 32 size int64 // size is the total size, must be set. 33 34 // mutable fields 35 rc io.ReadCloser // remote read closer 36 brd *bufio.Reader // internal buffered io 37 offset int64 // offset is the current read offset 38 err error // terminal error, if set, reader is closed 39 } 40 41 // newFileReader initializes a file reader for the remote file. The reader 42 // takes on the size and path that must be determined externally with a stat 43 // call. The reader operates optimistically, assuming that the file is already 44 // there. 45 func newFileReader(ctx context.Context, driver storagedriver.StorageDriver, path string, size int64) (*fileReader, error) { 46 return &fileReader{ 47 ctx: ctx, 48 driver: driver, 49 path: path, 50 size: size, 51 }, nil 52 } 53 54 func (fr *fileReader) Read(p []byte) (n int, err error) { 55 if fr.err != nil { 56 return 0, fr.err 57 } 58 59 rd, err := fr.reader() 60 if err != nil { 61 return 0, err 62 } 63 64 n, err = rd.Read(p) 65 fr.offset += int64(n) 66 67 // Simulate io.EOR error if we reach filesize. 68 if err == nil && fr.offset >= fr.size { 69 err = io.EOF 70 } 71 72 return n, err 73 } 74 75 func (fr *fileReader) Seek(offset int64, whence int) (int64, error) { 76 if fr.err != nil { 77 return 0, fr.err 78 } 79 80 var err error 81 newOffset := fr.offset 82 83 switch whence { 84 case os.SEEK_CUR: 85 newOffset += int64(offset) 86 case os.SEEK_END: 87 newOffset = fr.size + int64(offset) 88 case os.SEEK_SET: 89 newOffset = int64(offset) 90 } 91 92 if newOffset < 0 { 93 err = fmt.Errorf("cannot seek to negative position") 94 } else { 95 if fr.offset != newOffset { 96 fr.reset() 97 } 98 99 // No problems, set the offset. 100 fr.offset = newOffset 101 } 102 103 return fr.offset, err 104 } 105 106 func (fr *fileReader) Close() error { 107 return fr.closeWithErr(fmt.Errorf("fileReader: closed")) 108 } 109 110 // reader prepares the current reader at the lrs offset, ensuring its buffered 111 // and ready to go. 112 func (fr *fileReader) reader() (io.Reader, error) { 113 if fr.err != nil { 114 return nil, fr.err 115 } 116 117 if fr.rc != nil { 118 return fr.brd, nil 119 } 120 121 // If we don't have a reader, open one up. 122 rc, err := fr.driver.ReadStream(fr.ctx, fr.path, fr.offset) 123 if err != nil { 124 switch err := err.(type) { 125 case storagedriver.PathNotFoundError: 126 // NOTE(stevvooe): If the path is not found, we simply return a 127 // reader that returns io.EOF. However, we do not set fr.rc, 128 // allowing future attempts at getting a reader to possibly 129 // succeed if the file turns up later. 130 return ioutil.NopCloser(bytes.NewReader([]byte{})), nil 131 default: 132 return nil, err 133 } 134 } 135 136 fr.rc = rc 137 138 if fr.brd == nil { 139 fr.brd = bufio.NewReaderSize(fr.rc, fileReaderBufferSize) 140 } else { 141 fr.brd.Reset(fr.rc) 142 } 143 144 return fr.brd, nil 145 } 146 147 // resetReader resets the reader, forcing the read method to open up a new 148 // connection and rebuild the buffered reader. This should be called when the 149 // offset and the reader will become out of sync, such as during a seek 150 // operation. 151 func (fr *fileReader) reset() { 152 if fr.err != nil { 153 return 154 } 155 if fr.rc != nil { 156 fr.rc.Close() 157 fr.rc = nil 158 } 159 } 160 161 func (fr *fileReader) closeWithErr(err error) error { 162 if fr.err != nil { 163 return fr.err 164 } 165 166 fr.err = err 167 168 // close and release reader chain 169 if fr.rc != nil { 170 fr.rc.Close() 171 } 172 173 fr.rc = nil 174 fr.brd = nil 175 176 return fr.err 177 }