github.com/rawahars/moby@v24.0.4+incompatible/pkg/devicemapper/devmapper_wrapper_dynamic_dlsym_deferred_remove.go (about)

     1  //go:build linux && cgo && !static_build && libdm_dlsym_deferred_remove && !libdm_no_deferred_remove
     2  // +build linux,cgo,!static_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  }