diff --git a/internal/server/storage/drivers/driver_common.go b/internal/server/storage/drivers/driver_common.go index 1f734a27b43..69f5397ca67 100644 --- a/internal/server/storage/drivers/driver_common.go +++ b/internal/server/storage/drivers/driver_common.go @@ -277,6 +277,26 @@ func (d *common) moveGPTAltHeader(devPath string) error { return nil } + // Our images and VM drives use a 512 bytes sector size. + // If the underlying block device uses a different sector size, we + // need to fake the correct size through a loop device so sgdisk can + // correctly re-locate the partition tables. + if linux.IsBlockdevPath(devPath) { + blockSize, err := GetPhysicalBlockSize(devPath) + if err != nil { + return err + } + + if blockSize != 512 { + devPath, err = loopDeviceSetupAlign(devPath) + if err != nil { + return err + } + + defer func() { _ = loopDeviceAutoDetach(devPath) }() + } + } + _, err = subprocess.RunCommand(path, "--move-second-header", devPath) if err == nil { d.logger.Debug("Moved GPT alternative header to end of disk", logger.Ctx{"dev": devPath}) diff --git a/internal/server/storage/drivers/driver_lvm_utils.go b/internal/server/storage/drivers/driver_lvm_utils.go index 25c0250e3b5..775dc24d682 100644 --- a/internal/server/storage/drivers/driver_lvm_utils.go +++ b/internal/server/storage/drivers/driver_lvm_utils.go @@ -9,9 +9,6 @@ import ( "path/filepath" "strconv" "strings" - "unsafe" - - "golang.org/x/sys/unix" internalInstance "github.com/lxc/incus/v6/internal/instance" "github.com/lxc/incus/v6/internal/linux" @@ -888,22 +885,3 @@ func (d *lvm) deactivateVolume(vol Volume) (bool, error) { return false, nil } - -func (d *lvm) getBlockSize(path string) (int, error) { - // Open the block device. - f, err := os.Open(path) - if err != nil { - return -1, err - } - - defer func() { _ = f.Close() }() - - // Query the physical block size. - var res int32 - _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(f.Fd()), unix.BLKPBSZGET, uintptr(unsafe.Pointer(&res))) - if errno != 0 { - return -1, fmt.Errorf("Failed to BLKPBSZGET: %w", unix.Errno(errno)) - } - - return int(res), nil -} diff --git a/internal/server/storage/drivers/driver_lvm_volumes.go b/internal/server/storage/drivers/driver_lvm_volumes.go index 5fb88c75bc3..ea80e1d1f07 100644 --- a/internal/server/storage/drivers/driver_lvm_volumes.go +++ b/internal/server/storage/drivers/driver_lvm_volumes.go @@ -70,18 +70,6 @@ func (d *lvm) CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Oper if err != nil { return err } - - // Check the block size for image volumes. - if vol.volType == VolumeTypeImage { - blockSize, err := d.getBlockSize(devPath) - if err != nil { - return err - } - - if blockSize != 512 { - return fmt.Errorf("Underlying storage uses %d bytes sector size when virtual machine images require 512 bytes", blockSize) - } - } } allowUnsafeResize := false diff --git a/internal/server/storage/drivers/utils.go b/internal/server/storage/drivers/utils.go index 36414dad473..3c878f36f68 100644 --- a/internal/server/storage/drivers/utils.go +++ b/internal/server/storage/drivers/utils.go @@ -12,6 +12,7 @@ import ( "sort" "strings" "time" + "unsafe" "golang.org/x/sys/unix" @@ -797,6 +798,26 @@ func BlockDiskSizeBytes(blockDiskPath string) (int64, error) { return fi.Size(), nil } +// GetPhysicalBlockSize returns the physical block size for the device. +func GetPhysicalBlockSize(blockDiskPath string) (int, error) { + // Open the block device. + f, err := os.Open(blockDiskPath) + if err != nil { + return -1, err + } + + defer func() { _ = f.Close() }() + + // Query the physical block size. + var res int32 + _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(f.Fd()), unix.BLKPBSZGET, uintptr(unsafe.Pointer(&res))) + if errno != 0 { + return -1, fmt.Errorf("Failed to BLKPBSZGET: %w", unix.Errno(errno)) + } + + return int(res), nil +} + // OperationLockName returns the storage specific lock name to use with locking package. func OperationLockName(operationName string, poolName string, volType VolumeType, contentType ContentType, volName string) string { return fmt.Sprintf("%s/%s/%s/%s/%s", operationName, poolName, volType, contentType, volName) @@ -841,6 +862,16 @@ func loopDeviceSetup(sourcePath string) (string, error) { return strings.TrimSpace(out), nil } +// loopDeviceSetupAlign creates a forced 512-byte aligned loop device. +func loopDeviceSetupAlign(sourcePath string) (string, error) { + out, err := subprocess.RunCommand("losetup", "-b", "512", "--find", "--nooverlap", "--show", sourcePath) + if err != nil { + return "", err + } + + return strings.TrimSpace(out), nil +} + // loopFileAutoDetach enables auto detach mode for a loop device. func loopDeviceAutoDetach(loopDevPath string) error { _, err := subprocess.RunCommand("losetup", "--detach", loopDevPath)