lib/store/base/file_readwriter.go (121 lines of code) (raw):
// Copyright (c) 2016-2019 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package base
import (
"io"
"os"
)
// FileReader provides read operation on a file.
type FileReader interface {
io.Reader
io.ReaderAt
io.Seeker
io.Closer
Size() int64
}
// FileReadWriter provides read/write operation on a file.
type FileReadWriter interface {
FileReader
io.Writer
io.WriterAt
Cancel() error // required by docker registry.
Commit() error // required by docker registry.
}
// LocalFileReadWriter implements FileReadWriter interface, provides read/write
// operation on a local file.
type localFileReadWriter struct {
entry *localFileEntry
descriptor *os.File
writePartSize int
readPartSize int
}
func (readWriter *localFileReadWriter) close() error {
return readWriter.descriptor.Close()
}
// Close closes underlying OS.File object.
func (readWriter localFileReadWriter) Close() error {
return readWriter.close()
}
// Write writes up to len(b) bytes to the File.
func (readWriter localFileReadWriter) Write(p []byte) (int, error) {
if readWriter.writePartSize == 0 {
return readWriter.descriptor.Write(p)
}
totalBytesWritten := 0
for totalBytesWritten < len(p) {
blockSize := readWriter.writePartSize
if remainning := len(p) - totalBytesWritten; remainning < blockSize {
blockSize = remainning
}
n, err := readWriter.descriptor.Write(p[totalBytesWritten : totalBytesWritten+blockSize])
totalBytesWritten += n
if err != nil {
return totalBytesWritten, err
}
}
return totalBytesWritten, nil
}
// WriteAt writes len(p) bytes from p to the underlying data stream at offset.
func (readWriter localFileReadWriter) WriteAt(p []byte, offset int64) (int, error) {
if readWriter.writePartSize == 0 {
return readWriter.descriptor.WriteAt(p, offset)
}
totalBytesWritten := 0
for totalBytesWritten < len(p) {
blockSize := readWriter.writePartSize
if remainning := len(p) - totalBytesWritten; remainning < blockSize {
blockSize = remainning
}
n, err := readWriter.descriptor.WriteAt(p[totalBytesWritten:totalBytesWritten+blockSize], offset)
totalBytesWritten += n
offset += int64(n)
if err != nil {
return totalBytesWritten, err
}
}
return totalBytesWritten, nil
}
// Read reads up to len(b) bytes from the File.
func (readWriter localFileReadWriter) Read(p []byte) (int, error) {
if readWriter.readPartSize == 0 {
return readWriter.descriptor.Read(p)
}
totalBytesRead := 0
for totalBytesRead < len(p) {
blockSize := readWriter.readPartSize
if remaining := len(p) - totalBytesRead; remaining < blockSize {
blockSize = remaining
}
n, err := readWriter.descriptor.Read(p[totalBytesRead : totalBytesRead+blockSize])
totalBytesRead += n
if err != nil {
return totalBytesRead, err
}
}
return totalBytesRead, nil
}
// ReadAt reads len(b) bytes from the File starting at byte offset off.
func (readWriter localFileReadWriter) ReadAt(p []byte, offset int64) (int, error) {
if readWriter.readPartSize == 0 {
return readWriter.descriptor.ReadAt(p, offset)
}
totalBytesRead := 0
for totalBytesRead < len(p) {
blockSize := readWriter.readPartSize
if remaining := len(p) - totalBytesRead; remaining < blockSize {
blockSize = remaining
}
n, err := readWriter.descriptor.ReadAt(p[totalBytesRead:totalBytesRead+blockSize], offset)
totalBytesRead += n
offset += int64(n)
if err != nil {
return totalBytesRead, err
}
}
return totalBytesRead, nil
}
// Seek sets the offset for the next Read or Write on file to offset,
// interpreted according to whence:
// 0 means relative to the origin of the file;
// 1 means relative to the current offset;
// 2 means relative to the end.
func (readWriter localFileReadWriter) Seek(offset int64, whence int) (int64, error) {
return readWriter.descriptor.Seek(offset, whence)
}
// Size returns the size of the file.
func (readWriter localFileReadWriter) Size() int64 {
// Use file entry instead of descriptor, because descriptor could have been closed.
fileInfo, err := readWriter.entry.GetStat()
if err != nil {
return 0
}
return fileInfo.Size()
}
// Cancel is supposed to remove any written content.
// In this implementation file is not actually removed, and it's fine since there won't be name
// collision between upload files.
func (readWriter localFileReadWriter) Cancel() error {
return readWriter.close()
}
// Commit is supposed to flush all content for buffered writer.
// In this implementation all writes write to the file directly through syscall.
func (readWriter localFileReadWriter) Commit() error {
return readWriter.close()
}