How to mount QCOW images as Linux block devices
tl;dr
- guestmount (requires libguestfs-tools)
sudo guestmount -d <vm-name> --ro -i <mountpoint>
- qemu-nbd (requires the nbd driver)
- Load the kernel module
modprobe nbd max_part=8
- Bind the device to the image
qemu-nbd --connect=/dev/nbd0 <vmdiskimage.qcow>
- Assuming partition #1 is the target
mount /dev/ndb0p1 /a
- Load the kernel module
- loopback mount. Requires converting qcow to raw
- Convert qcow to raw
qemu-img convert vmdisk.qcow2 -f qcow2 -O raw vmdisk.raw
- Create a loopback device
losetup -f -P vmdisk.raw
- Locate name of loopback device
losetup -l | grep vmdisk.raw
- Mount (assuming partition #1 on loopback device 99
mount /dev/mapper/loop99p1 /a
- Convert qcow to raw
Mounting qcow guest disks on KVM host with guestmount
If you are working with vanilla kvm
and are on the KVM host, the easiest way to mount disks (at least disks with standard Linux fileystems) is to use guestmount
– which is installed with libguestfs-tools
. Assume we have a mountpoint /a
, a vm called vm3-full-kernel-20G
whose disk is located at /vmdisks/ubuntu-20.10-server-cloudimg-amd64_VM1_root-full-kernel-20G.qcow2
mount disks READ ONLY from a running domain (VM)
- mount disk read only
$ sudo guestmount -d vm3-full-kernel-20G --ro -i /a
- unmount using standard command
umount /a
- Appears as
fuse
in the output ofmount
$ mount | grep /a
/dev/fuse on /a type fuse (rw,nosuid,nodev,relatime,user_id=0,group_id=0)
mount disks READ/WRITE for a shutdown VM (domain)
- mount disk read/write
sudo guestmount -d vm3-full-kernel-20G -i /a
Example – write a file in the guest FS from the virtualization host
# Virtualization host
$ sudo guestmount -d vm3-full-kernel-20G -i /a
$ sudo touch /a/gary
$ sudo umount /a
$ virsh start vm3-full-kernel-20G
$ virsh console vm3-full-kernel-20G
# Guest vm (vm3-full-kernel-20G)
ubuntu@ubuntu:~$ date
Thu Sep 8 19:21:56 UTC 2022
ubuntu@ubuntu:~$ ls -l /gary
-rw-r--r-- 1 root root 0 Sep 8 19:20 /gary
Mounting qcow disks with nbd
nbd
is usually used to access remote disks – but with the qemu-nbd
utility you can use the nbd
driver to mount qcow files.
- Load the kernel module
# modprobe nbd max_part=8
- Bind the nbd device to a
qcow2
file usingqemu-nbd
# qemu-nbd --connect=/dev/nbd0 /vmdisks/ubuntu-20.10-server-cloudimg-amd64_VM1_root-full-kernel-20G.qcow2
- List the available disk partitions on the device
fdisk -l /dev/nbd0
fdisk -l /dev/nbd0
Disk /dev/nbd0: 20 GiB, 21474836480 bytes, 41943040 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: D0DABDE9-0E22-4536-BA44-05BCDE0661E9
Device Start End Sectors Size Type
/dev/nbd0p1 227328 41943006 41715679 19.9G Linux filesystem
/dev/nbd0p14 2048 10239 8192 4M BIOS boot
/dev/nbd0p15 10240 227327 217088 106M EFI System
Partition table entries are not in disk order.
- mount the partition
mount /dev/ndb0p1 /a
- unmount
umount /a
- disconnect the qcow from nbd
qemu-nbd --disconnect /dev/nbd0
- Unload the kernel module
rmmod nbd
Example mounting NTFS guest disk to Linux
root@aws-i3en-jump:/home/ubuntu# modprobe nbd max_part=8
root@aws-i3en-jump:/home/ubuntu# qemu-nbd --connect=/dev/nbd0 sqlservertpcc3-disk-index-3.qcow2
root@aws-i3en-jump:/home/ubuntu# fdisk /dev/nbd0 -l
Disk /dev/nbd0: 50 GiB, 53687091200 bytes, 104857600 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: DDA58512-CF96-417F-AEDE-1B787E1247AB
Device Start End Sectors Size Type
/dev/nbd0p1 34 262177 262144 128M Microsoft reserved
/dev/nbd0p2 264192 104855551 104591360 49.9G Microsoft basic data
root@aws-i3en-jump:/home/ubuntu# mount -t ntfs /dev/nbd0p2 /a
root@aws-i3en-jump:/home/ubuntu# cd /a
root@aws-i3en-jump:/a# ls
'$RECYCLE.BIN' tempdb.mdf tempdb_mssql_5.ndf
'System Volume Information' tempdb_mssql_3.ndf tempdb_mssql_7.ndf
Mounting raw disks with losetup
If the guest disk is raw
either natively or having been converted using something like below. The name used in the mount table will be the name of the ‘volume’ in the image not the name of the image file itself.
qemu-img convert ubuntu-clone-no-resize.qcow2 -f qcow2 -O raw ubuntu-clone-no-resize.raw
The disk can be mounted simply by using losetup
– on my ubuntu
running losetup
also mounted the drive under /media/gary/cloudimg-rootfs
Run the command losetup
-f
find the next free /dev/loop
slot and -P
scan for partitions
# losetup -f -P ubuntu-clone-no-resize.raw
Find the loopback device
# losetup -l | grep ubuntu-clone-no-resize.raw
/dev/loop99 0 0 1 0 /vmdisks/ubuntu-clone-no-resize.raw 0 512
Confirm – the partitions will not show up under /dev/loop – look under /dev/mapper
.
# ls -l /dev/loop99*
brw-rw---- 1 root disk 7, 99 Sep 8 16:35 /dev/loop99
# ls /dev/mapper/loop99*
/dev/mapper/loop99p1 /dev/mapper/loop99p14 /dev/mapper/loop99p15
Mount the device (if not automounted)
# mount /dev/mapper/loop99p1 /a
Find the mountpoint if automounted – else cd to the mount point
# mount | grep loop
/dev/mapper/loop99p1 on /media/gary/cloudimg-rootfs type ext4 (rw,nosuid,nodev,relatime,errors=remount-ro,uhelper=udisks2)
Access the mounted file/filesystem
# ls -l /media/gary/cloudimg-rootfs/
total 80
lrwxrwxrwx 1 root root 7 Jul 20 2021 bin -> usr/bin
drwxr-xr-x 4 root root 4096 Jul 20 2021 boot
drwxr-xr-x 4 root root 4096 Jul 20 2021 dev
drwxr-xr-x 90 root root 4096 Sep 8 12:16 etc
drwxr-xr-x 3 root root 4096 Sep 8 12:16 home
lrwxrwxrwx 1 root root 7 Jul 20 2021 lib -> usr/lib
lrwxrwxrwx 1 root root 9 Jul 20 2021 lib32 -> usr/lib32
lrwxrwxrwx 1 root root 9 Jul 20 2021 lib64 -> usr/lib64
lrwxrwxrwx 1 root root 10 Jul 20 2021 libx32 -> usr/libx32
drwx------ 2 root root 16384 Jul 20 2021 lost+found
drwxr-xr-x 2 root root 4096 Jul 20 2021 media
drwxr-xr-x 2 root root 4096 Jul 20 2021 mnt
drwxr-xr-x 2 root root 4096 Jul 20 2021 opt
drwxr-xr-x 2 root root 4096 Oct 16 2020 proc
drwx------ 4 root root 4096 Sep 8 12:16 root
drwxr-xr-x 3 root root 4096 Jul 20 2021 run
lrwxrwxrwx 1 root root 8 Jul 20 2021 sbin -> usr/sbin
Unmount and release the loopback device
# umount /a
# losetup -d /dev/loop99
If you got into some weird dm
situation – use dmsetup info
them dmsetup remove <device>
root@rodney:/# dmsetup info
Name: loop99p1
State: ACTIVE
Read Ahead: 256
Tables present: LIVE
Open count: 0
Event number: 0
Major, minor: 253, 2
Number of targets: 1
UUID: part1-devnode_7:99_Wh5pYvM
...
Name: loop99p14
...
Name: loop99p15
root@rodney:/# dmsetup remove loop99p1
root@rodney:/# dmsetup remove loop99p14
root@rodney:/# dmsetup remove loop99p15
Then the loopback device is removed
# losetup -l
NAME SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE DIO LOG-SEC
/dev/loop1 0 0 1 1 /var/lib/snapd/snaps/core_13425.snap 0 512
/dev/loop15 0 0 1 1 /var/lib/snapd/snaps/snap-store_558.snap 0 512
/dev/loop6 0 0 1 1 /var/lib/snapd/snaps/firefox_1775.snap 0 512
/dev/loop4 0 0 1 1 /var/lib/snapd/snaps/core20_1611.snap 0 512
/dev/loop11 0 0 1 1 /var/lib/snapd/snaps/gnome-3-38-2004_115.snap 0 512
/dev/loop16 0 0 1 1 /var/lib/snapd/snaps/snapd-desktop-integration_14.snap 0 512
/dev/loop14 0 0 1 1 /var/lib/snapd/snaps/gtk-common-themes_1535.snap 0 512