/
metadata.go
149 lines (130 loc) · 3.88 KB
/
metadata.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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}