diff --git a/internal/rbd/features/dlsym.go b/internal/rbd/features/dlsym.go new file mode 100644 index 000000000..54ffb4509 --- /dev/null +++ b/internal/rbd/features/dlsym.go @@ -0,0 +1,48 @@ +/* +Copyright 2024 The Ceph-CSI Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package features + +/* +#cgo LDFLAGS: -ldl +#include +#include +*/ +import "C" + +import ( + "fmt" + "unsafe" +) + +// dlsym checks if the given symbol is provided by the currently loaded +// libraries. If the symbol is available, no error is returned. +func dlsym(symbol string) error { + c_symbol := C.CString(symbol) + //nolint:nlreturn // linter complains about missing empty line!? + defer C.free(unsafe.Pointer(c_symbol)) + + // clear dlerror before looking up the symbol + C.dlerror() + _ = C.dlsym(nil, c_symbol) + e := C.dlerror() + err := C.GoString(e) + if err != "" { + return fmt.Errorf("dlsym: %s", err) + } + + return nil +} diff --git a/internal/rbd/features/features.go b/internal/rbd/features/features.go new file mode 100644 index 000000000..0b2e5434d --- /dev/null +++ b/internal/rbd/features/features.go @@ -0,0 +1,58 @@ +/* +Copyright 2024 The Ceph-CSI Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package features + +/* +#cgo LDFLAGS: -lrbd +#include +*/ +import "C" + +import ( + "strings" + "sync" +) + +var ( + groupGetSnapInfoOnce sync.Once + errGroupGetSnapInfo error + groupGetSnapInfoSupported = false +) + +// SupportsGroupSnapGetInfo detects if librbd has the rbd_group_snap_get_info +// function. +func SupportsGroupSnapGetInfo() (bool, error) { + groupGetSnapInfoOnce.Do(func() { + // make sure librbd.so.x is loaded, might not (yet) be the case + // if no rbd functions are called + var opts C.rbd_image_options_t + //nolint:gocritic // ignore result of rbd_image_options functions + C.rbd_image_options_create(&opts) + C.rbd_image_options_destroy(opts) + + // check for rbd_group_snap_get_info() in loaded libs/symbols + errGroupGetSnapInfo = dlsym("rbd_group_snap_get_info") + + if errGroupGetSnapInfo == nil { + groupGetSnapInfoSupported = true + } else if strings.Contains(errGroupGetSnapInfo.Error(), "undefined symbol") { + errGroupGetSnapInfo = nil + } + }) + + return groupGetSnapInfoSupported, errGroupGetSnapInfo +} diff --git a/internal/rbd/features/features_test.go b/internal/rbd/features/features_test.go new file mode 100644 index 000000000..fb7730796 --- /dev/null +++ b/internal/rbd/features/features_test.go @@ -0,0 +1,32 @@ +/* +Copyright 2024 ceph-csi authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package features + +import ( + "testing" +) + +func TestSupportsGroupSnapGetInfo(t *testing.T) { + t.Parallel() + + supported, err := SupportsGroupSnapGetInfo() + if err != nil { + t.Errorf("failed to check support for GroupSnapGetInfo: %v", err) + } + + t.Logf("GroupSnapGetInfo supported: %t", supported) +}