mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-12-26 23:10:22 +00:00
150 lines
3.9 KiB
Go
150 lines
3.9 KiB
Go
|
package rbd
|
||
|
|
||
|
// #cgo LDFLAGS: -lrbd
|
||
|
// #include <stdlib.h>
|
||
|
// #include <rbd/librbd.h>
|
||
|
import "C"
|
||
|
|
||
|
import (
|
||
|
"unsafe"
|
||
|
|
||
|
"github.com/ceph/go-ceph/internal/cutil"
|
||
|
"github.com/ceph/go-ceph/internal/retry"
|
||
|
)
|
||
|
|
||
|
// GetMetadata returns the metadata string associated with the given key.
|
||
|
//
|
||
|
// Implements:
|
||
|
// int rbd_metadata_get(rbd_image_t image, const char *key, char *value, size_t *vallen)
|
||
|
func (image *Image) GetMetadata(key string) (string, error) {
|
||
|
if err := image.validate(imageIsOpen); err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
|
||
|
c_key := C.CString(key)
|
||
|
defer C.free(unsafe.Pointer(c_key))
|
||
|
|
||
|
var (
|
||
|
buf []byte
|
||
|
err error
|
||
|
)
|
||
|
retry.WithSizes(4096, 262144, func(size int) retry.Hint {
|
||
|
csize := C.size_t(size)
|
||
|
buf = make([]byte, csize)
|
||
|
// rbd_metadata_get is a bit quirky and *does not* update the size
|
||
|
// value if the size passed in >= the needed size.
|
||
|
ret := C.rbd_metadata_get(
|
||
|
image.image, c_key, (*C.char)(unsafe.Pointer(&buf[0])), &csize)
|
||
|
err = getError(ret)
|
||
|
return retry.Size(int(csize)).If(err == errRange)
|
||
|
})
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
return C.GoString((*C.char)(unsafe.Pointer(&buf[0]))), nil
|
||
|
}
|
||
|
|
||
|
// SetMetadata updates the metadata string associated with the given key.
|
||
|
//
|
||
|
// Implements:
|
||
|
// int rbd_metadata_set(rbd_image_t image, const char *key, const char *value)
|
||
|
func (image *Image) SetMetadata(key string, value string) error {
|
||
|
if err := image.validate(imageIsOpen); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
c_key := C.CString(key)
|
||
|
c_value := C.CString(value)
|
||
|
defer C.free(unsafe.Pointer(c_key))
|
||
|
defer C.free(unsafe.Pointer(c_value))
|
||
|
|
||
|
ret := C.rbd_metadata_set(image.image, c_key, c_value)
|
||
|
if ret < 0 {
|
||
|
return rbdError(ret)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// RemoveMetadata clears the metadata associated with the given key.
|
||
|
//
|
||
|
// Implements:
|
||
|
// int rbd_metadata_remove(rbd_image_t image, const char *key)
|
||
|
func (image *Image) RemoveMetadata(key string) error {
|
||
|
if err := image.validate(imageIsOpen); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
c_key := C.CString(key)
|
||
|
defer C.free(unsafe.Pointer(c_key))
|
||
|
|
||
|
ret := C.rbd_metadata_remove(image.image, c_key)
|
||
|
if ret < 0 {
|
||
|
return rbdError(ret)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// ListMetadata returns a map containing all metadata assigned to the RBD image.
|
||
|
//
|
||
|
// Implements:
|
||
|
// int rbd_metadata_list(rbd_image_t image, const char *start, uint64_t max,
|
||
|
// char *keys, size_t *key_len, char *values, size_t *vals_len);
|
||
|
func (image *Image) ListMetadata() (map[string]string, error) {
|
||
|
if err := image.validate(imageIsOpen); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
err error
|
||
|
keysbuf []byte
|
||
|
keysSize C.size_t
|
||
|
valsbuf []byte
|
||
|
valsSize C.size_t
|
||
|
)
|
||
|
retry.WithSizes(4096, 262144, func(size int) retry.Hint {
|
||
|
keysbuf = make([]byte, size)
|
||
|
keysSize = C.size_t(size)
|
||
|
valsbuf = make([]byte, size)
|
||
|
valsSize = C.size_t(size)
|
||
|
// the rbd_metadata_list function can use a start point and a limit.
|
||
|
// we do not use it and prefer our retry helper and just allocating
|
||
|
// buffers large enough to take all the keys and values
|
||
|
ret := C.rbd_metadata_list(
|
||
|
image.image,
|
||
|
(*C.char)(unsafe.Pointer(&empty[0])), // always start at the beginning (no paging)
|
||
|
0, // fetch all key-value pairs
|
||
|
(*C.char)(unsafe.Pointer(&keysbuf[0])),
|
||
|
&keysSize,
|
||
|
(*C.char)(unsafe.Pointer(&valsbuf[0])),
|
||
|
&valsSize)
|
||
|
|
||
|
err = getError(ret)
|
||
|
nextSize := valsSize
|
||
|
if keysSize > nextSize {
|
||
|
nextSize = keysSize
|
||
|
}
|
||
|
return retry.Size(int(nextSize)).If(err == errRange)
|
||
|
})
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
m := map[string]string{}
|
||
|
keys := cutil.SplitBuffer(keysbuf[:keysSize])
|
||
|
vals := cutil.SplitBuffer(valsbuf[:valsSize])
|
||
|
if len(keys) != len(vals) {
|
||
|
// this should not happen (famous last words)
|
||
|
return nil, errRange
|
||
|
}
|
||
|
for i := range keys {
|
||
|
m[keys[i]] = vals[i]
|
||
|
}
|
||
|
return m, nil
|
||
|
}
|
||
|
|
||
|
// rather than allocate memory every time that ListMetadata is called,
|
||
|
// define a static byte slice to stand in for the C "empty string"
|
||
|
var empty = []byte{0}
|