run fsck on startup (#1350)

* add code to un e2fsck on startup

* fix imports

* don't auto undo

* switch enum to newtype

* more explicit caller variable
This commit is contained in:
Aiden McClelland
2022-03-23 17:24:46 -05:00
committed by GitHub
parent e7f4aefb72
commit f3a30e40fe
6 changed files with 164 additions and 20 deletions

View File

@@ -4,7 +4,9 @@ use std::time::Duration;
use embassy::context::rpc::RpcContextConfig;
use embassy::context::{DiagnosticContext, SetupContext};
use embassy::disk::fsck::RepairStrategy;
use embassy::disk::main::DEFAULT_PASSWORD;
use embassy::disk::REPAIR_DISK_PATH;
use embassy::hostname::get_product_key;
use embassy::middleware::cors::cors;
use embassy::middleware::diagnostic::diagnostic;
@@ -79,9 +81,17 @@ async fn setup_or_init(cfg_path: Option<&str>) -> Result<(), Error> {
.await?
.trim(),
cfg.datadir(),
if tokio::fs::metadata(REPAIR_DISK_PATH).await.is_ok() {
RepairStrategy::Aggressive
} else {
RepairStrategy::Preen
},
DEFAULT_PASSWORD,
)
.await?;
tokio::fs::remove_file(REPAIR_DISK_PATH)
.await
.with_ctx(|_| (embassy::ErrorKind::Filesystem, REPAIR_DISK_PATH))?;
tracing::info!("Loaded Disk");
embassy::init::init(&cfg, &get_product_key().await?).await?;
}

View File

@@ -5,6 +5,7 @@ use rpc_toolkit::command;
use rpc_toolkit::yajrc::RpcError;
use crate::context::DiagnosticContext;
use crate::disk::repair;
use crate::logs::{display_logs, fetch_logs, LogResponse, LogSource};
use crate::shutdown::Shutdown;
use crate::util::display_none;
@@ -12,7 +13,7 @@ use crate::Error;
pub const SYSTEMD_UNIT: &'static str = "embassy-init";
#[command(subcommands(error, logs, exit, restart, forget_disk))]
#[command(subcommands(error, logs, exit, restart, forget_disk, disk))]
pub fn diagnostic() -> Result<(), Error> {
Ok(())
}
@@ -56,7 +57,12 @@ pub fn restart(#[context] ctx: DiagnosticContext) -> Result<(), Error> {
Ok(())
}
#[command(rename = "forget-disk", display(display_none))]
#[command(subcommands(forget_disk, repair))]
pub fn disk() -> Result<(), Error> {
Ok(())
}
#[command(rename = "forget", display(display_none))]
pub async fn forget_disk() -> Result<(), Error> {
let disk_guid = Path::new("/embassy-os/disk.guid");
if tokio::fs::metadata(disk_guid).await.is_ok() {

100
backend/src/disk/fsck.rs Normal file
View File

@@ -0,0 +1,100 @@
use std::ffi::OsStr;
use std::path::Path;
use color_eyre::eyre::eyre;
use tokio::process::Command;
use tracing::instrument;
use crate::{Error, ResultExt};
#[derive(Debug, Clone, Copy)]
pub struct RequiresReboot(pub bool);
impl std::ops::BitOrAssign for RequiresReboot {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0
}
}
#[derive(Debug, Clone, Copy)]
pub enum RepairStrategy {
Preen,
Aggressive,
}
impl RepairStrategy {
pub async fn e2fsck(
&self,
logicalname: impl AsRef<Path> + std::fmt::Debug,
) -> Result<RequiresReboot, Error> {
match self {
RepairStrategy::Preen => e2fsck_preen(logicalname).await,
RepairStrategy::Aggressive => e2fsck_aggressive(logicalname).await,
}
}
}
#[instrument]
pub async fn e2fsck_preen(
logicalname: impl AsRef<Path> + std::fmt::Debug,
) -> Result<RequiresReboot, Error> {
e2fsck_runner(Command::new("e2fsck").arg("-p"), logicalname).await
}
#[instrument]
pub async fn e2fsck_aggressive(
logicalname: impl AsRef<Path> + std::fmt::Debug,
) -> Result<RequiresReboot, Error> {
e2fsck_runner(
Command::new("e2fsck").arg("-y").arg("-z").arg(
Path::new("/embassy-os")
.join(
logicalname
.as_ref()
.file_name()
.unwrap_or(OsStr::new("unknown")),
)
.with_extension("e2undo"),
),
logicalname,
)
.await
}
async fn e2fsck_runner(
e2fsck_cmd: &mut Command,
logicalname: impl AsRef<Path> + std::fmt::Debug,
) -> Result<RequiresReboot, Error> {
let e2fsck_out = e2fsck_cmd.arg(logicalname.as_ref()).output().await?;
let e2fsck_stderr = String::from_utf8(e2fsck_out.stderr)?;
let code = e2fsck_out.status.code().ok_or_else(|| {
Error::new(
eyre!("e2fsck: process terminated by signal"),
crate::ErrorKind::DiskManagement,
)
})?;
if code & 4 != 0 {
tracing::error!(
"some filesystem errors NOT corrected on {}:\n{}",
logicalname.as_ref().display(),
e2fsck_stderr,
);
} else if code & 1 != 0 {
tracing::warn!(
"filesystem errors corrected on {}:\n{}",
logicalname.as_ref().display(),
e2fsck_stderr,
);
}
if code < 8 {
if code & 2 != 0 {
tracing::warn!("reboot required");
Ok(RequiresReboot(true))
} else {
Ok(RequiresReboot(false))
}
} else {
Err(Error::new(
eyre!("e2fsck: {}", e2fsck_stderr),
crate::ErrorKind::DiskManagement,
))
}
}

View File

@@ -5,6 +5,7 @@ use color_eyre::eyre::eyre;
use tokio::process::Command;
use tracing::instrument;
use super::fsck::{RepairStrategy, RequiresReboot};
use super::util::pvscan;
use crate::disk::mount::filesystem::block_dev::mount;
use crate::disk::mount::filesystem::ReadWrite;
@@ -196,7 +197,12 @@ pub async fn export<P: AsRef<Path>>(guid: &str, datadir: P) -> Result<(), Error>
}
#[instrument(skip(datadir, password))]
pub async fn import<P: AsRef<Path>>(guid: &str, datadir: P, password: &str) -> Result<(), Error> {
pub async fn import<P: AsRef<Path>>(
guid: &str,
datadir: P,
repair: RepairStrategy,
password: &str,
) -> Result<(), Error> {
let scan = pvscan().await?;
if scan
.values()
@@ -244,7 +250,7 @@ pub async fn import<P: AsRef<Path>>(guid: &str, datadir: P, password: &str) -> R
.arg(guid)
.invoke(crate::ErrorKind::DiskManagement)
.await?;
mount_all_fs(guid, datadir, password).await?;
mount_all_fs(guid, datadir, repair, password).await?;
Ok(())
}
@@ -253,8 +259,9 @@ pub async fn mount_fs<P: AsRef<Path>>(
guid: &str,
datadir: P,
name: &str,
repair: RepairStrategy,
password: &str,
) -> Result<(), Error> {
) -> Result<RequiresReboot, Error> {
tokio::fs::write(PASSWORD_PATH, password)
.await
.with_ctx(|_| (crate::ErrorKind::Filesystem, PASSWORD_PATH))?;
@@ -267,27 +274,26 @@ pub async fn mount_fs<P: AsRef<Path>>(
.arg(format!("{}_{}", guid, name))
.invoke(crate::ErrorKind::DiskManagement)
.await?;
mount(
Path::new("/dev/mapper").join(format!("{}_{}", guid, name)),
datadir.as_ref().join(name),
ReadWrite,
)
.await?;
let mapper_path = Path::new("/dev/mapper").join(format!("{}_{}", guid, name));
let reboot = repair.e2fsck(&mapper_path).await?;
mount(&mapper_path, datadir.as_ref().join(name), ReadWrite).await?;
tokio::fs::remove_file(PASSWORD_PATH)
.await
.with_ctx(|_| (crate::ErrorKind::Filesystem, PASSWORD_PATH))?;
Ok(())
Ok(reboot)
}
#[instrument(skip(datadir, password))]
pub async fn mount_all_fs<P: AsRef<Path>>(
guid: &str,
datadir: P,
repair: RepairStrategy,
password: &str,
) -> Result<(), Error> {
mount_fs(guid, &datadir, "main", password).await?;
mount_fs(guid, &datadir, "package-data", password).await?;
Ok(())
) -> Result<RequiresReboot, Error> {
let mut reboot = RequiresReboot(false);
reboot |= mount_fs(guid, &datadir, "main", repair, password).await?;
reboot |= mount_fs(guid, &datadir, "package-data", repair, password).await?;
Ok(reboot)
}

View File

@@ -2,17 +2,20 @@ use clap::ArgMatches;
use rpc_toolkit::command;
use self::util::DiskListResponse;
use crate::util::display_none;
use crate::util::serde::{display_serializable, IoFormat};
use crate::Error;
pub mod fsck;
pub mod main;
pub mod mount;
pub mod quirks;
pub mod util;
pub const BOOT_RW_PATH: &'static str = "/media/boot-rw";
pub const BOOT_RW_PATH: &str = "/media/boot-rw";
pub const REPAIR_DISK_PATH: &str = "/embassy-os/repair-disk";
#[command(subcommands(list))]
#[command(subcommands(list, repair))]
pub fn disk() -> Result<(), Error> {
Ok(())
}
@@ -79,3 +82,9 @@ pub async fn list(
) -> Result<DiskListResponse, Error> {
crate::disk::util::list().await
}
#[command(display(display_none))]
pub async fn repair() -> Result<(), Error> {
tokio::fs::write(REPAIR_DISK_PATH, b"").await?;
Ok(())
}

View File

@@ -28,6 +28,7 @@ use crate::context::rpc::RpcContextConfig;
use crate::context::setup::SetupResult;
use crate::context::SetupContext;
use crate::db::model::RecoveredPackageInfo;
use crate::disk::fsck::RepairStrategy;
use crate::disk::main::DEFAULT_PASSWORD;
use crate::disk::mount::filesystem::block_dev::BlockDev;
use crate::disk::mount::filesystem::cifs::Cifs;
@@ -96,7 +97,13 @@ pub async fn attach(
#[context] ctx: SetupContext,
#[arg] guid: Arc<String>,
) -> Result<SetupResult, Error> {
crate::disk::main::import(&*guid, &ctx.datadir, DEFAULT_PASSWORD).await?;
crate::disk::main::import(
&*guid,
&ctx.datadir,
RepairStrategy::Preen,
DEFAULT_PASSWORD,
)
.await?;
let product_key_path = Path::new("/embassy-data/main/product_key.txt");
if tokio::fs::metadata(product_key_path).await.is_ok() {
let pkey = tokio::fs::read_to_string(product_key_path).await?;
@@ -301,7 +308,13 @@ pub async fn execute_inner(
)
.await?,
);
crate::disk::main::import(&*guid, &ctx.datadir, DEFAULT_PASSWORD).await?;
crate::disk::main::import(
&*guid,
&ctx.datadir,
RepairStrategy::Preen,
DEFAULT_PASSWORD,
)
.await?;
let res = if let Some(recovery_source) = recovery_source {
let (tor_addr, root_ca, recover_fut) = recover(