Merge pull request #35518 from cyphar/libdm-dlsym-deferred_remove

pkg: devmapper: dynamically load dm_task_deferred_remove
This commit is contained in:
Vincent Demeester 2018-04-11 14:11:16 +02:00 committed by GitHub
commit 5219725890
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 149 additions and 7 deletions

View file

@ -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

View file

@ -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 {

View file

@ -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))))
}

View file

@ -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 {