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 }