Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions meta-avocado-nxp/stone/stone-imx95-frdm.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,6 @@
{
"name": "var",
"image": "var",
"size": 512,
"size_unit": "mebibytes",
"expand": "true"
}
]
Expand Down
49 changes: 46 additions & 3 deletions meta-avocado/recipes-core/cryptsetup-var/files/cryptsetup-var.sh
Original file line number Diff line number Diff line change
Expand Up @@ -217,13 +217,45 @@ maybe_resize() {
# Interrupted mid-way (power cut), LUKS2 records the reencryption in its
# metadata; open_var resumes it before opening. A filesystem whose size cannot
# be read gets the whole-partition reencryption instead - slower, still correct.
#
# A /var that has already been GROWN to fill the partition (a deployed device
# whose OTA turns encryption on) has no free tail for the header. btrfs can
# shrink online, so give the header its 32 MiB back first: mount, resize by
# the deficit, unmount, then reencrypt as usual. Anything else about the
# migration is the same; it just starts from a filesystem that no longer ends
# at the partition's last sector.
btrfs_total_bytes() {
btrfs inspect-internal dump-super "$VAR_DEV" 2>/dev/null | awk '/^total_bytes/{print $2}'
}

shrink_btrfs_for_header() {
shrink_mib="$1"
mnt=/run/cryptsetup-var-shrink
echo "cryptsetup-var: btrfs on $VAR_DEV fills the partition - shrinking it by ${shrink_mib} MiB to make room for the LUKS2 header"
mkdir -p "$mnt"
if ! mount -t btrfs "$VAR_DEV" "$mnt"; then
echo "cryptsetup-var: cannot mount $VAR_DEV to shrink it - cannot encrypt in place" >&2
exit 1
fi
if ! btrfs filesystem resize "-${shrink_mib}M" "$mnt"; then
umount "$mnt"
echo "cryptsetup-var: btrfs refused to shrink by ${shrink_mib} MiB (tail in use or too full) - cannot encrypt in place" >&2
exit 1
fi
umount "$mnt"
}

encrypt_in_place() {
fstype="$1"
part_bytes=$(blockdev --getsize64 "$VAR_DEV")
fs_bytes=""
if [ "$fstype" = "btrfs" ] && command -v btrfs >/dev/null 2>&1; then
fs_bytes=$(btrfs inspect-internal dump-super "$VAR_DEV" 2>/dev/null \
| awk '/^total_bytes/{print $2}')
fs_bytes=$(btrfs_total_bytes)
if [ -n "$fs_bytes" ] && [ "$fs_bytes" -gt 0 ] 2>/dev/null \
&& [ $(( part_bytes - fs_bytes )) -lt 33554432 ]; then
shrink_btrfs_for_header $(( (33554432 - (part_bytes - fs_bytes) + 1048575) / 1048576 ))
fs_bytes=$(btrfs_total_bytes)
fi
fi
size_opt=""
if [ -n "$fs_bytes" ] && [ "$fs_bytes" -gt 0 ] 2>/dev/null; then
Expand All @@ -237,15 +269,26 @@ encrypt_in_place() {
echo "cryptsetup-var: $fstype on $VAR_DEV leaves no 32 MiB for a LUKS header - cannot encrypt in place" >&2
exit 1
fi
# Tell whoever is watching the console what is about to happen: a seeded
# /var can be tens of GiB, so this step can run for many minutes with
# nothing else on screen. It is a one-time migration and resumable after a
# power cut (resume_reencrypt). Periodic progress needs cryptsetup >= 2.4.
if [ -n "$size_opt" ]; then work_mib=$want_mib; else work_mib=$(( part_bytes / 1048576 )); fi
progress_opt=""
if cryptsetup --help 2>&1 | grep -q -- --progress-frequency; then
progress_opt="--progress-frequency 30"
fi
echo "cryptsetup-var: first boot - encrypting existing $fstype on $VAR_DEV in place${size_opt:+ ($size_opt)}"
# shellcheck disable=SC2086 # size_opt is two words on purpose
echo "cryptsetup-var: ${work_mib} MiB to convert - one-time migration, resumes after a power cut${progress_opt:+, progress every 30 s}"
# shellcheck disable=SC2086 # size_opt / progress_opt are two words on purpose
cryptsetup reencrypt --encrypt \
--type luks2 \
--cipher aes-xts-plain64 \
--key-size 512 \
--hash sha256 \
--reduce-device-size 32M \
$size_opt \
$progress_opt \
--key-file "$KEY_FILE" \
--batch-mode \
"$VAR_DEV"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ cat > "$work/bin/cryptsetup" <<'S'
#!/bin/sh
echo "cryptsetup $*" >> "$LOG"
case "$1" in
--help) echo " --progress-frequency=secs" ;;
isLuks) [ -e "$STATE/luks" ] ;;
luksDump) cat "$STATE/dump" 2>/dev/null; exit 0 ;;
luksFormat) touch "$STATE/luks" ;;
Expand All @@ -46,8 +47,13 @@ S
cat > "$work/bin/btrfs" <<'S'
#!/bin/sh
echo "btrfs $*" >> "$LOG"
case "$1" in inspect-internal) printf 'total_bytes\t\t%s\n' "$(cat "$STATE/fsbytes")" ;; esac
case "$1" in
inspect-internal) printf 'total_bytes\t\t%s\n' "$(cat "$STATE/fsbytes")" ;;
filesystem) [ "$2" = resize ] && { n="${3#-}"; n="${n%M}"; echo $(( $(cat "$STATE/fsbytes") - n * 1048576 )) > "$STATE/fsbytes"; } ;;
esac
S
printf '#!/bin/sh\necho "mount $*" >> "$LOG"\n' > "$work/bin/mount"
printf '#!/bin/sh\necho "umount $*" >> "$LOG"\n' > "$work/bin/umount"
printf '#!/bin/sh\necho "0 4194304 crypt aes-xts-plain64 :64:logon:x 0 %s 32768 1 allow_discards"\n' "8:0" > "$work/bin/dmsetup"
printf '#!/bin/sh\nexit 0\n' > "$work/bin/modprobe"
printf '#!/bin/sh\nexit 0\n' > "$work/bin/systemd-cryptenroll"
Expand All @@ -66,8 +72,8 @@ run() { # run <state-dir>
# --- Case 1: flashed 128 MiB btrfs -> in-place reencrypt confined to fs+32M ---
s="$work/c1"; mkdir -p "$s"; echo btrfs > "$s/fstype"; echo 134217728 > "$s/fsbytes"
run "$s" || bad "case 1 script exit"
if grep -q "^cryptsetup reencrypt --encrypt --type luks2 --cipher aes-xts-plain64 --key-size 512 --hash sha256 --reduce-device-size 32M --device-size 160M --key-file .* --batch-mode $blk\$" "$s/log"; then
ok "plaintext btrfs is re-encrypted in place, confined to fs + 32 MiB (160M)"
if grep -q "^cryptsetup reencrypt --encrypt --type luks2 --cipher aes-xts-plain64 --key-size 512 --hash sha256 --reduce-device-size 32M --device-size 160M --progress-frequency 30 --key-file .* --batch-mode $blk\$" "$s/log"; then
ok "plaintext btrfs is re-encrypted in place, confined to fs + 32 MiB (160M), with progress"
else bad "unexpected reencrypt command: $(grep reencrypt "$s/log" || echo none)"; fi
grep -q "^cryptsetup luksFormat" "$s/log" && bad "luksFormat ran over a flashed filesystem" || ok "luksFormat never touches a flashed filesystem"
grep -q "^cryptsetup luksOpen --key-file" "$s/log" && ok "container opened with the recovery key afterwards" || bad "container not opened"
Expand All @@ -85,4 +91,22 @@ if grep -q "^cryptsetup reencrypt --resume-only" "$s/log" && [ "$(grep -n 'reenc
ok "interrupted reencryption is resumed before the container is opened"
else bad "resume not attempted first: $(grep -n 'reencrypt\|luksOpen' "$s/log")"; fi

# --- Case 4: btrfs grown to fill the 2 GiB partition (deployed device, OTA turns
# encryption on) -> shrink it by the 32 MiB header deficit first, then reencrypt ---
s="$work/c4"; mkdir -p "$s"; echo btrfs > "$s/fstype"; echo 2147483648 > "$s/fsbytes"
run "$s" || bad "case 4 script exit"
if grep -q "^btrfs filesystem resize -32M /run/cryptsetup-var-shrink$" "$s/log"; then
ok "grown btrfs is shrunk by exactly the 32 MiB header deficit"
else bad "no/unexpected shrink: $(grep 'btrfs filesystem' "$s/log" || echo none)"; fi
seq_ok=1
m=$(grep -n '^mount -t btrfs' "$s/log" | head -1 | cut -d: -f1); r=$(grep -n '^btrfs filesystem resize' "$s/log" | head -1 | cut -d: -f1)
u=$(grep -n '^umount' "$s/log" | head -1 | cut -d: -f1); e=$(grep -n '^cryptsetup reencrypt --encrypt' "$s/log" | head -1 | cut -d: -f1)
{ [ -n "$m" ] && [ -n "$r" ] && [ -n "$u" ] && [ -n "$e" ] && [ "$m" -lt "$r" ] && [ "$r" -lt "$u" ] && [ "$u" -lt "$e" ]; } || seq_ok=0
[ "$seq_ok" = 1 ] && ok "shrink is mount -> resize -> umount, all before the reencrypt" || bad "wrong shrink ordering: $(grep -n 'mount\|resize\|reencrypt --encrypt' "$s/log")"
grep -q "^cryptsetup reencrypt --encrypt .*--device-size 2048M " "$s/log" && ok "reencrypt then covers the shrunk fs + header (2048M = whole partition)" || bad "unexpected device-size after shrink: $(grep 'reencrypt --encrypt' "$s/log")"
grep -q "MiB to convert" "$s/out" && ok "operator is told how much is being converted" || bad "no size/progress line on the console"

# --- Case 5: flashed small btrfs (case 1 shape) must NOT be shrunk ---
grep -q "^btrfs filesystem resize" "$work/c1/log" && bad "small flashed btrfs was shrunk needlessly" || ok "a filesystem with free tail is left alone"

echo; echo "passed: $pass failed: $fail"; [ "$fail" -eq 0 ]
Loading