mirror of
https://github.com/QubesOS/qubes-doc.git
synced 2024-12-22 05:55:05 -05:00
237 lines
8.0 KiB
ReStructuredText
237 lines
8.0 KiB
ReStructuredText
=======================
|
||
Template implementation
|
||
=======================
|
||
|
||
|
||
Block devices of a VM
|
||
---------------------
|
||
|
||
|
||
Every VM has 4 block devices connected:
|
||
|
||
- **xvda** – base root device (/) – details described below
|
||
|
||
- **xvdb** – private.img – place where VM always can write.
|
||
|
||
- **xvdc** – volatile.img, discarded at each VM restart – here is
|
||
placed swap and temporal “/” modifications (see below)
|
||
|
||
- **xvdd** – modules.img – kernel modules and firmware
|
||
|
||
|
||
|
||
private.img (xvdb)
|
||
^^^^^^^^^^^^^^^^^^
|
||
|
||
|
||
This is mounted as /rw and here is placed all VM private data. This
|
||
includes:
|
||
|
||
- */home* – which is bind mounted to /rw/home
|
||
|
||
- */usr/local* – which is symlink to /rw/usrlocal
|
||
|
||
- some config files (/rw/config) called by qubes core scripts (ex
|
||
/rw/config/rc.local)
|
||
|
||
|
||
|
||
**Note:** Whenever a TemplateBasedVM is created, the contents of the
|
||
``/home`` directory of its parent TemplateVM :ref:`are not copied to the
|
||
child TemplateBasedVM’s
|
||
/home <user/templates/templates:inheritance and persistence>`. The child
|
||
TemplateBasedVM’s ``/home`` is independent from its parent TemplateVM’s
|
||
``/home``, which means that any changes to the parent TemplateVM’s
|
||
``/home`` will not affect the child TemplateBasedVM’s ``/home``. Once a
|
||
TemplateBasedVM has been created, any changes in its ``/home``,
|
||
``/usr/local``, or ``/rw/config`` directories will be persistent across
|
||
reboots, which means that any files stored there will still be available
|
||
after restarting the TemplateBasedVM. No changes in any other
|
||
directories in TemplateBasedVMs persist in this manner. If you would
|
||
like to make changes in other directories which *do* persist in this
|
||
manner, you must make those changes in the parent TemplateVM.
|
||
|
||
modules.img (xvdd)
|
||
^^^^^^^^^^^^^^^^^^
|
||
|
||
|
||
As the kernel is chosen in dom0, there must be some way to provide
|
||
matching kernel modules to VM. Qubes kernel directory consists of 3
|
||
files:
|
||
|
||
- *vmlinuz* – actual kernel
|
||
|
||
- *initramfs* – initial ramdisk containing script to setup snapshot
|
||
devices (see below) and mount /lib/modules
|
||
|
||
- *modules.img* – filesystem image of /lib/modules with matching kernel
|
||
modules and firmware (/lib/firmware/updates is symlinked to
|
||
/lib/modules/firmware)
|
||
|
||
|
||
|
||
Normally kernel “package” is common for many VMs (can be set using
|
||
qvm-prefs). One of them can be set as default (qvm-set-default-kernel)
|
||
to simplify kernel updates (by default all VMs use the default kernel).
|
||
All installed kernels are placed in /var/lib/qubes/vm-kernels as
|
||
separate subdirs. In this case, modules.img is attached to the VM as R/O
|
||
device.
|
||
|
||
There is a special case when the VM can have a custom kernel – when it
|
||
is updateable (StandaloneVM or TemplateVM) and the kernel is set to
|
||
“none” (by qvm-prefs). In this case the VM uses the kernel from the
|
||
“kernels” VM subdir and modules.img is attached as R/W device.
|
||
|
||
Qubes TemplateVM implementation
|
||
-------------------------------
|
||
|
||
|
||
TemplateVM has a shared root.img across all AppVMs that are based on it.
|
||
This mechanism has some advantages over a simple common device connected
|
||
to multiple VMs:
|
||
|
||
- root.img can be modified while there are AppVMs running – without
|
||
corrupting the filesystem
|
||
|
||
- multiple AppVMs that are using different versions of root.img (from
|
||
various points in time) can be running concurrently
|
||
|
||
|
||
|
||
There are two layers of the device-mapper snapshot device; the first one
|
||
enables modifying root.img without stopping the AppVMs and the second
|
||
one, which is contained in the AppVM, enables temporal modifications to
|
||
its filesystem. These modifications will be discarded after a restart of
|
||
the AppVM.
|
||
|
||
.. figure:: /attachment/doc/TemplateSharing2.png
|
||
:alt: TemplateSharing2.png
|
||
|
||
TemplateSharing2.png
|
||
|
||
Snapshot device in Dom0
|
||
^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
|
||
This device consists of:
|
||
|
||
- root.img – real template filesystem
|
||
|
||
- root-cow.img – differences between the device as seen by AppVM and
|
||
the current root.img
|
||
|
||
|
||
|
||
The above is achieved through creating device-mapper snapshots for each
|
||
version of root.img. When an AppVM is started, a xen hotplug script
|
||
(/etc/xen/scripts/block-snapshot) reads the inode numbers of root.img
|
||
and root-cow.img; these numbers are used as the snapshot device’s name.
|
||
When a device with the same name exists the new AppVM will use it –
|
||
therefore, AppVMs based on the same version of root.img will use the
|
||
same device. Of course, the device-mapper cannot use the files directly
|
||
– it must be connected through /dev/loop*. The same mechanism detects
|
||
if there is a loop device associated with a file determined by the
|
||
device and inode numbers – or if creating a new loop device is
|
||
necessary.
|
||
|
||
When an AppVM is stopped the xen hotplug script checks whether the
|
||
device is still in use – if it is not, the script removes the snapshot
|
||
and frees the loop device.
|
||
|
||
Changes to template filesystem
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
|
||
In order for the full potential of the snapshot device to be realized,
|
||
every change in root.img must save the original version of the modified
|
||
block in root-cow.img. This is achieved by a snapshot-origin device.
|
||
|
||
When TemplateVM is started, it receives the snapshot-origin device
|
||
connected as a root device (in read-write mode). Therefore, every change
|
||
to this device is immediately saved in root.img – but remains invisible
|
||
to the AppVM, which uses the snapshot.
|
||
|
||
When TemplateVM is stopped, the xen script moves root-cow.img to
|
||
root-cow.img.old and creates a new one (using the
|
||
``qvm-template-commit`` tool). The snapshot device will remain untouched
|
||
due to the loop device, which uses an actual file on the disk (by inode,
|
||
not by name). Linux kernel frees the old root-cow.img files as soon as
|
||
they are unused by all snapshot devices (to be exact, loop devices). The
|
||
new root-cow.img file will get a new inode number, and so new AppVMs
|
||
will get new snapshot devices (with different names).
|
||
|
||
Rollback template changes
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
|
||
There is possibility to rollback last template changes. Saved
|
||
root-cow.img.old contains all changes made during last TemplateVM run.
|
||
Rolling back changes is done by reverting this “binary patch”.
|
||
|
||
This is done using snapshot-merge device-mapper target (available from
|
||
2.6.34 kernel). It requires that no other snapshot device uses
|
||
underlying block devices (root.img, root-cow.img via loop device).
|
||
Because of this all AppVMs based on this template must be halted during
|
||
this operation.
|
||
|
||
Steps performed by **qvm-revert-template-changes**:
|
||
|
||
1. Ensure that no other VMs uses this template.
|
||
|
||
2. Prepare snapshot device with **root-cow.img.old** instead of
|
||
*root-cow.img* (*/etc/xen/scripts/block-snapshot prepare*).
|
||
|
||
3. Replace *snapshot* device-mapper target with *snapshot-merge*, other
|
||
parameters (chunk size etc) remains untouched. Now kernel starts
|
||
merging changes stored in *root-cow.img.old* into *root.img*. d-m
|
||
device can be used normally (if needed).
|
||
|
||
4. Waits for merge completed: *dmsetup status* shows used snapshot
|
||
blocks – it should be equal to metadata size when completed.
|
||
|
||
5. Replace *snapshot-merge* d-m target back to *snapshot*.
|
||
|
||
6. Cleanup snapshot device (if nobody uses it at the moment).
|
||
|
||
7. Move *root-cow.img.old* to *root-cow.img* (overriding existing file).
|
||
|
||
|
||
|
||
Snapshot device in AppVM
|
||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||
|
||
|
||
Root device is exposed to AppVM in read-only mode. AppVM can write only
|
||
in:
|
||
|
||
- private.img – persistent storage (mounted in /rw) used for /home,
|
||
/usr/local – in future versions, its use may be extended
|
||
|
||
- volatile.img – temporary storage, which is discarded after an AppVM
|
||
restart
|
||
|
||
|
||
|
||
volatile.img is divided into two partitions:
|
||
|
||
1. changes to root device
|
||
|
||
2. swap partition
|
||
|
||
|
||
|
||
Inside of an AppVM, the root device is wrapped by the snapshot in the
|
||
first partition of volatile.img. Therefore, the AppVM can write anything
|
||
to its filesystem – however, such changes will be discarded after a
|
||
restart.
|
||
|
||
StandaloneVM
|
||
^^^^^^^^^^^^
|
||
|
||
|
||
Standalone VM enables user to modify root filesystem persistently. It
|
||
can be created using *–standalone* switch to *qvm-create*.
|
||
|
||
It is implemented just like TemplateVM (has own root.img connected as
|
||
R/W device), but no other VMs can be based on it.
|