1. Overview
QCOW2, which stands for QEMU Copy-On-Write version 2, is a popular file format used for virtual disk images in QEMU. In this tutorial, we’ll look at different methods of mounting QCOW2 image files in a Linux environment, using the official QCOW2 image of Debian 12, saved locally as example.qcow2, as a reference.
Before mounting a QCOW2 image, it’s recommended to disable os-prober, as it may add unwanted and incorrect menu entries to GRUB. These entries will be unusable because they will refer to our QCOW2 virtual disks, which GRUB doesn’t support.
In the following steps, we’ll see the necessary Debian packages that we can install with apt. Other distributions have their own equivalent packages.
2. Network Block Device (NBD)
Network Block Device (NBD) is a protocol that provides a versatile way to access block devices over a network. Essentially, it treats remote block devices as if they’re locally attached to our system so we can manage them with the same tools and protocols we use for local storage.
It’s one of the most appropriate solutions for mounting QCOW2 images in Linux because it efficiently handles the advanced features of QCOW2 at the kernel level, ensuring that the underlying complexities are transparent to the user.
2.1. Connect the QCOW2 Image to a NBD Device
First, let’s load the NBD kernel module with modprobe. In most standard Linux distributions, this command should work out of the box:
$ sudo modprobe nbd max_part=8
The Linux kernel needs the max_part option to create device files for the partitions on the NBD device. We can adjust the choice of 8 based on the maximum number of partitions we expect to have on the QCOW2 image.
Next, let’s connect the QCOW2 image to the NBD device with qemu-nbd, which is part of the qemu-utils package:
$ sudo qemu-nbd --connect=/dev/nbd0 ~/example.qcow2
If we are using the graphical environment of a recent Linux distribution, we may find the QCOW2 disk already added to the local file manager with read/write permissions without any further action. It can be as simple as adding a USB stick. For example, here is the result in Linux Mint 21:
Out of curiosity, let’s check where the local file manager mounted the QCOW2 disk:
$ mount
[...]
/dev/nbd0p1 on /media/francesco/8f994d1d-8ae8-4dff-aaa5-9aef4574329b type ext4 (rw,[...])
However, the file manager of our distribution may not have this feature.
2.2. Mount an NBD Partition
We may still need or want to manually mount an NBD partition. In this case, let’s first check the available partitions with fdisk to find the one we want to mount:
$ sudo fdisk -l /dev/nbd0
Disk /dev/nbd0: 2 GiB, 2147483648 bytes, 4194304 sectors
[...]
Device Start End Sectors Size Type
/dev/nbd0p1 262144 4192255 3930112 1,9G Linux filesystem
/dev/nbd0p14 2048 8191 6144 3M BIOS boot
/dev/nbd0p15 8192 262143 253952 124M EFI System
[...]
We are interested in /dev/nbd0p1. Let’s mount it with read/write permissions to a local directory:
$ sudo mkdir /mnt/qcow2
$ sudo mount /dev/nbd0p1 /mnt/qcow2
If we want read-only access to prevent any changes to the QCOW2 image, we can use the -o ro option:
$ sudo mount -o ro /dev/nbd0p1 /mnt/qcow2
Let’s remember to unmount the virtual hard disk after using it:
$ sudo umount /mnt/qcow2
As a final note, when the system is rebooted, the NBD kernel module is typically unloaded, and any established NBD connections are disconnected.
2.3. Automatically Mount QCOW2 Image on Boot
To make our NBD setup persistent across reboots, we can use a systemd service to load the kernel module and mount the QCOW2 image.
Let’s create the file /etc/systemd/system/mount-qcow2.service with the following content, making sure we replace /home/user/example.qcow2 with the actual path to our QCOW2 file and /mnt/qcow2 with the mount point we want to use:
[Unit]
Description=Mount QCOW2 Image using NBD
After=network-online.target
[Service]
Type=oneshot
ExecStart=/bin/sh -c 'modprobe nbd max_part=8 && qemu-nbd --connect=/dev/nbd0 /home/user/example.qcow2 && sleep 5 && mount /dev/nbd0p1 /mnt/qcow2'
RemainAfterExit=yes
ExecStop=/bin/sh -c 'umount /mnt/qcow2 && qemu-nbd --disconnect /dev/nbd0'
TimeoutSec=0
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
Compared to the commands we saw earlier, the only addition in this service is sleep 5. This introduces a delay between the qemu-nbd command and the mount command because the /dev/nbd0 device may take a moment to be ready after it is connected to qemu-nbd.
After creating this service file, let’s enable it:
$ sudo systemctl enable mount-qcow2.service
Created symlink /etc/systemd/system/multi-user.target.wants/mount-qcow2.service → /etc/systemd/system/mount-qcow2.service.
At the next reboot, the service automatically loads the NBD module and mounts the QCOW2 image. If there are any problems, let’s take a look at the status of the service:
$ systemctl status mount-qcow2.service
● mount-qcow2.service - Mount QCOW2 Image using NBD
[...]
Active: active (exited) since Tue 2023-11-07 20:48:48 +07; 1min 8s ago
[...]
If that is not enough, we can also check all its logs:
$ sudo journalctl -u mount-qcow2.service
The combination of systemctl status and journalctl commands is usually sufficient to diagnose most service-related problems in systemd.
3. Alternatives to NBD
Despite its benefits, there are scenarios where NBD is not an option. Let’s explore some alternatives.
3.1. Loop Device
A loop device in Linux is a pseudo-device that allows a file to be treated as a block device. This means we can take an image file containing a file system and make the system treat it like a hard drive, CD-ROM, USB stick, or other physical storage device.
Mounting a QCOW2 image directly as a loop device isn’t possible because the QCOW2 format includes features like snapshots, compression, copy-on-write, dynamic size, and encryption that the loop device driver doesn’t understand. However, qemu-img, which is part of the qemu-utils package, can convert a QCOW2 image to an uncompressed and fixed-size raw disk image format, which we can then mount as a loop device:
$ qemu-img convert -O raw ~/example.qcow2 ~/example.raw
$ file example.*
example.qcow2: QEMU QCOW2 Image (v3), 2147483648 bytes
example.raw: DOS/MBR boot sector, extended partition table (last)
$ ls -l example.*
-rw-rw-r-- 1 francesco francesco 390266880 Nov 8 19:48 example.qcow2
-rw-r--r-- 1 francesco francesco 2147483648 Nov 8 23:25 example.raw
losetup associates loop devices with regular files or block devices. The -f flag tells losetup to use the first unused loop device, and –show shows the name of the associated loop device:
$ sudo losetup -f --show ~/example.raw
/dev/loop28
This way, the loop device /dev/loop28 maps the file ~/example.raw. At this point, we can see its partitions:
$ sudo fdisk -l /dev/loop28
Disk /dev/loop28: 2 GiB, 2147483648 bytes, 4194304 sectors
[...]
Device Start End Sectors Size Type
/dev/loop28p1 262144 4192255 3930112 1,9G Linux filesystem
/dev/loop28p14 2048 8191 6144 3M BIOS boot
/dev/loop28p15 8192 262143 253952 124M EFI System
Sometimes, the kernel doesn’t automatically know the partition table of the loop device, even if fdisk shows it. That’s why it’s better to force the kernel to read it using partprobe before proceeding:
$ sudo partprobe /dev/loop28
Now we’re ready to mount the partition we’re interested in:
$ sudo mount /dev/loop28p1 /mnt/qcow2
$ sudo du -sh /mnt/qcow2
988M /mnt/qcow2
The most obvious drawback to this approach is the amount of space it takes. The file system contained in the QCOW2 image is 988MB, the QCOW2 file is only 372MB due to compression, but the raw image we use here takes up 2GB.
3.2. guestfish
guestfish is a shell and command line tool for safely and conveniently editing virtual machine disk images. It’s provided by the guestfish package. Its shell has a rich set of commands to access and modify the contents of disk images used by virtual machines, such as those in QCOW2 format.
First, let’s start guestfish in read/write mode without a disk image:
$ sudo guestfish --rw
Welcome to guestfish, the guest filesystem shell for
editing virtual machine filesystems and disk images.
[...]
><fs>
Next, inside the guestfish shell, let’s attach the disk image, replacing our sample full path with the actual one:
><fs> add /home/user/example.qcow2
Now, let’s execute the run command to detect the partitions and file systems:
><fs> run
100% ⟦▒▒▒▒▒▒▒▒▒⟧ 00:00
Let’s list the file systems guestfish has just detected:
><fs> list-filesystems
/dev/sda1: ext4
/dev/sda14: unknown
/dev/sda15: vfat
We’re ready to mount the desired partition and list the contents of the root directory:
><fs> mount /dev/sda1 /
><fs> ls /
bin
boot
dev
[...]
usr
var
Let’s look at the contents of a test file that we previously created with NBD:
><fs> cat /tmp/testfile.txt
Test4
After finishing the operations, let’s remember to terminate guestfish:
><fs> quit
For simple tasks, guestfish can be more complex than a standard mount operation. Another drawback is that it has its own set of commands and syntax, which may require additional learning for new users.
3.3. guestmount
guestmount is a libguestfs tool distributed in a separate package. It allows us to mount virtual machine disk images, including QCOW2:
$ sudo guestmount --add ~/example.qcow2 --mount /dev/sda1 /mnt/qcow2
In this example:
- –add option specifies the path to the disk image
- –mount option specifies the partition to mount, in this case, /dev/sda1
- /mnt/qcow2 is the directory where the file system will be mounted
Let’s do a test:
$ sudo ls /mnt/qcow2
bin dev home lib32 libx32 media opt root sbin sys usr
boot etc lib lib64 lost+found mnt proc run srv tmp var
$ sudo cat /mnt/qcow2/tmp/testfile.txt
Test4
guestmount has its place and may be the preferred option when the goal is to quickly access the file system inside a disk image. However, when comparing guestmount to NBD, the better efficiency of NBD is certainly a factor, but there are other considerations as well:
- NBD can offer better support for concurrent access
- NBD has more mature support for caching and cache coherency
- In the event of a system crash or failure, recovery from an NBD-mounted image can be easier
In addition, NBD exposes the image as a block device, which means it’s possible to perform block-level operations that might not be possible with guestmount. This includes things like using dd to clone sections of the disk, resizing partitions with tools like gparted or even running fsck on the file system.
4. Conclusion
In this article, we’ve discussed how to mount QCOW2 images under Linux, with different methods appropriate for different use cases:
- NBD is the most robust and efficient solution
- guestmount is the simplest solution
- guestfish is the only way to modify a virtual disk without mounting it in the host system
- A loop device is a well-supported solution but requires conversion from QCOW2 to raw
Whether we’re working in a development environment, performing forensic analysis, or managing virtual machine storage, mounting QCOW2 images with confidence is a valuable skill.