github.com/pmorton/docker@v1.5.0/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  // Useful helper for cleanup
   343  func RemoveDevice(name string) error {
   344  	log.Debugf("[devmapper] RemoveDevice START(%s)", name)
   345  	defer log.Debugf("[devmapper] RemoveDevice END(%s)", name)
   346  	task, err := TaskCreateNamed(DeviceRemove, name)
   347  	if task == nil {
   348  		return err
   349  	}
   350  
   351  	var cookie uint = 0
   352  	if err := task.SetCookie(&cookie, 0); err != nil {
   353  		return fmt.Errorf("Can not set cookie: %s", err)
   354  	}
   355  	defer UdevWait(cookie)
   356  
   357  	dmSawBusy = false // reset before the task is run
   358  	if err = task.Run(); err != nil {
   359  		if dmSawBusy {
   360  			return ErrBusy
   361  		}
   362  		return fmt.Errorf("Error running RemoveDevice %s", err)
   363  	}
   364  
   365  	return nil
   366  }
   367  
   368  func GetBlockDeviceSize(file *os.File) (uint64, error) {
   369  	size, err := ioctlBlkGetSize64(file.Fd())
   370  	if err != nil {
   371  		log.Errorf("Error getblockdevicesize: %s", err)
   372  		return 0, ErrGetBlockSize
   373  	}
   374  	return uint64(size), nil
   375  }
   376  
   377  func BlockDeviceDiscard(path string) error {
   378  	file, err := os.OpenFile(path, os.O_RDWR, 0)
   379  	if err != nil {
   380  		return err
   381  	}
   382  	defer file.Close()
   383  
   384  	size, err := GetBlockDeviceSize(file)
   385  	if err != nil {
   386  		return err
   387  	}
   388  
   389  	if err := ioctlBlkDiscard(file.Fd(), 0, size); err != nil {
   390  		return err
   391  	}
   392  
   393  	// Without this sometimes the remove of the device that happens after
   394  	// discard fails with EBUSY.
   395  	syscall.Sync()
   396  
   397  	return nil
   398  }
   399  
   400  // This is the programmatic example of "dmsetup create"
   401  func CreatePool(poolName string, dataFile, metadataFile *os.File, poolBlockSize uint32) error {
   402  	task, err := TaskCreateNamed(DeviceCreate, poolName)
   403  	if task == nil {
   404  		return err
   405  	}
   406  
   407  	size, err := GetBlockDeviceSize(dataFile)
   408  	if err != nil {
   409  		return fmt.Errorf("Can't get data size %s", err)
   410  	}
   411  
   412  	params := fmt.Sprintf("%s %s %d 32768 1 skip_block_zeroing", metadataFile.Name(), dataFile.Name(), poolBlockSize)
   413  	if err := task.AddTarget(0, size/512, "thin-pool", params); err != nil {
   414  		return fmt.Errorf("Can't add target %s", err)
   415  	}
   416  
   417  	var cookie uint = 0
   418  	var flags uint16 = DmUdevDisableSubsystemRulesFlag | DmUdevDisableDiskRulesFlag | DmUdevDisableOtherRulesFlag
   419  	if err := task.SetCookie(&cookie, flags); err != nil {
   420  		return fmt.Errorf("Can't set cookie %s", err)
   421  	}
   422  	defer UdevWait(cookie)
   423  
   424  	if err := task.Run(); err != nil {
   425  		return fmt.Errorf("Error running DeviceCreate (CreatePool) %s", err)
   426  	}
   427  
   428  	return nil
   429  }
   430  
   431  func ReloadPool(poolName string, dataFile, metadataFile *os.File, poolBlockSize uint32) error {
   432  	task, err := TaskCreateNamed(DeviceReload, poolName)
   433  	if task == nil {
   434  		return err
   435  	}
   436  
   437  	size, err := GetBlockDeviceSize(dataFile)
   438  	if err != nil {
   439  		return fmt.Errorf("Can't get data size %s", err)
   440  	}
   441  
   442  	params := fmt.Sprintf("%s %s %d 32768 1 skip_block_zeroing", metadataFile.Name(), dataFile.Name(), poolBlockSize)
   443  	if err := task.AddTarget(0, size/512, "thin-pool", params); err != nil {
   444  		return fmt.Errorf("Can't add target %s", err)
   445  	}
   446  
   447  	if err := task.Run(); err != nil {
   448  		return fmt.Errorf("Error running DeviceCreate %s", err)
   449  	}
   450  
   451  	return nil
   452  }
   453  
   454  func GetDeps(name string) (*Deps, error) {
   455  	task, err := TaskCreateNamed(DeviceDeps, name)
   456  	if task == nil {
   457  		return nil, err
   458  	}
   459  	if err := task.Run(); err != nil {
   460  		return nil, err
   461  	}
   462  	return task.GetDeps()
   463  }
   464  
   465  func GetInfo(name string) (*Info, error) {
   466  	task, err := TaskCreateNamed(DeviceInfo, name)
   467  	if task == nil {
   468  		return nil, err
   469  	}
   470  	if err := task.Run(); err != nil {
   471  		return nil, err
   472  	}
   473  	return task.GetInfo()
   474  }
   475  
   476  func GetDriverVersion() (string, error) {
   477  	task := TaskCreate(DeviceVersion)
   478  	if task == nil {
   479  		return "", fmt.Errorf("Can't create DeviceVersion task")
   480  	}
   481  	if err := task.Run(); err != nil {
   482  		return "", err
   483  	}
   484  	return task.GetDriverVersion()
   485  }
   486  
   487  func GetStatus(name string) (uint64, uint64, string, string, error) {
   488  	task, err := TaskCreateNamed(DeviceStatus, name)
   489  	if task == nil {
   490  		log.Debugf("GetStatus: Error TaskCreateNamed: %s", err)
   491  		return 0, 0, "", "", err
   492  	}
   493  	if err := task.Run(); err != nil {
   494  		log.Debugf("GetStatus: Error Run: %s", err)
   495  		return 0, 0, "", "", err
   496  	}
   497  
   498  	devinfo, err := task.GetInfo()
   499  	if err != nil {
   500  		log.Debugf("GetStatus: Error GetInfo: %s", err)
   501  		return 0, 0, "", "", err
   502  	}
   503  	if devinfo.Exists == 0 {
   504  		log.Debugf("GetStatus: Non existing device %s", name)
   505  		return 0, 0, "", "", fmt.Errorf("Non existing device %s", name)
   506  	}
   507  
   508  	_, start, length, targetType, params := task.GetNextTarget(0)
   509  	return start, length, targetType, params, nil
   510  }
   511  
   512  func SetTransactionId(poolName string, oldId uint64, newId uint64) error {
   513  	task, err := TaskCreateNamed(DeviceTargetMsg, poolName)
   514  	if task == nil {
   515  		return err
   516  	}
   517  
   518  	if err := task.SetSector(0); err != nil {
   519  		return fmt.Errorf("Can't set sector %s", err)
   520  	}
   521  
   522  	if err := task.SetMessage(fmt.Sprintf("set_transaction_id %d %d", oldId, newId)); err != nil {
   523  		return fmt.Errorf("Can't set message %s", err)
   524  	}
   525  
   526  	if err := task.Run(); err != nil {
   527  		return fmt.Errorf("Error running SetTransactionId %s", err)
   528  	}
   529  	return nil
   530  }
   531  
   532  func SuspendDevice(name string) error {
   533  	task, err := TaskCreateNamed(DeviceSuspend, name)
   534  	if task == nil {
   535  		return err
   536  	}
   537  	if err := task.Run(); err != nil {
   538  		return fmt.Errorf("Error running DeviceSuspend %s", err)
   539  	}
   540  	return nil
   541  }
   542  
   543  func ResumeDevice(name string) error {
   544  	task, err := TaskCreateNamed(DeviceResume, name)
   545  	if task == nil {
   546  		return err
   547  	}
   548  
   549  	var cookie uint = 0
   550  	if err := task.SetCookie(&cookie, 0); err != nil {
   551  		return fmt.Errorf("Can't set cookie %s", err)
   552  	}
   553  	defer UdevWait(cookie)
   554  
   555  	if err := task.Run(); err != nil {
   556  		return fmt.Errorf("Error running DeviceResume %s", err)
   557  	}
   558  
   559  	return nil
   560  }
   561  
   562  func CreateDevice(poolName string, deviceId int) error {
   563  	log.Debugf("[devmapper] CreateDevice(poolName=%v, deviceId=%v)", poolName, deviceId)
   564  	task, err := TaskCreateNamed(DeviceTargetMsg, poolName)
   565  	if task == nil {
   566  		return err
   567  	}
   568  
   569  	if err := task.SetSector(0); err != nil {
   570  		return fmt.Errorf("Can't set sector %s", err)
   571  	}
   572  
   573  	if err := task.SetMessage(fmt.Sprintf("create_thin %d", deviceId)); err != nil {
   574  		return fmt.Errorf("Can't set message %s", err)
   575  	}
   576  
   577  	dmSawExist = false // reset before the task is run
   578  	if err := task.Run(); err != nil {
   579  		// Caller wants to know about ErrDeviceIdExists so that it can try with a different device id.
   580  		if dmSawExist {
   581  			return ErrDeviceIdExists
   582  		} else {
   583  			return fmt.Errorf("Error running CreateDevice %s", err)
   584  		}
   585  	}
   586  	return nil
   587  }
   588  
   589  func DeleteDevice(poolName string, deviceId int) error {
   590  	task, err := TaskCreateNamed(DeviceTargetMsg, poolName)
   591  	if task == nil {
   592  		return err
   593  	}
   594  
   595  	if err := task.SetSector(0); err != nil {
   596  		return fmt.Errorf("Can't set sector %s", err)
   597  	}
   598  
   599  	if err := task.SetMessage(fmt.Sprintf("delete %d", deviceId)); err != nil {
   600  		return fmt.Errorf("Can't set message %s", err)
   601  	}
   602  
   603  	if err := task.Run(); err != nil {
   604  		return fmt.Errorf("Error running DeleteDevice %s", err)
   605  	}
   606  	return nil
   607  }
   608  
   609  func ActivateDevice(poolName string, name string, deviceId int, size uint64) error {
   610  	task, err := TaskCreateNamed(DeviceCreate, name)
   611  	if task == nil {
   612  		return err
   613  	}
   614  
   615  	params := fmt.Sprintf("%s %d", poolName, deviceId)
   616  	if err := task.AddTarget(0, size/512, "thin", params); err != nil {
   617  		return fmt.Errorf("Can't add target %s", err)
   618  	}
   619  	if err := task.SetAddNode(AddNodeOnCreate); err != nil {
   620  		return fmt.Errorf("Can't add node %s", err)
   621  	}
   622  
   623  	var cookie uint = 0
   624  	if err := task.SetCookie(&cookie, 0); err != nil {
   625  		return fmt.Errorf("Can't set cookie %s", err)
   626  	}
   627  
   628  	defer UdevWait(cookie)
   629  
   630  	if err := task.Run(); err != nil {
   631  		return fmt.Errorf("Error running DeviceCreate (ActivateDevice) %s", err)
   632  	}
   633  
   634  	return nil
   635  }
   636  
   637  func CreateSnapDevice(poolName string, deviceId int, baseName string, baseDeviceId int) error {
   638  	devinfo, _ := GetInfo(baseName)
   639  	doSuspend := devinfo != nil && devinfo.Exists != 0
   640  
   641  	if doSuspend {
   642  		if err := SuspendDevice(baseName); err != nil {
   643  			return err
   644  		}
   645  	}
   646  
   647  	task, err := TaskCreateNamed(DeviceTargetMsg, poolName)
   648  	if task == nil {
   649  		if doSuspend {
   650  			ResumeDevice(baseName)
   651  		}
   652  		return err
   653  	}
   654  
   655  	if err := task.SetSector(0); err != nil {
   656  		if doSuspend {
   657  			ResumeDevice(baseName)
   658  		}
   659  		return fmt.Errorf("Can't set sector %s", err)
   660  	}
   661  
   662  	if err := task.SetMessage(fmt.Sprintf("create_snap %d %d", deviceId, baseDeviceId)); err != nil {
   663  		if doSuspend {
   664  			ResumeDevice(baseName)
   665  		}
   666  		return fmt.Errorf("Can't set message %s", err)
   667  	}
   668  
   669  	dmSawExist = false // reset before the task is run
   670  	if err := task.Run(); err != nil {
   671  		if doSuspend {
   672  			ResumeDevice(baseName)
   673  		}
   674  		// Caller wants to know about ErrDeviceIdExists so that it can try with a different device id.
   675  		if dmSawExist {
   676  			return ErrDeviceIdExists
   677  		} else {
   678  			return fmt.Errorf("Error running DeviceCreate (createSnapDevice) %s", err)
   679  		}
   680  	}
   681  
   682  	if doSuspend {
   683  		if err := ResumeDevice(baseName); err != nil {
   684  			return err
   685  		}
   686  	}
   687  
   688  	return nil
   689  }