Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect rbd_clone4 support by using dlsym() #1013

Merged
merged 3 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions internal/dlsym/dlsym.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package dlsym

// #cgo LDFLAGS: -ldl
anoopcs9 marked this conversation as resolved.
Show resolved Hide resolved
//
// #include <stdlib.h>
// #include <dlfcn.h>
//
// #ifndef RTLD_DEFAULT /* from dlfcn.h */
// #define RTLD_DEFAULT ((void *) 0)
// #endif
import "C"

import (
"errors"
"fmt"
"unsafe"
)

// ErrUndefinedSymbol is returned by LookupSymbol when the requested symbol
// could not be found.
var ErrUndefinedSymbol = errors.New("symbol not found")

// LookupSymbol resolves the named symbol from the already dynamically loaded
// libraries. If the symbol is found, a pointer to it is returned, in case of a
// failure, the message provided by dlerror() is included in the error message.
func LookupSymbol(symbol string) (unsafe.Pointer, error) {
cSymName := C.CString(symbol)
defer C.free(unsafe.Pointer(cSymName))

// clear dlerror before looking up the symbol
C.dlerror()
// resolve the address of the symbol
sym := C.dlsym(C.RTLD_DEFAULT, cSymName)
e := C.dlerror()
dlerr := C.GoString(e)
if dlerr != "" {
return nil, fmt.Errorf("%w: %s", ErrUndefinedSymbol, dlerr)
}

return sym, nil
}
22 changes: 22 additions & 0 deletions internal/dlsym/dlsym_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package dlsym

import (
"errors"
"testing"

"github.com/stretchr/testify/assert"
)

func TestLookupSymbol(t *testing.T) {
t.Run("ValidSymbol", func(t *testing.T) {
sym, err := LookupSymbol("dlsym")
assert.NotNil(t, sym)
assert.NoError(t, err)
})

t.Run("InvalidSymbol", func(t *testing.T) {
sym, err := LookupSymbol("go_ceph_dlsym")
assert.Nil(t, sym)
assert.True(t, errors.Is(err, ErrUndefinedSymbol))
})
}
53 changes: 45 additions & 8 deletions rbd/clone_image_by_id.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,46 @@
//go:build !(nautilus || octopus || pacific || quincy || reef) && ceph_preview
//go:build ceph_preview

package rbd

// #cgo LDFLAGS: -lrbd
// #include <errno.h>
// #include <stdlib.h>
// #include <rados/librados.h>
// #include <rbd/librbd.h>
/*
#cgo LDFLAGS: -lrbd
#include <errno.h>
#include <stdlib.h>
#include <rados/librados.h>
#include <rbd/librbd.h>

// rbd_clone4_fn matches the rbd_clone4 function signature.
typedef int(*rbd_clone4_fn)(rados_ioctx_t p_ioctx, const char *p_name,
uint64_t p_snap_id, rados_ioctx_t c_ioctx,
const char *c_name, rbd_image_options_t c_opts);

anoopcs9 marked this conversation as resolved.
Show resolved Hide resolved
// rbd_clone4_dlsym take *fn as rbd_clone4_fn and calls the dynamically loaded
// rbd_clone4 function passed as 1st argument.
static inline int rbd_clone4_dlsym(void *fn, rados_ioctx_t p_ioctx,
const char *p_name, uint64_t p_snap_id,
rados_ioctx_t c_ioctx, const char *c_name,
rbd_image_options_t c_opts) {
// cast function pointer fn to rbd_clone4 and call the function
return ((rbd_clone4_fn) fn)(p_ioctx, p_name, p_snap_id, c_ioctx, c_name, c_opts);
}
*/
import "C"

import (
"fmt"
"sync"
"unsafe"

"github.com/ceph/go-ceph/internal/dlsym"
"github.com/ceph/go-ceph/rados"
)

var (
rbdClone4Once sync.Once
rbdClone4 unsafe.Pointer
rbdClone4Err error
)

// CloneImageByID creates a clone of the image from a snapshot with the given
// ID in the provided io-context with the given name and image options.
//
Expand All @@ -25,22 +51,33 @@ import (
// const char *c_name, rbd_image_options_t c_opts);
func CloneImageByID(ioctx *rados.IOContext, parentName string, snapID uint64,
destctx *rados.IOContext, name string, rio *ImageOptions) error {

if rio == nil {
return rbdError(C.EINVAL)
}

rbdClone4Once.Do(func() {
rbdClone4, rbdClone4Err = dlsym.LookupSymbol("rbd_clone4")
})

if rbdClone4Err != nil {
return fmt.Errorf("%w: %w", ErrNotImplemented, rbdClone4Err)
}

cParentName := C.CString(parentName)
defer C.free(unsafe.Pointer(cParentName))
cCloneName := C.CString(name)
defer C.free(unsafe.Pointer(cCloneName))

ret := C.rbd_clone4(
// call rbd_clone4_dlsym with the function pointer to rbd_clone4 as 1st
// argument
ret := C.rbd_clone4_dlsym(
rbdClone4,
cephIoctx(ioctx),
cParentName,
C.uint64_t(snapID),
cephIoctx(destctx),
cCloneName,
C.rbd_image_options_t(rio.options))

return getError(ret)
}
15 changes: 11 additions & 4 deletions rbd/clone_image_by_id_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
//go:build !(nautilus || octopus || pacific || quincy || reef) && ceph_preview
//go:build ceph_preview

package rbd

import (
"errors"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -91,6 +92,9 @@ func TestCloneImageByID(t *testing.T) {

// Create a clone of the image using the snapshot.
err = CloneImageByID(ioctx, name1, snapID, ioctx, cloneName, optionsClone)
if errors.Is(err, ErrNotImplemented) {
t.Skipf("CloneImageByID is not supported: %v", err)
}
assert.NoError(t, err)
defer func() { assert.NoError(t, RemoveImage(ioctx, cloneName)) }()

Expand All @@ -112,6 +116,9 @@ func TestCloneImageByID(t *testing.T) {
t.Run("CloneFromGroupSnap", func(t *testing.T) {
err := GroupSnapCreate(ioctx, gname, "groupsnap")
assert.NoError(t, err)
defer func() {
assert.NoError(t, GroupSnapRemove(ioctx, gname, "groupsnap"))
}()

cloneName := "img-clone"
optionsClone := NewRbdImageOptions()
Expand All @@ -132,6 +139,9 @@ func TestCloneImageByID(t *testing.T) {

// Create a clone of the image using the snapshot.
err = CloneImageByID(ioctx, name1, snapID, ioctx, cloneName, optionsClone)
if errors.Is(err, ErrNotImplemented) {
t.Skipf("CloneImageByID is not supported: %v", err)
}
assert.NoError(t, err)
defer func() { assert.NoError(t, RemoveImage(ioctx, cloneName)) }()

Expand All @@ -147,8 +157,5 @@ func TestCloneImageByID(t *testing.T) {
assert.Equal(t, parentInfo.Snap.ID, snapID)
assert.Equal(t, parentInfo.Image.PoolName, poolname)
assert.False(t, parentInfo.Image.Trash)

err = GroupSnapRemove(ioctx, gname, "groupsnap")
assert.NoError(t, err)
})
}
2 changes: 2 additions & 0 deletions rbd/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ var (
const (
// ErrNotExist indicates a non-specific missing resource.
ErrNotExist = rbdError(-C.ENOENT)
// ErrNotImplemented indicates a function is not implemented in by librbd.
ErrNotImplemented = rbdError(-C.ENOSYS)
)

// Private errors:
Expand Down
Loading