Skip to content

Commit bc483d8

Browse files
doug445claude
andcommitted
--dry-run never writes to the target: no resume, no tune hand-off, no unmount or close, no journal replay, and a cleanup that undoes only its own
luks-deploy.sh's resume path had no DRY_RUN check: a dry run answered "yes", or given LUKS_RESUME=yes, ran cryptsetup reencrypt --resume-only (and cryptsetup repair after a hard interruption) on the target. The script's own header promised that nothing on the target is modified in a dry run. The whole script was audited for anything that can write to the target, or change what a dry run leaves behind, before the point of no return. Fixed - each prints "[dry-run] Would ..." and stops, as convert_luks1 does: - resume: stops before the prompt, whatever LUKS_RESUME says, and before any passphrase is asked for; - the hand-off to luks-tune.sh: stops, and names luks-tune.sh --dry-run; - a mapper an earlier run left open: kept, it is read as it stands and /mnt is not unmounted; closing it stops the dry run; - a kept mapper backed by another device: stops instead of closing it; - a mount an earlier run left at /mnt_temp: stops instead of mounting over it. Two more that wrote without anyone asking: - the exit trap unmounted everything under /mnt, deleted /mnt/tmp/.luks-* and closed the mapper on every exit, dry run included, whoever had mounted or opened them - and unmounting a read-write filesystem writes to it. A dry run now undoes only its own /mnt_temp mounts, its probe mounts and a mapper it opened itself. A real run's cleanup is unchanged. - "ro" is not "no writes": a dirty ext3/4, xfs, btrfs or f2fs replays its journal even on a read-only mount. Discovery's mounts of the root and of /boot, and lib-uki.sh's uki_probe_dev, now go through ll_ro_mount, which in a dry run adds noload (ext3/4), norecovery (xfs, f2fs) or rescue=nologreplay, falling back to nologreplay (btrfs). A real run mounts with plain "ro" as before. Checked and already safe: ensure_unmounted (reports, unmounts nothing), convert_luks1 (exits before its header backup and convert; its read-only open is skipped; the GRUB probe mounts FAT read-only), the ALIGN partition move (after the confirm gate; the dry run only plans it), the integrity check (skipped), the state backup (to the deployment drive), every luksHeaderBackup (after the dry-run exits), the partition check's probes (FAT ro, ext3/4 ro,noload), and everything after the point of no return. A config-only dry run still opens the container to read it, as documented. Tests. loopback-core-test.sh section 12 (root, CI): the real luks-deploy.sh --dry-run against a LUKS2 volume mid-encryption with LUKS_RESUME=yes and a passphrase file - not resumed, every byte of the device unchanged, the online-reencrypt flag still there; against a finished LUKS2 with LUKS_EXISTING=tune; and three times with a mapper left open and its filesystem mounted at /mnt (kept with another target, closed, kept through config-only), each leaving both as they were. Section 11's runs now use their own mapper name, so a host whose root mapper is root_crypt cannot interfere. uki-fixture-test.sh section 11: ll_ro_mount's options for every filesystem in both modes, the btrfs fallback, the discovery mounts going through it, and the cleanup trap with its commands recorded - dry run with and without its own mounts, and a real run unchanged. Against 1.6.5 the new uki checks report 7 failures. The script's --dry-run text, the README, ABOUT, INSTALL and SECURITY now say exactly this. CI: actions/checkout@v7, as linux-backup-system uses; the runners stay pinned, so no required check changes its name. uki 117/0 (1 skip) on the M2 Max and again with every Asahi signal hidden in a user namespace; cmdline 71/0; bash -n and shellcheck -S warning clean. loopback needs root and runs in CI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 47f57f6 commit bc483d8

9 files changed

Lines changed: 368 additions & 39 deletions

File tree

‎.github/workflows/lint.yml‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ permissions:
1010
# 2026-10-19, which would change shellcheck and the test environment under
1111
# the x86 legs while the arm legs stayed on 24.04. A gate has to mean the same
1212
# thing on every run, and the two legs of a matrix must differ only by
13-
# architecture. actions/checkout@v5 runs on Node 24.
13+
# architecture. actions/checkout@v7 runs on Node 24.
1414

1515
jobs:
1616
shellcheck:
1717
runs-on: ubuntu-24.04
1818
steps:
19-
- uses: actions/checkout@v5
19+
- uses: actions/checkout@v7
2020

2121
- name: bash syntax check (bash -n)
2222
run: |
@@ -45,7 +45,7 @@ jobs:
4545
runner: [ubuntu-24.04, ubuntu-24.04-arm]
4646
runs-on: ${{ matrix.runner }}
4747
steps:
48-
- uses: actions/checkout@v5
48+
- uses: actions/checkout@v7
4949
- name: install cryptsetup + btrfs-progs + gdisk + dosfstools
5050
run: |
5151
sudo apt-get update -q
@@ -62,7 +62,7 @@ jobs:
6262
runner: [ubuntu-24.04, ubuntu-24.04-arm]
6363
runs-on: ${{ matrix.runner }}
6464
steps:
65-
- uses: actions/checkout@v5
65+
- uses: actions/checkout@v7
6666
- name: install binutils (objcopy, for .cmdline extraction)
6767
run: sudo apt-get update -q && sudo apt-get install -y -q binutils
6868
- name: UKI fixture test
@@ -78,6 +78,6 @@ jobs:
7878
runner: [ubuntu-24.04, ubuntu-24.04-arm]
7979
runs-on: ${{ matrix.runner }}
8080
steps:
81-
- uses: actions/checkout@v5
81+
- uses: actions/checkout@v7
8282
- name: command-line carrier fixture test
8383
run: bash tests/cmdline-fixture-test.sh

‎README.md‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,12 @@ boot machinery, and shows you what it found. You confirm the selection, type
8989
`ENCRYPT`, and choose a passphrase. Everything after that is automated,
9090
including recovery if a step fails partway.
9191

92-
`--dry-run` runs the entire read-only half — detection, cross-checks, KDF
93-
benchmark, the exact `cryptsetup` command it would issue — and exits before
94-
the point of no return.
92+
`--dry-run` runs the read-only half — detection, cross-checks, KDF benchmark,
93+
the exact `cryptsetup` command it would issue — and exits before the point of
94+
no return, writing nothing to the target. A step that would change something
95+
sooner (finishing an interrupted encryption, converting a LUKS1 header,
96+
handing over to `luks-tune.sh`) is not taken: the dry run says what it would
97+
do and stops there.
9598

9699
---
97100

@@ -729,8 +732,8 @@ you, and the part that differs on every distro:
729732
| Undo / recovery | Whatever you thought to save | Header backups plus a labeled bundle of every changed file, with distro-specific repair commands |
730733

731734
If you want to understand what it changes before trusting it, `--dry-run`
732-
prints every action, including the exact `cryptsetup` invocation, and changes
733-
nothing.
735+
prints what it would do, including the exact `cryptsetup` invocation, and
736+
changes nothing.
734737

735738
---
736739

‎SECURITY.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,8 @@ interesting surface:
162162
redaction failing to apply when it was not disabled with `--no-redact`.
163163
* **A read-only tool that writes.** `luks-tune.sh` must never create or destroy
164164
a keyslot, change a passphrase, or touch data; `--dry-run` anywhere must reach
165-
no point of no return; `extras/luks-fetch-cache` must read public header
165+
no point of no return and write nothing to the target;
166+
`extras/luks-fetch-cache` must read public header
166167
metadata and nothing else, and its cache at `/var/cache/luks-fetch.txt` is
167168
world-readable by design, so anything resembling key material appearing in it
168169
is a finding; `bin/linuxlocker-diag.sh` must mount nothing read-write and

‎bin/lib-uki.sh‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,14 @@ uki_scan_dir() {
109109
# uki_probe_dev <device> <label> — mount a block device read-only and scan it.
110110
# Used during discovery, when /boot and the ESP are still just partitions.
111111
uki_probe_dev() {
112-
local dev="$1" label="$2" probe
112+
local dev="$1" label="$2" probe mnt=(mount -o ro)
113113
[ -n "$dev" ] && [ -b "$dev" ] || return 0
114+
# The caller's read-only mount is used when it has one: luks-deploy.sh's
115+
# ll_ro_mount also replays no journal during a dry run, where a plain "ro"
116+
# mount of a dirty /boot would write the replay to the target.
117+
declare -F ll_ro_mount >/dev/null 2>&1 && mnt=(ll_ro_mount)
114118
probe=$(mktemp -d /tmp/luks-uki-probe.XXXXXX) || return 0
115-
if mount -o ro "$dev" "$probe" 2>/dev/null; then
119+
if "${mnt[@]}" "$dev" "$probe" 2>/dev/null; then
116120
uki_scan_dir "$probe" "$label"
117121
umount "$probe" 2>/dev/null || true
118122
fi

‎bin/luks-deploy.sh‎

Lines changed: 139 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,14 @@ esac
8686
# --dry-run (or LUKS_DRY_RUN=1): run every read-only phase — detection, menus,
8787
# fstab cross-checks, KDF benchmark, state backup to the deployment drive —
8888
# print the full plan, and exit BEFORE the point of no return. Nothing on the
89-
# TARGET is modified (config-only dry-run still asks for the passphrase to
90-
# unlock the container for discovery; all discovery mounts are read-only).
89+
# TARGET is modified. Discovery mounts are read-only and replay no journal
90+
# (ll_ro_mount); a config-only dry run still asks for the passphrase and
91+
# opens the container to read it. A step that would change something before
92+
# the plan is complete — finishing an interrupted encryption, converting a
93+
# LUKS1 header, handing over to luks-tune.sh, closing a mapper or unmounting
94+
# what an earlier run left mounted — is not taken: the dry run prints what it
95+
# would do and stops there, whatever LUKS_RESUME, LUKS_EXISTING or
96+
# LUKS_STALE_MAPPER say. At exit it undoes only its own mounts and mapper.
9197
DRY_RUN="${LUKS_DRY_RUN:-0}"
9298
for arg in "$@"; do
9399
case "$arg" in
@@ -361,6 +367,31 @@ harden_path() { # harden_path <octal-mode> <path>
361367
return 0
362368
}
363369

370+
# ─── Read-only mounts that replay no journal in a dry run ────────────────────
371+
# "ro" alone does not mean "no writes": a dirty ext3/ext4, xfs, btrfs or f2fs
372+
# replays its journal even on a read-only mount, and the replay is written to
373+
# the device. A dry run writes nothing to the target, so its read-only mounts
374+
# also skip the replay — noload (ext3/ext4), norecovery (xfs, f2fs),
375+
# rescue=nologreplay or the older nologreplay (btrfs) — and read the
376+
# filesystem as it stands on disk. A real run mounts with plain "ro", as it
377+
# always has: it is about to write to the target anyway. lib-uki.sh's
378+
# uki_probe_dev mounts through this too when it is defined.
379+
# --- ll_ro_mount ---
380+
ll_ro_mount() { # ll_ro_mount <dev> <dir> [extra mount options]
381+
local dev="$1" dir="$2" extra="${3:+,$3}" nr=""
382+
if [ "$DRY_RUN" = "1" ]; then
383+
case "$(blkid -s TYPE -o value "$dev" 2>/dev/null)" in
384+
ext3|ext4) nr=",noload" ;;
385+
xfs|f2fs) nr=",norecovery" ;;
386+
btrfs)
387+
mount -o "ro,rescue=nologreplay$extra" "$dev" "$dir" 2>/dev/null && return 0
388+
nr=",nologreplay" ;;
389+
esac
390+
fi
391+
mount -o "ro$nr$extra" "$dev" "$dir"
392+
}
393+
# --- end ll_ro_mount ---
394+
364395
# ─── LUKS2 KDF profiles ──────────────────────────────────────────────────────
365396
# Three presets, chosen interactively before encryption starts. The KDF is
366397
# re-run in the INITRAMFS at every boot, so its memory cost must be allocatable
@@ -439,7 +470,10 @@ harden_path() { # harden_path <octal-mode> <path>
439470
# See docs/BOOTLOADERS.md for all of the above.
440471
# LUKS_DRY_RUN=1 (or --dry-run flag) preview: full detection + plan, no
441472
# changes to the target, exit before
442-
# the point of no return
473+
# the point of no return — or earlier,
474+
# saying what it would do, at a step
475+
# that would change something (see
476+
# "CLI flags" above)
443477
# LUKS_GRUB_KDF_FACTOR=<x> unlock-time multiplier shown for a volume that
444478
# GRUB itself unlocks (encrypted /boot). Default
445479
# 8.5, measured on one machine; see lib-boot.sh
@@ -860,6 +894,14 @@ fs_grow_max() {
860894
}
861895

862896
# ─── Cleanup Trap ────────────────────────────────────────────────────────────
897+
# A dry run undoes only what it did itself. It never mounts anything under
898+
# /mnt and never writes there, so whatever is mounted under /mnt belongs to an
899+
# earlier run or to you — and unmounting a read-write filesystem writes to it.
900+
# It closes ${LUKS_NAME} only when it opened it, and unmounts /mnt_temp only
901+
# when it mounted it there. A real run cleans up as it always has.
902+
MNT_TEMP_MOUNTED_HERE=0 # 1 once this run has mounted something at /mnt_temp
903+
MAPPER_OPENED_HERE=0 # 1 once this run has opened /dev/mapper/${LUKS_NAME}
904+
# --- cleanup ---
863905
cleanup() {
864906
local exit_code=$?
865907
if [ $exit_code -ne 0 ]; then
@@ -868,6 +910,29 @@ cleanup() {
868910
err " Script exited with error (code $exit_code). Cleaning up..."
869911
err "════════════════════════════════════════════════════════════"
870912
fi
913+
if [ "$DRY_RUN" = "1" ]; then
914+
if [ "$MNT_TEMP_MOUNTED_HERE" = "1" ]; then
915+
umount -R /mnt_temp 2>/dev/null || umount /mnt_temp 2>/dev/null || true
916+
rmdir /mnt_temp 2>/dev/null || true
917+
fi
918+
for uki_probe_dir in /tmp/luks-uki-probe.* /tmp/luks-grub-probe.* /tmp/luks-asahi-probe.*; do
919+
[ -d "$uki_probe_dir" ] || continue
920+
umount "$uki_probe_dir" 2>/dev/null || true
921+
rmdir "$uki_probe_dir" 2>/dev/null || true
922+
done
923+
cryptsetup close "luks-probe-$$" 2>/dev/null || true
924+
if [ "$MAPPER_OPENED_HERE" = "1" ]; then
925+
cryptsetup close "${LUKS_NAME}" 2>/dev/null || true
926+
fi
927+
if [ $exit_code -ne 0 ]; then
928+
echo ""
929+
echo " [dry-run] Nothing on the target was changed."
930+
echo " Full log: $DEPLOY_LOG"
931+
fi
932+
sync
933+
sleep 1
934+
return
935+
fi
871936
# Clean up any temp mounts first (-R: the BLS check nests a boot mount
872937
# inside /mnt_temp, and a plain umount would fail on the child mount)
873938
umount -R /mnt_temp 2>/dev/null || umount /mnt_temp 2>/dev/null || true
@@ -913,6 +978,7 @@ cleanup() {
913978
sync
914979
sleep 1
915980
}
981+
# --- end cleanup ---
916982
trap cleanup EXIT
917983

918984
# ─── Root Check ──────────────────────────────────────────────────────────────
@@ -932,20 +998,41 @@ if [ -b /dev/mapper/${LUKS_NAME} ]; then
932998
echo " keep = leave it open and reuse it (saves a passphrase prompt in config-only mode)"
933999
echo " close = close it and start fresh"
9341000
ask STALE_CHOICE LUKS_STALE_MAPPER "Keep or close? (keep/close): "
935-
umount -R /mnt 2>/dev/null || true
936-
case "$STALE_CHOICE" in
937-
keep)
938-
log " Keeping ${LUKS_NAME} open — it will be reused."
939-
;;
940-
close)
941-
cryptsetup close ${LUKS_NAME} 2>/dev/null \
942-
|| fatal "Could not close ${LUKS_NAME} (still in use?). Close it manually and re-run."
943-
log " Closed stale ${LUKS_NAME}."
944-
;;
945-
*)
946-
fatal "Answer 'keep' or 'close'. Nothing was changed."
947-
;;
948-
esac
1001+
if [ "$DRY_RUN" = "1" ]; then
1002+
# A dry run unmounts and closes nothing. Kept, the mapper is read as
1003+
# it stands; closing it would be a change, so the dry run stops there.
1004+
case "$STALE_CHOICE" in
1005+
keep)
1006+
if findmnt -rno TARGET 2>/dev/null | grep -qE '^/mnt(/|$)'; then
1007+
log " [dry-run] A real run would first unmount what is mounted under /mnt; this one leaves it."
1008+
fi
1009+
log " [dry-run] Keeping ${LUKS_NAME} open — it is read as it stands."
1010+
;;
1011+
close)
1012+
log "[dry-run] Would unmount anything under /mnt, then close /dev/mapper/${LUKS_NAME}."
1013+
log "[dry-run] Nothing was changed."
1014+
exit 0
1015+
;;
1016+
*)
1017+
fatal "Answer 'keep' or 'close'. Nothing was changed."
1018+
;;
1019+
esac
1020+
else
1021+
umount -R /mnt 2>/dev/null || true
1022+
case "$STALE_CHOICE" in
1023+
keep)
1024+
log " Keeping ${LUKS_NAME} open — it will be reused."
1025+
;;
1026+
close)
1027+
cryptsetup close ${LUKS_NAME} 2>/dev/null \
1028+
|| fatal "Could not close ${LUKS_NAME} (still in use?). Close it manually and re-run."
1029+
log " Closed stale ${LUKS_NAME}."
1030+
;;
1031+
*)
1032+
fatal "Answer 'keep' or 'close'. Nothing was changed."
1033+
;;
1034+
esac
1035+
fi
9491036
fi
9501037

9511038
# ─── Dependency Check (live environment core) ────────────────────────────────
@@ -1218,6 +1305,12 @@ if [ -b /dev/mapper/${LUKS_NAME} ]; then
12181305
else
12191306
err "/dev/mapper/${LUKS_NAME} is open but backed by: $MAPPER_BACKING"
12201307
err "You selected ROOT: $TARGET_ROOT_REAL"
1308+
if [ "$DRY_RUN" = "1" ]; then
1309+
err "Reusing this mapper would configure the WRONG system — a real run closes it here."
1310+
log "[dry-run] Would close /dev/mapper/${LUKS_NAME}, then go on with $TARGET_ROOT."
1311+
log "[dry-run] Nothing was changed."
1312+
exit 0
1313+
fi
12211314
err "Reusing this mapper would configure the WRONG system — closing it."
12221315
if cryptsetup close ${LUKS_NAME} 2>/dev/null; then
12231316
log " Closed mismatched ${LUKS_NAME}; the selected device will be opened when needed."
@@ -1616,6 +1709,16 @@ if blkid "$TARGET_ROOT" | grep -q 'TYPE="crypto_LUKS"'; then
16161709
echo " (reencrypt --resume-only) and then redo the configuration"
16171710
echo " phase. This script can do both now."
16181711
echo ""
1712+
# Finishing it writes the whole device, and everything after it reads
1713+
# the finished volume, so a dry run stops here — whatever LUKS_RESUME
1714+
# says. Not even the passphrase is asked for.
1715+
if [ "$DRY_RUN" = "1" ]; then
1716+
log "[dry-run] Would finish the interrupted encryption: cryptsetup reencrypt --resume-only $TARGET_ROOT"
1717+
log "[dry-run] (cryptsetup repair first, if a hard interruption left its journal dirty), then redo"
1718+
log "[dry-run] the configuration phase. The rest of the plan reads the finished volume, so the"
1719+
log "[dry-run] dry run stops here. Nothing was changed."
1720+
exit 0
1721+
fi
16191722
ask RESUME_OK LUKS_RESUME " Resume the interrupted encryption now? (yes/no): "
16201723
[ "$RESUME_OK" = "yes" ] \
16211724
|| fatal "Aborted. Resume manually with: cryptsetup reencrypt --resume-only $TARGET_ROOT"
@@ -1652,6 +1755,13 @@ if blkid "$TARGET_ROOT" | grep -q 'TYPE="crypto_LUKS"'; then
16521755
ask LUKS2_CHOICE LUKS_EXISTING " Select [1-3, default 3=quit]: "
16531756
case "${LUKS2_CHOICE:-3}" in
16541757
1|tune)
1758+
if [ "$DRY_RUN" = "1" ]; then
1759+
log "[dry-run] Would start luks-tune.sh to re-cost the keyslots of $TARGET_ROOT."
1760+
log "[dry-run] It has a dry run of its own, which shows the exact command and changes"
1761+
log "[dry-run] nothing: $SCRIPT_DIR/luks-tune.sh --dry-run"
1762+
log "[dry-run] Nothing was changed."
1763+
exit 0
1764+
fi
16551765
log "Launching the KDF tuning UI..."
16561766
[ -x "$SCRIPT_DIR/luks-tune.sh" ] \
16571767
|| fatal "luks-tune.sh not found next to this script."
@@ -1706,6 +1816,7 @@ if [ "$DEPLOY_MODE" = "config-only" ]; then
17061816
if [ ! -b /dev/mapper/${LUKS_NAME} ]; then
17071817
log "Unlocking $TARGET_ROOT (passphrase required)..."
17081818
cryptsetup open "${CRYPT_PASS_ARGS[@]}" "$TARGET_ROOT" ${LUKS_NAME}
1819+
MAPPER_OPENED_HERE=1
17091820
fi
17101821
DISCOVERY_DEV="/dev/mapper/${LUKS_NAME}"
17111822
fi
@@ -1881,15 +1992,23 @@ fi
18811992
echo ""
18821993
log "Auto-discovering the target system's configuration..."
18831994

1995+
# A dry run mounts at /mnt_temp only over nothing. A mount an earlier run left
1996+
# there is not its to touch: unmounting a read-write filesystem writes to it.
1997+
if [ "$DRY_RUN" = "1" ] && findmnt -rno TARGET 2>/dev/null | grep -qE '^/mnt_temp(/|$)'; then
1998+
log "[dry-run] /mnt_temp is still mounted from an earlier run, and a dry run mounts nothing over"
1999+
log "[dry-run] it. Unmount it, or run without --dry-run. Nothing was changed."
2000+
exit 0
2001+
fi
18842002
mkdir -p /mnt_temp
2003+
MNT_TEMP_MOUNTED_HERE=1
18852004

18862005
ROOT_SUBVOL=""
18872006
HOME_SUBVOL=""
18882007
SUBPATH="" # path prefix of the root subvolume inside /mnt_temp
18892008
if [ "$IS_BTRFS" -eq 1 ]; then
18902009
# Mount top-level subvolume (ID 5) to see all subvolume directories
18912010
# (through the opened mapper in config-only mode, raw partition otherwise)
1892-
mount -o subvolid=5,ro "$DISCOVERY_DEV" /mnt_temp
2011+
ll_ro_mount "$DISCOVERY_DEV" /mnt_temp subvolid=5
18932012
for try_sub in root @rootfs @ ""; do
18942013
if [ -f "/mnt_temp/${try_sub}${try_sub:+/}etc/fstab" ]; then
18952014
ROOT_SUBVOL="$try_sub"
@@ -1898,7 +2017,7 @@ if [ "$IS_BTRFS" -eq 1 ]; then
18982017
fi
18992018
done
19002019
else
1901-
mount -o ro "$DISCOVERY_DEV" /mnt_temp
2020+
ll_ro_mount "$DISCOVERY_DEV" /mnt_temp
19022021
[ -f /mnt_temp/etc/fstab ] && SUBPATH=""
19032022
fi
19042023

@@ -2185,7 +2304,7 @@ BLS_ROOT_SUBVOL=""
21852304
if [ "$SYSTEM_MODE" -eq 1 ]; then
21862305
BLS_SRC=""
21872306
if [ "$BOOT_IS_SEPARATE" -eq 1 ]; then
2188-
if mount -o ro "$TARGET_BOOT" "/mnt_temp/${SUBPATH}boot" 2>/dev/null; then
2307+
if ll_ro_mount "$TARGET_BOOT" "/mnt_temp/${SUBPATH}boot" 2>/dev/null; then
21892308
BLS_SRC="/mnt_temp/${SUBPATH}boot"
21902309
fi
21912310
else

‎docs/ABOUT.md‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,12 @@ work. The same entry point handles a plain filesystem, an interrupted run, a
104104
finished LUKS2 volume and a LUKS1 volume — you never have to know which state
105105
you are in.
106106

107-
**`--dry-run` is real.** It runs the entire read-only half — detection, the
108-
fstab cross-check, the KDF benchmark, and the exact `cryptsetup` invocation it
109-
would issue — and exits before the point of no return.
107+
**`--dry-run` is real.** It runs the read-only half — detection, the fstab
108+
cross-check, the KDF benchmark, and the exact `cryptsetup` invocation it would
109+
issue — and exits before the point of no return without writing to the
110+
target: its mounts are read-only and replay no journal, and a step that would
111+
change something sooner, such as finishing an interrupted encryption, is
112+
described and not taken.
110113

111114
## What it deliberately does not do
112115

‎docs/INSTALL.md‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,9 @@ of the inner filesystem, boot configuration, initramfs rebuilds, and the
6969
final verification gate. **Do not reboot if the gate reports errors** — it
7070
prints exactly what failed.
7171

72-
Want a rehearsal first? `sudo ./bin/linuxlocker.sh deploy --dry-run` runs all
73-
of the detection and prints the exact plan, changing nothing.
72+
Want a rehearsal first? `sudo ./bin/linuxlocker.sh deploy --dry-run` runs the
73+
detection and prints the plan, changing nothing; where a step would have to
74+
change something first, it says what that step would do and stops there.
7475

7576
## 4. First boot
7677

0 commit comments

Comments
 (0)