2020-10-28 15:28:06 +00:00
|
|
|
// Copyright (C) 2020 Storj Labs, Inc.
|
|
|
|
// See LICENSE for copying information.
|
|
|
|
|
|
|
|
package metabase
|
|
|
|
|
|
|
|
import (
|
|
|
|
"database/sql/driver"
|
|
|
|
"encoding/binary"
|
|
|
|
|
|
|
|
"github.com/jackc/pgtype"
|
|
|
|
|
|
|
|
"storj.io/common/storj"
|
|
|
|
)
|
|
|
|
|
|
|
|
type encryptionParameters struct {
|
|
|
|
*storj.EncryptionParameters
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that EncryptionParameters layout doesn't change.
|
|
|
|
var _ struct {
|
|
|
|
CipherSuite storj.CipherSuite
|
|
|
|
BlockSize int32
|
|
|
|
} = storj.EncryptionParameters{}
|
|
|
|
|
|
|
|
// Value implements sql/driver.Valuer interface.
|
|
|
|
func (params encryptionParameters) Value() (driver.Value, error) {
|
|
|
|
var bytes [8]byte
|
|
|
|
bytes[0] = byte(params.CipherSuite)
|
|
|
|
binary.LittleEndian.PutUint32(bytes[1:], uint32(params.BlockSize))
|
|
|
|
return int64(binary.LittleEndian.Uint64(bytes[:])), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Scan implements sql.Scanner interface.
|
|
|
|
func (params encryptionParameters) Scan(value interface{}) error {
|
|
|
|
switch value := value.(type) {
|
|
|
|
case int64:
|
|
|
|
var bytes [8]byte
|
|
|
|
binary.LittleEndian.PutUint64(bytes[:], uint64(value))
|
|
|
|
params.CipherSuite = storj.CipherSuite(bytes[0])
|
|
|
|
params.BlockSize = int32(binary.LittleEndian.Uint32(bytes[1:]))
|
|
|
|
return nil
|
|
|
|
default:
|
|
|
|
return Error.New("unable to scan %T into EncryptionParameters", value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Value implements sql/driver.Valuer interface.
|
|
|
|
func (params SegmentPosition) Value() (driver.Value, error) {
|
|
|
|
return int64(params.Encode()), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Scan implements sql.Scanner interface.
|
|
|
|
func (params *SegmentPosition) Scan(value interface{}) error {
|
|
|
|
switch value := value.(type) {
|
|
|
|
case int64:
|
|
|
|
*params = SegmentPositionFromEncoded(uint64(value))
|
|
|
|
return nil
|
|
|
|
default:
|
2022-03-23 13:16:46 +00:00
|
|
|
return Error.New("unable to scan %T into SegmentPosition", value)
|
2020-10-28 15:28:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type redundancyScheme struct {
|
|
|
|
*storj.RedundancyScheme
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that RedundancyScheme layout doesn't change.
|
|
|
|
var _ struct {
|
|
|
|
Algorithm storj.RedundancyAlgorithm
|
|
|
|
ShareSize int32
|
|
|
|
RequiredShares int16
|
|
|
|
RepairShares int16
|
|
|
|
OptimalShares int16
|
|
|
|
TotalShares int16
|
|
|
|
} = storj.RedundancyScheme{}
|
|
|
|
|
|
|
|
func (params redundancyScheme) Value() (driver.Value, error) {
|
|
|
|
switch {
|
|
|
|
case params.ShareSize < 0 || params.ShareSize >= 1<<24:
|
|
|
|
return nil, Error.New("invalid share size %v", params.ShareSize)
|
|
|
|
case params.RequiredShares < 0 || params.RequiredShares >= 1<<8:
|
|
|
|
return nil, Error.New("invalid required shares %v", params.RequiredShares)
|
|
|
|
case params.RepairShares < 0 || params.RepairShares >= 1<<8:
|
|
|
|
return nil, Error.New("invalid repair shares %v", params.RepairShares)
|
|
|
|
case params.OptimalShares < 0 || params.OptimalShares >= 1<<8:
|
|
|
|
return nil, Error.New("invalid optimal shares %v", params.OptimalShares)
|
|
|
|
case params.TotalShares < 0 || params.TotalShares >= 1<<8:
|
|
|
|
return nil, Error.New("invalid total shares %v", params.TotalShares)
|
|
|
|
}
|
|
|
|
|
|
|
|
var bytes [8]byte
|
|
|
|
bytes[0] = byte(params.Algorithm)
|
|
|
|
|
|
|
|
// little endian uint32
|
|
|
|
bytes[1] = byte(params.ShareSize >> 0)
|
|
|
|
bytes[2] = byte(params.ShareSize >> 8)
|
|
|
|
bytes[3] = byte(params.ShareSize >> 16)
|
|
|
|
|
|
|
|
bytes[4] = byte(params.RequiredShares)
|
|
|
|
bytes[5] = byte(params.RepairShares)
|
|
|
|
bytes[6] = byte(params.OptimalShares)
|
|
|
|
bytes[7] = byte(params.TotalShares)
|
|
|
|
|
|
|
|
return int64(binary.LittleEndian.Uint64(bytes[:])), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (params redundancyScheme) Scan(value interface{}) error {
|
|
|
|
switch value := value.(type) {
|
|
|
|
case int64:
|
|
|
|
var bytes [8]byte
|
|
|
|
binary.LittleEndian.PutUint64(bytes[:], uint64(value))
|
|
|
|
|
|
|
|
params.Algorithm = storj.RedundancyAlgorithm(bytes[0])
|
|
|
|
|
|
|
|
// little endian uint32
|
|
|
|
params.ShareSize = int32(bytes[1]) | int32(bytes[2])<<8 | int32(bytes[3])<<16
|
|
|
|
|
|
|
|
params.RequiredShares = int16(bytes[4])
|
|
|
|
params.RepairShares = int16(bytes[5])
|
|
|
|
params.OptimalShares = int16(bytes[6])
|
|
|
|
params.TotalShares = int16(bytes[7])
|
|
|
|
|
|
|
|
return nil
|
|
|
|
default:
|
|
|
|
return Error.New("unable to scan %T into RedundancyScheme", value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Value implements sql/driver.Valuer interface.
|
|
|
|
func (pieces Pieces) Value() (driver.Value, error) {
|
|
|
|
if len(pieces) == 0 {
|
|
|
|
arr := &pgtype.ByteaArray{Status: pgtype.Null}
|
|
|
|
return arr.Value()
|
|
|
|
}
|
|
|
|
|
|
|
|
elems := make([]pgtype.Bytea, len(pieces))
|
|
|
|
for i, piece := range pieces {
|
|
|
|
var buf [2 + len(piece.StorageNode)]byte
|
|
|
|
binary.BigEndian.PutUint16(buf[0:], piece.Number)
|
|
|
|
copy(buf[2:], piece.StorageNode[:])
|
|
|
|
|
|
|
|
elems[i].Bytes = buf[:]
|
|
|
|
elems[i].Status = pgtype.Present
|
|
|
|
}
|
|
|
|
|
|
|
|
arr := &pgtype.ByteaArray{
|
|
|
|
Elements: elems,
|
|
|
|
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(pieces)), LowerBound: 1}},
|
|
|
|
Status: pgtype.Present,
|
|
|
|
}
|
|
|
|
return arr.Value()
|
|
|
|
}
|
|
|
|
|
|
|
|
type unexpectedDimension struct{}
|
|
|
|
type invalidElementLength struct{}
|
|
|
|
|
|
|
|
func (unexpectedDimension) Error() string { return "unexpected data dimension" }
|
|
|
|
func (invalidElementLength) Error() string { return "invalid element length" }
|
|
|
|
|
|
|
|
// Scan implements sql.Scanner interface.
|
|
|
|
func (pieces *Pieces) Scan(value interface{}) error {
|
|
|
|
var arr pgtype.ByteaArray
|
|
|
|
if err := arr.Scan(value); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(arr.Dimensions) == 0 {
|
|
|
|
*pieces = nil
|
|
|
|
return nil
|
|
|
|
} else if len(arr.Dimensions) != 1 {
|
|
|
|
return unexpectedDimension{}
|
|
|
|
}
|
|
|
|
|
|
|
|
scan := make(Pieces, len(arr.Elements))
|
|
|
|
for i, elem := range arr.Elements {
|
|
|
|
piece := Piece{}
|
|
|
|
if len(elem.Bytes) != 2+len(piece.StorageNode) {
|
|
|
|
return invalidElementLength{}
|
|
|
|
}
|
|
|
|
|
|
|
|
piece.Number = binary.BigEndian.Uint16(elem.Bytes[0:])
|
|
|
|
copy(piece.StorageNode[:], elem.Bytes[2:])
|
|
|
|
scan[i] = piece
|
|
|
|
}
|
|
|
|
|
|
|
|
*pieces = scan
|
|
|
|
return nil
|
|
|
|
}
|