Skip to content

Commit f8388b7

Browse files
authored
Merge pull request #120 from learmj/ab_dev
AB: immutable root with per-slot /var, shared identity, logs, and home
2 parents b56ab99 + ec885ac commit f8388b7

File tree

17 files changed

+507
-50
lines changed

17 files changed

+507
-50
lines changed

bin/systemd-major

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
usage() {
5+
echo "Usage: $(basename "$0") <chroot-path>" >&2
6+
exit 2
7+
}
8+
9+
[ $# -ge 1 ] || usage
10+
root="$1"
11+
[ -d "$root" ] || { echo "error: chroot path not found: $root" >&2; exit 2; }
12+
13+
# Try to run systemd --version inside the chroot and extract the major number.
14+
# Requires privileges to chroot into <root>.
15+
ver="$(
16+
chroot "$root" /bin/sh -eu -c '
17+
PATH=/usr/sbin:/usr/bin:/sbin:/bin
18+
for b in /usr/lib/systemd/systemd /lib/systemd/systemd systemd; do
19+
if [ "$b" = systemd ]; then
20+
command -v systemd >/dev/null 2>&1 || continue
21+
cmd=systemd
22+
else
23+
[ -x "$b" ] || continue
24+
cmd="$b"
25+
fi
26+
"$cmd" --version 2>/dev/null | sed -n "s/^systemd \([0-9][0-9]*\).*/\1/p" | head -n1
27+
exit 0
28+
done
29+
exit 127
30+
' 2>/dev/null || true
31+
)"
32+
33+
[ -n "${ver:-}" ] || { echo "error: could not determine systemd version in $root" >&2; exit 1; }
34+
printf '%s\n' "$ver"

config/bookworm-minbase-ab.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ device:
33

44
image:
55
layer: image-rota
6-
boot_part_size: 200%
7-
system_part_size: 300%
6+
boot_part_size: 128M
7+
system_part_size: 512M
8+
data_part_size: 1G
89
name: deb12-arm64-min-ab
910

1011
layer:

config/trixie-minbase-ab.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ device:
33

44
image:
55
layer: image-rota
6-
boot_part_size: 200%
7-
system_part_size: 300%
6+
boot_part_size: 128M
7+
system_part_size: 512M
8+
data_part_size: 1G
89
name: deb13-arm64-min-ab
910

1011
layer:

docs/layer/image-rota.html

Lines changed: 172 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -123,17 +123,175 @@
123123
<div class="header">
124124
<h1>image-rota</h1>
125125
<span class="badge">image</span>
126-
<span class="badge">v3.1.0</span>
127-
<p>Rotational OTA and AB support with GPT.
128-
This layout supports redundancy of boot and system slot components and
129-
provides a seperate user data partition.</p>
126+
<span class="badge">v4.0.0</span>
127+
<p>Immutable GPT A/B layout for rotational OTA updates,
128+
boot/system redundancy, and a shared persistent data partition.</p>
130129
</div>
131130

132131

133132
<div class="section">
134133
<h2>Additional Documentation</h2>
135134
<div class="companion-content">
136135
<div class="sect1">
136+
<h2 id="_immutable_root_and_mount_points">Immutable root and mount points</h2>
137+
<div class="sectionbody">
138+
<div class="paragraph">
139+
<p>This system uses an A/B, read‑only root with all writable state on a single persistent partition.</p>
140+
</div>
141+
<table class="tableblock frame-all grid-all stripes-even stretch">
142+
<colgroup>
143+
<col style="width: 11.1111%;">
144+
<col style="width: 33.3333%;">
145+
<col style="width: 11.1111%;">
146+
<col style="width: 44.4445%;">
147+
</colgroup>
148+
<thead>
149+
<tr>
150+
<th class="tableblock halign-left valign-top">Mount point</th>
151+
<th class="tableblock halign-left valign-top">Backing</th>
152+
<th class="tableblock halign-left valign-top">Type</th>
153+
<th class="tableblock halign-left valign-top">Notes</th>
154+
</tr>
155+
</thead>
156+
<tbody>
157+
<tr>
158+
<td class="tableblock halign-left valign-top"><p class="tableblock">/</p></td>
159+
<td class="tableblock halign-left valign-top"><p class="tableblock">/dev/disk/by-slot/active/system</p></td>
160+
<td class="tableblock halign-left valign-top"><p class="tableblock">ext4</p></td>
161+
<td class="tableblock halign-left valign-top"><p class="tableblock">Read‑only system root (active slot A or B)</p></td>
162+
</tr>
163+
<tr>
164+
<td class="tableblock halign-left valign-top"><p class="tableblock">/boot/firmware</p></td>
165+
<td class="tableblock halign-left valign-top"><p class="tableblock">/dev/disk/by-slot/active/boot</p></td>
166+
<td class="tableblock halign-left valign-top"><p class="tableblock">vfat</p></td>
167+
<td class="tableblock halign-left valign-top"><p class="tableblock">Boot files (active slot A or B)</p></td>
168+
</tr>
169+
<tr>
170+
<td class="tableblock halign-left valign-top"><p class="tableblock">/bootfs</p></td>
171+
<td class="tableblock halign-left valign-top"><p class="tableblock">BOOTFS</p></td>
172+
<td class="tableblock halign-left valign-top"><p class="tableblock">vfat</p></td>
173+
<td class="tableblock halign-left valign-top"><p class="tableblock">Boot metadata</p></td>
174+
</tr>
175+
<tr>
176+
<td class="tableblock halign-left valign-top"><p class="tableblock">/persistent</p></td>
177+
<td class="tableblock halign-left valign-top"><p class="tableblock">PERSISTENT</p></td>
178+
<td class="tableblock halign-left valign-top"><p class="tableblock">ext4</p></td>
179+
<td class="tableblock halign-left valign-top"><p class="tableblock">Shared persistent storage</p></td>
180+
</tr>
181+
<tr>
182+
<td class="tableblock halign-left valign-top"><p class="tableblock">/var</p></td>
183+
<td class="tableblock halign-left valign-top"><p class="tableblock">/persistent/slots/&lt;slot&gt;/var</p></td>
184+
<td class="tableblock halign-left valign-top"><p class="tableblock">bind</p></td>
185+
<td class="tableblock halign-left valign-top"><p class="tableblock">Per‑slot runtime state (systemd, caches, etc.)</p></td>
186+
</tr>
187+
<tr>
188+
<td class="tableblock halign-left valign-top"><p class="tableblock">/home</p></td>
189+
<td class="tableblock halign-left valign-top"><p class="tableblock">/persistent/home</p></td>
190+
<td class="tableblock halign-left valign-top"><p class="tableblock">bind</p></td>
191+
<td class="tableblock halign-left valign-top"><p class="tableblock">User data shared across slots</p></td>
192+
</tr>
193+
<tr>
194+
<td class="tableblock halign-left valign-top"><p class="tableblock">/var/log/journal</p></td>
195+
<td class="tableblock halign-left valign-top"><p class="tableblock">/persistent/log/journal</p></td>
196+
<td class="tableblock halign-left valign-top"><p class="tableblock">bind</p></td>
197+
<td class="tableblock halign-left valign-top"><p class="tableblock">Single log directory used by both slots</p></td>
198+
</tr>
199+
</tbody>
200+
</table>
201+
<div class="ulist">
202+
<ul>
203+
<li>
204+
<p><strong>Rationale (immutable root + A/B)</strong></p>
205+
<div class="ulist">
206+
<ul>
207+
<li>
208+
<p>Supports delta/incremental OTA updates by treating root as a static image.</p>
209+
</li>
210+
<li>
211+
<p>Reliable rollbacks: slot can be flipped if a new root fails health checks.</p>
212+
</li>
213+
<li>
214+
<p>Reduced write amplification and storage wear, clearer separation of state.</p>
215+
</li>
216+
<li>
217+
<p>Predictable per‑slot state in <code>/var</code>.</p>
218+
</li>
219+
<li>
220+
<p>Shared <code>/home</code> for slot agnostic user storage.</p>
221+
</li>
222+
<li>
223+
<p>Shared journalling: centralised point for device logging.</p>
224+
</li>
225+
<li>
226+
<p>Preserves SBOM accuracy: executing software matches the manifest exactly.</p>
227+
</li>
228+
<li>
229+
<p>Blocks on-device package installs, preventing SBOM drift.</p>
230+
</li>
231+
<li>
232+
<p>Enables auditable, reproducible releases and stronger supply-chain assurances.</p>
233+
</li>
234+
</ul>
235+
</div>
236+
</li>
237+
<li>
238+
<p><strong>Logging</strong></p>
239+
<div class="ulist">
240+
<ul>
241+
<li>
242+
<p>A single persistent journal directory at <code>/persistent/log/journal</code> stores logs from either slot.</p>
243+
</li>
244+
<li>
245+
<p>Journaling is configured for endurance and reliability.</p>
246+
</li>
247+
</ul>
248+
</div>
249+
</li>
250+
<li>
251+
<p><strong>Machine Identity (systemd)</strong></p>
252+
<div class="ulist">
253+
<ul>
254+
<li>
255+
<p><code>/etc/machine-id</code> is synchronised early with <code>/persistent/common/etc/machine-id</code> using a oneshot unit.</p>
256+
</li>
257+
</ul>
258+
</div>
259+
</li>
260+
</ul>
261+
</div>
262+
<div class="admonitionblock warning">
263+
<table>
264+
<tr>
265+
<td class="icon">
266+
<div class="title">Warning</div>
267+
</td>
268+
<td class="content">
269+
<div class="paragraph">
270+
<p>Slot partition GPT labels are mandatory to associate the immutable root with its matching persistent storage.</p>
271+
</div>
272+
<div class="ulist">
273+
<ul>
274+
<li>
275+
<p>Root slots must have stable PARTLABELs: e.g. <code>system_a</code> and <code>system_b</code>.</p>
276+
</li>
277+
<li>
278+
<p>At boot, a generator reads the root slot’s <code>PARTLABEL</code> to select <code>/persistent/slots/&lt;slot&gt;/var</code>.</p>
279+
</li>
280+
</ul>
281+
</div>
282+
<div class="paragraph">
283+
<p>If slot GPT labels are missing/duplicated, <code>/var</code> binding will fail.</p>
284+
</div>
285+
<div class="paragraph">
286+
<p>If slot GPT labels can’t be guaranteed, this layout is not suitable for your device.</p>
287+
</div>
288+
</td>
289+
</tr>
290+
</table>
291+
</div>
292+
</div>
293+
</div>
294+
<div class="sect1">
137295
<h2 id="_slot_selection_run_time">Slot Selection (run-time)</h2>
138296
<div class="sectionbody">
139297
<div class="paragraph">
@@ -227,6 +385,7 @@ <h2>Relationships</h2>
227385
<a href="image-base.html" class="dep-badge">image-base</a>
228386
<a href="device-base.html" class="dep-badge">device-base</a>
229387
<a href="rpi-ab-slot-mapper.html" class="dep-badge">rpi-ab-slot-mapper</a>
388+
<a href="systemd-min.html" class="dep-badge">systemd-min</a>
230389
</div>
231390

232391

@@ -263,11 +422,11 @@ <h2>Configuration Variables</h2>
263422

264423
<tr>
265424
<td><code>IGconf_image_boot_part_size</code></td>
266-
<td>Boot partition size per slot.</td>
425+
<td>Boot partition size per-slot.</td>
267426
<td>
268427

269428

270-
<code>100%</code>
429+
<code>96M</code>
271430

272431

273432
</td>
@@ -279,11 +438,11 @@ <h2>Configuration Variables</h2>
279438

280439
<tr>
281440
<td><code>IGconf_image_system_part_size</code></td>
282-
<td>System partition size per slot.</td>
441+
<td>System partition size per-slot.</td>
283442
<td>
284443

285444

286-
<code>100%</code>
445+
<code>512M</code>
287446

288447

289448
</td>
@@ -295,11 +454,12 @@ <h2>Configuration Variables</h2>
295454

296455
<tr>
297456
<td><code>IGconf_image_data_part_size</code></td>
298-
<td>Data partition retained across rotations.</td>
457+
<td>Writable storage partition retained across
458+
slot rotations.</td>
299459
<td>
300460

301461

302-
<code>256M</code>
462+
<code>1G</code>
303463

304464

305465
</td>
@@ -329,7 +489,8 @@ <h2>Configuration Variables</h2>
329489
<td><code>IGconf_image_pmap</code></td>
330490
<td>Provisioning Map type for this image layout.
331491
All partitions will be provisioned unencrypted (clear).
332-
System partitions will be provisioned encrypted (crypt).</td>
492+
System partitions will be provisioned encrypted (crypt).
493+
System B will be provisioned encrypted (hybrid). Development only.</td>
333494
<td>
334495

335496

docs/layer/index.html

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -843,6 +843,10 @@ <h3>General</h3>
843843
<a href="rpi-misc-utils.html">rpi-misc-utils</a><span class="layer-desc">- Raspberry Pi system utilities</span>
844844
</div>
845845

846+
<div class="layer-item">
847+
<a href="rpi-splash-screen.html">rpi-splash-screen</a><span class="layer-desc">- Raspberry Pi fullscreen splash screen support with custom image configuration.</span>
848+
</div>
849+
846850
<div class="layer-item">
847851
<a href="rpi-user-credentials.html">rpi-user-credentials</a><span class="layer-desc">- Raspberry Pi base layer for local user admin.
848852
Creates a local account for user IGconf_device_user1, a home directory...</span>
@@ -867,9 +871,8 @@ <h3>Image</h3>
867871
</div>
868872

869873
<div class="layer-item">
870-
<a href="image-rota.html">image-rota</a><span class="layer-desc">- Rotational OTA and AB support with GPT.
871-
This layout supports redundancy of boot and system slot components and
872-
prov...</span>
874+
<a href="image-rota.html">image-rota</a><span class="layer-desc">- Immutable GPT A/B layout for rotational OTA updates,
875+
boot/system redundancy, and a shared persistent data partition.</span>
873876
</div>
874877

875878
<div class="layer-item">

docs/layer/systemd-min.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ <h2>Relationships</h2>
138138
<p><strong>Required by:</strong></p>
139139
<div class="deps">
140140

141+
<a href="image-rota.html" class="dep-badge">image-rota</a>
142+
141143
<a href="rpi-connect.html" class="dep-badge">rpi-connect</a>
142144

143145
<a href="rpi-connect-lite.html" class="dep-badge">rpi-connect-lite</a>

image/gpt/ab_userdata/device/provisionmap-clear.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575
{
7676
"partitions": [
7777
{
78-
"image": "data"
78+
"image": "persistent"
7979
}
8080
]
8181
}

image/gpt/ab_userdata/device/provisionmap-crypt.json

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,12 @@
7676
}
7777
]
7878
}
79-
}
79+
},
80+
"partitions": [
81+
{
82+
"image": "persistent"
83+
}
84+
]
8085
}
81-
},
82-
{
83-
"partitions": [
84-
{
85-
"image": "data"
86-
}
87-
]
8886
}
8987
]

image/gpt/ab_userdata/device/provisionmap-hybrid.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@
8686
{
8787
"partitions": [
8888
{
89-
"image": "data"
89+
"image": "persistent"
9090
}
9191
]
9292
}

image/gpt/ab_userdata/device/rootfs-overlay/data/.empty

Whitespace-only changes.

0 commit comments

Comments
 (0)