2019-01-24 20:15:10 +00:00
|
|
|
// Copyright (C) 2019 Storj Labs, Inc.
|
2018-10-25 17:38:53 +01:00
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package kvmetainfo
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2019-06-20 22:50:13 +01:00
|
|
|
|
|
|
|
"github.com/zeebo/errs"
|
2018-10-25 17:38:53 +01:00
|
|
|
|
2019-06-21 12:29:31 +01:00
|
|
|
"storj.io/storj/pkg/encryption"
|
2018-10-25 17:38:53 +01:00
|
|
|
"storj.io/storj/pkg/storj"
|
2019-06-21 12:29:31 +01:00
|
|
|
"storj.io/storj/storage"
|
2019-10-28 16:40:46 +00:00
|
|
|
"storj.io/storj/uplink/metainfo"
|
2018-10-25 17:38:53 +01:00
|
|
|
)
|
|
|
|
|
2019-07-12 13:57:02 +01:00
|
|
|
// CreateBucket creates a new bucket
|
|
|
|
func (db *Project) CreateBucket(ctx context.Context, bucketName string, info *storj.Bucket) (_ storj.Bucket, err error) {
|
2018-11-16 13:59:27 +00:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
2019-04-03 09:46:21 +01:00
|
|
|
if bucketName == "" {
|
2018-11-12 13:23:19 +00:00
|
|
|
return storj.Bucket{}, storj.ErrNoBucket.New("")
|
2018-10-25 17:38:53 +01:00
|
|
|
}
|
2019-04-10 23:27:04 +01:00
|
|
|
if info == nil {
|
2019-07-03 19:07:44 +01:00
|
|
|
info = &storj.Bucket{PathCipher: storj.EncAESGCM}
|
2019-04-10 23:27:04 +01:00
|
|
|
}
|
2019-07-08 23:32:18 +01:00
|
|
|
if info.DefaultEncryptionParameters.CipherSuite == storj.EncUnspecified {
|
|
|
|
info.DefaultEncryptionParameters.CipherSuite = storj.EncAESGCM
|
2019-04-10 23:27:04 +01:00
|
|
|
}
|
2019-07-08 23:32:18 +01:00
|
|
|
if info.DefaultEncryptionParameters.BlockSize == 0 {
|
|
|
|
info.DefaultEncryptionParameters.BlockSize = db.encryptedBlockSize
|
2019-04-10 23:27:04 +01:00
|
|
|
}
|
2019-07-08 23:32:18 +01:00
|
|
|
if info.DefaultRedundancyScheme.Algorithm == storj.InvalidRedundancyAlgorithm {
|
|
|
|
info.DefaultRedundancyScheme.Algorithm = storj.ReedSolomon
|
2019-04-10 23:27:04 +01:00
|
|
|
}
|
2019-07-08 23:32:18 +01:00
|
|
|
if info.DefaultRedundancyScheme.RequiredShares == 0 {
|
|
|
|
info.DefaultRedundancyScheme.RequiredShares = int16(db.redundancy.RequiredCount())
|
2019-04-10 23:27:04 +01:00
|
|
|
}
|
2019-07-08 23:32:18 +01:00
|
|
|
if info.DefaultRedundancyScheme.RepairShares == 0 {
|
|
|
|
info.DefaultRedundancyScheme.RepairShares = int16(db.redundancy.RepairThreshold())
|
2019-04-10 23:27:04 +01:00
|
|
|
}
|
2019-07-08 23:32:18 +01:00
|
|
|
if info.DefaultRedundancyScheme.OptimalShares == 0 {
|
|
|
|
info.DefaultRedundancyScheme.OptimalShares = int16(db.redundancy.OptimalThreshold())
|
2019-04-10 23:27:04 +01:00
|
|
|
}
|
2019-07-08 23:32:18 +01:00
|
|
|
if info.DefaultRedundancyScheme.TotalShares == 0 {
|
|
|
|
info.DefaultRedundancyScheme.TotalShares = int16(db.redundancy.TotalCount())
|
2019-04-10 23:27:04 +01:00
|
|
|
}
|
2019-07-08 23:32:18 +01:00
|
|
|
if info.DefaultRedundancyScheme.ShareSize == 0 {
|
|
|
|
info.DefaultRedundancyScheme.ShareSize = int32(db.redundancy.ErasureShareSize())
|
2019-04-10 23:27:04 +01:00
|
|
|
}
|
2019-07-08 23:32:18 +01:00
|
|
|
if info.DefaultSegmentsSize == 0 {
|
|
|
|
info.DefaultSegmentsSize = db.segmentsSize
|
2019-04-10 23:27:04 +01:00
|
|
|
}
|
2018-10-25 17:38:53 +01:00
|
|
|
|
2019-07-08 23:32:18 +01:00
|
|
|
if err := validateBlockSize(info.DefaultRedundancyScheme, info.DefaultEncryptionParameters.BlockSize); err != nil {
|
2019-07-12 13:57:02 +01:00
|
|
|
return storj.Bucket{}, storj.ErrBucket.Wrap(err)
|
2019-06-06 19:55:10 +01:00
|
|
|
}
|
|
|
|
|
2019-07-03 19:07:44 +01:00
|
|
|
if info.PathCipher < storj.EncNull || info.PathCipher > storj.EncSecretBox {
|
2019-06-21 12:29:31 +01:00
|
|
|
return storj.Bucket{}, encryption.ErrInvalidConfig.New("encryption type %d is not supported", info.PathCipher)
|
|
|
|
}
|
|
|
|
|
2019-07-12 13:57:02 +01:00
|
|
|
info.Name = bucketName
|
2019-10-28 16:40:46 +00:00
|
|
|
|
|
|
|
// uuid MarshalJSON implementation always returns err == nil
|
|
|
|
partnerID, _ := info.PartnerID.MarshalJSON()
|
|
|
|
newBucket, err := db.metainfo.CreateBucket(ctx, metainfo.CreateBucketParams{
|
|
|
|
Name: []byte(info.Name),
|
|
|
|
PathCipher: info.PathCipher,
|
|
|
|
PartnerID: partnerID,
|
|
|
|
DefaultSegmentsSize: info.DefaultSegmentsSize,
|
|
|
|
DefaultRedundancyScheme: info.DefaultRedundancyScheme,
|
|
|
|
DefaultEncryptionParameters: info.DefaultEncryptionParameters,
|
|
|
|
})
|
2018-10-25 17:38:53 +01:00
|
|
|
if err != nil {
|
2019-07-12 13:57:02 +01:00
|
|
|
return storj.Bucket{}, storj.ErrBucket.Wrap(err)
|
2018-10-25 17:38:53 +01:00
|
|
|
}
|
|
|
|
|
2019-07-12 13:57:02 +01:00
|
|
|
return newBucket, nil
|
2018-10-25 17:38:53 +01:00
|
|
|
}
|
|
|
|
|
2019-06-06 19:55:10 +01:00
|
|
|
// validateBlockSize confirms the encryption block size aligns with stripe size.
|
|
|
|
// Stripes contain encrypted data therefore we want the stripe boundaries to match
|
|
|
|
// with the encryption block size boundaries. We also want stripes to be small for
|
|
|
|
// audits, but encryption can be a bit larger. All told, block size should be an integer
|
|
|
|
// multiple of stripe size.
|
|
|
|
func validateBlockSize(redundancyScheme storj.RedundancyScheme, blockSize int32) error {
|
|
|
|
stripeSize := redundancyScheme.StripeSize()
|
|
|
|
|
|
|
|
if blockSize%stripeSize != 0 {
|
2019-06-20 22:50:13 +01:00
|
|
|
return errs.New("encryption BlockSize (%d) must be a multiple of RS ShareSize (%d) * RS RequiredShares (%d)",
|
2019-06-06 19:55:10 +01:00
|
|
|
blockSize, redundancyScheme.ShareSize, redundancyScheme.RequiredShares,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-10-25 17:38:53 +01:00
|
|
|
// DeleteBucket deletes bucket
|
2019-04-03 09:46:21 +01:00
|
|
|
func (db *Project) DeleteBucket(ctx context.Context, bucketName string) (err error) {
|
2018-11-16 13:59:27 +00:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
2019-04-03 09:46:21 +01:00
|
|
|
if bucketName == "" {
|
2018-11-12 13:23:19 +00:00
|
|
|
return storj.ErrNoBucket.New("")
|
2018-10-25 17:38:53 +01:00
|
|
|
}
|
2019-10-28 16:40:46 +00:00
|
|
|
err = db.metainfo.DeleteBucket(ctx, metainfo.DeleteBucketParams{
|
|
|
|
Name: []byte(bucketName),
|
|
|
|
})
|
2019-07-12 13:57:02 +01:00
|
|
|
if err != nil {
|
|
|
|
if storage.ErrKeyNotFound.Has(err) {
|
|
|
|
err = storj.ErrBucketNotFound.Wrap(err)
|
|
|
|
}
|
|
|
|
return storj.ErrBucket.Wrap(err)
|
2019-06-21 12:29:31 +01:00
|
|
|
}
|
|
|
|
|
2019-07-12 13:57:02 +01:00
|
|
|
return nil
|
2018-10-25 17:38:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetBucket gets bucket information
|
2019-07-12 13:57:02 +01:00
|
|
|
func (db *Project) GetBucket(ctx context.Context, bucketName string) (_ storj.Bucket, err error) {
|
2018-11-16 13:59:27 +00:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
|
|
|
|
2019-04-03 09:46:21 +01:00
|
|
|
if bucketName == "" {
|
2018-11-12 13:23:19 +00:00
|
|
|
return storj.Bucket{}, storj.ErrNoBucket.New("")
|
2018-10-25 17:38:53 +01:00
|
|
|
}
|
|
|
|
|
2019-10-28 16:40:46 +00:00
|
|
|
bucket, err := db.metainfo.GetBucket(ctx, metainfo.GetBucketParams{
|
|
|
|
Name: []byte(bucketName),
|
|
|
|
})
|
2018-10-25 17:38:53 +01:00
|
|
|
if err != nil {
|
2019-07-12 13:57:02 +01:00
|
|
|
return storj.Bucket{}, storj.ErrBucket.Wrap(err)
|
2018-10-25 17:38:53 +01:00
|
|
|
}
|
|
|
|
|
2019-07-12 13:57:02 +01:00
|
|
|
return bucket, nil
|
2018-10-25 17:38:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// ListBuckets lists buckets
|
2019-07-12 13:57:02 +01:00
|
|
|
func (db *Project) ListBuckets(ctx context.Context, listOpts storj.BucketListOptions) (_ storj.BucketList, err error) {
|
2018-11-16 13:59:27 +00:00
|
|
|
defer mon.Task()(&ctx)(&err)
|
2019-10-28 16:40:46 +00:00
|
|
|
|
|
|
|
bucketList, err := db.metainfo.ListBuckets(ctx, metainfo.ListBucketsParams{
|
|
|
|
ListOpts: listOpts,
|
|
|
|
})
|
2018-10-25 17:38:53 +01:00
|
|
|
if err != nil {
|
2019-07-12 13:57:02 +01:00
|
|
|
return storj.BucketList{}, storj.ErrBucket.Wrap(err)
|
2018-10-25 17:38:53 +01:00
|
|
|
}
|
|
|
|
|
2019-07-12 13:57:02 +01:00
|
|
|
return bucketList, nil
|
2018-10-25 17:38:53 +01:00
|
|
|
}
|