2020-08-03 11:48:33 +00:00
|
|
|
package layer
|
|
|
|
|
|
|
|
import (
|
2022-08-01 16:52:09 +00:00
|
|
|
"bytes"
|
2020-08-03 11:48:33 +00:00
|
|
|
"context"
|
2021-05-28 20:48:23 +00:00
|
|
|
"crypto/ecdsa"
|
2022-08-01 16:52:09 +00:00
|
|
|
"crypto/hmac"
|
2022-05-18 07:48:30 +00:00
|
|
|
"crypto/rand"
|
2022-08-01 16:52:09 +00:00
|
|
|
"crypto/sha256"
|
|
|
|
"encoding/hex"
|
|
|
|
errorsStd "errors"
|
2021-05-20 10:14:17 +00:00
|
|
|
"fmt"
|
2020-08-03 11:48:33 +00:00
|
|
|
"io"
|
2020-10-24 13:09:22 +00:00
|
|
|
"net/url"
|
2022-08-01 16:52:09 +00:00
|
|
|
"strconv"
|
|
|
|
"strings"
|
2022-05-20 15:02:00 +00:00
|
|
|
"time"
|
2020-08-03 11:48:33 +00:00
|
|
|
|
2022-08-01 16:52:09 +00:00
|
|
|
"github.com/minio/sio"
|
2022-03-05 06:58:54 +00:00
|
|
|
"github.com/nats-io/nats.go"
|
2021-10-19 15:08:07 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
2021-05-18 11:10:08 +00:00
|
|
|
"github.com/nspcc-dev/neofs-s3-gw/api"
|
2021-08-10 11:19:26 +00:00
|
|
|
"github.com/nspcc-dev/neofs-s3-gw/api/cache"
|
2021-09-10 06:56:56 +00:00
|
|
|
"github.com/nspcc-dev/neofs-s3-gw/api/data"
|
2021-08-09 08:53:58 +00:00
|
|
|
"github.com/nspcc-dev/neofs-s3-gw/api/errors"
|
2022-01-11 10:09:34 +00:00
|
|
|
"github.com/nspcc-dev/neofs-s3-gw/api/resolver"
|
2021-06-21 10:54:57 +00:00
|
|
|
"github.com/nspcc-dev/neofs-s3-gw/creds/accessbox"
|
2022-06-01 14:00:30 +00:00
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/bearer"
|
2021-11-15 12:56:16 +00:00
|
|
|
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
|
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/eacl"
|
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/netmap"
|
2022-05-19 09:17:56 +00:00
|
|
|
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
|
2021-11-15 12:56:16 +00:00
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/session"
|
2022-04-25 09:57:58 +00:00
|
|
|
"github.com/nspcc-dev/neofs-sdk-go/user"
|
2020-08-03 11:48:33 +00:00
|
|
|
"go.uber.org/zap"
|
|
|
|
)
|
|
|
|
|
|
|
|
type (
|
2022-04-29 13:08:22 +00:00
|
|
|
EventListener interface {
|
2022-03-05 06:58:54 +00:00
|
|
|
Subscribe(context.Context, string, MsgHandler) error
|
|
|
|
Listen(context.Context)
|
|
|
|
}
|
|
|
|
|
|
|
|
MsgHandler interface {
|
|
|
|
HandleMessage(context.Context, *nats.Msg) error
|
|
|
|
}
|
|
|
|
|
|
|
|
MsgHandlerFunc func(context.Context, *nats.Msg) error
|
|
|
|
|
2020-08-03 11:48:33 +00:00
|
|
|
layer struct {
|
2022-06-06 11:09:09 +00:00
|
|
|
neoFS NeoFS
|
2021-08-18 13:48:58 +00:00
|
|
|
log *zap.Logger
|
2021-10-19 15:08:07 +00:00
|
|
|
anonKey AnonymousKey
|
2022-01-11 10:09:34 +00:00
|
|
|
resolver *resolver.BucketResolver
|
2022-04-29 13:08:22 +00:00
|
|
|
ncontroller EventListener
|
2021-09-10 06:56:56 +00:00
|
|
|
listsCache *cache.ObjectsListCache
|
|
|
|
objCache *cache.ObjectsCache
|
|
|
|
namesCache *cache.ObjectsNameCache
|
|
|
|
bucketCache *cache.BucketCache
|
|
|
|
systemCache *cache.SystemCache
|
2022-04-22 07:18:21 +00:00
|
|
|
treeService TreeService
|
2020-10-19 01:04:37 +00:00
|
|
|
}
|
|
|
|
|
2021-11-22 09:16:05 +00:00
|
|
|
Config struct {
|
2022-03-05 06:58:54 +00:00
|
|
|
ChainAddress string
|
|
|
|
Caches *CachesConfig
|
|
|
|
AnonKey AnonymousKey
|
|
|
|
Resolver *resolver.BucketResolver
|
2022-04-22 07:18:21 +00:00
|
|
|
TreeService TreeService
|
2021-11-22 09:16:05 +00:00
|
|
|
}
|
|
|
|
|
2021-10-19 15:08:07 +00:00
|
|
|
// AnonymousKey contains data for anonymous requests.
|
|
|
|
AnonymousKey struct {
|
|
|
|
Key *keys.PrivateKey
|
|
|
|
}
|
|
|
|
|
2021-09-10 06:56:56 +00:00
|
|
|
// CachesConfig contains params for caches.
|
|
|
|
CachesConfig struct {
|
|
|
|
Objects *cache.Config
|
|
|
|
ObjectsList *cache.Config
|
|
|
|
Names *cache.Config
|
|
|
|
Buckets *cache.Config
|
|
|
|
System *cache.Config
|
2021-08-16 09:58:09 +00:00
|
|
|
}
|
|
|
|
|
2021-05-13 20:25:31 +00:00
|
|
|
// GetObjectParams stores object get request parameters.
|
2020-08-03 11:48:33 +00:00
|
|
|
GetObjectParams struct {
|
2021-08-10 10:03:09 +00:00
|
|
|
Range *RangeParams
|
2021-09-10 06:56:56 +00:00
|
|
|
ObjectInfo *data.ObjectInfo
|
2022-06-02 16:56:04 +00:00
|
|
|
BucketInfo *data.BucketInfo
|
2021-08-19 06:55:22 +00:00
|
|
|
Writer io.Writer
|
2022-08-01 16:52:09 +00:00
|
|
|
Encryption EncryptionParams
|
2021-08-10 10:03:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// HeadObjectParams stores object head request parameters.
|
|
|
|
HeadObjectParams struct {
|
2022-03-18 13:04:09 +00:00
|
|
|
BktInfo *data.BucketInfo
|
2021-08-10 10:03:09 +00:00
|
|
|
Object string
|
|
|
|
VersionID string
|
2020-08-03 11:48:33 +00:00
|
|
|
}
|
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
// ObjectVersion stores object version info.
|
|
|
|
ObjectVersion struct {
|
2022-06-15 12:17:29 +00:00
|
|
|
BktInfo *data.BucketInfo
|
|
|
|
ObjectName string
|
|
|
|
VersionID string
|
|
|
|
NoErrorOnDeleteMarker bool
|
2022-05-26 13:11:14 +00:00
|
|
|
}
|
|
|
|
|
2021-06-24 11:10:00 +00:00
|
|
|
// RangeParams stores range header request parameters.
|
|
|
|
RangeParams struct {
|
|
|
|
Start uint64
|
|
|
|
End uint64
|
|
|
|
}
|
|
|
|
|
2022-08-01 16:52:09 +00:00
|
|
|
// AES256Key is a key for encryption.
|
|
|
|
AES256Key [32]byte
|
|
|
|
|
|
|
|
EncryptionParams struct {
|
|
|
|
enabled bool
|
|
|
|
customerKey AES256Key
|
|
|
|
}
|
|
|
|
|
2021-05-13 20:25:31 +00:00
|
|
|
// PutObjectParams stores object put request parameters.
|
2020-08-03 11:48:33 +00:00
|
|
|
PutObjectParams struct {
|
2022-08-01 16:52:09 +00:00
|
|
|
BktInfo *data.BucketInfo
|
|
|
|
Object string
|
|
|
|
Size int64
|
|
|
|
Reader io.Reader
|
|
|
|
Header map[string]string
|
|
|
|
Lock *data.ObjectLock
|
|
|
|
Encryption EncryptionParams
|
2020-08-03 11:48:33 +00:00
|
|
|
}
|
|
|
|
|
2022-03-31 06:24:29 +00:00
|
|
|
DeleteObjectParams struct {
|
2022-06-24 12:39:30 +00:00
|
|
|
BktInfo *data.BucketInfo
|
|
|
|
Objects []*VersionedObject
|
|
|
|
Settings *data.BucketSettings
|
2022-03-31 06:24:29 +00:00
|
|
|
}
|
|
|
|
|
2022-02-28 08:02:05 +00:00
|
|
|
// PutSettingsParams stores object copy request parameters.
|
|
|
|
PutSettingsParams struct {
|
|
|
|
BktInfo *data.BucketInfo
|
|
|
|
Settings *data.BucketSettings
|
2021-08-10 08:58:40 +00:00
|
|
|
}
|
|
|
|
|
2021-10-04 14:32:35 +00:00
|
|
|
// PutCORSParams stores PutCORS request parameters.
|
|
|
|
PutCORSParams struct {
|
2021-10-13 18:50:02 +00:00
|
|
|
BktInfo *data.BucketInfo
|
|
|
|
Reader io.Reader
|
2021-10-04 14:32:35 +00:00
|
|
|
}
|
|
|
|
|
2021-05-13 20:25:31 +00:00
|
|
|
// CopyObjectParams stores object copy request parameters.
|
2020-08-03 11:48:33 +00:00
|
|
|
CopyObjectParams struct {
|
2022-03-18 13:04:09 +00:00
|
|
|
SrcObject *data.ObjectInfo
|
2022-06-02 16:56:04 +00:00
|
|
|
ScrBktInfo *data.BucketInfo
|
2022-03-18 13:04:09 +00:00
|
|
|
DstBktInfo *data.BucketInfo
|
|
|
|
DstObject string
|
|
|
|
SrcSize int64
|
|
|
|
Header map[string]string
|
|
|
|
Range *RangeParams
|
|
|
|
Lock *data.ObjectLock
|
2022-08-01 16:52:09 +00:00
|
|
|
Encryption EncryptionParams
|
2020-08-03 11:48:33 +00:00
|
|
|
}
|
2021-06-23 20:21:15 +00:00
|
|
|
// CreateBucketParams stores bucket create request parameters.
|
|
|
|
CreateBucketParams struct {
|
2022-06-21 15:21:20 +00:00
|
|
|
Name string
|
|
|
|
Policy netmap.PlacementPolicy
|
|
|
|
EACL *eacl.Table
|
|
|
|
SessionContainerCreation *session.Container
|
|
|
|
SessionEACL *session.Container
|
|
|
|
LocationConstraint string
|
|
|
|
ObjectLockEnabled bool
|
2021-06-23 20:21:15 +00:00
|
|
|
}
|
2021-07-21 11:59:46 +00:00
|
|
|
// PutBucketACLParams stores put bucket acl request parameters.
|
|
|
|
PutBucketACLParams struct {
|
2022-06-21 15:21:20 +00:00
|
|
|
BktInfo *data.BucketInfo
|
|
|
|
EACL *eacl.Table
|
|
|
|
SessionToken *session.Container
|
2021-07-21 11:59:46 +00:00
|
|
|
}
|
2021-06-23 20:25:00 +00:00
|
|
|
// DeleteBucketParams stores delete bucket request parameters.
|
|
|
|
DeleteBucketParams struct {
|
2022-06-22 13:22:28 +00:00
|
|
|
BktInfo *data.BucketInfo
|
|
|
|
SessionToken *session.Container
|
2021-06-23 20:25:00 +00:00
|
|
|
}
|
2021-10-04 14:30:38 +00:00
|
|
|
|
2021-07-05 19:18:58 +00:00
|
|
|
// ListObjectVersionsParams stores list objects versions parameters.
|
|
|
|
ListObjectVersionsParams struct {
|
2022-03-18 13:04:09 +00:00
|
|
|
BktInfo *data.BucketInfo
|
2021-07-05 19:18:58 +00:00
|
|
|
Delimiter string
|
|
|
|
KeyMarker string
|
|
|
|
MaxKeys int
|
|
|
|
Prefix string
|
|
|
|
VersionIDMarker string
|
|
|
|
Encode string
|
|
|
|
}
|
2020-08-03 11:48:33 +00:00
|
|
|
|
2021-09-07 06:17:12 +00:00
|
|
|
// VersionedObject stores info about objects to delete.
|
2021-08-10 12:08:15 +00:00
|
|
|
VersionedObject struct {
|
2021-09-07 06:17:12 +00:00
|
|
|
Name string
|
|
|
|
VersionID string
|
|
|
|
DeleteMarkVersion string
|
2022-03-31 06:24:57 +00:00
|
|
|
DeleteMarkerEtag string
|
2021-09-07 06:17:12 +00:00
|
|
|
Error error
|
2021-08-10 12:08:15 +00:00
|
|
|
}
|
|
|
|
|
2021-05-13 20:25:31 +00:00
|
|
|
// Client provides S3 API client interface.
|
2020-08-03 11:48:33 +00:00
|
|
|
Client interface {
|
2022-04-29 13:08:22 +00:00
|
|
|
Initialize(ctx context.Context, c EventListener) error
|
2021-10-19 15:08:07 +00:00
|
|
|
EphemeralKey() *keys.PublicKey
|
|
|
|
|
2022-02-28 08:02:05 +00:00
|
|
|
GetBucketSettings(ctx context.Context, bktInfo *data.BucketInfo) (*data.BucketSettings, error)
|
|
|
|
PutBucketSettings(ctx context.Context, p *PutSettingsParams) error
|
2021-08-09 14:29:44 +00:00
|
|
|
|
2021-10-04 14:32:35 +00:00
|
|
|
PutBucketCORS(ctx context.Context, p *PutCORSParams) error
|
2021-10-13 18:50:02 +00:00
|
|
|
GetBucketCORS(ctx context.Context, bktInfo *data.BucketInfo) (*data.CORSConfiguration, error)
|
2021-10-04 14:32:35 +00:00
|
|
|
DeleteBucketCORS(ctx context.Context, bktInfo *data.BucketInfo) error
|
|
|
|
|
2021-09-10 06:56:56 +00:00
|
|
|
ListBuckets(ctx context.Context) ([]*data.BucketInfo, error)
|
|
|
|
GetBucketInfo(ctx context.Context, name string) (*data.BucketInfo, error)
|
2022-03-18 13:04:09 +00:00
|
|
|
GetBucketACL(ctx context.Context, bktInfo *data.BucketInfo) (*BucketACL, error)
|
2021-07-21 11:59:46 +00:00
|
|
|
PutBucketACL(ctx context.Context, p *PutBucketACLParams) error
|
2022-03-18 13:04:09 +00:00
|
|
|
CreateBucket(ctx context.Context, p *CreateBucketParams) (*data.BucketInfo, error)
|
2021-06-23 20:25:00 +00:00
|
|
|
DeleteBucket(ctx context.Context, p *DeleteBucketParams) error
|
2020-08-03 11:48:33 +00:00
|
|
|
|
|
|
|
GetObject(ctx context.Context, p *GetObjectParams) error
|
2022-08-05 00:54:21 +00:00
|
|
|
GetObjectInfo(ctx context.Context, p *HeadObjectParams) (*data.ObjectInfo, error)
|
|
|
|
GetExtendedObjectInfo(ctx context.Context, p *HeadObjectParams) (*data.ExtendedObjectInfo, error)
|
2022-05-24 06:58:33 +00:00
|
|
|
|
2022-05-26 13:11:14 +00:00
|
|
|
GetLockInfo(ctx context.Context, obj *ObjectVersion) (*data.LockInfo, error)
|
|
|
|
PutLockInfo(ctx context.Context, p *ObjectVersion, lock *data.ObjectLock) error
|
|
|
|
|
2022-06-27 09:08:26 +00:00
|
|
|
GetBucketTagging(ctx context.Context, cnrID cid.ID) (map[string]string, error)
|
|
|
|
PutBucketTagging(ctx context.Context, cnrID cid.ID, tagSet map[string]string) error
|
|
|
|
DeleteBucketTagging(ctx context.Context, cnrID cid.ID) error
|
2022-05-24 06:58:33 +00:00
|
|
|
|
2022-06-07 09:12:07 +00:00
|
|
|
GetObjectTagging(ctx context.Context, p *ObjectVersion) (string, map[string]string, error)
|
2022-07-18 14:51:34 +00:00
|
|
|
PutObjectTagging(ctx context.Context, p *ObjectVersion, tagSet map[string]string) (*data.NodeVersion, error)
|
|
|
|
DeleteObjectTagging(ctx context.Context, p *ObjectVersion) (*data.NodeVersion, error)
|
2020-08-03 11:48:33 +00:00
|
|
|
|
2021-09-10 06:56:56 +00:00
|
|
|
PutObject(ctx context.Context, p *PutObjectParams) (*data.ObjectInfo, error)
|
2020-08-03 11:48:33 +00:00
|
|
|
|
2021-09-10 06:56:56 +00:00
|
|
|
CopyObject(ctx context.Context, p *CopyObjectParams) (*data.ObjectInfo, error)
|
2020-08-03 11:48:33 +00:00
|
|
|
|
2021-07-18 13:40:19 +00:00
|
|
|
ListObjectsV1(ctx context.Context, p *ListObjectsParamsV1) (*ListObjectsInfoV1, error)
|
|
|
|
ListObjectsV2(ctx context.Context, p *ListObjectsParamsV2) (*ListObjectsInfoV2, error)
|
2021-07-05 19:18:58 +00:00
|
|
|
ListObjectVersions(ctx context.Context, p *ListObjectVersionsParams) (*ListObjectVersionsInfo, error)
|
2020-08-03 11:48:33 +00:00
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
DeleteObjects(ctx context.Context, p *DeleteObjectParams) []*VersionedObject
|
2021-11-25 15:05:58 +00:00
|
|
|
|
2022-05-23 14:34:13 +00:00
|
|
|
CreateMultipartUpload(ctx context.Context, p *CreateMultipartParams) error
|
2022-05-24 14:55:56 +00:00
|
|
|
CompleteMultipartUpload(ctx context.Context, p *CompleteMultipartParams) (*UploadData, *data.ObjectInfo, error)
|
2022-05-24 11:30:37 +00:00
|
|
|
UploadPart(ctx context.Context, p *UploadPartParams) (string, error)
|
2021-11-25 15:05:58 +00:00
|
|
|
UploadPartCopy(ctx context.Context, p *UploadCopyParams) (*data.ObjectInfo, error)
|
|
|
|
ListMultipartUploads(ctx context.Context, p *ListMultipartUploadsParams) (*ListMultipartUploadsInfo, error)
|
|
|
|
AbortMultipartUpload(ctx context.Context, p *UploadInfoParams) error
|
|
|
|
ListParts(ctx context.Context, p *ListPartsParams) (*ListPartsInfo, error)
|
2022-02-17 18:58:51 +00:00
|
|
|
|
|
|
|
PutBucketNotificationConfiguration(ctx context.Context, p *PutBucketNotificationConfigurationParams) error
|
|
|
|
GetBucketNotificationConfiguration(ctx context.Context, bktInfo *data.BucketInfo) (*data.NotificationConfiguration, error)
|
2022-05-31 14:27:08 +00:00
|
|
|
|
|
|
|
// Compound methods for optimizations
|
|
|
|
|
|
|
|
// GetObjectTaggingAndLock unifies GetObjectTagging and GetLock methods in single tree service invocation.
|
2022-06-28 13:35:05 +00:00
|
|
|
GetObjectTaggingAndLock(ctx context.Context, p *ObjectVersion, nodeVersion *data.NodeVersion) (map[string]string, *data.LockInfo, error)
|
2020-08-03 11:48:33 +00:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2021-08-17 11:57:24 +00:00
|
|
|
const (
|
2022-05-26 13:11:14 +00:00
|
|
|
tagPrefix = "S3-Tag-"
|
2022-08-01 16:52:09 +00:00
|
|
|
|
|
|
|
AESEncryptionAlgorithm = "AES256"
|
|
|
|
AESKeySize = 32
|
|
|
|
AttributeEncryptionAlgorithm = api.NeoFSSystemMetadataPrefix + "Algorithm"
|
|
|
|
AttributeDecryptedSize = api.NeoFSSystemMetadataPrefix + "Decrypted-Size"
|
|
|
|
AttributeHMACSalt = api.NeoFSSystemMetadataPrefix + "HMAC-Salt"
|
|
|
|
AttributeHMACKey = api.NeoFSSystemMetadataPrefix + "HMAC-Key"
|
2021-08-17 11:57:24 +00:00
|
|
|
)
|
2021-08-17 08:04:42 +00:00
|
|
|
|
2021-08-10 12:08:15 +00:00
|
|
|
func (t *VersionedObject) String() string {
|
|
|
|
return t.Name + ":" + t.VersionID
|
|
|
|
}
|
|
|
|
|
2022-03-05 06:58:54 +00:00
|
|
|
func (f MsgHandlerFunc) HandleMessage(ctx context.Context, msg *nats.Msg) error {
|
|
|
|
return f(ctx, msg)
|
|
|
|
}
|
|
|
|
|
2022-08-01 16:52:09 +00:00
|
|
|
// NewEncryptionParams create new params to encrypt with provided key.
|
|
|
|
func NewEncryptionParams(key AES256Key) EncryptionParams {
|
|
|
|
return EncryptionParams{
|
|
|
|
enabled: true,
|
|
|
|
customerKey: key,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Key returns encryption key as slice.
|
|
|
|
func (p EncryptionParams) Key() []byte {
|
|
|
|
return p.customerKey[:]
|
|
|
|
}
|
|
|
|
|
|
|
|
// AESKey returns encryption key.
|
|
|
|
func (p EncryptionParams) AESKey() AES256Key {
|
|
|
|
return p.customerKey
|
|
|
|
}
|
|
|
|
|
|
|
|
// Enabled returns true if key isn't empty.
|
|
|
|
func (p EncryptionParams) Enabled() bool {
|
|
|
|
return p.enabled
|
|
|
|
}
|
|
|
|
|
|
|
|
// HMAC compute salted HMAC.
|
|
|
|
func (p EncryptionParams) HMAC() ([]byte, []byte, error) {
|
|
|
|
mac := hmac.New(sha256.New, p.Key())
|
|
|
|
|
|
|
|
salt := make([]byte, 16)
|
|
|
|
if _, err := rand.Read(salt); err != nil {
|
|
|
|
return nil, nil, errorsStd.New("failed to init create salt")
|
|
|
|
}
|
|
|
|
|
|
|
|
mac.Write(salt)
|
|
|
|
return mac.Sum(nil), salt, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// MatchObjectEncryption check if encryption params are valid for provided object.
|
|
|
|
func (p EncryptionParams) MatchObjectEncryption(encInfo data.EncryptionInfo) error {
|
|
|
|
if p.Enabled() != encInfo.Enabled {
|
|
|
|
return errorsStd.New("invalid encryption view")
|
|
|
|
}
|
|
|
|
|
|
|
|
if !encInfo.Enabled {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
hmacSalt, err := hex.DecodeString(encInfo.HMACSalt)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("invalid hmacSalt '%s': %w", encInfo.HMACSalt, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
hmacKey, err := hex.DecodeString(encInfo.HMACKey)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("invalid hmacKey '%s': %w", encInfo.HMACKey, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
mac := hmac.New(sha256.New, p.Key())
|
|
|
|
mac.Write(hmacSalt)
|
|
|
|
expectedHmacKey := mac.Sum(nil)
|
|
|
|
if !bytes.Equal(expectedHmacKey, hmacKey) {
|
|
|
|
return errorsStd.New("mismatched hmac key")
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-09-10 06:56:56 +00:00
|
|
|
// DefaultCachesConfigs returns filled configs.
|
2022-06-06 08:01:12 +00:00
|
|
|
func DefaultCachesConfigs(logger *zap.Logger) *CachesConfig {
|
2021-09-10 06:56:56 +00:00
|
|
|
return &CachesConfig{
|
2022-06-06 08:01:12 +00:00
|
|
|
Objects: cache.DefaultObjectsConfig(logger),
|
|
|
|
ObjectsList: cache.DefaultObjectsListConfig(logger),
|
|
|
|
Names: cache.DefaultObjectsNameConfig(logger),
|
|
|
|
Buckets: cache.DefaultBucketConfig(logger),
|
|
|
|
System: cache.DefaultSystemConfig(logger),
|
2021-09-10 06:56:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-13 16:56:58 +00:00
|
|
|
// NewLayer creates an instance of a layer. It checks credentials
|
|
|
|
// and establishes gRPC connection with the node.
|
2022-06-06 11:09:09 +00:00
|
|
|
func NewLayer(log *zap.Logger, neoFS NeoFS, config *Config) Client {
|
2020-08-03 11:48:33 +00:00
|
|
|
return &layer{
|
2022-03-01 19:02:24 +00:00
|
|
|
neoFS: neoFS,
|
2021-09-10 06:56:56 +00:00
|
|
|
log: log,
|
2021-11-22 09:16:05 +00:00
|
|
|
anonKey: config.AnonKey,
|
2022-01-11 10:09:34 +00:00
|
|
|
resolver: config.Resolver,
|
2021-11-22 09:16:05 +00:00
|
|
|
listsCache: cache.NewObjectsListCache(config.Caches.ObjectsList),
|
|
|
|
objCache: cache.New(config.Caches.Objects),
|
|
|
|
namesCache: cache.NewObjectsNameCache(config.Caches.Names),
|
|
|
|
bucketCache: cache.NewBucketCache(config.Caches.Buckets),
|
|
|
|
systemCache: cache.NewSystemCache(config.Caches.System),
|
2022-04-22 07:18:21 +00:00
|
|
|
treeService: config.TreeService,
|
2020-11-24 07:01:38 +00:00
|
|
|
}
|
2020-08-03 11:48:33 +00:00
|
|
|
}
|
|
|
|
|
2021-10-19 15:08:07 +00:00
|
|
|
func (n *layer) EphemeralKey() *keys.PublicKey {
|
|
|
|
return n.anonKey.Key.PublicKey()
|
|
|
|
}
|
|
|
|
|
2022-04-29 13:08:22 +00:00
|
|
|
func (n *layer) Initialize(ctx context.Context, c EventListener) error {
|
2022-03-05 06:58:54 +00:00
|
|
|
if n.IsNotificationEnabled() {
|
|
|
|
return fmt.Errorf("already initialized")
|
|
|
|
}
|
|
|
|
|
2022-03-17 14:03:06 +00:00
|
|
|
// todo add notification handlers (e.g. for lifecycles)
|
2022-03-05 06:58:54 +00:00
|
|
|
|
|
|
|
c.Listen(ctx)
|
|
|
|
|
|
|
|
n.ncontroller = c
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-02-08 15:34:31 +00:00
|
|
|
func (n *layer) IsNotificationEnabled() bool {
|
|
|
|
return n.ncontroller != nil
|
|
|
|
}
|
|
|
|
|
2022-04-13 16:56:58 +00:00
|
|
|
// IsAuthenticatedRequest checks if access box exists in the current request.
|
2022-01-21 09:52:16 +00:00
|
|
|
func IsAuthenticatedRequest(ctx context.Context) bool {
|
|
|
|
_, ok := ctx.Value(api.BoxData).(*accessbox.Box)
|
|
|
|
return ok
|
|
|
|
}
|
|
|
|
|
2020-11-27 12:36:15 +00:00
|
|
|
// Owner returns owner id from BearerToken (context) or from client owner.
|
2022-04-25 09:57:58 +00:00
|
|
|
func (n *layer) Owner(ctx context.Context) user.ID {
|
2022-06-01 14:00:30 +00:00
|
|
|
if bd, ok := ctx.Value(api.BoxData).(*accessbox.Box); ok && bd != nil && bd.Gate != nil && bd.Gate.BearerToken != nil {
|
|
|
|
return bearer.ResolveIssuer(*bd.Gate.BearerToken)
|
2020-11-27 12:36:15 +00:00
|
|
|
}
|
|
|
|
|
2022-04-25 09:57:58 +00:00
|
|
|
var ownerID user.ID
|
|
|
|
user.IDFromKey(&ownerID, (ecdsa.PublicKey)(*n.EphemeralKey()))
|
|
|
|
|
|
|
|
return ownerID
|
2020-11-27 12:36:15 +00:00
|
|
|
}
|
|
|
|
|
2022-06-06 11:09:09 +00:00
|
|
|
func (n *layer) prepareAuthParameters(ctx context.Context, prm *PrmAuth, bktOwner user.ID) {
|
2022-06-03 08:20:47 +00:00
|
|
|
if bd, ok := ctx.Value(api.BoxData).(*accessbox.Box); ok && bd != nil && bd.Gate != nil && bd.Gate.BearerToken != nil {
|
|
|
|
if bktOwner.Equals(bearer.ResolveIssuer(*bd.Gate.BearerToken)) {
|
2022-06-01 17:35:20 +00:00
|
|
|
prm.BearerToken = bd.Gate.BearerToken
|
|
|
|
return
|
|
|
|
}
|
2022-03-01 19:02:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
prm.PrivateKey = &n.anonKey.Key.PrivateKey
|
|
|
|
}
|
|
|
|
|
2021-05-20 10:14:17 +00:00
|
|
|
// GetBucketInfo returns bucket info by name.
|
2021-09-10 06:56:56 +00:00
|
|
|
func (n *layer) GetBucketInfo(ctx context.Context, name string) (*data.BucketInfo, error) {
|
2020-10-24 13:09:22 +00:00
|
|
|
name, err := url.QueryUnescape(name)
|
|
|
|
if err != nil {
|
2022-06-22 19:40:52 +00:00
|
|
|
return nil, fmt.Errorf("unescape bucket name: %w", err)
|
2020-10-24 13:09:22 +00:00
|
|
|
}
|
|
|
|
|
2021-08-18 13:48:58 +00:00
|
|
|
if bktInfo := n.bucketCache.Get(name); bktInfo != nil {
|
|
|
|
return bktInfo, nil
|
|
|
|
}
|
|
|
|
|
2021-11-22 09:16:05 +00:00
|
|
|
containerID, err := n.ResolveBucket(ctx, name)
|
|
|
|
if err != nil {
|
|
|
|
n.log.Debug("bucket not found", zap.Error(err))
|
2021-08-09 08:53:58 +00:00
|
|
|
return nil, errors.GetAPIError(errors.ErrNoSuchBucket)
|
2020-08-03 11:48:33 +00:00
|
|
|
}
|
|
|
|
|
2022-06-27 09:08:26 +00:00
|
|
|
return n.containerInfo(ctx, containerID)
|
2020-08-03 11:48:33 +00:00
|
|
|
}
|
|
|
|
|
2021-07-21 11:59:46 +00:00
|
|
|
// GetBucketACL returns bucket acl info by name.
|
2022-03-18 13:04:09 +00:00
|
|
|
func (n *layer) GetBucketACL(ctx context.Context, bktInfo *data.BucketInfo) (*BucketACL, error) {
|
|
|
|
eACL, err := n.GetContainerEACL(ctx, bktInfo.CID)
|
2021-07-21 11:59:46 +00:00
|
|
|
if err != nil {
|
2022-06-22 19:40:52 +00:00
|
|
|
return nil, fmt.Errorf("get container eacl: %w", err)
|
2021-07-21 11:59:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return &BucketACL{
|
2022-03-18 13:04:09 +00:00
|
|
|
Info: bktInfo,
|
2022-03-01 19:02:24 +00:00
|
|
|
EACL: eACL,
|
2021-07-21 11:59:46 +00:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2022-04-13 16:56:58 +00:00
|
|
|
// PutBucketACL puts bucket acl by name.
|
2021-07-21 11:59:46 +00:00
|
|
|
func (n *layer) PutBucketACL(ctx context.Context, param *PutBucketACLParams) error {
|
2022-06-21 15:21:20 +00:00
|
|
|
return n.setContainerEACLTable(ctx, param.BktInfo.CID, param.EACL, param.SessionToken)
|
2021-07-21 11:59:46 +00:00
|
|
|
}
|
|
|
|
|
2022-04-13 16:56:58 +00:00
|
|
|
// ListBuckets returns all user containers. The name of the bucket is a container
|
2020-08-03 11:48:33 +00:00
|
|
|
// id. Timestamp is omitted since it is not saved in neofs container.
|
2021-09-10 06:56:56 +00:00
|
|
|
func (n *layer) ListBuckets(ctx context.Context) ([]*data.BucketInfo, error) {
|
2020-08-03 11:48:33 +00:00
|
|
|
return n.containerList(ctx)
|
|
|
|
}
|
|
|
|
|
2022-08-01 16:52:09 +00:00
|
|
|
func formEncryptedParts(header string) ([]EncryptedPart, error) {
|
|
|
|
partInfos := strings.Split(header, ",")
|
|
|
|
result := make([]EncryptedPart, len(partInfos))
|
|
|
|
|
|
|
|
for i, partInfo := range partInfos {
|
|
|
|
part, err := parseCompletedPartHeader(partInfo)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
encPartSize, err := sio.EncryptedSize(uint64(part.Size))
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("compute encrypted size: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
result[i] = EncryptedPart{
|
|
|
|
Part: *part,
|
|
|
|
EncryptedSize: int64(encPartSize),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type decrypter struct {
|
|
|
|
reader io.Reader
|
|
|
|
decReader io.Reader
|
|
|
|
parts []EncryptedPart
|
|
|
|
currentPart int
|
|
|
|
encryption EncryptionParams
|
|
|
|
|
|
|
|
rangeParam *RangeParams
|
|
|
|
|
|
|
|
partDataRemain int64
|
|
|
|
encPartRangeLen int64
|
|
|
|
|
|
|
|
seqNumber uint64
|
|
|
|
decLen int64
|
|
|
|
skipLen uint64
|
|
|
|
|
|
|
|
ln uint64
|
|
|
|
off uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d decrypter) decLength() int64 {
|
|
|
|
return d.decLen
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d decrypter) encLength() uint64 {
|
|
|
|
return d.ln
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d decrypter) encOffset() uint64 {
|
|
|
|
return d.off
|
|
|
|
}
|
|
|
|
|
|
|
|
func getDecryptReader(p *GetObjectParams) (*decrypter, error) {
|
|
|
|
if !p.Encryption.Enabled() {
|
|
|
|
return nil, errorsStd.New("couldn't create decrypter with disabled encryption")
|
|
|
|
}
|
|
|
|
|
|
|
|
rangeParam := p.Range
|
|
|
|
|
|
|
|
var err error
|
|
|
|
var parts []EncryptedPart
|
|
|
|
header := p.ObjectInfo.Headers[UploadCompletedParts]
|
|
|
|
if len(header) != 0 {
|
|
|
|
parts, err = formEncryptedParts(header)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("form parts: %w", err)
|
|
|
|
}
|
|
|
|
if rangeParam == nil {
|
|
|
|
decSizeHeader := p.ObjectInfo.Headers[AttributeDecryptedSize]
|
|
|
|
size, err := strconv.ParseUint(decSizeHeader, 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("parse dec size header '%s': %w", decSizeHeader, err)
|
|
|
|
}
|
|
|
|
rangeParam = &RangeParams{
|
|
|
|
Start: 0,
|
|
|
|
End: size - 1,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
decSize, err := sio.DecryptedSize(uint64(p.ObjectInfo.Size))
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("compute decrypted size: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
parts = []EncryptedPart{{
|
|
|
|
Part: Part{Size: int64(decSize)},
|
|
|
|
EncryptedSize: p.ObjectInfo.Size,
|
|
|
|
}}
|
|
|
|
}
|
|
|
|
|
|
|
|
if rangeParam != nil && rangeParam.Start > rangeParam.End {
|
|
|
|
return nil, fmt.Errorf("invalid range: %d %d", rangeParam.Start, rangeParam.End)
|
|
|
|
}
|
|
|
|
|
|
|
|
decReader := &decrypter{
|
|
|
|
parts: parts,
|
|
|
|
rangeParam: rangeParam,
|
|
|
|
encryption: p.Encryption,
|
|
|
|
}
|
|
|
|
|
|
|
|
decReader.initRangeParams()
|
|
|
|
|
|
|
|
return decReader, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
const (
|
|
|
|
blockSize = 1 << 16 // 64KB
|
|
|
|
fullBlockSize = blockSize + 32
|
|
|
|
)
|
|
|
|
|
|
|
|
func (d *decrypter) initRangeParams() {
|
|
|
|
d.partDataRemain = d.parts[d.currentPart].Size
|
|
|
|
d.encPartRangeLen = d.parts[d.currentPart].EncryptedSize
|
|
|
|
if d.rangeParam == nil {
|
|
|
|
d.decLen = d.partDataRemain
|
|
|
|
d.ln = uint64(d.encPartRangeLen)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
start, end := d.rangeParam.Start, d.rangeParam.End
|
|
|
|
|
|
|
|
var sum, encSum uint64
|
|
|
|
var partStart int
|
|
|
|
for i, part := range d.parts {
|
|
|
|
if start < sum+uint64(part.Size) {
|
|
|
|
partStart = i
|
|
|
|
break
|
|
|
|
}
|
|
|
|
sum += uint64(part.Size)
|
|
|
|
encSum += uint64(part.EncryptedSize)
|
|
|
|
}
|
|
|
|
|
|
|
|
d.skipLen = (start - sum) % blockSize
|
|
|
|
d.seqNumber = (start - sum) / blockSize
|
|
|
|
encOffPart := d.seqNumber * fullBlockSize
|
|
|
|
d.off = encSum + encOffPart
|
|
|
|
d.encPartRangeLen = d.encPartRangeLen - int64(encOffPart)
|
|
|
|
d.partDataRemain = d.partDataRemain + int64(sum-start)
|
|
|
|
|
|
|
|
var partEnd int
|
|
|
|
for i, part := range d.parts[partStart:] {
|
|
|
|
index := partStart + i
|
|
|
|
if end < sum+uint64(part.Size) {
|
|
|
|
partEnd = index
|
|
|
|
break
|
|
|
|
}
|
|
|
|
sum += uint64(part.Size)
|
|
|
|
encSum += uint64(part.EncryptedSize)
|
|
|
|
}
|
|
|
|
|
|
|
|
payloadPartEnd := (end - sum) / blockSize
|
|
|
|
endEnc := encSum + (payloadPartEnd+1)*fullBlockSize
|
|
|
|
|
|
|
|
endPartEnc := encSum + uint64(d.parts[partEnd].EncryptedSize)
|
|
|
|
if endPartEnc < endEnc {
|
|
|
|
endEnc = endPartEnc
|
|
|
|
}
|
|
|
|
d.ln = endEnc - d.off
|
|
|
|
d.decLen = int64(end - start + 1)
|
|
|
|
|
|
|
|
if int64(d.ln) < d.encPartRangeLen {
|
|
|
|
d.encPartRangeLen = int64(d.ln)
|
|
|
|
}
|
|
|
|
if d.decLen < d.partDataRemain {
|
|
|
|
d.partDataRemain = d.decLen
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *decrypter) updateRangeParams() {
|
|
|
|
d.partDataRemain = d.parts[d.currentPart].Size
|
|
|
|
d.encPartRangeLen = d.parts[d.currentPart].EncryptedSize
|
|
|
|
d.seqNumber = 0
|
|
|
|
d.skipLen = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *decrypter) Read(p []byte) (int, error) {
|
|
|
|
if int64(len(p)) < d.partDataRemain {
|
|
|
|
n, err := d.decReader.Read(p)
|
|
|
|
if err != nil {
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
d.partDataRemain -= int64(n)
|
|
|
|
return n, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
n1, err := io.ReadFull(d.decReader, p[:d.partDataRemain])
|
|
|
|
if err != nil {
|
|
|
|
return n1, err
|
|
|
|
}
|
|
|
|
|
|
|
|
d.currentPart++
|
|
|
|
if d.currentPart == len(d.parts) {
|
|
|
|
return n1, io.EOF
|
|
|
|
}
|
|
|
|
|
|
|
|
d.updateRangeParams()
|
|
|
|
|
|
|
|
err = d.initNextDecReader()
|
|
|
|
if err != nil {
|
|
|
|
return n1, err
|
|
|
|
}
|
|
|
|
|
|
|
|
n2, err := d.decReader.Read(p[n1:])
|
|
|
|
if err != nil {
|
|
|
|
return n1 + n2, err
|
|
|
|
}
|
|
|
|
|
|
|
|
d.partDataRemain -= int64(n2)
|
|
|
|
|
|
|
|
return n1 + n2, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *decrypter) SetReader(r io.Reader) error {
|
|
|
|
d.reader = r
|
|
|
|
return d.initNextDecReader()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *decrypter) initNextDecReader() error {
|
|
|
|
if d.reader == nil {
|
|
|
|
return errorsStd.New("reader isn't set")
|
|
|
|
}
|
|
|
|
|
|
|
|
r, err := sio.DecryptReader(io.LimitReader(d.reader, d.encPartRangeLen),
|
|
|
|
sio.Config{
|
|
|
|
MinVersion: sio.Version20,
|
|
|
|
SequenceNumber: uint32(d.seqNumber),
|
|
|
|
Key: d.encryption.Key(),
|
|
|
|
CipherSuites: []byte{sio.AES_256_GCM},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("couldn't create decrypter: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if d.skipLen > 0 {
|
|
|
|
if _, err = io.CopyN(io.Discard, r, int64(d.skipLen)); err != nil {
|
|
|
|
return fmt.Errorf("couldn't skip some bytes: %w", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
d.decReader = r
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-08-03 11:48:33 +00:00
|
|
|
// GetObject from storage.
|
|
|
|
func (n *layer) GetObject(ctx context.Context, p *GetObjectParams) error {
|
2022-02-08 16:54:04 +00:00
|
|
|
var params getParams
|
2020-08-03 11:48:33 +00:00
|
|
|
|
2022-05-31 12:38:06 +00:00
|
|
|
params.oid = p.ObjectInfo.ID
|
2022-06-02 16:56:04 +00:00
|
|
|
params.bktInfo = p.BucketInfo
|
2020-10-19 01:04:37 +00:00
|
|
|
|
2022-08-01 16:52:09 +00:00
|
|
|
var decReader *decrypter
|
|
|
|
if p.Encryption.Enabled() {
|
|
|
|
var err error
|
|
|
|
decReader, err = getDecryptReader(p)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("creating decrypter: %w", err)
|
|
|
|
}
|
|
|
|
params.off = decReader.encOffset()
|
|
|
|
params.ln = decReader.encLength()
|
|
|
|
} else {
|
|
|
|
if p.Range != nil {
|
|
|
|
if p.Range.Start > p.Range.End {
|
|
|
|
panic("invalid range")
|
|
|
|
}
|
|
|
|
params.ln = p.Range.End - p.Range.Start + 1
|
|
|
|
params.off = p.Range.Start
|
2022-02-08 16:54:04 +00:00
|
|
|
}
|
2021-06-24 11:10:00 +00:00
|
|
|
}
|
2020-08-03 11:48:33 +00:00
|
|
|
|
2022-03-02 16:09:02 +00:00
|
|
|
payload, err := n.initObjectPayloadReader(ctx, params)
|
2021-05-20 10:14:17 +00:00
|
|
|
if err != nil {
|
2022-03-02 16:09:02 +00:00
|
|
|
return fmt.Errorf("init object payload reader: %w", err)
|
|
|
|
}
|
|
|
|
|
2022-08-01 16:52:09 +00:00
|
|
|
bufSize := uint64(32 * 1024) // configure?
|
|
|
|
if params.ln != 0 && params.ln < bufSize {
|
|
|
|
bufSize = params.ln
|
2022-03-02 16:09:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// alloc buffer for copying
|
2022-08-01 16:52:09 +00:00
|
|
|
buf := make([]byte, bufSize) // sync-pool it?
|
|
|
|
|
|
|
|
r := payload
|
|
|
|
if decReader != nil {
|
|
|
|
if err = decReader.SetReader(payload); err != nil {
|
|
|
|
return fmt.Errorf("set reader to decrypter: %w", err)
|
|
|
|
}
|
|
|
|
r = io.LimitReader(decReader, decReader.decLength())
|
|
|
|
}
|
2022-03-02 16:09:02 +00:00
|
|
|
|
|
|
|
// copy full payload
|
2022-08-01 16:52:09 +00:00
|
|
|
written, err := io.CopyBuffer(p.Writer, r, buf)
|
2022-03-02 16:09:02 +00:00
|
|
|
if err != nil {
|
2022-08-01 16:52:09 +00:00
|
|
|
return fmt.Errorf("copy object payload written: '%d', decLength: '%d', params.ln: '%d' : %w", written, decReader.decLength(), params.ln, err)
|
2021-05-20 10:14:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
2020-08-03 11:48:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetObjectInfo returns meta information about the object.
|
2022-08-05 00:54:21 +00:00
|
|
|
func (n *layer) GetObjectInfo(ctx context.Context, p *HeadObjectParams) (*data.ObjectInfo, error) {
|
|
|
|
extendedObjectInfo, err := n.GetExtendedObjectInfo(ctx, p)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return extendedObjectInfo.ObjectInfo, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetExtendedObjectInfo returns meta information and corresponding info from the tree service about the object.
|
|
|
|
func (n *layer) GetExtendedObjectInfo(ctx context.Context, p *HeadObjectParams) (*data.ExtendedObjectInfo, error) {
|
2021-08-10 10:03:09 +00:00
|
|
|
if len(p.VersionID) == 0 {
|
2022-03-18 13:04:09 +00:00
|
|
|
return n.headLastVersionIfNotDeleted(ctx, p.BktInfo, p.Object)
|
2021-08-10 12:08:15 +00:00
|
|
|
}
|
|
|
|
|
2022-03-18 13:04:09 +00:00
|
|
|
return n.headVersion(ctx, p.BktInfo, p)
|
2020-08-03 11:48:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// CopyObject from one bucket into another bucket.
|
2021-09-10 06:56:56 +00:00
|
|
|
func (n *layer) CopyObject(ctx context.Context, p *CopyObjectParams) (*data.ObjectInfo, error) {
|
2020-08-03 11:48:33 +00:00
|
|
|
pr, pw := io.Pipe()
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
err := n.GetObject(ctx, &GetObjectParams{
|
2021-08-10 10:03:09 +00:00
|
|
|
ObjectInfo: p.SrcObject,
|
|
|
|
Writer: pw,
|
2021-11-25 15:05:58 +00:00
|
|
|
Range: p.Range,
|
2022-06-02 16:56:04 +00:00
|
|
|
BucketInfo: p.ScrBktInfo,
|
2022-08-01 16:52:09 +00:00
|
|
|
Encryption: p.Encryption,
|
2020-08-03 11:48:33 +00:00
|
|
|
})
|
|
|
|
|
2020-10-24 13:09:22 +00:00
|
|
|
if err = pw.CloseWithError(err); err != nil {
|
|
|
|
n.log.Error("could not get object", zap.Error(err))
|
|
|
|
}
|
2020-08-03 11:48:33 +00:00
|
|
|
}()
|
|
|
|
|
|
|
|
return n.PutObject(ctx, &PutObjectParams{
|
2022-08-01 16:52:09 +00:00
|
|
|
BktInfo: p.DstBktInfo,
|
|
|
|
Object: p.DstObject,
|
|
|
|
Size: p.SrcSize,
|
|
|
|
Reader: pr,
|
|
|
|
Header: p.Header,
|
|
|
|
Encryption: p.Encryption,
|
2020-08-03 11:48:33 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-06-27 09:33:36 +00:00
|
|
|
func getRandomOID() (oid.ID, error) {
|
2022-05-18 07:48:30 +00:00
|
|
|
b := [32]byte{}
|
|
|
|
if _, err := rand.Read(b[:]); err != nil {
|
2022-06-27 09:33:36 +00:00
|
|
|
return oid.ID{}, err
|
2022-05-18 07:48:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var objID oid.ID
|
|
|
|
objID.SetSHA256(b)
|
2022-06-27 09:33:36 +00:00
|
|
|
return objID, nil
|
2022-05-18 07:48:30 +00:00
|
|
|
}
|
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
func (n *layer) deleteObject(ctx context.Context, bkt *data.BucketInfo, settings *data.BucketSettings, obj *VersionedObject) *VersionedObject {
|
2022-07-19 14:58:18 +00:00
|
|
|
if len(obj.VersionID) != 0 || settings.Unversioned() {
|
2022-06-24 12:39:30 +00:00
|
|
|
var nodeVersion *data.NodeVersion
|
|
|
|
if nodeVersion, obj.Error = n.getNodeVersionToDelete(ctx, bkt, obj); obj.Error != nil {
|
2022-07-25 13:00:35 +00:00
|
|
|
return dismissNotFoundError(obj)
|
2022-06-15 12:17:29 +00:00
|
|
|
}
|
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
if obj.DeleteMarkVersion, obj.Error = n.removeOldVersion(ctx, bkt, nodeVersion, obj); obj.Error != nil {
|
2022-05-18 07:48:30 +00:00
|
|
|
return obj
|
|
|
|
}
|
2022-05-19 09:17:56 +00:00
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
obj.Error = n.treeService.RemoveVersion(ctx, bkt.CID, nodeVersion.ID)
|
2022-07-05 06:25:23 +00:00
|
|
|
n.listsCache.CleanCacheEntriesContainingObject(obj.Name, bkt.CID)
|
2022-06-24 12:39:30 +00:00
|
|
|
return obj
|
|
|
|
}
|
2022-05-19 09:17:56 +00:00
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
var newVersion *data.NodeVersion
|
2022-05-31 08:12:53 +00:00
|
|
|
|
2022-07-19 14:58:18 +00:00
|
|
|
if settings.VersioningSuspended() {
|
2022-08-08 22:35:26 +00:00
|
|
|
obj.VersionID = data.UnversionedObjectVersionID
|
2022-01-19 09:02:08 +00:00
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
var nodeVersion *data.NodeVersion
|
|
|
|
if nodeVersion, obj.Error = n.getNodeVersionToDelete(ctx, bkt, obj); obj.Error != nil {
|
2022-07-25 13:00:35 +00:00
|
|
|
return dismissNotFoundError(obj)
|
2022-06-15 12:17:29 +00:00
|
|
|
}
|
|
|
|
|
2022-07-25 13:00:35 +00:00
|
|
|
if obj.DeleteMarkVersion, obj.Error = n.removeOldVersion(ctx, bkt, nodeVersion, obj); obj.Error != nil {
|
2021-09-07 06:17:12 +00:00
|
|
|
return obj
|
2021-08-17 11:23:49 +00:00
|
|
|
}
|
2020-08-03 11:48:33 +00:00
|
|
|
}
|
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
randOID, err := getRandomOID()
|
|
|
|
if err != nil {
|
|
|
|
obj.Error = fmt.Errorf("couldn't get random oid: %w", err)
|
|
|
|
return obj
|
|
|
|
}
|
2022-07-05 08:04:21 +00:00
|
|
|
|
|
|
|
obj.DeleteMarkVersion = randOID.EncodeToString()
|
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
newVersion = &data.NodeVersion{
|
|
|
|
BaseNodeVersion: data.BaseNodeVersion{
|
|
|
|
OID: randOID,
|
|
|
|
FilePath: obj.Name,
|
|
|
|
},
|
|
|
|
DeleteMarker: &data.DeleteMarkerInfo{
|
|
|
|
Created: time.Now(),
|
|
|
|
Owner: n.Owner(ctx),
|
|
|
|
},
|
2022-07-19 14:58:18 +00:00
|
|
|
IsUnversioned: settings.VersioningSuspended(),
|
2022-06-24 12:39:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if obj.Error = n.treeService.AddVersion(ctx, bkt.CID, newVersion); obj.Error != nil {
|
|
|
|
return obj
|
|
|
|
}
|
2022-07-05 06:25:23 +00:00
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
n.namesCache.Delete(bkt.Name + "/" + obj.Name)
|
2022-07-05 06:25:23 +00:00
|
|
|
n.listsCache.CleanCacheEntriesContainingObject(obj.Name, bkt.CID)
|
2022-03-09 10:23:33 +00:00
|
|
|
|
2021-09-07 06:17:12 +00:00
|
|
|
return obj
|
2020-08-03 11:48:33 +00:00
|
|
|
}
|
|
|
|
|
2022-07-25 13:00:35 +00:00
|
|
|
func dismissNotFoundError(obj *VersionedObject) *VersionedObject {
|
|
|
|
if errors.IsS3Error(obj.Error, errors.ErrNoSuchKey) ||
|
|
|
|
errors.IsS3Error(obj.Error, errors.ErrNoSuchVersion) {
|
|
|
|
obj.Error = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return obj
|
|
|
|
}
|
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
func (n *layer) getNodeVersionToDelete(ctx context.Context, bkt *data.BucketInfo, obj *VersionedObject) (*data.NodeVersion, error) {
|
|
|
|
objVersion := &ObjectVersion{
|
|
|
|
BktInfo: bkt,
|
|
|
|
ObjectName: obj.Name,
|
|
|
|
VersionID: obj.VersionID,
|
|
|
|
NoErrorOnDeleteMarker: true,
|
|
|
|
}
|
|
|
|
|
|
|
|
return n.getNodeVersion(ctx, objVersion)
|
|
|
|
}
|
|
|
|
|
2022-06-15 12:17:29 +00:00
|
|
|
func (n *layer) removeOldVersion(ctx context.Context, bkt *data.BucketInfo, nodeVersion *data.NodeVersion, obj *VersionedObject) (string, error) {
|
2022-08-09 12:10:04 +00:00
|
|
|
if nodeVersion.IsDeleteMarker() {
|
2022-06-15 12:17:29 +00:00
|
|
|
return obj.VersionID, nil
|
2022-05-19 09:17:56 +00:00
|
|
|
}
|
|
|
|
|
2022-06-15 12:17:29 +00:00
|
|
|
return "", n.objectDelete(ctx, bkt, nodeVersion.OID)
|
2022-05-18 06:51:12 +00:00
|
|
|
}
|
|
|
|
|
2020-08-03 11:48:33 +00:00
|
|
|
// DeleteObjects from the storage.
|
2022-06-24 12:39:30 +00:00
|
|
|
func (n *layer) DeleteObjects(ctx context.Context, p *DeleteObjectParams) []*VersionedObject {
|
2022-03-31 06:24:29 +00:00
|
|
|
for i, obj := range p.Objects {
|
2022-06-24 12:39:30 +00:00
|
|
|
p.Objects[i] = n.deleteObject(ctx, p.BktInfo, p.Settings, obj)
|
2020-08-03 11:48:33 +00:00
|
|
|
}
|
|
|
|
|
2022-06-24 12:39:30 +00:00
|
|
|
return p.Objects
|
2020-08-03 11:48:33 +00:00
|
|
|
}
|
2021-06-23 20:21:15 +00:00
|
|
|
|
2022-03-18 13:04:09 +00:00
|
|
|
func (n *layer) CreateBucket(ctx context.Context, p *CreateBucketParams) (*data.BucketInfo, error) {
|
2022-02-15 09:06:00 +00:00
|
|
|
bktInfo, err := n.GetBucketInfo(ctx, p.Name)
|
2021-07-07 14:56:29 +00:00
|
|
|
if err != nil {
|
2021-08-09 08:53:58 +00:00
|
|
|
if errors.IsS3Error(err, errors.ErrNoSuchBucket) {
|
2021-07-07 14:56:29 +00:00
|
|
|
return n.createContainer(ctx, p)
|
|
|
|
}
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2022-06-21 15:21:20 +00:00
|
|
|
if p.SessionContainerCreation != nil && session.IssuedBy(*p.SessionContainerCreation, bktInfo.Owner) {
|
2022-02-15 09:06:00 +00:00
|
|
|
return nil, errors.GetAPIError(errors.ErrBucketAlreadyOwnedByYou)
|
|
|
|
}
|
|
|
|
|
2021-08-09 08:53:58 +00:00
|
|
|
return nil, errors.GetAPIError(errors.ErrBucketAlreadyExists)
|
2021-06-23 20:21:15 +00:00
|
|
|
}
|
2021-06-23 20:25:00 +00:00
|
|
|
|
2022-06-27 09:08:26 +00:00
|
|
|
func (n *layer) ResolveBucket(ctx context.Context, name string) (cid.ID, error) {
|
2022-04-25 09:57:58 +00:00
|
|
|
var cnrID cid.ID
|
|
|
|
if err := cnrID.DecodeString(name); err != nil {
|
2022-01-11 10:09:34 +00:00
|
|
|
return n.resolver.Resolve(ctx, name)
|
2021-11-22 09:16:05 +00:00
|
|
|
}
|
|
|
|
|
2022-06-27 09:08:26 +00:00
|
|
|
return cnrID, nil
|
2021-11-22 09:16:05 +00:00
|
|
|
}
|
|
|
|
|
2021-06-23 20:25:00 +00:00
|
|
|
func (n *layer) DeleteBucket(ctx context.Context, p *DeleteBucketParams) error {
|
2022-07-21 09:05:47 +00:00
|
|
|
nodeVersions, err := n.bucketNodeVersions(ctx, p.BktInfo, "")
|
2021-08-10 12:08:15 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2022-07-21 09:05:47 +00:00
|
|
|
if len(nodeVersions) != 0 {
|
2021-08-11 10:02:13 +00:00
|
|
|
return errors.GetAPIError(errors.ErrBucketNotEmpty)
|
2021-08-10 12:08:15 +00:00
|
|
|
}
|
|
|
|
|
2022-03-18 13:04:09 +00:00
|
|
|
n.bucketCache.Delete(p.BktInfo.Name)
|
2022-06-24 04:51:35 +00:00
|
|
|
return n.neoFS.DeleteContainer(ctx, p.BktInfo.CID, p.SessionToken)
|
2021-08-09 14:29:44 +00:00
|
|
|
}
|