2018-08-16 15:32:28 +01:00
|
|
|
// Copyright (C) 2018 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package buckets
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
2018-11-13 12:21:52 +00:00
|
|
|
"strconv"
|
2018-08-16 15:32:28 +01:00
|
|
|
"time"
|
|
|
|
|
|
|
|
monkit "gopkg.in/spacemonkeygo/monkit.v2"
|
2018-09-09 18:31:26 +01:00
|
|
|
|
2018-11-14 09:26:18 +00:00
|
|
|
"storj.io/storj/pkg/encryption"
|
2018-08-16 15:32:28 +01:00
|
|
|
"storj.io/storj/pkg/storage/meta"
|
|
|
|
"storj.io/storj/pkg/storage/objects"
|
2018-11-13 12:21:52 +00:00
|
|
|
"storj.io/storj/pkg/storage/streams"
|
2018-11-12 13:23:19 +00:00
|
|
|
"storj.io/storj/pkg/storj"
|
2018-09-09 18:31:26 +01:00
|
|
|
"storj.io/storj/storage"
|
2018-08-16 15:32:28 +01:00
|
|
|
)
|
|
|
|
|
2018-09-09 18:31:26 +01:00
|
|
|
var mon = monkit.Package()
|
|
|
|
|
2018-08-16 15:32:28 +01:00
|
|
|
// Store creates an interface for interacting with buckets
|
|
|
|
type Store interface {
|
|
|
|
Get(ctx context.Context, bucket string) (meta Meta, err error)
|
2018-11-14 09:26:18 +00:00
|
|
|
Put(ctx context.Context, bucket string, pathCipher storj.Cipher) (meta Meta, err error)
|
2018-08-16 15:32:28 +01:00
|
|
|
Delete(ctx context.Context, bucket string) (err error)
|
2018-09-07 15:20:15 +01:00
|
|
|
List(ctx context.Context, startAfter, endBefore string, limit int) (items []ListItem, more bool, err error)
|
2018-08-16 15:32:28 +01:00
|
|
|
GetObjectStore(ctx context.Context, bucketName string) (store objects.Store, err error)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ListItem is a single item in a listing
|
|
|
|
type ListItem struct {
|
|
|
|
Bucket string
|
|
|
|
Meta Meta
|
|
|
|
}
|
|
|
|
|
|
|
|
// BucketStore contains objects store
|
|
|
|
type BucketStore struct {
|
2018-11-14 09:26:18 +00:00
|
|
|
store objects.Store
|
|
|
|
stream streams.Store
|
2018-08-16 15:32:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Meta is the bucket metadata struct
|
|
|
|
type Meta struct {
|
2018-11-13 12:21:52 +00:00
|
|
|
Created time.Time
|
|
|
|
PathEncryptionType storj.Cipher
|
2018-08-16 15:32:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewStore instantiates BucketStore
|
2018-11-14 09:26:18 +00:00
|
|
|
func NewStore(stream streams.Store) Store {
|
2018-11-13 12:21:52 +00:00
|
|
|
// root object store for storing the buckets with unencrypted names
|
|
|
|
store := objects.NewStore(stream, storj.Unencrypted)
|
2018-11-14 09:26:18 +00:00
|
|
|
return &BucketStore{store: store, stream: stream}
|
2018-08-16 15:32:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetObjectStore returns an implementation of objects.Store
|
|
|
|
func (b *BucketStore) GetObjectStore(ctx context.Context, bucket string) (objects.Store, error) {
|
2018-09-09 18:31:26 +01:00
|
|
|
if bucket == "" {
|
2018-11-12 13:23:19 +00:00
|
|
|
return nil, storj.ErrNoBucket.New("")
|
2018-09-09 18:31:26 +01:00
|
|
|
}
|
|
|
|
|
2018-11-13 12:21:52 +00:00
|
|
|
m, err := b.Get(ctx, bucket)
|
2018-08-16 15:32:28 +01:00
|
|
|
if err != nil {
|
|
|
|
if storage.ErrKeyNotFound.Has(err) {
|
2018-11-21 11:17:28 +00:00
|
|
|
err = storj.ErrBucketNotFound.Wrap(err)
|
2018-08-16 15:32:28 +01:00
|
|
|
}
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
prefixed := prefixedObjStore{
|
2018-11-13 12:21:52 +00:00
|
|
|
store: objects.NewStore(b.stream, m.PathEncryptionType),
|
2018-08-16 15:32:28 +01:00
|
|
|
prefix: bucket,
|
|
|
|
}
|
|
|
|
return &prefixed, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get calls objects store Get
|
|
|
|
func (b *BucketStore) Get(ctx context.Context, bucket string) (meta Meta, err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
2018-10-25 17:38:53 +01:00
|
|
|
|
2018-09-09 18:31:26 +01:00
|
|
|
if bucket == "" {
|
2018-11-12 13:23:19 +00:00
|
|
|
return Meta{}, storj.ErrNoBucket.New("")
|
2018-09-09 18:31:26 +01:00
|
|
|
}
|
|
|
|
|
2018-11-13 12:21:52 +00:00
|
|
|
objMeta, err := b.store.Meta(ctx, bucket)
|
2018-08-16 15:32:28 +01:00
|
|
|
if err != nil {
|
2018-11-21 11:17:28 +00:00
|
|
|
if storage.ErrKeyNotFound.Has(err) {
|
|
|
|
err = storj.ErrBucketNotFound.Wrap(err)
|
|
|
|
}
|
2018-08-16 15:32:28 +01:00
|
|
|
return Meta{}, err
|
|
|
|
}
|
2018-11-21 11:17:28 +00:00
|
|
|
|
2018-11-13 12:21:52 +00:00
|
|
|
return convertMeta(objMeta)
|
2018-08-16 15:32:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Put calls objects store Put
|
2018-11-14 09:26:18 +00:00
|
|
|
func (b *BucketStore) Put(ctx context.Context, bucket string, pathCipher storj.Cipher) (meta Meta, err error) {
|
2018-08-16 15:32:28 +01:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
2018-10-25 17:38:53 +01:00
|
|
|
|
2018-09-09 18:31:26 +01:00
|
|
|
if bucket == "" {
|
2018-11-12 13:23:19 +00:00
|
|
|
return Meta{}, storj.ErrNoBucket.New("")
|
2018-09-09 18:31:26 +01:00
|
|
|
}
|
|
|
|
|
2018-11-14 09:26:18 +00:00
|
|
|
if pathCipher < storj.Unencrypted || pathCipher > storj.SecretBox {
|
|
|
|
return Meta{}, encryption.ErrInvalidConfig.New("encryption type %d is not supported", pathCipher)
|
|
|
|
}
|
|
|
|
|
2018-08-16 15:32:28 +01:00
|
|
|
r := bytes.NewReader(nil)
|
2018-11-13 12:21:52 +00:00
|
|
|
userMeta := map[string]string{
|
2018-11-14 09:26:18 +00:00
|
|
|
"path-enc-type": strconv.Itoa(int(pathCipher)),
|
2018-11-13 12:21:52 +00:00
|
|
|
}
|
2018-08-16 15:32:28 +01:00
|
|
|
var exp time.Time
|
2018-11-13 12:21:52 +00:00
|
|
|
m, err := b.store.Put(ctx, bucket, r, objects.SerializableMeta{UserDefined: userMeta}, exp)
|
2018-08-16 15:32:28 +01:00
|
|
|
if err != nil {
|
|
|
|
return Meta{}, err
|
|
|
|
}
|
2018-11-13 12:21:52 +00:00
|
|
|
return convertMeta(m)
|
2018-08-16 15:32:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Delete calls objects store Delete
|
|
|
|
func (b *BucketStore) Delete(ctx context.Context, bucket string) (err error) {
|
|
|
|
defer mon.Task()(&ctx)(&err)
|
2018-10-25 17:38:53 +01:00
|
|
|
|
2018-09-09 18:31:26 +01:00
|
|
|
if bucket == "" {
|
2018-11-12 13:23:19 +00:00
|
|
|
return storj.ErrNoBucket.New("")
|
2018-09-09 18:31:26 +01:00
|
|
|
}
|
|
|
|
|
2018-11-21 11:17:28 +00:00
|
|
|
err = b.store.Delete(ctx, bucket)
|
|
|
|
|
|
|
|
if storage.ErrKeyNotFound.Has(err) {
|
|
|
|
err = storj.ErrBucketNotFound.Wrap(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return err
|
2018-08-16 15:32:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// List calls objects store List
|
2018-10-25 17:38:53 +01:00
|
|
|
func (b *BucketStore) List(ctx context.Context, startAfter, endBefore string, limit int) (items []ListItem, more bool, err error) {
|
2018-08-16 15:32:28 +01:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
2018-10-25 17:38:53 +01:00
|
|
|
|
2018-11-13 12:21:52 +00:00
|
|
|
objItems, more, err := b.store.List(ctx, "", startAfter, endBefore, false, limit, meta.Modified)
|
2018-09-13 18:34:01 +01:00
|
|
|
if err != nil {
|
|
|
|
return items, more, err
|
|
|
|
}
|
2018-10-25 17:38:53 +01:00
|
|
|
|
2018-09-07 15:20:15 +01:00
|
|
|
items = make([]ListItem, 0, len(objItems))
|
|
|
|
for _, itm := range objItems {
|
|
|
|
if itm.IsPrefix {
|
|
|
|
continue
|
|
|
|
}
|
2018-11-13 12:21:52 +00:00
|
|
|
m, err := convertMeta(itm.Meta)
|
|
|
|
if err != nil {
|
|
|
|
return items, more, err
|
|
|
|
}
|
2018-09-07 15:20:15 +01:00
|
|
|
items = append(items, ListItem{
|
2018-10-25 21:28:16 +01:00
|
|
|
Bucket: itm.Path,
|
2018-11-13 12:21:52 +00:00
|
|
|
Meta: m,
|
2018-09-07 15:20:15 +01:00
|
|
|
})
|
2018-08-16 15:32:28 +01:00
|
|
|
}
|
|
|
|
return items, more, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// convertMeta converts stream metadata to object metadata
|
2018-11-13 12:21:52 +00:00
|
|
|
func convertMeta(m objects.Meta) (Meta, error) {
|
|
|
|
var cipher storj.Cipher
|
|
|
|
|
|
|
|
pathEncType := m.UserDefined["path-enc-type"]
|
|
|
|
|
|
|
|
if pathEncType == "" {
|
|
|
|
// backward compatibility for old buckets
|
|
|
|
cipher = storj.AESGCM
|
|
|
|
} else {
|
|
|
|
pet, err := strconv.Atoi(pathEncType)
|
|
|
|
if err != nil {
|
|
|
|
return Meta{}, err
|
|
|
|
}
|
|
|
|
cipher = storj.Cipher(pet)
|
2018-08-16 15:32:28 +01:00
|
|
|
}
|
2018-11-13 12:21:52 +00:00
|
|
|
|
|
|
|
return Meta{
|
|
|
|
Created: m.Modified,
|
|
|
|
PathEncryptionType: cipher,
|
|
|
|
}, nil
|
2018-08-16 15:32:28 +01:00
|
|
|
}
|