[lxc-devel] [lxd/master] Storage cleanup preparation

monstermunchkin on Github lxc-bot at linuxcontainers.org
Thu Aug 15 13:18:36 UTC 2019


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 311 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20190815/540d3f7f/attachment-0001.bin>
-------------- next part --------------
From 8455cc8273c0a62c3d381ae7c7c0ce1f72f40521 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Thu, 2 May 2019 15:04:28 +0200
Subject: [PATCH 1/4] lxd: Move storage cgo to storage package

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 lxd/{ => storage}/storage_cgo.go | 16 +++++++++-------
 lxd/storage_btrfs.go             |  3 ++-
 lxd/storage_lvm.go               |  7 ++++---
 3 files changed, 15 insertions(+), 11 deletions(-)
 rename lxd/{ => storage}/storage_cgo.go (93%)

diff --git a/lxd/storage_cgo.go b/lxd/storage/storage_cgo.go
similarity index 93%
rename from lxd/storage_cgo.go
rename to lxd/storage/storage_cgo.go
index 7edba24f92..437e67ef1b 100644
--- a/lxd/storage_cgo.go
+++ b/lxd/storage/storage_cgo.go
@@ -1,7 +1,7 @@
 // +build linux
 // +build cgo
 
-package main
+package storage
 
 /*
 #define _GNU_SOURCE
@@ -19,8 +19,8 @@ package main
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#include "include/macro.h"
-#include "include/memory_utils.h"
+#include "../include/macro.h"
+#include "../include/memory_utils.h"
 
 #define LXD_MAXPATH 4096
 #define LXD_NUMSTRLEN64 21
@@ -256,10 +256,10 @@ import (
 // close.
 const LoFlagsAutoclear int = C.LO_FLAGS_AUTOCLEAR
 
-// prepareLoopDev() detects and sets up a loop device for source. It returns an
+// PrepareLoopDev detects and sets up a loop device for source. It returns an
 // open file descriptor to the free loop device and the path of the free loop
 // device. It's the callers responsibility to close the open file descriptor.
-func prepareLoopDev(source string, flags int) (*os.File, error) {
+func PrepareLoopDev(source string, flags int) (*os.File, error) {
 	cLoopDev := C.malloc(C.size_t(C.LO_NAME_SIZE))
 	if cLoopDev == nil {
 		return nil, fmt.Errorf("Failed to allocate memory in C")
@@ -285,7 +285,8 @@ func prepareLoopDev(source string, flags int) (*os.File, error) {
 	return os.NewFile(uintptr(loopFd), C.GoString((*C.char)(cLoopDev))), nil
 }
 
-func setAutoclearOnLoopDev(loopFd int) error {
+// SetAutoclearOnLoopDev enables autodestruction of the provided loopback device.
+func SetAutoclearOnLoopDev(loopFd int) error {
 	ret, err := C.set_autoclear_loop_device(C.int(loopFd))
 	if ret < 0 {
 		if err != nil {
@@ -297,7 +298,8 @@ func setAutoclearOnLoopDev(loopFd int) error {
 	return nil
 }
 
-func unsetAutoclearOnLoopDev(loopFd int) error {
+// UnsetAutoclearOnLoopDev disables autodestruction of the provided loopback device.
+func UnsetAutoclearOnLoopDev(loopFd int) error {
 	ret, err := C.unset_autoclear_loop_device(C.int(loopFd))
 	if ret < 0 {
 		if err != nil {
diff --git a/lxd/storage_btrfs.go b/lxd/storage_btrfs.go
index 043b2f7aad..66d541a9ab 100644
--- a/lxd/storage_btrfs.go
+++ b/lxd/storage_btrfs.go
@@ -19,6 +19,7 @@ import (
 	"github.com/lxc/lxd/lxd/migration"
 	"github.com/lxc/lxd/lxd/project"
 	"github.com/lxc/lxd/lxd/state"
+	driver "github.com/lxc/lxd/lxd/storage"
 	"github.com/lxc/lxd/lxd/util"
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/api"
@@ -422,7 +423,7 @@ func (s *storageBtrfs) StoragePoolMount() (bool, error) {
 			// Since we mount the loop device LO_FLAGS_AUTOCLEAR is
 			// fine since the loop device will be kept around for as
 			// long as the mount exists.
-			loopF, loopErr := prepareLoopDev(source, LoFlagsAutoclear)
+			loopF, loopErr := driver.PrepareLoopDev(source, driver.LoFlagsAutoclear)
 			if loopErr != nil {
 				return false, loopErr
 			}
diff --git a/lxd/storage_lvm.go b/lxd/storage_lvm.go
index d805035435..97ece383c6 100644
--- a/lxd/storage_lvm.go
+++ b/lxd/storage_lvm.go
@@ -15,6 +15,7 @@ import (
 
 	"github.com/lxc/lxd/lxd/migration"
 	"github.com/lxc/lxd/lxd/project"
+	driver "github.com/lxc/lxd/lxd/storage"
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/api"
 	"github.com/lxc/lxd/shared/ioprogress"
@@ -387,7 +388,7 @@ func (s *storageLvm) StoragePoolDelete() error {
 	if s.loopInfo != nil {
 		// Set LO_FLAGS_AUTOCLEAR before we remove the loop file
 		// otherwise we will get EBADF.
-		err = setAutoclearOnLoopDev(int(s.loopInfo.Fd()))
+		err = driver.SetAutoclearOnLoopDev(int(s.loopInfo.Fd()))
 		if err != nil {
 			logger.Warnf("Failed to set LO_FLAGS_AUTOCLEAR on loop device: %s, manual cleanup needed", err)
 		}
@@ -459,12 +460,12 @@ func (s *storageLvm) StoragePoolMount() (bool, error) {
 
 	if filepath.IsAbs(source) && !shared.IsBlockdevPath(source) {
 		// Try to prepare new loop device.
-		loopF, loopErr := prepareLoopDev(source, 0)
+		loopF, loopErr := driver.PrepareLoopDev(source, 0)
 		if loopErr != nil {
 			return false, loopErr
 		}
 		// Make sure that LO_FLAGS_AUTOCLEAR is unset.
-		loopErr = unsetAutoclearOnLoopDev(int(loopF.Fd()))
+		loopErr = driver.UnsetAutoclearOnLoopDev(int(loopF.Fd()))
 		if loopErr != nil {
 			return false, loopErr
 		}

From 3faeabb253bcbf28b08198c7a9e4508414ea69b8 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Tue, 13 Aug 2019 15:04:10 +0200
Subject: [PATCH 2/4] lxd: Move ContainerPath() to storage package

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 lxd/api_internal.go    | 10 +++++-----
 lxd/container.go       |  8 --------
 lxd/container_lxc.go   |  3 ++-
 lxd/container_test.go  |  4 ++--
 lxd/storage/storage.go | 12 ++++++++++++
 lxd/storage_dir.go     |  3 ++-
 lxd/storage_zfs.go     |  3 ++-
 7 files changed, 25 insertions(+), 18 deletions(-)
 create mode 100644 lxd/storage/storage.go

diff --git a/lxd/api_internal.go b/lxd/api_internal.go
index fee4d9ebfa..babac2c8d6 100644
--- a/lxd/api_internal.go
+++ b/lxd/api_internal.go
@@ -9,6 +9,7 @@ import (
 	"os"
 	"path/filepath"
 	"runtime"
+	runtimeDebug "runtime/debug"
 	"strconv"
 	"strings"
 
@@ -21,13 +22,12 @@ import (
 	"github.com/lxc/lxd/lxd/db/node"
 	"github.com/lxc/lxd/lxd/db/query"
 	"github.com/lxc/lxd/lxd/project"
+	driver "github.com/lxc/lxd/lxd/storage"
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/api"
+	log "github.com/lxc/lxd/shared/log15"
 	"github.com/lxc/lxd/shared/logger"
 	"github.com/lxc/lxd/shared/osarch"
-
-	log "github.com/lxc/lxd/shared/log15"
-	runtimeDebug "runtime/debug"
 )
 
 var apiInternal = []APIEndpoint{
@@ -680,7 +680,7 @@ func internalImport(d *Daemon, r *http.Request) Response {
 				onDiskPoolName = poolName
 			}
 			snapName := fmt.Sprintf("%s/%s", req.Name, od)
-			snapPath := containerPath(snapName, true)
+			snapPath := driver.ContainerPath(snapName, true)
 			err = lvmContainerDeleteInternal(projectName, poolName, req.Name,
 				true, onDiskPoolName, snapPath)
 		case "ceph":
@@ -1022,7 +1022,7 @@ func internalImport(d *Daemon, r *http.Request) Response {
 		return SmartError(err)
 	}
 
-	containerPath := containerPath(project.Prefix(projectName, req.Name), false)
+	containerPath := driver.ContainerPath(project.Prefix(projectName, req.Name), false)
 	isPrivileged := false
 	if backup.Container.Config["security.privileged"] == "" {
 		isPrivileged = true
diff --git a/lxd/container.go b/lxd/container.go
index 5b795f3d55..f01f2bc58d 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -75,14 +75,6 @@ func containerGetParentAndSnapshotName(name string) (string, string, bool) {
 	return fields[0], fields[1], true
 }
 
-func containerPath(name string, isSnapshot bool) string {
-	if isSnapshot {
-		return shared.VarPath("snapshots", name)
-	}
-
-	return shared.VarPath("containers", name)
-}
-
 func containerValidName(name string) error {
 	if strings.Contains(name, shared.SnapshotDelimiter) {
 		return fmt.Errorf(
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 6a97f78242..95400bdb39 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -35,6 +35,7 @@ import (
 	"github.com/lxc/lxd/lxd/maas"
 	"github.com/lxc/lxd/lxd/project"
 	"github.com/lxc/lxd/lxd/state"
+	driver "github.com/lxc/lxd/lxd/storage"
 	"github.com/lxc/lxd/lxd/template"
 	"github.com/lxc/lxd/lxd/util"
 	"github.com/lxc/lxd/shared"
@@ -7858,7 +7859,7 @@ func (c *containerLXC) State() string {
 // Various container paths
 func (c *containerLXC) Path() string {
 	name := project.Prefix(c.Project(), c.Name())
-	return containerPath(name, c.IsSnapshot())
+	return driver.ContainerPath(name, c.IsSnapshot())
 }
 
 func (c *containerLXC) DevicesPath() string {
diff --git a/lxd/container_test.go b/lxd/container_test.go
index 4bbebde328..6b0d946f1c 100644
--- a/lxd/container_test.go
+++ b/lxd/container_test.go
@@ -163,7 +163,7 @@ func (suite *containerTestSuite) TestContainer_Path_Regular() {
 
 	suite.Req.False(c.IsSnapshot(), "Shouldn't be a snapshot.")
 	suite.Req.Equal(shared.VarPath("containers", "testFoo"), c.Path())
-	suite.Req.Equal(shared.VarPath("containers", "testFoo2"), containerPath("testFoo2", false))
+	suite.Req.Equal(shared.VarPath("containers", "testFoo2"), driver.ContainerPath("testFoo2", false))
 }
 
 func (suite *containerTestSuite) TestContainer_Path_Snapshot() {
@@ -184,7 +184,7 @@ func (suite *containerTestSuite) TestContainer_Path_Snapshot() {
 		c.Path())
 	suite.Req.Equal(
 		shared.VarPath("snapshots", "test", "snap1"),
-		containerPath("test/snap1", true))
+		driver.ContainerPath("test/snap1", true))
 }
 
 func (suite *containerTestSuite) TestContainer_LogPath() {
diff --git a/lxd/storage/storage.go b/lxd/storage/storage.go
new file mode 100644
index 0000000000..7d378ef50c
--- /dev/null
+++ b/lxd/storage/storage.go
@@ -0,0 +1,12 @@
+package storage
+
+import "github.com/lxc/lxd/shared"
+
+// ContainerPath returns the directory of a container or snapshot.
+func ContainerPath(name string, isSnapshot bool) string {
+	if isSnapshot {
+		return shared.VarPath("snapshots", name)
+	}
+
+	return shared.VarPath("containers", name)
+}
diff --git a/lxd/storage_dir.go b/lxd/storage_dir.go
index 967a4656f1..46482a01aa 100644
--- a/lxd/storage_dir.go
+++ b/lxd/storage_dir.go
@@ -14,6 +14,7 @@ import (
 
 	"github.com/lxc/lxd/lxd/migration"
 	"github.com/lxc/lxd/lxd/project"
+	driver "github.com/lxc/lxd/lxd/storage"
 	"github.com/lxc/lxd/lxd/storage/quota"
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/api"
@@ -1221,7 +1222,7 @@ func (s *storageDir) ContainerBackupLoad(info backupInfo, data io.ReadSeeker, ta
 
 	// Create mountpoints
 	containerMntPoint := getContainerMountPoint(info.Project, s.pool.Name, info.Name)
-	err = createContainerMountpoint(containerMntPoint, containerPath(project.Prefix(info.Project, info.Name), false), info.Privileged)
+	err = createContainerMountpoint(containerMntPoint, driver.ContainerPath(project.Prefix(info.Project, info.Name), false), info.Privileged)
 	if err != nil {
 		return errors.Wrap(err, "Create container mount point")
 	}
diff --git a/lxd/storage_zfs.go b/lxd/storage_zfs.go
index 5d9500b9b8..d98f530956 100644
--- a/lxd/storage_zfs.go
+++ b/lxd/storage_zfs.go
@@ -16,6 +16,7 @@ import (
 
 	"github.com/lxc/lxd/lxd/migration"
 	"github.com/lxc/lxd/lxd/project"
+	driver "github.com/lxc/lxd/lxd/storage"
 	"github.com/lxc/lxd/lxd/util"
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/api"
@@ -2131,7 +2132,7 @@ func (s *storageZfs) ContainerBackupCreate(backup backup, source container) erro
 func (s *storageZfs) doContainerBackupLoadOptimized(info backupInfo, data io.ReadSeeker, tarArgs []string) error {
 	containerName, _, _ := containerGetParentAndSnapshotName(info.Name)
 	containerMntPoint := getContainerMountPoint(info.Project, s.pool.Name, containerName)
-	err := createContainerMountpoint(containerMntPoint, containerPath(info.Name, false), info.Privileged)
+	err := createContainerMountpoint(containerMntPoint, driver.ContainerPath(info.Name, false), info.Privileged)
 	if err != nil {
 		return err
 	}

From b2405f5831ae5694f742d2f3177a6e3c55218355 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Thu, 15 Aug 2019 12:16:59 +0200
Subject: [PATCH 3/4] lxd: Move storage_utils to storage/utils

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 lxd/container_test.go           |   4 +-
 lxd/patches.go                  |  18 +-
 lxd/storage/utils.go            | 362 ++++++++++++++++++++++++++++++++
 lxd/storage_btrfs.go            |  36 ++--
 lxd/storage_ceph.go             |  31 +--
 lxd/storage_ceph_utils.go       |  21 +-
 lxd/storage_cephfs.go           |  15 +-
 lxd/storage_dir.go              |   2 +-
 lxd/storage_lvm.go              |  34 +--
 lxd/storage_lvm_utils.go        |   9 +-
 lxd/storage_pools_utils.go      |  13 +-
 lxd/storage_utils.go            | 339 +-----------------------------
 lxd/storage_volumes_snapshot.go |   3 +-
 lxd/storage_zfs.go              |  30 +--
 lxd/storage_zfs_utils.go        |   5 +-
 15 files changed, 480 insertions(+), 442 deletions(-)
 create mode 100644 lxd/storage/utils.go

diff --git a/lxd/container_test.go b/lxd/container_test.go
index 6b0d946f1c..d6698cbd16 100644
--- a/lxd/container_test.go
+++ b/lxd/container_test.go
@@ -4,12 +4,14 @@ import (
 	"fmt"
 	"testing"
 
+	"github.com/stretchr/testify/suite"
+
 	"github.com/lxc/lxd/lxd/db"
 	"github.com/lxc/lxd/lxd/device/config"
+	driver "github.com/lxc/lxd/lxd/storage"
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/api"
 	"github.com/lxc/lxd/shared/idmap"
-	"github.com/stretchr/testify/suite"
 )
 
 type containerTestSuite struct {
diff --git a/lxd/patches.go b/lxd/patches.go
index f6023eab95..b876b590de 100644
--- a/lxd/patches.go
+++ b/lxd/patches.go
@@ -10,14 +10,16 @@ import (
 	"strings"
 	"syscall"
 
+	"github.com/pkg/errors"
+	"golang.org/x/sys/unix"
+
 	"github.com/lxc/lxd/lxd/cluster"
 	"github.com/lxc/lxd/lxd/db"
 	"github.com/lxc/lxd/lxd/db/query"
+	driver "github.com/lxc/lxd/lxd/storage"
 	"github.com/lxc/lxd/shared"
 	log "github.com/lxc/lxd/shared/log15"
 	"github.com/lxc/lxd/shared/logger"
-	"github.com/pkg/errors"
-	"golang.org/x/sys/unix"
 )
 
 /* Patches are one-time actions that are sometimes needed to update
@@ -1088,7 +1090,7 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d
 		// Unmount the logical volume.
 		oldContainerMntPoint := shared.VarPath("containers", ct)
 		if shared.IsMountPoint(oldContainerMntPoint) {
-			err := tryUnmount(oldContainerMntPoint, unix.MNT_DETACH)
+			err := driver.TryUnmount(oldContainerMntPoint, unix.MNT_DETACH)
 			if err != nil {
 				logger.Errorf("Failed to unmount LVM logical volume \"%s\": %s", oldContainerMntPoint, err)
 				return err
@@ -1263,7 +1265,7 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d
 				if shared.PathExists(oldLvDevPath) {
 					// Unmount the logical volume.
 					if shared.IsMountPoint(oldSnapshotMntPoint) {
-						err := tryUnmount(oldSnapshotMntPoint, unix.MNT_DETACH)
+						err := driver.TryUnmount(oldSnapshotMntPoint, unix.MNT_DETACH)
 						if err != nil {
 							logger.Errorf("Failed to unmount LVM logical volume \"%s\": %s", oldSnapshotMntPoint, err)
 							return err
@@ -1366,7 +1368,7 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d
 		}
 
 		if !shared.IsMountPoint(newContainerMntPoint) {
-			err := tryMount(containerLvDevPath, newContainerMntPoint, lvFsType, 0, mountOptions)
+			err := driver.TryMount(containerLvDevPath, newContainerMntPoint, lvFsType, 0, mountOptions)
 			if err != nil {
 				logger.Errorf("Failed to mount LVM logical \"%s\" onto \"%s\" : %s", containerLvDevPath, newContainerMntPoint, err)
 				return err
@@ -1414,7 +1416,7 @@ func upgradeFromStorageTypeLvm(name string, d *Daemon, defaultPoolName string, d
 		// Unmount the logical volume.
 		oldImageMntPoint := shared.VarPath("images", img+".lv")
 		if shared.IsMountPoint(oldImageMntPoint) {
-			err := tryUnmount(oldImageMntPoint, unix.MNT_DETACH)
+			err := driver.TryUnmount(oldImageMntPoint, unix.MNT_DETACH)
 			if err != nil {
 				return err
 			}
@@ -1614,7 +1616,7 @@ func upgradeFromStorageTypeZfs(name string, d *Daemon, defaultPoolName string, d
 			_, err := shared.TryRunCommand("zfs", "unmount", "-f", ctDataset)
 			if err != nil {
 				logger.Warnf("Failed to unmount ZFS filesystem via zfs unmount, trying lazy umount (MNT_DETACH)...")
-				err := tryUnmount(oldContainerMntPoint, unix.MNT_DETACH)
+				err := driver.TryUnmount(oldContainerMntPoint, unix.MNT_DETACH)
 				if err != nil {
 					failedUpgradeEntities = append(failedUpgradeEntities, fmt.Sprintf("containers/%s: Failed to umount zfs filesystem.", ct))
 					continue
@@ -1762,7 +1764,7 @@ func upgradeFromStorageTypeZfs(name string, d *Daemon, defaultPoolName string, d
 			_, err := shared.TryRunCommand("zfs", "unmount", "-f", imageDataset)
 			if err != nil {
 				logger.Warnf("Failed to unmount ZFS filesystem via zfs unmount, trying lazy umount (MNT_DETACH)...")
-				err := tryUnmount(oldImageMntPoint, unix.MNT_DETACH)
+				err := driver.TryUnmount(oldImageMntPoint, unix.MNT_DETACH)
 				if err != nil {
 					logger.Warnf("Failed to unmount ZFS filesystem: %s", err)
 				}
diff --git a/lxd/storage/utils.go b/lxd/storage/utils.go
new file mode 100644
index 0000000000..d23d4f4171
--- /dev/null
+++ b/lxd/storage/utils.go
@@ -0,0 +1,362 @@
+package storage
+
+import (
+	"fmt"
+	"os"
+	"strings"
+	"time"
+
+	"golang.org/x/sys/unix"
+
+	"github.com/lxc/lxd/lxd/db"
+	"github.com/lxc/lxd/shared"
+	"github.com/lxc/lxd/shared/api"
+	"github.com/lxc/lxd/shared/logger"
+)
+
+// MkfsOptions represents options for filesystem creation.
+type MkfsOptions struct {
+	Label string
+}
+
+// Export the mount options map since we might find it useful in other parts of
+// LXD.
+type mountOptions struct {
+	capture bool
+	flag    uintptr
+}
+
+// MountOptions represents a list of possible mount options.
+var MountOptions = map[string]mountOptions{
+	"async":         {false, unix.MS_SYNCHRONOUS},
+	"atime":         {false, unix.MS_NOATIME},
+	"bind":          {true, unix.MS_BIND},
+	"defaults":      {true, 0},
+	"dev":           {false, unix.MS_NODEV},
+	"diratime":      {false, unix.MS_NODIRATIME},
+	"dirsync":       {true, unix.MS_DIRSYNC},
+	"exec":          {false, unix.MS_NOEXEC},
+	"lazytime":      {true, unix.MS_LAZYTIME},
+	"mand":          {true, unix.MS_MANDLOCK},
+	"noatime":       {true, unix.MS_NOATIME},
+	"nodev":         {true, unix.MS_NODEV},
+	"nodiratime":    {true, unix.MS_NODIRATIME},
+	"noexec":        {true, unix.MS_NOEXEC},
+	"nomand":        {false, unix.MS_MANDLOCK},
+	"norelatime":    {false, unix.MS_RELATIME},
+	"nostrictatime": {false, unix.MS_STRICTATIME},
+	"nosuid":        {true, unix.MS_NOSUID},
+	"rbind":         {true, unix.MS_BIND | unix.MS_REC},
+	"relatime":      {true, unix.MS_RELATIME},
+	"remount":       {true, unix.MS_REMOUNT},
+	"ro":            {true, unix.MS_RDONLY},
+	"rw":            {false, unix.MS_RDONLY},
+	"strictatime":   {true, unix.MS_STRICTATIME},
+	"suid":          {false, unix.MS_NOSUID},
+	"sync":          {true, unix.MS_SYNCHRONOUS},
+}
+
+// LXDResolveMountoptions resolves the provided mount options.
+func LXDResolveMountoptions(options string) (uintptr, string) {
+	mountFlags := uintptr(0)
+	tmp := strings.SplitN(options, ",", -1)
+	for i := 0; i < len(tmp); i++ {
+		opt := tmp[i]
+		do, ok := MountOptions[opt]
+		if !ok {
+			continue
+		}
+
+		if do.capture {
+			mountFlags |= do.flag
+		} else {
+			mountFlags &= ^do.flag
+		}
+
+		copy(tmp[i:], tmp[i+1:])
+		tmp[len(tmp)-1] = ""
+		tmp = tmp[:len(tmp)-1]
+		i--
+	}
+
+	return mountFlags, strings.Join(tmp, ",")
+}
+
+// TryMount tries mounting a filesystem multiple times. This is useful for unreliable backends.
+func TryMount(src string, dst string, fs string, flags uintptr, options string) error {
+	var err error
+
+	for i := 0; i < 20; i++ {
+		err = unix.Mount(src, dst, fs, flags, options)
+		if err == nil {
+			break
+		}
+
+		time.Sleep(500 * time.Millisecond)
+	}
+
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// TryUnmount tries unmounting a filesystem multiple times. This is useful for unreliable backends.
+func TryUnmount(path string, flags int) error {
+	var err error
+
+	for i := 0; i < 20; i++ {
+		err = unix.Unmount(path, flags)
+		if err == nil {
+			break
+		}
+
+		time.Sleep(500 * time.Millisecond)
+	}
+
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// ValidName validates the provided name, and returns an error if it's not a valid storage name.
+func ValidName(value string) error {
+	if strings.Contains(value, "/") {
+		return fmt.Errorf("Invalid storage volume name \"%s\". Storage volumes cannot contain \"/\" in their name", value)
+	}
+
+	return nil
+}
+
+// ConfigDiff returns a diff of the provided configs. Additionally, it returns whether or not
+// only user properties have been changed.
+func ConfigDiff(oldConfig map[string]string, newConfig map[string]string) ([]string, bool) {
+	changedConfig := []string{}
+	userOnly := true
+	for key := range oldConfig {
+		if oldConfig[key] != newConfig[key] {
+			if !strings.HasPrefix(key, "user.") {
+				userOnly = false
+			}
+
+			if !shared.StringInSlice(key, changedConfig) {
+				changedConfig = append(changedConfig, key)
+			}
+		}
+	}
+
+	for key := range newConfig {
+		if oldConfig[key] != newConfig[key] {
+			if !strings.HasPrefix(key, "user.") {
+				userOnly = false
+			}
+
+			if !shared.StringInSlice(key, changedConfig) {
+				changedConfig = append(changedConfig, key)
+			}
+		}
+	}
+
+	// Skip on no change
+	if len(changedConfig) == 0 {
+		return nil, false
+	}
+
+	return changedConfig, userOnly
+}
+
+// StoragePoolsDirMode represents the default permissions for the storage pool directory.
+const StoragePoolsDirMode os.FileMode = 0711
+
+// ContainersDirMode represents the default permissions for the containers directory.
+const ContainersDirMode os.FileMode = 0711
+
+// CustomDirMode represents the default permissions for the custom directory.
+const CustomDirMode os.FileMode = 0711
+
+// ImagesDirMode represents the default permissions for the images directory.
+const ImagesDirMode os.FileMode = 0700
+
+// SnapshotsDirMode represents the default permissions for the snapshots directory.
+const SnapshotsDirMode os.FileMode = 0700
+
+// LXDUsesPool detect whether LXD already uses the given storage pool.
+func LXDUsesPool(dbObj *db.Cluster, onDiskPoolName string, driver string, onDiskProperty string) (bool, string, error) {
+	pools, err := dbObj.StoragePools()
+	if err != nil && err != db.ErrNoSuchObject {
+		return false, "", err
+	}
+
+	for _, pool := range pools {
+		_, pl, err := dbObj.StoragePoolGet(pool)
+		if err != nil {
+			continue
+		}
+
+		if pl.Driver != driver {
+			continue
+		}
+
+		if pl.Config[onDiskProperty] == onDiskPoolName {
+			return true, pl.Name, nil
+		}
+	}
+
+	return false, "", nil
+}
+
+// MakeFSType creates the provided filesystem.
+func MakeFSType(path string, fsType string, options *MkfsOptions) (string, error) {
+	var err error
+	var msg string
+
+	fsOptions := options
+	if fsOptions == nil {
+		fsOptions = &MkfsOptions{}
+	}
+
+	cmd := []string{fmt.Sprintf("mkfs.%s", fsType), path}
+	if fsOptions.Label != "" {
+		cmd = append(cmd, "-L", fsOptions.Label)
+	}
+
+	if fsType == "ext4" {
+		cmd = append(cmd, "-E", "nodiscard,lazy_itable_init=0,lazy_journal_init=0")
+	}
+
+	msg, err = shared.TryRunCommand(cmd[0], cmd[1:]...)
+	if err != nil {
+		return msg, err
+	}
+
+	return "", nil
+}
+
+// FSGenerateNewUUID generates a UUID for the given path for btrfs and xfs filesystems.
+func FSGenerateNewUUID(fstype string, lvpath string) (string, error) {
+	switch fstype {
+	case "btrfs":
+		return btrfsGenerateNewUUID(lvpath)
+	case "xfs":
+		return xfsGenerateNewUUID(lvpath)
+	}
+
+	return "", nil
+}
+
+func xfsGenerateNewUUID(devPath string) (string, error) {
+	// Attempt to generate a new UUID
+	msg, err := shared.RunCommand("xfs_admin", "-U", "generate", devPath)
+	if err != nil {
+		return msg, err
+	}
+
+	if msg != "" {
+		// Exit 0 with a msg usually means some log entry getting in the way
+		msg, err = shared.RunCommand("xfs_repair", "-o", "force_geometry", "-L", devPath)
+		if err != nil {
+			return msg, err
+		}
+
+		// Attempt to generate a new UUID again
+		msg, err = shared.RunCommand("xfs_admin", "-U", "generate", devPath)
+		if err != nil {
+			return msg, err
+		}
+	}
+
+	return msg, nil
+}
+
+func btrfsGenerateNewUUID(lvpath string) (string, error) {
+	msg, err := shared.RunCommand(
+		"btrfstune",
+		"-f",
+		"-u",
+		lvpath)
+	if err != nil {
+		return msg, err
+	}
+
+	return msg, nil
+}
+
+// GrowFileSystem grows a filesystem if it is supported.
+func GrowFileSystem(fsType string, devPath string, mntpoint string) error {
+	var msg string
+	var err error
+	switch fsType {
+	case "": // if not specified, default to ext4
+		fallthrough
+	case "ext4":
+		msg, err = shared.TryRunCommand("resize2fs", devPath)
+	case "xfs":
+		msg, err = shared.TryRunCommand("xfs_growfs", devPath)
+	case "btrfs":
+		msg, err = shared.TryRunCommand("btrfs", "filesystem", "resize", "max", mntpoint)
+	default:
+		return fmt.Errorf(`Growing not supported for filesystem type "%s"`, fsType)
+	}
+
+	if err != nil {
+		errorMsg := fmt.Sprintf(`Could not extend underlying %s filesystem for "%s": %s`, fsType, devPath, msg)
+		logger.Errorf(errorMsg)
+		return fmt.Errorf(errorMsg)
+	}
+
+	logger.Debugf(`extended underlying %s filesystem for "%s"`, fsType, devPath)
+	return nil
+}
+
+// ShrinkFileSystem shrinks a filesystem if it is supported.
+func ShrinkFileSystem(fsType string, devPath string, mntpoint string, byteSize int64) error {
+	strSize := fmt.Sprintf("%dK", byteSize/1024)
+
+	switch fsType {
+	case "": // if not specified, default to ext4
+		fallthrough
+	case "ext4":
+		_, err := shared.TryRunCommand("e2fsck", "-f", "-y", devPath)
+		if err != nil {
+			return err
+		}
+
+		_, err = shared.TryRunCommand("resize2fs", devPath, strSize)
+		if err != nil {
+			return err
+		}
+	case "btrfs":
+		_, err := shared.TryRunCommand("btrfs", "filesystem", "resize", strSize, mntpoint)
+		if err != nil {
+			return err
+		}
+	default:
+		return fmt.Errorf(`Shrinking not supported for filesystem type "%s"`, fsType)
+	}
+
+	return nil
+}
+
+// GetStorageResource returns the available resources of a given path.
+func GetStorageResource(path string) (*api.ResourcesStoragePool, error) {
+	st, err := shared.Statvfs(path)
+	if err != nil {
+		return nil, err
+	}
+
+	res := api.ResourcesStoragePool{}
+	res.Space.Total = st.Blocks * uint64(st.Bsize)
+	res.Space.Used = (st.Blocks - st.Bfree) * uint64(st.Bsize)
+
+	// Some filesystems don't report inodes since they allocate them
+	// dynamically e.g. btrfs.
+	if st.Files > 0 {
+		res.Inodes.Total = st.Files
+		res.Inodes.Used = st.Files - st.Ffree
+	}
+
+	return &res, nil
+}
diff --git a/lxd/storage_btrfs.go b/lxd/storage_btrfs.go
index 66d541a9ab..5be6c321a4 100644
--- a/lxd/storage_btrfs.go
+++ b/lxd/storage_btrfs.go
@@ -157,7 +157,7 @@ func (s *storageBtrfs) StoragePoolCreate() error {
 			return fmt.Errorf("Failed to create sparse file %s: %s", source, err)
 		}
 
-		output, err := makeFSType(source, "btrfs", &mkfsOptions{label: s.pool.Name})
+		output, err := driver.MakeFSType(source, "btrfs", &driver.MkfsOptions{Label: s.pool.Name})
 		if err != nil {
 			return fmt.Errorf("Failed to create the BTRFS pool: %s", output)
 		}
@@ -168,7 +168,7 @@ func (s *storageBtrfs) StoragePoolCreate() error {
 		if filepath.IsAbs(source) {
 			isBlockDev = shared.IsBlockdevPath(source)
 			if isBlockDev {
-				output, err := makeFSType(source, "btrfs", &mkfsOptions{label: s.pool.Name})
+				output, err := driver.MakeFSType(source, "btrfs", &driver.MkfsOptions{Label: s.pool.Name})
 				if err != nil {
 					return fmt.Errorf("Failed to create the BTRFS pool: %s", output)
 				}
@@ -208,7 +208,7 @@ func (s *storageBtrfs) StoragePoolCreate() error {
 
 	poolMntPoint := getStoragePoolMountPoint(s.pool.Name)
 	if !shared.PathExists(poolMntPoint) {
-		err := os.MkdirAll(poolMntPoint, storagePoolsDirMode)
+		err := os.MkdirAll(poolMntPoint, driver.StoragePoolsDirMode)
 		if err != nil {
 			return err
 		}
@@ -216,7 +216,7 @@ func (s *storageBtrfs) StoragePoolCreate() error {
 
 	var err1 error
 	var devUUID string
-	mountFlags, mountOptions := lxdResolveMountoptions(s.getBtrfsMountOptions())
+	mountFlags, mountOptions := driver.LXDResolveMountoptions(s.getBtrfsMountOptions())
 	mountFlags |= s.remount
 	if isBlockDev && filepath.IsAbs(source) {
 		devUUID, _ = shared.LookupUUIDByBlockDevPath(source)
@@ -399,7 +399,7 @@ func (s *storageBtrfs) StoragePoolMount() (bool, error) {
 
 	// Check whether the mount poolMntPoint exits.
 	if !shared.PathExists(poolMntPoint) {
-		err := os.MkdirAll(poolMntPoint, storagePoolsDirMode)
+		err := os.MkdirAll(poolMntPoint, driver.StoragePoolsDirMode)
 		if err != nil {
 			return false, err
 		}
@@ -409,7 +409,7 @@ func (s *storageBtrfs) StoragePoolMount() (bool, error) {
 		return false, nil
 	}
 
-	mountFlags, mountOptions := lxdResolveMountoptions(s.getBtrfsMountOptions())
+	mountFlags, mountOptions := driver.LXDResolveMountoptions(s.getBtrfsMountOptions())
 	mountSource := source
 	isBlockDev := shared.IsBlockdevPath(source)
 	if filepath.IsAbs(source) {
@@ -821,7 +821,7 @@ func (s *storageBtrfs) doContainerCreate(projectName, name string, privileged bo
 	// doesn't already.
 	containerSubvolumePath := s.getContainerSubvolumePath(s.pool.Name)
 	if !shared.PathExists(containerSubvolumePath) {
-		err := os.MkdirAll(containerSubvolumePath, containersDirMode)
+		err := os.MkdirAll(containerSubvolumePath, driver.ContainersDirMode)
 		if err != nil {
 			return err
 		}
@@ -876,7 +876,7 @@ func (s *storageBtrfs) ContainerCreateFromImage(container container, fingerprint
 	// doesn't already.
 	containerSubvolumePath := s.getContainerSubvolumePath(s.pool.Name)
 	if !shared.PathExists(containerSubvolumePath) {
-		err := os.MkdirAll(containerSubvolumePath, containersDirMode)
+		err := os.MkdirAll(containerSubvolumePath, driver.ContainersDirMode)
 		if err != nil {
 			return errors.Wrap(err, "Failed to create volume directory")
 		}
@@ -995,7 +995,7 @@ func (s *storageBtrfs) copyContainer(target container, source container) error {
 	containersPath := getContainerMountPoint("default", s.pool.Name, "")
 	// Ensure that the directories immediately preceding the subvolume directory exist.
 	if !shared.PathExists(containersPath) {
-		err := os.MkdirAll(containersPath, containersDirMode)
+		err := os.MkdirAll(containersPath, driver.ContainersDirMode)
 		if err != nil {
 			return err
 		}
@@ -1036,7 +1036,7 @@ func (s *storageBtrfs) copySnapshot(target container, source container) error {
 
 	// Ensure that the directories immediately preceding the subvolume directory exist.
 	if !shared.PathExists(containersPath) {
-		err := os.MkdirAll(containersPath, containersDirMode)
+		err := os.MkdirAll(containersPath, driver.ContainersDirMode)
 		if err != nil {
 			return err
 		}
@@ -1379,7 +1379,7 @@ func (s *storageBtrfs) doContainerSnapshotCreate(projectName string, targetName
 	// doesn't already.
 	snapshotSubvolumePath := getSnapshotSubvolumePath(projectName, s.pool.Name, sourceName)
 	if !shared.PathExists(snapshotSubvolumePath) {
-		err := os.MkdirAll(snapshotSubvolumePath, containersDirMode)
+		err := os.MkdirAll(snapshotSubvolumePath, driver.ContainersDirMode)
 		if err != nil {
 			return err
 		}
@@ -1389,7 +1389,7 @@ func (s *storageBtrfs) doContainerSnapshotCreate(projectName string, targetName
 	snapshotMntPointSymlink := shared.VarPath("snapshots", project.Prefix(projectName, sourceName))
 	if !shared.PathExists(snapshotMntPointSymlink) {
 		if !shared.PathExists(snapshotMntPointSymlinkTarget) {
-			err = os.MkdirAll(snapshotMntPointSymlinkTarget, snapshotsDirMode)
+			err = os.MkdirAll(snapshotMntPointSymlinkTarget, driver.SnapshotsDirMode)
 			if err != nil {
 				return err
 			}
@@ -1567,7 +1567,7 @@ func (s *storageBtrfs) ContainerSnapshotCreateEmpty(snapshotContainer container)
 	snapshotSubvolumePath := getSnapshotSubvolumePath(snapshotContainer.Project(), s.pool.Name, sourceName)
 	snapshotSubvolumeName := getSnapshotMountPoint(snapshotContainer.Project(), s.pool.Name, snapshotContainer.Name())
 	if !shared.PathExists(snapshotSubvolumePath) {
-		err := os.MkdirAll(snapshotSubvolumePath, containersDirMode)
+		err := os.MkdirAll(snapshotSubvolumePath, driver.ContainersDirMode)
 		if err != nil {
 			return err
 		}
@@ -2000,7 +2000,7 @@ func (s *storageBtrfs) ImageCreate(fingerprint string, tracker *ioprogress.Progr
 	// doesn't already.
 	imageSubvolumePath := s.getImageSubvolumePath(s.pool.Name)
 	if !shared.PathExists(imageSubvolumePath) {
-		err := os.MkdirAll(imageSubvolumePath, imagesDirMode)
+		err := os.MkdirAll(imageSubvolumePath, driver.ImagesDirMode)
 		if err != nil {
 			return err
 		}
@@ -2723,7 +2723,7 @@ func (s *storageBtrfs) MigrationSink(conn *websocket.Conn, op *operation, args M
 	_, containerPool, _ := args.Container.Storage().GetContainerPoolInfo()
 	containersPath := getSnapshotMountPoint(args.Container.Project(), containerPool, containerName)
 	if !args.ContainerOnly && len(args.Snapshots) > 0 {
-		err := os.MkdirAll(containersPath, containersDirMode)
+		err := os.MkdirAll(containersPath, driver.ContainersDirMode)
 		if err != nil {
 			return err
 		}
@@ -2961,7 +2961,7 @@ func (s *storageBtrfs) StoragePoolResources() (*api.ResourcesStoragePool, error)
 
 	// Inode allocation is dynamic so no use in reporting them.
 
-	return storageResource(poolMntPoint)
+	return driver.GetStorageResource(poolMntPoint)
 }
 
 func (s *storageBtrfs) StoragePoolVolumeCopy(source *api.StorageVolumeSource) error {
@@ -3037,7 +3037,7 @@ func (s *storageBtrfs) copyVolume(sourcePool string, sourceName string, targetNa
 	}
 
 	if !shared.PathExists(customDir) {
-		err := os.MkdirAll(customDir, customDirMode)
+		err := os.MkdirAll(customDir, driver.CustomDirMode)
 		if err != nil {
 			logger.Errorf("Failed to create directory \"%s\" for storage volume \"%s\" on storage pool \"%s\": %s", customDir, s.volume.Name, s.pool.Name, err)
 			return err
@@ -3163,7 +3163,7 @@ func (s *storageBtrfs) doVolumeSnapshotCreate(sourcePool string, sourceName stri
 
 	customSnapshotSubvolumeName := getStoragePoolVolumeSnapshotMountPoint(s.pool.Name, s.volume.Name)
 
-	err = os.MkdirAll(customSnapshotSubvolumeName, snapshotsDirMode)
+	err = os.MkdirAll(customSnapshotSubvolumeName, driver.SnapshotsDirMode)
 	if err != nil && !os.IsNotExist(err) {
 		return err
 	}
diff --git a/lxd/storage_ceph.go b/lxd/storage_ceph.go
index e603190047..76fff33607 100644
--- a/lxd/storage_ceph.go
+++ b/lxd/storage_ceph.go
@@ -15,6 +15,7 @@ import (
 
 	"github.com/lxc/lxd/lxd/db"
 	"github.com/lxc/lxd/lxd/project"
+	driver "github.com/lxc/lxd/lxd/storage"
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/api"
 	"github.com/lxc/lxd/shared/ioprogress"
@@ -358,7 +359,7 @@ func (s *storageCeph) StoragePoolVolumeCreate() error {
 	RBDFilesystem := s.getRBDFilesystem()
 	logger.Debugf(`Retrieved filesystem type "%s" of RBD storage volume "%s" on storage pool "%s"`, RBDFilesystem, s.volume.Name, s.pool.Name)
 
-	msg, err := makeFSType(RBDDevPath, RBDFilesystem, nil)
+	msg, err := driver.MakeFSType(RBDDevPath, RBDFilesystem, nil)
 	if err != nil {
 		logger.Errorf(`Failed to create filesystem type "%s" on device path "%s" for RBD storage volume "%s" on storage pool "%s": %s`, RBDFilesystem, RBDDevPath, s.volume.Name, s.pool.Name, msg)
 		return err
@@ -424,7 +425,7 @@ func (s *storageCeph) StoragePoolVolumeDelete() error {
 
 	volumeMntPoint := getStoragePoolVolumeMountPoint(s.pool.Name, s.volume.Name)
 	if shared.IsMountPoint(volumeMntPoint) {
-		err := tryUnmount(volumeMntPoint, unix.MNT_DETACH)
+		err := driver.TryUnmount(volumeMntPoint, unix.MNT_DETACH)
 		if err != nil {
 			logger.Errorf(`Failed to unmount RBD storage volume "%s" on storage pool "%s": %s`, s.volume.Name, s.pool.Name, err)
 		}
@@ -508,8 +509,8 @@ func (s *storageCeph) StoragePoolVolumeMount() (bool, error) {
 		RBDDevPath, ret = getRBDMappedDevPath(s.ClusterName, s.OSDPoolName,
 			storagePoolVolumeTypeNameCustom, s.volume.Name, true,
 			s.UserName)
-		mountFlags, mountOptions := lxdResolveMountoptions(s.getRBDMountOptions())
-		customerr = tryMount(
+		mountFlags, mountOptions := driver.LXDResolveMountoptions(s.getRBDMountOptions())
+		customerr = driver.TryMount(
 			RBDDevPath,
 			volumeMntPoint,
 			RBDFilesystem,
@@ -560,7 +561,7 @@ func (s *storageCeph) StoragePoolVolumeUmount() (bool, error) {
 	var customerr error
 	ourUmount := false
 	if shared.IsMountPoint(volumeMntPoint) {
-		customerr = tryUnmount(volumeMntPoint, unix.MNT_DETACH)
+		customerr = driver.TryUnmount(volumeMntPoint, unix.MNT_DETACH)
 		ourUmount = true
 	}
 
@@ -1366,7 +1367,7 @@ func (s *storageCeph) ContainerUmount(c container, path string) (bool, error) {
 	var mounterr error
 	ourUmount := false
 	if shared.IsMountPoint(containerMntPoint) {
-		mounterr = tryUnmount(containerMntPoint, 0)
+		mounterr = driver.TryUnmount(containerMntPoint, 0)
 		ourUmount = true
 	}
 
@@ -1804,7 +1805,7 @@ func (s *storageCeph) ContainerSnapshotStart(c container) (bool, error) {
 
 	containerMntPoint := getSnapshotMountPoint(c.Project(), s.pool.Name, containerName)
 	RBDFilesystem := s.getRBDFilesystem()
-	mountFlags, mountOptions := lxdResolveMountoptions(s.getRBDMountOptions())
+	mountFlags, mountOptions := driver.LXDResolveMountoptions(s.getRBDMountOptions())
 	if RBDFilesystem == "xfs" {
 		idx := strings.Index(mountOptions, "nouuid")
 		if idx < 0 {
@@ -1812,7 +1813,7 @@ func (s *storageCeph) ContainerSnapshotStart(c container) (bool, error) {
 		}
 	}
 
-	err = tryMount(
+	err = driver.TryMount(
 		RBDDevPath,
 		containerMntPoint,
 		RBDFilesystem,
@@ -1845,7 +1846,7 @@ func (s *storageCeph) ContainerSnapshotStop(c container) (bool, error) {
 	}
 
 	// Unmount
-	err := tryUnmount(containerMntPoint, unix.MNT_DETACH)
+	err := driver.TryUnmount(containerMntPoint, unix.MNT_DETACH)
 	if err != nil {
 		logger.Errorf("Failed to unmount %s: %s", containerMntPoint, err)
 		return false, err
@@ -2103,7 +2104,7 @@ func (s *storageCeph) ImageCreate(fingerprint string, tracker *ioprogress.Progre
 
 		// get filesystem
 		RBDFilesystem := s.getRBDFilesystem()
-		msg, err := makeFSType(RBDDevPath, RBDFilesystem, nil)
+		msg, err := driver.MakeFSType(RBDDevPath, RBDFilesystem, nil)
 		if err != nil {
 			logger.Errorf(`Failed to create filesystem "%s" for RBD storage volume for image "%s" on storage pool "%s": %s`, RBDFilesystem, fingerprint,
 				s.pool.Name, msg)
@@ -2339,7 +2340,7 @@ func (s *storageCeph) ImageMount(fingerprint string) (bool, error) {
 
 	RBDFilesystem := s.getRBDFilesystem()
 	RBDMountOptions := s.getRBDMountOptions()
-	mountFlags, mountOptions := lxdResolveMountoptions(RBDMountOptions)
+	mountFlags, mountOptions := driver.LXDResolveMountoptions(RBDMountOptions)
 	RBDDevPath, ret := getRBDMappedDevPath(s.ClusterName, s.OSDPoolName,
 		storagePoolVolumeTypeNameImage, fingerprint, true, s.UserName)
 	errMsg := fmt.Sprintf("Failed to mount RBD device %s onto %s",
@@ -2349,7 +2350,7 @@ func (s *storageCeph) ImageMount(fingerprint string) (bool, error) {
 		return false, fmt.Errorf(errMsg)
 	}
 
-	err := tryMount(RBDDevPath, imageMntPoint, RBDFilesystem, mountFlags, mountOptions)
+	err := driver.TryMount(RBDDevPath, imageMntPoint, RBDFilesystem, mountFlags, mountOptions)
 	if err != nil || ret < 0 {
 		return false, err
 	}
@@ -2366,7 +2367,7 @@ func (s *storageCeph) ImageUmount(fingerprint string) (bool, error) {
 		return false, nil
 	}
 
-	err := tryUnmount(imageMntPoint, 0)
+	err := driver.TryUnmount(imageMntPoint, 0)
 	if err != nil {
 		return false, err
 	}
@@ -2633,7 +2634,7 @@ func (s *storageCeph) StoragePoolVolumeCopy(source *api.StorageVolumeSource) err
 			// create snapshot mountpoint
 			newTargetName := fmt.Sprintf("%s/%s", s.volume.Name, snapOnlyName)
 			targetPath := getStoragePoolVolumeSnapshotMountPoint(s.pool.Name, newTargetName)
-			err = os.MkdirAll(targetPath, snapshotsDirMode)
+			err = os.MkdirAll(targetPath, driver.SnapshotsDirMode)
 			if err != nil {
 				logger.Errorf("Failed to create mountpoint \"%s\" for RBD storage volume \"%s\" on storage pool \"%s\": %s", targetPath, s.volume.Name, s.pool.Name, err)
 				return err
@@ -2718,7 +2719,7 @@ func (s *storageCeph) StoragePoolVolumeSnapshotCreate(target *api.StorageVolumeS
 	}
 
 	targetPath := getStoragePoolVolumeSnapshotMountPoint(s.pool.Name, target.Name)
-	err = os.MkdirAll(targetPath, snapshotsDirMode)
+	err = os.MkdirAll(targetPath, driver.SnapshotsDirMode)
 	if err != nil {
 		logger.Errorf("Failed to create mountpoint \"%s\" for RBD storage volume \"%s\" on storage pool \"%s\": %s", targetPath, s.volume.Name, s.pool.Name, err)
 		return err
diff --git a/lxd/storage_ceph_utils.go b/lxd/storage_ceph_utils.go
index 9c458ad929..dacfec3b30 100644
--- a/lxd/storage_ceph_utils.go
+++ b/lxd/storage_ceph_utils.go
@@ -16,6 +16,7 @@ import (
 
 	"github.com/lxc/lxd/lxd/db"
 	"github.com/lxc/lxd/lxd/project"
+	driver "github.com/lxc/lxd/lxd/storage"
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/api"
 	"github.com/lxc/lxd/shared/logger"
@@ -705,7 +706,7 @@ func (s *storageCeph) getRBDFilesystem() string {
 // getRBDMountOptions returns the mount options the storage volume is supposed
 // to be mounted with
 // The option string that is returned needs to be passed to the approriate
-// helper (currently named "lxdResolveMountoptions") which will take on the job
+// helper (currently named "LXDResolveMountoptions") which will take on the job
 // of splitting it into appropriate flags and string options.
 func (s *storageCeph) getRBDMountOptions() string {
 	if s.volume.Config["block.mount_options"] != "" {
@@ -1556,7 +1557,7 @@ func (s *storageCeph) rbdGrow(path string, size int64, fsType string,
 	}
 
 	// Grow the filesystem
-	return growFileSystem(fsType, path, fsMntPoint)
+	return driver.GrowFileSystem(fsType, path, fsMntPoint)
 }
 
 // copyWithSnapshots creates a non-sparse copy of a container including its
@@ -1647,7 +1648,7 @@ func (s *storageCeph) cephRBDVolumeBackupCreate(tmpPath string, backup backup, s
 
 	// Generate a new UUID if needed
 	RBDFilesystem := s.getRBDFilesystem()
-	msg, err := fsGenerateNewUUID(RBDFilesystem, RBDDevPath)
+	msg, err := driver.FSGenerateNewUUID(RBDFilesystem, RBDDevPath)
 	if err != nil {
 		logger.Errorf("Failed to create new UUID for filesystem \"%s\": %s: %s", RBDFilesystem, msg, err)
 		return err
@@ -1666,14 +1667,14 @@ func (s *storageCeph) cephRBDVolumeBackupCreate(tmpPath string, backup backup, s
 	}
 
 	// Mount the volume
-	mountFlags, mountOptions := lxdResolveMountoptions(s.getRBDMountOptions())
-	err = tryMount(RBDDevPath, tmpContainerMntPoint, RBDFilesystem, mountFlags, mountOptions)
+	mountFlags, mountOptions := driver.LXDResolveMountoptions(s.getRBDMountOptions())
+	err = driver.TryMount(RBDDevPath, tmpContainerMntPoint, RBDFilesystem, mountFlags, mountOptions)
 	if err != nil {
 		logger.Errorf("Failed to mount RBD device %s onto %s: %s", RBDDevPath, tmpContainerMntPoint, err)
 		return err
 	}
 	logger.Debugf("Mounted RBD device %s onto %s", RBDDevPath, tmpContainerMntPoint)
-	defer tryUnmount(tmpContainerMntPoint, unix.MNT_DETACH)
+	defer driver.TryUnmount(tmpContainerMntPoint, unix.MNT_DETACH)
 
 	// Figure out the target name
 	targetName := sourceContainerName
@@ -1753,7 +1754,7 @@ func (s *storageCeph) doContainerCreate(projectName, name string, privileged boo
 
 	// get filesystem
 	RBDFilesystem := s.getRBDFilesystem()
-	msg, err := makeFSType(RBDDevPath, RBDFilesystem, nil)
+	msg, err := driver.MakeFSType(RBDDevPath, RBDFilesystem, nil)
 	if err != nil {
 		logger.Errorf(`Failed to create filesystem type "%s" on device path "%s" for RBD storage volume for container "%s" on storage pool "%s": %s`, RBDFilesystem, RBDDevPath, name, s.pool.Name, msg)
 		return err
@@ -1820,8 +1821,8 @@ func (s *storageCeph) doContainerMount(projectName string, name string) (bool, e
 			s.OSDPoolName, storagePoolVolumeTypeNameContainer,
 			volumeName, true, s.UserName)
 		if ret >= 0 {
-			mountFlags, mountOptions := lxdResolveMountoptions(s.getRBDMountOptions())
-			mounterr = tryMount(RBDDevPath, containerMntPoint,
+			mountFlags, mountOptions := driver.LXDResolveMountoptions(s.getRBDMountOptions())
+			mounterr = driver.TryMount(RBDDevPath, containerMntPoint,
 				RBDFilesystem, mountFlags, mountOptions)
 			ourMount = true
 		}
@@ -2060,7 +2061,7 @@ func (s *storageCeph) cephRBDGenerateUUID(volumeName string, volumeType string)
 	defer cephRBDVolumeUnmap(s.ClusterName, s.OSDPoolName, volumeName, volumeType, s.UserName, true)
 
 	// Update the UUID
-	msg, err := fsGenerateNewUUID(s.getRBDFilesystem(), RBDDevPath)
+	msg, err := driver.FSGenerateNewUUID(s.getRBDFilesystem(), RBDDevPath)
 	if err != nil {
 		return fmt.Errorf("Failed to regenerate UUID for '%v': %v: %v", volumeName, err, msg)
 	}
diff --git a/lxd/storage_cephfs.go b/lxd/storage_cephfs.go
index 7f4d5f86ec..58651befec 100644
--- a/lxd/storage_cephfs.go
+++ b/lxd/storage_cephfs.go
@@ -15,6 +15,7 @@ import (
 
 	"github.com/lxc/lxd/lxd/migration"
 	"github.com/lxc/lxd/lxd/state"
+	driver "github.com/lxc/lxd/lxd/storage"
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/api"
 	"github.com/lxc/lxd/shared/ioprogress"
@@ -151,13 +152,13 @@ func (s *storageCephFs) StoragePoolCreate() error {
 	connected := false
 	for _, monAddress := range monAddresses {
 		uri := fmt.Sprintf("%s:6789:/", monAddress)
-		err = tryMount(uri, mountPoint, "ceph", 0, fmt.Sprintf("name=%v,secret=%v,mds_namespace=%v", s.UserName, userSecret, fsName))
+		err = driver.TryMount(uri, mountPoint, "ceph", 0, fmt.Sprintf("name=%v,secret=%v,mds_namespace=%v", s.UserName, userSecret, fsName))
 		if err != nil {
 			continue
 		}
 
 		connected = true
-		defer tryUnmount(mountPoint, syscall.MNT_DETACH)
+		defer driver.TryUnmount(mountPoint, syscall.MNT_DETACH)
 		break
 	}
 
@@ -227,13 +228,13 @@ func (s *storageCephFs) StoragePoolDelete() error {
 	connected := false
 	for _, monAddress := range monAddresses {
 		uri := fmt.Sprintf("%s:6789:/", monAddress)
-		err = tryMount(uri, mountPoint, "ceph", 0, fmt.Sprintf("name=%v,secret=%v,mds_namespace=%v", s.UserName, userSecret, fsName))
+		err = driver.TryMount(uri, mountPoint, "ceph", 0, fmt.Sprintf("name=%v,secret=%v,mds_namespace=%v", s.UserName, userSecret, fsName))
 		if err != nil {
 			continue
 		}
 
 		connected = true
-		defer tryUnmount(mountPoint, syscall.MNT_DETACH)
+		defer driver.TryUnmount(mountPoint, syscall.MNT_DETACH)
 		break
 	}
 
@@ -340,7 +341,7 @@ func (s *storageCephFs) StoragePoolMount() (bool, error) {
 	connected := false
 	for _, monAddress := range monAddresses {
 		uri := fmt.Sprintf("%s:6789:/%s", monAddress, fsPath)
-		err = tryMount(uri, poolMntPoint, "ceph", 0, fmt.Sprintf("name=%v,secret=%v,mds_namespace=%v", s.UserName, secret, fsName))
+		err = driver.TryMount(uri, poolMntPoint, "ceph", 0, fmt.Sprintf("name=%v,secret=%v,mds_namespace=%v", s.UserName, secret, fsName))
 		if err != nil {
 			continue
 		}
@@ -395,7 +396,7 @@ func (s *storageCephFs) StoragePoolUmount() (bool, error) {
 	}
 
 	// Unmount
-	err := tryUnmount(poolMntPoint, syscall.MNT_DETACH)
+	err := driver.TryUnmount(poolMntPoint, syscall.MNT_DETACH)
 	if err != nil {
 		return false, err
 	}
@@ -756,7 +757,7 @@ func (s *storageCephFs) StoragePoolResources() (*api.ResourcesStoragePool, error
 	}
 
 	poolMntPoint := getStoragePoolMountPoint(s.pool.Name)
-	return storageResource(poolMntPoint)
+	return driver.GetStorageResource(poolMntPoint)
 }
 
 func (s *storageCephFs) StoragePoolVolumeCopy(source *api.StorageVolumeSource) error {
diff --git a/lxd/storage_dir.go b/lxd/storage_dir.go
index 46482a01aa..edd26d8686 100644
--- a/lxd/storage_dir.go
+++ b/lxd/storage_dir.go
@@ -1386,7 +1386,7 @@ func (s *storageDir) StoragePoolResources() (*api.ResourcesStoragePool, error) {
 
 	poolMntPoint := getStoragePoolMountPoint(s.pool.Name)
 
-	return storageResource(poolMntPoint)
+	return driver.GetStorageResource(poolMntPoint)
 }
 
 func (s *storageDir) StoragePoolVolumeCopy(source *api.StorageVolumeSource) error {
diff --git a/lxd/storage_lvm.go b/lxd/storage_lvm.go
index 97ece383c6..c168697b33 100644
--- a/lxd/storage_lvm.go
+++ b/lxd/storage_lvm.go
@@ -302,7 +302,7 @@ func (s *storageLvm) StoragePoolCreate() error {
 		}
 
 		// Check that we don't already use this volume group.
-		inUse, user, err := lxdUsesPool(s.s.Cluster, poolName, s.pool.Driver, "lvm.vg_name")
+		inUse, user, err := driver.LXDUsesPool(s.s.Cluster, poolName, s.pool.Driver, "lvm.vg_name")
 		if err != nil {
 			return err
 		}
@@ -619,8 +619,8 @@ func (s *storageLvm) StoragePoolVolumeMount() (bool, error) {
 	var customerr error
 	ourMount := false
 	if !shared.IsMountPoint(customPoolVolumeMntPoint) {
-		mountFlags, mountOptions := lxdResolveMountoptions(s.getLvmMountOptions())
-		customerr = tryMount(lvmVolumePath, customPoolVolumeMntPoint, lvFsType, mountFlags, mountOptions)
+		mountFlags, mountOptions := driver.LXDResolveMountoptions(s.getLvmMountOptions())
+		customerr = driver.TryMount(lvmVolumePath, customPoolVolumeMntPoint, lvFsType, mountFlags, mountOptions)
 		ourMount = true
 	}
 
@@ -662,7 +662,7 @@ func (s *storageLvm) StoragePoolVolumeUmount() (bool, error) {
 	var customerr error
 	ourUmount := false
 	if shared.IsMountPoint(customPoolVolumeMntPoint) {
-		customerr = tryUnmount(customPoolVolumeMntPoint, 0)
+		customerr = driver.TryUnmount(customPoolVolumeMntPoint, 0)
 		ourUmount = true
 	}
 
@@ -1028,7 +1028,7 @@ func (s *storageLvm) ContainerCreateFromImage(container container, fingerprint s
 	containerLvDevPath := getLvmDevPath(container.Project(), poolName, storagePoolVolumeAPIEndpointContainers, containerLvmName)
 	// Generate a new xfs's UUID
 	lvFsType := s.getLvmFilesystem()
-	msg, err := fsGenerateNewUUID(lvFsType, containerLvDevPath)
+	msg, err := driver.FSGenerateNewUUID(lvFsType, containerLvDevPath)
 	if err != nil {
 		logger.Errorf("Failed to create new \"%s\" UUID for container \"%s\" on storage pool \"%s\": %s", lvFsType, containerName, s.pool.Name, msg)
 		return err
@@ -1073,7 +1073,7 @@ func lvmContainerDeleteInternal(projectName, poolName string, ctName string, isS
 	}
 
 	if shared.IsMountPoint(containerMntPoint) {
-		err := tryUnmount(containerMntPoint, 0)
+		err := driver.TryUnmount(containerMntPoint, 0)
 		if err != nil {
 			return fmt.Errorf(`Failed to unmount container path `+
 				`"%s": %s`, containerMntPoint, err)
@@ -1265,7 +1265,7 @@ func (s *storageLvm) doContainerMount(project, name string, snap bool) (bool, er
 	var mounterr error
 	ourMount := false
 	if !shared.IsMountPoint(containerMntPoint) {
-		mountFlags, mountOptions := lxdResolveMountoptions(s.getLvmMountOptions())
+		mountFlags, mountOptions := driver.LXDResolveMountoptions(s.getLvmMountOptions())
 		if snap && lvFsType == "xfs" {
 			idx := strings.Index(mountOptions, "nouuid")
 			if idx < 0 {
@@ -1273,7 +1273,7 @@ func (s *storageLvm) doContainerMount(project, name string, snap bool) (bool, er
 			}
 		}
 
-		mounterr = tryMount(containerLvmPath, containerMntPoint, lvFsType, mountFlags, mountOptions)
+		mounterr = driver.TryMount(containerLvmPath, containerMntPoint, lvFsType, mountFlags, mountOptions)
 		ourMount = true
 	}
 
@@ -1321,7 +1321,7 @@ func (s *storageLvm) umount(project, name string, path string) (bool, error) {
 	var imgerr error
 	ourUmount := false
 	if shared.IsMountPoint(containerMntPoint) {
-		imgerr = tryUnmount(containerMntPoint, 0)
+		imgerr = driver.TryUnmount(containerMntPoint, 0)
 		ourUmount = true
 	}
 
@@ -1585,7 +1585,7 @@ func (s *storageLvm) ContainerSnapshotStart(container container) (bool, error) {
 	containerMntPoint := getSnapshotMountPoint(container.Project(), s.pool.Name, containerName)
 	if !shared.IsMountPoint(containerMntPoint) {
 		mntOptString := s.getLvmMountOptions()
-		mountFlags, mountOptions := lxdResolveMountoptions(mntOptString)
+		mountFlags, mountOptions := driver.LXDResolveMountoptions(mntOptString)
 
 		if lvFsType == "xfs" {
 			idx := strings.Index(mountOptions, "nouuid")
@@ -1594,7 +1594,7 @@ func (s *storageLvm) ContainerSnapshotStart(container container) (bool, error) {
 			}
 		}
 
-		err = tryMount(containerLvmPath, containerMntPoint, lvFsType, mountFlags, mountOptions)
+		err = driver.TryMount(containerLvmPath, containerMntPoint, lvFsType, mountFlags, mountOptions)
 		if err != nil {
 			logger.Errorf(`Failed to mount LVM snapshot "%s" with filesystem "%s" options "%s" onto "%s": %s`, s.volume.Name, lvFsType, mntOptString, containerMntPoint, err)
 			return false, err
@@ -1619,7 +1619,7 @@ func (s *storageLvm) ContainerSnapshotStop(container container) (bool, error) {
 	poolName := s.getOnDiskPoolName()
 
 	if shared.IsMountPoint(snapshotMntPoint) {
-		err := tryUnmount(snapshotMntPoint, 0)
+		err := driver.TryUnmount(snapshotMntPoint, 0)
 		if err != nil {
 			return false, err
 		}
@@ -2028,8 +2028,8 @@ func (s *storageLvm) ImageMount(fingerprint string) (bool, error) {
 
 	poolName := s.getOnDiskPoolName()
 	lvmVolumePath := getLvmDevPath("default", poolName, storagePoolVolumeAPIEndpointImages, fingerprint)
-	mountFlags, mountOptions := lxdResolveMountoptions(s.getLvmMountOptions())
-	err := tryMount(lvmVolumePath, imageMntPoint, lvmFstype, mountFlags, mountOptions)
+	mountFlags, mountOptions := driver.LXDResolveMountoptions(s.getLvmMountOptions())
+	err := driver.TryMount(lvmVolumePath, imageMntPoint, lvmFstype, mountFlags, mountOptions)
 	if err != nil {
 		logger.Errorf(fmt.Sprintf("Error mounting image LV for unpacking: %s", err))
 		return false, fmt.Errorf("Error mounting image LV: %v", err)
@@ -2047,7 +2047,7 @@ func (s *storageLvm) ImageUmount(fingerprint string) (bool, error) {
 		return false, nil
 	}
 
-	err := tryUnmount(imageMntPoint, 0)
+	err := driver.TryUnmount(imageMntPoint, 0)
 	if err != nil {
 		return false, err
 	}
@@ -2287,7 +2287,7 @@ func (s *storageLvm) StoragePoolVolumeSnapshotCreate(target *api.StorageVolumeSn
 	}
 
 	targetPath := getStoragePoolVolumeSnapshotMountPoint(s.pool.Name, target.Name)
-	err = os.MkdirAll(targetPath, snapshotsDirMode)
+	err = os.MkdirAll(targetPath, driver.SnapshotsDirMode)
 	if err != nil {
 		logger.Errorf("Failed to create mountpoint \"%s\" for RBD storage volume \"%s\" on storage pool \"%s\": %s", targetPath, s.volume.Name, s.pool.Name, err)
 		return err
@@ -2303,7 +2303,7 @@ func (s *storageLvm) StoragePoolVolumeSnapshotDelete() error {
 	snapshotLVName := containerNameToLVName(s.volume.Name)
 	storageVolumeSnapshotPath := getStoragePoolVolumeSnapshotMountPoint(s.pool.Name, s.volume.Name)
 	if shared.IsMountPoint(storageVolumeSnapshotPath) {
-		err := tryUnmount(storageVolumeSnapshotPath, 0)
+		err := driver.TryUnmount(storageVolumeSnapshotPath, 0)
 		if err != nil {
 			return fmt.Errorf("Failed to unmount snapshot path \"%s\": %s", storageVolumeSnapshotPath, err)
 		}
diff --git a/lxd/storage_lvm_utils.go b/lxd/storage_lvm_utils.go
index c35e66bcca..297813efa2 100644
--- a/lxd/storage_lvm_utils.go
+++ b/lxd/storage_lvm_utils.go
@@ -13,6 +13,7 @@ import (
 	"github.com/lxc/lxd/lxd/db"
 	"github.com/lxc/lxd/lxd/project"
 	"github.com/lxc/lxd/lxd/state"
+	driver "github.com/lxc/lxd/lxd/storage"
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/api"
 	"github.com/lxc/lxd/shared/logger"
@@ -58,7 +59,7 @@ func (s *storageLvm) lvExtend(lvPath string, lvSize int64, fsType string, fsMntP
 			`volume type %d`, volumeType)
 	}
 
-	return growFileSystem(fsType, lvPath, fsMntPoint)
+	return driver.GrowFileSystem(fsType, lvPath, fsMntPoint)
 }
 
 func (s *storageLvm) lvReduce(lvPath string, lvSize int64, fsType string, fsMntPoint string, volumeType int, data interface{}) error {
@@ -331,7 +332,7 @@ func (s *storageLvm) copyContainerThinpool(target container, source container, r
 		}
 	}
 
-	msg, err := fsGenerateNewUUID(LVFilesystem, containerLvDevPath)
+	msg, err := driver.FSGenerateNewUUID(LVFilesystem, containerLvDevPath)
 	if err != nil {
 		logger.Errorf("Failed to create new \"%s\" UUID for container \"%s\" on storage pool \"%s\": %s", LVFilesystem, containerName, s.pool.Name, msg)
 		return err
@@ -853,7 +854,7 @@ func lvmCreateLv(projectName, vgName string, thinPoolName string, lvName string,
 
 	fsPath := getLvmDevPath(projectName, vgName, volumeType, lvName)
 
-	output, err = makeFSType(fsPath, lvFsType, nil)
+	output, err = driver.MakeFSType(fsPath, lvFsType, nil)
 	if err != nil {
 		logger.Errorf("Filesystem creation failed: %s", output)
 		return fmt.Errorf("Error making filesystem on image LV: %v", err)
@@ -1078,7 +1079,7 @@ func (s *storageLvm) copyVolumeThinpool(source string, target string, readOnly b
 
 	lvDevPath := getLvmDevPath("default", poolName, storagePoolVolumeAPIEndpointCustom, targetLvmName)
 
-	msg, err := fsGenerateNewUUID(lvFsType, lvDevPath)
+	msg, err := driver.FSGenerateNewUUID(lvFsType, lvDevPath)
 	if err != nil {
 		logger.Errorf("Failed to create new UUID for filesystem \"%s\" for RBD storage volume \"%s\" on storage pool \"%s\": %s: %s", lvFsType, s.volume.Name, s.pool.Name, msg, err)
 		return err
diff --git a/lxd/storage_pools_utils.go b/lxd/storage_pools_utils.go
index 5b52dc5aa9..1831ea5970 100644
--- a/lxd/storage_pools_utils.go
+++ b/lxd/storage_pools_utils.go
@@ -7,6 +7,7 @@ import (
 
 	"github.com/lxc/lxd/lxd/db"
 	"github.com/lxc/lxd/lxd/state"
+	driver "github.com/lxc/lxd/lxd/storage"
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/version"
 )
@@ -41,7 +42,7 @@ func storagePoolUpdate(state *state.State, name, newDescription string, newConfi
 		}
 	}()
 
-	changedConfig, userOnly := storageConfigDiff(oldConfig, newConfig)
+	changedConfig, userOnly := driver.ConfigDiff(oldConfig, newConfig)
 	// Apply config changes if there are any
 	if len(changedConfig) != 0 {
 		newWritable.Description = newDescription
@@ -191,15 +192,15 @@ func storagePoolDBCreate(s *state.State, poolName, poolDescription string, drive
 	return nil
 }
 
-func storagePoolValidate(poolName string, driver string, config map[string]string) error {
+func storagePoolValidate(poolName string, driverName string, config map[string]string) error {
 	// Check if the storage pool name is valid.
-	err := storageValidName(poolName)
+	err := driver.ValidName(poolName)
 	if err != nil {
 		return err
 	}
 
 	// Validate the requested storage pool configuration.
-	err = storagePoolValidateConfig(poolName, driver, config, nil)
+	err = storagePoolValidateConfig(poolName, driverName, config, nil)
 	if err != nil {
 		return err
 	}
@@ -229,7 +230,7 @@ func storagePoolCreateInternal(state *state.State, poolName, poolDescription str
 }
 
 // This performs all non-db related work needed to create the pool.
-func doStoragePoolCreateInternal(state *state.State, poolName, poolDescription string, driver string, config map[string]string, isNotification bool) error {
+func doStoragePoolCreateInternal(state *state.State, poolName, poolDescription string, driverName string, config map[string]string, isNotification bool) error {
 	tryUndo := true
 	s, err := storagePoolInit(state, poolName)
 	if err != nil {
@@ -263,7 +264,7 @@ func doStoragePoolCreateInternal(state *state.State, poolName, poolDescription s
 	// callback. So diff the config here to see if something like this has
 	// happened.
 	postCreateConfig := s.GetStoragePoolWritable().Config
-	configDiff, _ := storageConfigDiff(config, postCreateConfig)
+	configDiff, _ := driver.ConfigDiff(config, postCreateConfig)
 	if len(configDiff) > 0 {
 		// Create the database entry for the storage pool.
 		err = state.Cluster.StoragePoolUpdate(poolName, poolDescription, postCreateConfig)
diff --git a/lxd/storage_utils.go b/lxd/storage_utils.go
index 76daed4da1..fd4ecf11bd 100644
--- a/lxd/storage_utils.go
+++ b/lxd/storage_utils.go
@@ -2,326 +2,11 @@ package main
 
 import (
 	"fmt"
-	"os"
-	"strings"
-	"time"
 
-	"golang.org/x/sys/unix"
-
-	"github.com/lxc/lxd/lxd/db"
-	"github.com/lxc/lxd/shared"
-	"github.com/lxc/lxd/shared/api"
+	driver "github.com/lxc/lxd/lxd/storage"
 	"github.com/lxc/lxd/shared/logger"
 )
 
-// Options for filesystem creation
-type mkfsOptions struct {
-	label string
-}
-
-// Export the mount options map since we might find it useful in other parts of
-// LXD.
-type mountOptions struct {
-	capture bool
-	flag    uintptr
-}
-
-var MountOptions = map[string]mountOptions{
-	"async":         {false, unix.MS_SYNCHRONOUS},
-	"atime":         {false, unix.MS_NOATIME},
-	"bind":          {true, unix.MS_BIND},
-	"defaults":      {true, 0},
-	"dev":           {false, unix.MS_NODEV},
-	"diratime":      {false, unix.MS_NODIRATIME},
-	"dirsync":       {true, unix.MS_DIRSYNC},
-	"exec":          {false, unix.MS_NOEXEC},
-	"lazytime":      {true, unix.MS_LAZYTIME},
-	"mand":          {true, unix.MS_MANDLOCK},
-	"noatime":       {true, unix.MS_NOATIME},
-	"nodev":         {true, unix.MS_NODEV},
-	"nodiratime":    {true, unix.MS_NODIRATIME},
-	"noexec":        {true, unix.MS_NOEXEC},
-	"nomand":        {false, unix.MS_MANDLOCK},
-	"norelatime":    {false, unix.MS_RELATIME},
-	"nostrictatime": {false, unix.MS_STRICTATIME},
-	"nosuid":        {true, unix.MS_NOSUID},
-	"rbind":         {true, unix.MS_BIND | unix.MS_REC},
-	"relatime":      {true, unix.MS_RELATIME},
-	"remount":       {true, unix.MS_REMOUNT},
-	"ro":            {true, unix.MS_RDONLY},
-	"rw":            {false, unix.MS_RDONLY},
-	"strictatime":   {true, unix.MS_STRICTATIME},
-	"suid":          {false, unix.MS_NOSUID},
-	"sync":          {true, unix.MS_SYNCHRONOUS},
-}
-
-func lxdResolveMountoptions(options string) (uintptr, string) {
-	mountFlags := uintptr(0)
-	tmp := strings.SplitN(options, ",", -1)
-	for i := 0; i < len(tmp); i++ {
-		opt := tmp[i]
-		do, ok := MountOptions[opt]
-		if !ok {
-			continue
-		}
-
-		if do.capture {
-			mountFlags |= do.flag
-		} else {
-			mountFlags &= ^do.flag
-		}
-
-		copy(tmp[i:], tmp[i+1:])
-		tmp[len(tmp)-1] = ""
-		tmp = tmp[:len(tmp)-1]
-		i--
-	}
-
-	return mountFlags, strings.Join(tmp, ",")
-}
-
-// Useful functions for unreliable backends
-func tryMount(src string, dst string, fs string, flags uintptr, options string) error {
-	var err error
-
-	for i := 0; i < 20; i++ {
-		err = unix.Mount(src, dst, fs, flags, options)
-		if err == nil {
-			break
-		}
-
-		time.Sleep(500 * time.Millisecond)
-	}
-
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
-func tryUnmount(path string, flags int) error {
-	var err error
-
-	for i := 0; i < 20; i++ {
-		err = unix.Unmount(path, flags)
-		if err == nil {
-			break
-		}
-
-		time.Sleep(500 * time.Millisecond)
-	}
-
-	if err != nil {
-		return err
-	}
-
-	return nil
-}
-
-func storageValidName(value string) error {
-	if strings.Contains(value, "/") {
-		return fmt.Errorf("Invalid storage volume name \"%s\". Storage volumes cannot contain \"/\" in their name", value)
-	}
-
-	return nil
-}
-
-func storageConfigDiff(oldConfig map[string]string, newConfig map[string]string) ([]string, bool) {
-	changedConfig := []string{}
-	userOnly := true
-	for key := range oldConfig {
-		if oldConfig[key] != newConfig[key] {
-			if !strings.HasPrefix(key, "user.") {
-				userOnly = false
-			}
-
-			if !shared.StringInSlice(key, changedConfig) {
-				changedConfig = append(changedConfig, key)
-			}
-		}
-	}
-
-	for key := range newConfig {
-		if oldConfig[key] != newConfig[key] {
-			if !strings.HasPrefix(key, "user.") {
-				userOnly = false
-			}
-
-			if !shared.StringInSlice(key, changedConfig) {
-				changedConfig = append(changedConfig, key)
-			}
-		}
-	}
-
-	// Skip on no change
-	if len(changedConfig) == 0 {
-		return nil, false
-	}
-
-	return changedConfig, userOnly
-}
-
-// Default permissions for folders in ${LXD_DIR}
-const storagePoolsDirMode os.FileMode = 0711
-const containersDirMode os.FileMode = 0711
-const customDirMode os.FileMode = 0711
-const imagesDirMode os.FileMode = 0700
-const snapshotsDirMode os.FileMode = 0700
-
-// Detect whether LXD already uses the given storage pool.
-func lxdUsesPool(dbObj *db.Cluster, onDiskPoolName string, driver string, onDiskProperty string) (bool, string, error) {
-	pools, err := dbObj.StoragePools()
-	if err != nil && err != db.ErrNoSuchObject {
-		return false, "", err
-	}
-
-	for _, pool := range pools {
-		_, pl, err := dbObj.StoragePoolGet(pool)
-		if err != nil {
-			continue
-		}
-
-		if pl.Driver != driver {
-			continue
-		}
-
-		if pl.Config[onDiskProperty] == onDiskPoolName {
-			return true, pl.Name, nil
-		}
-	}
-
-	return false, "", nil
-}
-
-func makeFSType(path string, fsType string, options *mkfsOptions) (string, error) {
-	var err error
-	var msg string
-
-	fsOptions := options
-	if fsOptions == nil {
-		fsOptions = &mkfsOptions{}
-	}
-
-	cmd := []string{fmt.Sprintf("mkfs.%s", fsType), path}
-	if fsOptions.label != "" {
-		cmd = append(cmd, "-L", fsOptions.label)
-	}
-
-	if fsType == "ext4" {
-		cmd = append(cmd, "-E", "nodiscard,lazy_itable_init=0,lazy_journal_init=0")
-	}
-
-	msg, err = shared.TryRunCommand(cmd[0], cmd[1:]...)
-	if err != nil {
-		return msg, err
-	}
-
-	return "", nil
-}
-
-func fsGenerateNewUUID(fstype string, lvpath string) (string, error) {
-	switch fstype {
-	case "btrfs":
-		return btrfsGenerateNewUUID(lvpath)
-	case "xfs":
-		return xfsGenerateNewUUID(lvpath)
-	}
-
-	return "", nil
-}
-
-func xfsGenerateNewUUID(devPath string) (string, error) {
-	// Attempt to generate a new UUID
-	msg, err := shared.RunCommand("xfs_admin", "-U", "generate", devPath)
-	if err != nil {
-		return msg, err
-	}
-
-	if msg != "" {
-		// Exit 0 with a msg usually means some log entry getting in the way
-		msg, err = shared.RunCommand("xfs_repair", "-o", "force_geometry", "-L", devPath)
-		if err != nil {
-			return msg, err
-		}
-
-		// Attempt to generate a new UUID again
-		msg, err = shared.RunCommand("xfs_admin", "-U", "generate", devPath)
-		if err != nil {
-			return msg, err
-		}
-	}
-
-	return msg, nil
-}
-
-func btrfsGenerateNewUUID(lvpath string) (string, error) {
-	msg, err := shared.RunCommand(
-		"btrfstune",
-		"-f",
-		"-u",
-		lvpath)
-	if err != nil {
-		return msg, err
-	}
-
-	return msg, nil
-}
-
-func growFileSystem(fsType string, devPath string, mntpoint string) error {
-	var msg string
-	var err error
-	switch fsType {
-	case "": // if not specified, default to ext4
-		fallthrough
-	case "ext4":
-		msg, err = shared.TryRunCommand("resize2fs", devPath)
-	case "xfs":
-		msg, err = shared.TryRunCommand("xfs_growfs", devPath)
-	case "btrfs":
-		msg, err = shared.TryRunCommand("btrfs", "filesystem", "resize", "max", mntpoint)
-	default:
-		return fmt.Errorf(`Growing not supported for filesystem type "%s"`, fsType)
-	}
-
-	if err != nil {
-		errorMsg := fmt.Sprintf(`Could not extend underlying %s filesystem for "%s": %s`, fsType, devPath, msg)
-		logger.Errorf(errorMsg)
-		return fmt.Errorf(errorMsg)
-	}
-
-	logger.Debugf(`extended underlying %s filesystem for "%s"`, fsType, devPath)
-	return nil
-}
-
-func shrinkFileSystem(fsType string, devPath string, mntpoint string, byteSize int64) error {
-	strSize := fmt.Sprintf("%dK", byteSize/1024)
-
-	switch fsType {
-	case "": // if not specified, default to ext4
-		fallthrough
-	case "ext4":
-		_, err := shared.TryRunCommand("e2fsck", "-f", "-y", devPath)
-		if err != nil {
-			return err
-		}
-
-		_, err = shared.TryRunCommand("resize2fs", devPath, strSize)
-		if err != nil {
-			return err
-		}
-	case "btrfs":
-		_, err := shared.TryRunCommand("btrfs", "filesystem", "resize", strSize, mntpoint)
-		if err != nil {
-			return err
-		}
-	default:
-		return fmt.Errorf(`Shrinking not supported for filesystem type "%s"`, fsType)
-	}
-
-	return nil
-}
-
 func shrinkVolumeFilesystem(s storage, volumeType int, fsType string, devPath string, mntpoint string, byteSize int64, data interface{}) (func() (bool, error), error) {
 	var cleanupFunc func() (bool, error)
 	switch fsType {
@@ -359,26 +44,6 @@ func shrinkVolumeFilesystem(s storage, volumeType int, fsType string, devPath st
 		return nil, fmt.Errorf(`Shrinking not supported for filesystem type "%s"`, fsType)
 	}
 
-	err := shrinkFileSystem(fsType, devPath, mntpoint, byteSize)
+	err := driver.ShrinkFileSystem(fsType, devPath, mntpoint, byteSize)
 	return cleanupFunc, err
 }
-
-func storageResource(path string) (*api.ResourcesStoragePool, error) {
-	st, err := shared.Statvfs(path)
-	if err != nil {
-		return nil, err
-	}
-
-	res := api.ResourcesStoragePool{}
-	res.Space.Total = st.Blocks * uint64(st.Bsize)
-	res.Space.Used = (st.Blocks - st.Bfree) * uint64(st.Bsize)
-
-	// Some filesystems don't report inodes since they allocate them
-	// dynamically e.g. btrfs.
-	if st.Files > 0 {
-		res.Inodes.Total = st.Files
-		res.Inodes.Used = st.Files - st.Ffree
-	}
-
-	return &res, nil
-}
diff --git a/lxd/storage_volumes_snapshot.go b/lxd/storage_volumes_snapshot.go
index 8e87a6e925..6de54b156b 100644
--- a/lxd/storage_volumes_snapshot.go
+++ b/lxd/storage_volumes_snapshot.go
@@ -9,6 +9,7 @@ import (
 	"github.com/gorilla/mux"
 
 	"github.com/lxc/lxd/lxd/db"
+	driver "github.com/lxc/lxd/lxd/storage"
 	"github.com/lxc/lxd/lxd/util"
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/api"
@@ -66,7 +67,7 @@ func storagePoolVolumeSnapshotsTypePost(d *Daemon, r *http.Request) Response {
 	}
 
 	// Validate the name
-	err = storageValidName(req.Name)
+	err = driver.ValidName(req.Name)
 	if err != nil {
 		return BadRequest(err)
 	}
diff --git a/lxd/storage_zfs.go b/lxd/storage_zfs.go
index d98f530956..cabdc2f993 100644
--- a/lxd/storage_zfs.go
+++ b/lxd/storage_zfs.go
@@ -273,14 +273,14 @@ func (s *storageZfs) zfsPoolCreate() error {
 	}
 
 	fixperms := shared.VarPath("storage-pools", s.pool.Name, "containers")
-	err = os.MkdirAll(fixperms, containersDirMode)
+	err = os.MkdirAll(fixperms, driver.ContainersDirMode)
 	if err != nil && !os.IsNotExist(err) {
 		return err
 	}
 
-	err = os.Chmod(fixperms, containersDirMode)
+	err = os.Chmod(fixperms, driver.ContainersDirMode)
 	if err != nil {
-		logger.Warnf("Failed to chmod \"%s\" to \"0%s\": %s", fixperms, strconv.FormatInt(int64(containersDirMode), 8), err)
+		logger.Warnf("Failed to chmod \"%s\" to \"0%s\": %s", fixperms, strconv.FormatInt(int64(driver.ContainersDirMode), 8), err)
 	}
 
 	dataset = fmt.Sprintf("%s/images", poolName)
@@ -291,13 +291,13 @@ func (s *storageZfs) zfsPoolCreate() error {
 	}
 
 	fixperms = shared.VarPath("storage-pools", s.pool.Name, "images")
-	err = os.MkdirAll(fixperms, imagesDirMode)
+	err = os.MkdirAll(fixperms, driver.ImagesDirMode)
 	if err != nil && !os.IsNotExist(err) {
 		return err
 	}
-	err = os.Chmod(fixperms, imagesDirMode)
+	err = os.Chmod(fixperms, driver.ImagesDirMode)
 	if err != nil {
-		logger.Warnf("Failed to chmod \"%s\" to \"0%s\": %s", fixperms, strconv.FormatInt(int64(imagesDirMode), 8), err)
+		logger.Warnf("Failed to chmod \"%s\" to \"0%s\": %s", fixperms, strconv.FormatInt(int64(driver.ImagesDirMode), 8), err)
 	}
 
 	dataset = fmt.Sprintf("%s/custom", poolName)
@@ -308,13 +308,13 @@ func (s *storageZfs) zfsPoolCreate() error {
 	}
 
 	fixperms = shared.VarPath("storage-pools", s.pool.Name, "custom")
-	err = os.MkdirAll(fixperms, customDirMode)
+	err = os.MkdirAll(fixperms, driver.CustomDirMode)
 	if err != nil && !os.IsNotExist(err) {
 		return err
 	}
-	err = os.Chmod(fixperms, customDirMode)
+	err = os.Chmod(fixperms, driver.CustomDirMode)
 	if err != nil {
-		logger.Warnf("Failed to chmod \"%s\" to \"0%s\": %s", fixperms, strconv.FormatInt(int64(customDirMode), 8), err)
+		logger.Warnf("Failed to chmod \"%s\" to \"0%s\": %s", fixperms, strconv.FormatInt(int64(driver.CustomDirMode), 8), err)
 	}
 
 	dataset = fmt.Sprintf("%s/deleted", poolName)
@@ -332,13 +332,13 @@ func (s *storageZfs) zfsPoolCreate() error {
 	}
 
 	fixperms = shared.VarPath("storage-pools", s.pool.Name, "containers-snapshots")
-	err = os.MkdirAll(fixperms, snapshotsDirMode)
+	err = os.MkdirAll(fixperms, driver.SnapshotsDirMode)
 	if err != nil && !os.IsNotExist(err) {
 		return err
 	}
-	err = os.Chmod(fixperms, snapshotsDirMode)
+	err = os.Chmod(fixperms, driver.SnapshotsDirMode)
 	if err != nil {
-		logger.Warnf("Failed to chmod \"%s\" to \"0%s\": %s", fixperms, strconv.FormatInt(int64(snapshotsDirMode), 8), err)
+		logger.Warnf("Failed to chmod \"%s\" to \"0%s\": %s", fixperms, strconv.FormatInt(int64(driver.SnapshotsDirMode), 8), err)
 	}
 
 	dataset = fmt.Sprintf("%s/custom-snapshots", poolName)
@@ -349,13 +349,13 @@ func (s *storageZfs) zfsPoolCreate() error {
 	}
 
 	fixperms = shared.VarPath("storage-pools", s.pool.Name, "custom-snapshots")
-	err = os.MkdirAll(fixperms, snapshotsDirMode)
+	err = os.MkdirAll(fixperms, driver.SnapshotsDirMode)
 	if err != nil && !os.IsNotExist(err) {
 		return err
 	}
-	err = os.Chmod(fixperms, snapshotsDirMode)
+	err = os.Chmod(fixperms, driver.SnapshotsDirMode)
 	if err != nil {
-		logger.Warnf("Failed to chmod \"%s\" to \"0%s\": %s", fixperms, strconv.FormatInt(int64(snapshotsDirMode), 8), err)
+		logger.Warnf("Failed to chmod \"%s\" to \"0%s\": %s", fixperms, strconv.FormatInt(int64(driver.SnapshotsDirMode), 8), err)
 	}
 
 	return nil
diff --git a/lxd/storage_zfs_utils.go b/lxd/storage_zfs_utils.go
index 7e42cab87c..6bd0403d59 100644
--- a/lxd/storage_zfs_utils.go
+++ b/lxd/storage_zfs_utils.go
@@ -14,6 +14,7 @@ import (
 	"golang.org/x/sys/unix"
 
 	"github.com/lxc/lxd/lxd/project"
+	driver "github.com/lxc/lxd/lxd/storage"
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/logger"
 )
@@ -519,7 +520,7 @@ func zfsUmount(poolName string, path string, mountpoint string) error {
 		fmt.Sprintf("%s/%s", poolName, path))
 	if err != nil {
 		logger.Warnf("Failed to unmount ZFS filesystem via zfs unmount: %s. Trying lazy umount (MNT_DETACH)...", output)
-		err := tryUnmount(mountpoint, unix.MNT_DETACH)
+		err := driver.TryUnmount(mountpoint, unix.MNT_DETACH)
 		if err != nil {
 			logger.Warnf("Failed to unmount ZFS filesystem via lazy umount (MNT_DETACH)...")
 			return err
@@ -682,7 +683,7 @@ func (s *storageZfs) doContainerMount(projectName, name string, privileged bool)
 	if !shared.IsMountPoint(containerPoolVolumeMntPoint) {
 		source := fmt.Sprintf("%s/%s", s.getOnDiskPoolName(), fs)
 		zfsMountOptions := fmt.Sprintf("rw,zfsutil,mntpoint=%s", containerPoolVolumeMntPoint)
-		mounterr := tryMount(source, containerPoolVolumeMntPoint, "zfs", 0, zfsMountOptions)
+		mounterr := driver.TryMount(source, containerPoolVolumeMntPoint, "zfs", 0, zfsMountOptions)
 		if mounterr != nil {
 			if mounterr != unix.EBUSY {
 				logger.Errorf("Failed to mount ZFS dataset \"%s\" onto \"%s\": %v", source, containerPoolVolumeMntPoint, mounterr)

From 30ac4707cec720a8f3b25b3558f37295a35b2061 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Thu, 15 Aug 2019 15:17:01 +0200
Subject: [PATCH 4/4] test: Lint storage package

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 test/suites/static_analysis.sh | 1 +
 1 file changed, 1 insertion(+)

diff --git a/test/suites/static_analysis.sh b/test/suites/static_analysis.sh
index 1c601e9019..cd2da54018 100644
--- a/test/suites/static_analysis.sh
+++ b/test/suites/static_analysis.sh
@@ -82,6 +82,7 @@ test_static_analysis() {
       #golint -set_exit_status lxd/migration
       golint -set_exit_status lxd/node
       golint -set_exit_status lxd/state
+      golint -set_exit_status lxd/storage
       golint -set_exit_status lxd/sys
       golint -set_exit_status lxd/task
       golint -set_exit_status lxd/template


More information about the lxc-devel mailing list