From 79817a4a3fc5a4bfd57ee53041c1aca48e60fcb1 Mon Sep 17 00:00:00 2001 From: Matthew Arnold Date: Mon, 28 Sep 2020 14:43:50 -0400 Subject: [PATCH 1/9] Add license and README. --- LICENSE | 30 ++++++++++++++++++++++++++++++ README.md | 5 +++++ 2 files changed, 35 insertions(+) create mode 100644 LICENSE create mode 100644 README.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2582495 --- /dev/null +++ b/LICENSE @@ -0,0 +1,30 @@ +nbdkit +Copyright (C) 2013-2020 Red Hat Inc. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +* Neither the name of Red Hat nor the names of its contributors may be +used to endorse or promote products derived from this software without +specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..765b426 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# go-nbdkit +Importable Golang bindings for nbdkit, extracted from official libguestfs.org releases. + +This repository includes all the auto-generated golang files from the official libguestfs.org releases of nbdkit, so that the bindings can be imported with `go.mod` or `go get`. This is intended as a temporary measure to allow [CDI](https://github.com/kubevirt/containerized-data-importer) to make use of nbdkit plugins for functional tests. + From 9d15554eb819aedf19ebe39b72cf623833c9eef4 Mon Sep 17 00:00:00 2001 From: Matthew Arnold <5075485+mrnold@users.noreply.github.com> Date: Mon, 28 Sep 2020 15:59:35 -0400 Subject: [PATCH 2/9] Add automatic update workflow. --- .github/workflows/update.yml | 107 +++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 .github/workflows/update.yml diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml new file mode 100644 index 0000000..88931c6 --- /dev/null +++ b/.github/workflows/update.yml @@ -0,0 +1,107 @@ +name: Update + +on: + schedule: + - cron: '0 14 * * 1' + workflow_dispatch: + +jobs: + update: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Install xmllint + run: | + sudo apt-get update + sudo apt-get install libxml2-utils + - name: Update nbdkit bindings from libguestfs.org releases + run: | + #!/usr/bin/env bash + RELEASES=$(curl --silent https://download.libguestfs.org/nbdkit/ | xmllint --html --noblanks --xpath "/html/body/table/tr[td/img/@alt = '[DIR]']/td[a/.]/a/text()" -) + git config --global user.email "marnold@redhat.com" + git config --global user.name "Matthew Arnold" + git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }} + git pull --all --set-upstream + for release in ${RELEASES} + do + release=${release%?} + TARBALLS=$(curl --silent https://download.libguestfs.org/nbdkit/$release/ | xmllint --html --noblanks --xpath "/html/body/table/tr/td[contains(a/@href, '.tar.gz') and not(contains(a/@href, '.tar.gz.sig'))]/a/@href" -) + for tarball in ${TARBALLS} + do + IFS=\"$IFS read href tarball suffix <<< $tarball # Intentionally overwrite tarball + IFS="-.$IFS" read nbdkit major minor build suffix <<< $tarball + if (($major \* 100000000 + $minor \* 10000 + $build \< 100190009)) + then # Go bindings added in 1.19.9 + echo Ignoring release older than 1.19.9: $release/$tarball + echo + continue + fi + version=$major.$minor.$build + tag=v$major.$minor.$build + echo Branch: $release Tar: $tarball Tag: $tag + if git rev-parse --verify $tag + then + echo Already present in repository: $tag + else + if ! (git rev-parse --verify $release || git rev-parse --verify origin/$release) + then + echo Creating new branch: $release + root=$(git rev-list --max-parents=0 HEAD) + git checkout $root + git checkout -b $release + else + echo Checking out existing branch: $release + git checkout $release + fi + echo Updating $release branch with new version $version + curl --silent https://download.libguestfs.org/nbdkit/$release/$tarball | tar -xzv --strip-components=6 nbdkit-$version/plugins/golang/src/libguestfs.org/nbdkit/ + rm -f .gitignore + git add --all + git diff-index --quiet HEAD || git commit -m "Import nbdkit-$version" + git tag $tag + fi + if git rev-parse --verify $tag-cdi + then + echo Already present in repository: $tag-cdi + else + if ! (git rev-parse --verify $release-cdi || git rev-parse --verify origin/$release-cdi) + then + echo Creating new branch: $release-cdi + root=$(git rev-list --max-parents=0 HEAD) + git checkout $root + git checkout -b $release-cdi + else + echo Checking out existing branch: $release-cdi + git checkout $release-cdi + fi + git checkout $tag ./ + find . -type f -name '*.go' -exec sed -i 's/#cgo pkg-config: nbdkit/#cgo LDFLAGS: -lnbdkit/g' {} + + git add --all + git diff-index --quiet HEAD || git commit -m "Tweak nbdkit-$version for CDI build" + git tag $tag-cdi + fi + echo + echo + done + done + + latest=$(tail -n 1 <<< $release) + git checkout main + if git rev-parse --verify $release + then + echo Copy newest bindings from local $latest + git checkout $latest ./ + elif git rev-parse --verify origin/$release + then + echo Copy newest bindings from remote $latest + git checkout origin/$latest ./ + else + echo Latest branch not present in local or remote? Check libguestfs.org for unpopulated directory. + fi + git diff-index --quiet HEAD || git commit -m "Update latest nbdkit bindings." + git push --all -u + git push --tags From 25cdc74820ce080abb583d7b3b09aa6b97ae4a08 Mon Sep 17 00:00:00 2001 From: Matthew Arnold Date: Mon, 28 Sep 2020 20:01:28 +0000 Subject: [PATCH 3/9] Import nbdkit-1.23.1 --- nbdkit.go | 569 ++++++++++++++++++++++++++++++++++++++++++++++++++++ utils.go | 75 +++++++ wrappers.go | 196 ++++++++++++++++++ wrappers.h | 57 ++++++ 4 files changed, 897 insertions(+) create mode 100644 nbdkit.go create mode 100644 utils.go create mode 100644 wrappers.go create mode 100644 wrappers.h diff --git a/nbdkit.go b/nbdkit.go new file mode 100644 index 0000000..aa17fd7 --- /dev/null +++ b/nbdkit.go @@ -0,0 +1,569 @@ +/* Go helper functions. + * Copyright (C) 2013-2020 Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Red Hat nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +package nbdkit + +/* +#cgo pkg-config: nbdkit +#cgo LDFLAGS: -Wl,--unresolved-symbols=ignore-in-object-files + +#include +#include +#include + +#define NBDKIT_API_VERSION 2 +#include +#include "wrappers.h" +*/ +import "C" + +import ( + "fmt" + "reflect" + "syscall" + "unsafe" +) + +// The plugin may raise errors by returning this struct (instead of nil). +type PluginError struct { + Errmsg string // string (passed to nbdkit_error) + Errno syscall.Errno // errno (optional, use 0 if not available) +} + +func (e PluginError) String() string { + if e.Errno != 0 { + return e.Errmsg + } else { + return fmt.Sprintf("%s (errno %d)", e.Errmsg, e.Errno) + } +} + +func (e PluginError) Error() string { + return e.String() +} + +// Flags and other constants. +const ( + ThreadModelSerializeConnections = uint32(C.NBDKIT_THREAD_MODEL_SERIALIZE_CONNECTIONS) + ThreadModelSerializeAllRequests = uint32(C.NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS) + ThreadModelSerializeRequests = uint32(C.NBDKIT_THREAD_MODEL_SERIALIZE_REQUESTS) + ThreadModelParallel = uint32(C.NBDKIT_THREAD_MODEL_PARALLEL) + + FlagMayTrim = uint32(C.NBDKIT_FLAG_MAY_TRIM) + FlagFUA = uint32(C.NBDKIT_FLAG_FUA) + FlagReqOne = uint32(C.NBDKIT_FLAG_REQ_ONE) + FlagFastZero = uint32(C.NBDKIT_FLAG_FAST_ZERO) + + FUANone = uint32(C.NBDKIT_FUA_NONE) + FUAEmulate = uint32(C.NBDKIT_FUA_EMULATE) + FUANative = uint32(C.NBDKIT_FUA_NATIVE) + + CacheNone = uint32(C.NBDKIT_CACHE_NONE) + CacheEmulate = uint32(C.NBDKIT_CACHE_EMULATE) + CacheNative = uint32(C.NBDKIT_CACHE_NATIVE) + + ExtentHole = uint32(C.NBDKIT_EXTENT_HOLE) + ExtentZero = uint32(C.NBDKIT_EXTENT_ZERO) + + // This is not defined by the header, but by this file. It + // might be useful for plugins to know this (even though they + // probably wouldn't be source-compatible with other API + // versions) so we expose it to golang code. + APIVersion = uint32(C.NBDKIT_API_VERSION) +) + +// The plugin interface. +type PluginInterface interface { + Load() + Unload() + + DumpPlugin() + + Config(key string, value string) error + ConfigComplete() error + GetReady() error + + PreConnect(readonly bool) error + Open(readonly bool) (ConnectionInterface, error) // required +} + +// The client connection interface. +type ConnectionInterface interface { + GetSize() (uint64, error) // required + IsRotational() (bool, error) + CanMultiConn() (bool, error) + + PRead(buf []byte, offset uint64, flags uint32) error // required + + // NB: PWrite will NOT be called unless CanWrite returns true. + CanWrite() (bool, error) + PWrite(buf []byte, offset uint64, flags uint32) error + + // NB: Flush will NOT be called unless CanFlush returns true. + CanFlush() (bool, error) + Flush(flags uint32) error + + // NB: Trim will NOT be called unless CanTrim returns true. + CanTrim() (bool, error) + Trim(count uint32, offset uint64, flags uint32) error + + // NB: Zero will NOT be called unless CanZero returns true. + CanZero() (bool, error) + Zero(count uint32, offset uint64, flags uint32) error + + Close() +} + +// Default implementations for plugin interface methods. +type Plugin struct{} +type Connection struct{} + +func (p *Plugin) Load() { +} + +func (p *Plugin) Unload() { +} + +func (p *Plugin) DumpPlugin() { +} + +func (p *Plugin) Config(key string, value string) error { + return nil +} + +func (p *Plugin) ConfigComplete() error { + return nil +} + +func (p *Plugin) GetReady() error { + return nil +} + +func (p *Plugin) PreConnect(readonly bool) error { + return nil +} + +func (p *Plugin) Open(readonly bool) (ConnectionInterface, error) { + panic("plugin must implement Open()") +} + +func (c *Connection) Close() { +} + +func (c *Connection) GetSize() (uint64, error) { + panic("plugin must implement GetSize()") +} + +func (c *Connection) CanWrite() (bool, error) { + return false, nil +} + +func (c *Connection) CanFlush() (bool, error) { + return false, nil +} + +func (c *Connection) IsRotational() (bool, error) { + return false, nil +} + +func (c *Connection) CanTrim() (bool, error) { + return false, nil +} + +func (c *Connection) CanZero() (bool, error) { + return false, nil +} + +func (c *Connection) CanMultiConn() (bool, error) { + return false, nil +} + +func (c *Connection) PRead(buf []byte, offset uint64, flags uint32) error { + panic("plugin must implement PRead()") +} + +func (c *Connection) PWrite(buf []byte, offset uint64, flags uint32) error { + panic("plugin CanWrite returns true, but no PWrite() function") +} + +func (c *Connection) Flush(flags uint32) error { + panic("plugin CanFlush returns true, but no Flush() function") +} + +func (c *Connection) Trim(count uint32, offset uint64, flags uint32) error { + panic("plugin CanTrim returns true, but no Trim() function") +} + +func (c *Connection) Zero(count uint32, offset uint64, flags uint32) error { + panic("plugin CanZero returns true, but no Zero() function") +} + +// The implementation of the user plugin. +var pluginImpl PluginInterface +var nextConnectionId uintptr +var connectionMap map[uintptr]ConnectionInterface + +// Callbacks from the server. These translate C to Go and back. + +func set_error(err error) { + perr, ok := err.(PluginError) + if ok { + if perr.Errno != 0 { + SetError(perr.Errno) + } + Error(perr.Errmsg) + } else { + Error(err.Error()) + } +} + +//export implLoad +func implLoad() { + pluginImpl.Load() +} + +//export implUnload +func implUnload() { + pluginImpl.Unload() +} + +//export implDumpPlugin +func implDumpPlugin() { + pluginImpl.DumpPlugin() +} + +//export implConfig +func implConfig(key *C.char, value *C.char) C.int { + err := pluginImpl.Config(C.GoString(key), C.GoString(value)) + if err != nil { + set_error(err) + return -1 + } + return 0 +} + +//export implConfigComplete +func implConfigComplete() C.int { + err := pluginImpl.ConfigComplete() + if err != nil { + set_error(err) + return -1 + } + return 0 +} + +//export implGetReady +func implGetReady() C.int { + err := pluginImpl.GetReady() + if err != nil { + set_error(err) + return -1 + } + return 0 +} + +//export implPreConnect +func implPreConnect(c_readonly C.int) C.int { + readonly := false + if c_readonly != 0 { + readonly = true + } + err := pluginImpl.PreConnect(readonly) + if err != nil { + set_error(err) + return -1 + } + return 0 +} + +//export implOpen +func implOpen(c_readonly C.int) unsafe.Pointer { + readonly := false + if c_readonly != 0 { + readonly = true + } + h, err := pluginImpl.Open(readonly) + if err != nil { + set_error(err) + return nil + } + id := nextConnectionId + nextConnectionId++ + connectionMap[id] = h + return unsafe.Pointer(id) +} + +func getConn(handle unsafe.Pointer) ConnectionInterface { + id := uintptr(handle) + h, ok := connectionMap[id] + if !ok { + panic(fmt.Sprintf("connection %d was not open", id)) + } + return h +} + +//export implClose +func implClose(handle unsafe.Pointer) { + h := getConn(handle) + h.Close() + id := uintptr(handle) + delete(connectionMap, id) +} + +//export implGetSize +func implGetSize(handle unsafe.Pointer) C.int64_t { + h := getConn(handle) + size, err := h.GetSize() + if err != nil { + set_error(err) + return -1 + } + return C.int64_t(size) +} + +//export implCanWrite +func implCanWrite(handle unsafe.Pointer) C.int { + h := getConn(handle) + b, err := h.CanWrite() + if err != nil { + set_error(err) + return -1 + } + if b { + return 1 + } else { + return 0 + } +} + +//export implCanFlush +func implCanFlush(handle unsafe.Pointer) C.int { + h := getConn(handle) + b, err := h.CanFlush() + if err != nil { + set_error(err) + return -1 + } + if b { + return 1 + } else { + return 0 + } +} + +//export implIsRotational +func implIsRotational(handle unsafe.Pointer) C.int { + h := getConn(handle) + b, err := h.IsRotational() + if err != nil { + set_error(err) + return -1 + } + if b { + return 1 + } else { + return 0 + } +} + +//export implCanTrim +func implCanTrim(handle unsafe.Pointer) C.int { + h := getConn(handle) + b, err := h.CanTrim() + if err != nil { + set_error(err) + return -1 + } + if b { + return 1 + } else { + return 0 + } +} + +//export implCanZero +func implCanZero(handle unsafe.Pointer) C.int { + h := getConn(handle) + b, err := h.CanZero() + if err != nil { + set_error(err) + return -1 + } + if b { + return 1 + } else { + return 0 + } +} + +//export implCanMultiConn +func implCanMultiConn(handle unsafe.Pointer) C.int { + h := getConn(handle) + b, err := h.CanMultiConn() + if err != nil { + set_error(err) + return -1 + } + if b { + return 1 + } else { + return 0 + } +} + +//export implPRead +func implPRead(handle unsafe.Pointer, c_buf unsafe.Pointer, + count C.uint32_t, offset C.uint64_t, flags C.uint32_t) C.int { + h := getConn(handle) + // https://github.com/golang/go/issues/13656 + // https://stackoverflow.com/a/25776046 + hdr := reflect.SliceHeader{ + Data: uintptr(c_buf), + Len: int(count), + Cap: int(count), + } + buf := *(*[]byte)(unsafe.Pointer(&hdr)) + err := h.PRead(buf, uint64(offset), uint32(flags)) + if err != nil { + set_error(err) + return -1 + } + return 0 +} + +//export implPWrite +func implPWrite(handle unsafe.Pointer, c_buf unsafe.Pointer, + count C.uint32_t, offset C.uint64_t, flags C.uint32_t) C.int { + h := getConn(handle) + // https://github.com/golang/go/issues/13656 + // https://stackoverflow.com/a/25776046 + hdr := reflect.SliceHeader{ + Data: uintptr(c_buf), + Len: int(count), + Cap: int(count), + } + buf := *(*[]byte)(unsafe.Pointer(&hdr)) + err := h.PWrite(buf, uint64(offset), uint32(flags)) + if err != nil { + set_error(err) + return -1 + } + return 0 +} + +//export implFlush +func implFlush(handle unsafe.Pointer, flags C.uint32_t) C.int { + h := getConn(handle) + err := h.Flush(uint32(flags)) + if err != nil { + set_error(err) + return -1 + } + return 0 +} + +//export implTrim +func implTrim(handle unsafe.Pointer, + count C.uint32_t, offset C.uint64_t, flags C.uint32_t) C.int { + h := getConn(handle) + err := h.Trim(uint32(count), uint64(offset), uint32(flags)) + if err != nil { + set_error(err) + return -1 + } + return 0 +} + +//export implZero +func implZero(handle unsafe.Pointer, + count C.uint32_t, offset C.uint64_t, flags C.uint32_t) C.int { + h := getConn(handle) + err := h.Zero(uint32(count), uint64(offset), uint32(flags)) + if err != nil { + set_error(err) + return -1 + } + return 0 +} + +// Called from C plugin_init function. +func PluginInitialize(name string, impl PluginInterface) unsafe.Pointer { + // Initialize the connection map. Note that connection IDs + // must start counting from 1 since we must never return what + // looks like a NULL pointer to the C code. + connectionMap = make(map[uintptr]ConnectionInterface) + nextConnectionId = 1 + + pluginImpl = impl + + plugin := C.struct_nbdkit_plugin{} + + // Set up the hidden plugin fields as for C. + struct_size := C.ulong(unsafe.Sizeof(plugin)) + plugin._struct_size = struct_size + plugin._api_version = C.NBDKIT_API_VERSION + plugin._thread_model = C.NBDKIT_THREAD_MODEL_PARALLEL + + // Set up the other fields. + plugin.name = C.CString(name) + plugin.load = (*[0]byte)(C.wrapper_load) + plugin.unload = (*[0]byte)(C.wrapper_unload) + plugin.dump_plugin = (*[0]byte)(C.wrapper_dump_plugin) + plugin.config = (*[0]byte)(C.wrapper_config) + plugin.config_complete = (*[0]byte)(C.wrapper_config_complete) + plugin.get_ready = (*[0]byte)(C.wrapper_get_ready) + plugin.preconnect = (*[0]byte)(C.wrapper_preconnect) + plugin.open = (*[0]byte)(C.wrapper_open) + plugin.close = (*[0]byte)(C.wrapper_close) + plugin.get_size = (*[0]byte)(C.wrapper_get_size) + plugin.can_write = (*[0]byte)(C.wrapper_can_write) + plugin.can_flush = (*[0]byte)(C.wrapper_can_flush) + plugin.is_rotational = (*[0]byte)(C.wrapper_is_rotational) + plugin.can_trim = (*[0]byte)(C.wrapper_can_trim) + plugin.can_zero = (*[0]byte)(C.wrapper_can_zero) + plugin.can_multi_conn = (*[0]byte)(C.wrapper_can_multi_conn) + plugin.pread = (*[0]byte)(C.wrapper_pread) + plugin.pwrite = (*[0]byte)(C.wrapper_pwrite) + plugin.flush = (*[0]byte)(C.wrapper_flush) + plugin.trim = (*[0]byte)(C.wrapper_trim) + plugin.zero = (*[0]byte)(C.wrapper_zero) + + // Golang plugins don't preserve errno correctly. + plugin.errno_is_preserved = 0 + + // Return a newly malloced copy of the struct. This must be + // globally available to the C code in the server, so it is + // never freed. + p := (*C.struct_nbdkit_plugin)(C.malloc(struct_size)) + *p = plugin + return unsafe.Pointer(p) +} diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..d9c0c18 --- /dev/null +++ b/utils.go @@ -0,0 +1,75 @@ +/* cgo wrappers. + * Copyright (C) 2013-2020 Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Red Hat nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +package nbdkit + +/* +#cgo pkg-config: nbdkit + +#define NBDKIT_API_VERSION 2 +#include + +// cgo cannot call varargs functions. +void +_nbdkit_debug (const char *s) +{ + nbdkit_debug ("%s", s); +} + +// cgo cannot call varargs functions. +void +_nbdkit_error (const char *s) +{ + nbdkit_error ("%s", s); +} +*/ +import "C" +import "syscall" + +// Utility functions. + +func Debug(s string) { + C._nbdkit_debug(C.CString(s)) +} + +// This function is provided but plugins would rarely need to call +// this explicitly since returning an error from a plugin callback +// will call it implicitly. +func Error(s string) { + C._nbdkit_error(C.CString(s)) +} + +// Same applies as for Error(). Callers should not usually need to +// call this. +func SetError(err syscall.Errno) { + C.nbdkit_set_error(C.int(err)) +} diff --git a/wrappers.go b/wrappers.go new file mode 100644 index 0000000..604500c --- /dev/null +++ b/wrappers.go @@ -0,0 +1,196 @@ +/* cgo wrappers. + * Copyright (C) 2013-2020 Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Red Hat nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +package nbdkit + +/* +#cgo pkg-config: nbdkit + +#include +#include + +#define NBDKIT_API_VERSION 2 +#include +#include "wrappers.h" + +extern void implLoad (); +void +wrapper_load (void) +{ + implLoad (); +} + +extern void implUnload (); +void +wrapper_unload (void) +{ + implUnload (); +} + +extern void implDumpPlugin (); +void +wrapper_dump_plugin (void) +{ + implDumpPlugin (); +} + +extern int implConfig (); +int +wrapper_config (const char *key, const char *value) +{ + return implConfig (key, value); +} + +extern int implConfigComplete (); +int +wrapper_config_complete (void) +{ + return implConfigComplete (); +} + +extern int implGetReady (); +int +wrapper_get_ready (void) +{ + return implGetReady (); +} + +extern int implPreConnect (); +int +wrapper_preconnect (int readonly) +{ + return implPreConnect (readonly); +} + +extern void *implOpen (); +void * +wrapper_open (int readonly) +{ + return implOpen (readonly); +} + +extern void implClose (); +void +wrapper_close (void *handle) +{ + return implClose (handle); +} + +extern int64_t implGetSize (); +int64_t +wrapper_get_size (void *handle) +{ + return implGetSize (handle); +} + +extern int implCanWrite (); +int +wrapper_can_write (void *handle) +{ + return implCanWrite (handle); +} + +extern int implCanFlush (); +int +wrapper_can_flush (void *handle) +{ + return implCanFlush (handle); +} + +extern int implIsRotational (); +int +wrapper_is_rotational (void *handle) +{ + return implIsRotational (handle); +} + +extern int implCanTrim (); +int +wrapper_can_trim (void *handle) +{ + return implCanTrim (handle); +} + +extern int implCanZero (); +int +wrapper_can_zero (void *handle) +{ + return implCanZero (handle); +} + +extern int implCanMultiConn (); +int +wrapper_can_multi_conn (void *handle) +{ + return implCanMultiConn (handle); +} + +extern int implPRead (); +int +wrapper_pread (void *handle, void *buf, + uint32_t count, uint64_t offset, uint32_t flags) +{ + return implPRead (handle, buf, count, offset, flags); +} + +extern int implPWrite (); +int +wrapper_pwrite (void *handle, const void *buf, + uint32_t count, uint64_t offset, uint32_t flags) +{ + return implPWrite (handle, buf, count, offset, flags); +} + +extern int implFlush (); +int +wrapper_flush (void *handle, uint32_t flags) +{ + return implFlush (handle, flags); +} + +extern int implTrim (); +int +wrapper_trim (void *handle, + uint32_t count, uint64_t offset, uint32_t flags) +{ + return implTrim (handle, count, offset, flags); +} + +extern int implZero (); +int +wrapper_zero (void *handle, + uint32_t count, uint64_t offset, uint32_t flags) +{ + return implZero (handle, count, offset, flags); +} +*/ +import "C" diff --git a/wrappers.h b/wrappers.h new file mode 100644 index 0000000..522b7a9 --- /dev/null +++ b/wrappers.h @@ -0,0 +1,57 @@ +/* cgo wrappers. + * Copyright (C) 2013-2020 Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Red Hat nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern void wrapper_load (); +extern void wrapper_unload (); +extern void wrapper_dump_plugin (); +extern int wrapper_config (const char *key, const char *value); +extern int wrapper_config_complete (void); +extern int wrapper_get_ready (void); +extern int wrapper_preconnect (int readonly); +extern void * wrapper_open (int readonly); +extern void wrapper_close (void *handle); +extern int64_t wrapper_get_size (void *handle); +extern int wrapper_can_write (void *handle); +extern int wrapper_can_flush (void *handle); +extern int wrapper_is_rotational (void *handle); +extern int wrapper_can_trim (void *handle); +extern int wrapper_can_zero (void *handle); +extern int wrapper_can_multi_conn (void *handle); +extern int wrapper_pread (void *handle, void *buf, + uint32_t count, uint64_t offset, uint32_t flags); +extern int wrapper_pwrite (void *handle, const void *buf, + uint32_t count, uint64_t offset, uint32_t flags); +extern int wrapper_flush (void *handle, uint32_t flags); +extern int wrapper_trim (void *handle, + uint32_t count, uint64_t offset, uint32_t flags); +extern int wrapper_zero (void *handle, + uint32_t count, uint64_t offset, uint32_t flags); From 710be3402d631f83e193dd234f6fa3c3dcd56bf5 Mon Sep 17 00:00:00 2001 From: Matthew Arnold Date: Mon, 28 Sep 2020 20:01:35 +0000 Subject: [PATCH 4/9] Update latest nbdkit bindings. --- nbdkit.go | 569 ++++++++++++++++++++++++++++++++++++++++++++++++++++ utils.go | 75 +++++++ wrappers.go | 196 ++++++++++++++++++ wrappers.h | 57 ++++++ 4 files changed, 897 insertions(+) create mode 100644 nbdkit.go create mode 100644 utils.go create mode 100644 wrappers.go create mode 100644 wrappers.h diff --git a/nbdkit.go b/nbdkit.go new file mode 100644 index 0000000..79344be --- /dev/null +++ b/nbdkit.go @@ -0,0 +1,569 @@ +/* Go helper functions. + * Copyright (C) 2013-2020 Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Red Hat nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +package nbdkit + +/* +#cgo pkg-config: nbdkit +#cgo LDFLAGS: -Wl,--unresolved-symbols=ignore-in-object-files + +#include +#include +#include + +#define NBDKIT_API_VERSION 2 +#include +#include "wrappers.h" +*/ +import "C" + +import ( + "fmt" + "reflect" + "syscall" + "unsafe" +) + +// The plugin may raise errors by returning this struct (instead of nil). +type PluginError struct { + Errmsg string // string (passed to nbdkit_error) + Errno syscall.Errno // errno (optional, use 0 if not available) +} + +func (e PluginError) String() string { + if e.Errno != 0 { + return e.Errmsg + } else { + return fmt.Sprintf("%s (errno %d)", e.Errmsg, e.Errno) + } +} + +func (e PluginError) Error() string { + return e.String() +} + +// Flags and other constants. +const ( + ThreadModelSerializeConnections = uint32(C.NBDKIT_THREAD_MODEL_SERIALIZE_CONNECTIONS) + ThreadModelSerializeAllRequests = uint32(C.NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS) + ThreadModelSerializeRequests = uint32(C.NBDKIT_THREAD_MODEL_SERIALIZE_REQUESTS) + ThreadModelParallel = uint32(C.NBDKIT_THREAD_MODEL_PARALLEL) + + FlagMayTrim = uint32(C.NBDKIT_FLAG_MAY_TRIM) + FlagFUA = uint32(C.NBDKIT_FLAG_FUA) + FlagReqOne = uint32(C.NBDKIT_FLAG_REQ_ONE) + FlagFastZero = uint32(C.NBDKIT_FLAG_FAST_ZERO) + + FUANone = uint32(C.NBDKIT_FUA_NONE) + FUAEmulate = uint32(C.NBDKIT_FUA_EMULATE) + FUANative = uint32(C.NBDKIT_FUA_NATIVE) + + CacheNone = uint32(C.NBDKIT_CACHE_NONE) + CacheEmulate = uint32(C.NBDKIT_CACHE_EMULATE) + CacheNative = uint32(C.NBDKIT_CACHE_NATIVE) + + ExtentHole = uint32(C.NBDKIT_EXTENT_HOLE) + ExtentZero = uint32(C.NBDKIT_EXTENT_ZERO) + + // This is not defined by the header, but by this file. It + // might be useful for plugins to know this (even though they + // probably wouldn't be source-compatible with other API + // versions) so we expose it to golang code. + APIVersion = uint32(C.NBDKIT_API_VERSION) +) + +// The plugin interface. +type PluginInterface interface { + Load() + Unload() + + DumpPlugin() + + Config(key string, value string) error + ConfigComplete() error + GetReady() error + + PreConnect(readonly bool) error + Open(readonly bool) (ConnectionInterface, error) // required +} + +// The client connection interface. +type ConnectionInterface interface { + GetSize() (uint64, error) // required + IsRotational() (bool, error) + CanMultiConn() (bool, error) + + PRead(buf []byte, offset uint64, flags uint32) error // required + + // NB: PWrite will NOT be called unless CanWrite returns true. + CanWrite() (bool, error) + PWrite(buf []byte, offset uint64, flags uint32) error + + // NB: Flush will NOT be called unless CanFlush returns true. + CanFlush() (bool, error) + Flush(flags uint32) error + + // NB: Trim will NOT be called unless CanTrim returns true. + CanTrim() (bool, error) + Trim(count uint32, offset uint64, flags uint32) error + + // NB: Zero will NOT be called unless CanZero returns true. + CanZero() (bool, error) + Zero(count uint32, offset uint64, flags uint32) error + + Close() +} + +// Default implementations for plugin interface methods. +type Plugin struct{} +type Connection struct{} + +func (p *Plugin) Load() { +} + +func (p *Plugin) Unload() { +} + +func (p *Plugin) DumpPlugin() { +} + +func (p *Plugin) Config(key string, value string) error { + return nil +} + +func (p *Plugin) ConfigComplete() error { + return nil +} + +func (p *Plugin) GetReady() error { + return nil +} + +func (p *Plugin) PreConnect(readonly bool) error { + return nil +} + +func (p *Plugin) Open(readonly bool) (ConnectionInterface, error) { + panic("plugin must implement Open()") +} + +func (c *Connection) Close() { +} + +func (c *Connection) GetSize() (uint64, error) { + panic("plugin must implement GetSize()") +} + +func (c *Connection) CanWrite() (bool, error) { + return false, nil +} + +func (c *Connection) CanFlush() (bool, error) { + return false, nil +} + +func (c *Connection) IsRotational() (bool, error) { + return false, nil +} + +func (c *Connection) CanTrim() (bool, error) { + return false, nil +} + +func (c *Connection) CanZero() (bool, error) { + return false, nil +} + +func (c *Connection) CanMultiConn() (bool, error) { + return false, nil +} + +func (c *Connection) PRead(buf []byte, offset uint64, flags uint32) error { + panic("plugin must implement PRead()") +} + +func (c *Connection) PWrite(buf []byte, offset uint64, flags uint32) error { + panic("plugin CanWrite returns true, but no PWrite() function") +} + +func (c *Connection) Flush(flags uint32) error { + panic("plugin CanFlush returns true, but no Flush() function") +} + +func (c *Connection) Trim(count uint32, offset uint64, flags uint32) error { + panic("plugin CanTrim returns true, but no Trim() function") +} + +func (c *Connection) Zero(count uint32, offset uint64, flags uint32) error { + panic("plugin CanZero returns true, but no Zero() function") +} + +// The implementation of the user plugin. +var pluginImpl PluginInterface +var nextConnectionId uintptr +var connectionMap map[uintptr]ConnectionInterface + +// Callbacks from the server. These translate C to Go and back. + +func set_error(err error) { + perr, ok := err.(PluginError) + if ok { + if perr.Errno != 0 { + SetError(perr.Errno) + } + Error(perr.Errmsg) + } else { + Error(err.Error()) + } +} + +//export implLoad +func implLoad() { + pluginImpl.Load() +} + +//export implUnload +func implUnload() { + pluginImpl.Unload() +} + +//export implDumpPlugin +func implDumpPlugin() { + pluginImpl.DumpPlugin() +} + +//export implConfig +func implConfig(key *C.char, value *C.char) C.int { + err := pluginImpl.Config(C.GoString(key), C.GoString(value)) + if err != nil { + set_error(err) + return -1 + } + return 0 +} + +//export implConfigComplete +func implConfigComplete() C.int { + err := pluginImpl.ConfigComplete() + if err != nil { + set_error(err) + return -1 + } + return 0 +} + +//export implGetReady +func implGetReady() C.int { + err := pluginImpl.GetReady() + if err != nil { + set_error(err) + return -1 + } + return 0 +} + +//export implPreConnect +func implPreConnect(c_readonly C.int) C.int { + readonly := false + if c_readonly != 0 { + readonly = true + } + err := pluginImpl.PreConnect(readonly) + if err != nil { + set_error(err) + return -1 + } + return 0 +} + +//export implOpen +func implOpen(c_readonly C.int) unsafe.Pointer { + readonly := false + if c_readonly != 0 { + readonly = true + } + h, err := pluginImpl.Open(readonly) + if err != nil { + set_error(err) + return nil + } + id := nextConnectionId + nextConnectionId++ + connectionMap[id] = h + return unsafe.Pointer(id) +} + +func getConn(handle unsafe.Pointer) ConnectionInterface { + id := uintptr(handle) + h, ok := connectionMap[id] + if !ok { + panic(fmt.Sprintf("connection %d was not open", id)) + } + return h +} + +//export implClose +func implClose(handle unsafe.Pointer) { + h := getConn(handle) + h.Close() + id := uintptr(handle) + delete(connectionMap, id) +} + +//export implGetSize +func implGetSize(handle unsafe.Pointer) C.int64_t { + h := getConn(handle) + size, err := h.GetSize() + if err != nil { + set_error(err) + return -1 + } + return C.int64_t(size) +} + +//export implCanWrite +func implCanWrite(handle unsafe.Pointer) C.int { + h := getConn(handle) + b, err := h.CanWrite() + if err != nil { + set_error(err) + return -1 + } + if b { + return 1 + } else { + return 0 + } +} + +//export implCanFlush +func implCanFlush(handle unsafe.Pointer) C.int { + h := getConn(handle) + b, err := h.CanFlush() + if err != nil { + set_error(err) + return -1 + } + if b { + return 1 + } else { + return 0 + } +} + +//export implIsRotational +func implIsRotational(handle unsafe.Pointer) C.int { + h := getConn(handle) + b, err := h.IsRotational() + if err != nil { + set_error(err) + return -1 + } + if b { + return 1 + } else { + return 0 + } +} + +//export implCanTrim +func implCanTrim(handle unsafe.Pointer) C.int { + h := getConn(handle) + b, err := h.CanTrim() + if err != nil { + set_error(err) + return -1 + } + if b { + return 1 + } else { + return 0 + } +} + +//export implCanZero +func implCanZero(handle unsafe.Pointer) C.int { + h := getConn(handle) + b, err := h.CanZero() + if err != nil { + set_error(err) + return -1 + } + if b { + return 1 + } else { + return 0 + } +} + +//export implCanMultiConn +func implCanMultiConn(handle unsafe.Pointer) C.int { + h := getConn(handle) + b, err := h.CanMultiConn() + if err != nil { + set_error(err) + return -1 + } + if b { + return 1 + } else { + return 0 + } +} + +//export implPRead +func implPRead(handle unsafe.Pointer, c_buf unsafe.Pointer, + count C.uint32_t, offset C.uint64_t, flags C.uint32_t) C.int { + h := getConn(handle) + // https://github.com/golang/go/issues/13656 + // https://stackoverflow.com/a/25776046 + hdr := reflect.SliceHeader{ + Data: uintptr(c_buf), + Len: int(count), + Cap: int(count), + } + buf := *(*[]byte)(unsafe.Pointer(&hdr)) + err := h.PRead(buf, uint64(offset), uint32(flags)) + if err != nil { + set_error(err) + return -1 + } + return 0 +} + +//export implPWrite +func implPWrite(handle unsafe.Pointer, c_buf unsafe.Pointer, + count C.uint32_t, offset C.uint64_t, flags C.uint32_t) C.int { + h := getConn(handle) + // https://github.com/golang/go/issues/13656 + // https://stackoverflow.com/a/25776046 + hdr := reflect.SliceHeader{ + Data: uintptr(c_buf), + Len: int(count), + Cap: int(count), + } + buf := *(*[]byte)(unsafe.Pointer(&hdr)) + err := h.PWrite(buf, uint64(offset), uint32(flags)) + if err != nil { + set_error(err) + return -1 + } + return 0 +} + +//export implFlush +func implFlush(handle unsafe.Pointer, flags C.uint32_t) C.int { + h := getConn(handle) + err := h.Flush(uint32(flags)) + if err != nil { + set_error(err) + return -1 + } + return 0 +} + +//export implTrim +func implTrim(handle unsafe.Pointer, + count C.uint32_t, offset C.uint64_t, flags C.uint32_t) C.int { + h := getConn(handle) + err := h.Trim(uint32(count), uint64(offset), uint32(flags)) + if err != nil { + set_error(err) + return -1 + } + return 0 +} + +//export implZero +func implZero(handle unsafe.Pointer, + count C.uint32_t, offset C.uint64_t, flags C.uint32_t) C.int { + h := getConn(handle) + err := h.Zero(uint32(count), uint64(offset), uint32(flags)) + if err != nil { + set_error(err) + return -1 + } + return 0 +} + +// Called from C plugin_init function. +func PluginInitialize(name string, impl PluginInterface) unsafe.Pointer { + // Initialize the connection map. Note that connection IDs + // must start counting from 1 since we must never return what + // looks like a NULL pointer to the C code. + connectionMap = make(map[uintptr]ConnectionInterface) + nextConnectionId = 1 + + pluginImpl = impl + + plugin := C.struct_nbdkit_plugin{} + + // Set up the hidden plugin fields as for C. + struct_size := C.ulong(unsafe.Sizeof(plugin)) + plugin._struct_size = C.uint64_t(struct_size) + plugin._api_version = C.NBDKIT_API_VERSION + plugin._thread_model = C.NBDKIT_THREAD_MODEL_PARALLEL + + // Set up the other fields. + plugin.name = C.CString(name) + plugin.load = (*[0]byte)(C.wrapper_load) + plugin.unload = (*[0]byte)(C.wrapper_unload) + plugin.dump_plugin = (*[0]byte)(C.wrapper_dump_plugin) + plugin.config = (*[0]byte)(C.wrapper_config) + plugin.config_complete = (*[0]byte)(C.wrapper_config_complete) + plugin.get_ready = (*[0]byte)(C.wrapper_get_ready) + plugin.preconnect = (*[0]byte)(C.wrapper_preconnect) + plugin.open = (*[0]byte)(C.wrapper_open) + plugin.close = (*[0]byte)(C.wrapper_close) + plugin.get_size = (*[0]byte)(C.wrapper_get_size) + plugin.can_write = (*[0]byte)(C.wrapper_can_write) + plugin.can_flush = (*[0]byte)(C.wrapper_can_flush) + plugin.is_rotational = (*[0]byte)(C.wrapper_is_rotational) + plugin.can_trim = (*[0]byte)(C.wrapper_can_trim) + plugin.can_zero = (*[0]byte)(C.wrapper_can_zero) + plugin.can_multi_conn = (*[0]byte)(C.wrapper_can_multi_conn) + plugin.pread = (*[0]byte)(C.wrapper_pread) + plugin.pwrite = (*[0]byte)(C.wrapper_pwrite) + plugin.flush = (*[0]byte)(C.wrapper_flush) + plugin.trim = (*[0]byte)(C.wrapper_trim) + plugin.zero = (*[0]byte)(C.wrapper_zero) + + // Golang plugins don't preserve errno correctly. + plugin.errno_is_preserved = 0 + + // Return a newly malloced copy of the struct. This must be + // globally available to the C code in the server, so it is + // never freed. + p := (*C.struct_nbdkit_plugin)(C.malloc(C.size_t(struct_size))) + *p = plugin + return unsafe.Pointer(p) +} diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..d9c0c18 --- /dev/null +++ b/utils.go @@ -0,0 +1,75 @@ +/* cgo wrappers. + * Copyright (C) 2013-2020 Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Red Hat nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +package nbdkit + +/* +#cgo pkg-config: nbdkit + +#define NBDKIT_API_VERSION 2 +#include + +// cgo cannot call varargs functions. +void +_nbdkit_debug (const char *s) +{ + nbdkit_debug ("%s", s); +} + +// cgo cannot call varargs functions. +void +_nbdkit_error (const char *s) +{ + nbdkit_error ("%s", s); +} +*/ +import "C" +import "syscall" + +// Utility functions. + +func Debug(s string) { + C._nbdkit_debug(C.CString(s)) +} + +// This function is provided but plugins would rarely need to call +// this explicitly since returning an error from a plugin callback +// will call it implicitly. +func Error(s string) { + C._nbdkit_error(C.CString(s)) +} + +// Same applies as for Error(). Callers should not usually need to +// call this. +func SetError(err syscall.Errno) { + C.nbdkit_set_error(C.int(err)) +} diff --git a/wrappers.go b/wrappers.go new file mode 100644 index 0000000..604500c --- /dev/null +++ b/wrappers.go @@ -0,0 +1,196 @@ +/* cgo wrappers. + * Copyright (C) 2013-2020 Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Red Hat nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +package nbdkit + +/* +#cgo pkg-config: nbdkit + +#include +#include + +#define NBDKIT_API_VERSION 2 +#include +#include "wrappers.h" + +extern void implLoad (); +void +wrapper_load (void) +{ + implLoad (); +} + +extern void implUnload (); +void +wrapper_unload (void) +{ + implUnload (); +} + +extern void implDumpPlugin (); +void +wrapper_dump_plugin (void) +{ + implDumpPlugin (); +} + +extern int implConfig (); +int +wrapper_config (const char *key, const char *value) +{ + return implConfig (key, value); +} + +extern int implConfigComplete (); +int +wrapper_config_complete (void) +{ + return implConfigComplete (); +} + +extern int implGetReady (); +int +wrapper_get_ready (void) +{ + return implGetReady (); +} + +extern int implPreConnect (); +int +wrapper_preconnect (int readonly) +{ + return implPreConnect (readonly); +} + +extern void *implOpen (); +void * +wrapper_open (int readonly) +{ + return implOpen (readonly); +} + +extern void implClose (); +void +wrapper_close (void *handle) +{ + return implClose (handle); +} + +extern int64_t implGetSize (); +int64_t +wrapper_get_size (void *handle) +{ + return implGetSize (handle); +} + +extern int implCanWrite (); +int +wrapper_can_write (void *handle) +{ + return implCanWrite (handle); +} + +extern int implCanFlush (); +int +wrapper_can_flush (void *handle) +{ + return implCanFlush (handle); +} + +extern int implIsRotational (); +int +wrapper_is_rotational (void *handle) +{ + return implIsRotational (handle); +} + +extern int implCanTrim (); +int +wrapper_can_trim (void *handle) +{ + return implCanTrim (handle); +} + +extern int implCanZero (); +int +wrapper_can_zero (void *handle) +{ + return implCanZero (handle); +} + +extern int implCanMultiConn (); +int +wrapper_can_multi_conn (void *handle) +{ + return implCanMultiConn (handle); +} + +extern int implPRead (); +int +wrapper_pread (void *handle, void *buf, + uint32_t count, uint64_t offset, uint32_t flags) +{ + return implPRead (handle, buf, count, offset, flags); +} + +extern int implPWrite (); +int +wrapper_pwrite (void *handle, const void *buf, + uint32_t count, uint64_t offset, uint32_t flags) +{ + return implPWrite (handle, buf, count, offset, flags); +} + +extern int implFlush (); +int +wrapper_flush (void *handle, uint32_t flags) +{ + return implFlush (handle, flags); +} + +extern int implTrim (); +int +wrapper_trim (void *handle, + uint32_t count, uint64_t offset, uint32_t flags) +{ + return implTrim (handle, count, offset, flags); +} + +extern int implZero (); +int +wrapper_zero (void *handle, + uint32_t count, uint64_t offset, uint32_t flags) +{ + return implZero (handle, count, offset, flags); +} +*/ +import "C" diff --git a/wrappers.h b/wrappers.h new file mode 100644 index 0000000..522b7a9 --- /dev/null +++ b/wrappers.h @@ -0,0 +1,57 @@ +/* cgo wrappers. + * Copyright (C) 2013-2020 Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Red Hat nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +extern void wrapper_load (); +extern void wrapper_unload (); +extern void wrapper_dump_plugin (); +extern int wrapper_config (const char *key, const char *value); +extern int wrapper_config_complete (void); +extern int wrapper_get_ready (void); +extern int wrapper_preconnect (int readonly); +extern void * wrapper_open (int readonly); +extern void wrapper_close (void *handle); +extern int64_t wrapper_get_size (void *handle); +extern int wrapper_can_write (void *handle); +extern int wrapper_can_flush (void *handle); +extern int wrapper_is_rotational (void *handle); +extern int wrapper_can_trim (void *handle); +extern int wrapper_can_zero (void *handle); +extern int wrapper_can_multi_conn (void *handle); +extern int wrapper_pread (void *handle, void *buf, + uint32_t count, uint64_t offset, uint32_t flags); +extern int wrapper_pwrite (void *handle, const void *buf, + uint32_t count, uint64_t offset, uint32_t flags); +extern int wrapper_flush (void *handle, uint32_t flags); +extern int wrapper_trim (void *handle, + uint32_t count, uint64_t offset, uint32_t flags); +extern int wrapper_zero (void *handle, + uint32_t count, uint64_t offset, uint32_t flags); From 93cdaa1de1a2eae059bf057ecf5231a2c560cb36 Mon Sep 17 00:00:00 2001 From: Matthew Arnold Date: Mon, 28 Sep 2020 20:01:35 +0000 Subject: [PATCH 5/9] Import nbdkit-1.23.6 --- nbdkit.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nbdkit.go b/nbdkit.go index aa17fd7..79344be 100644 --- a/nbdkit.go +++ b/nbdkit.go @@ -529,7 +529,7 @@ func PluginInitialize(name string, impl PluginInterface) unsafe.Pointer { // Set up the hidden plugin fields as for C. struct_size := C.ulong(unsafe.Sizeof(plugin)) - plugin._struct_size = struct_size + plugin._struct_size = C.uint64_t(struct_size) plugin._api_version = C.NBDKIT_API_VERSION plugin._thread_model = C.NBDKIT_THREAD_MODEL_PARALLEL @@ -563,7 +563,7 @@ func PluginInitialize(name string, impl PluginInterface) unsafe.Pointer { // Return a newly malloced copy of the struct. This must be // globally available to the C code in the server, so it is // never freed. - p := (*C.struct_nbdkit_plugin)(C.malloc(struct_size)) + p := (*C.struct_nbdkit_plugin)(C.malloc(C.size_t(struct_size))) *p = plugin return unsafe.Pointer(p) } From fd424f6985f12031ec3b38304342cda78e59de46 Mon Sep 17 00:00:00 2001 From: Matthew Arnold <5075485+mrnold@users.noreply.github.com> Date: Mon, 23 Nov 2020 20:23:28 -0500 Subject: [PATCH 6/9] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 765b426..7c00af6 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,4 @@ Importable Golang bindings for nbdkit, extracted from official libguestfs.org re This repository includes all the auto-generated golang files from the official libguestfs.org releases of nbdkit, so that the bindings can be imported with `go.mod` or `go get`. This is intended as a temporary measure to allow [CDI](https://github.com/kubevirt/containerized-data-importer) to make use of nbdkit plugins for functional tests. +Requires a minimum of nbdkit version 1.20. From 8112e9337690bf378c25ed9c72291323f06b6912 Mon Sep 17 00:00:00 2001 From: Matthew Arnold <5075485+mrnold@users.noreply.github.com> Date: Mon, 18 Jan 2021 14:22:38 -0500 Subject: [PATCH 7/9] Try to not fail job on empty directories. --- .github/workflows/update.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml index 88931c6..cacf31b 100644 --- a/.github/workflows/update.yml +++ b/.github/workflows/update.yml @@ -29,7 +29,7 @@ jobs: for release in ${RELEASES} do release=${release%?} - TARBALLS=$(curl --silent https://download.libguestfs.org/nbdkit/$release/ | xmllint --html --noblanks --xpath "/html/body/table/tr/td[contains(a/@href, '.tar.gz') and not(contains(a/@href, '.tar.gz.sig'))]/a/@href" -) + TARBALLS=$(curl --silent https://download.libguestfs.org/nbdkit/$release/ | xmllint --html --noblanks --xpath "/html/body/table/tr/td[contains(a/@href, '.tar.gz') and not(contains(a/@href, '.tar.gz.sig'))]/a/@href" -) || true for tarball in ${TARBALLS} do IFS=\"$IFS read href tarball suffix <<< $tarball # Intentionally overwrite tarball @@ -40,6 +40,7 @@ jobs: echo continue fi + latestrelease=$release version=$major.$minor.$build tag=v$major.$minor.$build echo Branch: $release Tar: $tarball Tag: $tag @@ -89,13 +90,13 @@ jobs: done done - latest=$(tail -n 1 <<< $release) + latest=$(tail -n 1 <<< $latestrelease) git checkout main - if git rev-parse --verify $release + if git rev-parse --verify $latestrelease then echo Copy newest bindings from local $latest git checkout $latest ./ - elif git rev-parse --verify origin/$release + elif git rev-parse --verify origin/$latestrelease then echo Copy newest bindings from remote $latest git checkout origin/$latest ./ From 8b11b56685e2b46645784e08102f0afc26040859 Mon Sep 17 00:00:00 2001 From: Matthew Arnold Date: Mon, 5 Apr 2021 14:19:09 +0000 Subject: [PATCH 8/9] Update latest nbdkit bindings. --- go.mod | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 go.mod diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..6ba51bd --- /dev/null +++ b/go.mod @@ -0,0 +1,4 @@ +module libguestfs.org/nbdkit + +// First version of golang with working module support. +go 1.13 From 7b1f8fc8bed86047c36cece508aa4312e05b0da3 Mon Sep 17 00:00:00 2001 From: Vincent Batts Date: Thu, 16 Feb 2023 14:44:50 +0000 Subject: [PATCH 9/9] gomod: fix the project name Signed-off-by: Vincent Batts --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 6ba51bd..dafa432 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module libguestfs.org/nbdkit +module git.batts.cloud/vbatts/go-nbdkit // First version of golang with working module support. go 1.13