6e5904b7ca
- Add a patch to dynamically probe whether libdevmapper supports dm_task_deferred_remove. This is necessary because we build the containers module on a SLE12 base, but later SLE versions have libdevmapper support. This should not affect openSUSE, as all openSUSE versions have a new enough libdevmapper. Backport of https://github.com/moby/moby/pull/35518. bsc#1021227 bsc#1029320 bsc#1058173 + bsc1021227-0001-pkg-devmapper-dynamically-load-dm_task_deferred_remo.patch OBS-URL: https://build.opensuse.org/request/show/558082 OBS-URL: https://build.opensuse.org/package/show/Virtualization:containers/docker?expand=0&rev=227
245 lines
8.8 KiB
Diff
245 lines
8.8 KiB
Diff
From 69d2f2339e43e44ea23bb9b9f699b093046568fe Mon Sep 17 00:00:00 2001
|
|
From: Aleksa Sarai <asarai@suse.de>
|
|
Date: Thu, 16 Nov 2017 17:09:16 +1100
|
|
Subject: [PATCH] pkg: devmapper: dynamically load dm_task_deferred_remove
|
|
|
|
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.
|
|
|
|
This can only be done if Docker was built dynamically, for obvious
|
|
reasons.
|
|
|
|
In order to avoid having issues arise when dlsym(3) was unnecessary,
|
|
gate the whole dlsym(3) logic behind a buildflag that we disable by
|
|
default (libdm_dlsym_deferred_remove).
|
|
|
|
SUSE-Bugs: bsc#1021227 bsc#1029320 bsc#1058173
|
|
SUSE-Backport: https://github.com/moby/moby/pull/35518
|
|
Signed-off-by: Aleksa Sarai <asarai@suse.de>
|
|
---
|
|
hack/make.sh | 12 +-
|
|
...> devmapper_wrapper_dynamic_deferred_remove.go} | 10 +-
|
|
...mapper_wrapper_dynamic_dlsym_deferred_remove.go | 128 +++++++++++++++++++++
|
|
.../devmapper_wrapper_no_deferred_remove.go | 6 +-
|
|
4 files changed, 149 insertions(+), 7 deletions(-)
|
|
rename pkg/devicemapper/{devmapper_wrapper_deferred_remove.go => devmapper_wrapper_dynamic_deferred_remove.go} (78%)
|
|
create mode 100644 pkg/devicemapper/devmapper_wrapper_dynamic_dlsym_deferred_remove.go
|
|
|
|
diff --git a/hack/make.sh b/hack/make.sh
|
|
index 58e0d8cd628a..3b78ddef30b0 100755
|
|
--- a/hack/make.sh
|
|
+++ b/hack/make.sh
|
|
@@ -112,6 +112,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
|
|
@@ -127,12 +133,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 -ldevmapper &> /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
|
|
diff --git a/pkg/devicemapper/devmapper_wrapper_deferred_remove.go b/pkg/devicemapper/devmapper_wrapper_dynamic_deferred_remove.go
|
|
similarity index 78%
|
|
rename from pkg/devicemapper/devmapper_wrapper_deferred_remove.go
|
|
rename to pkg/devicemapper/devmapper_wrapper_dynamic_deferred_remove.go
|
|
index 7f793c270868..bf57371ff4cf 100644
|
|
--- a/pkg/devicemapper/devmapper_wrapper_deferred_remove.go
|
|
+++ b/pkg/devicemapper/devmapper_wrapper_dynamic_deferred_remove.go
|
|
@@ -1,14 +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
|
|
|
|
/*
|
|
-#cgo LDFLAGS: -L. -ldevmapper
|
|
#include <libdevmapper.h>
|
|
*/
|
|
import "C"
|
|
|
|
-// LibraryDeferredRemovalSupport is supported when statically linked.
|
|
+// LibraryDeferredRemovalSupport tells if the feature is supported by the
|
|
+// current Docker invocation.
|
|
const LibraryDeferredRemovalSupport = true
|
|
|
|
func dmTaskDeferredRemoveFct(task *cdmTask) int {
|
|
diff --git a/pkg/devicemapper/devmapper_wrapper_dynamic_dlsym_deferred_remove.go b/pkg/devicemapper/devmapper_wrapper_dynamic_dlsym_deferred_remove.go
|
|
new file mode 100644
|
|
index 000000000000..5dfb369f1ff8
|
|
--- /dev/null
|
|
+++ b/pkg/devicemapper/devmapper_wrapper_dynamic_dlsym_deferred_remove.go
|
|
@@ -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))))
|
|
+}
|
|
diff --git a/pkg/devicemapper/devmapper_wrapper_no_deferred_remove.go b/pkg/devicemapper/devmapper_wrapper_no_deferred_remove.go
|
|
index a880fec8c499..80b034b3ff17 100644
|
|
--- a/pkg/devicemapper/devmapper_wrapper_no_deferred_remove.go
|
|
+++ b/pkg/devicemapper/devmapper_wrapper_no_deferred_remove.go
|
|
@@ -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
|
|
|
|
-// LibraryDeferredRemovalSupport is not supported when statically linked.
|
|
+// LibraryDeferredRemovalSupport tells if the feature is supported by the
|
|
+// current Docker invocation.
|
|
const LibraryDeferredRemovalSupport = false
|
|
|
|
func dmTaskDeferredRemoveFct(task *cdmTask) int {
|
|
--
|
|
2.15.1
|
|
|