devmapper: Move all "raw" libdevmapper wrappers to devmapper.go

This separates out the DeviceSet logic a bit better from the raw
device mapper operations.

devicemapper: Serialize addess to the devicemapper deviceset

This code is not safe to run in multiple threads at the same time,
and neither is libdevmapper.

DeviceMapper: Move deactivate into UnmountDevice

This way the deactivate is atomic wrt othe device mapper operations
and will not fail with EBUSY if someone else starts a devicemapper
operation inbetween unmount and deactivate.

devmapper: Fix loopback mounting regression

Some changes were added to attach_loop_device which added
a perror() in a place that caused it to override errno so that
a later errno != EBUSY failed. This fixes that and cleans up
the error reporting a bit.

devmapper: Build on old kernels without LOOP_CTL_GET_FREE define
diff --git a/deviceset.go b/deviceset.go
index b2a9687..21d5fd4 100644
--- a/deviceset.go
+++ b/deviceset.go
@@ -6,7 +6,7 @@
 	DeactivateDevice(hash string) error
 	RemoveDevice(hash string) error
 	MountDevice(hash, path string) error
-	UnmountDevice(hash, path string) error
+	UnmountDevice(hash, path string, deactivate bool) error
 	HasDevice(hash string) bool
 	HasInitializedDevice(hash string) bool
 	HasActivatedDevice(hash string) bool
diff --git a/devmapper/deviceset_devmapper.go b/devmapper/deviceset_devmapper.go
index b789f94..d0d4fbf 100644
--- a/devmapper/deviceset_devmapper.go
+++ b/devmapper/deviceset_devmapper.go
@@ -13,6 +13,7 @@
 	"strconv"
 	"strings"
 	"syscall"
+	"sync"
 )
 
 const (
@@ -36,6 +37,7 @@
 
 type DeviceSetDM struct {
 	MetaData
+	sync.Mutex
 	initialized      bool
 	root             string
 	devicePrefix     string
@@ -77,74 +79,6 @@
 	return getDevName(devices.getPoolName())
 }
 
-func (devices *DeviceSetDM) createTask(t TaskType, name string) (*Task, error) {
-	task := TaskCreate(t)
-	if task == nil {
-		return nil, fmt.Errorf("Can't create task of type %d", int(t))
-	}
-	if err := task.SetName(name); err != nil {
-		return nil, fmt.Errorf("Can't set task name %s", name)
-	}
-	return task, nil
-}
-
-func (devices *DeviceSetDM) getInfo(name string) (*Info, error) {
-	task, err := devices.createTask(DeviceInfo, name)
-	if task == nil {
-		return nil, err
-	}
-	if err := task.Run(); err != nil {
-		return nil, err
-	}
-	return task.GetInfo()
-}
-
-func (devices *DeviceSetDM) getStatus(name string) (uint64, uint64, string, string, error) {
-	task, err := devices.createTask(DeviceStatus, name)
-	if task == nil {
-		utils.Debugf("getStatus: Error createTask: %s", err)
-		return 0, 0, "", "", err
-	}
-	if err := task.Run(); err != nil {
-		utils.Debugf("getStatus: Error Run: %s", err)
-		return 0, 0, "", "", err
-	}
-
-	devinfo, err := task.GetInfo()
-	if err != nil {
-		utils.Debugf("getStatus: Error GetInfo: %s", err)
-		return 0, 0, "", "", err
-	}
-	if devinfo.Exists == 0 {
-		utils.Debugf("getStatus: Non existing device %s", name)
-		return 0, 0, "", "", fmt.Errorf("Non existing device %s", name)
-	}
-
-	_, start, length, target_type, params := task.GetNextTarget(0)
-	return start, length, target_type, params, nil
-}
-
-func (devices *DeviceSetDM) setTransactionId(oldId uint64, newId uint64) error {
-	task, err := devices.createTask(DeviceTargetMsg, devices.getPoolDevName())
-	if task == nil {
-		utils.Debugf("\n--->Err: %s\n", err)
-		return err
-	}
-
-	if err := task.SetSector(0); err != nil {
-		return fmt.Errorf("Can't set sector")
-	}
-
-	if err := task.SetMessage(fmt.Sprintf("set_transaction_id %d %d", oldId, newId)); err != nil {
-		return fmt.Errorf("Can't set message")
-	}
-
-	if err := task.Run(); err != nil {
-		return fmt.Errorf("Error running setTransactionId")
-	}
-	return nil
-}
-
 func (devices *DeviceSetDM) hasImage(name string) bool {
 	dirname := devices.loopbackDir()
 	filename := path.Join(dirname, name)
@@ -178,194 +112,6 @@
 	return filename, nil
 }
 
-func (devices *DeviceSetDM) createPool(dataFile *os.File, metadataFile *os.File) error {
-	utils.Debugf("Activating device-mapper pool %s", devices.getPoolName())
-	task, err := devices.createTask(DeviceCreate, devices.getPoolName())
-	if task == nil {
-		utils.Debugf("\n--->Err: %s\n", err)
-		return err
-	}
-
-	size, err := GetBlockDeviceSize(dataFile)
-	if err != nil {
-		return fmt.Errorf("Can't get data size")
-	}
-
-	params := metadataFile.Name() + " " + dataFile.Name() + " 512 8192"
-	if err := task.AddTarget(0, size/512, "thin-pool", params); err != nil {
-		return fmt.Errorf("Can't add target")
-	}
-
-	var cookie uint32 = 0
-	if err := task.SetCookie(&cookie, 0); err != nil {
-		return fmt.Errorf("Can't set cookie")
-	}
-
-	if err := task.Run(); err != nil {
-		return fmt.Errorf("Error running DeviceCreate")
-	}
-
-	UdevWait(cookie)
-
-	return nil
-}
-
-func (devices *DeviceSetDM) suspendDevice(info *DevInfo) error {
-	task, err := devices.createTask(DeviceSuspend, info.Name())
-	if task == nil {
-		utils.Debugf("\n--->Err: %s\n", err)
-		return err
-	}
-	if err := task.Run(); err != nil {
-		return fmt.Errorf("Error running DeviceSuspend")
-	}
-	return nil
-}
-
-func (devices *DeviceSetDM) resumeDevice(info *DevInfo) error {
-	task, err := devices.createTask(DeviceResume, info.Name())
-	if task == nil {
-		utils.Debugf("\n--->Err: %s\n", err)
-		return err
-	}
-
-	var cookie uint32 = 0
-	if err := task.SetCookie(&cookie, 0); err != nil {
-		return fmt.Errorf("Can't set cookie")
-	}
-
-	if err := task.Run(); err != nil {
-		return fmt.Errorf("Error running DeviceSuspend")
-	}
-
-	UdevWait(cookie)
-
-	return nil
-}
-
-func (devices *DeviceSetDM) createDevice(deviceId int) error {
-	task, err := devices.createTask(DeviceTargetMsg, devices.getPoolDevName())
-	if task == nil {
-		utils.Debugf("\n--->Err: %s\n", err)
-		return err
-	}
-
-	if err := task.SetSector(0); err != nil {
-		return fmt.Errorf("Can't set sector")
-	}
-
-	if err := task.SetMessage(fmt.Sprintf("create_thin %d", deviceId)); err != nil {
-		return fmt.Errorf("Can't set message")
-	}
-
-	if err := task.Run(); err != nil {
-		return fmt.Errorf("Error running createDevice")
-	}
-	return nil
-}
-
-func (devices *DeviceSetDM) createSnapDevice(deviceId int, baseInfo *DevInfo) error {
-	devinfo, _ := devices.getInfo(baseInfo.Name())
-	doSuspend := devinfo != nil && devinfo.Exists != 0
-
-	if doSuspend {
-		if err := devices.suspendDevice(baseInfo); err != nil {
-			utils.Debugf("\n--->Err: %s\n", err)
-			return err
-		}
-	}
-
-	task, err := devices.createTask(DeviceTargetMsg, devices.getPoolDevName())
-	if task == nil {
-		devices.resumeDevice(baseInfo)
-		utils.Debugf("\n--->Err: %s\n", err)
-		return err
-	}
-
-	if err := task.SetSector(0); err != nil {
-		devices.resumeDevice(baseInfo)
-		return fmt.Errorf("Can't set sector")
-	}
-
-	if err := task.SetMessage(fmt.Sprintf("create_snap %d %d", deviceId, baseInfo.DeviceId)); err != nil {
-		devices.resumeDevice(baseInfo)
-		return fmt.Errorf("Can't set message")
-	}
-
-	if err := task.Run(); err != nil {
-		devices.resumeDevice(baseInfo)
-		return fmt.Errorf("Error running DeviceCreate")
-	}
-
-	if doSuspend {
-		if err := devices.resumeDevice(baseInfo); err != nil {
-			utils.Debugf("\n--->Err: %s\n", err)
-			return err
-		}
-	}
-
-	return nil
-}
-
-func (devices *DeviceSetDM) deleteDevice(deviceId int) error {
-	task, err := devices.createTask(DeviceTargetMsg, devices.getPoolDevName())
-	if task == nil {
-		utils.Debugf("\n--->Err: %s\n", err)
-		return err
-	}
-
-	if err := task.SetSector(0); err != nil {
-		return fmt.Errorf("Can't set sector")
-	}
-
-	if err := task.SetMessage(fmt.Sprintf("delete %d", deviceId)); err != nil {
-		return fmt.Errorf("Can't set message")
-	}
-
-	if err := task.Run(); err != nil {
-		return fmt.Errorf("Error running deleteDevice")
-	}
-	return nil
-}
-
-func (devices *DeviceSetDM) removeDevice(name string) error {
-	task, err := devices.createTask(DeviceRemove, name)
-	if task == nil {
-		utils.Debugf("\n--->Err: %s\n", err)
-		return err
-	}
-	if err = task.Run(); err != nil {
-		return fmt.Errorf("Error running removeDevice")
-	}
-	return nil
-}
-
-func (devices *DeviceSetDM) activateDevice(info *DevInfo) error {
-	task, err := devices.createTask(DeviceCreate, info.Name())
-	if task == nil {
-		utils.Debugf("\n--->Err: %s\n", err)
-		return err
-	}
-
-	params := fmt.Sprintf("%s %d", devices.getPoolDevName(), info.DeviceId)
-	if err := task.AddTarget(0, info.Size/512, "thin", params); err != nil {
-		return fmt.Errorf("Can't add target")
-	}
-
-	var cookie uint32 = 0
-	if err := task.SetCookie(&cookie, 0); err != nil {
-		return fmt.Errorf("Can't set cookie")
-	}
-
-	if err := task.Run(); err != nil {
-		return fmt.Errorf("Error running DeviceCreate")
-	}
-
-	UdevWait(cookie)
-
-	return nil
-}
-
 func (devices *DeviceSetDM) allocateDeviceId() int {
 	// TODO: Add smarter reuse of deleted devices
 	id := devices.nextFreeDevice
@@ -412,7 +158,7 @@
 	}
 
 	if devices.NewTransactionId != devices.TransactionId {
-		if err = devices.setTransactionId(devices.TransactionId, devices.NewTransactionId); err != nil {
+		if err = setTransactionId(devices.getPoolDevName(), devices.TransactionId, devices.NewTransactionId); err != nil {
 			utils.Debugf("\n--->Err: %s\n", err)
 			return err
 		}
@@ -448,11 +194,11 @@
 		return fmt.Errorf("Unknown device %s", hash)
 	}
 
-	if devinfo, _ := devices.getInfo(info.Name()); devinfo != nil && devinfo.Exists != 0 {
+	if devinfo, _ := getInfo(info.Name()); devinfo != nil && devinfo.Exists != 0 {
 		return nil
 	}
 
-	return devices.activateDevice(info)
+	return activateDevice(devices.getPoolDevName(), info.Name(), info.DeviceId, info.Size)
 }
 
 func (devices *DeviceSetDM) createFilesystem(info *DevInfo) error {
@@ -470,7 +216,7 @@
 }
 
 func (devices *DeviceSetDM) loadMetaData() error {
-	_, _, _, params, err := devices.getStatus(devices.getPoolName())
+	_, _, _, params, err := getStatus(devices.getPoolName())
 	if err != nil {
 		utils.Debugf("\n--->Err: %s\n", err)
 		return err
@@ -521,7 +267,7 @@
 
 	if oldInfo != nil && !oldInfo.Initialized {
 		utils.Debugf("Removing uninitialized base image")
-		if err := devices.RemoveDevice(""); err != nil {
+		if err := devices.removeDevice(""); err != nil {
 			utils.Debugf("\n--->Err: %s\n", err)
 			return err
 		}
@@ -532,14 +278,14 @@
 	id := devices.allocateDeviceId()
 
 	// Create initial device
-	if err := devices.createDevice(id); err != nil {
+	if err := createDevice(devices.getPoolDevName(), id); err != nil {
 		utils.Debugf("\n--->Err: %s\n", err)
 		return err
 	}
 
 	info, err := devices.registerDevice(id, "", defaultBaseFsSize)
 	if err != nil {
-		_ = devices.deleteDevice(id)
+		_ = deleteDevice(devices.getPoolDevName(), id)
 		utils.Debugf("\n--->Err: %s\n", err)
 		return err
 	}
@@ -582,7 +328,7 @@
 }
 
 func (devices *DeviceSetDM) initDevmapper() error {
-	info, err := devices.getInfo(devices.getPoolName())
+	info, err := getInfo(devices.getPoolName())
 	if info == nil {
 		utils.Debugf("Error device getInfo: %s", err)
 		return err
@@ -636,7 +382,7 @@
 	}
 	defer metadataFile.Close()
 
-	if err := devices.createPool(dataFile, metadataFile); err != nil {
+	if err := createPool(devices.getPoolName(), dataFile, metadataFile); err != nil {
 		utils.Debugf("\n--->Err: %s\n", err)
 		return err
 	}
@@ -657,6 +403,9 @@
 }
 
 func (devices *DeviceSetDM) AddDevice(hash, baseHash string) error {
+	devices.Lock()
+	defer devices.Unlock()
+
 	if err := devices.ensureInit(); err != nil {
 		utils.Debugf("Error init: %s\n", err)
 		return err
@@ -674,33 +423,28 @@
 
 	deviceId := devices.allocateDeviceId()
 
-	if err := devices.createSnapDevice(deviceId, baseInfo); err != nil {
+	if err := devices.createSnapDevice(devices.getPoolDevName(), deviceId, baseInfo.Name(), baseInfo.DeviceId); err != nil {
 		utils.Debugf("Error creating snap device: %s\n", err)
 		return err
 	}
 
 	if _, err := devices.registerDevice(deviceId, hash, baseInfo.Size); err != nil {
-		devices.deleteDevice(deviceId)
+		deleteDevice(devices.getPoolDevName(), deviceId)
 		utils.Debugf("Error registering device: %s\n", err)
 		return err
 	}
 	return nil
 }
 
-func (devices *DeviceSetDM) RemoveDevice(hash string) error {
-	if err := devices.ensureInit(); err != nil {
-		utils.Debugf("\n--->Err: %s\n", err)
-		return err
-	}
-
+func (devices *DeviceSetDM) removeDevice(hash string) error {
 	info := devices.Devices[hash]
 	if info == nil {
 		return fmt.Errorf("hash %s doesn't exists", hash)
 	}
 
-	devinfo, _ := devices.getInfo(info.Name())
+	devinfo, _ := getInfo(info.Name())
 	if devinfo != nil && devinfo.Exists != 0 {
-		if err := devices.removeDevice(info.Name()); err != nil {
+		if err := removeDevice(info.Name()); err != nil {
 			utils.Debugf("Error removing device: %s\n", err)
 			return err
 		}
@@ -714,7 +458,7 @@
 		}
 	}
 
-	if err := devices.deleteDevice(info.DeviceId); err != nil {
+	if err := deleteDevice(devices.getPoolDevName(), info.DeviceId); err != nil {
 		utils.Debugf("Error deleting device: %s\n", err)
 		return err
 	}
@@ -731,24 +475,32 @@
 	return nil
 }
 
-func (devices *DeviceSetDM) DeactivateDevice(hash string) error {
+func (devices *DeviceSetDM) RemoveDevice(hash string) error {
+	devices.Lock()
+	defer devices.Unlock()
+
 	if err := devices.ensureInit(); err != nil {
 		utils.Debugf("\n--->Err: %s\n", err)
 		return err
 	}
 
+
+	return devices.removeDevice(hash)
+}
+
+func (devices *DeviceSetDM) deactivateDevice(hash string) error {
 	info := devices.Devices[hash]
 	if info == nil {
 		return fmt.Errorf("hash %s doesn't exists", hash)
 	}
 
-	devinfo, err := devices.getInfo(info.Name())
+	devinfo, err := getInfo(info.Name())
 	if err != nil {
 		utils.Debugf("\n--->Err: %s\n", err)
 		return err
 	}
 	if devinfo.Exists != 0 {
-		if err := devices.removeDevice(info.Name()); err != nil {
+		if err := removeDevice(info.Name()); err != nil {
 			utils.Debugf("\n--->Err: %s\n", err)
 			return err
 		}
@@ -757,7 +509,24 @@
 	return nil
 }
 
+func (devices *DeviceSetDM) DeactivateDevice(hash string) error {
+	devices.Lock()
+	defer devices.Unlock()
+
+	if err := devices.ensureInit(); err != nil {
+		utils.Debugf("\n--->Err: %s\n", err)
+		return err
+	}
+
+	utils.Debugf("DeactivateDevice %s", hash)
+	return devices.deactivateDevice(hash);
+}
+
+
 func (devices *DeviceSetDM) Shutdown() error {
+	devices.Lock()
+	defer devices.Unlock()
+
 	if !devices.initialized {
 		return nil
 	}
@@ -772,14 +541,14 @@
 	}
 
 	for _, d := range devices.Devices {
-		if err := devices.DeactivateDevice(d.Hash); err != nil {
+		if err := devices.deactivateDevice(d.Hash); err != nil {
 			utils.Debugf("Shutdown deactivate %s , error: %s\n", d.Hash, err)
 		}
 	}
 
 	pool := devices.getPoolDevName()
-	if devinfo, err := devices.getInfo(pool); err == nil && devinfo.Exists != 0 {
-		if err := devices.removeDevice(pool); err != nil {
+	if devinfo, err := getInfo(pool); err == nil && devinfo.Exists != 0 {
+		if err := removeDevice(pool); err != nil {
 			utils.Debugf("Shutdown deactivate %s , error: %s\n", pool, err)
 		}
 	}
@@ -788,6 +557,9 @@
 }
 
 func (devices *DeviceSetDM) MountDevice(hash, path string) error {
+	devices.Lock()
+	defer devices.Unlock()
+
 	if err := devices.ensureInit(); err != nil {
 		utils.Debugf("\n--->Err: %s\n", err)
 		return err
@@ -815,7 +587,10 @@
 	return nil
 }
 
-func (devices *DeviceSetDM) UnmountDevice(hash, path string) error {
+func (devices *DeviceSetDM) UnmountDevice(hash, path string, deactivate bool) error {
+	devices.Lock()
+	defer devices.Unlock()
+
 	if err := syscall.Unmount(path, 0); err != nil {
 		utils.Debugf("\n--->Err: %s\n", err)
 		return err
@@ -827,10 +602,17 @@
 		delete(devices.activeMounts, path)
 	}
 
+	if deactivate {
+		devices.deactivateDevice(hash)
+	}
+
 	return nil
 }
 
 func (devices *DeviceSetDM) HasDevice(hash string) bool {
+	devices.Lock()
+	defer devices.Unlock()
+
 	if err := devices.ensureInit(); err != nil {
 		return false
 	}
@@ -838,6 +620,9 @@
 }
 
 func (devices *DeviceSetDM) HasInitializedDevice(hash string) bool {
+	devices.Lock()
+	defer devices.Unlock()
+
 	if err := devices.ensureInit(); err != nil {
 		return false
 	}
@@ -847,6 +632,9 @@
 }
 
 func (devices *DeviceSetDM) HasActivatedDevice(hash string) bool {
+	devices.Lock()
+	defer devices.Unlock()
+
 	if err := devices.ensureInit(); err != nil {
 		return false
 	}
@@ -855,11 +643,14 @@
 	if info == nil {
 		return false
 	}
-	devinfo, _ := devices.getInfo(info.Name())
+	devinfo, _ := getInfo(info.Name())
 	return devinfo != nil && devinfo.Exists != 0
 }
 
 func (devices *DeviceSetDM) SetInitialized(hash string) error {
+	devices.Lock()
+	defer devices.Unlock()
+
 	if err := devices.ensureInit(); err != nil {
 		utils.Debugf("\n--->Err: %s\n", err)
 		return err
diff --git a/devmapper/devmapper.go b/devmapper/devmapper.go
index 02922e8..e4db906 100644
--- a/devmapper/devmapper.go
+++ b/devmapper/devmapper.go
@@ -14,6 +14,10 @@
 #include <linux/fs.h>
 #include <errno.h>
 
+#ifndef LOOP_CTL_GET_FREE
+#define LOOP_CTL_GET_FREE       0x4C82
+#endif
+
 char*			attach_loop_device(const char *filename, int *loop_fd_out)
 {
   struct loop_info64	loopinfo = {0};
@@ -56,19 +60,18 @@
     loop_fd = open(buf, O_RDWR);
     if (loop_fd < 0 && errno == ENOENT) {
       close(fd);
-      perror("open");
       fprintf (stderr, "no available loopback device!");
       return NULL;
     } else if (loop_fd < 0)
       continue;
 
     if (ioctl (loop_fd, LOOP_SET_FD, (void *)(size_t)fd) < 0) {
-      perror("ioctl");
+      int errsv = errno;
       close(loop_fd);
       loop_fd = -1;
-      if (errno != EBUSY) {
+      if (errsv != EBUSY) {
         close (fd);
-        fprintf (stderr, "cannot set up loopback device %s", buf);
+        fprintf (stderr, "cannot set up loopback device %s: %s", buf, strerror(errsv));
         return NULL;
       }
       continue;
@@ -388,3 +391,255 @@
 func free(p *C.char) {
 	C.free(unsafe.Pointer(p))
 }
+
+func createPool(poolName string, dataFile *os.File, metadataFile *os.File) error {
+	task, err := createTask(DeviceCreate, poolName)
+	if task == nil {
+		return err
+	}
+
+	size, err := GetBlockDeviceSize(dataFile)
+	if err != nil {
+		return fmt.Errorf("Can't get data size")
+	}
+
+	params := metadataFile.Name() + " " + dataFile.Name() + " 512 8192"
+	if err := task.AddTarget(0, size/512, "thin-pool", params); err != nil {
+		return fmt.Errorf("Can't add target")
+	}
+
+	var cookie uint32 = 0
+	if err := task.SetCookie(&cookie, 0); err != nil {
+		return fmt.Errorf("Can't set cookie")
+	}
+
+	if err := task.Run(); err != nil {
+		return fmt.Errorf("Error running DeviceCreate")
+	}
+
+	UdevWait(cookie)
+
+	return nil
+}
+
+func createTask(t TaskType, name string) (*Task, error) {
+	task := TaskCreate(t)
+	if task == nil {
+		return nil, fmt.Errorf("Can't create task of type %d", int(t))
+	}
+	if err := task.SetName(name); err != nil {
+		return nil, fmt.Errorf("Can't set task name %s", name)
+	}
+	return task, nil
+}
+
+func getInfo(name string) (*Info, error) {
+	task, err := createTask(DeviceInfo, name)
+	if task == nil {
+		return nil, err
+	}
+	if err := task.Run(); err != nil {
+		return nil, err
+	}
+	return task.GetInfo()
+}
+
+func getStatus(name string) (uint64, uint64, string, string, error) {
+	task, err := createTask(DeviceStatus, name)
+	if task == nil {
+		utils.Debugf("getStatus: Error createTask: %s", err)
+		return 0, 0, "", "", err
+	}
+	if err := task.Run(); err != nil {
+		utils.Debugf("getStatus: Error Run: %s", err)
+		return 0, 0, "", "", err
+	}
+
+	devinfo, err := task.GetInfo()
+	if err != nil {
+		utils.Debugf("getStatus: Error GetInfo: %s", err)
+		return 0, 0, "", "", err
+	}
+	if devinfo.Exists == 0 {
+		utils.Debugf("getStatus: Non existing device %s", name)
+		return 0, 0, "", "", fmt.Errorf("Non existing device %s", name)
+	}
+
+	_, start, length, target_type, params := task.GetNextTarget(0)
+	return start, length, target_type, params, nil
+}
+
+func setTransactionId(poolName string, oldId uint64, newId uint64) error {
+	task, err := createTask(DeviceTargetMsg, poolName)
+	if task == nil {
+		return err
+	}
+
+	if err := task.SetSector(0); err != nil {
+		return fmt.Errorf("Can't set sector")
+	}
+
+	if err := task.SetMessage(fmt.Sprintf("set_transaction_id %d %d", oldId, newId)); err != nil {
+		return fmt.Errorf("Can't set message")
+	}
+
+	if err := task.Run(); err != nil {
+		return fmt.Errorf("Error running setTransactionId")
+	}
+	return nil
+}
+
+func suspendDevice(name string) error {
+	task, err := createTask(DeviceSuspend, name)
+	if task == nil {
+		return err
+	}
+	if err := task.Run(); err != nil {
+		return fmt.Errorf("Error running DeviceSuspend")
+	}
+	return nil
+}
+
+func resumeDevice(name string) error {
+	task, err := createTask(DeviceResume, name)
+	if task == nil {
+		return err
+	}
+
+	var cookie uint32 = 0
+	if err := task.SetCookie(&cookie, 0); err != nil {
+		return fmt.Errorf("Can't set cookie")
+	}
+
+	if err := task.Run(); err != nil {
+		return fmt.Errorf("Error running DeviceSuspend")
+	}
+
+	UdevWait(cookie)
+
+	return nil
+}
+
+func createDevice(poolName string, deviceId int) error {
+	task, err := createTask(DeviceTargetMsg, poolName)
+	if task == nil {
+		return err
+	}
+
+	if err := task.SetSector(0); err != nil {
+		return fmt.Errorf("Can't set sector")
+	}
+
+	if err := task.SetMessage(fmt.Sprintf("create_thin %d", deviceId)); err != nil {
+		return fmt.Errorf("Can't set message")
+	}
+
+	if err := task.Run(); err != nil {
+		return fmt.Errorf("Error running createDevice")
+	}
+	return nil
+}
+
+func deleteDevice(poolName string, deviceId int) error {
+	task, err := createTask(DeviceTargetMsg, poolName)
+	if task == nil {
+		return err
+	}
+
+	if err := task.SetSector(0); err != nil {
+		return fmt.Errorf("Can't set sector")
+	}
+
+	if err := task.SetMessage(fmt.Sprintf("delete %d", deviceId)); err != nil {
+		return fmt.Errorf("Can't set message")
+	}
+
+	if err := task.Run(); err != nil {
+		return fmt.Errorf("Error running deleteDevice")
+	}
+	return nil
+}
+
+func removeDevice(name string) error {
+	task, err := createTask(DeviceRemove, name)
+	if task == nil {
+		return err
+	}
+	if err = task.Run(); err != nil {
+		return fmt.Errorf("Error running removeDevice")
+	}
+	return nil
+}
+
+func activateDevice(poolName string, name string, deviceId int, size uint64) error {
+	task, err := createTask(DeviceCreate, name)
+	if task == nil {
+		return err
+	}
+
+	params := fmt.Sprintf("%s %d", poolName, deviceId)
+	if err := task.AddTarget(0, size/512, "thin", params); err != nil {
+		return fmt.Errorf("Can't add target")
+	}
+
+	var cookie uint32 = 0
+	if err := task.SetCookie(&cookie, 0); err != nil {
+		return fmt.Errorf("Can't set cookie")
+	}
+
+	if err := task.Run(); err != nil {
+		return fmt.Errorf("Error running DeviceCreate")
+	}
+
+	UdevWait(cookie)
+
+	return nil
+}
+
+func (devices *DeviceSetDM) 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
+		}
+	}
+
+	task, err := createTask(DeviceTargetMsg, poolName)
+	if task == nil {
+		if doSuspend {
+			resumeDevice(baseName)
+		}
+		return err
+	}
+
+	if err := task.SetSector(0); err != nil {
+		if doSuspend {
+			resumeDevice(baseName)
+		}
+		return fmt.Errorf("Can't set sector")
+	}
+
+	if err := task.SetMessage(fmt.Sprintf("create_snap %d %d", deviceId, baseDeviceId)); err != nil {
+		if doSuspend {
+			resumeDevice(baseName)
+		}
+		return fmt.Errorf("Can't set message")
+	}
+
+	if err := task.Run(); err != nil {
+		if doSuspend {
+			resumeDevice(baseName)
+		}
+		return fmt.Errorf("Error running DeviceCreate")
+	}
+
+	if doSuspend {
+		if err := resumeDevice(baseName); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
diff --git a/image.go b/image.go
index a937c74..6a1191e 100644
--- a/image.go
+++ b/image.go
@@ -391,14 +391,14 @@
 
 	if err := ioutil.WriteFile(path.Join(mountDir, ".docker-id"), []byte(image.ID), 0600); err != nil {
 		utils.Debugf("Error writing file: %s", err)
-		devices.UnmountDevice(image.ID, mountDir)
+		devices.UnmountDevice(image.ID, mountDir, true)
 		devices.RemoveDevice(image.ID)
 		return err
 	}
 
 	if err = image.applyLayer(layerPath(root), mountDir); err != nil {
 		utils.Debugf("Error applying layer: %s", err)
-		devices.UnmountDevice(image.ID, mountDir)
+		devices.UnmountDevice(image.ID, mountDir, true)
 		devices.RemoveDevice(image.ID)
 		return err
 	}
@@ -411,28 +411,24 @@
 	// part of the container changes
 	dockerinitLayer, err := image.getDockerInitLayer()
 	if err != nil {
-		devices.UnmountDevice(image.ID, mountDir)
+		devices.UnmountDevice(image.ID, mountDir, true)
 		devices.RemoveDevice(image.ID)
 		return err
 	}
 
 	if err := image.applyLayer(dockerinitLayer, mountDir); err != nil {
-		devices.UnmountDevice(image.ID, mountDir)
+		devices.UnmountDevice(image.ID, mountDir, true)
 		devices.RemoveDevice(image.ID)
 		return err
 	}
 
-	if err := devices.UnmountDevice(image.ID, mountDir); err != nil {
+	if err := devices.UnmountDevice(image.ID, mountDir, true); err != nil {
 		devices.RemoveDevice(image.ID)
 		return err
 	}
 
 	devices.SetInitialized(image.ID)
 
-	// No need to the device-mapper device to hang around once we've written
-	// the image, it can be enabled on-demand when needed
-	devices.DeactivateDevice(image.ID)
-
 	return nil
 }
 
@@ -491,11 +487,11 @@
 		return err
 	}
 
-	if err = devices.UnmountDevice(id, root); err != nil {
+	if err = devices.UnmountDevice(id, root, true); err != nil {
 		return err
 	}
 
-	return devices.DeactivateDevice(id)
+	return nil
 }
 
 func (image *Image) Changes(runtime *Runtime, root, rw, id string) ([]Change, error) {
@@ -518,10 +514,7 @@
 	}
 
 	changes, err := ChangesDirs(root, rw)
-	devices.UnmountDevice(image.ID, rw)
-	if !wasActivated {
-		devices.DeactivateDevice(image.ID)
-	}
+	devices.UnmountDevice(image.ID, rw, !wasActivated)
 	if err != nil {
 		return nil, err
 	}
diff --git a/utils_test.go b/utils_test.go
index 8348bc4..1232c1f 100644
--- a/utils_test.go
+++ b/utils_test.go
@@ -445,8 +445,8 @@
 	return wrapper.wrapped.MountDevice(wrapper.wrap(hash), path)
 }
 
-func (wrapper *DeviceSetWrapper) UnmountDevice(hash, path string) error {
-	return wrapper.wrapped.UnmountDevice(wrapper.wrap(hash), path)
+func (wrapper *DeviceSetWrapper) UnmountDevice(hash, path string, deactivate bool) error {
+	return wrapper.wrapped.UnmountDevice(wrapper.wrap(hash), path, deactivate)
 }
 
 func (wrapper *DeviceSetWrapper) HasDevice(hash string) bool {