diff --git a/rados/striper/errors.go b/rados/striper/errors.go new file mode 100644 index 000000000..7da16bf16 --- /dev/null +++ b/rados/striper/errors.go @@ -0,0 +1,32 @@ +//go:build ceph_preview + +package striper + +/* +#include +*/ +import "C" + +import ( + "github.com/ceph/go-ceph/internal/errutil" +) + +// radosStriperError represents an error condition returned from the Ceph +// rados striper APIs. +type radosStriperError int + +// Error returns the error string for the radosStriperError type. +func (e radosStriperError) Error() string { + return errutil.FormatErrorCode("rados", int(e)) +} + +func (e radosStriperError) ErrorCode() int { + return int(e) +} + +func getError(e C.int) error { + if e == 0 { + return nil + } + return radosStriperError(e) +} diff --git a/rados/striper/read.go b/rados/striper/read.go new file mode 100644 index 000000000..5fdd9c15a --- /dev/null +++ b/rados/striper/read.go @@ -0,0 +1,42 @@ +//go:build ceph_preview + +package striper + +// #cgo LDFLAGS: -lrados -lradosstriper +// #include +// #include +import "C" + +import ( + "unsafe" +) + +// Read bytes into data from the striped object at the specified offset. +// +// Implements: +// +// int rados_striper_read(rados_striper_t striper, +// const char *soid, +// const char *buf, +// size_t len, +// uint64_t off); +func (s *Striper) Read(soid string, data []byte, offset uint64) (int, error) { + csoid := C.CString(soid) + defer C.free(unsafe.Pointer(csoid)) + + var bufptr *C.char + if len(data) > 0 { + bufptr = (*C.char)(unsafe.Pointer(&data[0])) + } + + ret := C.rados_striper_read( + s.striper, + csoid, + bufptr, + C.size_t(len(data)), + C.uint64_t(offset)) + if ret >= 0 { + return int(ret), nil + } + return 0, getError(ret) +} diff --git a/rados/striper/stat.go b/rados/striper/stat.go new file mode 100644 index 000000000..6efb4aaec --- /dev/null +++ b/rados/striper/stat.go @@ -0,0 +1,47 @@ +//go:build (octopus || pacific || quincy) && ceph_preview + +package striper + +// #cgo LDFLAGS: -lrados -lradosstriper +// #include +// #include +import "C" + +import ( + "unsafe" +) + +// Stat returns metadata describing the striped object. +// This version of Stat uses an older API that does not provide time +// granularity below a second: the Nsec value of the StatInfo.ModTime field +// will always be zero. +// +// Implements: +// +// int rados_striper_stat(rados_striper_t striper, +// const char* soid, +// uint64_t *psize, +// time_t *pmtime); +func (s *Striper) Stat(soid string) (StatInfo, error) { + csoid := C.CString(soid) + defer C.free(unsafe.Pointer(csoid)) + + var ( + size C.uint64_t + mtime C.time_t + ) + ret := C.rados_striper_stat( + s.striper, + csoid, + &size, + &mtime) + + if ret < 0 { + return StatInfo{}, getError(ret) + } + modts := Timespec{Sec: int64(mtime)} + return StatInfo{ + Size: uint64(size), + ModTime: modts, + }, nil +} diff --git a/rados/striper/stat2.go b/rados/striper/stat2.go new file mode 100644 index 000000000..d38d827fe --- /dev/null +++ b/rados/striper/stat2.go @@ -0,0 +1,45 @@ +//go:build !(octopus || pacific || quincy) && ceph_preview + +package striper + +// #cgo LDFLAGS: -lrados -lradosstriper +// #include +// #include +import "C" + +import ( + "unsafe" + + ts "github.com/ceph/go-ceph/internal/timespec" +) + +// Stat returns metadata describing the striped object. +// +// Implements: +// +// int rados_striper_stat2(rados_striper_t striper, +// const char* soid, +// uint64_t *psize, +// struct timespec *pmtime); +func (s *Striper) Stat(soid string) (StatInfo, error) { + csoid := C.CString(soid) + defer C.free(unsafe.Pointer(csoid)) + + var ( + size C.uint64_t + mtime C.struct_timespec + ) + ret := C.rados_striper_stat2( + s.striper, + csoid, + &size, + &mtime) + + if ret < 0 { + return StatInfo{}, getError(ret) + } + return StatInfo{ + Size: uint64(size), + ModTime: Timespec(ts.CStructToTimespec(ts.CTimespecPtr(&mtime))), + }, nil +} diff --git a/rados/striper/stat_info.go b/rados/striper/stat_info.go new file mode 100644 index 000000000..8553a617b --- /dev/null +++ b/rados/striper/stat_info.go @@ -0,0 +1,16 @@ +//go:build ceph_preview + +package striper + +import ( + ts "github.com/ceph/go-ceph/internal/timespec" +) + +// Timespec behaves similarly to C's struct timespec. +type Timespec ts.Timespec + +// StatInfo contains values returned by a Striper's Stat call. +type StatInfo struct { + Size uint64 + ModTime Timespec +} diff --git a/rados/striper/striper.go b/rados/striper/striper.go new file mode 100644 index 000000000..900bee697 --- /dev/null +++ b/rados/striper/striper.go @@ -0,0 +1,121 @@ +//go:build ceph_preview + +package striper + +// #cgo LDFLAGS: -lrados -lradosstriper +// #include +// #include +// #include +import "C" + +import ( + "github.com/ceph/go-ceph/rados" +) + +// Striper helps manage the reading, writing, and management of RADOS +// striped objects. +type Striper struct { + striper C.rados_striper_t + + // Hold a reference back to the ioctx that the striper depends on so + // that Go doesn't garbage collect it prematurely. + ioctx *rados.IOContext +} + +// Layout contains a group of values used to define the size parameters of +// striped objects. Note that these parameters only effect new striped objects. +// Existing striped objects retain the parameters they were created with. +type Layout struct { + StripeUnit uint + StripeCount uint + ObjectSize uint +} + +// New returns a rados Striper object created from a rados IOContext. +func New(ioctx *rados.IOContext) (*Striper, error) { + var s C.rados_striper_t + ret := C.rados_striper_create(cephIoctx(ioctx), &s) + if err := getError(ret); err != nil { + return nil, err + } + return &Striper{s, ioctx}, nil +} + +// NewWithLayout returns a rados Striper object created from a rados IOContext +// and striper layout parameters. These parameters will be used when new +// objects are created. +func NewWithLayout(ioctx *rados.IOContext, layout Layout) (*Striper, error) { + striper, err := New(ioctx) + if err != nil { + return nil, err + } + if err := striper.SetObjectLayoutStripeUnit(layout.StripeUnit); err != nil { + return nil, err + } + if err := striper.SetObjectLayoutStripeCount(layout.StripeCount); err != nil { + return nil, err + } + if err := striper.SetObjectLayoutObjectSize(layout.ObjectSize); err != nil { + return nil, err + } + return striper, nil +} + +// Destroy the radosstriper object at the Ceph API level. +func (s *Striper) Destroy() { + C.rados_striper_destroy(s.striper) +} + +// SetObjectLayoutStripeUnit sets the stripe unit value used to layout +// new objects. +// +// Implements: +// +// int rados_striper_set_object_layout_stripe_unit(rados_striper_t striper, +// unsigned int stripe_unit); +func (s *Striper) SetObjectLayoutStripeUnit(count uint) error { + ret := C.rados_striper_set_object_layout_stripe_unit( + s.striper, + C.uint(count), + ) + return getError(ret) +} + +// SetObjectLayoutStripeCount sets the stripe count value used to layout +// new objects. +// +// Implements: +// +// int rados_striper_set_object_layout_stripe_count(rados_striper_t striper, +// unsigned int stripe_count); +func (s *Striper) SetObjectLayoutStripeCount(count uint) error { + ret := C.rados_striper_set_object_layout_stripe_count( + s.striper, + C.uint(count), + ) + return getError(ret) +} + +// SetObjectLayoutObjectSize sets the object size value used to layout +// new objects. +// +// Implements: +// +// int rados_striper_set_object_layout_object_size(rados_striper_t striper, +// unsigned int object_size); +func (s *Striper) SetObjectLayoutObjectSize(count uint) error { + ret := C.rados_striper_set_object_layout_object_size( + s.striper, + C.uint(count), + ) + return getError(ret) +} + +// cephIoctx returns a ceph rados_ioctx_t given a go-ceph rados IOContext. +func cephIoctx(radosIoctx *rados.IOContext) C.rados_ioctx_t { + p := radosIoctx.Pointer() + if p == nil { + panic("invalid IOContext pointer") + } + return C.rados_ioctx_t(p) +} diff --git a/rados/striper/write.go b/rados/striper/write.go new file mode 100644 index 000000000..26d9e7ef9 --- /dev/null +++ b/rados/striper/write.go @@ -0,0 +1,106 @@ +//go:build ceph_preview + +package striper + +// #cgo LDFLAGS: -lrados -lradosstriper +// #include +// #include +import "C" + +import "unsafe" + +// Write bytes from data into the striped object at the specified offset. +// +// Implements: +// +// int rados_striper_write(rados_striper_t striper, +// const char *soid, +// const char *buf, +// size_t len, +// uint64_t off); +func (s *Striper) Write(soid string, data []byte, offset uint64) error { + csoid := C.CString(soid) + defer C.free(unsafe.Pointer(csoid)) + + bufptr := (*C.char)(unsafe.Pointer(&data[0])) + ret := C.rados_striper_write( + s.striper, + csoid, + bufptr, + C.size_t(len(data)), + C.uint64_t(offset)) + return getError(ret) +} + +// WriteFull writes all of the bytes in data to the striped object, truncating +// the object to the length of data. +// +// Implements: +// +// int rados_striper_write_full(rados_striper_t striper, +// const char *soid, +// const char *buf, +// size_t len); +func (s *Striper) WriteFull(soid string, data []byte) error { + csoid := C.CString(soid) + defer C.free(unsafe.Pointer(csoid)) + + bufptr := (*C.char)(unsafe.Pointer(&data[0])) + ret := C.rados_striper_write_full( + s.striper, + csoid, + bufptr, + C.size_t(len(data))) + return getError(ret) +} + +// Append the bytes in data to the end of the striped object. +// +// Implements: +// +// int rados_striper_append(rados_striper_t striper, +// const char *soid, +// const char *buf, +// size_t len); +func (s *Striper) Append(soid string, data []byte) error { + csoid := C.CString(soid) + defer C.free(unsafe.Pointer(csoid)) + + bufptr := (*C.char)(unsafe.Pointer(&data[0])) + ret := C.rados_striper_append( + s.striper, + csoid, + bufptr, + C.size_t(len(data))) + return getError(ret) +} + +// Remove a striped RADOS object. +// +// Implements: +// +// int rados_striper_remove(rados_striper_t striper, +// const char *soid); +func (s *Striper) Remove(soid string) error { + csoid := C.CString(soid) + defer C.free(unsafe.Pointer(csoid)) + + ret := C.rados_striper_remove(s.striper, csoid) + return getError(ret) +} + +// Truncate a striped object, setting it to the specified size. +// +// Implements: +// +// int rados_striper_trunc(rados_striper_t striper, const char *soid, uint64_t size); +func (s *Striper) Truncate(soid string, size uint64) error { + csoid := C.CString(soid) + defer C.free(unsafe.Pointer(csoid)) + + ret := C.rados_striper_trunc( + s.striper, + csoid, + C.uint64_t(size)) + return getError(ret) +}