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