2019-01-24 20:15:10 +00:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
2018-07-16 21:44:28 +01:00
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package objects
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"io"
|
|
|
|
"time"
|
|
|
|
|
2018-11-20 18:29:07 +00:00
|
|
|
"github.com/gogo/protobuf/proto"
|
2018-07-30 19:57:50 +01:00
|
|
|
"go.uber.org/zap"
|
2018-07-16 21:44:28 +01:00
|
|
|
monkit "gopkg.in/spacemonkeygo/monkit.v2"
|
|
|
|
|
2018-11-30 15:02:01 +00:00
|
|
|
"storj.io/storj/pkg/pb"
|
2018-07-16 21:44:28 +01:00
|
|
|
"storj.io/storj/pkg/ranger"
|
|
|
|
"storj.io/storj/pkg/storage/streams"
|
2018-10-25 21:28:16 +01:00
|
|
|
"storj.io/storj/pkg/storj"
|
2018-11-21 11:17:28 +00:00
|
|
|
"storj.io/storj/storage"
|
2018-07-16 21:44:28 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
var mon = monkit.Package()
|
|
|
|
|
2018-07-30 19:57:50 +01:00
|
|
|
// Meta is the full object metadata
|
|
|
|
type Meta struct {
|
2018-11-30 15:02:01 +00:00
|
|
|
pb.SerializableMeta
|
2019-04-10 23:27:04 +01:00
|
|
|
Modified time.Time
|
|
|
|
Expiration time.Time
|
|
|
|
Size int64
|
|
|
|
Checksum string
|
2018-07-30 19:57:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// ListItem is a single item in a listing
|
|
|
|
type ListItem struct {
|
2018-10-25 21:28:16 +01:00
|
|
|
Path storj.Path
|
2018-09-07 15:20:15 +01:00
|
|
|
Meta Meta
|
|
|
|
IsPrefix bool
|
2018-07-30 19:57:50 +01:00
|
|
|
}
|
|
|
|
|
2018-07-16 21:44:28 +01:00
|
|
|
// Store for objects
|
|
|
|
type Store interface {
|
2018-10-25 21:28:16 +01:00
|
|
|
Meta(ctx context.Context, path storj.Path) (meta Meta, err error)
|
|
|
|
Get(ctx context.Context, path storj.Path) (rr ranger.Ranger, meta Meta, err error)
|
2018-11-30 15:02:01 +00:00
|
|
|
Put(ctx context.Context, path storj.Path, data io.Reader, metadata pb.SerializableMeta, expiration time.Time) (meta Meta, err error)
|
2018-10-25 21:28:16 +01:00
|
|
|
Delete(ctx context.Context, path storj.Path) (err error)
|
|
|
|
List(ctx context.Context, prefix, startAfter, endBefore storj.Path, recursive bool, limit int, metaFlags uint32) (items []ListItem, more bool, err error)
|
2018-07-16 21:44:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type objStore struct {
|
2018-11-13 12:21:52 +00:00
|
|
|
store streams.Store
|
|
|
|
pathCipher storj.Cipher
|
2018-07-16 21:44:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewStore for objects
|
2018-11-13 12:21:52 +00:00
|
|
|
func NewStore(store streams.Store, pathCipher storj.Cipher) Store {
|
|
|
|
return &objStore{store: store, pathCipher: pathCipher}
|
2018-07-16 21:44:28 +01:00
|
|
|
}
|
|
|
|
|
2018-10-25 21:28:16 +01:00
|
|
|
func (o *objStore) Meta(ctx context.Context, path storj.Path) (meta Meta, err error) {
|
2018-07-16 21:44:28 +01:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
2018-09-09 18:31:26 +01:00
|
|
|
|
|
|
|
if len(path) == 0 {
|
2018-11-21 11:17:28 +00:00
|
|
|
return Meta{}, storj.ErrNoPath.New("")
|
2018-09-09 18:31:26 +01:00
|
|
|
}
|
|
|
|
|
2018-11-13 12:21:52 +00:00
|
|
|
m, err := o.store.Meta(ctx, path, o.pathCipher)
|
2018-11-21 11:17:28 +00:00
|
|
|
|
|
|
|
if storage.ErrKeyNotFound.Has(err) {
|
|
|
|
err = storj.ErrObjectNotFound.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2018-07-30 19:57:50 +01:00
|
|
|
return convertMeta(m), err
|
2018-07-16 21:44:28 +01:00
|
|
|
}
|
|
|
|
|
2018-10-25 21:28:16 +01:00
|
|
|
func (o *objStore) Get(ctx context.Context, path storj.Path) (
|
2018-09-14 15:10:43 +01:00
|
|
|
rr ranger.Ranger, meta Meta, err error) {
|
2018-07-16 21:44:28 +01:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
2018-09-09 18:31:26 +01:00
|
|
|
|
|
|
|
if len(path) == 0 {
|
2018-11-21 11:17:28 +00:00
|
|
|
return nil, Meta{}, storj.ErrNoPath.New("")
|
2018-09-09 18:31:26 +01:00
|
|
|
}
|
|
|
|
|
2018-11-13 12:21:52 +00:00
|
|
|
rr, m, err := o.store.Get(ctx, path, o.pathCipher)
|
2018-11-21 11:17:28 +00:00
|
|
|
|
|
|
|
if storage.ErrKeyNotFound.Has(err) {
|
|
|
|
err = storj.ErrObjectNotFound.Wrap(err)
|
|
|
|
}
|
|
|
|
|
2018-07-30 19:57:50 +01:00
|
|
|
return rr, convertMeta(m), err
|
2018-07-16 21:44:28 +01:00
|
|
|
}
|
|
|
|
|
2018-11-30 15:02:01 +00:00
|
|
|
func (o *objStore) Put(ctx context.Context, path storj.Path, data io.Reader, metadata pb.SerializableMeta, expiration time.Time) (meta Meta, err error) {
|
2018-07-16 21:44:28 +01:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
2018-08-27 18:28:16 +01:00
|
|
|
|
2018-09-09 18:31:26 +01:00
|
|
|
if len(path) == 0 {
|
2018-11-21 11:17:28 +00:00
|
|
|
return Meta{}, storj.ErrNoPath.New("")
|
2018-09-09 18:31:26 +01:00
|
|
|
}
|
|
|
|
|
2018-08-27 18:28:16 +01:00
|
|
|
// TODO(kaloyan): autodetect content type
|
|
|
|
// if metadata.GetContentType() == "" {}
|
|
|
|
|
2018-07-16 21:44:28 +01:00
|
|
|
b, err := proto.Marshal(&metadata)
|
|
|
|
if err != nil {
|
2018-07-30 19:57:50 +01:00
|
|
|
return Meta{}, err
|
2018-07-16 21:44:28 +01:00
|
|
|
}
|
2018-11-13 12:21:52 +00:00
|
|
|
m, err := o.store.Put(ctx, path, o.pathCipher, data, b, expiration)
|
2018-07-30 19:57:50 +01:00
|
|
|
return convertMeta(m), err
|
2018-07-16 21:44:28 +01:00
|
|
|
}
|
|
|
|
|
2018-10-25 21:28:16 +01:00
|
|
|
func (o *objStore) Delete(ctx context.Context, path storj.Path) (err error) {
|
2018-07-16 21:44:28 +01:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
2018-09-09 18:31:26 +01:00
|
|
|
|
|
|
|
if len(path) == 0 {
|
2018-11-21 11:17:28 +00:00
|
|
|
return storj.ErrNoPath.New("")
|
|
|
|
}
|
|
|
|
|
|
|
|
err = o.store.Delete(ctx, path, o.pathCipher)
|
|
|
|
|
|
|
|
if storage.ErrKeyNotFound.Has(err) {
|
|
|
|
err = storj.ErrObjectNotFound.Wrap(err)
|
2018-09-09 18:31:26 +01:00
|
|
|
}
|
|
|
|
|
2018-11-21 11:17:28 +00:00
|
|
|
return err
|
2018-07-16 21:44:28 +01:00
|
|
|
}
|
|
|
|
|
2018-10-25 21:28:16 +01:00
|
|
|
func (o *objStore) List(ctx context.Context, prefix, startAfter, endBefore storj.Path, recursive bool, limit int, metaFlags uint32) (
|
2018-07-30 19:57:50 +01:00
|
|
|
items []ListItem, more bool, err error) {
|
2018-07-16 21:44:28 +01:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
2018-07-30 19:57:50 +01:00
|
|
|
|
2018-11-13 12:21:52 +00:00
|
|
|
strItems, more, err := o.store.List(ctx, prefix, startAfter, endBefore, o.pathCipher, recursive, limit, metaFlags)
|
2018-07-30 19:57:50 +01:00
|
|
|
if err != nil {
|
|
|
|
return nil, false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
items = make([]ListItem, len(strItems))
|
|
|
|
for i, itm := range strItems {
|
|
|
|
items[i] = ListItem{
|
2018-09-07 15:20:15 +01:00
|
|
|
Path: itm.Path,
|
|
|
|
Meta: convertMeta(itm.Meta),
|
|
|
|
IsPrefix: itm.IsPrefix,
|
2018-07-30 19:57:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return items, more, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// convertMeta converts stream metadata to object metadata
|
|
|
|
func convertMeta(m streams.Meta) Meta {
|
2018-11-30 15:02:01 +00:00
|
|
|
ser := pb.SerializableMeta{}
|
2018-07-30 19:57:50 +01:00
|
|
|
err := proto.Unmarshal(m.Data, &ser)
|
|
|
|
if err != nil {
|
|
|
|
zap.S().Warnf("Failed deserializing metadata: %v", err)
|
|
|
|
}
|
|
|
|
return Meta{
|
|
|
|
Modified: m.Modified,
|
|
|
|
Expiration: m.Expiration,
|
|
|
|
Size: m.Size,
|
|
|
|
SerializableMeta: ser,
|
|
|
|
}
|
2018-07-16 21:44:28 +01:00
|
|
|
}
|