storj/lib/uplinkc/bucket.go
Jeff Wendling a35eb8027e lib/uplinkc: do some checks around conversions and allocations
ensure that every integer conversion fits into the destination
type, and for any unchecked ones, annotate why they are safe.
additionally, any integers we pass into slice headers need to
check that they are not negative.

all of our allocations should check for allocation failure and
return an error if there was a problem. even though an allocation
just failed, we don't pre-allocate the failure because we don't
want the callers to free it like they have to with the other
errors we return. maybe we should just panic.

Change-Id: Id4dfad802d312d35e565041efc9872453c90d793
2020-01-01 17:36:34 +00:00

219 lines
6.0 KiB
Go

// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package main
// #include "uplink_definitions.h"
import "C"
import (
"fmt"
"reflect"
"unsafe"
"storj.io/common/storj"
"storj.io/storj/lib/uplink"
)
// Bucket is a scoped uplink.Bucket
type Bucket struct {
scope
*uplink.Bucket
}
//export create_bucket
// create_bucket creates a new bucket if authorized.
func create_bucket(projectHandle C.ProjectRef, name *C.char, bucketConfig *C.BucketConfig, cerr **C.char) C.BucketInfo {
project, ok := universe.Get(projectHandle._handle).(*Project)
if !ok {
*cerr = C.CString("invalid project")
return C.BucketInfo{}
}
var config *uplink.BucketConfig
if bucketConfig != nil {
config = &uplink.BucketConfig{
PathCipher: storj.CipherSuite(bucketConfig.path_cipher),
EncryptionParameters: storj.EncryptionParameters{
CipherSuite: storj.CipherSuite(bucketConfig.encryption_parameters.cipher_suite),
BlockSize: int32(bucketConfig.encryption_parameters.block_size),
},
}
config.Volatile.RedundancyScheme = storj.RedundancyScheme{
Algorithm: storj.RedundancyAlgorithm(bucketConfig.redundancy_scheme.algorithm),
ShareSize: int32(bucketConfig.redundancy_scheme.share_size),
RequiredShares: int16(bucketConfig.redundancy_scheme.required_shares),
RepairShares: int16(bucketConfig.redundancy_scheme.repair_shares),
OptimalShares: int16(bucketConfig.redundancy_scheme.optimal_shares),
TotalShares: int16(bucketConfig.redundancy_scheme.total_shares),
}
}
bucket, err := project.CreateBucket(project.scope.ctx, C.GoString(name), config)
if err != nil {
*cerr = C.CString(fmt.Sprintf("%+v", err))
return C.BucketInfo{}
}
return newBucketInfo(&bucket)
}
//export get_bucket_info
// get_bucket_info returns info about the requested bucket if authorized.
func get_bucket_info(projectHandle C.ProjectRef, bucketName *C.char, cerr **C.char) C.BucketInfo {
project, ok := universe.Get(projectHandle._handle).(*Project)
if !ok {
*cerr = C.CString("invalid project")
return C.BucketInfo{}
}
bucket, _, err := project.GetBucketInfo(project.scope.ctx, C.GoString(bucketName))
if err != nil {
*cerr = C.CString(fmt.Sprintf("%+v", err))
return C.BucketInfo{}
}
return newBucketInfo(&bucket)
}
//export open_bucket
// open_bucket returns a Bucket handle with the given encryption context information.
func open_bucket(projectHandle C.ProjectRef, name *C.char, encryptionAccess *C.char, cerr **C.char) C.BucketRef {
project, ok := universe.Get(projectHandle._handle).(*Project)
if !ok {
*cerr = C.CString("invalid project")
return C.BucketRef{}
}
access, err := uplink.ParseEncryptionAccess(C.GoString(encryptionAccess))
if err != nil {
*cerr = C.CString(fmt.Sprintf("%+v", err))
return C.BucketRef{}
}
scope := project.scope.child()
bucket, err := project.OpenBucket(scope.ctx, C.GoString(name), access)
if err != nil {
*cerr = C.CString(fmt.Sprintf("%+v", err))
return C.BucketRef{}
}
return C.BucketRef{universe.Add(&Bucket{scope, bucket})}
}
//export list_buckets
// list_buckets will list authorized buckets.
func list_buckets(projectHandle C.ProjectRef, bucketListOptions *C.BucketListOptions, cerr **C.char) C.BucketList {
project, ok := universe.Get(projectHandle._handle).(*Project)
if !ok {
*cerr = C.CString("invalid project")
return C.BucketList{}
}
var opts *uplink.BucketListOptions
if bucketListOptions != nil {
opts = &uplink.BucketListOptions{
Cursor: C.GoString(bucketListOptions.cursor),
Direction: storj.Forward,
Limit: int(bucketListOptions.limit), // sadly, limit is an int64_t
}
}
bucketList, err := project.ListBuckets(project.scope.ctx, opts)
if err != nil {
*cerr = C.CString(fmt.Sprintf("%+v", err))
return C.BucketList{}
}
listLen := len(bucketList.Items)
if C.size_t(listLen) > C.SIZE_MAX/C.sizeof_BucketInfo || int(int32(listLen)) != listLen {
*cerr = C.CString("elements too large to be allocated")
return C.BucketList{}
}
itemsPtr := C.calloc(C.size_t(listLen), C.sizeof_BucketInfo)
if itemsPtr == nil {
*cerr = C.CString("unable to allocate")
return C.BucketList{}
}
items := *(*[]C.BucketInfo)(unsafe.Pointer(
&reflect.SliceHeader{
Data: uintptr(itemsPtr),
Len: listLen,
Cap: listLen,
},
))
for i, bucket := range bucketList.Items {
bucket := bucket
items[i] = newBucketInfo(&bucket)
}
return C.BucketList{
more: C.bool(bucketList.More),
items: (*C.BucketInfo)(itemsPtr),
length: C.int32_t(listLen),
}
}
//export delete_bucket
// delete_bucket deletes a bucket if authorized. If the bucket contains any
// Objects at the time of deletion, they may be lost permanently.
func delete_bucket(projectHandle C.ProjectRef, bucketName *C.char, cerr **C.char) {
project, ok := universe.Get(projectHandle._handle).(*Project)
if !ok {
*cerr = C.CString("invalid project")
return
}
if err := project.DeleteBucket(project.scope.ctx, C.GoString(bucketName)); err != nil {
*cerr = C.CString(fmt.Sprintf("%+v", err))
return
}
}
//export close_bucket
// close_bucket closes a Bucket handle.
func close_bucket(bucketHandle C.BucketRef, cerr **C.char) {
bucket, ok := universe.Get(bucketHandle._handle).(*Bucket)
if !ok {
*cerr = C.CString("invalid bucket")
return
}
universe.Del(bucketHandle._handle)
defer bucket.cancel()
if err := bucket.Close(); err != nil {
*cerr = C.CString(fmt.Sprintf("%+v", err))
return
}
}
//export free_bucket_info
// free_bucket_info frees bucket info.
func free_bucket_info(bucketInfo *C.BucketInfo) {
C.free(unsafe.Pointer(bucketInfo.name))
bucketInfo.name = nil
}
//export free_bucket_list
// free_bucket_list will free a list of buckets
func free_bucket_list(bucketlist *C.BucketList) {
items := *(*[]C.BucketInfo)(unsafe.Pointer(
&reflect.SliceHeader{
Data: uintptr(unsafe.Pointer(bucketlist.items)),
Len: int(bucketlist.length), // int32_t => int is safe
Cap: int(bucketlist.length), // int32_t => int is safe
},
))
for i := range items {
free_bucket_info(&items[i])
}
C.free(unsafe.Pointer(bucketlist.items))
bucketlist.items = nil
bucketlist.length = 0
}