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

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) ===

From 949aaf40b2faa5eff6c6c8caeb22787fb2c5af9a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 24 Mar 2020 14:39:33 +0000
Subject: [PATCH 1/2] lxd/storage/drivers: Moves functions from generic.go to
 generic_vfs.go

And adds VFS to function names.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/drivers/generic.go     | 423 -----------------------------
 lxd/storage/drivers/generic_vfs.go | 407 +++++++++++++++++++++++++++
 2 files changed, 407 insertions(+), 423 deletions(-)
 delete mode 100644 lxd/storage/drivers/generic.go

diff --git a/lxd/storage/drivers/generic.go b/lxd/storage/drivers/generic.go
deleted file mode 100644
index ca18a04fc1..0000000000
--- a/lxd/storage/drivers/generic.go
+++ /dev/null
@@ -1,423 +0,0 @@
-package drivers
-
-import (
-       "fmt"
-       "io"
-       "os"
-
-       "github.com/pkg/errors"
-
-       "github.com/lxc/lxd/lxd/migration"
-       "github.com/lxc/lxd/lxd/operations"
-       "github.com/lxc/lxd/lxd/revert"
-       "github.com/lxc/lxd/lxd/rsync"
-       "github.com/lxc/lxd/shared"
-       "github.com/lxc/lxd/shared/ioprogress"
-       log "github.com/lxc/lxd/shared/log15"
-)
-
-// genericCopyVolume copies a volume and its snapshots using a non-optimized 
method.
-// initVolume is run against the main volume (not the snapshots) and is often 
used for quota initialization.
-func genericCopyVolume(d Driver, initVolume func(vol Volume) (func(), error), 
vol Volume, srcVol Volume, srcSnapshots []Volume, refresh bool, op 
*operations.Operation) error {
-       if vol.contentType != srcVol.contentType {
-               return fmt.Errorf("Content type of source and target must be 
the same")
-       }
-
-       bwlimit := d.Config()["rsync.bwlimit"]
-
-       revert := revert.New()
-       defer revert.Fail()
-
-       // Create the main volume if not refreshing.
-       if !refresh {
-               err := d.CreateVolume(vol, nil, op)
-               if err != nil {
-                       return err
-               }
-
-               revert.Add(func() { d.DeleteVolume(vol, op) })
-       }
-
-       // Ensure the volume is mounted.
-       err := vol.MountTask(func(mountPath string, op *operations.Operation) 
error {
-               // If copying snapshots is indicated, check the source isn't 
itself a snapshot.
-               if len(srcSnapshots) > 0 && !srcVol.IsSnapshot() {
-                       for _, srcSnapshot := range srcSnapshots {
-                               _, snapName, _ := 
shared.InstanceGetParentAndSnapshotName(srcSnapshot.name)
-
-                               // Mount the source snapshot.
-                               err := srcSnapshot.MountTask(func(srcMountPath 
string, op *operations.Operation) error {
-                                       // Copy the snapshot.
-                                       _, err := rsync.LocalCopy(srcMountPath, 
mountPath, bwlimit, true)
-                                       if err != nil {
-                                               return err
-                                       }
-
-                                       if srcSnapshot.IsVMBlock() {
-                                               srcDevPath, err := 
d.GetVolumeDiskPath(srcSnapshot)
-                                               if err != nil {
-                                                       return err
-                                               }
-
-                                               targetDevPath, err := 
d.GetVolumeDiskPath(vol)
-                                               if err != nil {
-                                                       return err
-                                               }
-
-                                               err = copyDevice(srcDevPath, 
targetDevPath)
-                                               if err != nil {
-                                                       return err
-                                               }
-                                       }
-
-                                       return nil
-                               }, op)
-                               if err != nil {
-                                       return err
-                               }
-
-                               fullSnapName := GetSnapshotVolumeName(vol.name, 
snapName)
-                               snapVol := NewVolume(d, d.Name(), vol.volType, 
vol.contentType, fullSnapName, vol.config, vol.poolConfig)
-
-                               // Create the snapshot itself.
-                               err = d.CreateVolumeSnapshot(snapVol, op)
-                               if err != nil {
-                                       return err
-                               }
-
-                               // Setup the revert.
-                               revert.Add(func() {
-                                       d.DeleteVolumeSnapshot(snapVol, op)
-                               })
-                       }
-               }
-
-               // Run volume-specific init logic.
-               if initVolume != nil {
-                       _, err := initVolume(vol)
-                       if err != nil {
-                               return err
-                       }
-               }
-
-               // Copy source to destination (mounting each volume if needed).
-               err := srcVol.MountTask(func(srcMountPath string, op 
*operations.Operation) error {
-                       _, err := rsync.LocalCopy(srcMountPath, mountPath, 
bwlimit, true)
-                       if err != nil {
-                               return err
-                       }
-
-                       if srcVol.IsVMBlock() {
-                               srcDevPath, err := d.GetVolumeDiskPath(srcVol)
-                               if err != nil {
-                                       return err
-                               }
-
-                               targetDevPath, err := d.GetVolumeDiskPath(vol)
-                               if err != nil {
-                                       return err
-                               }
-
-                               err = copyDevice(srcDevPath, targetDevPath)
-                               if err != nil {
-                                       return err
-                               }
-                       }
-
-                       return nil
-               }, op)
-               if err != nil {
-                       return err
-               }
-
-               // Run EnsureMountPath after mounting and copying to ensure the 
mounted directory has the
-               // correct permissions set.
-               err = vol.EnsureMountPath()
-               if err != nil {
-                       return err
-               }
-
-               return nil
-       }, op)
-       if err != nil {
-               return err
-       }
-
-       revert.Success()
-       return nil
-}
-
-// genericCreateVolumeFromMigration receives a volume and its snapshots over a 
non-optimized method.
-// initVolume is run against the main volume (not the snapshots) and is often 
used for quota initialization.
-func genericCreateVolumeFromMigration(d Driver, initVolume func(vol Volume) 
(func(), error), vol Volume, conn io.ReadWriteCloser, volTargetArgs 
migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) 
error {
-       // Check migration transport type matches volume type.
-       if vol.IsVMBlock() {
-               if volTargetArgs.MigrationType.FSType != 
migration.MigrationFSType_BLOCK_AND_RSYNC {
-                       return ErrNotSupported
-               }
-       } else if volTargetArgs.MigrationType.FSType != 
migration.MigrationFSType_RSYNC {
-               return ErrNotSupported
-       }
-
-       revert := revert.New()
-       defer revert.Fail()
-
-       // Create the main volume if not refreshing.
-       if !volTargetArgs.Refresh {
-               err := d.CreateVolume(vol, preFiller, op)
-               if err != nil {
-                       return err
-               }
-
-               revert.Add(func() { d.DeleteVolume(vol, op) })
-       }
-
-       recvFSVol := func(volName string, conn io.ReadWriteCloser, path string) 
error {
-               var wrapper *ioprogress.ProgressTracker
-               if volTargetArgs.TrackProgress {
-                       wrapper = migration.ProgressTracker(op, "fs_progress", 
volName)
-               }
-
-               d.Logger().Debug("Receiving filesystem volume", 
log.Ctx{"volName": volName, "path": path})
-               return rsync.Recv(path, conn, wrapper, 
volTargetArgs.MigrationType.Features)
-       }
-
-       recvBlockVol := func(volName string, conn io.ReadWriteCloser, path 
string) error {
-               var wrapper *ioprogress.ProgressTracker
-               if volTargetArgs.TrackProgress {
-                       wrapper = migration.ProgressTracker(op, 
"block_progress", volName)
-               }
-
-               to, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0)
-               if err != nil {
-                       return errors.Wrapf(err, "Error opening file for 
writing %q", path)
-               }
-               defer to.Close()
-
-               // Setup progress tracker.
-               fromPipe := io.ReadCloser(conn)
-               if wrapper != nil {
-                       fromPipe = &ioprogress.ProgressReader{
-                               ReadCloser: fromPipe,
-                               Tracker:    wrapper,
-                       }
-               }
-
-               d.Logger().Debug("Receiving block volume", log.Ctx{"volName": 
volName, "path": path})
-               _, err = io.Copy(to, fromPipe)
-               if err != nil {
-                       return errors.Wrapf(err, "Error copying from migration 
connection to %q", path)
-               }
-
-               return nil
-       }
-
-       // Ensure the volume is mounted.
-       err := vol.MountTask(func(mountPath string, op *operations.Operation) 
error {
-               var err error
-
-               // Setup paths to the main volume. We will receive each 
snapshot to these paths and then create
-               // a snapshot of the main volume for each one.
-               path := shared.AddSlash(mountPath)
-               pathBlock := ""
-
-               if vol.IsVMBlock() {
-                       pathBlock, err = d.GetVolumeDiskPath(vol)
-                       if err != nil {
-                               return errors.Wrapf(err, "Error getting VM 
block volume disk path")
-                       }
-               }
-
-               // Snapshots are sent first by the sender, so create these 
first.
-               for _, snapName := range volTargetArgs.Snapshots {
-                       fullSnapshotName := GetSnapshotVolumeName(vol.name, 
snapName)
-                       snapVol := NewVolume(d, d.Name(), vol.volType, 
vol.contentType, fullSnapshotName, vol.config, vol.poolConfig)
-
-                       // Receive the filesystem snapshot first (as it is sent 
first).
-                       err = recvFSVol(snapVol.name, conn, path)
-                       if err != nil {
-                               return err
-                       }
-
-                       // Receive the block snapshot next (if needed).
-                       if vol.IsVMBlock() {
-                               err = recvBlockVol(snapVol.name, conn, 
pathBlock)
-                               if err != nil {
-                                       return err
-                               }
-                       }
-
-                       // Create the snapshot itself.
-                       err = d.CreateVolumeSnapshot(snapVol, op)
-                       if err != nil {
-                               return err
-                       }
-
-                       // Setup the revert.
-                       revert.Add(func() {
-                               d.DeleteVolumeSnapshot(snapVol, op)
-                       })
-               }
-
-               // Run volume-specific init logic.
-               if initVolume != nil {
-                       _, err := initVolume(vol)
-                       if err != nil {
-                               return err
-                       }
-               }
-
-               // Receive main volume.
-               err = recvFSVol(vol.name, conn, path)
-               if err != nil {
-                       return err
-               }
-
-               // Receive the final main volume sync if needed.
-               if volTargetArgs.Live {
-                       d.Logger().Debug("Starting main volume final sync", 
log.Ctx{"volName": vol.name, "path": path})
-                       err = recvFSVol(vol.name, conn, path)
-                       if err != nil {
-                               return err
-                       }
-               }
-
-               // Run EnsureMountPath after mounting and syncing to ensure the 
mounted directory has the
-               // correct permissions set.
-               err = vol.EnsureMountPath()
-               if err != nil {
-                       return err
-               }
-
-               // Receive the block volume next (if needed).
-               if vol.IsVMBlock() {
-                       err = recvBlockVol(vol.name, conn, pathBlock)
-                       if err != nil {
-                               return err
-                       }
-               }
-
-               return nil
-       }, op)
-       if err != nil {
-               return err
-       }
-
-       revert.Success()
-       return nil
-}
-
-// genericBackupUnpack unpacks a non-optimized backup tarball through a 
storage driver.
-// Returns a post hook function that should be called once the database 
entries for the restored backup have been
-// created and a revert function that can be used to undo the actions this 
function performs should something
-// subsequently fail.
-func genericBackupUnpack(d Driver, vol Volume, snapshots []string, srcData 
io.ReadSeeker, op *operations.Operation) (func(vol Volume) error, func(), 
error) {
-       revert := revert.New()
-       defer revert.Fail()
-
-       // 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
-       }
-
-       if d.HasVolume(vol) {
-               return nil, nil, fmt.Errorf("Cannot restore volume, already 
exists on target")
-       }
-
-       // Create new empty volume.
-       err = d.CreateVolume(vol, nil, nil)
-       if err != nil {
-               return nil, nil, err
-       }
-       revert.Add(func() { d.DeleteVolume(vol, op) })
-
-       if len(snapshots) > 0 {
-               // Create new snapshots directory.
-               err := createParentSnapshotDirIfMissing(d.Name(), vol.volType, 
vol.name)
-               if err != nil {
-                       return nil, nil, err
-               }
-       }
-
-       for _, snapName := range snapshots {
-               err = vol.MountTask(func(mountPath string, op 
*operations.Operation) error {
-                       // Prepare tar arguments.
-                       args := append(tarArgs, []string{
-                               "-",
-                               "--recursive-unlink",
-                               "--xattrs-include=*",
-                               "--strip-components=3",
-                               "-C", mountPath, 
fmt.Sprintf("backup/snapshots/%s", snapName),
-                       }...)
-
-                       // Extract snapshot.
-                       srcData.Seek(0, 0)
-                       err = shared.RunCommandWithFds(srcData, nil, "tar", 
args...)
-                       if err != nil {
-                               return err
-                       }
-
-                       return nil
-               }, op)
-               if err != nil {
-                       return nil, nil, err
-               }
-
-               snapVol, err := vol.NewSnapshot(snapName)
-               if err != nil {
-                       return nil, nil, err
-               }
-
-               err = d.CreateVolumeSnapshot(snapVol, op)
-               if err != nil {
-                       return nil, nil, err
-               }
-               revert.Add(func() { d.DeleteVolumeSnapshot(snapVol, op) })
-       }
-
-       // Mount main volume and leave mounted (as is needed during backup.yaml 
generation during latter parts of
-       // the backup restoration process).
-       ourMount, err := d.MountVolume(vol, op)
-       if err != nil {
-               return nil, nil, err
-       }
-
-       // Create a post hook function that will be called at the end of the 
backup restore process to unmount
-       // the volume if needed.
-       postHook := func(vol Volume) error {
-               if ourMount {
-                       d.UnmountVolume(vol, op)
-               }
-
-               return nil
-       }
-
-       // Prepare tar extraction arguments.
-       args := append(tarArgs, []string{
-               "-",
-               "--recursive-unlink",
-               "--strip-components=2",
-               "--xattrs-include=*",
-               "-C", vol.MountPath(), "backup/container",
-       }...)
-
-       // Extract instance.
-       srcData.Seek(0, 0)
-       err = shared.RunCommandWithFds(srcData, nil, "tar", args...)
-       if err != nil {
-               return nil, nil, err
-       }
-
-       // Run EnsureMountPath after mounting and unpacking to ensure the 
mounted directory has the
-       // correct permissions set.
-       err = vol.EnsureMountPath()
-       if err != nil {
-               return nil, nil, err
-       }
-
-       revertExternal := revert.Clone() // Clone before calling 
revert.Success() so we can return the Fail func.
-       revert.Success()
-       return postHook, revertExternal.Fail, nil
-}
diff --git a/lxd/storage/drivers/generic_vfs.go 
b/lxd/storage/drivers/generic_vfs.go
index f62686ba19..d52421066b 100644
--- a/lxd/storage/drivers/generic_vfs.go
+++ b/lxd/storage/drivers/generic_vfs.go
@@ -12,6 +12,7 @@ import (
 
        "github.com/lxc/lxd/lxd/migration"
        "github.com/lxc/lxd/lxd/operations"
+       "github.com/lxc/lxd/lxd/revert"
        "github.com/lxc/lxd/lxd/rsync"
        "github.com/lxc/lxd/lxd/state"
        "github.com/lxc/lxd/shared"
@@ -257,6 +258,166 @@ func genericVFSMigrateVolume(d Driver, s *state.State, 
vol Volume, conn io.ReadW
        }, op)
 }
 
+// genericVFSCreateVolumeFromMigration receives a volume and its snapshots 
over a non-optimized method.
+// initVolume is run against the main volume (not the snapshots) and is often 
used for quota initialization.
+func genericVFSCreateVolumeFromMigration(d Driver, initVolume func(vol Volume) 
(func(), error), vol Volume, conn io.ReadWriteCloser, volTargetArgs 
migration.VolumeTargetArgs, preFiller *VolumeFiller, op *operations.Operation) 
error {
+       // Check migration transport type matches volume type.
+       if vol.IsVMBlock() {
+               if volTargetArgs.MigrationType.FSType != 
migration.MigrationFSType_BLOCK_AND_RSYNC {
+                       return ErrNotSupported
+               }
+       } else if volTargetArgs.MigrationType.FSType != 
migration.MigrationFSType_RSYNC {
+               return ErrNotSupported
+       }
+
+       revert := revert.New()
+       defer revert.Fail()
+
+       // Create the main volume if not refreshing.
+       if !volTargetArgs.Refresh {
+               err := d.CreateVolume(vol, preFiller, op)
+               if err != nil {
+                       return err
+               }
+
+               revert.Add(func() { d.DeleteVolume(vol, op) })
+       }
+
+       recvFSVol := func(volName string, conn io.ReadWriteCloser, path string) 
error {
+               var wrapper *ioprogress.ProgressTracker
+               if volTargetArgs.TrackProgress {
+                       wrapper = migration.ProgressTracker(op, "fs_progress", 
volName)
+               }
+
+               d.Logger().Debug("Receiving filesystem volume", 
log.Ctx{"volName": volName, "path": path})
+               return rsync.Recv(path, conn, wrapper, 
volTargetArgs.MigrationType.Features)
+       }
+
+       recvBlockVol := func(volName string, conn io.ReadWriteCloser, path 
string) error {
+               var wrapper *ioprogress.ProgressTracker
+               if volTargetArgs.TrackProgress {
+                       wrapper = migration.ProgressTracker(op, 
"block_progress", volName)
+               }
+
+               to, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0)
+               if err != nil {
+                       return errors.Wrapf(err, "Error opening file for 
writing %q", path)
+               }
+               defer to.Close()
+
+               // Setup progress tracker.
+               fromPipe := io.ReadCloser(conn)
+               if wrapper != nil {
+                       fromPipe = &ioprogress.ProgressReader{
+                               ReadCloser: fromPipe,
+                               Tracker:    wrapper,
+                       }
+               }
+
+               d.Logger().Debug("Receiving block volume", log.Ctx{"volName": 
volName, "path": path})
+               _, err = io.Copy(to, fromPipe)
+               if err != nil {
+                       return errors.Wrapf(err, "Error copying from migration 
connection to %q", path)
+               }
+
+               return nil
+       }
+
+       // Ensure the volume is mounted.
+       err := vol.MountTask(func(mountPath string, op *operations.Operation) 
error {
+               var err error
+
+               // Setup paths to the main volume. We will receive each 
snapshot to these paths and then create
+               // a snapshot of the main volume for each one.
+               path := shared.AddSlash(mountPath)
+               pathBlock := ""
+
+               if vol.IsVMBlock() {
+                       pathBlock, err = d.GetVolumeDiskPath(vol)
+                       if err != nil {
+                               return errors.Wrapf(err, "Error getting VM 
block volume disk path")
+                       }
+               }
+
+               // Snapshots are sent first by the sender, so create these 
first.
+               for _, snapName := range volTargetArgs.Snapshots {
+                       fullSnapshotName := GetSnapshotVolumeName(vol.name, 
snapName)
+                       snapVol := NewVolume(d, d.Name(), vol.volType, 
vol.contentType, fullSnapshotName, vol.config, vol.poolConfig)
+
+                       // Receive the filesystem snapshot first (as it is sent 
first).
+                       err = recvFSVol(snapVol.name, conn, path)
+                       if err != nil {
+                               return err
+                       }
+
+                       // Receive the block snapshot next (if needed).
+                       if vol.IsVMBlock() {
+                               err = recvBlockVol(snapVol.name, conn, 
pathBlock)
+                               if err != nil {
+                                       return err
+                               }
+                       }
+
+                       // Create the snapshot itself.
+                       err = d.CreateVolumeSnapshot(snapVol, op)
+                       if err != nil {
+                               return err
+                       }
+
+                       // Setup the revert.
+                       revert.Add(func() {
+                               d.DeleteVolumeSnapshot(snapVol, op)
+                       })
+               }
+
+               // Run volume-specific init logic.
+               if initVolume != nil {
+                       _, err := initVolume(vol)
+                       if err != nil {
+                               return err
+                       }
+               }
+
+               // Receive main volume.
+               err = recvFSVol(vol.name, conn, path)
+               if err != nil {
+                       return err
+               }
+
+               // Receive the final main volume sync if needed.
+               if volTargetArgs.Live {
+                       d.Logger().Debug("Starting main volume final sync", 
log.Ctx{"volName": vol.name, "path": path})
+                       err = recvFSVol(vol.name, conn, path)
+                       if err != nil {
+                               return err
+                       }
+               }
+
+               // Run EnsureMountPath after mounting and syncing to ensure the 
mounted directory has the
+               // correct permissions set.
+               err = vol.EnsureMountPath()
+               if err != nil {
+                       return err
+               }
+
+               // Receive the block volume next (if needed).
+               if vol.IsVMBlock() {
+                       err = recvBlockVol(vol.name, conn, pathBlock)
+                       if err != nil {
+                               return err
+                       }
+               }
+
+               return nil
+       }, op)
+       if err != nil {
+               return err
+       }
+
+       revert.Success()
+       return nil
+}
+
 // genericVFSHasVolume is a generic HasVolume implementation for VFS-only 
drivers.
 func genericVFSHasVolume(vol Volume) bool {
        if shared.PathExists(vol.MountPath()) {
@@ -349,6 +510,121 @@ func genericVFSBackupVolume(d Driver, vol Volume, 
tarWriter *instancewriter.Inst
        return nil
 }
 
+// genericVFSBackupUnpack unpacks a non-optimized backup tarball through a 
storage driver.
+// Returns a post hook function that should be called once the database 
entries for the restored backup have been
+// created and a revert function that can be used to undo the actions this 
function performs should something
+// subsequently fail.
+func genericVFSBackupUnpack(d Driver, vol Volume, snapshots []string, srcData 
io.ReadSeeker, op *operations.Operation) (func(vol Volume) error, func(), 
error) {
+       revert := revert.New()
+       defer revert.Fail()
+
+       // 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
+       }
+
+       if d.HasVolume(vol) {
+               return nil, nil, fmt.Errorf("Cannot restore volume, already 
exists on target")
+       }
+
+       // Create new empty volume.
+       err = d.CreateVolume(vol, nil, nil)
+       if err != nil {
+               return nil, nil, err
+       }
+       revert.Add(func() { d.DeleteVolume(vol, op) })
+
+       if len(snapshots) > 0 {
+               // Create new snapshots directory.
+               err := createParentSnapshotDirIfMissing(d.Name(), vol.volType, 
vol.name)
+               if err != nil {
+                       return nil, nil, err
+               }
+       }
+
+       for _, snapName := range snapshots {
+               err = vol.MountTask(func(mountPath string, op 
*operations.Operation) error {
+                       // Prepare tar arguments.
+                       args := append(tarArgs, []string{
+                               "-",
+                               "--recursive-unlink",
+                               "--xattrs-include=*",
+                               "--strip-components=3",
+                               "-C", mountPath, 
fmt.Sprintf("backup/snapshots/%s", snapName),
+                       }...)
+
+                       // Extract snapshot.
+                       srcData.Seek(0, 0)
+                       err = shared.RunCommandWithFds(srcData, nil, "tar", 
args...)
+                       if err != nil {
+                               return err
+                       }
+
+                       return nil
+               }, op)
+               if err != nil {
+                       return nil, nil, err
+               }
+
+               snapVol, err := vol.NewSnapshot(snapName)
+               if err != nil {
+                       return nil, nil, err
+               }
+
+               err = d.CreateVolumeSnapshot(snapVol, op)
+               if err != nil {
+                       return nil, nil, err
+               }
+               revert.Add(func() { d.DeleteVolumeSnapshot(snapVol, op) })
+       }
+
+       // Mount main volume and leave mounted (as is needed during backup.yaml 
generation during latter parts of
+       // the backup restoration process).
+       ourMount, err := d.MountVolume(vol, op)
+       if err != nil {
+               return nil, nil, err
+       }
+
+       // Create a post hook function that will be called at the end of the 
backup restore process to unmount
+       // the volume if needed.
+       postHook := func(vol Volume) error {
+               if ourMount {
+                       d.UnmountVolume(vol, op)
+               }
+
+               return nil
+       }
+
+       // Prepare tar extraction arguments.
+       args := append(tarArgs, []string{
+               "-",
+               "--recursive-unlink",
+               "--strip-components=2",
+               "--xattrs-include=*",
+               "-C", vol.MountPath(), "backup/container",
+       }...)
+
+       // Extract instance.
+       srcData.Seek(0, 0)
+       err = shared.RunCommandWithFds(srcData, nil, "tar", args...)
+       if err != nil {
+               return nil, nil, err
+       }
+
+       // Run EnsureMountPath after mounting and unpacking to ensure the 
mounted directory has the
+       // correct permissions set.
+       err = vol.EnsureMountPath()
+       if err != nil {
+               return nil, nil, err
+       }
+
+       revertExternal := revert.Clone() // Clone before calling 
revert.Success() so we can return the Fail func.
+       revert.Success()
+       return postHook, revertExternal.Fail, nil
+}
+
 // genericVFSResizeBlockFile resizes an existing block file to the specified 
size. Returns true if resize took
 // place, false if not. Both requested size and existing file size are rounded 
to nearest block size using
 // roundVolumeBlockFileSizeBytes() before decision whether to resize is taken.
@@ -386,3 +662,134 @@ func genericVFSResizeBlockFile(filePath, size string) 
(bool, error) {
 
        return true, nil
 }
+
+// genericVFSCopyVolume copies a volume and its snapshots using a 
non-optimized method.
+// initVolume is run against the main volume (not the snapshots) and is often 
used for quota initialization.
+func genericVFSCopyVolume(d Driver, initVolume func(vol Volume) (func(), 
error), vol Volume, srcVol Volume, srcSnapshots []Volume, refresh bool, op 
*operations.Operation) error {
+       if vol.contentType != srcVol.contentType {
+               return fmt.Errorf("Content type of source and target must be 
the same")
+       }
+
+       bwlimit := d.Config()["rsync.bwlimit"]
+
+       revert := revert.New()
+       defer revert.Fail()
+
+       // Create the main volume if not refreshing.
+       if !refresh {
+               err := d.CreateVolume(vol, nil, op)
+               if err != nil {
+                       return err
+               }
+
+               revert.Add(func() { d.DeleteVolume(vol, op) })
+       }
+
+       // Ensure the volume is mounted.
+       err := vol.MountTask(func(mountPath string, op *operations.Operation) 
error {
+               // If copying snapshots is indicated, check the source isn't 
itself a snapshot.
+               if len(srcSnapshots) > 0 && !srcVol.IsSnapshot() {
+                       for _, srcSnapshot := range srcSnapshots {
+                               _, snapName, _ := 
shared.InstanceGetParentAndSnapshotName(srcSnapshot.name)
+
+                               // Mount the source snapshot.
+                               err := srcSnapshot.MountTask(func(srcMountPath 
string, op *operations.Operation) error {
+                                       // Copy the snapshot.
+                                       _, err := rsync.LocalCopy(srcMountPath, 
mountPath, bwlimit, true)
+                                       if err != nil {
+                                               return err
+                                       }
+
+                                       if srcSnapshot.IsVMBlock() {
+                                               srcDevPath, err := 
d.GetVolumeDiskPath(srcSnapshot)
+                                               if err != nil {
+                                                       return err
+                                               }
+
+                                               targetDevPath, err := 
d.GetVolumeDiskPath(vol)
+                                               if err != nil {
+                                                       return err
+                                               }
+
+                                               err = copyDevice(srcDevPath, 
targetDevPath)
+                                               if err != nil {
+                                                       return err
+                                               }
+                                       }
+
+                                       return nil
+                               }, op)
+                               if err != nil {
+                                       return err
+                               }
+
+                               fullSnapName := GetSnapshotVolumeName(vol.name, 
snapName)
+                               snapVol := NewVolume(d, d.Name(), vol.volType, 
vol.contentType, fullSnapName, vol.config, vol.poolConfig)
+
+                               // Create the snapshot itself.
+                               err = d.CreateVolumeSnapshot(snapVol, op)
+                               if err != nil {
+                                       return err
+                               }
+
+                               // Setup the revert.
+                               revert.Add(func() {
+                                       d.DeleteVolumeSnapshot(snapVol, op)
+                               })
+                       }
+               }
+
+               // Run volume-specific init logic.
+               if initVolume != nil {
+                       _, err := initVolume(vol)
+                       if err != nil {
+                               return err
+                       }
+               }
+
+               // Copy source to destination (mounting each volume if needed).
+               err := srcVol.MountTask(func(srcMountPath string, op 
*operations.Operation) error {
+                       _, err := rsync.LocalCopy(srcMountPath, mountPath, 
bwlimit, true)
+                       if err != nil {
+                               return err
+                       }
+
+                       if srcVol.IsVMBlock() {
+                               srcDevPath, err := d.GetVolumeDiskPath(srcVol)
+                               if err != nil {
+                                       return err
+                               }
+
+                               targetDevPath, err := d.GetVolumeDiskPath(vol)
+                               if err != nil {
+                                       return err
+                               }
+
+                               err = copyDevice(srcDevPath, targetDevPath)
+                               if err != nil {
+                                       return err
+                               }
+                       }
+
+                       return nil
+               }, op)
+               if err != nil {
+                       return err
+               }
+
+               // Run EnsureMountPath after mounting and copying to ensure the 
mounted directory has the
+               // correct permissions set.
+               err = vol.EnsureMountPath()
+               if err != nil {
+                       return err
+               }
+
+               return nil
+       }, op)
+       if err != nil {
+               return err
+       }
+
+       revert.Success()
+       return nil
+}

From 563e6bdaa4310146aa92f3222c5442f41de95e07 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Tue, 24 Mar 2020 14:40:05 +0000
Subject: [PATCH 2/2] lxd/storage/drivers: Generic VFS function usage after
 move &rename

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/drivers/driver_btrfs_volumes.go | 6 +++---
 lxd/storage/drivers/driver_ceph_volumes.go  | 6 +++---
 lxd/storage/drivers/driver_dir_volumes.go   | 8 ++++----
 lxd/storage/drivers/driver_lvm_volumes.go   | 8 ++++----
 lxd/storage/drivers/driver_zfs_volumes.go   | 6 +++---
 5 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/lxd/storage/drivers/driver_btrfs_volumes.go 
b/lxd/storage/drivers/driver_btrfs_volumes.go
index 34d89d3247..97d6773305 100644
--- a/lxd/storage/drivers/driver_btrfs_volumes.go
+++ b/lxd/storage/drivers/driver_btrfs_volumes.go
@@ -101,7 +101,7 @@ func (d *btrfs) CreateVolume(vol Volume, filler 
*VolumeFiller, op *operations.Op
 func (d *btrfs) CreateVolumeFromBackup(vol Volume, snapshots []string, srcData 
io.ReadSeeker, optimized bool, op *operations.Operation) (func(vol Volume) 
error, func(), error) {
        // Handle the non-optimized tarballs through the generic unpacker.
        if !optimized {
-               return genericBackupUnpack(d, vol, snapshots, srcData, op)
+               return genericVFSBackupUnpack(d, vol, snapshots, srcData, op)
        }
 
        revert := revert.New()
@@ -265,7 +265,7 @@ func (d *btrfs) CreateVolumeFromCopy(vol Volume, srcVol 
Volume, copySnapshots bo
 func (d *btrfs) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, 
volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op 
*operations.Operation) error {
        // Handle simple rsync and block_and_rsync through generic.
        if volTargetArgs.MigrationType.FSType == 
migration.MigrationFSType_RSYNC || volTargetArgs.MigrationType.FSType == 
migration.MigrationFSType_BLOCK_AND_RSYNC {
-               return genericCreateVolumeFromMigration(d, nil, vol, conn, 
volTargetArgs, preFiller, op)
+               return genericVFSCreateVolumeFromMigration(d, nil, vol, conn, 
volTargetArgs, preFiller, op)
        } else if volTargetArgs.MigrationType.FSType != 
migration.MigrationFSType_BTRFS {
                return ErrNotSupported
        }
@@ -318,7 +318,7 @@ func (d *btrfs) CreateVolumeFromMigration(vol Volume, conn 
io.ReadWriteCloser, v
 
 // RefreshVolume provides same-pool volume and specific snapshots syncing 
functionality.
 func (d *btrfs) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots 
[]Volume, op *operations.Operation) error {
-       return genericCopyVolume(d, nil, vol, srcVol, srcSnapshots, true, op)
+       return genericVFSCopyVolume(d, nil, vol, srcVol, srcSnapshots, true, op)
 }
 
 // DeleteVolume deletes a volume of the storage device. If any snapshots of 
the volume remain then
diff --git a/lxd/storage/drivers/driver_ceph_volumes.go 
b/lxd/storage/drivers/driver_ceph_volumes.go
index f416d8d2e9..9cb8cd49a8 100644
--- a/lxd/storage/drivers/driver_ceph_volumes.go
+++ b/lxd/storage/drivers/driver_ceph_volumes.go
@@ -186,7 +186,7 @@ func (d *ceph) CreateVolume(vol Volume, filler 
*VolumeFiller, op *operations.Ope
 
 // CreateVolumeFromBackup re-creates a volume from its exported state.
 func (d *ceph) CreateVolumeFromBackup(vol Volume, snapshots []string, srcData 
io.ReadSeeker, optimizedStorage bool, op *operations.Operation) (func(vol 
Volume) error, func(), error) {
-       return genericBackupUnpack(d, vol, snapshots, srcData, op)
+       return genericVFSBackupUnpack(d, vol, snapshots, srcData, op)
 }
 
 // CreateVolumeFromCopy provides same-pool volume copying functionality.
@@ -376,7 +376,7 @@ func (d *ceph) CreateVolumeFromCopy(vol Volume, srcVol 
Volume, copySnapshots boo
 func (d *ceph) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, 
volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op 
*operations.Operation) error {
        // Handle simple rsync and block_and_rsync through generic.
        if volTargetArgs.MigrationType.FSType == 
migration.MigrationFSType_RSYNC || volTargetArgs.MigrationType.FSType == 
migration.MigrationFSType_BLOCK_AND_RSYNC {
-               return genericCreateVolumeFromMigration(d, nil, vol, conn, 
volTargetArgs, preFiller, op)
+               return genericVFSCreateVolumeFromMigration(d, nil, vol, conn, 
volTargetArgs, preFiller, op)
        } else if volTargetArgs.MigrationType.FSType != 
migration.MigrationFSType_RBD {
                return ErrNotSupported
        }
@@ -473,7 +473,7 @@ func (d *ceph) CreateVolumeFromMigration(vol Volume, conn 
io.ReadWriteCloser, vo
 
 // RefreshVolume updates an existing volume to match the state of another.
 func (d *ceph) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, 
op *operations.Operation) error {
-       return genericCopyVolume(d, nil, vol, srcVol, srcSnapshots, true, op)
+       return genericVFSCopyVolume(d, nil, vol, srcVol, srcSnapshots, true, op)
 }
 
 // DeleteVolume deletes a volume of the storage device. If any snapshots of 
the volume remain then
diff --git a/lxd/storage/drivers/driver_dir_volumes.go 
b/lxd/storage/drivers/driver_dir_volumes.go
index bb2ca165ba..d79ce2b155 100644
--- a/lxd/storage/drivers/driver_dir_volumes.go
+++ b/lxd/storage/drivers/driver_dir_volumes.go
@@ -85,7 +85,7 @@ func (d *dir) CreateVolume(vol Volume, filler *VolumeFiller, 
op *operations.Oper
 // CreateVolumeFromBackup restores a backup tarball onto the storage device.
 func (d *dir) CreateVolumeFromBackup(vol Volume, snapshots []string, srcData 
io.ReadSeeker, optimizedStorage bool, op *operations.Operation) (func(vol 
Volume) error, func(), error) {
        // Run the generic backup unpacker
-       postHook, revertHook, err := genericBackupUnpack(d.withoutGetVolID(), 
vol, snapshots, srcData, op)
+       postHook, revertHook, err := 
genericVFSBackupUnpack(d.withoutGetVolID(), vol, snapshots, srcData, op)
        if err != nil {
                return nil, nil, err
        }
@@ -125,17 +125,17 @@ func (d *dir) CreateVolumeFromCopy(vol Volume, srcVol 
Volume, copySnapshots bool
        }
 
        // Run the generic copy.
-       return genericCopyVolume(d, d.setupInitialQuota, vol, srcVol, 
srcSnapshots, false, op)
+       return genericVFSCopyVolume(d, d.setupInitialQuota, vol, srcVol, 
srcSnapshots, false, op)
 }
 
 // CreateVolumeFromMigration creates a volume being sent via a migration.
 func (d *dir) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, 
volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op 
*operations.Operation) error {
-       return genericCreateVolumeFromMigration(d, d.setupInitialQuota, vol, 
conn, volTargetArgs, preFiller, op)
+       return genericVFSCreateVolumeFromMigration(d, d.setupInitialQuota, vol, 
conn, volTargetArgs, preFiller, op)
 }
 
 // RefreshVolume provides same-pool volume and specific snapshots syncing 
functionality.
 func (d *dir) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, 
op *operations.Operation) error {
-       return genericCopyVolume(d, d.setupInitialQuota, vol, srcVol, 
srcSnapshots, true, op)
+       return genericVFSCopyVolume(d, d.setupInitialQuota, vol, srcVol, 
srcSnapshots, true, op)
 }
 
 // DeleteVolume deletes a volume of the storage device. If any snapshots of 
the volume remain then
diff --git a/lxd/storage/drivers/driver_lvm_volumes.go 
b/lxd/storage/drivers/driver_lvm_volumes.go
index 6527de2364..aa15f5b73c 100644
--- a/lxd/storage/drivers/driver_lvm_volumes.go
+++ b/lxd/storage/drivers/driver_lvm_volumes.go
@@ -104,7 +104,7 @@ func (d *lvm) CreateVolume(vol Volume, filler 
*VolumeFiller, op *operations.Oper
 
 // CreateVolumeFromBackup restores a backup tarball onto the storage device.
 func (d *lvm) CreateVolumeFromBackup(vol Volume, snapshots []string, srcData 
io.ReadSeeker, optimizedStorage bool, op *operations.Operation) (func(vol 
Volume) error, func(), error) {
-       return genericBackupUnpack(d, vol, snapshots, srcData, op)
+       return genericVFSBackupUnpack(d, vol, snapshots, srcData, op)
 }
 
 // CreateVolumeFromCopy provides same-pool volume copying functionality.
@@ -138,12 +138,12 @@ func (d *lvm) CreateVolumeFromCopy(vol, srcVol Volume, 
copySnapshots bool, op *o
        }
 
        // Otherwise run the generic copy.
-       return genericCopyVolume(d, nil, vol, srcVol, srcSnapshots, false, op)
+       return genericVFSCopyVolume(d, nil, vol, srcVol, srcSnapshots, false, 
op)
 }
 
 // CreateVolumeFromMigration creates a volume being sent via a migration.
 func (d *lvm) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, 
volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op 
*operations.Operation) error {
-       return genericCreateVolumeFromMigration(d, nil, vol, conn, 
volTargetArgs, preFiller, op)
+       return genericVFSCreateVolumeFromMigration(d, nil, vol, conn, 
volTargetArgs, preFiller, op)
 }
 
 // RefreshVolume provides same-pool volume and specific snapshots syncing 
functionality.
@@ -154,7 +154,7 @@ func (d *lvm) RefreshVolume(vol, srcVol Volume, 
srcSnapshots []Volume, op *opera
        }
 
        // Otherwise run the generic copy.
-       return genericCopyVolume(d, nil, vol, srcVol, srcSnapshots, true, op)
+       return genericVFSCopyVolume(d, nil, vol, srcVol, srcSnapshots, true, op)
 }
 
 // DeleteVolume deletes a volume of the storage device. If any snapshots of 
the volume remain then this function
diff --git a/lxd/storage/drivers/driver_zfs_volumes.go 
b/lxd/storage/drivers/driver_zfs_volumes.go
index 7fc9ac2cb3..4e899166c1 100644
--- a/lxd/storage/drivers/driver_zfs_volumes.go
+++ b/lxd/storage/drivers/driver_zfs_volumes.go
@@ -191,7 +191,7 @@ func (d *zfs) CreateVolume(vol Volume, filler 
*VolumeFiller, op *operations.Oper
 func (d *zfs) CreateVolumeFromBackup(vol Volume, snapshots []string, srcData 
io.ReadSeeker, optimized bool, op *operations.Operation) (func(vol Volume) 
error, func(), error) {
        // Handle the non-optimized tarballs through the generic unpacker.
        if !optimized {
-               return genericBackupUnpack(d, vol, snapshots, srcData, op)
+               return genericVFSBackupUnpack(d, vol, snapshots, srcData, op)
        }
 
        revert := revert.New()
@@ -502,7 +502,7 @@ func (d *zfs) CreateVolumeFromCopy(vol Volume, srcVol 
Volume, copySnapshots bool
 func (d *zfs) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, 
volTargetArgs migration.VolumeTargetArgs, preFiller *VolumeFiller, op 
*operations.Operation) error {
        // Handle simple rsync and block_and_rsync through generic.
        if volTargetArgs.MigrationType.FSType == 
migration.MigrationFSType_RSYNC || volTargetArgs.MigrationType.FSType == 
migration.MigrationFSType_BLOCK_AND_RSYNC {
-               return genericCreateVolumeFromMigration(d, nil, vol, conn, 
volTargetArgs, preFiller, op)
+               return genericVFSCreateVolumeFromMigration(d, nil, vol, conn, 
volTargetArgs, preFiller, op)
        } else if volTargetArgs.MigrationType.FSType != 
migration.MigrationFSType_ZFS {
                return ErrNotSupported
        }
@@ -581,7 +581,7 @@ func (d *zfs) CreateVolumeFromMigration(vol Volume, conn 
io.ReadWriteCloser, vol
 
 // RefreshVolume updates an existing volume to match the state of another.
 func (d *zfs) RefreshVolume(vol Volume, srcVol Volume, srcSnapshots []Volume, 
op *operations.Operation) error {
-       return genericCopyVolume(d, nil, vol, srcVol, srcSnapshots, true, op)
+       return genericVFSCopyVolume(d, nil, vol, srcVol, srcSnapshots, true, op)
 }
 
 // DeleteVolume deletes a volume of the storage device. If any snapshots of 
the volume remain then
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to