blob: 5dfb369f1ff85d37f4d488cae0dbb4a142ddcf06 [file] [log] [blame]
// +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))))
}