storj/lib/uplinkc/object.go

331 lines
8.1 KiB
Go

// Copyright (C) 2019 Storj Labs, Inc.
// See LICENSE for copying information.
package main
// #include "uplink_definitions.h"
import "C"
import (
"io"
"time"
"unsafe"
"storj.io/storj/lib/uplink"
"storj.io/storj/pkg/storj"
)
// Object is a scoped uplink.Object
type Object struct {
scope
*uplink.Object
}
// open_object returns an Object handle, if authorized.
//export open_object
func open_object(bucketHandle C.BucketRef, objectPath *C.char, cerr **C.char) C.ObjectRef {
bucket, ok := universe.Get(bucketHandle._handle).(*Bucket)
if !ok {
*cerr = C.CString("invalid bucket")
return C.ObjectRef{}
}
scope := bucket.scope.child()
object, err := bucket.OpenObject(scope.ctx, C.GoString(objectPath))
if err != nil {
*cerr = C.CString(err.Error())
return C.ObjectRef{}
}
return C.ObjectRef{universe.Add(&Object{scope, object})}
}
// close_object closes the object.
//export close_object
func close_object(objectHandle C.ObjectRef, cerr **C.char) {
object, ok := universe.Get(objectHandle._handle).(*Object)
if !ok {
*cerr = C.CString("invalid object")
return
}
universe.Del(objectHandle._handle)
defer object.cancel()
if err := object.Close(); err != nil {
*cerr = C.CString(err.Error())
return
}
}
// get_object_meta returns the object meta which contains metadata about a specific Object.
//export get_object_meta
func get_object_meta(cObject C.ObjectRef, cErr **C.char) C.ObjectMeta {
object, ok := universe.Get(cObject._handle).(*Object)
if !ok {
*cErr = C.CString("invalid object")
return C.ObjectMeta{}
}
checksumLen := len(object.Meta.Checksum)
checksumPtr := C.malloc(C.size_t(checksumLen))
checksum := (*[1 << 30]uint8)(checksumPtr)
copy((*checksum)[:], object.Meta.Checksum)
return C.ObjectMeta{
bucket: C.CString(object.Meta.Bucket),
path: C.CString(object.Meta.Path),
is_prefix: C.bool(object.Meta.IsPrefix),
content_type: C.CString(object.Meta.ContentType),
created: C.int64_t(object.Meta.Created.Unix()),
modified: C.int64_t(object.Meta.Modified.Unix()),
expires: C.int64_t(object.Meta.Expires.Unix()),
size: C.uint64_t(object.Meta.Size),
checksum_bytes: (*C.uint8_t)(checksumPtr),
checksum_length: C.uint64_t(checksumLen),
}
}
// Upload stores writecloser and context scope for uploading
type Upload struct {
scope
wc io.WriteCloser // 🚽
}
// upload uploads a new object, if authorized.
//export upload
func upload(cBucket C.BucketRef, path *C.char, cOpts *C.UploadOptions, cErr **C.char) (downloader C.UploaderRef) {
bucket, ok := universe.Get(cBucket._handle).(*Bucket)
if !ok {
*cErr = C.CString("invalid bucket")
return
}
scope := bucket.scope.child()
var opts *uplink.UploadOptions
if cOpts != nil {
var metadata map[string]string
opts = &uplink.UploadOptions{
ContentType: C.GoString(cOpts.content_type),
Metadata: metadata,
Expires: time.Unix(int64(cOpts.expires), 0),
}
}
writeCloser, err := bucket.NewWriter(scope.ctx, C.GoString(path), opts)
if err != nil {
*cErr = C.CString(err.Error())
return
}
return C.UploaderRef{universe.Add(&Upload{
scope: scope,
wc: writeCloser,
})}
}
//export upload_write
func upload_write(uploader C.UploaderRef, bytes *C.uint8_t, length C.size_t, cErr **C.char) (writeLength C.size_t) {
upload, ok := universe.Get(uploader._handle).(*Upload)
if !ok {
*cErr = C.CString("invalid uploader")
return C.size_t(0)
}
buf := (*[1 << 30]byte)(unsafe.Pointer(bytes))[:length]
n, err := upload.wc.Write(buf)
if err != nil {
*cErr = C.CString(err.Error())
}
return C.size_t(n)
}
//export upload_commit
func upload_commit(uploader C.UploaderRef, cErr **C.char) {
upload, ok := universe.Get(uploader._handle).(*Upload)
if !ok {
*cErr = C.CString("invalid uploader")
return
}
universe.Del(uploader._handle)
defer upload.cancel()
err := upload.wc.Close()
if err != nil {
*cErr = C.CString(err.Error())
return
}
}
// list_objects lists objects a user is authorized to see.
//export list_objects
func list_objects(bucketRef C.BucketRef, cListOpts *C.ListOptions, cErr **C.char) (cObjList C.ObjectList) {
bucket, ok := universe.Get(bucketRef._handle).(*Bucket)
if !ok {
*cErr = C.CString("invalid bucket")
return cObjList
}
scope := bucket.scope.child()
var opts *uplink.ListOptions
if unsafe.Pointer(cListOpts) != nil {
opts = &uplink.ListOptions{
Prefix: C.GoString(cListOpts.cursor),
Cursor: C.GoString(cListOpts.cursor),
Delimiter: rune(cListOpts.delimiter),
Recursive: bool(cListOpts.recursive),
Direction: storj.ListDirection(cListOpts.direction),
Limit: int(cListOpts.limit),
}
}
objectList, err := bucket.ListObjects(scope.ctx, opts)
if err != nil {
*cErr = C.CString(err.Error())
return cObjList
}
objListLen := len(objectList.Items)
objectSize := int(C.sizeof_ObjectInfo)
ptr := C.malloc(C.size_t(objListLen * objectSize))
cObjectsPtr := (*[1 << 30]C.ObjectInfo)(ptr)
for i, object := range objectList.Items {
object := object
cObjectsPtr[i] = newObjectInfo(&object)
}
return C.ObjectList{
bucket: C.CString(objectList.Bucket),
prefix: C.CString(objectList.Prefix),
more: C.bool(objectList.More),
items: (*C.ObjectInfo)(unsafe.Pointer(cObjectsPtr)),
length: C.int32_t(objListLen),
}
}
// Download stores readcloser and context scope for downloading
type Download struct {
scope
rc interface {
io.Reader
io.Seeker
io.Closer
}
}
// download returns an Object's data. A length of -1 will mean
// (Object.Size - offset).
//export download
func download(bucketRef C.BucketRef, path *C.char, cErr **C.char) (downloader C.DownloaderRef) {
bucket, ok := universe.Get(bucketRef._handle).(*Bucket)
if !ok {
*cErr = C.CString("invalid bucket")
return
}
scope := bucket.scope.child()
rc, err := bucket.NewReader(scope.ctx, C.GoString(path))
if err != nil {
*cErr = C.CString(err.Error())
return
}
return C.DownloaderRef{universe.Add(&Download{
scope: scope,
rc: rc,
})}
}
//export download_read
func download_read(downloader C.DownloaderRef, bytes *C.uint8_t, length C.size_t, cErr **C.char) C.size_t {
download, ok := universe.Get(downloader._handle).(*Download)
if !ok {
*cErr = C.CString("invalid downloader")
return C.size_t(0)
}
buf := (*[1 << 30]byte)(unsafe.Pointer(bytes))[:length]
n, err := download.rc.Read(buf)
if err != nil && err != io.EOF {
*cErr = C.CString(err.Error())
}
return C.size_t(n)
}
//export download_close
func download_close(downloader C.DownloaderRef, cErr **C.char) {
download, ok := universe.Get(downloader._handle).(*Download)
if !ok {
*cErr = C.CString("invalid downloader")
}
universe.Del(downloader._handle)
defer download.cancel()
err := download.rc.Close()
if err != nil {
*cErr = C.CString(err.Error())
return
}
}
//export free_upload_opts
func free_upload_opts(uploadOpts *C.UploadOptions) {
C.free(unsafe.Pointer(uploadOpts.content_type))
uploadOpts.content_type = nil
}
// free_object_meta frees the object meta
//export free_object_meta
func free_object_meta(objectMeta *C.ObjectMeta) {
C.free(unsafe.Pointer(objectMeta.bucket))
objectMeta.bucket = nil
C.free(unsafe.Pointer(objectMeta.path))
objectMeta.path = nil
C.free(unsafe.Pointer(objectMeta.content_type))
objectMeta.content_type = nil
C.free(unsafe.Pointer(objectMeta.checksum_bytes))
objectMeta.checksum_bytes = nil
}
// free_object_info frees the object info
//export free_object_info
func free_object_info(objectInfo *C.ObjectInfo) {
bucketInfo := objectInfo.bucket
free_bucket_info(&bucketInfo)
C.free(unsafe.Pointer(objectInfo.path))
objectInfo.path = nil
C.free(unsafe.Pointer(objectInfo.content_type))
objectInfo.content_type = nil
}
// free_list_objects frees the list of objects
//export free_list_objects
func free_list_objects(objectList *C.ObjectList) {
C.free(unsafe.Pointer(objectList.bucket))
objectList.bucket = nil
C.free(unsafe.Pointer(objectList.prefix))
objectList.prefix = nil
items := (*[1 << 30]C.ObjectInfo)(unsafe.Pointer(objectList.items))[:objectList.length]
for _, item := range items {
item := item
free_object_info((*C.ObjectInfo)(unsafe.Pointer(&item)))
}
}