[lxc-devel] [lxd/master] Storage: Moves functions in generic.go to generic_vfs.go

tomponline on Github lxc-bot at linuxcontainers.org
Tue Mar 24 14:42:08 UTC 2020


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200324/af87a742/attachment-0001.bin>
-------------- next part --------------
From 949aaf40b2faa5eff6c6c8caeb22787fb2c5af9a Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at 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.parrott at 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.parrott at 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.parrott at 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


More information about the lxc-devel mailing list