mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 10:21:52 +00:00
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:
@@ -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?;
|
||||
}
|
||||
|
||||
@@ -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
100
backend/src/disk/fsck.rs
Normal 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,
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user