mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
13ea237234
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
815 lines
23 KiB
Go
815 lines
23 KiB
Go
// +build linux,cgo
|
|
|
|
package devicemapper // import "github.com/docker/docker/pkg/devicemapper"
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"runtime"
|
|
"unsafe"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
// Same as DM_DEVICE_* enum values from libdevmapper.h
|
|
// nolint: deadcode
|
|
const (
|
|
deviceCreate TaskType = iota
|
|
deviceReload
|
|
deviceRemove
|
|
deviceRemoveAll
|
|
deviceSuspend
|
|
deviceResume
|
|
deviceInfo
|
|
deviceDeps
|
|
deviceRename
|
|
deviceVersion
|
|
deviceStatus
|
|
deviceTable
|
|
deviceWaitevent
|
|
deviceList
|
|
deviceClear
|
|
deviceMknodes
|
|
deviceListVersions
|
|
deviceTargetMsg
|
|
deviceSetGeometry
|
|
)
|
|
|
|
const (
|
|
addNodeOnResume AddNodeType = iota
|
|
addNodeOnCreate
|
|
)
|
|
|
|
// List of errors returned when using devicemapper.
|
|
var (
|
|
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")
|
|
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")
|
|
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")
|
|
)
|
|
|
|
var (
|
|
dmSawBusy bool
|
|
dmSawExist bool
|
|
dmSawEnxio bool // No Such Device or Address
|
|
dmSawEnoData bool // No data available
|
|
)
|
|
|
|
type (
|
|
// Task represents a devicemapper task (like lvcreate, etc.) ; a task is needed for each ioctl
|
|
// command to execute.
|
|
Task struct {
|
|
unmanaged *cdmTask
|
|
}
|
|
// Deps represents dependents (layer) of a device.
|
|
Deps struct {
|
|
Count uint32
|
|
Filler uint32
|
|
Device []uint64
|
|
}
|
|
// Info represents information about a device.
|
|
Info struct {
|
|
Exists int
|
|
Suspended int
|
|
LiveTable int
|
|
InactiveTable int
|
|
OpenCount int32
|
|
EventNr uint32
|
|
Major uint32
|
|
Minor uint32
|
|
ReadOnly int
|
|
TargetCount int32
|
|
DeferredRemove int
|
|
}
|
|
// TaskType represents a type of task
|
|
TaskType int
|
|
// AddNodeType represents a type of node to be added
|
|
AddNodeType int
|
|
)
|
|
|
|
// DeviceIDExists returns whether error conveys the information about device Id already
|
|
// 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.
|
|
func DeviceIDExists(err error) bool {
|
|
return fmt.Sprint(err) == fmt.Sprint(ErrDeviceIDExists)
|
|
}
|
|
|
|
func (t *Task) destroy() {
|
|
if t != nil {
|
|
DmTaskDestroy(t.unmanaged)
|
|
runtime.SetFinalizer(t, nil)
|
|
}
|
|
}
|
|
|
|
// 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 {
|
|
return nil, fmt.Errorf("devicemapper: Can't create task of type %d", int(t))
|
|
}
|
|
if err := task.setName(name); err != nil {
|
|
return nil, fmt.Errorf("devicemapper: Can't set task name %s", name)
|
|
}
|
|
return task, nil
|
|
}
|
|
|
|
// TaskCreate initializes a devicemapper task of tasktype
|
|
func TaskCreate(tasktype TaskType) *Task {
|
|
Ctask := DmTaskCreate(int(tasktype))
|
|
if Ctask == nil {
|
|
return nil
|
|
}
|
|
task := &Task{unmanaged: Ctask}
|
|
runtime.SetFinalizer(task, (*Task).destroy)
|
|
return task
|
|
}
|
|
|
|
func (t *Task) run() error {
|
|
if res := DmTaskRun(t.unmanaged); res != 1 {
|
|
return ErrTaskRun
|
|
}
|
|
runtime.KeepAlive(t)
|
|
return nil
|
|
}
|
|
|
|
func (t *Task) setName(name string) error {
|
|
if res := DmTaskSetName(t.unmanaged, name); res != 1 {
|
|
return ErrTaskSetName
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (t *Task) setMessage(message string) error {
|
|
if res := DmTaskSetMessage(t.unmanaged, message); res != 1 {
|
|
return ErrTaskSetMessage
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (t *Task) setSector(sector uint64) error {
|
|
if res := DmTaskSetSector(t.unmanaged, sector); res != 1 {
|
|
return ErrTaskSetSector
|
|
}
|
|
return nil
|
|
}
|
|
|
|
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
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (t *Task) setAddNode(addNode AddNodeType) error {
|
|
if addNode != addNodeOnResume && addNode != addNodeOnCreate {
|
|
return ErrInvalidAddNode
|
|
}
|
|
if res := DmTaskSetAddNode(t.unmanaged, addNode); res != 1 {
|
|
return ErrTaskSetAddNode
|
|
}
|
|
return nil
|
|
}
|
|
|
|
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) getDeps() (*Deps, error) {
|
|
var deps *Deps
|
|
if deps = DmTaskGetDeps(t.unmanaged); deps == nil {
|
|
return nil, ErrTaskGetDeps
|
|
}
|
|
return deps, nil
|
|
}
|
|
|
|
func (t *Task) getInfo() (*Info, error) {
|
|
info := &Info{}
|
|
if res := DmTaskGetInfo(t.unmanaged, info); res != 1 {
|
|
return nil, ErrTaskGetInfo
|
|
}
|
|
return info, nil
|
|
}
|
|
|
|
func (t *Task) getInfoWithDeferred() (*Info, error) {
|
|
info := &Info{}
|
|
if res := DmTaskGetInfoWithDeferred(t.unmanaged, info); res != 1 {
|
|
return nil, ErrTaskGetInfo
|
|
}
|
|
return info, nil
|
|
}
|
|
|
|
func (t *Task) getDriverVersion() (string, error) {
|
|
res := DmTaskGetDriverVersion(t.unmanaged)
|
|
if res == "" {
|
|
return "", ErrTaskGetDriverVersion
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func (t *Task) getNextTarget(next unsafe.Pointer) (nextPtr unsafe.Pointer, start uint64,
|
|
length uint64, targetType string, params string) {
|
|
|
|
return DmGetNextTarget(t.unmanaged, next, &start, &length,
|
|
&targetType, ¶ms),
|
|
start, length, targetType, params
|
|
}
|
|
|
|
// UdevWait waits for any processes that are waiting for udev to complete the specified cookie.
|
|
func UdevWait(cookie *uint) error {
|
|
if res := DmUdevWait(*cookie); res != 1 {
|
|
logrus.Debugf("devicemapper: Failed to wait on udev cookie %d, %d", *cookie, res)
|
|
return ErrUdevWait
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SetDevDir sets the dev folder for the device mapper library (usually /dev).
|
|
func SetDevDir(dir string) error {
|
|
if res := DmSetDevDir(dir); res != 1 {
|
|
logrus.Debug("devicemapper: Error dm_set_dev_dir")
|
|
return ErrSetDevDir
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetLibraryVersion returns the device mapper library version.
|
|
func GetLibraryVersion() (string, error) {
|
|
var version string
|
|
if res := DmGetLibraryVersion(&version); res != 1 {
|
|
return "", ErrGetLibraryVersion
|
|
}
|
|
return version, nil
|
|
}
|
|
|
|
// 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()
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// RemoveDevice is a useful helper for cleaning up a device.
|
|
func RemoveDevice(name string) error {
|
|
task, err := TaskCreateNamed(deviceRemove, name)
|
|
if task == nil {
|
|
return err
|
|
}
|
|
|
|
cookie := new(uint)
|
|
if err := task.setCookie(cookie, 0); err != nil {
|
|
return fmt.Errorf("devicemapper: Can not set cookie: %s", err)
|
|
}
|
|
defer UdevWait(cookie)
|
|
|
|
dmSawBusy = false // reset before the task is run
|
|
dmSawEnxio = false
|
|
if err = task.run(); err != nil {
|
|
if dmSawBusy {
|
|
return ErrBusy
|
|
}
|
|
if dmSawEnxio {
|
|
return ErrEnxio
|
|
}
|
|
return fmt.Errorf("devicemapper: Error running RemoveDevice %s", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// RemoveDeviceDeferred is a useful helper for cleaning up a device, but deferred.
|
|
func RemoveDeviceDeferred(name string) error {
|
|
logrus.Debugf("devicemapper: RemoveDeviceDeferred START(%s)", name)
|
|
defer logrus.Debugf("devicemapper: RemoveDeviceDeferred END(%s)", name)
|
|
task, err := TaskCreateNamed(deviceRemove, name)
|
|
if task == nil {
|
|
return err
|
|
}
|
|
|
|
if err := DmTaskDeferredRemove(task.unmanaged); err != 1 {
|
|
return ErrTaskDeferredRemove
|
|
}
|
|
|
|
// 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.
|
|
cookie := new(uint)
|
|
flags := uint16(DmUdevDisableLibraryFallback)
|
|
if err := task.setCookie(cookie, flags); err != nil {
|
|
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`
|
|
// will be reached, which will eventually make all following calls to 'task.SetCookie'
|
|
// 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.
|
|
defer UdevWait(cookie)
|
|
|
|
dmSawEnxio = false
|
|
if err = task.run(); err != nil {
|
|
if dmSawEnxio {
|
|
return ErrEnxio
|
|
}
|
|
return fmt.Errorf("devicemapper: Error running RemoveDeviceDeferred %s", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// CancelDeferredRemove cancels a deferred remove for a device.
|
|
func CancelDeferredRemove(deviceName string) error {
|
|
task, err := TaskCreateNamed(deviceTargetMsg, deviceName)
|
|
if task == nil {
|
|
return err
|
|
}
|
|
|
|
if err := task.setSector(0); err != nil {
|
|
return fmt.Errorf("devicemapper: Can't set sector %s", err)
|
|
}
|
|
|
|
if err := task.setMessage(fmt.Sprintf("@cancel_deferred_remove")); err != nil {
|
|
return fmt.Errorf("devicemapper: Can't set message %s", err)
|
|
}
|
|
|
|
dmSawBusy = false
|
|
dmSawEnxio = false
|
|
if err := task.run(); err != nil {
|
|
// A device might be being deleted already
|
|
if dmSawBusy {
|
|
return ErrBusy
|
|
} else if dmSawEnxio {
|
|
return ErrEnxio
|
|
}
|
|
return fmt.Errorf("devicemapper: Error running CancelDeferredRemove %s", err)
|
|
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetBlockDeviceSize returns the size of a block device identified by the specified file.
|
|
func GetBlockDeviceSize(file *os.File) (uint64, error) {
|
|
size, err := ioctlBlkGetSize64(file.Fd())
|
|
if err != nil {
|
|
logrus.Errorf("devicemapper: Error getblockdevicesize: %s", err)
|
|
return 0, ErrGetBlockSize
|
|
}
|
|
return uint64(size), nil
|
|
}
|
|
|
|
// 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
|
|
func BlockDeviceDiscard(path string) error {
|
|
file, err := os.OpenFile(path, os.O_RDWR, 0)
|
|
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.
|
|
unix.Sync()
|
|
|
|
return nil
|
|
}
|
|
|
|
// CreatePool is the programmatic example of "dmsetup create".
|
|
// It creates a device with the specified poolName, data and metadata file and block size.
|
|
func CreatePool(poolName string, dataFile, metadataFile *os.File, poolBlockSize uint32) error {
|
|
task, err := TaskCreateNamed(deviceCreate, poolName)
|
|
if task == nil {
|
|
return err
|
|
}
|
|
|
|
size, err := GetBlockDeviceSize(dataFile)
|
|
if err != nil {
|
|
return fmt.Errorf("devicemapper: Can't get data size %s", err)
|
|
}
|
|
|
|
params := fmt.Sprintf("%s %s %d 32768 1 skip_block_zeroing", metadataFile.Name(), dataFile.Name(), poolBlockSize)
|
|
if err := task.addTarget(0, size/512, "thin-pool", params); err != nil {
|
|
return fmt.Errorf("devicemapper: Can't add target %s", err)
|
|
}
|
|
|
|
cookie := new(uint)
|
|
flags := uint16(DmUdevDisableSubsystemRulesFlag | DmUdevDisableDiskRulesFlag | DmUdevDisableOtherRulesFlag)
|
|
if err := task.setCookie(cookie, flags); err != nil {
|
|
return fmt.Errorf("devicemapper: Can't set cookie %s", err)
|
|
}
|
|
defer UdevWait(cookie)
|
|
|
|
if err := task.run(); err != nil {
|
|
return fmt.Errorf("devicemapper: Error running deviceCreate (CreatePool) %s", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ReloadPool is the programmatic example of "dmsetup reload".
|
|
// It reloads the table with the specified poolName, data and metadata file and block size.
|
|
func ReloadPool(poolName string, dataFile, metadataFile *os.File, poolBlockSize uint32) error {
|
|
task, err := TaskCreateNamed(deviceReload, poolName)
|
|
if task == nil {
|
|
return err
|
|
}
|
|
|
|
size, err := GetBlockDeviceSize(dataFile)
|
|
if err != nil {
|
|
return fmt.Errorf("devicemapper: Can't get data size %s", err)
|
|
}
|
|
|
|
params := fmt.Sprintf("%s %s %d 32768 1 skip_block_zeroing", metadataFile.Name(), dataFile.Name(), poolBlockSize)
|
|
if err := task.addTarget(0, size/512, "thin-pool", params); err != nil {
|
|
return fmt.Errorf("devicemapper: Can't add target %s", err)
|
|
}
|
|
|
|
if err := task.run(); err != nil {
|
|
return fmt.Errorf("devicemapper: Error running ReloadPool %s", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetDeps is the programmatic example of "dmsetup deps".
|
|
// It outputs a list of devices referenced by the live table for the specified device.
|
|
func GetDeps(name string) (*Deps, error) {
|
|
task, err := TaskCreateNamed(deviceDeps, name)
|
|
if task == nil {
|
|
return nil, err
|
|
}
|
|
if err := task.run(); err != nil {
|
|
return nil, err
|
|
}
|
|
return task.getDeps()
|
|
}
|
|
|
|
// GetInfo is the programmatic example of "dmsetup info".
|
|
// It outputs some brief information about the device.
|
|
func GetInfo(name string) (*Info, error) {
|
|
task, err := TaskCreateNamed(deviceInfo, name)
|
|
if task == nil {
|
|
return nil, err
|
|
}
|
|
if err := task.run(); err != nil {
|
|
return nil, err
|
|
}
|
|
return task.getInfo()
|
|
}
|
|
|
|
// GetInfoWithDeferred is the programmatic example of "dmsetup info", but deferred.
|
|
// It outputs some brief information about the device.
|
|
func GetInfoWithDeferred(name string) (*Info, error) {
|
|
task, err := TaskCreateNamed(deviceInfo, name)
|
|
if task == nil {
|
|
return nil, err
|
|
}
|
|
if err := task.run(); err != nil {
|
|
return nil, err
|
|
}
|
|
return task.getInfoWithDeferred()
|
|
}
|
|
|
|
// GetDriverVersion is the programmatic example of "dmsetup version".
|
|
// It outputs version information of the driver.
|
|
func GetDriverVersion() (string, error) {
|
|
task := TaskCreate(deviceVersion)
|
|
if task == nil {
|
|
return "", fmt.Errorf("devicemapper: Can't create deviceVersion task")
|
|
}
|
|
if err := task.run(); err != nil {
|
|
return "", err
|
|
}
|
|
return task.getDriverVersion()
|
|
}
|
|
|
|
// GetStatus is the programmatic example of "dmsetup status".
|
|
// It outputs status information for the specified device name.
|
|
func GetStatus(name string) (uint64, uint64, string, string, error) {
|
|
task, err := TaskCreateNamed(deviceStatus, name)
|
|
if task == nil {
|
|
logrus.Debugf("devicemapper: GetStatus() Error TaskCreateNamed: %s", err)
|
|
return 0, 0, "", "", err
|
|
}
|
|
if err := task.run(); err != nil {
|
|
logrus.Debugf("devicemapper: GetStatus() Error Run: %s", err)
|
|
return 0, 0, "", "", err
|
|
}
|
|
|
|
devinfo, err := task.getInfo()
|
|
if err != nil {
|
|
logrus.Debugf("devicemapper: GetStatus() Error GetInfo: %s", err)
|
|
return 0, 0, "", "", err
|
|
}
|
|
if devinfo.Exists == 0 {
|
|
logrus.Debugf("devicemapper: GetStatus() Non existing device %s", name)
|
|
return 0, 0, "", "", fmt.Errorf("devicemapper: Non existing device %s", name)
|
|
}
|
|
|
|
_, start, length, targetType, params := task.getNextTarget(unsafe.Pointer(nil))
|
|
return start, length, targetType, params, nil
|
|
}
|
|
|
|
// GetTable is the programmatic example for "dmsetup table".
|
|
// It outputs the current table for the specified device name.
|
|
func GetTable(name string) (uint64, uint64, string, string, error) {
|
|
task, err := TaskCreateNamed(deviceTable, name)
|
|
if task == nil {
|
|
logrus.Debugf("devicemapper: GetTable() Error TaskCreateNamed: %s", err)
|
|
return 0, 0, "", "", err
|
|
}
|
|
if err := task.run(); err != nil {
|
|
logrus.Debugf("devicemapper: GetTable() Error Run: %s", err)
|
|
return 0, 0, "", "", err
|
|
}
|
|
|
|
devinfo, err := task.getInfo()
|
|
if err != nil {
|
|
logrus.Debugf("devicemapper: GetTable() Error GetInfo: %s", err)
|
|
return 0, 0, "", "", err
|
|
}
|
|
if devinfo.Exists == 0 {
|
|
logrus.Debugf("devicemapper: GetTable() Non existing device %s", name)
|
|
return 0, 0, "", "", fmt.Errorf("devicemapper: Non existing device %s", name)
|
|
}
|
|
|
|
_, start, length, targetType, params := task.getNextTarget(unsafe.Pointer(nil))
|
|
return start, length, targetType, params, nil
|
|
}
|
|
|
|
// SetTransactionID sets a transaction id for the specified device name.
|
|
func SetTransactionID(poolName string, oldID uint64, newID uint64) error {
|
|
task, err := TaskCreateNamed(deviceTargetMsg, poolName)
|
|
if task == nil {
|
|
return err
|
|
}
|
|
|
|
if err := task.setSector(0); err != nil {
|
|
return fmt.Errorf("devicemapper: Can't set sector %s", err)
|
|
}
|
|
|
|
if err := task.setMessage(fmt.Sprintf("set_transaction_id %d %d", oldID, newID)); err != nil {
|
|
return fmt.Errorf("devicemapper: Can't set message %s", err)
|
|
}
|
|
|
|
if err := task.run(); err != nil {
|
|
return fmt.Errorf("devicemapper: Error running SetTransactionID %s", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SuspendDevice is the programmatic example of "dmsetup suspend".
|
|
// It suspends the specified device.
|
|
func SuspendDevice(name string) error {
|
|
task, err := TaskCreateNamed(deviceSuspend, name)
|
|
if task == nil {
|
|
return err
|
|
}
|
|
if err := task.run(); err != nil {
|
|
return fmt.Errorf("devicemapper: Error running deviceSuspend %s", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ResumeDevice is the programmatic example of "dmsetup resume".
|
|
// It un-suspends the specified device.
|
|
func ResumeDevice(name string) error {
|
|
task, err := TaskCreateNamed(deviceResume, name)
|
|
if task == nil {
|
|
return err
|
|
}
|
|
|
|
cookie := new(uint)
|
|
if err := task.setCookie(cookie, 0); err != nil {
|
|
return fmt.Errorf("devicemapper: Can't set cookie %s", err)
|
|
}
|
|
defer UdevWait(cookie)
|
|
|
|
if err := task.run(); err != nil {
|
|
return fmt.Errorf("devicemapper: Error running deviceResume %s", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// CreateDevice creates a device with the specified poolName with the specified device id.
|
|
func CreateDevice(poolName string, deviceID int) error {
|
|
logrus.Debugf("devicemapper: CreateDevice(poolName=%v, deviceID=%v)", poolName, deviceID)
|
|
task, err := TaskCreateNamed(deviceTargetMsg, poolName)
|
|
if task == nil {
|
|
return err
|
|
}
|
|
|
|
if err := task.setSector(0); err != nil {
|
|
return fmt.Errorf("devicemapper: Can't set sector %s", err)
|
|
}
|
|
|
|
if err := task.setMessage(fmt.Sprintf("create_thin %d", deviceID)); err != nil {
|
|
return fmt.Errorf("devicemapper: Can't set message %s", err)
|
|
}
|
|
|
|
dmSawExist = false // reset before the task is run
|
|
if err := task.run(); err != nil {
|
|
// Caller wants to know about ErrDeviceIDExists so that it can try with a different device id.
|
|
if dmSawExist {
|
|
return ErrDeviceIDExists
|
|
}
|
|
|
|
return fmt.Errorf("devicemapper: Error running CreateDevice %s", err)
|
|
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// DeleteDevice deletes a device with the specified poolName with the specified device id.
|
|
func DeleteDevice(poolName string, deviceID int) error {
|
|
task, err := TaskCreateNamed(deviceTargetMsg, poolName)
|
|
if task == nil {
|
|
return err
|
|
}
|
|
|
|
if err := task.setSector(0); err != nil {
|
|
return fmt.Errorf("devicemapper: Can't set sector %s", err)
|
|
}
|
|
|
|
if err := task.setMessage(fmt.Sprintf("delete %d", deviceID)); err != nil {
|
|
return fmt.Errorf("devicemapper: Can't set message %s", err)
|
|
}
|
|
|
|
dmSawBusy = false
|
|
dmSawEnoData = false
|
|
if err := task.run(); err != nil {
|
|
if dmSawBusy {
|
|
return ErrBusy
|
|
}
|
|
if dmSawEnoData {
|
|
logrus.Debugf("devicemapper: Device(id: %d) from pool(%s) does not exist", deviceID, poolName)
|
|
return nil
|
|
}
|
|
return fmt.Errorf("devicemapper: Error running DeleteDevice %s", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// 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, "")
|
|
}
|
|
|
|
// ActivateDeviceWithExternal activates the device identified by the specified
|
|
// poolName, name and deviceID with the specified size.
|
|
func ActivateDeviceWithExternal(poolName string, name string, deviceID int, size uint64, external string) error {
|
|
return activateDevice(poolName, name, deviceID, size, external)
|
|
}
|
|
|
|
func activateDevice(poolName string, name string, deviceID int, size uint64, external string) error {
|
|
task, err := TaskCreateNamed(deviceCreate, name)
|
|
if task == nil {
|
|
return err
|
|
}
|
|
|
|
var params string
|
|
if len(external) > 0 {
|
|
params = fmt.Sprintf("%s %d %s", poolName, deviceID, external)
|
|
} else {
|
|
params = fmt.Sprintf("%s %d", poolName, deviceID)
|
|
}
|
|
if err := task.addTarget(0, size/512, "thin", params); err != nil {
|
|
return fmt.Errorf("devicemapper: Can't add target %s", err)
|
|
}
|
|
if err := task.setAddNode(addNodeOnCreate); err != nil {
|
|
return fmt.Errorf("devicemapper: Can't add node %s", err)
|
|
}
|
|
|
|
cookie := new(uint)
|
|
if err := task.setCookie(cookie, 0); err != nil {
|
|
return fmt.Errorf("devicemapper: Can't set cookie %s", err)
|
|
}
|
|
|
|
defer UdevWait(cookie)
|
|
|
|
if err := task.run(); err != nil {
|
|
return fmt.Errorf("devicemapper: Error running deviceCreate (ActivateDevice) %s", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// 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 {
|
|
task, err := TaskCreateNamed(deviceTargetMsg, poolName)
|
|
if task == nil {
|
|
return err
|
|
}
|
|
|
|
if err := task.setSector(0); err != nil {
|
|
return fmt.Errorf("devicemapper: Can't set sector %s", err)
|
|
}
|
|
|
|
if err := task.setMessage(fmt.Sprintf("create_snap %d %d", deviceID, baseDeviceID)); err != nil {
|
|
return fmt.Errorf("devicemapper: Can't set message %s", err)
|
|
}
|
|
|
|
dmSawExist = false // reset before the task is run
|
|
if err := task.run(); err != nil {
|
|
// Caller wants to know about ErrDeviceIDExists so that it can try with a different device id.
|
|
if dmSawExist {
|
|
return ErrDeviceIDExists
|
|
}
|
|
return fmt.Errorf("devicemapper: Error running deviceCreate (CreateSnapDeviceRaw) %s", err)
|
|
}
|
|
|
|
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
|
|
|
|
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
|
|
}
|
|
|
|
if doSuspend {
|
|
if err := ResumeDevice(baseName); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|