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, ¶ms),
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
} |