Parcourir la source

Merge pull request #26 from shykes/refactor_dm

Refactor dm
Michael Crosby il y a 11 ans
Parent
commit
539708aa8a
3 fichiers modifiés avec 632 ajouts et 252 suppressions
  1. 62 252
      devmapper/devmapper.go
  2. 221 0
      devmapper/devmapper_test.go
  3. 349 0
      devmapper/devmapper_wrapper.go

+ 62 - 252
devmapper/devmapper.go

@@ -1,163 +1,11 @@
 package devmapper
 
-/*
-#cgo LDFLAGS: -L. -ldevmapper
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <libdevmapper.h>
-#include <linux/loop.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <linux/fs.h>
-#include <errno.h>
-
-#ifndef LOOP_CTL_GET_FREE
-#define LOOP_CTL_GET_FREE       0x4C82
-#endif
-
-// FIXME: this could easily be rewritten in go
-char*			attach_loop_device(const char *filename, int *loop_fd_out)
-{
-  struct loop_info64	loopinfo = {0};
-  struct stat		st;
-  char			buf[64];
-  int			i, loop_fd, fd, start_index;
-  char*			loopname;
-
-
-  *loop_fd_out = -1;
-
-  start_index = 0;
-  fd = open("/dev/loop-control", O_RDONLY);
-  if (fd >= 0) {
-    start_index = ioctl(fd, LOOP_CTL_GET_FREE);
-    close(fd);
-
-    if (start_index < 0)
-      start_index = 0;
-  }
-
-  fd = open(filename, O_RDWR);
-  if (fd < 0) {
-    perror("open");
-    return NULL;
-  }
-
-  loop_fd = -1;
-  for (i = start_index ; loop_fd < 0 ; i++ ) {
-    if (sprintf(buf, "/dev/loop%d", i) < 0) {
-	close(fd);
-	return NULL;
-    }
-
-    if (stat(buf, &st)) {
-      if (!S_ISBLK(st.st_mode)) {
-	 fprintf(stderr, "[error] Loopback device %s is not a block device.\n", buf);
-      } else if (errno == ENOENT) {
-	fprintf(stderr, "[error] There are no more loopback device available.\n");
-      } else {
-	fprintf(stderr, "[error] Unkown error trying to stat the loopback device %s (errno: %d).\n", buf, errno);
-      }
-      close(fd);
-      return NULL;
-    }
-
-    loop_fd = open(buf, O_RDWR);
-    if (loop_fd < 0 && errno == ENOENT) {
-      fprintf(stderr, "[error] The loopback device %s does not exists.\n", buf);
-      close(fd);
-      return NULL;
-    } else if (loop_fd < 0) {
-	fprintf(stderr, "[error] Unkown error openning the loopback device %s. (errno: %d)\n", buf, errno);
-	continue;
-    }
-
-    if (ioctl(loop_fd, LOOP_SET_FD, (void *)(size_t)fd) < 0) {
-      int errsv = errno;
-      close(loop_fd);
-      loop_fd = -1;
-      if (errsv != EBUSY) {
-        close(fd);
-        fprintf(stderr, "cannot set up loopback device %s: %s", buf, strerror(errsv));
-        return NULL;
-      }
-      continue;
-    }
-
-    close(fd);
-
-    strncpy((char*)loopinfo.lo_file_name, buf, LO_NAME_SIZE);
-    loopinfo.lo_offset = 0;
-    loopinfo.lo_flags = LO_FLAGS_AUTOCLEAR;
-
-    if (ioctl(loop_fd, LOOP_SET_STATUS64, &loopinfo) < 0) {
-      perror("ioctl LOOP_SET_STATUS64");
-      if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) {
-        perror("ioctl LOOP_CLR_FD");
-      }
-      close(loop_fd);
-      fprintf (stderr, "cannot set up loopback device info");
-      return (NULL);
-    }
-
-    loopname = strdup(buf);
-    if (loopname == NULL) {
-      close(loop_fd);
-      return (NULL);
-    }
-
-    *loop_fd_out = loop_fd;
-    return (loopname);
-  }
-
-  return (NULL);
-}
-
-static int64_t	get_block_size(int fd)
-{
-  uint64_t	size;
-
-  if (ioctl(fd, BLKGETSIZE64, &size) == -1)
-    return -1;
-  return ((int64_t)size);
-}
-
-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[256];
-  va_list ap;
-
-  va_start(ap, f);
-  vsnprintf(buffer, 256, f, ap);
-  va_end(ap);
-
-  DevmapperLogCallback(level, (char *)file, line, dm_errno_or_class, buffer);
-}
-
-static void
-log_with_errno_init ()
-{
-  dm_log_with_errno_init(log_cb);
-}
-
-*/
-import "C"
-
 import (
 	"errors"
 	"fmt"
 	"github.com/dotcloud/docker/utils"
 	"os"
 	"runtime"
-	"syscall"
-	"unsafe"
 )
 
 type DevmapperLogger interface {
@@ -198,7 +46,11 @@ var (
 	ErrTaskSetAddNode       = errors.New("dm_task_set_add_node failed")
 	ErrTaskSetRO            = errors.New("dm_task_set_ro failed")
 	ErrTaskAddTarget        = errors.New("dm_task_add_target failed")
+	ErrTaskSetSector        = errors.New("dm_task_set_sector failed")
+	ErrGetInfo              = errors.New("dm_task_get_info failed")
 	ErrGetDriverVersion     = errors.New("dm_task_get_driver_version failed")
+	ErrTaskSetCookie        = errors.New("dm_task_set_cookie failed")
+	ErrNilCookie            = errors.New("cookie ptr can't be nil")
 	ErrAttachLoopbackDevice = errors.New("loopback mounting failed")
 	ErrGetBlockSize         = errors.New("Can't get block size")
 	ErrUdevWait             = errors.New("wait on udev cookie failed")
@@ -210,7 +62,7 @@ var (
 
 type (
 	Task struct {
-		unmanaged *C.struct_dm_task
+		unmanaged *CDmTask
 	}
 	Info struct {
 		Exists        int
@@ -230,167 +82,134 @@ type (
 
 func (t *Task) destroy() {
 	if t != nil {
-		C.dm_task_destroy(t.unmanaged)
+		DmTaskDestory(t.unmanaged)
 		runtime.SetFinalizer(t, nil)
 	}
 }
 
 func TaskCreate(tasktype TaskType) *Task {
-	c_task := C.dm_task_create(C.int(tasktype))
-	if c_task == nil {
+	Ctask := DmTaskCreate(int(tasktype))
+	if Ctask == nil {
 		return nil
 	}
-	task := &Task{unmanaged: c_task}
+	task := &Task{unmanaged: Ctask}
 	runtime.SetFinalizer(task, (*Task).destroy)
 	return task
 }
 
 func (t *Task) Run() error {
-	if res := C.dm_task_run(t.unmanaged); res != 1 {
+	if res := DmTaskRun(t.unmanaged); res != 1 {
 		return ErrTaskRun
 	}
 	return nil
 }
 
 func (t *Task) SetName(name string) error {
-	c_name := C.CString(name)
-	defer free(c_name)
-
-	if res := C.dm_task_set_name(t.unmanaged, c_name); res != 1 {
+	if res := DmTaskSetName(t.unmanaged, name); res != 1 {
 		return ErrTaskSetName
 	}
 	return nil
 }
 
 func (t *Task) SetMessage(message string) error {
-	c_message := C.CString(message)
-	defer free(c_message)
-
-	if res := C.dm_task_set_message(t.unmanaged, c_message); res != 1 {
+	if res := DmTaskSetMessage(t.unmanaged, message); res != 1 {
 		return ErrTaskSetMessage
 	}
 	return nil
 }
 
 func (t *Task) SetSector(sector uint64) error {
-	if res := C.dm_task_set_sector(t.unmanaged, C.uint64_t(sector)); res != 1 {
-		return ErrTaskSetAddNode
+	if res := DmTaskSetSector(t.unmanaged, sector); res != 1 {
+		return ErrTaskSetSector
 	}
 	return nil
 }
 
-func (t *Task) SetCookie(cookie *uint32, flags uint16) error {
-	c_cookie := C.uint32_t(*cookie)
-	if res := C.dm_task_set_cookie(t.unmanaged, &c_cookie, C.uint16_t(flags)); res != 1 {
-		return ErrTaskSetAddNode
+func (t *Task) SetCookie(cookie *uint, flags uint16) error {
+	if cookie == nil {
+		return ErrNilCookie
+	}
+	if res := DmTaskSetCookie(t.unmanaged, cookie, flags); res != 1 {
+		return ErrTaskSetCookie
 	}
-	*cookie = uint32(c_cookie)
 	return nil
 }
 
-func (t *Task) SetAddNode(add_node AddNodeType) error {
-	if res := C.dm_task_set_add_node(t.unmanaged, C.dm_add_node_t(add_node)); res != 1 {
+func (t *Task) SetAddNode(addNode AddNodeType) error {
+	if res := DmTaskSetAddNode(t.unmanaged, addNode); res != 1 {
 		return ErrTaskSetAddNode
 	}
 	return nil
 }
 
 func (t *Task) SetRo() error {
-	if res := C.dm_task_set_ro(t.unmanaged); res != 1 {
+	if res := DmTaskSetRo(t.unmanaged); res != 1 {
 		return ErrTaskSetRO
 	}
 	return nil
 }
 
-func (t *Task) AddTarget(start uint64, size uint64, ttype string, params string) error {
-	c_ttype := C.CString(ttype)
-	defer free(c_ttype)
-
-	c_params := C.CString(params)
-	defer free(c_params)
-
-	if res := C.dm_task_add_target(t.unmanaged, C.uint64_t(start), C.uint64_t(size), c_ttype, c_params); res != 1 {
+func (t *Task) AddTarget(start, size uint64, ttype, params string) error {
+	if res := DmTaskAddTarget(t.unmanaged, start, size,
+		ttype, params); res != 1 {
 		return ErrTaskAddTarget
 	}
 	return nil
 }
 
 func (t *Task) GetDriverVersion() (string, error) {
-	buffer := C.CString(string(make([]byte, 128)))
-	defer free(buffer)
-
-	if res := C.dm_task_get_driver_version(t.unmanaged, buffer, 128); res != 1 {
+	var version string
+	if res := DmTaskGetDriverVersion(t.unmanaged, &version); res != 1 {
 		return "", ErrGetDriverVersion
 	}
-	return C.GoString(buffer), nil
+	return version, nil
 }
 
 func (t *Task) GetInfo() (*Info, error) {
-	c_info := C.struct_dm_info{}
-	if res := C.dm_task_get_info(t.unmanaged, &c_info); res != 1 {
-		return nil, ErrGetDriverVersion
-	}
-	return &Info{
-		Exists:        int(c_info.exists),
-		Suspended:     int(c_info.suspended),
-		LiveTable:     int(c_info.live_table),
-		InactiveTable: int(c_info.inactive_table),
-		OpenCount:     int32(c_info.open_count),
-		EventNr:       uint32(c_info.event_nr),
-		Major:         uint32(c_info.major),
-		Minor:         uint32(c_info.minor),
-		ReadOnly:      int(c_info.read_only),
-		TargetCount:   int32(c_info.target_count),
-	}, nil
-}
-
-func (t *Task) GetNextTarget(next uintptr) (uintptr, uint64, uint64, string, string) {
-	var (
-		c_start, c_length       C.uint64_t
-		c_target_type, c_params *C.char
-	)
-
-	nextp := C.dm_get_next_target(t.unmanaged, unsafe.Pointer(next), &c_start, &c_length, &c_target_type, &c_params)
-	return uintptr(nextp), uint64(c_start), uint64(c_length), C.GoString(c_target_type), C.GoString(c_params)
+	info := &Info{}
+	if res := DmTaskGetInfo(t.unmanaged, info); res != 1 {
+		return nil, ErrGetInfo
+	}
+	return info, nil
+}
+
+func (t *Task) GetNextTarget(next uintptr) (nextPtr uintptr, start uint64,
+	length uint64, targetType string, params string) {
+
+	return DmGetNextTarget(t.unmanaged, next, &start, &length,
+			&targetType, &params),
+		start, length, targetType, params
 }
 
 func AttachLoopDevice(filename string) (*os.File, error) {
-	c_filename := C.CString(filename)
-	defer free(c_filename)
-
-	var fd C.int
-	res := C.attach_loop_device(c_filename, &fd)
-	if res == nil {
-		if os.Getenv("DEBUG") != "" {
-			C.perror(C.CString(fmt.Sprintf("[debug] Error attach_loop_device(%s, %d)", filename, int(fd))))
-		}
+	var fd int
+	res := DmAttachLoopDevice(filename, &fd)
+	if res == "" {
 		return nil, ErrAttachLoopbackDevice
 	}
-	defer free(res)
-
-	return os.NewFile(uintptr(fd), C.GoString(res)), nil
+	return os.NewFile(uintptr(fd), res), nil
 }
 
 func getBlockSize(fd uintptr) int {
 	var size uint64
 
-	if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.BLKGETSIZE64, uintptr(unsafe.Pointer(&size))); err != 0 {
-		utils.Debugf("Error ioctl: %s", err)
+	if err := SysGetBlockSize(fd, &size); err != 0 {
+		utils.Debugf("Error ioctl (getBlockSize: %s)", err)
 		return -1
 	}
 	return int(size)
 }
 
 func GetBlockDeviceSize(file *os.File) (uint64, error) {
-	if size := C.get_block_size(C.int(file.Fd())); size == -1 {
+	size := DmGetBlockSize(file.Fd())
+	if size == -1 {
 		return 0, ErrGetBlockSize
-	} else {
-		return uint64(size), nil
 	}
+	return uint64(size), nil
 }
 
-func UdevWait(cookie uint32) error {
-	if res := C.dm_udev_wait(C.uint32_t(cookie)); res != 1 {
+func UdevWait(cookie uint) error {
+	if res := DmUdevWait(cookie); res != 1 {
 		utils.Debugf("Failed to wait on udev cookie %d", cookie)
 		return ErrUdevWait
 	}
@@ -398,21 +217,18 @@ func UdevWait(cookie uint32) error {
 }
 
 func LogInitVerbose(level int) {
-	C.dm_log_init_verbose(C.int(level))
+	DmLogInitVerbose(level)
 }
 
 var dmLogger DevmapperLogger = nil
 
 func logInit(logger DevmapperLogger) {
 	dmLogger = logger
-	C.log_with_errno_init()
+	LogWithErrnoInit()
 }
 
 func SetDevDir(dir string) error {
-	c_dir := C.CString(dir)
-	defer free(c_dir)
-
-	if res := C.dm_set_dev_dir(c_dir); res != 1 {
+	if res := DmSetDevDir(dir); res != 1 {
 		utils.Debugf("Error dm_set_dev_dir")
 		return ErrSetDevDir
 	}
@@ -420,13 +236,11 @@ func SetDevDir(dir string) error {
 }
 
 func GetLibraryVersion() (string, error) {
-	buffer := C.CString(string(make([]byte, 128)))
-	defer free(buffer)
-
-	if res := C.dm_get_library_version(buffer, 128); res != 1 {
+	var version string
+	if res := DmGetLibraryVersion(&version); res != 1 {
 		return "", ErrGetLibraryVersion
 	}
-	return C.GoString(buffer), nil
+	return version, nil
 }
 
 // Useful helper for cleanup
@@ -445,10 +259,6 @@ func RemoveDevice(name string) error {
 	return nil
 }
 
-func free(p *C.char) {
-	C.free(unsafe.Pointer(p))
-}
-
 // This is the programmatic example of "dmsetup create"
 func createPool(poolName string, dataFile *os.File, metadataFile *os.File) error {
 	task, err := createTask(DeviceCreate, poolName)
@@ -466,7 +276,7 @@ func createPool(poolName string, dataFile *os.File, metadataFile *os.File) error
 		return fmt.Errorf("Can't add target")
 	}
 
-	var cookie uint32 = 0
+	var cookie uint = 0
 	if err := task.SetCookie(&cookie, 0); err != nil {
 		return fmt.Errorf("Can't set cookie")
 	}
@@ -564,7 +374,7 @@ func resumeDevice(name string) error {
 		return err
 	}
 
-	var cookie uint32 = 0
+	var cookie uint = 0
 	if err := task.SetCookie(&cookie, 0); err != nil {
 		return fmt.Errorf("Can't set cookie")
 	}
@@ -646,7 +456,7 @@ func activateDevice(poolName string, name string, deviceId int, size uint64) err
 		return fmt.Errorf("Can't add node")
 	}
 
-	var cookie uint32 = 0
+	var cookie uint = 0
 	if err := task.SetCookie(&cookie, 0); err != nil {
 		return fmt.Errorf("Can't set cookie")
 	}

+ 221 - 0
devmapper/devmapper_test.go

@@ -0,0 +1,221 @@
+package devmapper
+
+import (
+	"syscall"
+	"testing"
+)
+
+func TestTaskCreate(t *testing.T) {
+	// Test success
+	taskCreate(t, DeviceInfo)
+
+	// Test Failure
+	DmTaskCreate = dmTaskCreateFail
+	defer func() { DmTaskCreate = dmTaskCreateFct }()
+	if task := TaskCreate(-1); task != nil {
+		t.Fatalf("An error should have occured while creating an invalid task.")
+	}
+}
+
+func TestTaskRun(t *testing.T) {
+	task := taskCreate(t, DeviceInfo)
+
+	// Test success
+	// Perform the RUN
+	if err := task.Run(); err != nil {
+		t.Fatal(err)
+	}
+	// Make sure we don't have error with GetInfo
+	if _, err := task.GetInfo(); err != nil {
+		t.Fatal(err)
+	}
+
+	// Test failure
+	DmTaskRun = dmTaskRunFail
+	defer func() { DmTaskRun = dmTaskRunFct }()
+
+	task = taskCreate(t, DeviceInfo)
+	// Perform the RUN
+	if err := task.Run(); err == nil {
+		t.Fatalf("An error should have occured while running task.")
+	}
+	// Make sure GetInfo also fails
+	if _, err := task.GetInfo(); err == nil {
+		t.Fatalf("GetInfo should fail if task.Run() failed.")
+	}
+}
+
+func TestTaskSetName(t *testing.T) {
+	task := taskCreate(t, DeviceInfo)
+
+	// Test success
+	if err := task.SetName("test"); err != nil {
+		t.Fatal(err)
+	}
+
+	// Test failure
+	DmTaskSetName = dmTaskSetNameFail
+	defer func() { DmTaskSetName = dmTaskSetNameFct }()
+	if err := task.SetName("test"); err != ErrTaskSetName {
+		t.Fatalf("An error should have occured while runnign SetName.")
+	}
+}
+
+func TestTaskSetMessage(t *testing.T) {
+	task := taskCreate(t, DeviceInfo)
+
+	// Test success
+	if err := task.SetMessage("test"); err != nil {
+		t.Fatal(err)
+	}
+
+	// Test failure
+	DmTaskSetMessage = dmTaskSetMessageFail
+	defer func() { DmTaskSetMessage = dmTaskSetMessageFct }()
+	if err := task.SetMessage("test"); err != ErrTaskSetMessage {
+		t.Fatalf("An error should have occured while runnign SetMessage.")
+	}
+}
+
+func TestTaskSetSector(t *testing.T) {
+	task := taskCreate(t, DeviceInfo)
+
+	// Test success
+	if err := task.SetSector(128); err != nil {
+		t.Fatal(err)
+	}
+
+	DmTaskSetSector = dmTaskSetSectorFail
+	defer func() { DmTaskSetSector = dmTaskSetSectorFct }()
+
+	// Test failure
+	if err := task.SetSector(0); err != ErrTaskSetSector {
+		t.Fatalf("An error should have occured while running SetSector.")
+	}
+}
+
+func TestTaskSetCookie(t *testing.T) {
+	var (
+		cookie uint = 0
+		task        = taskCreate(t, DeviceInfo)
+	)
+
+	// Test success
+	if err := task.SetCookie(&cookie, 0); err != nil {
+		t.Fatal(err)
+	}
+
+	// Test failure
+	if err := task.SetCookie(nil, 0); err != ErrNilCookie {
+		t.Fatalf("An error should have occured while running SetCookie with nil cookie.")
+	}
+
+	DmTaskSetCookie = dmTaskSetCookieFail
+	defer func() { DmTaskSetCookie = dmTaskSetCookieFct }()
+
+	if err := task.SetCookie(&cookie, 0); err != ErrTaskSetCookie {
+		t.Fatalf("An error should have occured while running SetCookie.")
+	}
+}
+
+func TestTaskSetAddNode(t *testing.T) {
+	task := taskCreate(t, DeviceInfo)
+	if err := task.SetAddNode(0); err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestTaskSetRo(t *testing.T) {
+	task := taskCreate(t, DeviceInfo)
+	if err := task.SetRo(); err != nil {
+		t.Fatal(err)
+	}
+}
+
+func TestTaskAddTarget(t *testing.T) {
+	//	task := taskCreate(t, DeviceInfo)
+}
+
+/// Utils
+func taskCreate(t *testing.T, taskType TaskType) *Task {
+	task := TaskCreate(taskType)
+	if task == nil {
+		t.Fatalf("Error creating task")
+	}
+	return task
+}
+
+/// Failure function replacement
+func dmTaskCreateFail(t int) *CDmTask {
+	return nil
+}
+
+func dmTaskRunFail(task *CDmTask) int {
+	return -1
+}
+
+func dmTaskSetNameFail(task *CDmTask, name string) int {
+	return -1
+}
+
+func dmTaskSetMessageFail(task *CDmTask, message string) int {
+	return -1
+}
+
+func dmTaskSetSectorFail(task *CDmTask, sector uint64) int {
+	return -1
+}
+
+func dmTaskSetCookieFail(task *CDmTask, cookie *uint, flags uint16) int {
+	return -1
+}
+
+func dmTaskSetAddNodeFail(task *CDmTask, addNode AddNodeType) int {
+	return -1
+}
+
+func dmTaskSetRoFail(task *CDmTask) int {
+	return -1
+}
+
+func dmTaskAddTargetFail(task *CDmTask,
+	start, size uint64, ttype, params string) int {
+	return -1
+}
+
+func dmTaskGetDriverVersionFail(task *CDmTask, version *string) int {
+	return -1
+}
+
+func dmTaskGetInfoFail(task *CDmTask, info *Info) int {
+	return -1
+}
+
+func dmGetNextTargetFail(task *CDmTask, next uintptr, start, length *uint64,
+	target, params *string) uintptr {
+	return 0
+}
+
+func dmAttachLoopDeviceFail(filename string, fd *int) string {
+	return ""
+}
+
+func sysGetBlockSizeFail(fd uintptr, size *uint64) syscall.Errno {
+	return 1
+}
+
+func dmGetBlockSizeFail(fd uintptr) int64 {
+	return -1
+}
+
+func dmUdevWaitFail(cookie uint) int {
+	return -1
+}
+
+func dmSetDevDirFail(dir string) int {
+	return -1
+}
+
+func dmGetLibraryVersionFail(version *string) int {
+	return -1
+}

+ 349 - 0
devmapper/devmapper_wrapper.go

@@ -0,0 +1,349 @@
+package devmapper
+
+/*
+#cgo LDFLAGS: -L. -ldevmapper
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <libdevmapper.h>
+#include <linux/loop.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#include <errno.h>
+
+#ifndef LOOP_CTL_GET_FREE
+#define LOOP_CTL_GET_FREE       0x4C82
+#endif
+
+// FIXME: this could easily be rewritten in go
+char*			attach_loop_device(const char *filename, int *loop_fd_out)
+{
+  struct loop_info64	loopinfo = {0};
+  struct stat		st;
+  char			buf[64];
+  int			i, loop_fd, fd, start_index;
+  char*			loopname;
+
+
+  *loop_fd_out = -1;
+
+  start_index = 0;
+  fd = open("/dev/loop-control", O_RDONLY);
+  if (fd >= 0) {
+    start_index = ioctl(fd, LOOP_CTL_GET_FREE);
+    close(fd);
+
+    if (start_index < 0)
+      start_index = 0;
+  }
+
+  fd = open(filename, O_RDWR);
+  if (fd < 0) {
+    perror("open");
+    return NULL;
+  }
+
+  loop_fd = -1;
+  for (i = start_index ; loop_fd < 0 ; i++ ) {
+    if (sprintf(buf, "/dev/loop%d", i) < 0) {
+	close(fd);
+	return NULL;
+    }
+
+    if (stat(buf, &st)) {
+      if (!S_ISBLK(st.st_mode)) {
+	 fprintf(stderr, "[error] Loopback device %s is not a block device.\n", buf);
+      } else if (errno == ENOENT) {
+	fprintf(stderr, "[error] There are no more loopback device available.\n");
+      } else {
+	fprintf(stderr, "[error] Unkown error trying to stat the loopback device %s (errno: %d).\n", buf, errno);
+      }
+      close(fd);
+      return NULL;
+    }
+
+    loop_fd = open(buf, O_RDWR);
+    if (loop_fd < 0 && errno == ENOENT) {
+      fprintf(stderr, "[error] The loopback device %s does not exists.\n", buf);
+      close(fd);
+      return NULL;
+    } else if (loop_fd < 0) {
+	fprintf(stderr, "[error] Unkown error openning the loopback device %s. (errno: %d)\n", buf, errno);
+	continue;
+    }
+
+    if (ioctl(loop_fd, LOOP_SET_FD, (void *)(size_t)fd) < 0) {
+      int errsv = errno;
+      close(loop_fd);
+      loop_fd = -1;
+      if (errsv != EBUSY) {
+        close(fd);
+        fprintf(stderr, "cannot set up loopback device %s: %s", buf, strerror(errsv));
+        return NULL;
+      }
+      continue;
+    }
+
+    close(fd);
+
+    strncpy((char*)loopinfo.lo_file_name, buf, LO_NAME_SIZE);
+    loopinfo.lo_offset = 0;
+    loopinfo.lo_flags = LO_FLAGS_AUTOCLEAR;
+
+    if (ioctl(loop_fd, LOOP_SET_STATUS64, &loopinfo) < 0) {
+      perror("ioctl LOOP_SET_STATUS64");
+      if (ioctl(loop_fd, LOOP_CLR_FD, 0) < 0) {
+        perror("ioctl LOOP_CLR_FD");
+      }
+      close(loop_fd);
+      fprintf (stderr, "cannot set up loopback device info");
+      return (NULL);
+    }
+
+    loopname = strdup(buf);
+    if (loopname == NULL) {
+      close(loop_fd);
+      return (NULL);
+    }
+
+    *loop_fd_out = loop_fd;
+    return (loopname);
+  }
+
+  return (NULL);
+}
+
+static int64_t	get_block_size(int fd)
+{
+  uint64_t	size;
+
+  if (ioctl(fd, BLKGETSIZE64, &size) == -1)
+    return -1;
+  return ((int64_t)size);
+}
+
+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[256];
+  va_list ap;
+
+  va_start(ap, f);
+  vsnprintf(buffer, 256, f, ap);
+  va_end(ap);
+
+  DevmapperLogCallback(level, (char *)file, line, dm_errno_or_class, buffer);
+}
+
+static void	log_with_errno_init()
+{
+  dm_log_with_errno_init(log_cb);
+}
+
+*/
+import "C"
+
+import (
+	"syscall"
+	"unsafe"
+)
+
+type (
+	CDmTask C.struct_dm_task
+)
+
+var (
+	DmTaskDestory          = dmTaskDestroyFct
+	DmTaskCreate           = dmTaskCreateFct
+	DmTaskRun              = dmTaskRunFct
+	DmTaskSetName          = dmTaskSetNameFct
+	DmTaskSetMessage       = dmTaskSetMessageFct
+	DmTaskSetSector        = dmTaskSetSectorFct
+	DmTaskSetCookie        = dmTaskSetCookieFct
+	DmTaskSetAddNode       = dmTaskSetAddNodeFct
+	DmTaskSetRo            = dmTaskSetRoFct
+	DmTaskAddTarget        = dmTaskAddTargetFct
+	DmTaskGetDriverVersion = dmTaskGetDriverVersionFct
+	DmTaskGetInfo          = dmTaskGetInfoFct
+	DmGetNextTarget        = dmGetNextTargetFct
+	DmAttachLoopDevice     = dmAttachLoopDeviceFct
+	SysGetBlockSize        = sysGetBlockSizeFct
+	DmGetBlockSize         = dmGetBlockSizeFct
+	DmUdevWait             = dmUdevWaitFct
+	DmLogInitVerbose       = dmLogInitVerboseFct
+	LogWithErrnoInit       = logWithErrnoInitFct
+	DmSetDevDir            = dmSetDevDirFct
+	DmGetLibraryVersion    = dmGetLibraryVersionFct
+)
+
+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 {
+	return int(C.dm_task_run((*C.struct_dm_task)(task)))
+}
+
+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 dmTaskSetRoFct(task *CDmTask) int {
+	return int(C.dm_task_set_ro((*C.struct_dm_task)(task)))
+}
+
+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 dmTaskGetDriverVersionFct(task *CDmTask, version *string) int {
+	buffer := C.CString(string(make([]byte, 128)))
+	defer free(buffer)
+	defer func() {
+		*version = C.GoString(buffer)
+	}()
+	return int(C.dm_task_get_driver_version((*C.struct_dm_task)(task),
+		buffer, 128))
+}
+
+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 dmGetNextTargetFct(task *CDmTask, next uintptr, start, length *uint64,
+	target, params *string) uintptr {
+
+	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)
+	}()
+	nextp := C.dm_get_next_target((*C.struct_dm_task)(task),
+		unsafe.Pointer(next), &Cstart, &Clength, &CtargetType, &Cparams)
+	return uintptr(nextp)
+}
+
+func dmAttachLoopDeviceFct(filename string, fd *int) string {
+	cFilename := C.CString(filename)
+	defer free(cFilename)
+
+	var cFd C.int
+	defer func() {
+		*fd = int(cFd)
+	}()
+
+	ret := C.attach_loop_device(cFilename, &cFd)
+	defer free(ret)
+	return C.GoString(ret)
+}
+
+// sysGetBlockSizeFct retrieves the block size from IOCTL
+func sysGetBlockSizeFct(fd uintptr, size *uint64) syscall.Errno {
+	_, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, C.BLKGETSIZE64,
+		uintptr(unsafe.Pointer(&size)))
+	return err
+}
+
+// dmGetBlockSizeFct retrieves the block size from library call
+func dmGetBlockSizeFct(fd uintptr) int64 {
+	return int64(C.get_block_size(C.int(fd)))
+}
+
+func dmUdevWaitFct(cookie uint) int {
+	return int(C.dm_udev_wait(C.uint32_t(cookie)))
+}
+
+func dmLogInitVerboseFct(level int) {
+	C.dm_log_init_verbose(C.int(level))
+}
+
+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))
+}