Merge pull request #35518 from cyphar/libdm-dlsym-deferred_remove
pkg: devmapper: dynamically load dm_task_deferred_remove
This commit is contained in:
commit
5219725890
4 changed files with 149 additions and 7 deletions
12
hack/make.sh
12
hack/make.sh
|
@ -103,6 +103,12 @@ if [ ! "$GOPATH" ]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
# Adds $1_$2 to DOCKER_BUILDTAGS unless it already
|
||||
# contains a word starting from $1_
|
||||
add_buildtag() {
|
||||
[[ " $DOCKER_BUILDTAGS" == *" $1_"* ]] || DOCKER_BUILDTAGS+=" $1_$2"
|
||||
}
|
||||
|
||||
if ${PKG_CONFIG} 'libsystemd >= 209' 2> /dev/null ; then
|
||||
DOCKER_BUILDTAGS+=" journald"
|
||||
elif ${PKG_CONFIG} 'libsystemd-journal' 2> /dev/null ; then
|
||||
|
@ -118,12 +124,14 @@ if \
|
|||
fi
|
||||
|
||||
# test whether "libdevmapper.h" is new enough to support deferred remove
|
||||
# functionality.
|
||||
# functionality. We favour libdm_dlsym_deferred_remove over
|
||||
# libdm_no_deferred_remove in dynamic cases because the binary could be shipped
|
||||
# with a newer libdevmapper than the one it was built wih.
|
||||
if \
|
||||
command -v gcc &> /dev/null \
|
||||
&& ! ( echo -e '#include <libdevmapper.h>\nint main() { dm_task_deferred_remove(NULL); }'| gcc -xc - -o /dev/null $(pkg-config --libs devmapper) &> /dev/null ) \
|
||||
; then
|
||||
DOCKER_BUILDTAGS+=' libdm_no_deferred_remove'
|
||||
add_buildtag libdm dlsym_deferred_remove
|
||||
fi
|
||||
|
||||
# Use these flags when compiling the tests and final binary
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
// +build linux,cgo,!libdm_no_deferred_remove
|
||||
// +build linux,cgo,!static_build
|
||||
// +build !libdm_dlsym_deferred_remove,!libdm_no_deferred_remove
|
||||
|
||||
package devicemapper // import "github.com/docker/docker/pkg/devicemapper"
|
||||
|
||||
// #include <libdevmapper.h>
|
||||
/*
|
||||
#include <libdevmapper.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// LibraryDeferredRemovalSupport tells if the feature is enabled in the build
|
||||
// LibraryDeferredRemovalSupport tells if the feature is supported by the
|
||||
// current Docker invocation.
|
||||
const LibraryDeferredRemovalSupport = true
|
||||
|
||||
func dmTaskDeferredRemoveFct(task *cdmTask) int {
|
|
@ -0,0 +1,128 @@
|
|||
// +build linux,cgo,!static_build
|
||||
// +build libdm_dlsym_deferred_remove,!libdm_no_deferred_remove
|
||||
|
||||
package devicemapper
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -ldl
|
||||
#include <stdlib.h>
|
||||
#include <dlfcn.h>
|
||||
#include <libdevmapper.h>
|
||||
|
||||
// Yes, I know this looks scary. In order to be able to fill our own internal
|
||||
// dm_info with deferred_remove we need to have a struct definition that is
|
||||
// correct (regardless of the version of libdm that was used to compile it). To
|
||||
// this end, we define struct_backport_dm_info. This code comes from lvm2, and
|
||||
// I have verified that the structure has only ever had elements *appended* to
|
||||
// it (since 2001).
|
||||
//
|
||||
// It is also important that this structure be _larger_ than the dm_info that
|
||||
// libdevmapper expected. Otherwise libdm might try to write to memory it
|
||||
// shouldn't (they don't have a "known size" API).
|
||||
struct backport_dm_info {
|
||||
int exists;
|
||||
int suspended;
|
||||
int live_table;
|
||||
int inactive_table;
|
||||
int32_t open_count;
|
||||
uint32_t event_nr;
|
||||
uint32_t major;
|
||||
uint32_t minor;
|
||||
int read_only;
|
||||
|
||||
int32_t target_count;
|
||||
|
||||
int deferred_remove;
|
||||
int internal_suspend;
|
||||
|
||||
// Padding, purely for our own safety. This is to avoid cases where libdm
|
||||
// was updated underneath us and we call into dm_task_get_info() with too
|
||||
// small of a buffer.
|
||||
char _[512];
|
||||
};
|
||||
|
||||
// We have to wrap this in CGo, because Go really doesn't like function pointers.
|
||||
int call_dm_task_deferred_remove(void *fn, struct dm_task *task)
|
||||
{
|
||||
int (*_dm_task_deferred_remove)(struct dm_task *task) = fn;
|
||||
return _dm_task_deferred_remove(task);
|
||||
}
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// dm_task_deferred_remove is not supported by all distributions, due to
|
||||
// out-dated versions of devicemapper. However, in the case where the
|
||||
// devicemapper library was updated without rebuilding Docker (which can happen
|
||||
// in some distributions) then we should attempt to dynamically load the
|
||||
// relevant object rather than try to link to it.
|
||||
|
||||
// dmTaskDeferredRemoveFct is a "bound" version of dm_task_deferred_remove.
|
||||
// It is nil if dm_task_deferred_remove was not found in the libdevmapper that
|
||||
// is currently loaded.
|
||||
var dmTaskDeferredRemovePtr unsafe.Pointer
|
||||
|
||||
// LibraryDeferredRemovalSupport tells if the feature is supported by the
|
||||
// current Docker invocation. This value is fixed during init.
|
||||
var LibraryDeferredRemovalSupport bool
|
||||
|
||||
func init() {
|
||||
// Clear any errors.
|
||||
var err *C.char
|
||||
C.dlerror()
|
||||
|
||||
// The symbol we want to fetch.
|
||||
symName := C.CString("dm_task_deferred_remove")
|
||||
defer C.free(unsafe.Pointer(symName))
|
||||
|
||||
// See if we can find dm_task_deferred_remove. Since we already are linked
|
||||
// to libdevmapper, we can search our own address space (rather than trying
|
||||
// to guess what libdevmapper is called). We use NULL here, as RTLD_DEFAULT
|
||||
// is not available in CGO (even if you set _GNU_SOURCE for some reason).
|
||||
// The semantics are identical on glibc.
|
||||
sym := C.dlsym(nil, symName)
|
||||
err = C.dlerror()
|
||||
if err != nil {
|
||||
logrus.Debugf("devmapper: could not load dm_task_deferred_remove: %s", C.GoString(err))
|
||||
return
|
||||
}
|
||||
|
||||
logrus.Debugf("devmapper: found dm_task_deferred_remove at %x", uintptr(sym))
|
||||
dmTaskDeferredRemovePtr = sym
|
||||
LibraryDeferredRemovalSupport = true
|
||||
}
|
||||
|
||||
func dmTaskDeferredRemoveFct(task *cdmTask) int {
|
||||
sym := dmTaskDeferredRemovePtr
|
||||
if sym == nil || !LibraryDeferredRemovalSupport {
|
||||
return -1
|
||||
}
|
||||
return int(C.call_dm_task_deferred_remove(sym, (*C.struct_dm_task)(task)))
|
||||
}
|
||||
|
||||
func dmTaskGetInfoWithDeferredFct(task *cdmTask, info *Info) int {
|
||||
if !LibraryDeferredRemovalSupport {
|
||||
return -1
|
||||
}
|
||||
|
||||
Cinfo := C.struct_backport_dm_info{}
|
||||
defer func() {
|
||||
info.Exists = int(Cinfo.exists)
|
||||
info.Suspended = int(Cinfo.suspended)
|
||||
info.LiveTable = int(Cinfo.live_table)
|
||||
info.InactiveTable = int(Cinfo.inactive_table)
|
||||
info.OpenCount = int32(Cinfo.open_count)
|
||||
info.EventNr = uint32(Cinfo.event_nr)
|
||||
info.Major = uint32(Cinfo.major)
|
||||
info.Minor = uint32(Cinfo.minor)
|
||||
info.ReadOnly = int(Cinfo.read_only)
|
||||
info.TargetCount = int32(Cinfo.target_count)
|
||||
info.DeferredRemove = int(Cinfo.deferred_remove)
|
||||
}()
|
||||
return int(C.dm_task_get_info((*C.struct_dm_task)(task), (*C.struct_dm_info)(unsafe.Pointer(&Cinfo))))
|
||||
}
|
|
@ -1,8 +1,10 @@
|
|||
// +build linux,cgo,libdm_no_deferred_remove
|
||||
// +build linux,cgo
|
||||
// +build !libdm_dlsym_deferred_remove,libdm_no_deferred_remove
|
||||
|
||||
package devicemapper // import "github.com/docker/docker/pkg/devicemapper"
|
||||
|
||||
// LibraryDeferredRemovalSupport tells if the feature is enabled in the build
|
||||
// LibraryDeferredRemovalSupport tells if the feature is supported by the
|
||||
// current Docker invocation.
|
||||
const LibraryDeferredRemovalSupport = false
|
||||
|
||||
func dmTaskDeferredRemoveFct(task *cdmTask) int {
|
||||
|
|
Loading…
Reference in a new issue