| //go:build linux && cgo | |
| // +build linux,cgo | |
| package devicemapper // import "github.com/docker/docker/pkg/devicemapper" | |
| /* | |
| #define _GNU_SOURCE | |
| #include <libdevmapper.h> | |
| #include <linux/fs.h> // FIXME: present only for BLKGETSIZE64, maybe we can remove it? | |
| // FIXME: Can't we find a way to do the logging in pure Go? | |
| extern void DevmapperLogCallback(int level, char *file, int line, int dm_errno_or_class, char *str); | |
| static void log_cb(int level, const char *file, int line, int dm_errno_or_class, const char *f, ...) | |
| { | |
| char *buffer = NULL; | |
| va_list ap; | |
| int ret; | |
| va_start(ap, f); | |
| ret = vasprintf(&buffer, f, ap); | |
| va_end(ap); | |
| if (ret < 0) { | |
| // memory allocation failed -- should never happen? | |
| return; | |
| } | |
| DevmapperLogCallback(level, (char *)file, line, dm_errno_or_class, buffer); | |
| free(buffer); | |
| } | |
| static void log_with_errno_init() | |
| { | |
| dm_log_with_errno_init(log_cb); | |
| } | |
| */ | |
| import "C" | |
| import ( | |
| "reflect" | |
| "unsafe" | |
| ) | |
| type ( | |
| cdmTask C.struct_dm_task | |
| ) | |
| // IOCTL consts | |
| const ( | |
| BlkGetSize64 = C.BLKGETSIZE64 | |
| BlkDiscard = C.BLKDISCARD | |
| ) | |
| // Devicemapper cookie flags. | |
| const ( | |
| DmUdevDisableSubsystemRulesFlag = C.DM_UDEV_DISABLE_SUBSYSTEM_RULES_FLAG | |
| DmUdevDisableDiskRulesFlag = C.DM_UDEV_DISABLE_DISK_RULES_FLAG | |
| DmUdevDisableOtherRulesFlag = C.DM_UDEV_DISABLE_OTHER_RULES_FLAG | |
| DmUdevDisableLibraryFallback = C.DM_UDEV_DISABLE_LIBRARY_FALLBACK | |
| ) | |
| // DeviceMapper mapped functions. | |
| var ( | |
| DmGetLibraryVersion = dmGetLibraryVersionFct | |
| DmGetNextTarget = dmGetNextTargetFct | |
| DmSetDevDir = dmSetDevDirFct | |
| DmTaskAddTarget = dmTaskAddTargetFct | |
| DmTaskCreate = dmTaskCreateFct | |
| DmTaskDestroy = dmTaskDestroyFct | |
| DmTaskGetDeps = dmTaskGetDepsFct | |
| DmTaskGetInfo = dmTaskGetInfoFct | |
| DmTaskGetDriverVersion = dmTaskGetDriverVersionFct | |
| DmTaskRun = dmTaskRunFct | |
| DmTaskSetAddNode = dmTaskSetAddNodeFct | |
| DmTaskSetCookie = dmTaskSetCookieFct | |
| DmTaskSetMessage = dmTaskSetMessageFct | |
| DmTaskSetName = dmTaskSetNameFct | |
| DmTaskSetSector = dmTaskSetSectorFct | |
| DmUdevWait = dmUdevWaitFct | |
| DmUdevSetSyncSupport = dmUdevSetSyncSupportFct | |
| DmUdevGetSyncSupport = dmUdevGetSyncSupportFct | |
| DmCookieSupported = dmCookieSupportedFct | |
| LogWithErrnoInit = logWithErrnoInitFct | |
| DmTaskDeferredRemove = dmTaskDeferredRemoveFct | |
| DmTaskGetInfoWithDeferred = dmTaskGetInfoWithDeferredFct | |
| ) | |
| func free(p *C.char) { | |
| C.free(unsafe.Pointer(p)) | |
| } | |
| func dmTaskDestroyFct(task *cdmTask) { | |
| C.dm_task_destroy((*C.struct_dm_task)(task)) | |
| } | |
| func dmTaskCreateFct(taskType int) *cdmTask { | |
| return (*cdmTask)(C.dm_task_create(C.int(taskType))) | |
| } | |
| func dmTaskRunFct(task *cdmTask) int { | |
| ret, _ := C.dm_task_run((*C.struct_dm_task)(task)) | |
| return int(ret) | |
| } | |
| func dmTaskSetNameFct(task *cdmTask, name string) int { | |
| Cname := C.CString(name) | |
| defer free(Cname) | |
| return int(C.dm_task_set_name((*C.struct_dm_task)(task), Cname)) | |
| } | |
| func dmTaskSetMessageFct(task *cdmTask, message string) int { | |
| Cmessage := C.CString(message) | |
| defer free(Cmessage) | |
| return int(C.dm_task_set_message((*C.struct_dm_task)(task), Cmessage)) | |
| } | |
| func dmTaskSetSectorFct(task *cdmTask, sector uint64) int { | |
| return int(C.dm_task_set_sector((*C.struct_dm_task)(task), C.uint64_t(sector))) | |
| } | |
| func dmTaskSetCookieFct(task *cdmTask, cookie *uint, flags uint16) int { | |
| cCookie := C.uint32_t(*cookie) | |
| defer func() { | |
| *cookie = uint(cCookie) | |
| }() | |
| return int(C.dm_task_set_cookie((*C.struct_dm_task)(task), &cCookie, C.uint16_t(flags))) | |
| } | |
| func dmTaskSetAddNodeFct(task *cdmTask, addNode AddNodeType) int { | |
| return int(C.dm_task_set_add_node((*C.struct_dm_task)(task), C.dm_add_node_t(addNode))) | |
| } | |
| func dmTaskAddTargetFct(task *cdmTask, start, size uint64, ttype, params string) int { | |
| Cttype := C.CString(ttype) | |
| defer free(Cttype) | |
| Cparams := C.CString(params) | |
| defer free(Cparams) | |
| return int(C.dm_task_add_target((*C.struct_dm_task)(task), C.uint64_t(start), C.uint64_t(size), Cttype, Cparams)) | |
| } | |
| func dmTaskGetDepsFct(task *cdmTask) *Deps { | |
| Cdeps := C.dm_task_get_deps((*C.struct_dm_task)(task)) | |
| if Cdeps == nil { | |
| return nil | |
| } | |
| // golang issue: https://github.com/golang/go/issues/11925 | |
| var devices []C.uint64_t | |
| devicesHdr := (*reflect.SliceHeader)(unsafe.Pointer(&devices)) | |
| devicesHdr.Data = uintptr(unsafe.Pointer(uintptr(unsafe.Pointer(Cdeps)) + unsafe.Sizeof(*Cdeps))) | |
| devicesHdr.Len = int(Cdeps.count) | |
| devicesHdr.Cap = int(Cdeps.count) | |
| deps := &Deps{ | |
| Count: uint32(Cdeps.count), | |
| Filler: uint32(Cdeps.filler), | |
| } | |
| for _, device := range devices { | |
| deps.Device = append(deps.Device, uint64(device)) | |
| } | |
| return deps | |
| } | |
| func dmTaskGetInfoFct(task *cdmTask, info *Info) int { | |
| Cinfo := C.struct_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) | |
| }() | |
| return int(C.dm_task_get_info((*C.struct_dm_task)(task), &Cinfo)) | |
| } | |
| func dmTaskGetDriverVersionFct(task *cdmTask) string { | |
| buffer := C.malloc(128) | |
| defer C.free(buffer) | |
| res := C.dm_task_get_driver_version((*C.struct_dm_task)(task), (*C.char)(buffer), 128) | |
| if res == 0 { | |
| return "" | |
| } | |
| return C.GoString((*C.char)(buffer)) | |
| } | |
| func dmGetNextTargetFct(task *cdmTask, next unsafe.Pointer, start, length *uint64, target, params *string) unsafe.Pointer { | |
| var ( | |
| Cstart, Clength C.uint64_t | |
| CtargetType, Cparams *C.char | |
| ) | |
| defer func() { | |
| *start = uint64(Cstart) | |
| *length = uint64(Clength) | |
| *target = C.GoString(CtargetType) | |
| *params = C.GoString(Cparams) | |
| }() | |
| //lint:ignore SA4000 false positive on (identical expressions on the left and right side of the '==' operator) (staticcheck) | |
| nextp := C.dm_get_next_target((*C.struct_dm_task)(task), next, &Cstart, &Clength, &CtargetType, &Cparams) | |
| return nextp | |
| } | |
| func dmUdevSetSyncSupportFct(syncWithUdev int) { | |
| C.dm_udev_set_sync_support(C.int(syncWithUdev)) | |
| } | |
| func dmUdevGetSyncSupportFct() int { | |
| return int(C.dm_udev_get_sync_support()) | |
| } | |
| func dmUdevWaitFct(cookie uint) int { | |
| return int(C.dm_udev_wait(C.uint32_t(cookie))) | |
| } | |
| func dmCookieSupportedFct() int { | |
| return int(C.dm_cookie_supported()) | |
| } | |
| func logWithErrnoInitFct() { | |
| C.log_with_errno_init() | |
| } | |
| func dmSetDevDirFct(dir string) int { | |
| Cdir := C.CString(dir) | |
| defer free(Cdir) | |
| return int(C.dm_set_dev_dir(Cdir)) | |
| } | |
| func dmGetLibraryVersionFct(version *string) int { | |
| buffer := C.CString(string(make([]byte, 128))) | |
| defer free(buffer) | |
| defer func() { | |
| *version = C.GoString(buffer) | |
| }() | |
| return int(C.dm_get_library_version(buffer, 128)) | |
| } |