Skip to content
Merged
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
70 changes: 49 additions & 21 deletions cryptpilot-core/src/fs/mkfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,39 +191,67 @@ pub async fn force_mkfs(
Ok(())
}

pub async fn is_empty_disk(device_path: &Path) -> Result<bool> {
/// Checks whether the device contains valuable data (i.e., a known filesystem).
///
/// - Partition tables (e.g., PTTYPE="atari") are ignored and treated as "no valuable data".
/// - If `fs_hint` is `Some`, and the expected filesystem is not detected, returns an error.
/// - If `fs_hint` is `None`, returns `true` if something detected on the device.
pub async fn has_valuable_data(device_path: &Path, fs_hint: Option<MakeFsType>) -> Result<bool> {
Command::new("blkid")
.arg("-p")
.arg(device_path)
.env("LC_ALL", "C")
.run_with_status_checker(|code, stdout, stderr| {
match code {
0 => {
// Found signatures, check if they are ext4, xfs, vfat or swap
let output = String::from_utf8_lossy(&stdout);
let has_fs = output.contains("TYPE=\"ext4\"")
|| output.contains("TYPE=\"xfs\"")
|| output.contains("TYPE=\"vfat\"")
|| output.contains("TYPE=\"swap\"");
Ok(!has_fs) // Return true if NOT one of these filesystems (empty or other fs)

match fs_hint {
Some(expected_fs) => {
let expected_str = match expected_fs {
MakeFsType::Swap => "swap",
MakeFsType::Ext4 => "ext4",
MakeFsType::Xfs => "xfs",
MakeFsType::Vfat => "vfat",
};

if output.contains(&format!("TYPE=\"{}\"", expected_str)) {
Ok(true)
} else {
// Something else detected (may have partition table, unknown FS, etc.)
bail!(
"Something else on {device_path:?} is detected, but expected '{expected_str}', found blkid output '{}'",
output.trim()
);
}
}
None => {
// Check if it's mistakenly identified as PTTYPE="atari" partition table.
// See: https://bugs.launchpad.net/ubuntu/+source/util-linux/+bug/2015355
if output.contains("PTTYPE=\"atari\"") {
tracing::debug!("Found PTTYPE=\"atari\" partition table on {device_path:?}, treating as no valuable data");
Ok(false)
}else{
// Log which one was found (for debug)
tracing::debug!("Found filesystem signature on {device_path:?}, blkid output: {}", output.trim());
Ok(true)
}
}
}
}
2 => {
// No signatures found
Ok(false)
}
2 => Ok(true), // No signatures found
_ => {
let stdout = String::from_utf8_lossy(&stdout);
let stderr = String::from_utf8_lossy(&stderr);
if stdout.contains("Input/output error")
|| stderr.contains("Input/output error")
{
// If we can't even read the disk, treat it as "uninitialized"
Ok(true)
} else {
bail!(
"blkid failed with exit code {}: stdout={}, stderr={}",
code,
stdout,
stderr
)
}
bail!(
"blkid failed with exit code {}: stdout='{}', stderr='{}'",
code,
stdout.trim(),
stderr.trim()
);
}
}
})
Expand Down
6 changes: 5 additions & 1 deletion cryptpilot-crypt/src/cmd/open.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ pub async fn open_for_specific_volume(volume_config: &VolumeConfig, check_fs: bo
// Check if filesystem is ready
if check_fs
&& volume_config.extra_config.makefs.is_some()
&& cryptpilot::fs::mkfs::is_empty_disk(&volume_config.volume_path()).await?
&& !cryptpilot::fs::mkfs::has_valuable_data(
&volume_config.volume_path(),
volume_config.extra_config.makefs,
)
.await?
{
// TODO: replace with RAII here
let _ = cryptpilot::fs::luks2::close(&volume_config.volume).await;
Expand Down
28 changes: 24 additions & 4 deletions cryptpilot-crypt/tests/volume_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,11 @@ pub async fn run_test_on_volume(config_str: &str, use_external_suite: bool) -> R
open_then(&volume_config, |volume_config| async move {
if !matches!(volume_config.extra_config.integrity, Some(true)) {
assert!(
!cryptpilot::fs::mkfs::is_empty_disk(&volume_config.volume_path()).await?
cryptpilot::fs::mkfs::has_valuable_data(
&volume_config.volume_path(),
volume_config.extra_config.makefs
)
.await?
);
}
Ok(())
Expand Down Expand Up @@ -221,7 +225,11 @@ pub async fn run_test_on_volume(config_str: &str, use_external_suite: bool) -> R
open_then(&volume_config, |volume_config| async move {
if !matches!(volume_config.extra_config.integrity, Some(true)) {
assert!(
!cryptpilot::fs::mkfs::is_empty_disk(&volume_config.volume_path()).await?
cryptpilot::fs::mkfs::has_valuable_data(
&volume_config.volume_path(),
volume_config.extra_config.makefs
)
.await?
);
}
Ok(())
Expand Down Expand Up @@ -288,14 +296,26 @@ pub async fn run_test_on_volume(config_str: &str, use_external_suite: bool) -> R
// Just Open it and do nothing but checking
open_then(&volume_config, |volume_config| async move {
// Add assertion to check if disk is empty
assert!(cryptpilot::fs::mkfs::is_empty_disk(&volume_config.volume_path()).await?);
assert!(
!cryptpilot::fs::mkfs::has_valuable_data(
&volume_config.volume_path(),
volume_config.extra_config.makefs
)
.await?
);
Ok(())
})
.await?;
// Test again
open_then(&volume_config, |volume_config| async move {
// Add assertion to check if disk is still empty after re-open
assert!(cryptpilot::fs::mkfs::is_empty_disk(&volume_config.volume_path()).await?);
assert!(
!cryptpilot::fs::mkfs::has_valuable_data(
&volume_config.volume_path(),
volume_config.extra_config.makefs
)
.await?
);
Ok(())
})
.await?;
Expand Down