I was asked a few days ago if libguestfs has a way to apply a group of changes to an image together. The question was really about transaction support — applying a group of changes and then committing them or doing a rollback, with the final image either containing all the changes or none of them.
Although libguestfs doesn’t support this, you can do it using libguestfs and the qemu-img tool together. This post shows you how.
First I use virt-builder to quickly get a test image that I can play with:
$ virt-builder fedora-20
We create an overlay which will store the changes until we decide to commit or rollback:
$ qemu-img create -f qcow2 -b fedora-20.img overlay.img
Now open the overlay and make your changes:
$ guestfish -a overlay.img -i Welcome to guestfish, the guest filesystem shell for editing virtual machine filesystems and disk images. Type: 'help' for help on commands 'man' to read the manual 'quit' to quit the shell Operating system: Fedora release 20 (Heisenbug) /dev/sda3 mounted on / /dev/sda1 mounted on /boot ><fs> write-append /etc/issue.net \ "THIS IS A CHANGE TO ISSUE.NET\n" ><fs> cat /etc/issue.net Fedora release 20 (Heisenbug) Kernel \r on an \m (\l) THIS IS A CHANGE TO ISSUE.NET ><fs> exit
The base image (
fedora-20.img) is untouched, and the overlay contains the changes we made:
$ virt-cat -a fedora-20.img /etc/issue.net Fedora release 20 (Heisenbug) Kernel \r on an \m (\l) $ virt-cat -a overlay.img /etc/issue.net Fedora release 20 (Heisenbug) Kernel \r on an \m (\l) THIS IS A CHANGE TO ISSUE.NET
Rollback is pretty simple!
$ rm overlay.img
The more interesting one is how to commit the changes back to the original file. Using qemu-img you just do:
$ qemu-img commit overlay.img Image committed. $ rm overlay.img
The changes are now contained in the original image file:
$ virt-cat -a fedora-20.img /etc/issue.net Fedora release 20 (Heisenbug) Kernel \r on an \m (\l) THIS IS A CHANGE TO ISSUE.NET
Have we discovered the ACID properties of disk images? Not quite.
Although the change is atomic (A)1, the disk image is consistent (C) before and after the change, and the change is durable (D)2, the final property is not satisfied.
There is no isolation (I). Because it is infeasible to resolve conflicts at the block layer where qemu-img operates, it would be guaranteed corruption if you tried this technique in parallel on the same disk image. The only way to make it work reliably is to serialize every operation on the disk image with a mutex.
1 The change is only atomic if you don’t look at the backing file for the short time that
qemu-img commit runs.
2 Strictly speaking, you must call sync or fsync after the
qemu-img commit in order for the change to be durable.