pkg/devicemapper/devmapper.go
85357a11
 // +build linux,cgo
24c03b2d
 
e2f8fbfb
 package devicemapper
739af0a1
 
8b2f4aab
 import (
f29c500d
 	"errors"
8b2f4aab
 	"fmt"
39d244a5
 	"os"
8b2f4aab
 	"runtime"
6b764bba
 	"unsafe"
39d244a5
 
1009e6a4
 	"github.com/sirupsen/logrus"
069fdc8a
 	"golang.org/x/sys/unix"
8b2f4aab
 )
739af0a1
 
adce3ca4
 // Same as DM_DEVICE_* enum values from libdevmapper.h
62c1f0ef
 // nolint: deadcode
739af0a1
 const (
6990b76a
 	deviceCreate TaskType = iota
 	deviceReload
 	deviceRemove
 	deviceRemoveAll
 	deviceSuspend
 	deviceResume
 	deviceInfo
 	deviceDeps
 	deviceRename
 	deviceVersion
 	deviceStatus
 	deviceTable
 	deviceWaitevent
 	deviceList
 	deviceClear
 	deviceMknodes
 	deviceListVersions
 	deviceTargetMsg
 	deviceSetGeometry
739af0a1
 )
 
5ebaca7e
 const (
6990b76a
 	addNodeOnResume AddNodeType = iota
 	addNodeOnCreate
5ebaca7e
 )
 
6990b76a
 // List of errors returned when using devicemapper.
f29c500d
 var (
af597527
 	ErrTaskRun              = errors.New("dm_task_run failed")
 	ErrTaskSetName          = errors.New("dm_task_set_name failed")
 	ErrTaskSetMessage       = errors.New("dm_task_set_message failed")
 	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")
 	ErrTaskGetDeps          = errors.New("dm_task_get_deps failed")
 	ErrTaskGetInfo          = errors.New("dm_task_get_info failed")
 	ErrTaskGetDriverVersion = errors.New("dm_task_get_driver_version failed")
 	ErrTaskDeferredRemove   = errors.New("dm_task_deferred_remove failed")
 	ErrTaskSetCookie        = errors.New("dm_task_set_cookie failed")
 	ErrNilCookie            = errors.New("cookie ptr can't be nil")
 	ErrGetBlockSize         = errors.New("Can't get block size")
 	ErrUdevWait             = errors.New("wait on udev cookie failed")
 	ErrSetDevDir            = errors.New("dm_set_dev_dir failed")
 	ErrGetLibraryVersion    = errors.New("dm_get_library_version failed")
 	ErrCreateRemoveTask     = errors.New("Can't create task of type deviceRemove")
 	ErrRunRemoveDevice      = errors.New("running RemoveDevice failed")
 	ErrInvalidAddNode       = errors.New("Invalid AddNode type")
 	ErrBusy                 = errors.New("Device is Busy")
 	ErrDeviceIDExists       = errors.New("Device Id Exists")
 	ErrEnxio                = errors.New("No such device or address")
8451d03d
 	ErrEnoData              = errors.New("No data available")
6990b76a
 )
586a511c
 
6990b76a
 var (
8451d03d
 	dmSawBusy    bool
 	dmSawExist   bool
 	dmSawEnxio   bool // No Such Device or Address
 	dmSawEnoData bool // No data available
f29c500d
 )
739af0a1
 
f29c500d
 type (
6990b76a
 	// Task represents a devicemapper task (like lvcreate, etc.) ; a task is needed for each ioctl
 	// command to execute.
f29c500d
 	Task struct {
6990b76a
 		unmanaged *cdmTask
f29c500d
 	}
6990b76a
 	// Deps represents dependents (layer) of a device.
8e7aa44f
 	Deps struct {
 		Count  uint32
 		Filler uint32
 		Device []uint64
 	}
6990b76a
 	// Info represents information about a device.
f29c500d
 	Info struct {
4986ce7c
 		Exists         int
 		Suspended      int
 		LiveTable      int
 		InactiveTable  int
 		OpenCount      int32
 		EventNr        uint32
 		Major          uint32
 		Minor          uint32
 		ReadOnly       int
 		TargetCount    int32
 		DeferredRemove int
f29c500d
 	}
6990b76a
 	// TaskType represents a type of task
 	TaskType int
1dcb7d9e
 	// AddNodeType represents a type of node to be added
5ebaca7e
 	AddNodeType int
f29c500d
 )
739af0a1
 
6990b76a
 // DeviceIDExists returns whether error conveys the information about device Id already
7b2b15d3
 // exist or not. This will be true if device creation or snap creation
 // operation fails if device or snap device already exists in pool.
 // Current implementation is little crude as it scans the error string
 // for exact pattern match. Replacing it with more robust implementation
 // is desirable.
6990b76a
 func DeviceIDExists(err error) bool {
 	return fmt.Sprint(err) == fmt.Sprint(ErrDeviceIDExists)
7b2b15d3
 }
 
739af0a1
 func (t *Task) destroy() {
 	if t != nil {
023ff367
 		DmTaskDestroy(t.unmanaged)
739af0a1
 		runtime.SetFinalizer(t, nil)
 	}
 }
 
acdf7660
 // TaskCreateNamed is a convenience function for TaskCreate when a name
 // will be set on the task as well
 func TaskCreateNamed(t TaskType, name string) (*Task, error) {
 	task := TaskCreate(t)
 	if task == nil {
9b584781
 		return nil, fmt.Errorf("devicemapper: Can't create task of type %d", int(t))
acdf7660
 	}
6990b76a
 	if err := task.setName(name); err != nil {
9b584781
 		return nil, fmt.Errorf("devicemapper: Can't set task name %s", name)
acdf7660
 	}
 	return task, nil
 }
 
 // TaskCreate initializes a devicemapper task of tasktype
739af0a1
 func TaskCreate(tasktype TaskType) *Task {
1d188c87
 	Ctask := DmTaskCreate(int(tasktype))
 	if Ctask == nil {
739af0a1
 		return nil
 	}
1d188c87
 	task := &Task{unmanaged: Ctask}
739af0a1
 	runtime.SetFinalizer(task, (*Task).destroy)
 	return task
 }
 
6990b76a
 func (t *Task) run() error {
1d188c87
 	if res := DmTaskRun(t.unmanaged); res != 1 {
f29c500d
 		return ErrTaskRun
739af0a1
 	}
d764d8b1
 	runtime.KeepAlive(t)
739af0a1
 	return nil
 }
 
6990b76a
 func (t *Task) setName(name string) error {
1d188c87
 	if res := DmTaskSetName(t.unmanaged, name); res != 1 {
f29c500d
 		return ErrTaskSetName
739af0a1
 	}
 	return nil
 }
 
6990b76a
 func (t *Task) setMessage(message string) error {
1d188c87
 	if res := DmTaskSetMessage(t.unmanaged, message); res != 1 {
f29c500d
 		return ErrTaskSetMessage
739af0a1
 	}
 	return nil
 }
 
6990b76a
 func (t *Task) setSector(sector uint64) error {
1d188c87
 	if res := DmTaskSetSector(t.unmanaged, sector); res != 1 {
4bebca84
 		return ErrTaskSetSector
739af0a1
 	}
 	return nil
 }
 
6990b76a
 func (t *Task) setCookie(cookie *uint, flags uint16) error {
4bebca84
 	if cookie == nil {
 		return ErrNilCookie
 	}
1d188c87
 	if res := DmTaskSetCookie(t.unmanaged, cookie, flags); res != 1 {
4bebca84
 		return ErrTaskSetCookie
739af0a1
 	}
 	return nil
 }
 
6990b76a
 func (t *Task) setAddNode(addNode AddNodeType) error {
 	if addNode != addNodeOnResume && addNode != addNodeOnCreate {
05d70cbc
 		return ErrInvalidAddNode
 	}
1d188c87
 	if res := DmTaskSetAddNode(t.unmanaged, addNode); res != 1 {
5ebaca7e
 		return ErrTaskSetAddNode
 	}
 	return nil
 }
 
6990b76a
 func (t *Task) setRo() error {
1d188c87
 	if res := DmTaskSetRo(t.unmanaged); res != 1 {
05d70cbc
 		return ErrTaskSetRo
739af0a1
 	}
 	return nil
 }
 
6990b76a
 func (t *Task) addTarget(start, size uint64, ttype, params string) error {
1d188c87
 	if res := DmTaskAddTarget(t.unmanaged, start, size,
 		ttype, params); res != 1 {
f29c500d
 		return ErrTaskAddTarget
739af0a1
 	}
 	return nil
 }
 
6990b76a
 func (t *Task) getDeps() (*Deps, error) {
8e7aa44f
 	var deps *Deps
 	if deps = DmTaskGetDeps(t.unmanaged); deps == nil {
 		return nil, ErrTaskGetDeps
 	}
 	return deps, nil
 }
 
6990b76a
 func (t *Task) getInfo() (*Info, error) {
1d188c87
 	info := &Info{}
 	if res := DmTaskGetInfo(t.unmanaged, info); res != 1 {
05d70cbc
 		return nil, ErrTaskGetInfo
1d188c87
 	}
 	return info, nil
 }
 
6990b76a
 func (t *Task) getInfoWithDeferred() (*Info, error) {
4986ce7c
 	info := &Info{}
 	if res := DmTaskGetInfoWithDeferred(t.unmanaged, info); res != 1 {
 		return nil, ErrTaskGetInfo
 	}
 	return info, nil
 }
 
6990b76a
 func (t *Task) getDriverVersion() (string, error) {
948e54ac
 	res := DmTaskGetDriverVersion(t.unmanaged)
 	if res == "" {
 		return "", ErrTaskGetDriverVersion
 	}
 	return res, nil
 }
 
6990b76a
 func (t *Task) getNextTarget(next unsafe.Pointer) (nextPtr unsafe.Pointer, start uint64,
1d188c87
 	length uint64, targetType string, params string) {
 
 	return DmGetNextTarget(t.unmanaged, next, &start, &length,
 			&targetType, &params),
 		start, length, targetType, params
739af0a1
 }
 
1dcb7d9e
 // UdevWait waits for any processes that are waiting for udev to complete the specified cookie.
665656af
 func UdevWait(cookie *uint) error {
 	if res := DmUdevWait(*cookie); res != 1 {
547510fb
 		logrus.Debugf("devicemapper: Failed to wait on udev cookie %d, %d", *cookie, res)
f29c500d
 		return ErrUdevWait
739af0a1
 	}
 	return nil
 }
 
6990b76a
 // SetDevDir sets the dev folder for the device mapper library (usually /dev).
f29c500d
 func SetDevDir(dir string) error {
1d188c87
 	if res := DmSetDevDir(dir); res != 1 {
a72b45db
 		logrus.Debug("devicemapper: Error dm_set_dev_dir")
f29c500d
 		return ErrSetDevDir
 	}
 	return nil
 }
 
6990b76a
 // GetLibraryVersion returns the device mapper library version.
f29c500d
 func GetLibraryVersion() (string, error) {
1d188c87
 	var version string
 	if res := DmGetLibraryVersion(&version); res != 1 {
f29c500d
 		return "", ErrGetLibraryVersion
 	}
1d188c87
 	return version, nil
f29c500d
 }
 
9c338003
 // UdevSyncSupported returns whether device-mapper is able to sync with udev
 //
 // This is essential otherwise race conditions can arise where both udev and
 // device-mapper attempt to create and destroy devices.
 func UdevSyncSupported() bool {
 	return DmUdevGetSyncSupport() != 0
 }
 
 // UdevSetSyncSupport allows setting whether the udev sync should be enabled.
 // The return bool indicates the state of whether the sync is enabled.
 func UdevSetSyncSupport(enable bool) bool {
 	if enable {
 		DmUdevSetSyncSupport(1)
 	} else {
 		DmUdevSetSyncSupport(0)
 	}
 
 	return UdevSyncSupported()
 }
 
cb81ed34
 // CookieSupported returns whether the version of device-mapper supports the
 // use of cookie's in the tasks.
 // This is largely a lower level call that other functions use.
 func CookieSupported() bool {
 	return DmCookieSupported() != 0
 }
 
6990b76a
 // RemoveDevice is a useful helper for cleaning up a device.
03320f0d
 func RemoveDevice(name string) error {
6990b76a
 	task, err := TaskCreateNamed(deviceRemove, name)
03320f0d
 	if task == nil {
f29c500d
 		return err
03320f0d
 	}
c9a76622
 
edd1c9e3
 	cookie := new(uint)
 	if err := task.setCookie(cookie, 0); err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Can not set cookie: %s", err)
03320f0d
 	}
23dcfec1
 	defer UdevWait(cookie)
c9a76622
 
cef27e1d
 	dmSawBusy = false // reset before the task is run
36cb6efe
 	dmSawEnxio = false
6990b76a
 	if err = task.run(); err != nil {
c9a76622
 		if dmSawBusy {
 			return ErrBusy
 		}
36cb6efe
 		if dmSawEnxio {
 			return ErrEnxio
 		}
9b584781
 		return fmt.Errorf("devicemapper: Error running RemoveDevice %s", err)
c9a76622
 	}
 
23dcfec1
 	return nil
03320f0d
 }
f29c500d
 
6990b76a
 // RemoveDeviceDeferred is a useful helper for cleaning up a device, but deferred.
6964ab94
 func RemoveDeviceDeferred(name string) error {
9b584781
 	logrus.Debugf("devicemapper: RemoveDeviceDeferred START(%s)", name)
 	defer logrus.Debugf("devicemapper: RemoveDeviceDeferred END(%s)", name)
6990b76a
 	task, err := TaskCreateNamed(deviceRemove, name)
6964ab94
 	if task == nil {
 		return err
 	}
 
 	if err := DmTaskDeferredRemove(task.unmanaged); err != 1 {
 		return ErrTaskDeferredRemove
 	}
 
5e505d10
 	// set a task cookie and disable library fallback, or else libdevmapper will
 	// disable udev dm rules and delete the symlink under /dev/mapper by itself,
 	// even if the removal is deferred by the kernel.
edd1c9e3
 	cookie := new(uint)
f7f101d5
 	flags := uint16(DmUdevDisableLibraryFallback)
edd1c9e3
 	if err := task.setCookie(cookie, flags); err != nil {
5e505d10
 		return fmt.Errorf("devicemapper: Can not set cookie: %s", err)
 	}
 
 	// libdevmapper and udev relies on System V semaphore for synchronization,
 	// semaphores created in `task.setCookie` will be cleaned up in `UdevWait`.
 	// So these two function call must come in pairs, otherwise semaphores will
 	// be leaked, and the  limit of number of semaphores defined in `/proc/sys/kernel/sem`
39bcaee4
 	// will be reached, which will eventually make all following calls to 'task.SetCookie'
5e505d10
 	// fail.
 	// this call will not wait for the deferred removal's final executing, since no
 	// udev event will be generated, and the semaphore's value will not be incremented
 	// by udev, what UdevWait is just cleaning up the semaphore.
23dcfec1
 	defer UdevWait(cookie)
 
36cb6efe
 	dmSawEnxio = false
23dcfec1
 	if err = task.run(); err != nil {
36cb6efe
 		if dmSawEnxio {
 			return ErrEnxio
 		}
23dcfec1
 		return fmt.Errorf("devicemapper: Error running RemoveDeviceDeferred %s", err)
 	}
6964ab94
 
23dcfec1
 	return nil
6964ab94
 }
 
6990b76a
 // CancelDeferredRemove cancels a deferred remove for a device.
20b38f42
 func CancelDeferredRemove(deviceName string) error {
6990b76a
 	task, err := TaskCreateNamed(deviceTargetMsg, deviceName)
20b38f42
 	if task == nil {
 		return err
 	}
 
6990b76a
 	if err := task.setSector(0); err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Can't set sector %s", err)
20b38f42
 	}
 
6990b76a
 	if err := task.setMessage(fmt.Sprintf("@cancel_deferred_remove")); err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Can't set message %s", err)
20b38f42
 	}
 
 	dmSawBusy = false
 	dmSawEnxio = false
6990b76a
 	if err := task.run(); err != nil {
20b38f42
 		// A device might be being deleted already
 		if dmSawBusy {
 			return ErrBusy
 		} else if dmSawEnxio {
 			return ErrEnxio
 		}
9b584781
 		return fmt.Errorf("devicemapper: Error running CancelDeferredRemove %s", err)
20b38f42
 
 	}
 	return nil
 }
 
6990b76a
 // GetBlockDeviceSize returns the size of a block device identified by the specified file.
39d244a5
 func GetBlockDeviceSize(file *os.File) (uint64, error) {
1214b889
 	size, err := ioctlBlkGetSize64(file.Fd())
 	if err != nil {
9b584781
 		logrus.Errorf("devicemapper: Error getblockdevicesize: %s", err)
05d70cbc
 		return 0, ErrGetBlockSize
 	}
 	return uint64(size), nil
 }
 
6990b76a
 // BlockDeviceDiscard runs discard for the given path.
 // This is used as a workaround for the kernel not discarding block so
 // on the thin pool when we remove a thinp device, so we do it
 // manually
93e120e7
 func BlockDeviceDiscard(path string) error {
39d244a5
 	file, err := os.OpenFile(path, os.O_RDWR, 0)
93e120e7
 	if err != nil {
 		return err
 	}
 	defer file.Close()
 
 	size, err := GetBlockDeviceSize(file)
 	if err != nil {
 		return err
 	}
 
 	if err := ioctlBlkDiscard(file.Fd(), 0, size); err != nil {
 		return err
 	}
 
 	// Without this sometimes the remove of the device that happens after
 	// discard fails with EBUSY.
069fdc8a
 	unix.Sync()
93e120e7
 
 	return nil
 }
 
6990b76a
 // CreatePool is the programmatic example of "dmsetup create".
 // It creates a device with the specified poolName, data and metadata file and block size.
e2f8fbfb
 func CreatePool(poolName string, dataFile, metadataFile *os.File, poolBlockSize uint32) error {
6990b76a
 	task, err := TaskCreateNamed(deviceCreate, poolName)
c77697a4
 	if task == nil {
 		return err
 	}
 
 	size, err := GetBlockDeviceSize(dataFile)
 	if err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Can't get data size %s", err)
c77697a4
 	}
 
09ee269d
 	params := fmt.Sprintf("%s %s %d 32768 1 skip_block_zeroing", metadataFile.Name(), dataFile.Name(), poolBlockSize)
6990b76a
 	if err := task.addTarget(0, size/512, "thin-pool", params); err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Can't add target %s", err)
c77697a4
 	}
 
edd1c9e3
 	cookie := new(uint)
f7f101d5
 	flags := uint16(DmUdevDisableSubsystemRulesFlag | DmUdevDisableDiskRulesFlag | DmUdevDisableOtherRulesFlag)
edd1c9e3
 	if err := task.setCookie(cookie, flags); err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Can't set cookie %s", err)
c77697a4
 	}
23dcfec1
 	defer UdevWait(cookie)
c77697a4
 
6990b76a
 	if err := task.run(); err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Error running deviceCreate (CreatePool) %s", err)
c77697a4
 	}
 
23dcfec1
 	return nil
c77697a4
 }
 
6990b76a
 // ReloadPool is the programmatic example of "dmsetup reload".
 // It reloads the table with the specified poolName, data and metadata file and block size.
e2f8fbfb
 func ReloadPool(poolName string, dataFile, metadataFile *os.File, poolBlockSize uint32) error {
6990b76a
 	task, err := TaskCreateNamed(deviceReload, poolName)
a0224e61
 	if task == nil {
 		return err
 	}
 
 	size, err := GetBlockDeviceSize(dataFile)
 	if err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Can't get data size %s", err)
a0224e61
 	}
 
09ee269d
 	params := fmt.Sprintf("%s %s %d 32768 1 skip_block_zeroing", metadataFile.Name(), dataFile.Name(), poolBlockSize)
6990b76a
 	if err := task.addTarget(0, size/512, "thin-pool", params); err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Can't add target %s", err)
a0224e61
 	}
 
6990b76a
 	if err := task.run(); err != nil {
f93b41e9
 		return fmt.Errorf("devicemapper: Error running ReloadPool %s", err)
a0224e61
 	}
 
 	return nil
 }
 
6990b76a
 // GetDeps is the programmatic example of "dmsetup deps".
 // It outputs a list of devices referenced by the live table for the specified device.
e2f8fbfb
 func GetDeps(name string) (*Deps, error) {
6990b76a
 	task, err := TaskCreateNamed(deviceDeps, name)
8e7aa44f
 	if task == nil {
 		return nil, err
 	}
6990b76a
 	if err := task.run(); err != nil {
8e7aa44f
 		return nil, err
 	}
6990b76a
 	return task.getDeps()
8e7aa44f
 }
 
6990b76a
 // GetInfo is the programmatic example of "dmsetup info".
 // It outputs some brief information about the device.
e2f8fbfb
 func GetInfo(name string) (*Info, error) {
6990b76a
 	task, err := TaskCreateNamed(deviceInfo, name)
c77697a4
 	if task == nil {
 		return nil, err
 	}
6990b76a
 	if err := task.run(); err != nil {
c77697a4
 		return nil, err
 	}
6990b76a
 	return task.getInfo()
c77697a4
 }
 
6990b76a
 // GetInfoWithDeferred is the programmatic example of "dmsetup info", but deferred.
 // It outputs some brief information about the device.
4986ce7c
 func GetInfoWithDeferred(name string) (*Info, error) {
6990b76a
 	task, err := TaskCreateNamed(deviceInfo, name)
4986ce7c
 	if task == nil {
 		return nil, err
 	}
6990b76a
 	if err := task.run(); err != nil {
4986ce7c
 		return nil, err
 	}
6990b76a
 	return task.getInfoWithDeferred()
4986ce7c
 }
 
6990b76a
 // GetDriverVersion is the programmatic example of "dmsetup version".
 // It outputs version information of the driver.
e2f8fbfb
 func GetDriverVersion() (string, error) {
6990b76a
 	task := TaskCreate(deviceVersion)
948e54ac
 	if task == nil {
9b584781
 		return "", fmt.Errorf("devicemapper: Can't create deviceVersion task")
948e54ac
 	}
6990b76a
 	if err := task.run(); err != nil {
948e54ac
 		return "", err
 	}
6990b76a
 	return task.getDriverVersion()
948e54ac
 }
 
6990b76a
 // GetStatus is the programmatic example of "dmsetup status".
 // It outputs status information for the specified device name.
e2f8fbfb
 func GetStatus(name string) (uint64, uint64, string, string, error) {
6990b76a
 	task, err := TaskCreateNamed(deviceStatus, name)
c77697a4
 	if task == nil {
9b584781
 		logrus.Debugf("devicemapper: GetStatus() Error TaskCreateNamed: %s", err)
c77697a4
 		return 0, 0, "", "", err
 	}
6990b76a
 	if err := task.run(); err != nil {
9b584781
 		logrus.Debugf("devicemapper: GetStatus() Error Run: %s", err)
c77697a4
 		return 0, 0, "", "", err
 	}
 
6990b76a
 	devinfo, err := task.getInfo()
c77697a4
 	if err != nil {
9b584781
 		logrus.Debugf("devicemapper: GetStatus() Error GetInfo: %s", err)
c77697a4
 		return 0, 0, "", "", err
 	}
 	if devinfo.Exists == 0 {
9b584781
 		logrus.Debugf("devicemapper: GetStatus() Non existing device %s", name)
 		return 0, 0, "", "", fmt.Errorf("devicemapper: Non existing device %s", name)
c77697a4
 	}
 
6990b76a
 	_, start, length, targetType, params := task.getNextTarget(unsafe.Pointer(nil))
56901397
 	return start, length, targetType, params, nil
c77697a4
 }
 
6990b76a
 // GetTable is the programmatic example for "dmsetup table".
 // It outputs the current table for the specified device name.
bebf5344
 func GetTable(name string) (uint64, uint64, string, string, error) {
6990b76a
 	task, err := TaskCreateNamed(deviceTable, name)
bebf5344
 	if task == nil {
9b584781
 		logrus.Debugf("devicemapper: GetTable() Error TaskCreateNamed: %s", err)
bebf5344
 		return 0, 0, "", "", err
 	}
6990b76a
 	if err := task.run(); err != nil {
9b584781
 		logrus.Debugf("devicemapper: GetTable() Error Run: %s", err)
bebf5344
 		return 0, 0, "", "", err
 	}
 
6990b76a
 	devinfo, err := task.getInfo()
bebf5344
 	if err != nil {
9b584781
 		logrus.Debugf("devicemapper: GetTable() Error GetInfo: %s", err)
bebf5344
 		return 0, 0, "", "", err
 	}
 	if devinfo.Exists == 0 {
9b584781
 		logrus.Debugf("devicemapper: GetTable() Non existing device %s", name)
 		return 0, 0, "", "", fmt.Errorf("devicemapper: Non existing device %s", name)
bebf5344
 	}
 
6990b76a
 	_, start, length, targetType, params := task.getNextTarget(unsafe.Pointer(nil))
bebf5344
 	return start, length, targetType, params, nil
 }
 
6990b76a
 // SetTransactionID sets a transaction id for the specified device name.
 func SetTransactionID(poolName string, oldID uint64, newID uint64) error {
 	task, err := TaskCreateNamed(deviceTargetMsg, poolName)
c77697a4
 	if task == nil {
 		return err
 	}
 
6990b76a
 	if err := task.setSector(0); err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Can't set sector %s", err)
c77697a4
 	}
 
6990b76a
 	if err := task.setMessage(fmt.Sprintf("set_transaction_id %d %d", oldID, newID)); err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Can't set message %s", err)
c77697a4
 	}
 
6990b76a
 	if err := task.run(); err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Error running SetTransactionID %s", err)
c77697a4
 	}
 	return nil
 }
 
6990b76a
 // SuspendDevice is the programmatic example of "dmsetup suspend".
 // It suspends the specified device.
e2f8fbfb
 func SuspendDevice(name string) error {
6990b76a
 	task, err := TaskCreateNamed(deviceSuspend, name)
c77697a4
 	if task == nil {
 		return err
 	}
6990b76a
 	if err := task.run(); err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Error running deviceSuspend %s", err)
c77697a4
 	}
 	return nil
 }
 
6990b76a
 // ResumeDevice is the programmatic example of "dmsetup resume".
 // It un-suspends the specified device.
e2f8fbfb
 func ResumeDevice(name string) error {
6990b76a
 	task, err := TaskCreateNamed(deviceResume, name)
c77697a4
 	if task == nil {
 		return err
 	}
 
edd1c9e3
 	cookie := new(uint)
 	if err := task.setCookie(cookie, 0); err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Can't set cookie %s", err)
c77697a4
 	}
23dcfec1
 	defer UdevWait(cookie)
c77697a4
 
6990b76a
 	if err := task.run(); err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Error running deviceResume %s", err)
c77697a4
 	}
 
23dcfec1
 	return nil
c77697a4
 }
 
1dcb7d9e
 // CreateDevice creates a device with the specified poolName with the specified device id.
6990b76a
 func CreateDevice(poolName string, deviceID int) error {
9b584781
 	logrus.Debugf("devicemapper: CreateDevice(poolName=%v, deviceID=%v)", poolName, deviceID)
6990b76a
 	task, err := TaskCreateNamed(deviceTargetMsg, poolName)
7b2b15d3
 	if task == nil {
 		return err
 	}
c77697a4
 
6990b76a
 	if err := task.setSector(0); err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Can't set sector %s", err)
7b2b15d3
 	}
c77697a4
 
6990b76a
 	if err := task.setMessage(fmt.Sprintf("create_thin %d", deviceID)); err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Can't set message %s", err)
7b2b15d3
 	}
f26203cf
 
7b2b15d3
 	dmSawExist = false // reset before the task is run
6990b76a
 	if err := task.run(); err != nil {
 		// Caller wants to know about ErrDeviceIDExists so that it can try with a different device id.
7b2b15d3
 		if dmSawExist {
6990b76a
 			return ErrDeviceIDExists
f26203cf
 		}
1b6065de
 
9b584781
 		return fmt.Errorf("devicemapper: Error running CreateDevice %s", err)
1b6065de
 
c77697a4
 	}
 	return nil
 }
 
1dcb7d9e
 // DeleteDevice deletes a device with the specified poolName with the specified device id.
6990b76a
 func DeleteDevice(poolName string, deviceID int) error {
 	task, err := TaskCreateNamed(deviceTargetMsg, poolName)
c77697a4
 	if task == nil {
 		return err
 	}
 
6990b76a
 	if err := task.setSector(0); err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Can't set sector %s", err)
c77697a4
 	}
 
6990b76a
 	if err := task.setMessage(fmt.Sprintf("delete %d", deviceID)); err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Can't set message %s", err)
c77697a4
 	}
 
d929589c
 	dmSawBusy = false
8451d03d
 	dmSawEnoData = false
6990b76a
 	if err := task.run(); err != nil {
d929589c
 		if dmSawBusy {
 			return ErrBusy
 		}
8451d03d
 		if dmSawEnoData {
 			logrus.Debugf("devicemapper: Device(id: %d) from pool(%s) does not exist", deviceID, poolName)
 			return nil
 		}
9b584781
 		return fmt.Errorf("devicemapper: Error running DeleteDevice %s", err)
c77697a4
 	}
 	return nil
 }
 
6990b76a
 // ActivateDevice activates the device identified by the specified
 // poolName, name and deviceID with the specified size.
 func ActivateDevice(poolName string, name string, deviceID int, size uint64) error {
 	return activateDevice(poolName, name, deviceID, size, "")
8861d65e
 }
 
6990b76a
 // ActivateDeviceWithExternal activates the device identified by the specified
1dcb7d9e
 // poolName, name and deviceID with the specified size.
6990b76a
 func ActivateDeviceWithExternal(poolName string, name string, deviceID int, size uint64, external string) error {
 	return activateDevice(poolName, name, deviceID, size, external)
8861d65e
 }
 
6990b76a
 func activateDevice(poolName string, name string, deviceID int, size uint64, external string) error {
 	task, err := TaskCreateNamed(deviceCreate, name)
c77697a4
 	if task == nil {
 		return err
 	}
 
8861d65e
 	var params string
 	if len(external) > 0 {
6990b76a
 		params = fmt.Sprintf("%s %d %s", poolName, deviceID, external)
8861d65e
 	} else {
6990b76a
 		params = fmt.Sprintf("%s %d", poolName, deviceID)
8861d65e
 	}
6990b76a
 	if err := task.addTarget(0, size/512, "thin", params); err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Can't add target %s", err)
c77697a4
 	}
6990b76a
 	if err := task.setAddNode(addNodeOnCreate); err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Can't add node %s", err)
5ebaca7e
 	}
c77697a4
 
edd1c9e3
 	cookie := new(uint)
 	if err := task.setCookie(cookie, 0); err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Can't set cookie %s", err)
c77697a4
 	}
 
23dcfec1
 	defer UdevWait(cookie)
 
6990b76a
 	if err := task.run(); err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Error running deviceCreate (ActivateDevice) %s", err)
c77697a4
 	}
 
23dcfec1
 	return nil
c77697a4
 }
 
0e633ee1
 // CreateSnapDeviceRaw creates a snapshot device. Caller needs to suspend and resume the origin device if it is active.
 func CreateSnapDeviceRaw(poolName string, deviceID int, baseDeviceID int) error {
6990b76a
 	task, err := TaskCreateNamed(deviceTargetMsg, poolName)
7b2b15d3
 	if task == nil {
 		return err
 	}
c77697a4
 
6990b76a
 	if err := task.setSector(0); err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Can't set sector %s", err)
7b2b15d3
 	}
c77697a4
 
6990b76a
 	if err := task.setMessage(fmt.Sprintf("create_snap %d %d", deviceID, baseDeviceID)); err != nil {
9b584781
 		return fmt.Errorf("devicemapper: Can't set message %s", err)
7b2b15d3
 	}
c77697a4
 
7b2b15d3
 	dmSawExist = false // reset before the task is run
6990b76a
 	if err := task.run(); err != nil {
 		// Caller wants to know about ErrDeviceIDExists so that it can try with a different device id.
7b2b15d3
 		if dmSawExist {
6990b76a
 			return ErrDeviceIDExists
c77697a4
 		}
f93b41e9
 		return fmt.Errorf("devicemapper: Error running deviceCreate (CreateSnapDeviceRaw) %s", err)
0e633ee1
 	}
 
 	return nil
 }
 
 // CreateSnapDevice creates a snapshot based on the device identified by the baseName and baseDeviceId,
 func CreateSnapDevice(poolName string, deviceID int, baseName string, baseDeviceID int) error {
 	devinfo, _ := GetInfo(baseName)
 	doSuspend := devinfo != nil && devinfo.Exists != 0
1b6065de
 
0e633ee1
 	if doSuspend {
 		if err := SuspendDevice(baseName); err != nil {
 			return err
 		}
 	}
 
 	if err := CreateSnapDeviceRaw(poolName, deviceID, baseDeviceID); err != nil {
 		if doSuspend {
 			if err2 := ResumeDevice(baseName); err2 != nil {
 				return fmt.Errorf("CreateSnapDeviceRaw Error: (%v): ResumeDevice Error: (%v)", err, err2)
 			}
 		}
 		return err
c77697a4
 	}
 
 	if doSuspend {
e2f8fbfb
 		if err := ResumeDevice(baseName); err != nil {
c77697a4
 			return err
 		}
 	}
 
 	return nil
 }