github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/pkg/devicemapper/devmapper_wrapper_dynamic_dlsym_deferred_remove.go (about) 1 // +build linux,cgo,!static_build 2 // +build libdm_dlsym_deferred_remove,!libdm_no_deferred_remove 3 4 package devicemapper 5 6 /* 7 #cgo LDFLAGS: -ldl 8 #include <stdlib.h> 9 #include <dlfcn.h> 10 #include <libdevmapper.h> 11 12 // Yes, I know this looks scary. In order to be able to fill our own internal 13 // dm_info with deferred_remove we need to have a struct definition that is 14 // correct (regardless of the version of libdm that was used to compile it). To 15 // this end, we define struct_backport_dm_info. This code comes from lvm2, and 16 // I have verified that the structure has only ever had elements *appended* to 17 // it (since 2001). 18 // 19 // It is also important that this structure be _larger_ than the dm_info that 20 // libdevmapper expected. Otherwise libdm might try to write to memory it 21 // shouldn't (they don't have a "known size" API). 22 struct backport_dm_info { 23 int exists; 24 int suspended; 25 int live_table; 26 int inactive_table; 27 int32_t open_count; 28 uint32_t event_nr; 29 uint32_t major; 30 uint32_t minor; 31 int read_only; 32 33 int32_t target_count; 34 35 int deferred_remove; 36 int internal_suspend; 37 38 // Padding, purely for our own safety. This is to avoid cases where libdm 39 // was updated underneath us and we call into dm_task_get_info() with too 40 // small of a buffer. 41 char _[512]; 42 }; 43 44 // We have to wrap this in CGo, because Go really doesn't like function pointers. 45 int call_dm_task_deferred_remove(void *fn, struct dm_task *task) 46 { 47 int (*_dm_task_deferred_remove)(struct dm_task *task) = fn; 48 return _dm_task_deferred_remove(task); 49 } 50 */ 51 import "C" 52 53 import ( 54 "unsafe" 55 56 "github.com/sirupsen/logrus" 57 ) 58 59 // dm_task_deferred_remove is not supported by all distributions, due to 60 // out-dated versions of devicemapper. However, in the case where the 61 // devicemapper library was updated without rebuilding Docker (which can happen 62 // in some distributions) then we should attempt to dynamically load the 63 // relevant object rather than try to link to it. 64 65 // dmTaskDeferredRemoveFct is a "bound" version of dm_task_deferred_remove. 66 // It is nil if dm_task_deferred_remove was not found in the libdevmapper that 67 // is currently loaded. 68 var dmTaskDeferredRemovePtr unsafe.Pointer 69 70 // LibraryDeferredRemovalSupport tells if the feature is supported by the 71 // current Docker invocation. This value is fixed during init. 72 var LibraryDeferredRemovalSupport bool 73 74 func init() { 75 // Clear any errors. 76 var err *C.char 77 C.dlerror() 78 79 // The symbol we want to fetch. 80 symName := C.CString("dm_task_deferred_remove") 81 defer C.free(unsafe.Pointer(symName)) 82 83 // See if we can find dm_task_deferred_remove. Since we already are linked 84 // to libdevmapper, we can search our own address space (rather than trying 85 // to guess what libdevmapper is called). We use NULL here, as RTLD_DEFAULT 86 // is not available in CGO (even if you set _GNU_SOURCE for some reason). 87 // The semantics are identical on glibc. 88 sym := C.dlsym(nil, symName) 89 err = C.dlerror() 90 if err != nil { 91 logrus.Debugf("devmapper: could not load dm_task_deferred_remove: %s", C.GoString(err)) 92 return 93 } 94 95 logrus.Debugf("devmapper: found dm_task_deferred_remove at %x", uintptr(sym)) 96 dmTaskDeferredRemovePtr = sym 97 LibraryDeferredRemovalSupport = true 98 } 99 100 func dmTaskDeferredRemoveFct(task *cdmTask) int { 101 sym := dmTaskDeferredRemovePtr 102 if sym == nil || !LibraryDeferredRemovalSupport { 103 return -1 104 } 105 return int(C.call_dm_task_deferred_remove(sym, (*C.struct_dm_task)(task))) 106 } 107 108 func dmTaskGetInfoWithDeferredFct(task *cdmTask, info *Info) int { 109 if !LibraryDeferredRemovalSupport { 110 return -1 111 } 112 113 Cinfo := C.struct_backport_dm_info{} 114 defer func() { 115 info.Exists = int(Cinfo.exists) 116 info.Suspended = int(Cinfo.suspended) 117 info.LiveTable = int(Cinfo.live_table) 118 info.InactiveTable = int(Cinfo.inactive_table) 119 info.OpenCount = int32(Cinfo.open_count) 120 info.EventNr = uint32(Cinfo.event_nr) 121 info.Major = uint32(Cinfo.major) 122 info.Minor = uint32(Cinfo.minor) 123 info.ReadOnly = int(Cinfo.read_only) 124 info.TargetCount = int32(Cinfo.target_count) 125 info.DeferredRemove = int(Cinfo.deferred_remove) 126 }() 127 return int(C.dm_task_get_info((*C.struct_dm_task)(task), (*C.struct_dm_info)(unsafe.Pointer(&Cinfo)))) 128 }