<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>It's Pi all the way down... - lvm</title><link href="https://waldorf.waveform.org.uk/" rel="alternate"></link><link href="https://waldorf.waveform.org.uk/feeds/lvm.atom.xml" rel="self"></link><id>https://waldorf.waveform.org.uk/</id><updated>2023-11-06T08:37:40+00:00</updated><entry><title>Building Blocks</title><link href="https://waldorf.waveform.org.uk/2023/building-blocks.html" rel="alternate"></link><published>2023-10-24T00:00:00+01:00</published><updated>2023-11-06T08:37:40+00:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2023-10-24:/2023/building-blocks.html</id><summary type="html">&lt;p class="first last"&gt;In which we finally cover what a block device&amp;nbsp;is&lt;/p&gt;
</summary><content type="html">&lt;p&gt;In an earlier series of posts (Playing with Blocks &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/playing-with-blocks-lvm.html"&gt;1&lt;/a&gt;, &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/playing-with-blocks-luks.html"&gt;2&lt;/a&gt;, &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2022/playing-with-blocks-its-all-connected.html"&gt;3&lt;/a&gt;, and &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2023/playing-with-blocks-reluks.html"&gt;4&lt;/a&gt;) we had a
look at customizing Ubuntu images for the Raspberry Pi by playing around with
various block device transformations. However, we never covered &lt;em&gt;what&lt;/em&gt; a block
device actually &lt;em&gt;is&lt;/em&gt;. This is going to be a vaguely important topic in an
upcoming series of posts on &lt;abbr title="Network Block Device"&gt;&lt;span class="caps"&gt;NBD&lt;/span&gt;&lt;/abbr&gt; booting of
Ubuntu, so I figured some brief coverage of the topic is&amp;nbsp;warranted.&lt;/p&gt;
&lt;p&gt;Brief.&lt;/p&gt;
&lt;p&gt;Who am I&amp;nbsp;kidding?&lt;/p&gt;
&lt;div class="section" id="blockage"&gt;
&lt;h2&gt;Blockage&lt;/h2&gt;
&lt;p&gt;In the &lt;span class="caps"&gt;UNIX&lt;/span&gt; world a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Device_file"&gt;block device&lt;/a&gt; is a very simple concept: it&amp;#8217;s a storage
device which provides access to its contents as an ordered sequence of
identically sized blocks. That&amp;#8217;s it. No files, no directories, no permissions,
nothing more than a numbered sequence of blocks all with the same size.
Typically, the blocks are some whole multiple of an underlying device&amp;#8217;s sector
size, which over most of computing history has been 512 bytes. However, the
user may access block devices byte-by-byte if they&amp;nbsp;wish:&lt;/p&gt;
&lt;object data="https://waldorf.waveform.org.uk/images/block-ops.svg" type="image/svg+xml"&gt;&lt;/object&gt;
&lt;p&gt;For instance, a read of a few bytes (illustrated on the left in green) may read
a full 512-byte sector, throw away the bytes outside the requested range, and
return that which was requested &lt;a class="footnote-reference" href="#cache1" id="footnote-reference-1"&gt;[1]&lt;/a&gt;. A write of a few bytes (illustrated
in the middle in red) is a bit more complex; the kernel needs to read the full
sector from the underlying device, change those bytes we requested to write in
the middle, then write the whole sector back to the underlying device
&lt;a class="footnote-reference" href="#cache2" id="footnote-reference-2"&gt;[2]&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Crucially, block devices are &lt;em&gt;random&lt;/em&gt; access. We can access any block,
anywhere, at any time, without having to access all blocks prior to it. In
other words, block devices make storage look and &amp;#8220;feel&amp;#8221; like &lt;abbr title="Random Access Memory"&gt;&lt;span class="caps"&gt;RAM&lt;/span&gt;&lt;/abbr&gt;. This is as opposed to the &amp;#8220;byte-stream&amp;#8221; view of files which
owes more to the tape drives of old (&amp;#8220;seeking&amp;#8221; the head to a particular
location, then reading or writing from that&amp;nbsp;point).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="transform"&gt;
&lt;h2&gt;Transform!&lt;/h2&gt;
&lt;p&gt;The simplicity of block devices leads to them being wonderfully flexible things
that can often be converted into new block devices in useful ways. Probably the
most basic block device transformation that everyone is familiar with is
&amp;#8220;partitioning&amp;#8221;, which simply sub-divides one block device into multiple new
ones (minus some header space). This transformation effectively just re-numbers
the blocks but doesn&amp;#8217;t change their content or&amp;nbsp;ordering.&lt;/p&gt;
&lt;p&gt;More complex transformations are also&amp;nbsp;available.&lt;/p&gt;
&lt;p&gt;The &lt;abbr title="Logical Volume Manager"&gt;&lt;span class="caps"&gt;LVM&lt;/span&gt;&lt;/abbr&gt; sub-system (&lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/playing-with-blocks-lvm.html"&gt;explored previously&lt;/a&gt;) gathers multiple block devices into a &amp;#8220;volume group&amp;#8221;. The
volume group can then be used to produce other block devices called &amp;#8220;logical
volumes&amp;#8221;. These are similar to partitions, but have no need to be contiguous on
the underlying block devices, and can even span multiple underlying&amp;nbsp;devices.&lt;/p&gt;
&lt;object data="https://waldorf.waveform.org.uk/images/block-transforms.svg" type="image/svg+xml"&gt;A drive (sda) is divided into two partitions (sda1, sda2) the latter
of which is further divided into three block devices with &lt;span class="caps"&gt;LVM&lt;/span&gt; (root,
home, and swap). The home device has been expanded at some point in
its history as its storage is non-contiguous&lt;/object&gt;
&lt;p&gt;Speaking of multiple underlying devices, &lt;abbr title="Redundant Array of Inexpensive Disks"&gt;&lt;span class="caps"&gt;RAID&lt;/span&gt;&lt;/abbr&gt; systems (like &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Mdadm"&gt;mdadm&lt;/a&gt;) consume multiple block devices and
produce a single block device. The blocks in the produced device are duplicated
to the underlying block devices (in the simplest mirror case; more complex
transforms are involved in things like &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Standard_RAID_levels"&gt;&lt;span class="caps"&gt;RAID5&lt;/span&gt;&lt;/a&gt;). Corruption
or even loss of one of the underlying block devices, as in the failure of a
drive, can be entirely hidden from the consumer of the produced block&amp;nbsp;device.&lt;/p&gt;
&lt;object data="https://waldorf.waveform.org.uk/images/block-raid.svg" type="image/svg+xml"&gt;Four drives (sda, sdb, sdc, and sdd) are partitioned such that each
has a small initial partition (sda1 etc.) and a larger partition
(sda2 etc.); the small initial partitions are combined as a &lt;span class="caps"&gt;RAID1&lt;/span&gt;
mirror into a boot block device. The larger partitions are combined
as a &lt;span class="caps"&gt;RAID10&lt;/span&gt; device (a stripe of mirrors) as a root device.&lt;/object&gt;
&lt;p&gt;The &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Linux_Unified_Key_Setup"&gt;&lt;span class="caps"&gt;LUKS&lt;/span&gt;&lt;/a&gt; encryption mechanism (also explored in &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/playing-with-blocks-luks.html"&gt;previous posts&lt;/a&gt;) consumes one block device and produces a slightly smaller one
(minus the size of the &lt;span class="caps"&gt;LUKS&lt;/span&gt; header) that scrambles (and unscrambles) the
content of blocks as they are written (and&amp;nbsp;read).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-hierarchy"&gt;
&lt;h2&gt;The&amp;nbsp;Hierarchy&lt;/h2&gt;
&lt;p&gt;A &lt;a class="reference external" href="https://en.wikipedia.org/wiki/File_system"&gt;file-system&lt;/a&gt; is just another kind of transformation of a block device; one
which converts the flat sequence of blocks into a tree-like hierarchy of files
(optionally with attributes like ownership, groups, permissions, and all the
rest of that&amp;nbsp;gubbins).&lt;/p&gt;
&lt;p&gt;File systems can be relatively simple affairs, like the ancient but ubiquitous
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system"&gt;&lt;span class="caps"&gt;FAT&lt;/span&gt;&lt;/a&gt; which has no concept of file ownership, &lt;span class="caps"&gt;UNIX&lt;/span&gt; modes, and no symlinks.
Alternatively they can be complex, like the common &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Ext4"&gt;ext4&lt;/a&gt; file-system which
has journaling data recovery, the full suite of &lt;span class="caps"&gt;UNIX&lt;/span&gt; ownership, attributes, and
linkage, and numerous tunable&amp;nbsp;options.&lt;/p&gt;
&lt;p&gt;Ultimately, the file-systems produced from various block devices are all
grafted together (&amp;#8220;mounted&amp;#8221; in &lt;span class="caps"&gt;UNIX&lt;/span&gt; parlance) onto a &amp;#8220;virtual&amp;#8221; file-system
which is what all userland processes (and you, the user) generally consider
&amp;#8220;the file-system&amp;#8221;; the place you find all the files on your&amp;nbsp;computer.&lt;/p&gt;
&lt;object data="https://waldorf.waveform.org.uk/images/block-to-fs.svg" type="image/svg+xml"&gt;Two partitions (sda1 and sda2) are formatted as &lt;span class="caps"&gt;FAT&lt;/span&gt; and ext4
respectively. The vfat driver converts the sda1 block device into a
typical boot file-system for the Pi (containing bootcode.bin,
start4.elf, config.txt, etc). The ext4 driver converts the sda2 block
device into a typical Linux root file-system (containing bin, etc,
lib, usr, sbin directories). The virtual file-system then &amp;#8220;mounts&amp;#8221;
the ext4 hierarchy under / and the &lt;span class="caps"&gt;FAT&lt;/span&gt; hierarchy under /boot/firmware&lt;/object&gt;
&lt;p&gt;A common Linux storage layout is shown above. The drive (&lt;tt class="docutils literal"&gt;/dev/sda&lt;/tt&gt;) has two
partitions. The first partition (&lt;tt class="docutils literal"&gt;/dev/sda1&lt;/tt&gt;) is formatted as &lt;span class="caps"&gt;FAT&lt;/span&gt;, which the
kernel&amp;#8217;s vfat driver transforms into a file-system. The second partition
(&lt;tt class="docutils literal"&gt;/dev/sda2&lt;/tt&gt;) is formatted as ext4, which the ext4 driver transforms into
another file-system. The kernel has mounted the ext4 file-system as the root of
the &amp;#8220;virtual&amp;#8221; file-system, and the &lt;span class="caps"&gt;FAT&lt;/span&gt; file-system under the &lt;tt class="docutils literal"&gt;/boot/firmware&lt;/tt&gt;
mount-point.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="holy-cow"&gt;
&lt;h2&gt;Holy&amp;nbsp;CoW!&lt;/h2&gt;
&lt;p&gt;Some modern file-systems provide a copy-on-write facility (most notably
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Btrfs"&gt;btrfs&lt;/a&gt; and &lt;a class="reference external" href="https://en.wikipedia.org/wiki/ZFS"&gt;zfs&lt;/a&gt;). This allows rapid (near instantaneous) cloning of an
entire file-system hierarchy. The clone doesn&amp;#8217;t initially take any storage of
its own, and reads of the clone actually go to the original files. However,
writes to the clone allocate new storage for the changed&amp;nbsp;content.&lt;/p&gt;
&lt;p&gt;However, the facility is not limited to modern file-systems. &lt;span class="caps"&gt;LVM&lt;/span&gt; also provides
such a facility, initially intended to provide snapshots of file-systems at a
point in time (for backup, testing, or other&amp;nbsp;purposes).&lt;/p&gt;
&lt;object data="https://waldorf.waveform.org.uk/images/block-cow-snap.svg" type="image/svg+xml"&gt;The &amp;#8220;root&amp;#8221; block device contains a number of filled blocks in green,
while the &amp;#8220;snap&amp;#8221; block device has an equivalent number of &amp;#8220;empty&amp;#8221;
blocks. Two reads, indicated by arrows, are made against the &amp;#8220;snap&amp;#8221;
device but pass through to the underlying &amp;#8220;root&amp;#8221; device. Below this
an equivalent layout is show, but now one of the &amp;#8220;filled&amp;#8221; blocks is
&amp;#8220;red&amp;#8221; indicating a change in content. Above it, the equivalent block
in the &amp;#8220;snap&amp;#8221; device is green indicating it contains the original
content. A read against this block comes directly from the &amp;#8220;snap&amp;#8221;
device instead of passing through to the underlying &amp;#8220;root&amp;#8221; device.&lt;/object&gt;
&lt;p&gt;The illustration above shows a &amp;#8220;root&amp;#8221; block device, presumably containing a
root file-system. We create a snapshot of the &amp;#8220;root&amp;#8221; block device called
&amp;#8220;snap&amp;#8221;. Initially, the &amp;#8220;snap&amp;#8221; block device is empty, and reads to it pass
through to the underlying &amp;#8220;root&amp;#8221; block device. However, when a block is the
&amp;#8220;root&amp;#8221; device is written to, its original content is first copied to the &amp;#8220;snap&amp;#8221;
device. Subsequent reads of this block in the &amp;#8220;snap&amp;#8221; device will go to this
copied block instead. Further writes to the same block in the &amp;#8220;root&amp;#8221; device are
ignored because the &amp;#8220;snap&amp;#8221; device only cares about the content at the point in
time it was&amp;nbsp;created.&lt;/p&gt;
&lt;p&gt;Note that one of the beauties of this system is that &lt;em&gt;we don&amp;#8217;t care&lt;/em&gt; what
file-system is on the &amp;#8220;root&amp;#8221; device. It could be &lt;span class="caps"&gt;FAT&lt;/span&gt;, ext4, &lt;span class="caps"&gt;XFS&lt;/span&gt;, even
(redundantly) one of the more modern systems that supports this internally.
However, a limitation of this snapshot system is that we need to allocate space
for it up front &lt;a class="footnote-reference" href="#notall" id="footnote-reference-3"&gt;[4]&lt;/a&gt;. Can we come up with something in which the snapshot
allocates blocks&amp;nbsp;dynamically?&lt;/p&gt;
&lt;object data="https://waldorf.waveform.org.uk/images/block-cow-thin.svg" type="image/svg+xml"&gt;At the top, the &amp;#8220;image&amp;#8221; block device (read-only) contains a number
of filled blocks in green. Above it, a clone, &amp;#8220;clone1&amp;#8221;, exists with
the same number of blocks but all are empty. A read of two blocks in
&amp;#8220;clone1&amp;#8221;, indicated by arrows, passes through to the corresponding
blocks in &amp;#8220;image&amp;#8221;. Below, the one of the blocks in &amp;#8220;clone1&amp;#8221; is now
red indicating it has been changed from the original content in
&amp;#8220;image&amp;#8221;. Another arrow, indicating a read of that block now comes
straight from &amp;#8220;clone1&amp;#8221; and doesn&amp;#8217;t pass through. Another clone,
&amp;#8220;clone2&amp;#8221;, is below it with entirely empty blocks. A read of the same
block which is changed in &amp;#8220;clone1&amp;#8221;, passes through &amp;#8220;clone2&amp;#8221; to the
underlying &amp;#8220;image&amp;#8221; device.&lt;/object&gt;
&lt;p&gt;The illustration above shows &amp;#8220;thinly provisioned&amp;#8221; snapshots in &lt;span class="caps"&gt;LVM&lt;/span&gt;. This
operates a little like the regular snapshots in reverse. An underlying block
device called &amp;#8220;image&amp;#8221; presumably contains an &lt;span class="caps"&gt;OS&lt;/span&gt; image. We have created a
&amp;#8220;thinly provisioned&amp;#8221; snapshot of this called &amp;#8220;clone1&amp;#8221;. Reads of an unchanged
block pass through to the underlying (read-only) &amp;#8220;image&amp;#8221;. Writes to blocks in
&amp;#8220;clone1&amp;#8221; occur within its storage, with subsequent reads to that block no
longer passing through. Finally, we create another clone, &amp;#8220;clone2&amp;#8221;, which reads
the original block from the underlying&amp;nbsp;&amp;#8220;image&amp;#8221;.&lt;/p&gt;
&lt;p&gt;It&amp;#8217;s worth noting that most of the blocks in the clones are empty. Provided
they stay that way (i.e. the clones don&amp;#8217;t change too many blocks of the
underlying image), there&amp;#8217;s little sense in &lt;em&gt;actually&lt;/em&gt; allocating them. This is
the &amp;#8220;thin&amp;#8221; in &amp;#8220;thinly provisioned&amp;#8221;. The blocks of the clones are allocated on
demand so a clone initially takes up no space. This allocate-on-demand facility
can implement one form of &amp;#8220;over provisioning&amp;#8221; of storage. Each clone &lt;em&gt;thinks&lt;/em&gt;
it has all the storage allocated to the original image, but there&amp;#8217;s no need to
have all the storage available (unless the clones grow to require it,&amp;nbsp;naturally).&lt;/p&gt;
&lt;p&gt;Hopefully this gives some idea of how modern cloud systems can spin up clones
of an &lt;span class="caps"&gt;OS&lt;/span&gt; image nearly instantaneously, and how they can seemingly provide many
gigabytes of storage to containers or VMs, without necessarily having installed
all the storage they apparently&amp;nbsp;provide.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="layer-cake"&gt;
&lt;h2&gt;Layer&amp;nbsp;Cake&lt;/h2&gt;
&lt;p&gt;Finally, a brief illustration of just how silly this can get. Here&amp;#8217;s a
diagram of a storage layout I&amp;#8217;ve used on servers in the&amp;nbsp;past:&lt;/p&gt;
&lt;object data="https://waldorf.waveform.org.uk/images/block-stack.svg" type="image/svg+xml"&gt;A spiders web of storage. Four boxes represent four hard at the
bottom. Above this a larger box represents the &lt;span class="caps"&gt;RAID&lt;/span&gt; device built from
the drives. Above this a hexagonal volume group is built from the
large &lt;span class="caps"&gt;RAID&lt;/span&gt; device. From the volume group spring numerous block
devices, for &amp;#8220;tmp&amp;#8221;, &amp;#8220;root&amp;#8221;, &amp;#8220;home&amp;#8221;, and &amp;#8220;images&amp;#8221;. From the first
three spring regular file-systems, but from &amp;#8220;images&amp;#8221; spring several
more block devices named &amp;#8220;base&amp;#8221; and from that &amp;#8220;munin&amp;#8221;, &amp;#8220;docs&amp;#8221;, and
&amp;#8220;git&amp;#8221; (&lt;span class="caps"&gt;OS&lt;/span&gt; images running in containers).&lt;/object&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Four hard drives exist, each containing a small boot partition and a large
partition occupying the rest of the&amp;nbsp;capacity&lt;/li&gt;
&lt;li&gt;The four boot partitions are combined with a &lt;span class="caps"&gt;RAID1&lt;/span&gt; mirror into a boot block
device, &amp;#8220;md1&amp;#8221; &lt;a class="footnote-reference" href="#mirror" id="footnote-reference-4"&gt;[3]&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &amp;#8220;md1&amp;#8221; block device is formatted as &lt;span class="caps"&gt;FAT&lt;/span&gt; and contains the &amp;#8220;/boot&amp;#8221;&amp;nbsp;hierarchy.&lt;/li&gt;
&lt;li&gt;The four large partitions are combined with &lt;span class="caps"&gt;RAID5&lt;/span&gt; into a large block device,
&amp;#8220;md2&amp;#8221; (&lt;span class="caps"&gt;RAID5&lt;/span&gt; implies it has the capacity of n-1, i.e. three, of the
underlying devices, and can survive the loss of any one&amp;nbsp;device).&lt;/li&gt;
&lt;li&gt;The &amp;#8220;md2&amp;#8221; device is consumed by the &amp;#8220;raidvg&amp;#8221; volume group from which are
produced various block devices: the &amp;#8220;root&amp;#8221;, &amp;#8220;home&amp;#8221;, &amp;#8220;tmp&amp;#8221;, and &amp;#8220;images&amp;#8221;&amp;nbsp;volumes.&lt;/li&gt;
&lt;li&gt;&lt;span class="dquo"&gt;&amp;#8220;&lt;/span&gt;root&amp;#8221;, &amp;#8220;home&amp;#8221;, and &amp;#8220;tmp&amp;#8221; are formatted with a mix of file-systems, ext4 and
&lt;span class="caps"&gt;XFS&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;The &amp;#8220;images&amp;#8221; volume is a &amp;#8220;thin pool&amp;#8221; from which the &amp;#8220;base&amp;#8221; volume is&amp;nbsp;derived.&lt;/li&gt;
&lt;li&gt;The &amp;#8220;munin&amp;#8221;, &amp;#8220;docs&amp;#8221;, and &amp;#8220;git&amp;#8221; volumes are all thinly provisioned snapshots
of the &amp;#8220;base&amp;#8221;&amp;nbsp;volume.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This may seem like a complicated stack of transformations, but consider that in
essence most amount to little more than re-numbering and re-ordering of blocks
(the &lt;span class="caps"&gt;RAID5&lt;/span&gt; transform is a little more involved, admittedly). Hence, for very
little performance cost, we&amp;#8217;ve got redundancy (&lt;span class="caps"&gt;RAID&lt;/span&gt;), flexible volume creation
system with snapshotting (&lt;span class="caps"&gt;LVM&lt;/span&gt;), and copy-on-write cloning for VMs or containers
launched on the server (thin&amp;nbsp;provisioning).&lt;/p&gt;
&lt;p&gt;By this point you should have a reasonable understanding of what a block device
is, and what can be accomplished with the various transforms that are available
for them under Linux. Next time we&amp;#8217;ll take a look at the issues with netbooting
from &lt;span class="caps"&gt;NFS&lt;/span&gt; and how &lt;span class="caps"&gt;NBD&lt;/span&gt; (or block devices in general) can mitigate some of these&amp;nbsp;issues.&lt;/p&gt;
&lt;hr class="docutils" /&gt;
&lt;table class="docutils footnote" frame="void" id="cache1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;In practice it&amp;#8217;s unlikely the bytes will actually be thrown away;
the sector requested will be stored in a cache, in case a future read
requests more bytes from the same sector (very likely when reading through a
file sequentially).&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="cache2" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Again, the changed block will wind up in a cache so future
modifications can skip the initial read step.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="mirror" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-4"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Simple &lt;span class="caps"&gt;RAID1&lt;/span&gt; mirroring is used to ensure that the &lt;span class="caps"&gt;BIOS&lt;/span&gt; can use any
of the drives as its initial boot device without caring about the &lt;span class="caps"&gt;RAID&lt;/span&gt;
layout (which it doesn&amp;#8217;t understand). &lt;em&gt;Note&lt;/em&gt;: This carries risks as it means
the &lt;span class="caps"&gt;BIOS&lt;/span&gt; and bootloader must treat the boot partition as strictly read-only
(and that&amp;#8217;s not necessarily guaranteed).&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="notall" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label" /&gt;&lt;col /&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="#footnote-reference-3"&gt;[4]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;The snapshot doesn&amp;#8217;t require all the space to be allocated, as the
diagram suggests. In fact, assuming the underlying device changes
infrequently, it&amp;#8217;s common to only allocate ~5% of the origin&amp;#8217;s storage.
However, you do still need to provide &lt;em&gt;some&lt;/em&gt; up front.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="storage"></category><category term="lvm"></category><category term="luks"></category></entry><entry><title>Playing with Blocks - It’s All Connected</title><link href="https://waldorf.waveform.org.uk/2022/playing-with-blocks-its-all-connected.html" rel="alternate"></link><published>2022-09-08T00:00:00+01:00</published><updated>2023-08-01T17:02:11+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2022-09-08:/2022/playing-with-blocks-its-all-connected.html</id><summary type="html">&lt;p class="first last"&gt;Making a Pi image with encrypted volume-managed root&amp;nbsp;storage&lt;/p&gt;
</summary><content type="html">&lt;p&gt;In the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/playing-with-blocks-lvm.html"&gt;last&lt;/a&gt; &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/playing-with-blocks-luks.html"&gt;two&lt;/a&gt; articles we looked at &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Logical_Volume_Manager_(Linux)"&gt;&lt;span class="caps"&gt;LVM&lt;/span&gt;&lt;/a&gt; and
&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Linux_Unified_Key_Setup"&gt;&lt;span class="caps"&gt;LUKS&lt;/span&gt;&lt;/a&gt; for some enhancement of the basic storage of the standard Ubuntu Pi
images. This time around we&amp;#8217;ll combine both of those previous articles to make
an image in the same way that the Ubuntu Desktop for &lt;span class="caps"&gt;PC&lt;/span&gt; images install when
they&amp;#8217;re given free reign to overwrite the entire contents of the&amp;nbsp;drive.&lt;/p&gt;
&lt;p&gt;Specifically, this means we aim to wind up&amp;nbsp;with:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;A &lt;span class="caps"&gt;FAT&lt;/span&gt; boot partition (no difference there), 512 &lt;span class="caps"&gt;MB&lt;/span&gt; in&amp;nbsp;size&lt;/li&gt;
&lt;li&gt;A second partition taking up the rest of the card containing&lt;ul&gt;
&lt;li&gt;An encrypted &lt;span class="caps"&gt;LUKS&lt;/span&gt; container, containing&lt;ul&gt;
&lt;li&gt;An &lt;span class="caps"&gt;LVM&lt;/span&gt; physical volume (&lt;span class="caps"&gt;PV&lt;/span&gt;) belonging to&lt;ul&gt;
&lt;li&gt;An &lt;span class="caps"&gt;LVM&lt;/span&gt; volume group (&lt;span class="caps"&gt;VG&lt;/span&gt;) called &amp;#8220;pivg&amp;#8221; containing&lt;ul&gt;
&lt;li&gt;An &lt;span class="caps"&gt;LVM&lt;/span&gt; logical volume (&lt;span class="caps"&gt;LV&lt;/span&gt;) called &amp;#8220;root&amp;#8221; taking up some reasonable
portion of the disk space (8 &lt;span class="caps"&gt;GB&lt;/span&gt; in this&amp;nbsp;example)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;object data="https://waldorf.waveform.org.uk/images/luks-lvm-partitions.svg" type="image/svg+xml"&gt;A diagram illustrating the &lt;span class="caps"&gt;SD&lt;/span&gt; card layout showing: a large bar
representing the whole &lt;span class="caps"&gt;SD&lt;/span&gt; card /dev/mmcblk0 (N &lt;span class="caps"&gt;GB&lt;/span&gt;). Beneath
that, two bars: a small one on the left for the system-boot
partition /dev/mmcblk0p1 (512 &lt;span class="caps"&gt;MB&lt;/span&gt;), and a large one on the right
for the remaining partition /dev/mmcblk0p2 (N &lt;span class="caps"&gt;GB&lt;/span&gt; - 512 &lt;span class="caps"&gt;MB&lt;/span&gt;).
Beneath the large right-hand bar, a bar representing the &lt;span class="caps"&gt;LUKS&lt;/span&gt;
encrypted container (again, N &lt;span class="caps"&gt;GB&lt;/span&gt; - 512 &lt;span class="caps"&gt;MB&lt;/span&gt;). Beneath that, two
more bars: a tiny one on the left representing the &lt;span class="caps"&gt;LUKS&lt;/span&gt; header
(16 &lt;span class="caps"&gt;MB&lt;/span&gt;), and a much larger one on the right representing the
&lt;span class="caps"&gt;LVM&lt;/span&gt; &lt;span class="caps"&gt;PV&lt;/span&gt; /dev/mapper/rootfs within the &lt;span class="caps"&gt;LUKS&lt;/span&gt; container (N &lt;span class="caps"&gt;GB&lt;/span&gt; -
512 &lt;span class="caps"&gt;MB&lt;/span&gt; - 16 &lt;span class="caps"&gt;MB&lt;/span&gt;). Lower down, a dotted outline of the same size
representing the &lt;span class="caps"&gt;LVM&lt;/span&gt; &lt;span class="caps"&gt;VG&lt;/span&gt; /dev/pivg. Finally, beneath that, a
smaller bar representing the &lt;span class="caps"&gt;LVM&lt;/span&gt; &lt;span class="caps"&gt;LV&lt;/span&gt; /dev/mapper/pivg-root (8 &lt;span class="caps"&gt;GB&lt;/span&gt;).&lt;/object&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;One thing to note is that I&amp;#8217;ve only tested this with the Ubuntu 22.04
(jammy) server for pi image. Notably, jammy has &lt;tt class="docutils literal"&gt;cryptsetup&lt;/tt&gt; within the
default initramfs on the image. I would not necessarily be confident that
this works on earlier&amp;nbsp;releases.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="three-rowdy-layers"&gt;
&lt;h2&gt;Three (?) Rowdy&amp;nbsp;Layers&lt;/h2&gt;
&lt;p&gt;Rather than go through all the individual commands, which would result in this
post looking like a foreword followed by the dump of a shell script, I&amp;#8217;ll go
through the high level procedure because the detail is almost exactly the same
as the original articles but for a few minor differences and some deliberate&amp;nbsp;omissions.&lt;/p&gt;
&lt;p&gt;Firstly, we&amp;#8217;ll deal with the content of the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/playing-with-blocks-lvm.html"&gt;first article&lt;/a&gt; and set up &lt;span class="caps"&gt;LVM&lt;/span&gt;:&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;Follow the first article instructions until it&amp;#8217;s time to create the &lt;span class="caps"&gt;PV&lt;/span&gt; (with
&lt;tt class="docutils literal"&gt;pvcreate&lt;/tt&gt;):&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Plug in the &lt;span class="caps"&gt;SD&lt;/span&gt;&amp;nbsp;card&lt;/li&gt;
&lt;li&gt;Unmount any auto-mounted&amp;nbsp;partitions&lt;/li&gt;
&lt;li&gt;Repartition with &lt;tt class="docutils literal"&gt;gdisk&lt;/tt&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;At this point we need to create the &lt;span class="caps"&gt;PV&lt;/span&gt; with &lt;span class="caps"&gt;16MB&lt;/span&gt; &lt;em&gt;less&lt;/em&gt; space than is
available in the second partition, to leave a little room for the &lt;span class="caps"&gt;LUKS&lt;/span&gt;
header around the &lt;span class="caps"&gt;PV&lt;/span&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;From the &lt;tt class="docutils literal"&gt;gdisk&lt;/tt&gt; session, you should have a printout of the new partition
table; if you don&amp;#8217;t, run &lt;tt class="docutils literal"&gt;sudo gdisk &lt;span class="pre"&gt;-l&lt;/span&gt; /dev/mmcblk0&lt;/tt&gt; (assuming
&lt;tt class="docutils literal"&gt;/dev/mmcblk0&lt;/tt&gt; is your &lt;span class="caps"&gt;SD&lt;/span&gt;&amp;nbsp;card)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;For the second partition, calculate the number of sectors it occupies
(which is simply end sector minus start sector) and then subtract 32768
(each sector is 512 bytes, so 32768 is &lt;span class="caps"&gt;16MB&lt;/span&gt;). For example, on a &lt;span class="caps"&gt;32GB&lt;/span&gt; &lt;span class="caps"&gt;SD&lt;/span&gt;&amp;nbsp;card:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;gdisk&lt;span class="w"&gt; &lt;/span&gt;-l&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;GPT fdisk (gdisk) version 1.0.8

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.
Disk /dev/mmcblk0: 62333952 sectors, 29.7 GiB
Model: STORAGE DEVICE
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): A9BDFD97-F2A5-4419-9A83-B0160820FDBB
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 62333918
Partitions will be aligned on 2048-sector boundaries
Total free space is 4061 sectors (2.0 MiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1048576   511.0 MiB   0700  system-boot
   2         1050624        62333918   29.2 GiB    8E00  lvm
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nv"&gt;PV_SECTORS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;62333918&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1050624&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;32768&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$PV_SECTORS&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;61250526&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Proceed to creating the logical volume, but explicitly specify the number
of sectors to use when creating the &lt;span class="caps"&gt;PV&lt;/span&gt; with &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--setphysicalvolumesize&lt;/span&gt;&lt;/tt&gt;
and the number of sectors with the suffix&amp;nbsp;&amp;#8220;s&amp;#8221;:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;pvcreate&lt;span class="w"&gt; &lt;/span&gt;--setphysicalvolumesize&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PV_SECTORS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;s&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p2&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Physical volume &amp;quot;/dev/mmcblk0p2&amp;quot; successfully created.
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;vgcreate&lt;span class="w"&gt; &lt;/span&gt;pivg&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p2&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Volume group &amp;quot;pivg&amp;quot; successfully created
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;lvcreate&lt;span class="w"&gt; &lt;/span&gt;--size&lt;span class="w"&gt; &lt;/span&gt;8G&lt;span class="w"&gt; &lt;/span&gt;--name&lt;span class="w"&gt; &lt;/span&gt;root&lt;span class="w"&gt; &lt;/span&gt;pivg&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Logical volume &amp;quot;root&amp;quot; created.&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Finish the first article&amp;#8217;s&amp;nbsp;instructions:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Unpacking the&amp;nbsp;image&lt;/li&gt;
&lt;li&gt;Creating a loop device for the&amp;nbsp;image&lt;/li&gt;
&lt;li&gt;Transferring the content of the image partitions to the&amp;nbsp;card&lt;/li&gt;
&lt;li&gt;Updating the boot command line and fstab to point to the &amp;#8220;new&amp;#8221; root&amp;nbsp;device&lt;/li&gt;
&lt;li&gt;Unmount the root and boot partitions, and disable the &lt;span class="caps"&gt;VG&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Don&amp;#8217;t&lt;/em&gt; eject the &lt;span class="caps"&gt;SD&lt;/span&gt; card, we still need to do some&amp;nbsp;surgery!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="section" id="luks-dujour"&gt;
&lt;h2&gt;&lt;span class="caps"&gt;LUKS&lt;/span&gt;&amp;nbsp;Dujour&lt;/h2&gt;
&lt;p&gt;Now it&amp;#8217;s time to move on to the &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/playing-with-blocks-luks.html"&gt;second article&lt;/a&gt; and set up the encrypted &lt;span class="caps"&gt;LUKS&lt;/span&gt;
container around the physical volume (&lt;span class="caps"&gt;PV&lt;/span&gt;):&lt;/p&gt;
&lt;ol class="arabic"&gt;
&lt;li&gt;&lt;p class="first"&gt;Skip everything up to the encryption&amp;nbsp;step:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;No need to download another&amp;nbsp;image&lt;/li&gt;
&lt;li&gt;No need to boot the image&amp;nbsp;yet&lt;/li&gt;
&lt;li&gt;No need to unmount stuff (we already did&amp;nbsp;that)&lt;/li&gt;
&lt;li&gt;No need to shrink partitions (the root partition hasn&amp;#8217;t been auto-expanded
because we haven&amp;#8217;t booted anything, and we&amp;#8217;ve pre-shrunk the &lt;span class="caps"&gt;PV&lt;/span&gt; containing
the&amp;nbsp;root)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Now it&amp;#8217;s time to encrypt the second partition containing the &lt;span class="caps"&gt;PV&lt;/span&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;First the encryption; this is exactly as in the second&amp;nbsp;article:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;cryptsetup&lt;span class="w"&gt; &lt;/span&gt;reencrypt&lt;span class="w"&gt; &lt;/span&gt;--encrypt&lt;span class="w"&gt; &lt;/span&gt;--reduce-device-size&lt;span class="w"&gt; &lt;/span&gt;16M&lt;span class="w"&gt; &lt;/span&gt;--cipher&lt;span class="w"&gt; &lt;/span&gt;xchacha12,aes-adiantum-plain64&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p2&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;
WARNING!
========
This will overwrite data on LUKS2-temp-a823bd67-a632-41cb-955e-8776b8b3fb5b.new irrevocably.

Are you sure? (Type 'yes' in capital letters): YES
Enter passphrase for LUKS2-temp-a823bd67-a632-41cb-955e-8776b8b3fb5b.new:
Verify passphrase:
Finished, time 36:53.687, 29915 MiB written, speed  13.5 MiB/s&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Next we open the encrypted&amp;nbsp;container:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;cryptsetup&lt;span class="w"&gt; &lt;/span&gt;open&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p2&lt;span class="w"&gt; &lt;/span&gt;rootfs&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Enter passphrase for /dev/mmcblk0p2:&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Then we re-scan for physical volumes (PVs) and re-activate the pivg volume
group (&lt;span class="caps"&gt;VG&lt;/span&gt;) we created earlier; this isn&amp;#8217;t in the original article but is
necessary as we&amp;#8217;ve altered the mapper&amp;nbsp;configuration:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;pvscan&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;  PV /dev/mapper/rootfs   VG pivg            lvm2 [29.20 GiB / 21.20 GiB free]
  Total: 1 [29.20 GiB] / in use: 1 [29.20 GiB] / in no VG: 0 [0   ]
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;vgchange&lt;span class="w"&gt; &lt;/span&gt;-ay&lt;span class="w"&gt; &lt;/span&gt;pivg&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;  1 logical volume(s) in volume group &amp;quot;pivg&amp;quot; now active&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;We skip over all the resizing bits because we&amp;#8217;ve already established the
size of the root volume, and just mount the root file-system (we don&amp;#8217;t
need to mount the boot file-system). This is, again, slightly different to
the original article because the root file-system is a mapper device,
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;pivg-root&lt;/span&gt;&lt;/tt&gt; as in the first&amp;nbsp;article:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;/mnt/root&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;mount&lt;span class="w"&gt; &lt;/span&gt;/dev/mapper/pivg-root&lt;span class="w"&gt; &lt;/span&gt;/mnt/root
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Then a bit (but only a bit) of configuration&amp;nbsp;manipulation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Add our entry to &lt;tt class="docutils literal"&gt;/etc/crypttab&lt;/tt&gt;. Please heed the advice in the original
article regarding the source device here; this is the source device &lt;em&gt;as
seen by the Pi&lt;/em&gt;:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/mnt/root/etc/crypttab&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;&amp;lt;target&lt;span class="w"&gt; &lt;/span&gt;name&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;device&amp;gt;&lt;span class="w"&gt;         &lt;/span&gt;&amp;lt;key&lt;span class="w"&gt; &lt;/span&gt;file&amp;gt;&lt;span class="w"&gt;      &lt;/span&gt;&amp;lt;options&amp;gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;rootfs  /dev/mmcblk0p2  none  luks,discard&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/mnt/root/etc/crypttab&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/mnt/root/etc/crypttab&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;# &lt;/span&gt;&amp;lt;target&lt;span class="w"&gt; &lt;/span&gt;name&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="nb"&gt;source&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;device&amp;gt;&lt;span class="w"&gt;         &lt;/span&gt;&amp;lt;key&lt;span class="w"&gt; &lt;/span&gt;file&amp;gt;&lt;span class="w"&gt;      &lt;/span&gt;&amp;lt;options&amp;gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;rootfs  /dev/mmcblk0p2  none  luks,discard&lt;/span&gt;
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Skip the bits about editing &lt;tt class="docutils literal"&gt;fstab&lt;/tt&gt; and the boot command line; we&amp;#8217;ve
already done everything we need to for those. Just unmount root
file-system, and close the encrypted&amp;nbsp;container:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;umount&lt;span class="w"&gt; &lt;/span&gt;/mnt/root&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;cryptsetup&lt;span class="w"&gt; &lt;/span&gt;close&lt;span class="w"&gt; &lt;/span&gt;rootfs
&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Now just follow the second article to the&amp;nbsp;end:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Eject the card and boot it on your&amp;nbsp;Pi&lt;/li&gt;
&lt;li&gt;There&amp;#8217;s a &lt;em&gt;long&lt;/em&gt; delay while the initial root mount&amp;nbsp;fails&lt;/li&gt;
&lt;li&gt;In the emergency prompt that appears, open the encrypted device and exit
the&amp;nbsp;prompt&lt;/li&gt;
&lt;li&gt;Once booted, update the&amp;nbsp;initramfs&lt;/li&gt;
&lt;li&gt;Reboot to make sure everything&amp;nbsp;works!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div class="section" id="future-cases"&gt;
&lt;h2&gt;Future&amp;nbsp;Cases&lt;/h2&gt;
&lt;p&gt;Congratulations! At this point you should have a setup eerily similar to the
one I use on my main development Pi, albeit that one boots off a large &lt;span class="caps"&gt;SSD&lt;/span&gt;
instead of an &lt;span class="caps"&gt;SD&lt;/span&gt; card. Future things worth exploring with this&amp;nbsp;setup:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;How to get plymouth prompting nicely for the password; plymouth&amp;#8217;s always been
on the Pi server images but for some reason it doesn&amp;#8217;t work properly (unlike
the Pi desktop images where it&amp;#8217;s happy). I had a cursory look into this a
couple of cycles ago, but didn&amp;#8217;t get anywhere at that&amp;nbsp;time.&lt;/li&gt;
&lt;li&gt;How about ditching password encryption and using a key on removable storage.
For instance, if booting off &lt;span class="caps"&gt;SD&lt;/span&gt; card, how about using a &lt;span class="caps"&gt;USB&lt;/span&gt; key with the
encryption key. Or for my favoured setup where I boot off a &lt;span class="caps"&gt;USB3&lt;/span&gt; attached &lt;span class="caps"&gt;SSD&lt;/span&gt;
drive, how about using an &lt;span class="caps"&gt;SD&lt;/span&gt; card with the&amp;nbsp;key?&lt;/li&gt;
&lt;li&gt;With this configuration I usually set up &lt;span class="caps"&gt;LXD&lt;/span&gt; with an &lt;span class="caps"&gt;LVM&lt;/span&gt; thin-pool for its
default storage. This usually requires taking a crow-bar to &lt;span class="caps"&gt;LXD&lt;/span&gt; and forcing
it to use the thin-pool (with &lt;a class="reference external" href="https://linuxcontainers.org/lxd/docs/master/reference/storage_lvm/#storage-lvm-pool-config"&gt;lvm.vg.force_reuse&lt;/a&gt;; &lt;span class="caps"&gt;LXD&lt;/span&gt; doesn&amp;#8217;t like working
with a non-empty &lt;span class="caps"&gt;VG&lt;/span&gt; for reasons I&amp;#8217;ve never full understood, but at least it&amp;#8217;s
possible). Let me know in the comments if it would be useful to have another
article covering&amp;nbsp;this!&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="pi"></category><category term="ubuntu"></category><category term="storage"></category><category term="lvm"></category><category term="luks"></category></entry><entry><title>Playing with Blocks - LVM</title><link href="https://waldorf.waveform.org.uk/2021/playing-with-blocks-lvm.html" rel="alternate"></link><published>2021-10-18T00:00:00+01:00</published><updated>2023-08-01T17:02:11+01:00</updated><author><name>Dave Jones</name></author><id>tag:waldorf.waveform.org.uk,2021-10-18:/2021/playing-with-blocks-lvm.html</id><summary type="html">&lt;p class="first last"&gt;Making a Pi server with root under &lt;span class="caps"&gt;LVM&lt;/span&gt;&lt;/p&gt;
</summary><content type="html">&lt;p&gt;&lt;strong&gt;See Also&lt;/strong&gt;: &lt;a class="reference external" href="https://waldorf.waveform.org.uk/2021/playing-with-blocks-luks.html"&gt;&lt;span class="caps"&gt;LUKS&lt;/span&gt;&amp;nbsp;encryption&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is the first of a few posts I&amp;#8217;ll be doing on customizing the storage of
Ubuntu for Raspberry Pi&amp;nbsp;images.&lt;/p&gt;
&lt;p&gt;One of the first things I did when I moved to using a Pi as my primary
workstation was set up &lt;a class="reference external" href="https://linuxcontainers.org/lxd/introduction/"&gt;&lt;span class="caps"&gt;LXD&lt;/span&gt;&lt;/a&gt; to enable &amp;#8220;clean&amp;#8221; build environments to help with
debugging things. My favoured &lt;span class="caps"&gt;LXD&lt;/span&gt; storage backend is &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Logical_Volume_Manager_(Linux)"&gt;lvm&lt;/a&gt;&amp;nbsp;…&lt;/p&gt;
&lt;p&gt;(cue gasps of horror, followed by overlapping cries of &amp;#8220;but &lt;a class="reference external" href="https://en.wikipedia.org/wiki/ZFS"&gt;zfs&lt;/a&gt; rules!&amp;#8221; and
&amp;#8220;what about &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Btrfs"&gt;btrfs&lt;/a&gt;?&amp;#8221;, before the two sides descend into several parallel
flame wars over licensing and reliability and performance and … well, you get
the&amp;nbsp;point)&lt;/p&gt;
&lt;p&gt;Yes, I like lvm. Which means I&amp;#8217;d really like lvm on my Pi&amp;#8217;s &lt;span class="caps"&gt;SSD&lt;/span&gt;, preferably for
everything except the boot partition (which can&amp;#8217;t be lvm because the bootloader
doesn&amp;#8217;t handle esoterica like&amp;nbsp;that).&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m going to demonstrate the necessary steps with a fresh Ubuntu 21.10 (Impish
Indri) image on an &lt;span class="caps"&gt;SD&lt;/span&gt; card, but you can adapt the steps trivially to target an
&lt;span class="caps"&gt;SSD&lt;/span&gt; drive on &lt;span class="caps"&gt;USB&lt;/span&gt; (just substitute &lt;tt class="docutils literal"&gt;/dev/sdX&lt;/tt&gt; wherever you see
&lt;tt class="docutils literal"&gt;/dev/mmcblkX&lt;/tt&gt; below). With a teensy bit more effort, you could also operate
on an existing installation rather than a fresh image (but again, you&amp;#8217;ll need
separate target storage &amp;#8212; you can&amp;#8217;t trivially convert an existing installation
to lvm &lt;em&gt;on the same block device&lt;/em&gt;).&lt;/p&gt;
&lt;div class="section" id="tabula-rasa"&gt;
&lt;h2&gt;Tabula&amp;nbsp;Rasa&lt;/h2&gt;
&lt;p&gt;Start by grabbing your fresh &lt;span class="caps"&gt;SD&lt;/span&gt; card (or whatever storage you prefer). A word
of (frankly obvious) warning here: we&amp;#8217;re going to wipe everything on this
storage so make sure you&amp;#8217;re not going to mind losing anything on there&amp;nbsp;first.&lt;/p&gt;
&lt;p&gt;Instead of flashing an entire image to it, we&amp;#8217;re going to set it up with our
own partition table, create an &lt;span class="caps"&gt;LVM&lt;/span&gt; layout within that, then flash each
&lt;em&gt;partition&lt;/em&gt; of the image to it separately (all two of them, that&amp;nbsp;is).&lt;/p&gt;
&lt;p&gt;First things first: creating the new partition table. Plug in your storage, in
my case an &lt;span class="caps"&gt;SD&lt;/span&gt; card. If you&amp;#8217;re on an Ubuntu desktop (as I am) you&amp;#8217;ll need to
unmount any partitions that auto-mount from this storage before proceeding to
(re-)partition&amp;nbsp;it.&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;umount&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p1&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;umount&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p2&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;... repeat for any more auto-mounted partitions ...&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Now we&amp;#8217;re going to partition our storage with &lt;span class="caps"&gt;GPT&lt;/span&gt; (plus protective &lt;span class="caps"&gt;MBR&lt;/span&gt;), set
up the first partition as &lt;span class="caps"&gt;FAT&lt;/span&gt; (for the bootloader bits), and the second
partition as &lt;span class="caps"&gt;LVM&lt;/span&gt;. The commands we&amp;#8217;ll execute are as&amp;nbsp;follows:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;(&lt;strong&gt;p&lt;/strong&gt;)rint the current state of the partition table (not necessary, but just
to be sure we&amp;#8217;re looking at the &amp;#8220;right&amp;#8221;&amp;nbsp;disk)&lt;/li&gt;
&lt;li&gt;(&lt;strong&gt;o&lt;/strong&gt;)verwrite the partition table (to wipe it and start&amp;nbsp;again)&lt;/li&gt;
&lt;li&gt;(&lt;strong&gt;n&lt;/strong&gt;)ew partition, with the default number (1), default start sector
(2048), a size of &lt;span class="caps"&gt;512MB&lt;/span&gt; (actually larger than the source image which is
&lt;span class="caps"&gt;256MB&lt;/span&gt;, but that always feels a bit tight to me), and partition type &lt;tt class="docutils literal"&gt;0700&lt;/tt&gt;
(Microsoft basic&amp;nbsp;data)&lt;/li&gt;
&lt;li&gt;(&lt;strong&gt;n&lt;/strong&gt;)ew partition again, with the default number (2), default start sector
(1050624), default size (rest of the storage medium, however large that is),
and partition type of &lt;tt class="docutils literal"&gt;8e00&lt;/tt&gt; (Linux &lt;span class="caps"&gt;LVM&lt;/span&gt;)&lt;/li&gt;
&lt;li&gt;(&lt;strong&gt;c&lt;/strong&gt;)hange partition 1&amp;#8217;s name to&amp;nbsp;&amp;#8220;system-boot&amp;#8221;&lt;/li&gt;
&lt;li&gt;(&lt;strong&gt;c&lt;/strong&gt;)hange partition 2&amp;#8217;s name to&amp;nbsp;&amp;#8220;lvm&amp;#8221;&lt;/li&gt;
&lt;li&gt;(&lt;strong&gt;p&lt;/strong&gt;)rint the current state of the partition table again (not necessary,
but just to confirm things &amp;#8220;look&amp;nbsp;right&amp;#8221;)&lt;/li&gt;
&lt;li&gt;(&lt;strong&gt;w&lt;/strong&gt;)rite the new partition table and exit (confirming we want to&amp;nbsp;proceed)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;#8217;s a transcript of me doing this with a &lt;span class="caps"&gt;16GB&lt;/span&gt; &lt;span class="caps"&gt;SD&lt;/span&gt;&amp;nbsp;card:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;gdisk&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;GPT fdisk (gdisk) version 1.0.5

Partition table scan:
  MBR: MBR only
  BSD: not present
  APM: not present
  GPT: not present


***************************************************************
Found invalid GPT and valid MBR; converting MBR to GPT format
in memory. THIS OPERATION IS POTENTIALLY DESTRUCTIVE! Exit by
typing 'q' if you don't want to convert your MBR partitions
to GPT format!
***************************************************************


Command (? for help): p
Disk /dev/mmcblk0: 31116288 sectors, 14.8 GiB
Model: STORAGE DEVICE
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): 30D2AB17-E36C-4915-AF37-2C94E8FFE5C4
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 31116254
Partitions will be aligned on 2048-sector boundaries
Total free space is 2014 sectors (1007.0 KiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048          526335   256.0 MiB   0700  Microsoft basic data
   2          526336        31116254   14.6 GiB    8300  Linux filesystem

Command (? for help): o
This option deletes all partitions and creates a new protective MBR.
Proceed? (Y/N): y

Command (? for help): n
Partition number (1-128, default 1):
First sector (34-31116254, default = 2048) or {+-}size{KMGTP}:
Last sector (2048-31116254, default = 31116254) or {+-}size{KMGTP}: 512M
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): 0700
Changed type of partition to 'Microsoft basic data'

Command (? for help): n
Partition number (2-128, default 2):
First sector (34-31116254, default = 1050624) or {+-}size{KMGTP}:
Last sector (1050624-31116254, default = 31116254) or {+-}size{KMGTP}:
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): 8e00
Changed type of partition to 'Linux LVM'

Command (? for help): c
Partition number (1-2): 1
Enter name: system-boot

Command (? for help): c
Partition number (1-2): 2
Enter name: lvm

Command (? for help): p
Disk /dev/mmcblk0: 31116288 sectors, 14.8 GiB
Model: STORAGE DEVICE
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): C202E4F2-59CD-402D-9720-3C8D7D0E84AF
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 31116254
Partitions will be aligned on 2048-sector boundaries
Total free space is 4061 sectors (2.0 MiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048         1048576   511.0 MiB   0700  system-boot
   2         1050624        31116254   14.3 GiB    8E00  lvm

Command (? for help): w

Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!

Do you want to proceed? (Y/N): y
OK; writing new GUID partition table (GPT) to /dev/mmcblk0.
The operation has completed successfully.&lt;/span&gt;
&lt;/pre&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;You may note we&amp;#8217;re using &lt;span class="caps"&gt;GPT&lt;/span&gt; partitioning above. This is okay provided
you&amp;#8217;re using a reasonably modern version of the Pi&amp;#8217;s bootloader which has
supported &lt;span class="caps"&gt;GPT&lt;/span&gt; partitioning since mid-2020. In other words, this should work
with post-Focal images (including later Focal point-releases) but don&amp;#8217;t try
it with anything older than&amp;nbsp;that.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="more-logical-volume"&gt;
&lt;h2&gt;More (logical)&amp;nbsp;volume!&lt;/h2&gt;
&lt;p&gt;Now we set up the logical volume(s) on our &lt;span class="caps"&gt;GPT&lt;/span&gt; partitioned&amp;nbsp;card.&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;pvcreate&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p2&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Physical volume &amp;quot;/dev/mmcblk0p2&amp;quot; successfully created.
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;vgcreate&lt;span class="w"&gt; &lt;/span&gt;pivg&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p2&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Volume group &amp;quot;pivg&amp;quot; successfully created
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;lvcreate&lt;span class="w"&gt; &lt;/span&gt;--size&lt;span class="w"&gt; &lt;/span&gt;8G&lt;span class="w"&gt; &lt;/span&gt;--name&lt;span class="w"&gt; &lt;/span&gt;root&lt;span class="w"&gt; &lt;/span&gt;pivg&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;Logical volume &amp;quot;root&amp;quot; created.&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;You might want to make the root &lt;span class="caps"&gt;LV&lt;/span&gt; bigger than 8G, especially if you&amp;#8217;re using
the desktop image. I&amp;#8217;m specifically not using all the available storage (&lt;span class="caps"&gt;16GB&lt;/span&gt;
in this case) because I assume you want some spare for … whatever it is you
want &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Logical_Volume_Manager_(Linux)"&gt;lvm&lt;/a&gt; (in my case, a thin pool for &lt;span class="caps"&gt;LXD&lt;/span&gt;&amp;nbsp;containers).&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="going-loopy"&gt;
&lt;h2&gt;Going&amp;nbsp;loopy&lt;/h2&gt;
&lt;p&gt;Time to grab a source image. Below, I&amp;#8217;m getting a copy of the &lt;a class="reference external" href="https://ubuntu.com/download/raspberry-pi"&gt;Ubuntu 21.10&lt;/a&gt;
pre-installed server image for the Pi (substitute the desktop image here if you
like, there&amp;#8217;s no real difference in the procedure), and unpacking it with the
&lt;tt class="docutils literal"&gt;unxz&lt;/tt&gt; utility:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-y&lt;span class="w"&gt; &lt;/span&gt;xz-utils&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;... all the usual apt stuff ...
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;wget&lt;span class="w"&gt; &lt;/span&gt;http://cdimage.ubuntu.com/releases/impish/release/ubuntu-21.10-preinstalled-server-arm64+raspi.img.xz&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;unxz&lt;span class="w"&gt; &lt;/span&gt;ubuntu-21.10-preinstalled-server-arm64+raspi.img.xz
&lt;/pre&gt;
&lt;p&gt;We need to access the individual &lt;em&gt;partitions&lt;/em&gt; on the original image as we need
to copy its boot partition to our new boot partition, then its root partition
to our new root logical volume. To do this we set up a loop-device which treats
our unpacked image as a disk in its own right, and check that it finds the
right&amp;nbsp;partitions:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;losetup&lt;span class="w"&gt; &lt;/span&gt;--read-only&lt;span class="w"&gt; &lt;/span&gt;--find&lt;span class="w"&gt; &lt;/span&gt;--show&lt;span class="w"&gt; &lt;/span&gt;--partscan&lt;span class="w"&gt; &lt;/span&gt;ubuntu-21.10-preinstalled-server-arm64+raspi.img&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;/dev/loop34
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;-l&lt;span class="w"&gt; &lt;/span&gt;/dev/loop34*&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;brw-rw---- 1 root disk   7, 34 Oct 15 14:01 /dev/loop34
brw-rw---- 1 root disk 259,  6 Oct 15 14:01 /dev/loop34p1
brw-rw---- 1 root disk 259,  7 Oct 15 14:01 /dev/loop34p2&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;In the example above (your loop-device will likely have a different number),
the &amp;#8220;loop34p1&amp;#8221; and &amp;#8220;loop34p2&amp;#8221; block devices represent the first (boot)
partition of the image, and the second (root) partition. We can now use good
ol&amp;#8217; &lt;a class="reference external" href="https://manpages.ubuntu.com/manpages/jammy/en/man1/dd.1.html"&gt;disk-destroyer&lt;/a&gt; to perform the block transfers (as ever with &lt;tt class="docutils literal"&gt;dd&lt;/tt&gt;, be
&lt;em&gt;damned sure&lt;/em&gt; you get the &amp;#8220;of=&amp;#8221; device argument&amp;nbsp;correct):&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;dd&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/loop34p1&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/mmcblk0p1&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;16M&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;progress&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;16+0 records in
16+0 records out
268435456 bytes (268 MB, 256 MiB) copied, 15.8294 s, 17.0 MB/s
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;dd&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/loop34p2&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/mapper/pivg-root&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;16M&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;progress&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;2030043136 bytes (2.0 GB, 1.9 GiB) copied, 1 s, 2.0 GB/s
226+1 records in
226+1 records out
3798978560 bytes (3.8 GB, 3.5 GiB) copied, 246.436 s, 15.4 MB/s&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;At this point, we can clean up the loop-device as we don&amp;#8217;t need anything else
from the source&amp;nbsp;image.&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;losetup&lt;span class="w"&gt; &lt;/span&gt;-d&lt;span class="w"&gt; &lt;/span&gt;/dev/loop34
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="root-cause"&gt;
&lt;h2&gt;Root&amp;nbsp;Cause&lt;/h2&gt;
&lt;p&gt;Now mount the new boot partition so we can do a bit of surgery on where it
should look for the root device (in our new logical&amp;nbsp;volume):&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;/mnt/boot&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;mount&lt;span class="w"&gt; &lt;/span&gt;/dev/mmcblk0p1&lt;span class="w"&gt; &lt;/span&gt;/mnt/boot&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;ls&lt;span class="w"&gt; &lt;/span&gt;/mnt/boot&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;bcm2710-rpi-2-b.dtb       bootcode.bin  fixup_x.dat     start_db.elf
bcm2710-rpi-3-b.dtb       boot.scr      initrd.img      start.elf
bcm2710-rpi-3-b-plus.dtb  cmdline.txt   meta-data       start_x.elf
bcm2710-rpi-cm3.dtb       config.txt    network-config  uboot_rpi_3.bin
bcm2711-rpi-400.dtb       fixup4cd.dat  overlays        uboot_rpi_4.bin
bcm2711-rpi-4-b.dtb       fixup4.dat    README          uboot_rpi_arm64.bin
bcm2711-rpi-cm4.dtb       fixup4db.dat  start4cd.elf    user-data
bcm2837-rpi-3-a-plus.dtb  fixup4x.dat   start4db.elf    vmlinuz
bcm2837-rpi-3-b.dtb       fixup_cd.dat  start4.elf
bcm2837-rpi-3-b-plus.dtb  fixup.dat     start4x.elf
bcm2837-rpi-cm3-io3.dtb   fixup_db.dat  start_cd.elf
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/mnt/boot/cmdline.txt&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=LABEL=writable rootfstype=ext4 elevator=deadline rootwait fixrtc quiet splash
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;sed&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'s,root=[^ ]*,root=/dev/mapper/pivg-root,'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/mnt/boot/cmdline.txt&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/mnt/boot/cmdline.txt&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;dwc_otg.lpm_enable=0 console=serial0,115200 console=tty1 root=/dev/mapper/pivg-root rootfstype=ext4 elevator=deadline rootwait fixrtc quiet splash&lt;/span&gt;
&lt;/pre&gt;
&lt;div class="admonition note"&gt;
&lt;p class="first admonition-title"&gt;Note&lt;/p&gt;
&lt;p class="last"&gt;Feel free to use an editor here instead of messing around with &lt;tt class="docutils literal"&gt;sed&lt;/tt&gt;;
I&amp;#8217;ve illustrated the changes necessary by showing the before and after
states of the file&amp;nbsp;affected.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Some (very similar) surgery is also required on the &lt;tt class="docutils literal"&gt;/etc/fstab&lt;/tt&gt; file on our
new root&amp;nbsp;volume:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;/mnt/root&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;mount&lt;span class="w"&gt; &lt;/span&gt;/dev/mapper/pivg-root&lt;span class="w"&gt; &lt;/span&gt;/mnt/root&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/mnt/root/etc/fstab&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;LABEL=writable  /        ext4   discard,errors=remount-ro       0 1
LABEL=system-boot       /boot/firmware  vfat    defaults        0       1
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;sed&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'s,^LABEL=writable,/dev/mapper/pivg-root,'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/mnt/root/etc/fstab&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;/mnt/root/fstab&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;/dev/mapper/pivg-root  /        ext4   discard,errors=remount-ro       0 1
LABEL=system-boot       /boot/firmware  vfat    defaults        0       1&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Finally, clean up all our mounts, deactivate the volume group containing our
root volume, and remove all the temporary mount-points we&amp;nbsp;created:&lt;/p&gt;
&lt;pre class="code console literal-block"&gt;
&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;umount&lt;span class="w"&gt; &lt;/span&gt;/mnt/root&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;umount&lt;span class="w"&gt; &lt;/span&gt;/mnt/boot&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;vgchange&lt;span class="w"&gt; &lt;/span&gt;-an&lt;span class="w"&gt; &lt;/span&gt;pivg&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;rmdir&lt;span class="w"&gt; &lt;/span&gt;/mnt/root&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;$ &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;rmdir&lt;span class="w"&gt; &lt;/span&gt;/mnt/boot
&lt;/pre&gt;
&lt;p&gt;Now eject the &lt;span class="caps"&gt;SD&lt;/span&gt; card (or whatever storage you&amp;#8217;re using) and you should find it
boots happily on your Pi, with the root storage as &lt;span class="caps"&gt;LVM&lt;/span&gt;. Let me know below if
you run into any issues with&amp;nbsp;this!&lt;/p&gt;
&lt;/div&gt;
</content><category term="misc"></category><category term="pi"></category><category term="ubuntu"></category><category term="storage"></category><category term="lvm"></category></entry></feed>