a35eb8027e
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
219 lines
6.0 KiB
Go
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
|
|
}
|