2019-03-11 08:06:56 +00:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package filestore
|
|
|
|
|
|
|
|
import (
|
2020-04-14 13:39:42 +01:00
|
|
|
"bufio"
|
2019-06-05 14:06:06 +01:00
|
|
|
"context"
|
2019-03-11 08:06:56 +00:00
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
|
|
|
|
"github.com/zeebo/errs"
|
|
|
|
|
2023-05-05 13:08:40 +01:00
|
|
|
"storj.io/common/leak"
|
2023-04-05 18:03:06 +01:00
|
|
|
"storj.io/storj/storagenode/blobstore"
|
2019-03-11 08:06:56 +00:00
|
|
|
)
|
|
|
|
|
2019-08-08 02:47:30 +01:00
|
|
|
const (
|
|
|
|
// FormatV0 is the identifier for storage format v0, which also corresponds to an absence of
|
|
|
|
// format version information.
|
2023-04-05 18:03:06 +01:00
|
|
|
FormatV0 blobstore.FormatVersion = 0
|
2020-08-11 15:50:01 +01:00
|
|
|
// FormatV1 is the identifier for storage format v1.
|
2023-04-05 18:03:06 +01:00
|
|
|
FormatV1 blobstore.FormatVersion = 1
|
2019-08-08 02:47:30 +01:00
|
|
|
|
|
|
|
// Note: New FormatVersion values should be consecutive, as certain parts of this blob store
|
|
|
|
// iterate over them numerically and check for blobs stored with each version.
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// MaxFormatVersionSupported is the highest supported storage format version for reading, and
|
|
|
|
// the only supported storage format version for writing. If stored blobs claim a higher
|
|
|
|
// storage format version than this, or a caller requests _writing_ a storage format version
|
|
|
|
// which is not this, this software will not know how to perform the read or write and an error
|
|
|
|
// will be returned.
|
|
|
|
MaxFormatVersionSupported = FormatV1
|
|
|
|
|
|
|
|
// MinFormatVersionSupported is the lowest supported storage format version for reading. If
|
|
|
|
// stored blobs claim a lower storage format version than this, this software will not know how
|
|
|
|
// to perform the read and an error will be returned.
|
|
|
|
MinFormatVersionSupported = FormatV0
|
|
|
|
)
|
|
|
|
|
2020-07-16 15:18:02 +01:00
|
|
|
// blobReader implements reading blobs.
|
2019-03-11 08:06:56 +00:00
|
|
|
type blobReader struct {
|
|
|
|
*os.File
|
2023-04-05 18:03:06 +01:00
|
|
|
formatVersion blobstore.FormatVersion
|
2023-05-05 13:08:40 +01:00
|
|
|
|
|
|
|
track leak.Ref
|
2019-03-11 08:06:56 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 13:08:40 +01:00
|
|
|
func newBlobReader(track leak.Ref, file *os.File, formatVersion blobstore.FormatVersion) *blobReader {
|
|
|
|
return &blobReader{file, formatVersion, track}
|
2019-03-11 08:06:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Size returns how large is the blob.
|
|
|
|
func (blob *blobReader) Size() (int64, error) {
|
|
|
|
stat, err := blob.Stat()
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
return stat.Size(), err
|
|
|
|
}
|
|
|
|
|
2019-08-08 02:47:30 +01:00
|
|
|
// StorageFormatVersion gets the storage format version being used by the blob.
|
2023-04-05 18:03:06 +01:00
|
|
|
func (blob *blobReader) StorageFormatVersion() blobstore.FormatVersion {
|
2019-08-08 02:47:30 +01:00
|
|
|
return blob.formatVersion
|
|
|
|
}
|
|
|
|
|
2023-05-05 13:08:40 +01:00
|
|
|
// Close closes the reader.
|
|
|
|
func (blob *blobReader) Close() error {
|
|
|
|
return errs.Combine(blob.File.Close(), blob.track.Close())
|
|
|
|
}
|
|
|
|
|
2020-07-16 15:18:02 +01:00
|
|
|
// blobWriter implements writing blobs.
|
2019-03-11 08:06:56 +00:00
|
|
|
type blobWriter struct {
|
2023-04-05 18:03:06 +01:00
|
|
|
ref blobstore.BlobRef
|
2019-11-13 19:15:31 +00:00
|
|
|
store *blobStore
|
2019-08-08 02:47:30 +01:00
|
|
|
closed bool
|
2023-04-05 18:03:06 +01:00
|
|
|
formatVersion blobstore.FormatVersion
|
2020-04-14 13:39:42 +01:00
|
|
|
buffer *bufio.Writer
|
|
|
|
fh *os.File
|
2023-05-05 13:08:40 +01:00
|
|
|
|
|
|
|
track leak.Ref
|
2019-03-11 08:06:56 +00:00
|
|
|
}
|
|
|
|
|
2023-05-05 13:08:40 +01:00
|
|
|
func newBlobWriter(track leak.Ref, ref blobstore.BlobRef, store *blobStore, formatVersion blobstore.FormatVersion, file *os.File, bufferSize int) *blobWriter {
|
2019-08-08 02:47:30 +01:00
|
|
|
return &blobWriter{
|
|
|
|
ref: ref,
|
|
|
|
store: store,
|
|
|
|
closed: false,
|
|
|
|
formatVersion: formatVersion,
|
2020-04-14 13:39:42 +01:00
|
|
|
buffer: bufio.NewWriterSize(file, bufferSize),
|
|
|
|
fh: file,
|
2023-05-05 13:08:40 +01:00
|
|
|
|
|
|
|
track: track,
|
2019-08-08 02:47:30 +01:00
|
|
|
}
|
2019-03-11 08:06:56 +00:00
|
|
|
}
|
|
|
|
|
2020-04-14 13:39:42 +01:00
|
|
|
// Write adds data to the blob.
|
|
|
|
func (blob *blobWriter) Write(p []byte) (int, error) {
|
|
|
|
return blob.buffer.Write(p)
|
|
|
|
}
|
|
|
|
|
2019-03-11 08:06:56 +00:00
|
|
|
// Cancel discards the blob.
|
2019-06-05 14:06:06 +01:00
|
|
|
func (blob *blobWriter) Cancel(ctx context.Context) (err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
2020-04-14 13:39:42 +01:00
|
|
|
|
2019-03-18 14:29:54 +00:00
|
|
|
if blob.closed {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
blob.closed = true
|
2020-04-14 13:39:42 +01:00
|
|
|
|
|
|
|
err = blob.fh.Close()
|
|
|
|
removeErr := os.Remove(blob.fh.Name())
|
2023-05-05 13:08:40 +01:00
|
|
|
return Error.Wrap(errs.Combine(err, removeErr, blob.track.Close()))
|
2019-03-11 08:06:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Commit moves the file to the target location.
|
2019-06-05 14:06:06 +01:00
|
|
|
func (blob *blobWriter) Commit(ctx context.Context) (err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
2020-04-14 13:39:42 +01:00
|
|
|
|
2019-03-18 14:29:54 +00:00
|
|
|
if blob.closed {
|
|
|
|
return Error.New("already closed")
|
|
|
|
}
|
|
|
|
blob.closed = true
|
2020-04-14 13:39:42 +01:00
|
|
|
|
|
|
|
if err := blob.buffer.Flush(); err != nil {
|
2023-05-05 13:08:40 +01:00
|
|
|
// TODO: when flush fails, it looks like we don't close the file handle
|
2020-04-14 13:39:42 +01:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = blob.store.dir.Commit(ctx, blob.fh, blob.ref, blob.formatVersion)
|
2023-05-05 13:08:40 +01:00
|
|
|
return Error.Wrap(errs.Combine(err, blob.track.Close()))
|
2019-03-11 08:06:56 +00:00
|
|
|
}
|
|
|
|
|
2020-04-14 13:39:42 +01:00
|
|
|
// Seek flushes any buffer and seeks the underlying file.
|
|
|
|
func (blob *blobWriter) Seek(offset int64, whence int) (int64, error) {
|
|
|
|
if err := blob.buffer.Flush(); err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return blob.fh.Seek(offset, whence)
|
|
|
|
}
|
|
|
|
|
2019-03-11 08:06:56 +00:00
|
|
|
// Size returns how much has been written so far.
|
|
|
|
func (blob *blobWriter) Size() (int64, error) {
|
|
|
|
pos, err := blob.Seek(0, io.SeekCurrent)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2020-04-14 13:39:42 +01:00
|
|
|
|
2019-03-11 08:06:56 +00:00
|
|
|
return pos, err
|
|
|
|
}
|
2019-08-08 02:47:30 +01:00
|
|
|
|
|
|
|
// StorageFormatVersion indicates what storage format version the blob is using.
|
2023-04-05 18:03:06 +01:00
|
|
|
func (blob *blobWriter) StorageFormatVersion() blobstore.FormatVersion {
|
2019-08-08 02:47:30 +01:00
|
|
|
return blob.formatVersion
|
|
|
|
}
|