From 01a0073e781081b9c3832e34d51ef225d06f01c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Cluseau?= Date: Thu, 20 Nov 2025 09:11:12 +0100 Subject: [PATCH] cryptsetup: allow 'mass' reuse --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/cmd.rs | 1 - src/cmd/connect_boot.rs | 1 - src/cmd/init/bootstrap.rs | 1 + src/cmd/init/dmcrypt.rs | 95 +++++++++++++++++++++++++++++---------- src/cmd/init_input.rs | 4 ++ 7 files changed, 78 insertions(+), 28 deletions(-) delete mode 100644 src/cmd/connect_boot.rs diff --git a/Cargo.lock b/Cargo.lock index edf4b61..9b12934 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -817,7 +817,7 @@ dependencies = [ [[package]] name = "init" -version = "2.5.0" +version = "2.5.1" dependencies = [ "base64", "dkl", diff --git a/Cargo.toml b/Cargo.toml index 3a9baa6..49d59fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "init" -version = "2.5.0" +version = "2.5.1" edition = "2024" [profile.release] diff --git a/src/cmd.rs b/src/cmd.rs index fbfd7c9..ae2ab26 100644 --- a/src/cmd.rs +++ b/src/cmd.rs @@ -1,5 +1,4 @@ pub mod bootstrap; -pub mod connect_boot; pub mod init; pub mod init_input; pub mod version; diff --git a/src/cmd/connect_boot.rs b/src/cmd/connect_boot.rs deleted file mode 100644 index 8b13789..0000000 --- a/src/cmd/connect_boot.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/cmd/init/bootstrap.rs b/src/cmd/init/bootstrap.rs index ebdd13d..c5a019b 100644 --- a/src/cmd/init/bootstrap.rs +++ b/src/cmd/init/bootstrap.rs @@ -33,6 +33,7 @@ pub async fn bootstrap(cfg: Config) { .await; } + // prepare system let boot_version = utils::param("version").unwrap_or("current"); let base_dir = &format!("/bootstrap/{boot_version}"); diff --git a/src/cmd/init/dmcrypt.rs b/src/cmd/init/dmcrypt.rs index b4b6b54..3d74a51 100644 --- a/src/cmd/init/dmcrypt.rs +++ b/src/cmd/init/dmcrypt.rs @@ -56,27 +56,72 @@ pub async fn setup(devs: &[CryptDev]) { .await; } -static PREV_PW: Mutex = Mutex::const_new(String::new()); +struct PrevPw { + pw: String, + reuse: bool, +} + +impl PrevPw { + fn is_set(&self) -> bool { + !self.pw.is_empty() + } + + fn can_reuse(&self) -> bool { + self.reuse && self.is_set() + } + + fn invalidate(&mut self) { + self.pw = String::new(); + self.reuse = false; + } + + async fn input(&mut self, prompt: impl std::fmt::Display) -> String { + if self.can_reuse() { + info!("reusing password"); + self.pw.clone() + } else if self.is_set() { + let pw = + input::read_password(format!("{prompt} (\"\" reuse, \"*\" auto-reuse)? ")).await; + + match pw.as_str() { + "" => self.pw.clone(), + "*" => { + self.reuse = true; + self.pw.clone() + } + _ => { + self.pw = pw.clone(); + pw + } + } + } else { + let pw = loop { + let pw = input::read_password(format!("{prompt}? ")).await; + if pw.is_empty() { + error!("empty password provided!"); + continue; + } + break pw; + }; + + self.pw = pw.clone(); + pw + } + } +} + +static PREV_PW: Mutex = Mutex::const_new(PrevPw { + pw: String::new(), + reuse: false, +}); async fn crypt_open(crypt_dev: &str, dev_path: &str) -> Result<()> { 'open_loop: loop { let mut prev_pw = PREV_PW.lock().await; - let prompt = if prev_pw.is_empty() { - format!("crypt password for {crypt_dev}? ") - } else { - format!("crypt password for {crypt_dev} (enter = reuse previous)? ") - }; - let mut pw = input::read_password(prompt).await; - if pw.is_empty() { - pw = prev_pw.clone(); - } - if pw.is_empty() { - error!("empty password provided!"); - continue; - } - - *prev_pw = pw.clone(); + let pw = prev_pw + .input(format!("crypt password for {crypt_dev}")) + .await; if cryptsetup(&pw, ["open", dev_path, crypt_dev]).await? { return Ok(()); @@ -107,15 +152,17 @@ async fn crypt_open(crypt_dev: &str, dev_path: &str) -> Result<()> { } _ => unreachable!(), } - } else { - // device looks initialized, don't allow format - warn!("{dev_path} looks initialized, formatting not allowed from init"); + } - match input::read_choice(["[r]etry", "[i]gnore"]).await { - 'r' => continue 'open_loop, - 'i' => return Ok(()), - _ => unreachable!(), - } + // device looks initialized, don't allow format + warn!("{dev_path} looks initialized, formatting not allowed from init"); + + prev_pw.invalidate(); + + match input::read_choice(["[r]etry", "[i]gnore"]).await { + 'r' => continue 'open_loop, + 'i' => return Ok(()), + _ => unreachable!(), } } } diff --git a/src/cmd/init_input.rs b/src/cmd/init_input.rs index c5aa78b..568a596 100644 --- a/src/cmd/init_input.rs +++ b/src/cmd/init_input.rs @@ -2,6 +2,10 @@ use crate::input; pub async fn run() { tokio::spawn(async { + // give a bit of time for stdout + use tokio::time::{sleep, Duration}; + sleep(Duration::from_millis(200)).await; + if let Err(e) = input::forward_requests_from_socket().await { eprintln!("failed to forwards requests from socket: {e}"); std::process::exit(1);