github.com/christopherobin/docker@v1.6.2/pkg/devicemapper/devmapper.go (about)

     1  // +build linux
     2  
     3  package devicemapper
     4  
     5  import (
     6  	"errors"
     7  	"fmt"
     8  	"os"
     9  	"runtime"
    10  	"syscall"
    11  
    12  	log "github.com/Sirupsen/logrus"
    13  )
    14  
    15  type DevmapperLogger interface {
    16  	DMLog(level int, file string, line int, dmError int, message string)
    17  }
    18  
    19  const (
    20  	DeviceCreate TaskType = iota
    21  	DeviceReload
    22  	DeviceRemove
    23  	DeviceRemoveAll
    24  	DeviceSuspend
    25  	DeviceResume
    26  	DeviceInfo
    27  	DeviceDeps
    28  	DeviceRename
    29  	DeviceVersion
    30  	DeviceStatus
    31  	DeviceTable
    32  	DeviceWaitevent
    33  	DeviceList
    34  	DeviceClear
    35  	DeviceMknodes
    36  	DeviceListVersions
    37  	DeviceTargetMsg
    38  	DeviceSetGeometry
    39  )
    40  
    41  const (
    42  	AddNodeOnResume AddNodeType = iota
    43  	AddNodeOnCreate
    44  )
    45  
    46  var (
    47  	ErrTaskRun                = errors.New("dm_task_run failed")
    48  	ErrTaskSetName            = errors.New("dm_task_set_name failed")
    49  	ErrTaskSetMessage         = errors.New("dm_task_set_message failed")
    50  	ErrTaskSetAddNode         = errors.New("dm_task_set_add_node failed")
    51  	ErrTaskSetRo              = errors.New("dm_task_set_ro failed")
    52  	ErrTaskAddTarget          = errors.New("dm_task_add_target failed")
    53  	ErrTaskSetSector          = errors.New("dm_task_set_sector failed")
    54  	ErrTaskGetDeps            = errors.New("dm_task_get_deps failed")
    55  	ErrTaskGetInfo            = errors.New("dm_task_get_info failed")
    56  	ErrTaskGetDriverVersion   = errors.New("dm_task_get_driver_version failed")
    57  	ErrTaskSetCookie          = errors.New("dm_task_set_cookie failed")
    58  	ErrNilCookie              = errors.New("cookie ptr can't be nil")
    59  	ErrAttachLoopbackDevice   = errors.New("loopback mounting failed")
    60  	ErrGetBlockSize           = errors.New("Can't get block size")
    61  	ErrUdevWait               = errors.New("wait on udev cookie failed")
    62  	ErrSetDevDir              = errors.New("dm_set_dev_dir failed")
    63  	ErrGetLibraryVersion      = errors.New("dm_get_library_version failed")
    64  	ErrCreateRemoveTask       = errors.New("Can't create task of type DeviceRemove")
    65  	ErrRunRemoveDevice        = errors.New("running RemoveDevice failed")
    66  	ErrInvalidAddNode         = errors.New("Invalid AddNode type")
    67  	ErrGetLoopbackBackingFile = errors.New("Unable to get loopback backing file")
    68  	ErrLoopbackSetCapacity    = errors.New("Unable set loopback capacity")
    69  	ErrBusy                   = errors.New("Device is Busy")
    70  	ErrDeviceIdExists         = errors.New("Device Id Exists")
    71  
    72  	dmSawBusy  bool
    73  	dmSawExist bool
    74  )
    75  
    76  type (
    77  	Task struct {
    78  		unmanaged *CDmTask
    79  	}
    80  	Deps struct {
    81  		Count  uint32
    82  		Filler uint32
    83  		Device []uint64
    84  	}
    85  	Info struct {
    86  		Exists        int
    87  		Suspended     int
    88  		LiveTable     int
    89  		InactiveTable int
    90  		OpenCount     int32
    91  		EventNr       uint32
    92  		Major         uint32
    93  		Minor         uint32
    94  		ReadOnly      int
    95  		TargetCount   int32
    96  	}
    97  	TaskType    int
    98  	AddNodeType int
    99  )
   100  
   101  // Returns whether error conveys the information about device Id already
   102  // exist or not. This will be true if device creation or snap creation
   103  // operation fails if device or snap device already exists in pool.
   104  // Current implementation is little crude as it scans the error string
   105  // for exact pattern match. Replacing it with more robust implementation
   106  // is desirable.
   107  func DeviceIdExists(err error) bool {
   108  	return fmt.Sprint(err) == fmt.Sprint(ErrDeviceIdExists)
   109  }
   110  
   111  func (t *Task) destroy() {
   112  	if t != nil {
   113  		DmTaskDestroy(t.unmanaged)
   114  		runtime.SetFinalizer(t, nil)
   115  	}
   116  }
   117  
   118  // TaskCreateNamed is a convenience function for TaskCreate when a name
   119  // will be set on the task as well
   120  func TaskCreateNamed(t TaskType, name string) (*Task, error) {
   121  	task := TaskCreate(t)
   122  	if task == nil {
   123  		return nil, fmt.Errorf("Can't create task of type %d", int(t))
   124  	}
   125  	if err := task.SetName(name); err != nil {
   126  		return nil, fmt.Errorf("Can't set task name %s", name)
   127  	}
   128  	return task, nil
   129  }
   130  
   131  // TaskCreate initializes a devicemapper task of tasktype
   132  func TaskCreate(tasktype TaskType) *Task {
   133  	Ctask := DmTaskCreate(int(tasktype))
   134  	if Ctask == nil {
   135  		return nil
   136  	}
   137  	task := &Task{unmanaged: Ctask}
   138  	runtime.SetFinalizer(task, (*Task).destroy)
   139  	return task
   140  }
   141  
   142  func (t *Task) Run() error {
   143  	if res := DmTaskRun(t.unmanaged); res != 1 {
   144  		return ErrTaskRun
   145  	}
   146  	return nil
   147  }
   148  
   149  func (t *Task) SetName(name string) error {
   150  	if res := DmTaskSetName(t.unmanaged, name); res != 1 {
   151  		return ErrTaskSetName
   152  	}
   153  	return nil
   154  }
   155  
   156  func (t *Task) SetMessage(message string) error {
   157  	if res := DmTaskSetMessage(t.unmanaged, message); res != 1 {
   158  		return ErrTaskSetMessage
   159  	}
   160  	return nil
   161  }
   162  
   163  func (t *Task) SetSector(sector uint64) error {
   164  	if res := DmTaskSetSector(t.unmanaged, sector); res != 1 {
   165  		return ErrTaskSetSector
   166  	}
   167  	return nil
   168  }
   169  
   170  func (t *Task) SetCookie(cookie *uint, flags uint16) error {
   171  	if cookie == nil {
   172  		return ErrNilCookie
   173  	}
   174  	if res := DmTaskSetCookie(t.unmanaged, cookie, flags); res != 1 {
   175  		return ErrTaskSetCookie
   176  	}
   177  	return nil
   178  }
   179  
   180  func (t *Task) SetAddNode(addNode AddNodeType) error {
   181  	if addNode != AddNodeOnResume && addNode != AddNodeOnCreate {
   182  		return ErrInvalidAddNode
   183  	}
   184  	if res := DmTaskSetAddNode(t.unmanaged, addNode); res != 1 {
   185  		return ErrTaskSetAddNode
   186  	}
   187  	return nil
   188  }
   189  
   190  func (t *Task) SetRo() error {
   191  	if res := DmTaskSetRo(t.unmanaged); res != 1 {
   192  		return ErrTaskSetRo
   193  	}
   194  	return nil
   195  }
   196  
   197  func (t *Task) AddTarget(start, size uint64, ttype, params string) error {
   198  	if res := DmTaskAddTarget(t.unmanaged, start, size,
   199  		ttype, params); res != 1 {
   200  		return ErrTaskAddTarget
   201  	}
   202  	return nil
   203  }
   204  
   205  func (t *Task) GetDeps() (*Deps, error) {
   206  	var deps *Deps
   207  	if deps = DmTaskGetDeps(t.unmanaged); deps == nil {
   208  		return nil, ErrTaskGetDeps
   209  	}
   210  	return deps, nil
   211  }
   212  
   213  func (t *Task) GetInfo() (*Info, error) {
   214  	info := &Info{}
   215  	if res := DmTaskGetInfo(t.unmanaged, info); res != 1 {
   216  		return nil, ErrTaskGetInfo
   217  	}
   218  	return info, nil
   219  }
   220  
   221  func (t *Task) GetDriverVersion() (string, error) {
   222  	res := DmTaskGetDriverVersion(t.unmanaged)
   223  	if res == "" {
   224  		return "", ErrTaskGetDriverVersion
   225  	}
   226  	return res, nil
   227  }
   228  
   229  func (t *Task) GetNextTarget(next uintptr) (nextPtr uintptr, start uint64,
   230  	length uint64, targetType string, params string) {
   231  
   232  	return DmGetNextTarget(t.unmanaged, next, &start, &length,
   233  			&targetType, &params),
   234  		start, length, targetType, params
   235  }
   236  
   237  func getLoopbackBackingFile(file *os.File) (uint64, uint64, error) {
   238  	loopInfo, err := ioctlLoopGetStatus64(file.Fd())
   239  	if err != nil {
   240  		log.Errorf("Error get loopback backing file: %s", err)
   241  		return 0, 0, ErrGetLoopbackBackingFile
   242  	}
   243  	return loopInfo.loDevice, loopInfo.loInode, nil
   244  }
   245  
   246  func LoopbackSetCapacity(file *os.File) error {
   247  	if err := ioctlLoopSetCapacity(file.Fd(), 0); err != nil {
   248  		log.Errorf("Error loopbackSetCapacity: %s", err)
   249  		return ErrLoopbackSetCapacity
   250  	}
   251  	return nil
   252  }
   253  
   254  func FindLoopDeviceFor(file *os.File) *os.File {
   255  	stat, err := file.Stat()
   256  	if err != nil {
   257  		return nil
   258  	}
   259  	targetInode := stat.Sys().(*syscall.Stat_t).Ino
   260  	targetDevice := stat.Sys().(*syscall.Stat_t).Dev
   261  
   262  	for i := 0; true; i++ {
   263  		path := fmt.Sprintf("/dev/loop%d", i)
   264  
   265  		file, err := os.OpenFile(path, os.O_RDWR, 0)
   266  		if err != nil {
   267  			if os.IsNotExist(err) {
   268  				return nil
   269  			}
   270  
   271  			// Ignore all errors until the first not-exist
   272  			// we want to continue looking for the file
   273  			continue
   274  		}
   275  
   276  		dev, inode, err := getLoopbackBackingFile(file)
   277  		if err == nil && dev == targetDevice && inode == targetInode {
   278  			return file
   279  		}
   280  		file.Close()
   281  	}
   282  
   283  	return nil
   284  }
   285  
   286  func UdevWait(cookie uint) error {
   287  	if res := DmUdevWait(cookie); res != 1 {
   288  		log.Debugf("Failed to wait on udev cookie %d", cookie)
   289  		return ErrUdevWait
   290  	}
   291  	return nil
   292  }
   293  
   294  func LogInitVerbose(level int) {
   295  	DmLogInitVerbose(level)
   296  }
   297  
   298  var dmLogger DevmapperLogger = nil
   299  
   300  // initialize the logger for the device mapper library
   301  func LogInit(logger DevmapperLogger) {
   302  	dmLogger = logger
   303  	LogWithErrnoInit()
   304  }
   305  
   306  func SetDevDir(dir string) error {
   307  	if res := DmSetDevDir(dir); res != 1 {
   308  		log.Debugf("Error dm_set_dev_dir")
   309  		return ErrSetDevDir
   310  	}
   311  	return nil
   312  }
   313  
   314  func GetLibraryVersion() (string, error) {
   315  	var version string
   316  	if res := DmGetLibraryVersion(&version); res != 1 {
   317  		return "", ErrGetLibraryVersion
   318  	}
   319  	return version, nil
   320  }
   321  
   322  // UdevSyncSupported returns whether device-mapper is able to sync with udev
   323  //
   324  // This is essential otherwise race conditions can arise where both udev and
   325  // device-mapper attempt to create and destroy devices.
   326  func UdevSyncSupported() bool {
   327  	return DmUdevGetSyncSupport() != 0
   328  }
   329  
   330  // UdevSetSyncSupport allows setting whether the udev sync should be enabled.
   331  // The return bool indicates the state of whether the sync is enabled.
   332  func UdevSetSyncSupport(enable bool) bool {
   333  	if enable {
   334  		DmUdevSetSyncSupport(1)
   335  	} else {
   336  		DmUdevSetSyncSupport(0)
   337  	}
   338  
   339  	return UdevSyncSupported()
   340  }
   341  
   342  // CookieSupported returns whether the version of device-mapper supports the
   343  // use of cookie's in the tasks.
   344  // This is largely a lower level call that other functions use.
   345  func CookieSupported() bool {
   346  	return DmCookieSupported() != 0
   347  }
   348  
   349  // Useful helper for cleanup
   350  func RemoveDevice(name string) error {
   351  	log.Debugf("[devmapper] RemoveDevice START(%s)", name)
   352  	defer log.Debugf("[devmapper] RemoveDevice END(%s)", name)
   353  	task, err := TaskCreateNamed(DeviceRemove, name)
   354  	if task == nil {
   355  		return err
   356  	}
   357  
   358  	var cookie uint = 0
   359  	if err := task.SetCookie(&cookie, 0); err != nil {
   360  		return fmt.Errorf("Can not set cookie: %s", err)
   361  	}
   362  	defer UdevWait(cookie)
   363  
   364  	dmSawBusy = false // reset before the task is run
   365  	if err = task.Run(); err != nil {
   366  		if dmSawBusy {
   367  			return ErrBusy
   368  		}
   369  		return fmt.Errorf("Error running RemoveDevice %s", err)
   370  	}
   371  
   372  	return nil
   373  }
   374  
   375  func GetBlockDeviceSize(file *os.File) (uint64, error) {
   376  	size, err := ioctlBlkGetSize64(file.Fd())
   377  	if err != nil {
   378  		log.Errorf("Error getblockdevicesize: %s", err)
   379  		return 0, ErrGetBlockSize
   380  	}
   381  	return uint64(size), nil
   382  }
   383  
   384  func BlockDeviceDiscard(path string) error {
   385  	file, err := os.OpenFile(path, os.O_RDWR, 0)
   386  	if err != nil {
   387  		return err
   388  	}
   389  	defer file.Close()
   390  
   391  	size, err := GetBlockDeviceSize(file)
   392  	if err != nil {
   393  		return err
   394  	}
   395  
   396  	if err := ioctlBlkDiscard(file.Fd(), 0, size); err != nil {
   397  		return err
   398  	}
   399  
   400  	// Without this sometimes the remove of the device that happens after
   401  	// discard fails with EBUSY.
   402  	syscall.Sync()
   403  
   404  	return nil
   405  }
   406  
   407  // This is the programmatic example of "dmsetup create"
   408  func CreatePool(poolName string, dataFile, metadataFile *os.File, poolBlockSize uint32) error {
   409  	task, err := TaskCreateNamed(DeviceCreate, poolName)
   410  	if task == nil {
   411  		return err
   412  	}
   413  
   414  	size, err := GetBlockDeviceSize(dataFile)
   415  	if err != nil {
   416  		return fmt.Errorf("Can't get data size %s", err)
   417  	}
   418  
   419  	params := fmt.Sprintf("%s %s %d 32768 1 skip_block_zeroing", metadataFile.Name(), dataFile.Name(), poolBlockSize)
   420  	if err := task.AddTarget(0, size/512, "thin-pool", params); err != nil {
   421  		return fmt.Errorf("Can't add target %s", err)
   422  	}
   423  
   424  	var cookie uint = 0
   425  	var flags uint16 = DmUdevDisableSubsystemRulesFlag | DmUdevDisableDiskRulesFlag | DmUdevDisableOtherRulesFlag
   426  	if err := task.SetCookie(&cookie, flags); err != nil {
   427  		return fmt.Errorf("Can't set cookie %s", err)
   428  	}
   429  	defer UdevWait(cookie)
   430  
   431  	if err := task.Run(); err != nil {
   432  		return fmt.Errorf("Error running DeviceCreate (CreatePool) %s", err)
   433  	}
   434  
   435  	return nil
   436  }
   437  
   438  func ReloadPool(poolName string, dataFile, metadataFile *os.File, poolBlockSize uint32) error {
   439  	task, err := TaskCreateNamed(DeviceReload, poolName)
   440  	if task == nil {
   441  		return err
   442  	}
   443  
   444  	size, err := GetBlockDeviceSize(dataFile)
   445  	if err != nil {
   446  		return fmt.Errorf("Can't get data size %s", err)
   447  	}
   448  
   449  	params := fmt.Sprintf("%s %s %d 32768 1 skip_block_zeroing", metadataFile.Name(), dataFile.Name(), poolBlockSize)
   450  	if err := task.AddTarget(0, size/512, "thin-pool", params); err != nil {
   451  		return fmt.Errorf("Can't add target %s", err)
   452  	}
   453  
   454  	if err := task.Run(); err != nil {
   455  		return fmt.Errorf("Error running DeviceCreate %s", err)
   456  	}
   457  
   458  	return nil
   459  }
   460  
   461  func GetDeps(name string) (*Deps, error) {
   462  	task, err := TaskCreateNamed(DeviceDeps, name)
   463  	if task == nil {
   464  		return nil, err
   465  	}
   466  	if err := task.Run(); err != nil {
   467  		return nil, err
   468  	}
   469  	return task.GetDeps()
   470  }
   471  
   472  func GetInfo(name string) (*Info, error) {
   473  	task, err := TaskCreateNamed(DeviceInfo, name)
   474  	if task == nil {
   475  		return nil, err
   476  	}
   477  	if err := task.Run(); err != nil {
   478  		return nil, err
   479  	}
   480  	return task.GetInfo()
   481  }
   482  
   483  func GetDriverVersion() (string, error) {
   484  	task := TaskCreate(DeviceVersion)
   485  	if task == nil {
   486  		return "", fmt.Errorf("Can't create DeviceVersion task")
   487  	}
   488  	if err := task.Run(); err != nil {
   489  		return "", err
   490  	}
   491  	return task.GetDriverVersion()
   492  }
   493  
   494  func GetStatus(name string) (uint64, uint64, string, string, error) {
   495  	task, err := TaskCreateNamed(DeviceStatus, name)
   496  	if task == nil {
   497  		log.Debugf("GetStatus: Error TaskCreateNamed: %s", err)
   498  		return 0, 0, "", "", err
   499  	}
   500  	if err := task.Run(); err != nil {
   501  		log.Debugf("GetStatus: Error Run: %s", err)
   502  		return 0, 0, "", "", err
   503  	}
   504  
   505  	devinfo, err := task.GetInfo()
   506  	if err != nil {
   507  		log.Debugf("GetStatus: Error GetInfo: %s", err)
   508  		return 0, 0, "", "", err
   509  	}
   510  	if devinfo.Exists == 0 {
   511  		log.Debugf("GetStatus: Non existing device %s", name)
   512  		return 0, 0, "", "", fmt.Errorf("Non existing device %s", name)
   513  	}
   514  
   515  	_, start, length, targetType, params := task.GetNextTarget(0)
   516  	return start, length, targetType, params, nil
   517  }
   518  
   519  func SetTransactionId(poolName string, oldId uint64, newId uint64) error {
   520  	task, err := TaskCreateNamed(DeviceTargetMsg, poolName)
   521  	if task == nil {
   522  		return err
   523  	}
   524  
   525  	if err := task.SetSector(0); err != nil {
   526  		return fmt.Errorf("Can't set sector %s", err)
   527  	}
   528  
   529  	if err := task.SetMessage(fmt.Sprintf("set_transaction_id %d %d", oldId, newId)); err != nil {
   530  		return fmt.Errorf("Can't set message %s", err)
   531  	}
   532  
   533  	if err := task.Run(); err != nil {
   534  		return fmt.Errorf("Error running SetTransactionId %s", err)
   535  	}
   536  	return nil
   537  }
   538  
   539  func SuspendDevice(name string) error {
   540  	task, err := TaskCreateNamed(DeviceSuspend, name)
   541  	if task == nil {
   542  		return err
   543  	}
   544  	if err := task.Run(); err != nil {
   545  		return fmt.Errorf("Error running DeviceSuspend %s", err)
   546  	}
   547  	return nil
   548  }
   549  
   550  func ResumeDevice(name string) error {
   551  	task, err := TaskCreateNamed(DeviceResume, name)
   552  	if task == nil {
   553  		return err
   554  	}
   555  
   556  	var cookie uint = 0
   557  	if err := task.SetCookie(&cookie, 0); err != nil {
   558  		return fmt.Errorf("Can't set cookie %s", err)
   559  	}
   560  	defer UdevWait(cookie)
   561  
   562  	if err := task.Run(); err != nil {
   563  		return fmt.Errorf("Error running DeviceResume %s", err)
   564  	}
   565  
   566  	return nil
   567  }
   568  
   569  func CreateDevice(poolName string, deviceId int) error {
   570  	log.Debugf("[devmapper] CreateDevice(poolName=%v, deviceId=%v)", poolName, deviceId)
   571  	task, err := TaskCreateNamed(DeviceTargetMsg, poolName)
   572  	if task == nil {
   573  		return err
   574  	}
   575  
   576  	if err := task.SetSector(0); err != nil {
   577  		return fmt.Errorf("Can't set sector %s", err)
   578  	}
   579  
   580  	if err := task.SetMessage(fmt.Sprintf("create_thin %d", deviceId)); err != nil {
   581  		return fmt.Errorf("Can't set message %s", err)
   582  	}
   583  
   584  	dmSawExist = false // reset before the task is run
   585  	if err := task.Run(); err != nil {
   586  		// Caller wants to know about ErrDeviceIdExists so that it can try with a different device id.
   587  		if dmSawExist {
   588  			return ErrDeviceIdExists
   589  		} else {
   590  			return fmt.Errorf("Error running CreateDevice %s", err)
   591  		}
   592  	}
   593  	return nil
   594  }
   595  
   596  func DeleteDevice(poolName string, deviceId int) error {
   597  	task, err := TaskCreateNamed(DeviceTargetMsg, poolName)
   598  	if task == nil {
   599  		return err
   600  	}
   601  
   602  	if err := task.SetSector(0); err != nil {
   603  		return fmt.Errorf("Can't set sector %s", err)
   604  	}
   605  
   606  	if err := task.SetMessage(fmt.Sprintf("delete %d", deviceId)); err != nil {
   607  		return fmt.Errorf("Can't set message %s", err)
   608  	}
   609  
   610  	if err := task.Run(); err != nil {
   611  		return fmt.Errorf("Error running DeleteDevice %s", err)
   612  	}
   613  	return nil
   614  }
   615  
   616  func ActivateDevice(poolName string, name string, deviceId int, size uint64) error {
   617  	task, err := TaskCreateNamed(DeviceCreate, name)
   618  	if task == nil {
   619  		return err
   620  	}
   621  
   622  	params := fmt.Sprintf("%s %d", poolName, deviceId)
   623  	if err := task.AddTarget(0, size/512, "thin", params); err != nil {
   624  		return fmt.Errorf("Can't add target %s", err)
   625  	}
   626  	if err := task.SetAddNode(AddNodeOnCreate); err != nil {
   627  		return fmt.Errorf("Can't add node %s", err)
   628  	}
   629  
   630  	var cookie uint = 0
   631  	if err := task.SetCookie(&cookie, 0); err != nil {
   632  		return fmt.Errorf("Can't set cookie %s", err)
   633  	}
   634  
   635  	defer UdevWait(cookie)
   636  
   637  	if err := task.Run(); err != nil {
   638  		return fmt.Errorf("Error running DeviceCreate (ActivateDevice) %s", err)
   639  	}
   640  
   641  	return nil
   642  }
   643  
   644  func CreateSnapDevice(poolName string, deviceId int, baseName string, baseDeviceId int) error {
   645  	devinfo, _ := GetInfo(baseName)
   646  	doSuspend := devinfo != nil && devinfo.Exists != 0
   647  
   648  	if doSuspend {
   649  		if err := SuspendDevice(baseName); err != nil {
   650  			return err
   651  		}
   652  	}
   653  
   654  	task, err := TaskCreateNamed(DeviceTargetMsg, poolName)
   655  	if task == nil {
   656  		if doSuspend {
   657  			ResumeDevice(baseName)
   658  		}
   659  		return err
   660  	}
   661  
   662  	if err := task.SetSector(0); err != nil {
   663  		if doSuspend {
   664  			ResumeDevice(baseName)
   665  		}
   666  		return fmt.Errorf("Can't set sector %s", err)
   667  	}
   668  
   669  	if err := task.SetMessage(fmt.Sprintf("create_snap %d %d", deviceId, baseDeviceId)); err != nil {
   670  		if doSuspend {
   671  			ResumeDevice(baseName)
   672  		}
   673  		return fmt.Errorf("Can't set message %s", err)
   674  	}
   675  
   676  	dmSawExist = false // reset before the task is run
   677  	if err := task.Run(); err != nil {
   678  		if doSuspend {
   679  			ResumeDevice(baseName)
   680  		}
   681  		// Caller wants to know about ErrDeviceIdExists so that it can try with a different device id.
   682  		if dmSawExist {
   683  			return ErrDeviceIdExists
   684  		} else {
   685  			return fmt.Errorf("Error running DeviceCreate (createSnapDevice) %s", err)
   686  		}
   687  	}
   688  
   689  	if doSuspend {
   690  		if err := ResumeDevice(baseName); err != nil {
   691  			return err
   692  		}
   693  	}
   694  
   695  	return nil
   696  }