2021-08-23 09:14:53 -04:00
|
|
|
//go:build linux && cgo && !static_build && libdm_dlsym_deferred_remove && !libdm_no_deferred_remove
|
|
|
|
// +build linux,cgo,!static_build,libdm_dlsym_deferred_remove,!libdm_no_deferred_remove
|
2017-11-16 01:09:16 -05:00
|
|
|
|
|
|
|
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))))
|
|
|
|
}
|