QCOW 3 Ways

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
  • 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

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 of mount
$ 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 using qemu-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

Leave a Comment