1 /* Go helper functions.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of Red Hat nor the names of its contributors may be
16 * used to endorse or promote products derived from this software without
17 * specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #cgo pkg-config: nbdkit
37 #cgo LDFLAGS: -Wl,--unresolved-symbols=ignore-in-object-files
43 #define NBDKIT_API_VERSION 2
44 #include <nbdkit-plugin.h>
56 // The plugin may raise errors by returning this struct (instead of nil).
57 type PluginError
struct {
58 Errmsg
string // string (passed to nbdkit_error)
59 Errno syscall
.Errno
// errno (optional, use 0 if not available)
62 func (e PluginError
) String() string {
66 return fmt
.Sprintf("%s (errno %d)", e
.Errmsg
, e
.Errno
)
70 func (e PluginError
) Error() string {
74 // Flags and other constants.
76 ThreadModelSerializeConnections
= uint32(C
.NBDKIT_THREAD_MODEL_SERIALIZE_CONNECTIONS
)
77 ThreadModelSerializeAllRequests
= uint32(C
.NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS
)
78 ThreadModelSerializeRequests
= uint32(C
.NBDKIT_THREAD_MODEL_SERIALIZE_REQUESTS
)
79 ThreadModelParallel
= uint32(C
.NBDKIT_THREAD_MODEL_PARALLEL
)
81 FlagMayTrim
= uint32(C
.NBDKIT_FLAG_MAY_TRIM
)
82 FlagFUA
= uint32(C
.NBDKIT_FLAG_FUA
)
83 FlagReqOne
= uint32(C
.NBDKIT_FLAG_REQ_ONE
)
84 FlagFastZero
= uint32(C
.NBDKIT_FLAG_FAST_ZERO
)
86 FUANone
= uint32(C
.NBDKIT_FUA_NONE
)
87 FUAEmulate
= uint32(C
.NBDKIT_FUA_EMULATE
)
88 FUANative
= uint32(C
.NBDKIT_FUA_NATIVE
)
90 CacheNone
= uint32(C
.NBDKIT_CACHE_NONE
)
91 CacheEmulate
= uint32(C
.NBDKIT_CACHE_EMULATE
)
92 CacheNative
= uint32(C
.NBDKIT_CACHE_NATIVE
)
94 ExtentHole
= uint32(C
.NBDKIT_EXTENT_HOLE
)
95 ExtentZero
= uint32(C
.NBDKIT_EXTENT_ZERO
)
97 // This is not defined by the header, but by this file. It
98 // might be useful for plugins to know this (even though they
99 // probably wouldn't be source-compatible with other API
100 // versions) so we expose it to golang code.
101 APIVersion
= uint32(C
.NBDKIT_API_VERSION
)
104 // The plugin interface.
105 type PluginInterface
interface {
111 Config(key
string, value
string) error
112 ConfigComplete() error
115 PreConnect(readonly
bool) error
116 Open(readonly
bool) (ConnectionInterface
, error
) // required
119 // The client connection interface.
120 type ConnectionInterface
interface {
121 GetSize() (uint64, error
) // required
122 IsRotational() (bool, error
)
123 CanMultiConn() (bool, error
)
125 PRead(buf
[]byte, offset
uint64, flags
uint32) error
// required
127 // NB: PWrite will NOT be called unless CanWrite returns true.
128 CanWrite() (bool, error
)
129 PWrite(buf
[]byte, offset
uint64, flags
uint32) error
131 // NB: Flush will NOT be called unless CanFlush returns true.
132 CanFlush() (bool, error
)
133 Flush(flags
uint32) error
135 // NB: Trim will NOT be called unless CanTrim returns true.
136 CanTrim() (bool, error
)
137 Trim(count
uint32, offset
uint64, flags
uint32) error
139 // NB: Zero will NOT be called unless CanZero returns true.
140 CanZero() (bool, error
)
141 Zero(count
uint32, offset
uint64, flags
uint32) error
146 // Default implementations for plugin interface methods.
148 type Connection
struct{}
150 func (p
*Plugin
) Load() {
153 func (p
*Plugin
) Unload() {
156 func (p
*Plugin
) DumpPlugin() {
159 func (p
*Plugin
) Config(key
string, value
string) error
{
163 func (p
*Plugin
) ConfigComplete() error
{
167 func (p
*Plugin
) GetReady() error
{
171 func (p
*Plugin
) PreConnect(readonly
bool) error
{
175 func (p
*Plugin
) Open(readonly
bool) (ConnectionInterface
, error
) {
176 panic("plugin must implement Open()")
179 func (c
*Connection
) Close() {
182 func (c
*Connection
) GetSize() (uint64, error
) {
183 panic("plugin must implement GetSize()")
186 func (c
*Connection
) CanWrite() (bool, error
) {
190 func (c
*Connection
) CanFlush() (bool, error
) {
194 func (c
*Connection
) IsRotational() (bool, error
) {
198 func (c
*Connection
) CanTrim() (bool, error
) {
202 func (c
*Connection
) CanZero() (bool, error
) {
206 func (c
*Connection
) CanMultiConn() (bool, error
) {
210 func (c
*Connection
) PRead(buf
[]byte, offset
uint64, flags
uint32) error
{
211 panic("plugin must implement PRead()")
214 func (c
*Connection
) PWrite(buf
[]byte, offset
uint64, flags
uint32) error
{
215 panic("plugin CanWrite returns true, but no PWrite() function")
218 func (c
*Connection
) Flush(flags
uint32) error
{
219 panic("plugin CanFlush returns true, but no Flush() function")
222 func (c
*Connection
) Trim(count
uint32, offset
uint64, flags
uint32) error
{
223 panic("plugin CanTrim returns true, but no Trim() function")
226 func (c
*Connection
) Zero(count
uint32, offset
uint64, flags
uint32) error
{
227 panic("plugin CanZero returns true, but no Zero() function")
230 // The implementation of the user plugin.
231 var pluginImpl PluginInterface
232 var nextConnectionId
uintptr
233 var connectionMap
map[uintptr]ConnectionInterface
235 // Callbacks from the server. These translate C to Go and back.
237 func set_error(err error
) {
238 perr
, ok
:= err
.(PluginError
)
259 //export implDumpPlugin
260 func implDumpPlugin() {
261 pluginImpl
.DumpPlugin()
265 func implConfig(key
*C
.char
, value
*C
.char
) C
.int {
266 err
:= pluginImpl
.Config(C
.GoString(key
), C
.GoString(value
))
274 //export implConfigComplete
275 func implConfigComplete() C
.int {
276 err
:= pluginImpl
.ConfigComplete()
284 //export implGetReady
285 func implGetReady() C
.int {
286 err
:= pluginImpl
.GetReady()
294 //export implPreConnect
295 func implPreConnect(c_readonly C
.int) C
.int {
300 err
:= pluginImpl
.PreConnect(readonly
)
309 func implOpen(c_readonly C
.int) unsafe
.Pointer
{
314 h
, err
:= pluginImpl
.Open(readonly
)
319 id
:= nextConnectionId
321 connectionMap
[id
] = h
322 return unsafe
.Pointer(id
)
325 func getConn(handle unsafe
.Pointer
) ConnectionInterface
{
326 id
:= uintptr(handle
)
327 h
, ok
:= connectionMap
[id
]
329 panic(fmt
.Sprintf("connection %d was not open", id
))
335 func implClose(handle unsafe
.Pointer
) {
338 id
:= uintptr(handle
)
339 delete(connectionMap
, id
)
343 func implGetSize(handle unsafe
.Pointer
) C
.int64_t
{
345 size
, err
:= h
.GetSize()
350 return C
.int64_t(size
)
353 //export implCanWrite
354 func implCanWrite(handle unsafe
.Pointer
) C
.int {
356 b
, err
:= h
.CanWrite()
368 //export implCanFlush
369 func implCanFlush(handle unsafe
.Pointer
) C
.int {
371 b
, err
:= h
.CanFlush()
383 //export implIsRotational
384 func implIsRotational(handle unsafe
.Pointer
) C
.int {
386 b
, err
:= h
.IsRotational()
399 func implCanTrim(handle unsafe
.Pointer
) C
.int {
401 b
, err
:= h
.CanTrim()
414 func implCanZero(handle unsafe
.Pointer
) C
.int {
416 b
, err
:= h
.CanZero()
428 //export implCanMultiConn
429 func implCanMultiConn(handle unsafe
.Pointer
) C
.int {
431 b
, err
:= h
.CanMultiConn()
444 func implPRead(handle unsafe
.Pointer
, c_buf unsafe
.Pointer
,
445 count C
.uint32_t
, offset C
.uint64_t
, flags C
.uint32_t
) C
.int {
447 // https://github.com/golang/go/issues/13656
448 // https://stackoverflow.com/a/25776046
449 hdr
:= reflect
.SliceHeader
{
450 Data
: uintptr(c_buf
),
454 buf
:= *(*[]byte)(unsafe
.Pointer(&hdr
))
455 err
:= h
.PRead(buf
, uint64(offset
), uint32(flags
))
464 func implPWrite(handle unsafe
.Pointer
, c_buf unsafe
.Pointer
,
465 count C
.uint32_t
, offset C
.uint64_t
, flags C
.uint32_t
) C
.int {
467 // https://github.com/golang/go/issues/13656
468 // https://stackoverflow.com/a/25776046
469 hdr
:= reflect
.SliceHeader
{
470 Data
: uintptr(c_buf
),
474 buf
:= *(*[]byte)(unsafe
.Pointer(&hdr
))
475 err
:= h
.PWrite(buf
, uint64(offset
), uint32(flags
))
484 func implFlush(handle unsafe
.Pointer
, flags C
.uint32_t
) C
.int {
486 err
:= h
.Flush(uint32(flags
))
495 func implTrim(handle unsafe
.Pointer
,
496 count C
.uint32_t
, offset C
.uint64_t
, flags C
.uint32_t
) C
.int {
498 err
:= h
.Trim(uint32(count
), uint64(offset
), uint32(flags
))
507 func implZero(handle unsafe
.Pointer
,
508 count C
.uint32_t
, offset C
.uint64_t
, flags C
.uint32_t
) C
.int {
510 err
:= h
.Zero(uint32(count
), uint64(offset
), uint32(flags
))
518 // Called from C plugin_init function.
519 func PluginInitialize(name
string, impl PluginInterface
) unsafe
.Pointer
{
520 // Initialize the connection map. Note that connection IDs
521 // must start counting from 1 since we must never return what
522 // looks like a NULL pointer to the C code.
523 connectionMap
= make(map[uintptr]ConnectionInterface
)
528 plugin
:= C
.struct_nbdkit_plugin
{}
530 // Set up the hidden plugin fields as for C.
531 struct_size
:= C
.ulong(unsafe
.Sizeof(plugin
))
532 plugin
._struct_size
= C
.uint64_t(struct_size
)
533 plugin
._api_version
= C
.NBDKIT_API_VERSION
534 plugin
._thread_model
= C
.NBDKIT_THREAD_MODEL_PARALLEL
536 // Set up the other fields.
537 plugin
.name
= C
.CString(name
)
538 plugin
.load
= (*[0]byte)(C
.wrapper_load
)
539 plugin
.unload
= (*[0]byte)(C
.wrapper_unload
)
540 plugin
.dump_plugin
= (*[0]byte)(C
.wrapper_dump_plugin
)
541 plugin
.config
= (*[0]byte)(C
.wrapper_config
)
542 plugin
.config_complete
= (*[0]byte)(C
.wrapper_config_complete
)
543 plugin
.get_ready
= (*[0]byte)(C
.wrapper_get_ready
)
544 plugin
.preconnect
= (*[0]byte)(C
.wrapper_preconnect
)
545 plugin
.open
= (*[0]byte)(C
.wrapper_open
)
546 plugin
.close = (*[0]byte)(C
.wrapper_close
)
547 plugin
.get_size
= (*[0]byte)(C
.wrapper_get_size
)
548 plugin
.can_write
= (*[0]byte)(C
.wrapper_can_write
)
549 plugin
.can_flush
= (*[0]byte)(C
.wrapper_can_flush
)
550 plugin
.is_rotational
= (*[0]byte)(C
.wrapper_is_rotational
)
551 plugin
.can_trim
= (*[0]byte)(C
.wrapper_can_trim
)
552 plugin
.can_zero
= (*[0]byte)(C
.wrapper_can_zero
)
553 plugin
.can_multi_conn
= (*[0]byte)(C
.wrapper_can_multi_conn
)
554 plugin
.pread
= (*[0]byte)(C
.wrapper_pread
)
555 plugin
.pwrite
= (*[0]byte)(C
.wrapper_pwrite
)
556 plugin
.flush
= (*[0]byte)(C
.wrapper_flush
)
557 plugin
.trim
= (*[0]byte)(C
.wrapper_trim
)
558 plugin
.zero
= (*[0]byte)(C
.wrapper_zero
)
560 // Golang plugins don't preserve errno correctly.
561 plugin
.errno_is_preserved
= 0
563 // Return a newly malloced copy of the struct. This must be
564 // globally available to the C code in the server, so it is
566 p
:= (*C
.struct_nbdkit_plugin
)(C
.malloc(C
.size_t(struct_size
)))
568 return unsafe
.Pointer(p
)