The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/6522

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===
Includes https://github.com/lxc/lxd/pull/6521
From 6d9ad09ee7f3f98b1a8efd6bc290bc816f2c7433 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 27 Nov 2019 12:25:05 +0000
Subject: [PATCH 01/25] lxd/backup/backup/instance/config: Adds instance config
 backup.yml tools

Moved from main package and cleaned up.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/backup/backup_instance_config.go | 122 +++++++++++++++++++++++++++
 1 file changed, 122 insertions(+)
 create mode 100644 lxd/backup/backup_instance_config.go

diff --git a/lxd/backup/backup_instance_config.go 
b/lxd/backup/backup_instance_config.go
new file mode 100644
index 0000000000..a871ad0a7e
--- /dev/null
+++ b/lxd/backup/backup_instance_config.go
@@ -0,0 +1,122 @@
+package backup
+
+import (
+       "fmt"
+       "io/ioutil"
+       "os"
+
+       "gopkg.in/yaml.v2"
+
+       "github.com/lxc/lxd/lxd/db"
+       "github.com/lxc/lxd/lxd/project"
+       "github.com/lxc/lxd/shared"
+       "github.com/lxc/lxd/shared/api"
+)
+
+// InstanceConfig represents the config of an instance that can be stored in a 
backup.yaml file.
+type InstanceConfig struct {
+       Container *api.Instance           `yaml:"container"`
+       Snapshots []*api.InstanceSnapshot `yaml:"snapshots"`
+       Pool      *api.StoragePool        `yaml:"pool"`
+       Volume    *api.StorageVolume      `yaml:"volume"`
+}
+
+// ParseInstanceConfigYamlFile decodes the yaml file at path specified into an 
InstanceConfig.
+func ParseInstanceConfigYamlFile(path string) (*InstanceConfig, error) {
+       data, err := ioutil.ReadFile(path)
+       if err != nil {
+               return nil, err
+       }
+
+       backup := InstanceConfig{}
+       if err := yaml.Unmarshal(data, &backup); err != nil {
+               return nil, err
+       }
+
+       return &backup, nil
+}
+
+// updateRootDevicePool updates the root disk device in the supplied list of 
devices to the pool
+// specified. Returns true if a root disk device has been found and updated 
otherwise false.
+func updateRootDevicePool(devices map[string]map[string]string, poolName 
string) bool {
+       if devices != nil {
+               devName, _, err := shared.GetRootDiskDevice(devices)
+               if err == nil {
+                       devices[devName]["pool"] = poolName
+                       return true
+               }
+       }
+
+       return false
+}
+
+// UpdateInstanceConfigStoragePool changes the pool information in the 
backup.yaml to the pool
+// specified in b.Pool.
+func UpdateInstanceConfigStoragePool(c *db.Cluster, b Info) error {
+       // Load the storage pool.
+       _, pool, err := c.StoragePoolGet(b.Pool)
+       if err != nil {
+               return err
+       }
+
+       f := func(path string) error {
+               // Read in the backup.yaml file.
+               backup, err := ParseInstanceConfigYamlFile(path)
+               if err != nil {
+                       return err
+               }
+
+               rootDiskDeviceFound := false
+
+               // Change the pool in the backup.yaml.
+               backup.Pool = pool
+
+               if updateRootDevicePool(backup.Container.Devices, pool.Name) {
+                       rootDiskDeviceFound = true
+               }
+
+               if updateRootDevicePool(backup.Container.ExpandedDevices, 
pool.Name) {
+                       rootDiskDeviceFound = true
+               }
+
+               for _, snapshot := range backup.Snapshots {
+                       updateRootDevicePool(snapshot.Devices, pool.Name)
+                       updateRootDevicePool(snapshot.ExpandedDevices, 
pool.Name)
+               }
+
+               if !rootDiskDeviceFound {
+                       return fmt.Errorf("No root device could be found")
+               }
+
+               file, err := os.Create(path)
+               if err != nil {
+                       return err
+               }
+               defer file.Close()
+
+               data, err := yaml.Marshal(&backup)
+               if err != nil {
+                       return err
+               }
+
+               _, err = file.Write(data)
+               if err != nil {
+                       return err
+               }
+
+               return nil
+       }
+
+       err = f(shared.VarPath("storage-pools", pool.Name, "containers", 
project.Prefix(b.Project, b.Name), "backup.yaml"))
+       if err != nil {
+               return err
+       }
+
+       for _, snap := range b.Snapshots {
+               err = f(shared.VarPath("storage-pools", pool.Name, 
"containers-snapshots", project.Prefix(b.Project, b.Name), snap, "backup.yaml"))
+               if err != nil {
+                       return err
+               }
+       }
+       return nil
+}

From 66cbcfe212c3330b4357814dea2ad9accd095699 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 27 Nov 2019 12:26:08 +0000
Subject: [PATCH 02/25] lxd/api/internal: Removes slurpBackupFile and switches
 to backup.ParseInstanceConfigYamlFile

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/api_internal.go | 20 ++------------------
 1 file changed, 2 insertions(+), 18 deletions(-)

diff --git a/lxd/api_internal.go b/lxd/api_internal.go
index 957b0107b7..38ee0da172 100644
--- a/lxd/api_internal.go
+++ b/lxd/api_internal.go
@@ -4,7 +4,6 @@ import (
        "database/sql"
        "encoding/json"
        "fmt"
-       "io/ioutil"
        "net/http"
        "os"
        "path/filepath"
@@ -15,8 +14,8 @@ import (
 
        "github.com/gorilla/mux"
        "github.com/pkg/errors"
-       "gopkg.in/yaml.v2"
 
+       "github.com/lxc/lxd/lxd/backup"
        "github.com/lxc/lxd/lxd/db"
        "github.com/lxc/lxd/lxd/db/cluster"
        "github.com/lxc/lxd/lxd/db/node"
@@ -390,21 +389,6 @@ func internalSQLExec(tx *sql.Tx, query string, result 
*internalSQLResult) error
        return nil
 }
 
-func slurpBackupFile(path string) (*backupFile, error) {
-       data, err := ioutil.ReadFile(path)
-       if err != nil {
-               return nil, err
-       }
-
-       backup := backupFile{}
-
-       if err := yaml.Unmarshal(data, &backup); err != nil {
-               return nil, err
-       }
-
-       return &backup, nil
-}
-
 type internalImportPost struct {
        Name  string `json:"name" yaml:"name"`
        Force bool   `json:"force" yaml:"force"`
@@ -476,7 +460,7 @@ func internalImport(d *Daemon, r *http.Request) 
response.Response {
 
        // Read in the backup.yaml file.
        backupYamlPath := filepath.Join(containerMntPoint, "backup.yaml")
-       backup, err := slurpBackupFile(backupYamlPath)
+       backup, err := backup.ParseInstanceConfigYamlFile(backupYamlPath)
        if err != nil {
                return response.SmartError(err)
        }

From 036decee073c92bef4ae056d77cc65bd5b510fe9 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 27 Nov 2019 12:26:38 +0000
Subject: [PATCH 03/25] lxd/backup: Removes backupFixStoragePool

Replaced by backup.UpdateInstanceConfigStoragePool

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/backup.go | 94 ---------------------------------------------------
 1 file changed, 94 deletions(-)

diff --git a/lxd/backup.go b/lxd/backup.go
index be3b24511b..15189bffd7 100644
--- a/lxd/backup.go
+++ b/lxd/backup.go
@@ -101,100 +101,6 @@ func backupCreate(s *state.State, args 
db.InstanceBackupArgs, sourceInst instanc
        return nil
 }
 
-// fixBackupStoragePool changes the pool information in the backup.yaml. This
-// is done only if the provided pool doesn't exist. In this case, the pool of
-// the default profile will be used.
-func backupFixStoragePool(c *db.Cluster, b backup.Info, useDefaultPool bool) 
error {
-       var poolName string
-
-       if useDefaultPool {
-               // Get the default profile
-               _, profile, err := c.ProfileGet("default", "default")
-               if err != nil {
-                       return err
-               }
-
-               _, v, err := shared.GetRootDiskDevice(profile.Devices)
-               if err != nil {
-                       return err
-               }
-
-               poolName = v["pool"]
-       } else {
-               poolName = b.Pool
-       }
-
-       // Get the default's profile pool
-       _, pool, err := c.StoragePoolGet(poolName)
-       if err != nil {
-               return err
-       }
-
-       f := func(path string) error {
-               // Read in the backup.yaml file.
-               backup, err := slurpBackupFile(path)
-               if err != nil {
-                       return err
-               }
-
-               rootDiskDeviceFound := false
-
-               // Change the pool in the backup.yaml
-               backup.Pool = pool
-               if backup.Container.Devices != nil {
-                       devName, _, err := 
shared.GetRootDiskDevice(backup.Container.Devices)
-                       if err == nil {
-                               backup.Container.Devices[devName]["pool"] = 
poolName
-                               rootDiskDeviceFound = true
-                       }
-               }
-
-               if backup.Container.ExpandedDevices != nil {
-                       devName, _, err := 
shared.GetRootDiskDevice(backup.Container.ExpandedDevices)
-                       if err == nil {
-                               
backup.Container.ExpandedDevices[devName]["pool"] = poolName
-                               rootDiskDeviceFound = true
-                       }
-               }
-
-               if !rootDiskDeviceFound {
-                       return fmt.Errorf("No root device could be found")
-               }
-
-               file, err := os.Create(path)
-               if err != nil {
-                       return err
-               }
-               defer file.Close()
-
-               data, err := yaml.Marshal(&backup)
-               if err != nil {
-                       return err
-               }
-
-               _, err = file.Write(data)
-               if err != nil {
-                       return err
-               }
-
-               return nil
-       }
-
-       err = f(shared.VarPath("storage-pools", pool.Name, "containers", 
project.Prefix(b.Project, b.Name), "backup.yaml"))
-       if err != nil {
-               return err
-       }
-
-       for _, snap := range b.Snapshots {
-               err = f(shared.VarPath("storage-pools", pool.Name, 
"containers-snapshots", project.Prefix(b.Project, b.Name), snap,
-                       "backup.yaml"))
-               if err != nil {
-                       return err
-               }
-       }
-       return nil
-}
-
 func backupCreateTarball(s *state.State, path string, b backup.Backup, c 
instance.Instance) error {
        // Create the index
        pool, err := c.StoragePool()

From 9f74401c5453ab4943570806738cf918496e158d Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 27 Nov 2019 12:28:25 +0000
Subject: [PATCH 04/25] lxd/containers/post: Updates instanceCreateFromBackup
 usage

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/containers_post.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/containers_post.go b/lxd/containers_post.go
index eae96c5597..c3440ce4fb 100644
--- a/lxd/containers_post.go
+++ b/lxd/containers_post.go
@@ -657,7 +657,7 @@ func createFromBackup(d *Daemon, project string, data 
io.Reader, pool string) re
 
                // Dump tarball to storage.
                f.Seek(0, 0)
-               revertFunc, err := instanceCreateFromBackup(d.State(), *bInfo, 
f, pool != "")
+               revertFunc, err := instanceCreateFromBackup(d.State(), *bInfo, 
f)
                if err != nil {
                        return errors.Wrap(err, "Create instance from backup")
                }

From 2c7d6fbff24fc870bd740b30d4b9829fb69927ab Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 27 Nov 2019 15:43:27 +0000
Subject: [PATCH 05/25] lxd/container: Updates instanceCreateFromBackup
 signature

- Removes need for customBool variable and instead will just always update 
backup.yaml file to use current storage pool.
- This simplifies the function signature needed later for 
pool.CreateInstanceFromBackup().

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/container.go | 27 ++++++++++-----------------
 1 file changed, 10 insertions(+), 17 deletions(-)

diff --git a/lxd/container.go b/lxd/container.go
index 125e5c4b8e..625c57ffd4 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -307,13 +307,10 @@ func instanceCreateAsEmpty(d *Daemon, args 
db.InstanceArgs) (instance.Instance,
 
 // instanceCreateFromBackup imports a backup file to restore an instance. 
Returns a revert function
 // that can be called to delete the files created during import if subsequent 
steps fail.
-func instanceCreateFromBackup(s *state.State, info backup.Info, srcData 
io.ReadSeeker, customPool bool) (func(), error) {
-       var fixBackupFile = false
-       poolName := info.Pool
-
+func instanceCreateFromBackup(s *state.State, info backup.Info, srcData 
io.ReadSeeker) (func(), error) {
        // Check storage pool from index.yaml exists. If the storage pool in 
the index.yaml doesn't
-       // exist, try and use the default profile's storage pool.
-       _, _, err := s.Cluster.StoragePoolGet(poolName)
+       // exist, try and use the project's default profile storage pool.
+       _, _, err := s.Cluster.StoragePoolGet(info.Pool)
        if errors.Cause(err) == db.ErrNoSuchObject {
                // The backup is in binary format so we cannot alter the 
backup.yaml.
                if info.HasBinaryFormat {
@@ -332,10 +329,7 @@ func instanceCreateFromBackup(s *state.State, info 
backup.Info, srcData io.ReadS
                }
 
                // Use the default-profile's root pool.
-               poolName = v["pool"]
-
-               // Mark requirement to change the pool information in the 
backup.yaml file.
-               fixBackupFile = true
+               info.Pool = v["pool"]
        } else if err != nil {
                return nil, err
        }
@@ -385,7 +379,7 @@ func instanceCreateFromBackup(s *state.State, info 
backup.Info, srcData io.ReadS
                srcData = tarData
        }
 
-       pool, err := storagePoolInit(s, poolName)
+       pool, err := storagePoolInit(s, info.Pool)
        if err != nil {
                return nil, err
        }
@@ -396,12 +390,11 @@ func instanceCreateFromBackup(s *state.State, info 
backup.Info, srcData io.ReadS
                return nil, err
        }
 
-       if fixBackupFile || customPool {
-               // Update pool information in the backup.yaml file.
-               err = backupFixStoragePool(s.Cluster, info, !customPool)
-               if err != nil {
-                       return nil, err
-               }
+       // Update pool information in the backup.yaml file.
+       // Requires the volume and snapshots be mounted from 
pool.ContainerBackupLoad().
+       err = backup.UpdateInstanceConfigStoragePool(s.Cluster, info)
+       if err != nil {
+               return nil, err
        }
 
        // Create revert function to remove the files created so far.

From 46151f83a197316b4e89113aae0f6e22491d5812 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 28 Nov 2019 09:07:25 +0000
Subject: [PATCH 06/25] lxd/backup/backup/instance/config:
 UpdateInstanceConfigStoragePool no longer updates snapshots backup.yaml

Several storage drivers do not make backup.yaml available during backup restore 
(for example BTRFS snapshots are read only, and ZFS needs to be mounted).

As such we should only update the pool name of the main volume's backup.yaml 
during backup restore.

We regenerate the backup.yaml file of snapshots when they are restored to the 
main instance instead.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/backup/backup_instance_config.go | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/lxd/backup/backup_instance_config.go 
b/lxd/backup/backup_instance_config.go
index a871ad0a7e..d15dca1744 100644
--- a/lxd/backup/backup_instance_config.go
+++ b/lxd/backup/backup_instance_config.go
@@ -112,11 +112,5 @@ func UpdateInstanceConfigStoragePool(c *db.Cluster, b 
Info) error {
                return err
        }
 
-       for _, snap := range b.Snapshots {
-               err = f(shared.VarPath("storage-pools", pool.Name, 
"containers-snapshots", project.Prefix(b.Project, b.Name), snap, "backup.yaml"))
-               if err != nil {
-                       return err
-               }
-       }
        return nil
 }

From 1479cfb87005966497623f79f51d49cb4d4ff673 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 28 Nov 2019 09:10:05 +0000
Subject: [PATCH 07/25] lxd: Comment improvements

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/container.go     | 10 +++++-----
 lxd/container_lxc.go |  3 ++-
 lxd/vm_qemu.go       |  4 ++--
 3 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/lxd/container.go b/lxd/container.go
index 625c57ffd4..6a251fe92d 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -736,7 +736,7 @@ func instanceCreateAsSnapshot(s *state.State, args 
db.InstanceArgs, sourceInstan
                return nil, fmt.Errorf("Source instance and snapshot instance 
types do not match")
        }
 
-       // Deal with state
+       // Deal with state.
        if args.Stateful {
                if !sourceInstance.IsRunning() {
                        return nil, fmt.Errorf("Unable to create a stateful 
snapshot. The instance isn't running")
@@ -781,7 +781,7 @@ func instanceCreateAsSnapshot(s *state.State, args 
db.InstanceArgs, sourceInstan
                }
        }
 
-       // Create the snapshot
+       // Create the snapshot.
        inst, err := instanceCreateInternal(s, args)
        if err != nil {
                return nil, err
@@ -836,13 +836,13 @@ func instanceCreateAsSnapshot(s *state.State, args 
db.InstanceArgs, sourceInstan
                return nil, fmt.Errorf("Instance type not supported")
        }
 
-       // Attempt to update backup.yaml for instance
+       // Attempt to update backup.yaml for instance.
        err = writeBackupFile(sourceInstance)
        if err != nil {
                return nil, err
        }
 
-       // Once we're done, remove the state directory
+       // Once we're done, remove the state directory.
        if args.Stateful {
                os.RemoveAll(sourceInstance.StatePath())
        }
@@ -1113,7 +1113,7 @@ func instanceConfigureInternal(state *state.State, c 
instance.Instance) error {
 
                ct := c.(*containerLXC)
 
-               // handle quota: at this point, storage is guaranteed to be 
ready
+               // handle quota: at this point, storage is guaranteed to be 
ready.
                storage := ct.Storage()
                if rootDiskDevice["size"] != "" {
                        storageTypeName := storage.GetStorageTypeName()
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index c15289ac17..6f6977ac72 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -3328,6 +3328,7 @@ func (c *containerLXC) Restore(sourceContainer 
instance.Instance, stateful bool)
                        return err
                }
 
+               // Ensure that storage is mounted for state path checks and for 
backup.yaml updates.
                ourStart, err := pool.MountInstance(c, nil)
                if err != nil {
                        return err
@@ -3396,7 +3397,7 @@ func (c *containerLXC) Restore(sourceContainer 
instance.Instance, stateful bool)
                        return err
                }
 
-               // Ensure that storage is mounted for state path checks.
+               // Ensure that storage is mounted for state path checks and for 
backup.yaml updates.
                if pool != nil {
                        ourStart, err := pool.MountInstance(c, nil)
                        if err != nil {
diff --git a/lxd/vm_qemu.go b/lxd/vm_qemu.go
index e9002f18f9..62df3470f1 100644
--- a/lxd/vm_qemu.go
+++ b/lxd/vm_qemu.go
@@ -1660,14 +1660,14 @@ func (vm *vmQemu) Update(args db.InstanceArgs, 
userRequested bool) error {
        }
 
        if shared.StringInSlice("security.secureboot", changedConfig) {
-               // Re-generate the NVRAM
+               // Re-generate the NVRAM.
                err = vm.setupNvram()
                if err != nil {
                        return err
                }
        }
 
-       // Finally, apply the changes to the database
+       // Finally, apply the changes to the database.
        err = query.Retry(func() error {
                tx, err := vm.state.Cluster.Begin()
                if err != nil {

From a0ef27d1c045631ee7cd9ba8f186fe3e0f8a332d Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 28 Nov 2019 09:22:38 +0000
Subject: [PATCH 08/25] lxd/containers/post: Adds storage pool check to
 createFromBackup

This check differentiates between a user specified pool from the CLI vs the 
pool contained within the backup.yaml.

If the latter pool doesn't exist then the project's default pool is used, 
however if the former doesn't exist then an error is returned.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/containers_post.go | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/lxd/containers_post.go b/lxd/containers_post.go
index c3440ce4fb..d07066f337 100644
--- a/lxd/containers_post.go
+++ b/lxd/containers_post.go
@@ -652,6 +652,33 @@ func createFromBackup(d *Daemon, project string, data 
io.Reader, pool string) re
                bInfo.Pool = pool
        }
 
+       // Check storage pool exists.
+       _, _, err = d.State().Cluster.StoragePoolGet(bInfo.Pool)
+       if errors.Cause(err) == db.ErrNoSuchObject {
+               // The storage pool doesn't exist. If backup is in binary 
format (so we cannot alter
+               // the backup.yaml) or the pool has been specified directly 
from the user restoring
+               // the backup then we cannot proceed so return an error.
+               if bInfo.HasBinaryFormat || pool != "" {
+                       return response.InternalError(errors.Wrap(err, "Storage 
pool not found"))
+               }
+
+               // Otherwise try and restore to the project's default profile 
pool.
+               _, profile, err := d.State().Cluster.ProfileGet(bInfo.Project, 
"default")
+               if err != nil {
+                       return response.InternalError(errors.Wrap(err, "Failed 
to get default profile"))
+               }
+
+               _, v, err := shared.GetRootDiskDevice(profile.Devices)
+               if err != nil {
+                       return response.InternalError(errors.Wrap(err, "Failed 
to get root disk device"))
+               }
+
+               // Use the default-profile's root pool.
+               bInfo.Pool = v["pool"]
+       } else if err != nil {
+               return response.InternalError(err)
+       }
+
        run := func(op *operations.Operation) error {
                defer f.Close()
 

From e33c299ec5627ff71e6e95a3997f489512ae3897 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 28 Nov 2019 09:23:47 +0000
Subject: [PATCH 09/25] lxd/container: Removes storage pool check from
 instanceCreateFromBackup

So that this can be done earlier on where the user specified pool can be 
differentiated between that and the pool inside backup.yaml.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/container.go | 26 --------------------------
 1 file changed, 26 deletions(-)

diff --git a/lxd/container.go b/lxd/container.go
index 6a251fe92d..3cd9f24cfe 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -308,32 +308,6 @@ func instanceCreateAsEmpty(d *Daemon, args 
db.InstanceArgs) (instance.Instance,
 // instanceCreateFromBackup imports a backup file to restore an instance. 
Returns a revert function
 // that can be called to delete the files created during import if subsequent 
steps fail.
 func instanceCreateFromBackup(s *state.State, info backup.Info, srcData 
io.ReadSeeker) (func(), error) {
-       // Check storage pool from index.yaml exists. If the storage pool in 
the index.yaml doesn't
-       // exist, try and use the project's default profile storage pool.
-       _, _, err := s.Cluster.StoragePoolGet(info.Pool)
-       if errors.Cause(err) == db.ErrNoSuchObject {
-               // The backup is in binary format so we cannot alter the 
backup.yaml.
-               if info.HasBinaryFormat {
-                       return nil, err
-               }
-
-               // Get the default profile.
-               _, profile, err := s.Cluster.ProfileGet(info.Project, "default")
-               if err != nil {
-                       return nil, errors.Wrap(err, "Failed to get default 
profile")
-               }
-
-               _, v, err := shared.GetRootDiskDevice(profile.Devices)
-               if err != nil {
-                       return nil, errors.Wrap(err, "Failed to get root disk 
device")
-               }
-
-               // Use the default-profile's root pool.
-               info.Pool = v["pool"]
-       } else if err != nil {
-               return nil, err
-       }
-
        // Find the compression algorithm.
        tarArgs, algo, decomArgs, err := shared.DetectCompressionFile(srcData)
        if err != nil {

From 0e786b9dbc76a7c3366b65302ea5b8aceebc067f Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 27 Nov 2019 16:12:29 +0000
Subject: [PATCH 10/25] lxd/backup/backup: Removes squashfs handling from
 GetInfo

Expects to always be passed an (optionally compressed) tarball now.

Squashfs files should be converted to a tarball before being passed to this 
function.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/backup/backup.go | 23 +----------------------
 1 file changed, 1 insertion(+), 22 deletions(-)

diff --git a/lxd/backup/backup.go b/lxd/backup/backup.go
index 421517d1f4..8f3fdc1067 100644
--- a/lxd/backup/backup.go
+++ b/lxd/backup/backup.go
@@ -4,7 +4,6 @@ import (
        "archive/tar"
        "fmt"
        "io"
-       "io/ioutil"
        "os"
        "os/exec"
        "strings"
@@ -48,7 +47,7 @@ func GetInfo(r io.ReadSeeker) (*Info, error) {
 
        // Extract
        r.Seek(0, 0)
-       _, algo, unpacker, err := shared.DetectCompressionFile(r)
+       _, _, unpacker, err := shared.DetectCompressionFile(r)
        if err != nil {
                return nil, err
        }
@@ -59,26 +58,6 @@ func GetInfo(r io.ReadSeeker) (*Info, error) {
        }
 
        if len(unpacker) > 0 {
-               if algo == ".squashfs" {
-                       // 'sqfs2tar' tool does not support reading from stdin. 
So
-                       // create a temporary file to write the compressed data 
and
-                       // pass it as program argument
-                       tempfile, err := ioutil.TempFile("", "lxd_decompress_")
-                       if err != nil {
-                               return nil, err
-                       }
-                       defer tempfile.Close()
-                       defer os.Remove(tempfile.Name())
-
-                       // Write compressed data
-                       _, err = io.Copy(tempfile, r)
-                       if err != nil {
-                               return nil, err
-                       }
-
-                       // Prepare to pass the temporary file as program 
argument
-                       unpacker = append(unpacker, tempfile.Name())
-               }
                cmd := exec.Command(unpacker[0], unpacker[1:]...)
                cmd.Stdin = r
 

From e9372b8b3eb158d5bc3ec7f7db001ecc89a06c0a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 27 Nov 2019 16:14:08 +0000
Subject: [PATCH 11/25] lxd/container: Removes squashfs handling from
 instanceCreateBackup

Expects to always be passed an (optionally compressed) tarball now.

Squashfs files should be converted to a tarball before being passed to this 
function.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/container.go | 41 +----------------------------------------
 1 file changed, 1 insertion(+), 40 deletions(-)

diff --git a/lxd/container.go b/lxd/container.go
index 3cd9f24cfe..0b13dd3eb1 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -4,7 +4,6 @@ import (
        "context"
        "fmt"
        "io"
-       "io/ioutil"
        "os"
        "os/exec"
        "path/filepath"
@@ -309,50 +308,12 @@ func instanceCreateAsEmpty(d *Daemon, args 
db.InstanceArgs) (instance.Instance,
 // that can be called to delete the files created during import if subsequent 
steps fail.
 func instanceCreateFromBackup(s *state.State, info backup.Info, srcData 
io.ReadSeeker) (func(), error) {
        // Find the compression algorithm.
-       tarArgs, algo, decomArgs, err := shared.DetectCompressionFile(srcData)
+       tarArgs, _, _, err := shared.DetectCompressionFile(srcData)
        if err != nil {
                return nil, err
        }
        srcData.Seek(0, 0)
 
-       if algo == ".squashfs" {
-               // The 'sqfs2tar' tool does not support reading from stdin. So 
we need to create a
-               // temporary file, write the compressed data to it and pass it 
as program argument.
-               tempFile, err := ioutil.TempFile("", "lxd_decompress_")
-               if err != nil {
-                       return nil, err
-               }
-               defer tempFile.Close()
-               defer os.Remove(tempFile.Name())
-
-               // Copy the compressed data stream to the temporart file.
-               _, err = io.Copy(tempFile, srcData)
-               if err != nil {
-                       return nil, err
-               }
-
-               // Pass the temporary file as program argument to the 
decompression command.
-               decomArgs := append(decomArgs, tempFile.Name())
-
-               // Create another temporary file to write the decompressed data.
-               tarData, err := ioutil.TempFile("", "lxd_decompress_")
-               if err != nil {
-                       return nil, err
-               }
-               defer tarData.Close()
-               defer os.Remove(tarData.Name())
-
-               // Decompress to tarData temporary file.
-               err = shared.RunCommandWithFds(nil, tarData, decomArgs[0], 
decomArgs[1:]...)
-               if err != nil {
-                       return nil, err
-               }
-               tarData.Seek(0, 0)
-
-               // Set source data stream to newly created tar file handle.
-               srcData = tarData
-       }
-
        pool, err := storagePoolInit(s, info.Pool)
        if err != nil {
                return nil, err

From 2586514bb72cb79f3397d26dd74e181caf1b007c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 27 Nov 2019 16:14:54 +0000
Subject: [PATCH 12/25] lxd/containers/post: Moves backup restore squashfs
 handling to createFromBackup

This avoids copying the uploaded backup file to multiple temporary files and 
simplifies the downstream logic.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/containers_post.go | 58 ++++++++++++++++++++++++++++++++++--------
 1 file changed, 47 insertions(+), 11 deletions(-)

diff --git a/lxd/containers_post.go b/lxd/containers_post.go
index d07066f337..774e12891b 100644
--- a/lxd/containers_post.go
+++ b/lxd/containers_post.go
@@ -625,24 +625,59 @@ func createFromCopy(d *Daemon, project string, req 
*api.InstancesPost) response.
 }
 
 func createFromBackup(d *Daemon, project string, data io.Reader, pool string) 
response.Response {
-       // Write the data to a temp file.
-       f, err := ioutil.TempFile("", "lxd_backup_")
+       // Create temporary file to store uploaded backup data.
+       backupFile, err := ioutil.TempFile("", "lxd_backup_")
        if err != nil {
                return response.InternalError(err)
        }
-       defer os.Remove(f.Name())
+       defer os.Remove(backupFile.Name())
 
-       _, err = io.Copy(f, data)
+       // Stream uploaded backup data into temporary file.
+       _, err = io.Copy(backupFile, data)
        if err != nil {
-               f.Close()
+               backupFile.Close()
                return response.InternalError(err)
        }
 
+       // Detect squashfs compression and convert to tarball.
+       backupFile.Seek(0, 0)
+       _, algo, decomArgs, err := shared.DetectCompressionFile(backupFile)
+       if err != nil {
+               backupFile.Close()
+               return response.InternalError(err)
+       }
+
+       if algo == ".squashfs" {
+               // Pass the temporary file as program argument to the 
decompression command.
+               decomArgs := append(decomArgs, backupFile.Name())
+
+               // Create temporary file to store the decompressed tarball in.
+               tarFile, err := ioutil.TempFile("", "lxd_decompress_")
+               if err != nil {
+                       backupFile.Close()
+                       return response.InternalError(err)
+               }
+               defer os.Remove(tarFile.Name())
+
+               // Decompress to tarData temporary file.
+               err = shared.RunCommandWithFds(nil, tarFile, decomArgs[0], 
decomArgs[1:]...)
+               if err != nil {
+                       return response.InternalError(err)
+               }
+
+               // We don't need the original squashfs file anymore.
+               backupFile.Close()
+               os.Remove(backupFile.Name())
+
+               // Replace the backup file handle with the handle to the tar 
file.
+               backupFile = tarFile
+       }
+
        // Parse the backup information.
-       f.Seek(0, 0)
-       bInfo, err := backup.GetInfo(f)
+       backupFile.Seek(0, 0)
+       bInfo, err := backup.GetInfo(backupFile)
        if err != nil {
-               f.Close()
+               backupFile.Close()
                return response.BadRequest(err)
        }
        bInfo.Project = project
@@ -680,11 +715,11 @@ func createFromBackup(d *Daemon, project string, data 
io.Reader, pool string) re
        }
 
        run := func(op *operations.Operation) error {
-               defer f.Close()
+               defer backupFile.Close()
 
                // Dump tarball to storage.
-               f.Seek(0, 0)
-               revertFunc, err := instanceCreateFromBackup(d.State(), *bInfo, 
f)
+               backupFile.Seek(0, 0)
+               revertFunc, err := instanceCreateFromBackup(d.State(), *bInfo, 
backupFile)
                if err != nil {
                        return errors.Wrap(err, "Create instance from backup")
                }
@@ -739,6 +774,7 @@ func createFromBackup(d *Daemon, project string, data 
io.Reader, pool string) re
        op, err := operations.OperationCreate(d.State(), project, 
operations.OperationClassTask, db.OperationBackupRestore,
                resources, nil, run, nil, nil)
        if err != nil {
+               backupFile.Close()
                return response.InternalError(err)
        }
 

From dac07e89724be61d1eab61bc75bcd589d9754a28 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 27 Nov 2019 12:27:42 +0000
Subject: [PATCH 13/25] lxd/container/lxc: Updates use of backupFile to
 backup.InstanceConfig

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/container_lxc.go | 9 +--------
 1 file changed, 1 insertion(+), 8 deletions(-)

diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 6f6977ac72..09120cfca4 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -3997,13 +3997,6 @@ func (c *containerLXC) VolatileSet(changes 
map[string]string) error {
        return nil
 }
 
-type backupFile struct {
-       Container *api.Instance           `yaml:"container"`
-       Snapshots []*api.InstanceSnapshot `yaml:"snapshots"`
-       Pool      *api.StoragePool        `yaml:"pool"`
-       Volume    *api.StorageVolume      `yaml:"volume"`
-}
-
 func writeBackupFile(c instance.Instance) error {
        // We only write backup files out for actual containers
        if c.IsSnapshot() {
@@ -4058,7 +4051,7 @@ func writeBackupFile(c instance.Instance) error {
                return err
        }
 
-       data, err := yaml.Marshal(&backupFile{
+       data, err := yaml.Marshal(&backup.InstanceConfig{
                Container: ci.(*api.Instance),
                Snapshots: sis,
                Pool:      pool,

From 1acd579173937a2a052261ad08eabb950427d715 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 28 Nov 2019 12:19:35 +0000
Subject: [PATCH 14/25] lxd/storage/backend/lxd: Implements
 CreateInstanceFromBackup

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/backend_lxd.go | 93 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 91 insertions(+), 2 deletions(-)

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 1a904bc203..bd71ccf57e 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -330,8 +330,97 @@ func (b *lxdBackend) CreateInstance(inst 
instance.Instance, op *operations.Opera
        return nil
 }
 
-func (b *lxdBackend) CreateInstanceFromBackup(inst instance.Instance, 
sourcePath string, op *operations.Operation) error {
-       return ErrNotImplemented
+// CreateInstanceFromBackup restores a backup file onto the storage device. 
Because the backup file
+// is unpacked and restored onto the storage device before the instance is 
created in the database
+// it is neccessary to return two functions; a post hook that can be run once 
the instance has been
+// created in the database to run any storage layer finalisations, and a 
revert hook that can be
+// run if the instance database load process fails that will remove anything 
created thus far.
+func (b *lxdBackend) CreateInstanceFromBackup(srcBackup backup.Info, srcData 
io.ReadSeeker, op *operations.Operation) (func(instance.Instance) error, 
func(), error) {
+       logger := logging.AddContext(b.logger, log.Ctx{"project": 
srcBackup.Project, "instance": srcBackup.Name, "snapshots": 
srcBackup.Snapshots, "hasBinaryFormat": srcBackup.HasBinaryFormat})
+       logger.Debug("CreateInstanceFromBackup started")
+       defer logger.Debug("CreateInstanceFromBackup finished")
+
+       // Get the volume name on storage.
+       volStorageName := project.Prefix(srcBackup.Project, srcBackup.Name)
+
+       // Currently there is no concept of instance type in backups, so we 
assume container.
+       // We don't know the volume's config yet as tarball hasn't been 
unpacked.
+       // We will apply the config as part of the post hook function returned 
if driver needs to.
+       vol := b.newVolume(drivers.VolumeTypeContainer, drivers.ContentTypeFS, 
volStorageName, nil)
+
+       revertFuncs := []func(){}
+       defer func() {
+               for _, revertFunc := range revertFuncs {
+                       revertFunc()
+               }
+       }()
+
+       // Unpack the backup into the new storage volume(s).
+       volPostHook, revertHook, err := b.driver.RestoreBackupVolume(vol, 
srcBackup.Snapshots, srcData, op)
+       if err != nil {
+               return nil, nil, err
+       }
+
+       if revertHook != nil {
+               revertFuncs = append(revertFuncs, revertHook)
+       }
+
+       err = b.ensureInstanceSymlink(instancetype.Container, 
srcBackup.Project, srcBackup.Name, vol.MountPath())
+       if err != nil {
+               return nil, nil, err
+       }
+
+       revertFuncs = append(revertFuncs, func() {
+               b.removeInstanceSymlink(instancetype.Container, 
srcBackup.Project, srcBackup.Name)
+       })
+
+       if len(srcBackup.Snapshots) > 0 {
+               err = b.ensureInstanceSnapshotSymlink(instancetype.Container, 
srcBackup.Project, srcBackup.Name)
+               if err != nil {
+                       return nil, nil, err
+               }
+
+               revertFuncs = append(revertFuncs, func() {
+                       
b.removeInstanceSnapshotSymlinkIfUnused(instancetype.Container, 
srcBackup.Project, srcBackup.Name)
+               })
+       }
+
+       // Update pool information in the backup.yaml file.
+       vol.MountTask(func(mountPath string, op *operations.Operation) error {
+               return backup.UpdateInstanceConfigStoragePool(b.state.Cluster, 
srcBackup, mountPath)
+       }, op)
+
+       var postHook func(instance.Instance) error
+       if volPostHook != nil {
+               // Create a post hook function that will use the instance (that 
will be created) to
+               // setup a new volume containing the instance's root disk 
device's config so that
+               // the driver's post hook function can access that config to 
perform any post
+               // instance creation setup.
+               postHook = func(inst instance.Instance) error {
+                       // Get the root disk device config.
+                       _, rootDiskConf, err := 
shared.GetRootDiskDevice(inst.ExpandedDevices().CloneNative())
+                       if err != nil {
+                               return err
+                       }
+
+                       // Get the volume name on storage.
+                       volStorageName := project.Prefix(inst.Project(), 
inst.Name())
+
+                       volType, err := InstanceTypeToVolumeType(inst.Type())
+                       if err != nil {
+                               return err
+                       }
+
+                       contentType := InstanceContentType(inst)
+
+                       // Initialise new volume containing root disk config 
supplied in instance.
+                       vol := b.newVolume(volType, contentType, 
volStorageName, rootDiskConf)
+                       return volPostHook(vol)
+               }
+       }
+
+       revertFuncs = nil
+       return postHook, revertHook, nil
 }
 
 // CreateInstanceFromCopy copies an instance volume and optionally its 
snapshots to new volume(s).

From b8132f00b5d907d631a09b196ebada20535d4e11 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 27 Nov 2019 12:44:01 +0000
Subject: [PATCH 15/25] lxd/storage: Updates CreateInstanceFromBackup signature

Will use an io.ReadSeeker to access backup file source rather than opening file 
path.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/backend_lxd.go    | 1 +
 lxd/storage/backend_mock.go   | 5 +++--
 lxd/storage/pool_interface.go | 3 ++-
 3 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index bd71ccf57e..54bca4617c 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -8,6 +8,7 @@ import (
        "regexp"
        "strings"
 
+       "github.com/lxc/lxd/lxd/backup"
        "github.com/lxc/lxd/lxd/db"
        "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/instancetype"
diff --git a/lxd/storage/backend_mock.go b/lxd/storage/backend_mock.go
index cb3a464b71..089ff98676 100644
--- a/lxd/storage/backend_mock.go
+++ b/lxd/storage/backend_mock.go
@@ -3,6 +3,7 @@ package storage
 import (
        "io"
 
+       "github.com/lxc/lxd/lxd/backup"
        "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/migration"
        "github.com/lxc/lxd/lxd/operations"
@@ -63,8 +64,8 @@ func (b *mockBackend) CreateInstance(inst instance.Instance, 
op *operations.Oper
        return nil
 }
 
-func (b *mockBackend) CreateInstanceFromBackup(inst instance.Instance, 
sourcePath string, op *operations.Operation) error {
-       return nil
+func (b *mockBackend) CreateInstanceFromBackup(srcBackup backup.Info, srcData 
io.ReadSeeker, op *operations.Operation) (func(instance.Instance) error, 
func(), error) {
+       return nil, nil, nil
 }
 
 func (b *mockBackend) CreateInstanceFromCopy(inst instance.Instance, src 
instance.Instance, snapshots bool, op *operations.Operation) error {
diff --git a/lxd/storage/pool_interface.go b/lxd/storage/pool_interface.go
index e00cbcc24f..bd7462345f 100644
--- a/lxd/storage/pool_interface.go
+++ b/lxd/storage/pool_interface.go
@@ -3,6 +3,7 @@ package storage
 import (
        "io"
 
+       "github.com/lxc/lxd/lxd/backup"
        "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/migration"
        "github.com/lxc/lxd/lxd/operations"
@@ -29,7 +30,7 @@ type Pool interface {
 
        // Instances.
        CreateInstance(inst instance.Instance, op *operations.Operation) error
-       CreateInstanceFromBackup(inst instance.Instance, sourcePath string, op 
*operations.Operation) error
+       CreateInstanceFromBackup(srcBackup backup.Info, srcData io.ReadSeeker, 
op *operations.Operation) (func(instance.Instance) error, func(), error)
        CreateInstanceFromCopy(inst instance.Instance, src instance.Instance, 
snapshots bool, op *operations.Operation) error
        CreateInstanceFromImage(inst instance.Instance, fingerprint string, op 
*operations.Operation) error
        CreateInstanceFromMigration(inst instance.Instance, conn 
io.ReadWriteCloser, args migration.VolumeTargetArgs, op *operations.Operation) 
error

From 521b2d73546117c35f0943fbf93dfe484677be04 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 27 Nov 2019 12:32:52 +0000
Subject: [PATCH 16/25] lxd/container: Updates instanceCreateFromBackup to use
 new storage pkg

- pool.CreateInstanceFromBackup() will do decompression for new storage layer.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/container.go | 90 +++++++++++++++++++++++++++++++++---------------
 1 file changed, 62 insertions(+), 28 deletions(-)

diff --git a/lxd/container.go b/lxd/container.go
index 0b13dd3eb1..cf201ca6f0 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -304,40 +304,74 @@ func instanceCreateAsEmpty(d *Daemon, args 
db.InstanceArgs) (instance.Instance,
        return inst, nil
 }
 
-// instanceCreateFromBackup imports a backup file to restore an instance. 
Returns a revert function
-// that can be called to delete the files created during import if subsequent 
steps fail.
-func instanceCreateFromBackup(s *state.State, info backup.Info, srcData 
io.ReadSeeker) (func(), error) {
-       // Find the compression algorithm.
-       tarArgs, _, _, err := shared.DetectCompressionFile(srcData)
-       if err != nil {
-               return nil, err
-       }
-       srcData.Seek(0, 0)
+// instanceCreateFromBackup imports a backup file to restore an instance. 
Because the backup file
+// is unpacked and restored onto the storage device before the instance is 
created in the database
+// it is neccessary to return two functions; a post hook that can be run once 
the instance has been
+// created in the database to run any storage layer finalisations, and a 
revert hook that can be
+// run if the instance database load process fails that will remove anything 
created thus far.
+func instanceCreateFromBackup(s *state.State, info backup.Info, srcData 
io.ReadSeeker) (func(instance.Instance) error, func(), error) {
+       // Define hook functions that will be returned to caller.
+       var postHook func(instance.Instance) error
+       var revertHook func()
 
-       pool, err := storagePoolInit(s, info.Pool)
-       if err != nil {
-               return nil, err
-       }
+       // Check if we can load new storage layer for pool driver type.
+       pool, err := storagePools.GetPoolByName(s, info.Pool)
+       if err != storageDrivers.ErrUnknownDriver {
+               if err != nil {
+                       return nil, nil, err
+               }
 
-       // Unpack tarball from the source tar stream.
-       err = pool.ContainerBackupLoad(info, srcData, tarArgs)
-       if err != nil {
-               return nil, err
-       }
+               postHook, revertHook, err = pool.CreateInstanceFromBackup(info, 
srcData, nil)
+               if err != nil {
+                       return nil, nil, err
+               }
+       } else { // Fallback to old storage layer.
 
-       // Update pool information in the backup.yaml file.
-       // Requires the volume and snapshots be mounted from 
pool.ContainerBackupLoad().
-       err = backup.UpdateInstanceConfigStoragePool(s.Cluster, info)
-       if err != nil {
-               return nil, err
-       }
+               // Find the compression algorithm.
+               srcData.Seek(0, 0)
+               tarArgs, _, _, err := shared.DetectCompressionFile(srcData)
+               if err != nil {
+                       return nil, nil, err
+               }
 
-       // Create revert function to remove the files created so far.
-       revertFunc := func() {
-               pool.ContainerDelete(&containerLXC{name: info.Name, project: 
info.Project})
+               pool, err := storagePoolInit(s, info.Pool)
+               if err != nil {
+                       return nil, nil, err
+               }
+
+               // Unpack tarball from the source tar stream.
+               srcData.Seek(0, 0)
+               err = pool.ContainerBackupLoad(info, srcData, tarArgs)
+               if err != nil {
+                       return nil, nil, err
+               }
+
+               // Update pool information in the backup.yaml file.
+               // Requires the volume and snapshots be mounted from 
pool.ContainerBackupLoad().
+               err = backup.UpdateInstanceConfigStoragePool(s.Cluster, info)
+               if err != nil {
+                       return nil, nil, err
+               }
+
+               // Set revert function to remove the files created so far.
+               revertHook = func() {
+                       // Create a temporary container struct (because the 
container DB record
+                       // hasn't been imported yet) for use with storage layer.
+                       ctTmp := &containerLXC{name: info.Name, project: 
info.Project}
+                       pool.ContainerDelete(ctTmp)
+               }
+
+               postHook = func(inst instance.Instance) error {
+                       _, err = inst.StorageStop()
+                       if err != nil {
+                               return errors.Wrap(err, "Stop storage pool")
+                       }
+
+                       return nil
+               }
        }
 
-       return revertFunc, nil
+       return postHook, revertHook, nil
 }
 
 func containerCreateEmptySnapshot(s *state.State, args db.InstanceArgs) 
(instance.Instance, error) {

From 3cff3b25635e9fe29155756a646babad6d5f3f74 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Wed, 27 Nov 2019 16:55:18 +0000
Subject: [PATCH 17/25] lxd/containers/post: Updates instanceCreateFromBackup
 usage with hooks

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/containers_post.go | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/lxd/containers_post.go b/lxd/containers_post.go
index 774e12891b..9d45831ecc 100644
--- a/lxd/containers_post.go
+++ b/lxd/containers_post.go
@@ -718,8 +718,7 @@ func createFromBackup(d *Daemon, project string, data 
io.Reader, pool string) re
                defer backupFile.Close()
 
                // Dump tarball to storage.
-               backupFile.Seek(0, 0)
-               revertFunc, err := instanceCreateFromBackup(d.State(), *bInfo, 
backupFile)
+               postHook, revertHook, err := 
instanceCreateFromBackup(d.State(), *bInfo, backupFile)
                if err != nil {
                        return errors.Wrap(err, "Create instance from backup")
                }
@@ -730,7 +729,9 @@ func createFromBackup(d *Daemon, project string, data 
io.Reader, pool string) re
                                return
                        }
 
-                       revertFunc()
+                       if revertHook != nil {
+                               revertHook()
+                       }
                }()
 
                body, err := json.Marshal(&internalImportPost{
@@ -758,9 +759,13 @@ func createFromBackup(d *Daemon, project string, data 
io.Reader, pool string) re
                        return errors.Wrap(err, "Load instance")
                }
 
-               _, err = c.StorageStop()
-               if err != nil {
-                       return errors.Wrap(err, "Stop storage pool")
+               // Run the storage post hook to perform any final actions now 
that the instance
+               // has been created in the database.
+               if postHook != nil {
+                       err = postHook(c)
+                       if err != nil {
+                               return errors.Wrap(err, "Post hook")
+                       }
                }
 
                revert = false

From 42b5d89c2bd018024e4344e578a3afaaa3969502 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 28 Nov 2019 12:17:04 +0000
Subject: [PATCH 18/25] lxd/backup/backup/instance/config: Updates
 UpdateInstanceConfigStoragePool to take mount path

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/backup/backup_instance_config.go | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/lxd/backup/backup_instance_config.go 
b/lxd/backup/backup_instance_config.go
index d15dca1744..1a60152371 100644
--- a/lxd/backup/backup_instance_config.go
+++ b/lxd/backup/backup_instance_config.go
@@ -4,11 +4,11 @@ import (
        "fmt"
        "io/ioutil"
        "os"
+       "path/filepath"
 
        "gopkg.in/yaml.v2"
 
        "github.com/lxc/lxd/lxd/db"
-       "github.com/lxc/lxd/lxd/project"
        "github.com/lxc/lxd/shared"
        "github.com/lxc/lxd/shared/api"
 )
@@ -52,7 +52,7 @@ func updateRootDevicePool(devices 
map[string]map[string]string, poolName string)
 
 // UpdateInstanceConfigStoragePool changes the pool information in the 
backup.yaml to the pool
 // specified in b.Pool.
-func UpdateInstanceConfigStoragePool(c *db.Cluster, b Info) error {
+func UpdateInstanceConfigStoragePool(c *db.Cluster, b Info, mountPath string) 
error {
        // Load the storage pool.
        _, pool, err := c.StoragePoolGet(b.Pool)
        if err != nil {
@@ -107,7 +107,7 @@ func UpdateInstanceConfigStoragePool(c *db.Cluster, b Info) 
error {
                return nil
        }
 
-       err = f(shared.VarPath("storage-pools", pool.Name, "containers", 
project.Prefix(b.Project, b.Name), "backup.yaml"))
+       err = f(filepath.Join(mountPath, "backup.yaml"))
        if err != nil {
                return err
        }

From f201e53497f4dff07b63bd2f822072772f673fee Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 28 Nov 2019 12:17:30 +0000
Subject: [PATCH 19/25] lxd/container: Updates
 backup.UpdateInstanceConfigStoragePool usage

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/container.go | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/lxd/container.go b/lxd/container.go
index cf201ca6f0..86a5465c9e 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -24,6 +24,7 @@ import (
        "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/lxd/operations"
+       "github.com/lxc/lxd/lxd/project"
        "github.com/lxc/lxd/lxd/seccomp"
        "github.com/lxc/lxd/lxd/state"
        storagePools "github.com/lxc/lxd/lxd/storage"
@@ -348,7 +349,8 @@ func instanceCreateFromBackup(s *state.State, info 
backup.Info, srcData io.ReadS
 
                // Update pool information in the backup.yaml file.
                // Requires the volume and snapshots be mounted from 
pool.ContainerBackupLoad().
-               err = backup.UpdateInstanceConfigStoragePool(s.Cluster, info)
+               mountPath := shared.VarPath("storage-pools", info.Pool, 
"containers", project.Prefix(info.Project, info.Name))
+               err = backup.UpdateInstanceConfigStoragePool(s.Cluster, info, 
mountPath)
                if err != nil {
                        return nil, nil, err
                }

From 75a8e0bea3242e26d6072bcf18ac8ee0ecfeb3c4 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 28 Nov 2019 12:18:57 +0000
Subject: [PATCH 20/25] lxd/storage/backend/lxd: Switches to
 InstanceContentType function

Clarifies that image type is not the same as instance type.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/backend_lxd.go | 44 ++++++++++----------------------------
 1 file changed, 11 insertions(+), 33 deletions(-)

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 54bca4617c..2b0462736d 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -297,10 +297,7 @@ func (b *lxdBackend) CreateInstance(inst 
instance.Instance, op *operations.Opera
                b.DeleteInstance(inst, op)
        }()
 
-       contentType := drivers.ContentTypeFS
-       if inst.Type() == instancetype.VM {
-               contentType = drivers.ContentTypeBlock
-       }
+       contentType := InstanceContentType(inst)
 
        // Find the root device config for instance.
        _, rootDiskConf, err := 
shared.GetRootDiskDevice(inst.ExpandedDevices().CloneNative())
@@ -444,10 +441,7 @@ func (b *lxdBackend) CreateInstanceFromCopy(inst 
instance.Instance, src instance
                return err
        }
 
-       contentType := drivers.ContentTypeFS
-       if inst.Type() == instancetype.VM {
-               contentType = drivers.ContentTypeBlock
-       }
+       contentType := InstanceContentType(inst)
 
        if b.driver.HasVolume(volType, project.Prefix(inst.Project(), 
inst.Name())) {
                return fmt.Errorf("Cannot create volume, already exists on 
target")
@@ -593,10 +587,7 @@ func (b *lxdBackend) RefreshInstance(inst 
instance.Instance, src instance.Instan
                return err
        }
 
-       contentType := drivers.ContentTypeFS
-       if inst.Type() == instancetype.VM {
-               contentType = drivers.ContentTypeBlock
-       }
+       contentType := InstanceContentType(inst)
 
        // Get the root disk device config.
        _, rootDiskConf, err := 
shared.GetRootDiskDevice(inst.ExpandedDevices().CloneNative())
@@ -750,10 +741,7 @@ func (b *lxdBackend) CreateInstanceFromImage(inst 
instance.Instance, fingerprint
                return err
        }
 
-       contentType := drivers.ContentTypeFS
-       if inst.Type() == instancetype.VM {
-               contentType = drivers.ContentTypeBlock
-       }
+       contentType := InstanceContentType(inst)
 
        revert := true
        defer func() {
@@ -824,10 +812,7 @@ func (b *lxdBackend) CreateInstanceFromMigration(inst 
instance.Instance, conn io
                return err
        }
 
-       contentType := drivers.ContentTypeFS
-       if inst.Type() == instancetype.VM {
-               contentType = drivers.ContentTypeBlock
-       }
+       contentType := InstanceContentType(inst)
 
        volExists := b.driver.HasVolume(volType, project.Prefix(inst.Project(), 
inst.Name()))
        if args.Refresh && !volExists {
@@ -1041,10 +1026,7 @@ func (b *lxdBackend) MigrateInstance(inst 
instance.Instance, conn io.ReadWriteCl
                return err
        }
 
-       contentType := drivers.ContentTypeFS
-       if inst.Type() == instancetype.VM {
-               contentType = drivers.ContentTypeBlock
-       }
+       contentType := InstanceContentType(inst)
 
        // Get the root disk device config.
        _, rootDiskConf, err := 
shared.GetRootDiskDevice(inst.ExpandedDevices().CloneNative())
@@ -1077,10 +1059,7 @@ func (b *lxdBackend) BackupInstance(inst 
instance.Instance, targetPath string, o
                return err
        }
 
-       contentType := drivers.ContentTypeFS
-       if inst.Type() == instancetype.VM {
-               contentType = drivers.ContentTypeBlock
-       }
+       contentType := InstanceContentType(inst)
 
        // Get the root disk device config.
        _, rootDiskConf, err := 
shared.GetRootDiskDevice(inst.ExpandedDevices().CloneNative())
@@ -1376,10 +1355,7 @@ func (b *lxdBackend) RestoreInstanceSnapshot(inst 
instance.Instance, src instanc
                return err
        }
 
-       contentType := drivers.ContentTypeFS
-       if inst.Type() == instancetype.VM {
-               contentType = drivers.ContentTypeBlock
-       }
+       contentType := InstanceContentType(inst)
 
        // Find the root device config for source snapshot instance.
        _, rootDiskConf, err := 
shared.GetRootDiskDevice(src.ExpandedDevices().CloneNative())
@@ -1479,7 +1455,9 @@ func (b *lxdBackend) EnsureImage(fingerprint string, op 
*operations.Operation) e
        }
 
        contentType := drivers.ContentTypeFS
-       if api.InstanceType(image.Type) == api.InstanceTypeVM {
+
+       // Image types are not the same as instance types, so compare to 
instance type constants.
+       if image.Type == "virtual-machine" {
                contentType = drivers.ContentTypeBlock
        }
 

From 9253029eb1a419f88b535f4bb3b39eda92c12e5c Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 28 Nov 2019 12:20:36 +0000
Subject: [PATCH 21/25] lxd/storage/utils: Adds InstanceContentType

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/utils.go | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/lxd/storage/utils.go b/lxd/storage/utils.go
index 25e8e273fd..db4eb0f1ff 100644
--- a/lxd/storage/utils.go
+++ b/lxd/storage/utils.go
@@ -10,6 +10,7 @@ import (
        "golang.org/x/sys/unix"
 
        "github.com/lxc/lxd/lxd/db"
+       "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/lxd/state"
        "github.com/lxc/lxd/lxd/storage/drivers"
@@ -756,3 +757,13 @@ func ImageUnpack(imageFile, destPath, destBlockFile 
string, blockBackend, runnin
 
        return nil
 }
+
+// InstanceContentType returns the instance's content type.
+func InstanceContentType(inst instance.Instance) drivers.ContentType {
+       contentType := drivers.ContentTypeFS
+       if inst.Type() == instancetype.VM {
+               contentType = drivers.ContentTypeBlock
+       }
+
+       return contentType
+}

From f08ff272977c3fd7999eb92512e0e244e3d21d2f Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 28 Nov 2019 12:20:51 +0000
Subject: [PATCH 22/25] lxd/storage/drivers/interface: RestoreBackupVolume
 signature

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/drivers/interface.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/lxd/storage/drivers/interface.go b/lxd/storage/drivers/interface.go
index 11d8343f05..09082f128f 100644
--- a/lxd/storage/drivers/interface.go
+++ b/lxd/storage/drivers/interface.go
@@ -72,4 +72,5 @@ type Driver interface {
 
        // Backup.
        BackupVolume(vol Volume, targetPath string, optimized bool, snapshots 
bool, op *operations.Operation) error
+       RestoreBackupVolume(vol Volume, snapshots []string, srcData 
io.ReadSeeker, op *operations.Operation) (func(vol Volume) error, func(), error)
 }

From 9325180f9160b9261939202b0415c3b02d5b4dfe Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 28 Nov 2019 12:21:12 +0000
Subject: [PATCH 23/25] lxd/storage/drivers/driver/cephfs: RestoreBackupVolume
 placeholder

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/drivers/driver_cephfs.go | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/lxd/storage/drivers/driver_cephfs.go 
b/lxd/storage/drivers/driver_cephfs.go
index 98b593cd80..75e589ea68 100644
--- a/lxd/storage/drivers/driver_cephfs.go
+++ b/lxd/storage/drivers/driver_cephfs.go
@@ -991,3 +991,7 @@ func (d *cephfs) getConfig(clusterName string, userName 
string) ([]string, strin
 func (d *cephfs) BackupVolume(vol Volume, targetPath string, optimized bool, 
snapshots bool, op *operations.Operation) error {
        return ErrNotImplemented
 }
+
+func (d *cephfs) RestoreBackupVolume(vol Volume, snapshots []string, srcData 
io.ReadSeeker, op *operations.Operation) (func(vol Volume) error, func(), 
error) {
+       return nil, nil, ErrNotImplemented
+}

From f24f760761e9a32742d87858d4fafad839c23b70 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 28 Nov 2019 12:22:08 +0000
Subject: [PATCH 24/25] lxd/storage/drivers/driver/dir: Moves initial project
 quota setup to own function

Will be used in forthcoming backup restore function.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/drivers/driver_dir.go | 83 +++++++++++++++++++++----------
 1 file changed, 58 insertions(+), 25 deletions(-)

diff --git a/lxd/storage/drivers/driver_dir.go 
b/lxd/storage/drivers/driver_dir.go
index 66c85d9ce7..a5eec749a3 100644
--- a/lxd/storage/drivers/driver_dir.go
+++ b/lxd/storage/drivers/driver_dir.go
@@ -173,6 +173,51 @@ func (d *dir) GetVolumeDiskPath(volType VolumeType, 
volName string) (string, err
        return filepath.Join(GetVolumeMountPath(d.name, volType, volName), 
"root.img"), nil
 }
 
+// setupInitialQuota enables quota on a new volume and sets with an initial 
quota from config.
+// Returns a revert function that can be used to remove the quota if there is 
a subsequent error.
+func (d *dir) setupInitialQuota(vol Volume) (func(), error) {
+       // Extract specified size from pool or volume config.
+       size := d.config["volume.size"]
+       if vol.config["size"] != "" {
+               size = vol.config["size"]
+       }
+
+       volPath := vol.MountPath()
+
+       // Get the volume ID for the new volume, which is used to set project 
quota.
+       volID, err := d.getVolID(vol.volType, vol.name)
+       if err != nil {
+               return nil, err
+       }
+
+       // Define a function to revert the quota being setup.
+       revertFunc := func() {
+               d.deleteQuota(volPath, volID)
+       }
+
+       // Initialise the volume's quota using the volume ID.
+       err = d.initQuota(volPath, volID)
+       if err != nil {
+               return nil, err
+       }
+
+       revert := true
+       defer func() {
+               if revert {
+                       revertFunc()
+               }
+       }()
+
+       // Set the quota.
+       err = d.setQuota(volPath, volID, size)
+       if err != nil {
+               return nil, err
+       }
+
+       revert = false
+       return revertFunc, nil
+}
+
 // CreateVolume creates an empty volume and can optionally fill it by 
executing the supplied
 // filler function.
 func (d *dir) CreateVolume(vol Volume, filler func(mountPath, rootBlockPath 
string) error, op *operations.Operation) error {
@@ -182,12 +227,6 @@ func (d *dir) CreateVolume(vol Volume, filler 
func(mountPath, rootBlockPath stri
                return err
        }
 
-       // Extract specified size from pool or volume config.
-       size := d.config["volume.size"]
-       if vol.config["size"] != "" {
-               size = vol.config["size"]
-       }
-
        revertPath := true
        defer func() {
                if revertPath {
@@ -204,28 +243,17 @@ func (d *dir) CreateVolume(vol Volume, filler 
func(mountPath, rootBlockPath stri
                        return err
                }
        } else {
-               // Get the volume ID for the new volume, which is used to set 
project quota.
-               volID, err := d.getVolID(vol.volType, vol.name)
-               if err != nil {
-                       return err
-               }
-
-               // Initialise the volume's quota using the volume ID.
-               err = d.initQuota(volPath, volID)
+               revertFunc, err := d.setupInitialQuota(vol)
                if err != nil {
                        return err
                }
 
-               defer func() {
-                       if revertPath {
-                               d.deleteQuota(volPath, volID)
-                       }
-               }()
-
-               // Set the quota.
-               err = d.setQuota(volPath, volID, size)
-               if err != nil {
-                       return err
+               if revertFunc != nil {
+                       defer func() {
+                               if revertPath {
+                                       revertFunc()
+                               }
+                       }()
                }
        }
 
@@ -240,7 +268,12 @@ func (d *dir) CreateVolume(vol Volume, filler 
func(mountPath, rootBlockPath stri
        // If we are creating a block volume, resize it to the requested size 
or 10GB.
        // We expect the filler function to have converted the qcow2 image to 
raw into the rootBlockPath.
        if vol.contentType == ContentTypeBlock {
-               blockSize := size
+               // Extract specified size from pool or volume config.
+               blockSize := d.config["volume.size"]
+               if vol.config["size"] != "" {
+                       blockSize = vol.config["size"]
+               }
+
                if blockSize == "" {
                        blockSize = "10GB"
                }

From 8c95a4a78f1624bc0ae73568351d77ca359566f7 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 28 Nov 2019 12:22:40 +0000
Subject: [PATCH 25/25] lxd/storage/drivers/driver/dir: Implements
 RestoreBackupVolume

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/drivers/driver_dir.go | 86 +++++++++++++++++++++++++++++++
 1 file changed, 86 insertions(+)

diff --git a/lxd/storage/drivers/driver_dir.go 
b/lxd/storage/drivers/driver_dir.go
index a5eec749a3..2a6b83c6b3 100644
--- a/lxd/storage/drivers/driver_dir.go
+++ b/lxd/storage/drivers/driver_dir.go
@@ -978,3 +978,89 @@ func (d *dir) BackupVolume(vol Volume, targetPath string, 
_, snapshots bool, op
 
        return nil
 }
+
+// RestoreBackupVolume restores a backup tarball onto the storage device.
+func (d *dir) RestoreBackupVolume(vol Volume, snapshots []string, srcData 
io.ReadSeeker, op *operations.Operation) (func(vol Volume) error, func(), 
error) {
+       revert := true
+       revertPaths := []string{}
+
+       // Define a revert function that will be used both to revert if an 
error occurs inside this
+       // function but also return it for use from the calling functions if no 
error internally.
+       revertHook := func() {
+               for _, revertPath := range revertPaths {
+                       os.RemoveAll(revertPath)
+               }
+       }
+
+       // Only execute the revert function if we have had an error internally 
and revert is true.
+       defer func() {
+               if revert {
+                       revertHook()
+               }
+       }()
+
+       volPath := vol.MountPath()
+       err := vol.CreateMountPath()
+       if err != nil {
+               return nil, nil, err
+       }
+       revertPaths = append(revertPaths, volPath)
+
+       // Find the compression algorithm used for backup source data.
+       srcData.Seek(0, 0)
+       tarArgs, _, _, err := shared.DetectCompressionFile(srcData)
+       if err != nil {
+               return nil, nil, err
+       }
+
+       // Prepare tar extraction arguments.
+       args := append(tarArgs, []string{
+               "-",
+               "--strip-components=2",
+               "--xattrs-include=*",
+               "-C", volPath, "backup/container",
+       }...)
+
+       // Extract instance.
+       srcData.Seek(0, 0)
+       err = shared.RunCommandWithFds(srcData, nil, "tar", args...)
+       if err != nil {
+               return nil, nil, err
+       }
+
+       if len(snapshots) > 0 {
+               // Create new snapshots directory.
+               snapshotDir := GetVolumeSnapshotDir(d.name, vol.volType, 
vol.name)
+               err := os.MkdirAll(snapshotDir, 0711)
+               if err != nil {
+                       return nil, nil, err
+               }
+
+               revertPaths = append(revertPaths, snapshotDir)
+
+               // Prepare tar arguments.
+               args := append(tarArgs, []string{
+                       "-",
+                       "--strip-components=2",
+                       "--xattrs-include=*",
+                       "-C", snapshotDir, "backup/snapshots",
+               }...)
+
+               // Extract snapshots.
+               srcData.Seek(0, 0)
+               err = shared.RunCommandWithFds(srcData, nil, "tar", args...)
+               if err != nil {
+                       return nil, nil, err
+               }
+       }
+
+       // Define a post hook function that can be run once the backup config 
has been restored.
+       // This will setup the quota using the restored config.
+       postHook := func(vol Volume) error {
+               _, err := d.setupInitialQuota(vol)
+               return err
+       }
+
+       revert = false
+       return postHook, revertHook, nil
+}
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to