From 2a0425e968781a9bfc43139670716d33952cd5f4 Mon Sep 17 00:00:00 2001 From: Aiden McClelland <3732071+dr-bonez@users.noreply.github.com> Date: Mon, 1 Nov 2021 14:00:53 -0600 Subject: [PATCH] switch to LVM + LUKS + ext4 (#747) * code complete * misc fixes * magic --- appmgr/src/bin/embassy-init.rs | 37 ++- appmgr/src/bin/embassyd.rs | 28 ++- appmgr/src/context/diagnostic.rs | 25 +- appmgr/src/context/rpc.rs | 23 +- appmgr/src/context/setup.rs | 19 +- appmgr/src/diagnostic.rs | 3 +- appmgr/src/disk/main.rs | 407 +++++++++++++++++-------------- appmgr/src/disk/util.rs | 20 +- appmgr/src/error.rs | 4 +- appmgr/src/setup.rs | 21 +- appmgr/src/shutdown.rs | 18 +- build/initialization.sh | 2 - 12 files changed, 325 insertions(+), 282 deletions(-) diff --git a/appmgr/src/bin/embassy-init.rs b/appmgr/src/bin/embassy-init.rs index 15202c577..41bf279e4 100644 --- a/appmgr/src/bin/embassy-init.rs +++ b/appmgr/src/bin/embassy-init.rs @@ -1,4 +1,5 @@ use std::path::Path; +use std::sync::Arc; use embassy::context::rpc::RpcContextConfig; use embassy::context::{DiagnosticContext, SetupContext}; @@ -72,16 +73,20 @@ async fn init(cfg_path: Option<&str>) -> Result<(), Error> { }) .await .with_kind(embassy::ErrorKind::Network)?; - let pool_name = ctx.zfs_pool_name.clone(); drop(ctx); - embassy::disk::main::export(&*pool_name).await?; + embassy::disk::main::export( + tokio::fs::read_to_string("/embassy-os/disk.guid") // unique identifier for volume group - keeps track of the disk that goes with your embassy + .await? + .trim(), + cfg.datadir(), + ) + .await?; } - embassy::disk::main::load( - tokio::fs::read_to_string("/embassy-os/disk.guid") // unique identifier for zfs pool - keeps track of the disk that goes with your embassy + embassy::disk::main::import( + tokio::fs::read_to_string("/embassy-os/disk.guid") // unique identifier for volume group - keeps track of the disk that goes with your embassy .await? .trim(), - cfg.zfs_pool_name(), cfg.datadir(), DEFAULT_PASSWORD, ) @@ -99,7 +104,11 @@ async fn init(cfg_path: Option<&str>) -> Result<(), Error> { .invoke(embassy::ErrorKind::Journald) .await?; tracing::info!("Mounted Logs"); - let tmp_docker = cfg.datadir().join("tmp").join("docker"); + let tmp_dir = cfg.datadir().join("package-data/tmp"); + if tokio::fs::metadata(&tmp_dir).await.is_err() { + tokio::fs::create_dir_all(&tmp_dir).await?; + } + let tmp_docker = cfg.datadir().join("package-data/tmp/docker"); if tokio::fs::metadata(&tmp_docker).await.is_ok() { tokio::fs::remove_dir_all(&tmp_docker).await?; } @@ -210,7 +219,21 @@ async fn inner_main(cfg_path: Option<&str>) -> Result, Error> { .arg("nginx") .invoke(embassy::ErrorKind::Nginx) .await?; - let ctx = DiagnosticContext::init(cfg_path, e).await?; + let ctx = DiagnosticContext::init( + cfg_path, + if tokio::fs::metadata("/embassy-os/disk.guid").await.is_ok() { + Some(Arc::new( + tokio::fs::read_to_string("/embassy-os/disk.guid") // unique identifier for volume group - keeps track of the disk that goes with your embassy + .await? + .trim() + .to_owned(), + )) + } else { + None + }, + e, + ) + .await?; let mut shutdown_recv = ctx.shutdown.subscribe(); rpc_server!({ command: embassy::diagnostic_api, diff --git a/appmgr/src/bin/embassyd.rs b/appmgr/src/bin/embassyd.rs index 43a8acf2d..4c0551883 100644 --- a/appmgr/src/bin/embassyd.rs +++ b/appmgr/src/bin/embassyd.rs @@ -1,3 +1,4 @@ +use std::sync::Arc; use std::time::Duration; use color_eyre::eyre::eyre; @@ -36,7 +37,16 @@ fn err_to_500(e: Error) -> Response { #[instrument] async fn inner_main(cfg_path: Option<&str>) -> Result, Error> { - let rpc_ctx = RpcContext::init(cfg_path).await?; + let rpc_ctx = RpcContext::init( + cfg_path, + Arc::new( + tokio::fs::read_to_string("/embassy-os/disk.guid") // unique identifier for volume group - keeps track of the disk that goes with your embassy + .await? + .trim() + .to_owned(), + ), + ) + .await?; let mut shutdown_recv = rpc_ctx.shutdown.subscribe(); let sig_handler_ctx = rpc_ctx.clone(); @@ -302,7 +312,21 @@ fn main() { .arg("nginx") .invoke(embassy::ErrorKind::Nginx) .await?; - let ctx = DiagnosticContext::init(cfg_path, e).await?; + let ctx = DiagnosticContext::init( + cfg_path, + if tokio::fs::metadata("/embassy-os/disk.guid").await.is_ok() { + Some(Arc::new( + tokio::fs::read_to_string("/embassy-os/disk.guid") // unique identifier for volume group - keeps track of the disk that goes with your embassy + .await? + .trim() + .to_owned(), + )) + } else { + None + }, + e, + ) + .await?; rpc_server!({ command: embassy::diagnostic_api, context: ctx.clone(), diff --git a/appmgr/src/context/diagnostic.rs b/appmgr/src/context/diagnostic.rs index c4fba2f36..36ea6fe70 100644 --- a/appmgr/src/context/diagnostic.rs +++ b/appmgr/src/context/diagnostic.rs @@ -1,6 +1,6 @@ use std::net::{IpAddr, SocketAddr}; use std::ops::Deref; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::sync::Arc; use rpc_toolkit::yajrc::RpcError; @@ -20,7 +20,7 @@ use crate::{Error, ResultExt}; #[serde(rename_all = "kebab-case")] pub struct DiagnosticContextConfig { pub bind_rpc: Option, - pub zfs_pool_name: Option, + pub datadir: Option, } impl DiagnosticContextConfig { #[instrument(skip(path))] @@ -38,35 +38,40 @@ impl DiagnosticContextConfig { Ok(Self::default()) } } - pub fn zfs_pool_name(&self) -> &str { - self.zfs_pool_name - .as_ref() - .map(|s| s.as_str()) - .unwrap_or("embassy-data") + pub fn datadir(&self) -> &Path { + self.datadir + .as_deref() + .unwrap_or_else(|| Path::new("/embassy-data")) } } pub struct DiagnosticContextSeed { pub bind_rpc: SocketAddr, + pub datadir: PathBuf, pub shutdown: Sender>, pub error: Arc, - pub zfs_pool_name: Arc, + pub disk_guid: Option>, } #[derive(Clone)] pub struct DiagnosticContext(Arc); impl DiagnosticContext { #[instrument(skip(path))] - pub async fn init>(path: Option

, error: Error) -> Result { + pub async fn init>( + path: Option

, + disk_guid: Option>, + error: Error, + ) -> Result { let cfg = DiagnosticContextConfig::load(path).await?; let (shutdown, _) = tokio::sync::broadcast::channel(1); Ok(Self(Arc::new(DiagnosticContextSeed { bind_rpc: cfg.bind_rpc.unwrap_or(([127, 0, 0, 1], 5959).into()), + datadir: cfg.datadir().to_owned(), shutdown, + disk_guid, error: Arc::new(error.into()), - zfs_pool_name: Arc::new(cfg.zfs_pool_name().to_owned()), }))) } } diff --git a/appmgr/src/context/rpc.rs b/appmgr/src/context/rpc.rs index d64d79b39..1048611f5 100644 --- a/appmgr/src/context/rpc.rs +++ b/appmgr/src/context/rpc.rs @@ -43,7 +43,6 @@ pub struct RpcContextConfig { pub tor_control: Option, pub tor_socks: Option, pub revision_cache_size: Option, - pub zfs_pool_name: Option, pub datadir: Option, pub log_server: Option, } @@ -62,17 +61,10 @@ impl RpcContextConfig { Ok(Self::default()) } } - pub fn zfs_pool_name(&self) -> &str { - self.zfs_pool_name - .as_ref() - .map(|s| s.as_str()) - .unwrap_or("embassy-data") - } - pub fn datadir(&self) -> Cow<'_, Path> { + pub fn datadir(&self) -> &Path { self.datadir - .as_ref() - .map(|a| Cow::Borrowed(a.as_path())) - .unwrap_or_else(|| Cow::Owned(Path::new("/").join(self.zfs_pool_name()))) + .as_deref() + .unwrap_or_else(|| Path::new("/embassy-data")) } pub async fn db(&self, secret_store: &SqlitePool) -> Result { let db_path = self.datadir().join("main").join("embassy.db"); @@ -115,7 +107,7 @@ pub struct RpcContextSeed { pub bind_ws: SocketAddr, pub bind_static: SocketAddr, pub datadir: PathBuf, - pub zfs_pool_name: Arc, + pub disk_guid: Arc, pub db: PatchDb, pub secret_store: SqlitePool, pub docker: Docker, @@ -136,7 +128,10 @@ pub struct RpcContextSeed { pub struct RpcContext(Arc); impl RpcContext { #[instrument(skip(cfg_path))] - pub async fn init>(cfg_path: Option

) -> Result { + pub async fn init>( + cfg_path: Option

, + disk_guid: Arc, + ) -> Result { let base = RpcContextConfig::load(cfg_path).await?; let log_epoch = Arc::new(AtomicU64::new(rand::random())); let logger = EmbassyLogger::init(log_epoch.clone(), base.log_server.clone(), false); @@ -167,7 +162,7 @@ impl RpcContext { bind_ws: base.bind_ws.unwrap_or(([127, 0, 0, 1], 5960).into()), bind_static: base.bind_static.unwrap_or(([127, 0, 0, 1], 5961).into()), datadir: base.datadir().to_path_buf(), - zfs_pool_name: Arc::new(base.zfs_pool_name().to_owned()), + disk_guid, db, secret_store, docker, diff --git a/appmgr/src/context/setup.rs b/appmgr/src/context/setup.rs index b0d47f3a4..15fcf3cee 100644 --- a/appmgr/src/context/setup.rs +++ b/appmgr/src/context/setup.rs @@ -30,7 +30,6 @@ use crate::{Error, ResultExt}; #[serde(rename_all = "kebab-case")] pub struct SetupContextConfig { pub bind_rpc: Option, - pub zfs_pool_name: Option, pub datadir: Option, } impl SetupContextConfig { @@ -49,17 +48,10 @@ impl SetupContextConfig { Ok(Self::default()) } } - pub fn zfs_pool_name(&self) -> &str { - self.zfs_pool_name - .as_ref() - .map(|s| s.as_str()) - .unwrap_or("embassy-data") - } - pub fn datadir(&self) -> Cow<'_, Path> { + pub fn datadir(&self) -> &Path { self.datadir - .as_ref() - .map(|a| Cow::Borrowed(a.as_path())) - .unwrap_or_else(|| Cow::Owned(Path::new("/").join(self.zfs_pool_name()))) + .as_deref() + .unwrap_or_else(|| Path::new("/embassy-data")) } } @@ -67,7 +59,6 @@ pub struct SetupContextSeed { pub bind_rpc: SocketAddr, pub shutdown: Sender<()>, pub datadir: PathBuf, - pub zfs_pool_name: Arc, pub selected_v2_drive: RwLock>, pub cached_product_key: RwLock>>, pub recovery_status: RwLock>>, @@ -80,13 +71,11 @@ impl SetupContext { pub async fn init>(path: Option

) -> Result { let cfg = SetupContextConfig::load(path).await?; let (shutdown, _) = tokio::sync::broadcast::channel(1); - let datadir = cfg.datadir().into_owned(); - let zfs_pool_name = Arc::new(cfg.zfs_pool_name().to_owned()); + let datadir = cfg.datadir().to_owned(); Ok(Self(Arc::new(SetupContextSeed { bind_rpc: cfg.bind_rpc.unwrap_or(([127, 0, 0, 1], 5959).into()), shutdown, datadir, - zfs_pool_name, selected_v2_drive: RwLock::new(None), cached_product_key: RwLock::new(None), recovery_status: RwLock::new(None), diff --git a/appmgr/src/diagnostic.rs b/appmgr/src/diagnostic.rs index aec5d36ca..0564fd7ac 100644 --- a/appmgr/src/diagnostic.rs +++ b/appmgr/src/diagnostic.rs @@ -47,7 +47,8 @@ pub fn exit(#[context] ctx: DiagnosticContext) -> Result<(), Error> { pub fn restart(#[context] ctx: DiagnosticContext) -> Result<(), Error> { ctx.shutdown .send(Some(Shutdown { - zfs_pool: ctx.zfs_pool_name.clone(), + datadir: ctx.datadir.clone(), + disk_guid: ctx.disk_guid.clone(), db_handle: None, restart: true, })) diff --git a/appmgr/src/disk/main.rs b/appmgr/src/disk/main.rs index 3891badd7..ddbd1aff9 100644 --- a/appmgr/src/disk/main.rs +++ b/appmgr/src/disk/main.rs @@ -3,238 +3,273 @@ use std::path::Path; use tokio::process::Command; use tracing::instrument; +use crate::disk::util::{mount, unmount}; use crate::util::Invoke; use crate::{Error, ResultExt}; pub const PASSWORD_PATH: &'static str = "/etc/embassy/password"; pub const DEFAULT_PASSWORD: &'static str = "password"; +pub const MAIN_FS_SIZE: FsSize = FsSize::Gigabytes(8); +pub const SWAP_SIZE: FsSize = FsSize::Gigabytes(8); // TODO: use IncorrectDisk / DiskNotAvailable / DiskCorrupted -#[instrument(skip(disks))] -pub async fn create, P: AsRef>( - pool_name: &str, - disks: I, +#[instrument(skip(disks, datadir, password))] +pub async fn create( + disks: &I, + datadir: impl AsRef, password: &str, -) -> Result { - let guid = create_pool(pool_name, disks).await?; - create_fs(pool_name, password).await?; - export(pool_name).await?; +) -> Result +where + for<'a> &'a I: IntoIterator, + P: AsRef, +{ + let guid = create_pool(disks).await?; + create_all_fs(&guid, &datadir, password).await?; + export(&guid, datadir).await?; Ok(guid) } -#[instrument(skip(datadir))] -pub async fn load>( - guid: &str, - pool_name: &str, - datadir: P, - password: &str, -) -> Result<(), Error> { - import(guid).await?; - mount(pool_name, datadir, password).await?; - Ok(()) -} - #[instrument(skip(disks))] -pub async fn create_pool, P: AsRef>( - pool_name: &str, - disks: I, -) -> Result { - let mut cmd = Command::new("zpool"); - cmd.arg("create").arg("-f").arg(pool_name); +pub async fn create_pool(disks: &I) -> Result +where + for<'a> &'a I: IntoIterator, + P: AsRef, +{ + for disk in disks { + tokio::fs::write(disk.as_ref(), &[0; 2048]).await?; // wipe partition table and lvm2 metadata + Command::new("pvcreate") + .arg("-yff") + .arg(disk.as_ref()) + .invoke(crate::ErrorKind::DiskManagement) + .await?; + } + let guid = format!( + "EMBASSY_{}", + base32::encode( + base32::Alphabet::RFC4648 { padding: false }, + &rand::random::<[u8; 32]>(), + ) + ); + let mut cmd = Command::new("vgcreate"); + cmd.arg("-y").arg(&guid); for disk in disks { cmd.arg(disk.as_ref()); } - cmd.invoke(crate::ErrorKind::Zfs).await?; - Ok(String::from_utf8( - Command::new("zpool") - .arg("get") - .arg("-H") - .arg("-ovalue") - .arg("guid") - .arg(pool_name) - .invoke(crate::ErrorKind::Zfs) - .await?, - )? - .trim() // this allocates but fuck it - .to_owned()) + cmd.invoke(crate::ErrorKind::DiskManagement).await?; + Ok(guid) } -#[instrument] -pub async fn create_fs(pool_name: &str, password: &str) -> Result<(), Error> { +#[derive(Debug, Clone, Copy)] +pub enum FsSize { + Gigabytes(usize), + FreePercentage(usize), +} + +#[instrument(skip(datadir, password))] +pub async fn create_fs>( + guid: &str, + datadir: P, + name: &str, + size: FsSize, + swap: bool, + password: &str, +) -> Result<(), Error> { tokio::fs::write(PASSWORD_PATH, password) .await .with_ctx(|_| (crate::ErrorKind::Filesystem, PASSWORD_PATH))?; - Command::new("zfs") - .arg("create") - .arg("-o") - .arg("reservation=5G") - .arg("-o") - .arg("encryption=on") - .arg("-o") - .arg("keylocation=file:///etc/embassy/password") - .arg("-o") - .arg("keyformat=passphrase") - .arg(format!("{}/main", pool_name)) - .invoke(crate::ErrorKind::Zfs) + let mut cmd = Command::new("lvcreate"); + match size { + FsSize::Gigabytes(a) => cmd.arg("-L").arg(format!("{}G", a)), + FsSize::FreePercentage(a) => cmd.arg("-l").arg(format!("{}%FREE", a)), + }; + cmd.arg("-y") + .arg("-n") + .arg(name) + .arg(guid) + .invoke(crate::ErrorKind::DiskManagement) .await?; - Command::new("zfs") - .arg("create") - .arg("-o") - .arg("reservation=5G") - .arg(format!("{}/updates", pool_name)) - .invoke(crate::ErrorKind::Zfs) + Command::new("cryptsetup") + .arg("luksFormat") + .arg(format!("--key-file={}", PASSWORD_PATH)) + .arg(format!("--keyfile-size={}", password.len())) + .arg(Path::new("/dev").join(guid).join(name)) + .invoke(crate::ErrorKind::DiskManagement) .await?; - Command::new("zfs") - .arg("create") - .arg("-o") - .arg("encryption=on") - .arg("-o") - .arg("keylocation=file:///etc/embassy/password") - .arg("-o") - .arg("keyformat=passphrase") - .arg(format!("{}/package-data", pool_name)) - .invoke(crate::ErrorKind::Zfs) + Command::new("cryptsetup") + .arg("luksOpen") + .arg(format!("--key-file={}", PASSWORD_PATH)) + .arg(format!("--keyfile-size={}", password.len())) + .arg(Path::new("/dev").join(guid).join(name)) + .arg(format!("{}_{}", guid, name)) + .invoke(crate::ErrorKind::DiskManagement) .await?; - Command::new("zfs") - .arg("create") - .arg("-o") - .arg("encryption=on") - .arg("-o") - .arg("keylocation=file:///etc/embassy/password") - .arg("-o") - .arg("keyformat=passphrase") - .arg(format!("{}/tmp", pool_name)) - .invoke(crate::ErrorKind::Zfs) + if swap { + Command::new("mkswap") + .arg("-f") + .arg(Path::new("/dev/mapper").join(format!("{}_{}", guid, name))) + .invoke(crate::ErrorKind::DiskManagement) + .await?; + Command::new("swapon") + .arg(Path::new("/dev/mapper").join(format!("{}_{}", guid, name))) + .invoke(crate::ErrorKind::DiskManagement) + .await?; + } else { + Command::new("mkfs.ext4") + .arg(Path::new("/dev/mapper").join(format!("{}_{}", guid, name))) + .invoke(crate::ErrorKind::DiskManagement) + .await?; + mount( + Path::new("/dev/mapper").join(format!("{}_{}", guid, name)), + datadir.as_ref().join(name), + ) .await?; + } tokio::fs::remove_file(PASSWORD_PATH) .await .with_ctx(|_| (crate::ErrorKind::Filesystem, PASSWORD_PATH))?; Ok(()) } -#[instrument] -pub async fn create_swap(pool_name: &str) -> Result<(), Error> { - let pagesize = String::from_utf8( - Command::new("getconf") - .arg("PAGESIZE") - .invoke(crate::ErrorKind::Zfs) - .await?, - )?; - Command::new("zfs") - .arg("create") - .arg("-V8G") - .arg("-b") - .arg(pagesize) - .arg("-o") - .arg("logbias=throughput") - .arg("-o") - .arg("sync=always") - .arg("-o") - .arg("primarycache=metadata") - .arg("-o") - .arg("com.sun:auto-snapshot=false") - .invoke(crate::ErrorKind::Zfs) - .await?; - Command::new("mkswap") - .arg("-f") - .arg(Path::new("/dev/zvol").join(pool_name).join("swap")) - .invoke(crate::ErrorKind::Zfs) - .await?; - Ok(()) -} - -#[instrument] -pub async fn use_swap(pool_name: &str) -> Result<(), Error> { - Command::new("swapon") - .arg(Path::new("/dev/zvol").join(pool_name).join("swap")) - .invoke(crate::ErrorKind::Zfs) - .await?; - Ok(()) -} - -#[instrument] -pub async fn export(pool_name: &str) -> Result<(), Error> { - Command::new("zpool") - .arg("export") - .arg(pool_name) - .invoke(crate::ErrorKind::Zfs) - .await?; - Ok(()) -} - -#[instrument] -pub async fn import(guid: &str) -> Result<(), Error> { - Command::new("zpool") - .arg("import") - .arg("-f") - .arg(guid) - .invoke(crate::ErrorKind::Zfs) - .await?; +#[instrument(skip(datadir, password))] +pub async fn create_all_fs>( + guid: &str, + datadir: P, + password: &str, +) -> Result<(), Error> { + create_fs(guid, &datadir, "main", MAIN_FS_SIZE, false, password).await?; + create_fs(guid, &datadir, "swap", SWAP_SIZE, true, password).await?; + create_fs( + guid, + &datadir, + "package-data", + FsSize::FreePercentage(100), + false, + password, + ) + .await?; Ok(()) } #[instrument(skip(datadir))] -pub async fn mount>( - pool_name: &str, +pub async fn unmount_fs>( + guid: &str, datadir: P, + name: &str, + swap: bool, +) -> Result<(), Error> { + if swap { + Command::new("swapoff") + .arg(Path::new("/dev/mapper").join(format!("{}_{}", guid, name))) + .invoke(crate::ErrorKind::DiskManagement) + .await?; + } else { + unmount(datadir.as_ref().join(name)).await?; + } + Command::new("cryptsetup") + .arg("luksClose") + .arg(format!("{}_{}", guid, name)) + .invoke(crate::ErrorKind::DiskManagement) + .await?; + + Ok(()) +} + +#[instrument(skip(datadir))] +pub async fn unmount_all_fs>(guid: &str, datadir: P) -> Result<(), Error> { + unmount_fs(guid, &datadir, "main", false).await?; + unmount_fs(guid, &datadir, "swap", true).await?; + unmount_fs(guid, &datadir, "package-data", false).await?; + Ok(()) +} + +#[instrument(skip(datadir))] +pub async fn export>(guid: &str, datadir: P) -> Result<(), Error> { + unmount_all_fs(guid, datadir).await?; + Command::new("vgchange") + .arg("-an") + .arg(guid) + .invoke(crate::ErrorKind::DiskManagement) + .await?; + Command::new("vgexport") + .arg(guid) + .invoke(crate::ErrorKind::DiskManagement) + .await?; + Ok(()) +} + +#[instrument(skip(datadir, password))] +pub async fn import>(guid: &str, datadir: P, password: &str) -> Result<(), Error> { + match Command::new("vgimport") + .arg(guid) + .invoke(crate::ErrorKind::DiskManagement) + .await + { + Ok(_) => Ok(()), + Err(e) + if format!("{}", e.source).trim() + == format!("Volume group \"{}\" is not exported", guid) => + { + Ok(()) + } + Err(e) => Err(e), + }?; + Command::new("vgchange") + .arg("-ay") + .arg(guid) + .invoke(crate::ErrorKind::DiskManagement) + .await?; + mount_all_fs(guid, datadir, password).await?; + Ok(()) +} + +#[instrument(skip(datadir, password))] +pub async fn mount_fs>( + guid: &str, + datadir: P, + name: &str, + swap: bool, password: &str, ) -> Result<(), Error> { - let mountpoint = String::from_utf8( - Command::new("zfs") - .arg("get") - .arg("-H") - .arg("-ovalue") - .arg("mountpoint") - .arg(pool_name) - .invoke(crate::ErrorKind::Zfs) - .await?, - )?; - if Path::new(mountpoint.trim()) != datadir.as_ref() { - Command::new("zfs") - .arg("set") - .arg(format!("mountpoint={}", datadir.as_ref().display())) - .arg(pool_name) - .invoke(crate::ErrorKind::Zfs) - .await?; - } - tokio::fs::write(PASSWORD_PATH, password) .await .with_ctx(|_| (crate::ErrorKind::Filesystem, PASSWORD_PATH))?; - Command::new("zfs") - .arg("load-key") - .arg(format!("{}/main", pool_name)) - .invoke(crate::ErrorKind::Zfs) + Command::new("cryptsetup") + .arg("luksOpen") + .arg(format!("--key-file={}", PASSWORD_PATH)) + .arg(format!("--keyfile-size={}", password.len())) + .arg(Path::new("/dev").join(guid).join(name)) + .arg(format!("{}_{}", guid, name)) + .invoke(crate::ErrorKind::DiskManagement) .await?; - Command::new("zfs") - .arg("load-key") - .arg(format!("{}/package-data", pool_name)) - .invoke(crate::ErrorKind::Zfs) - .await?; - Command::new("zfs") - .arg("load-key") - .arg(format!("{}/tmp", pool_name)) - .invoke(crate::ErrorKind::Zfs) + if swap { + Command::new("swapon") + .arg(Path::new("/dev/mapper").join(format!("{}_{}", guid, name))) + .invoke(crate::ErrorKind::DiskManagement) + .await?; + } else { + mount( + Path::new("/dev/mapper").join(format!("{}_{}", guid, name)), + datadir.as_ref().join(name), + ) .await?; + } + tokio::fs::remove_file(PASSWORD_PATH) .await .with_ctx(|_| (crate::ErrorKind::Filesystem, PASSWORD_PATH))?; - Command::new("zfs") - .arg("mount") - .arg(format!("{}/main", pool_name)) - .invoke(crate::ErrorKind::Zfs) - .await?; - Command::new("zfs") - .arg("mount") - .arg(format!("{}/package-data", pool_name)) - .invoke(crate::ErrorKind::Zfs) - .await?; - Command::new("zfs") - .arg("mount") - .arg(format!("{}/tmp", pool_name)) - .invoke(crate::ErrorKind::Zfs) - .await?; + Ok(()) +} + +#[instrument(skip(datadir, password))] +pub async fn mount_all_fs>( + guid: &str, + datadir: P, + password: &str, +) -> Result<(), Error> { + mount_fs(guid, &datadir, "main", false, password).await?; + mount_fs(guid, &datadir, "swap", true, password).await?; + mount_fs(guid, &datadir, "package-data", false, password).await?; Ok(()) } diff --git a/appmgr/src/disk/util.rs b/appmgr/src/disk/util.rs index d1a192693..0d23b4703 100644 --- a/appmgr/src/disk/util.rs +++ b/appmgr/src/disk/util.rs @@ -62,7 +62,6 @@ const SYS_BLOCK_PATH: &'static str = "/sys/block"; lazy_static::lazy_static! { static ref PARTITION_REGEX: Regex = Regex::new("-part[0-9]+$").unwrap(); - static ref ZPOOL_REGEX: Regex = Regex::new("^\\s+([a-z0-9]+)\\s+ONLINE").unwrap(); } #[instrument(skip(path))] @@ -150,22 +149,7 @@ pub async fn get_used>(path: P) -> Result { #[instrument] pub async fn list() -> Result, Error> { - let zpool_drives: BTreeSet = match Command::new("zpool") - .arg("status") - .invoke(crate::ErrorKind::Zfs) - .await - { - Ok(v) => String::from_utf8(v)? - .lines() - .filter_map(|l| ZPOOL_REGEX.captures(l)) - .filter_map(|c| c.get(1)) - .map(|d| Path::new("/dev").join(d.as_str())) - .collect(), - Err(e) => { - tracing::warn!("`zpool status` returned error: {}", e); - BTreeSet::new() - } - }; + let internal_drives: BTreeSet = BTreeSet::new(); // todo!("parse pvscan"); let disks = tokio_stream::wrappers::ReadDirStream::new( tokio::fs::read_dir(DISK_PATH) .await @@ -233,7 +217,7 @@ pub async fn list() -> Result, Error> { tracing::warn!("Could not get capacity of {}: {}", disk.display(), e.source) }) .unwrap_or_default(); - if zpool_drives.contains(&disk) { + if internal_drives.contains(&disk) { internal = true; } else { for part in parts { diff --git a/appmgr/src/error.rs b/appmgr/src/error.rs index 26e639ffb..30b55b8dc 100644 --- a/appmgr/src/error.rs +++ b/appmgr/src/error.rs @@ -53,7 +53,7 @@ pub enum ErrorKind { ParseSysInfo = 45, Wifi = 46, Journald = 47, - Zfs = 48, + DiskManagement = 48, OpenSsl = 49, PasswordHashGeneration = 50, DiagnosticMode = 51, @@ -111,7 +111,7 @@ impl ErrorKind { ParseSysInfo => "System Info Parsing Error", Wifi => "WiFi Internal Error", Journald => "Journald Error", - Zfs => "ZFS Error", + DiskManagement => "Disk Management Error", OpenSsl => "OpenSSL Internal Error", PasswordHashGeneration => "Password Hash Generation Error", DiagnosticMode => "Embassy is in Diagnostic Mode", diff --git a/appmgr/src/setup.rs b/appmgr/src/setup.rs index 580a4ef15..ae7e7d573 100644 --- a/appmgr/src/setup.rs +++ b/appmgr/src/setup.rs @@ -142,25 +142,8 @@ pub async fn execute_inner( )); } let guid = - crate::disk::main::create(&ctx.zfs_pool_name, [embassy_logicalname], DEFAULT_PASSWORD) - .await?; - let search_string = format!("id: {}", guid); - let mut ctr = 0; - while { - ctr += 1; - ctr < 30 // 30s timeout - } && !String::from_utf8( - Command::new("zpool") - .arg("import") - .invoke(crate::ErrorKind::Zfs) - .await?, - )? - .lines() - .any(|line| line.trim() == &search_string) - { - tokio::time::sleep(Duration::from_secs(1)).await; - } - crate::disk::main::load(&guid, &ctx.zfs_pool_name, &ctx.datadir, DEFAULT_PASSWORD).await?; + crate::disk::main::create(&[embassy_logicalname], &ctx.datadir, DEFAULT_PASSWORD).await?; + crate::disk::main::import(&guid, &ctx.datadir, DEFAULT_PASSWORD).await?; let password = argon2::hash_encoded( embassy_password.as_bytes(), &rand::random::<[u8; 16]>()[..], diff --git a/appmgr/src/shutdown.rs b/appmgr/src/shutdown.rs index 8db8d1f0a..8d13ae708 100644 --- a/appmgr/src/shutdown.rs +++ b/appmgr/src/shutdown.rs @@ -1,3 +1,4 @@ +use std::path::PathBuf; use std::sync::Arc; use patch_db::{LockType, PatchDbHandle}; @@ -11,7 +12,8 @@ use crate::Error; #[derive(Debug, Clone)] pub struct Shutdown { - pub zfs_pool: Arc, + pub datadir: PathBuf, + pub disk_guid: Option>, pub restart: bool, pub db_handle: Option>, } @@ -45,9 +47,11 @@ impl Shutdown { tracing::error!("Error Stopping Docker: {}", e); tracing::debug!("{:?}", e); } - if let Err(e) = export(&*self.zfs_pool).await { - tracing::error!("Error Exporting ZFS Pool: {}", e); - tracing::debug!("{:?}", e); + if let Some(guid) = &self.disk_guid { + if let Err(e) = export(guid, &self.datadir).await { + tracing::error!("Error Exporting Volume Group: {}", e); + tracing::debug!("{:?}", e); + } } if let Err(e) = MARIO_DEATH.play().await { tracing::error!("Error Playing Shutdown Song: {}", e); @@ -76,7 +80,8 @@ pub async fn shutdown(#[context] ctx: RpcContext) -> Result<(), Error> { .await; ctx.shutdown .send(Some(Shutdown { - zfs_pool: ctx.zfs_pool_name.clone(), + datadir: ctx.datadir.clone(), + disk_guid: Some(ctx.disk_guid.clone()), restart: false, db_handle: Some(Arc::new(db)), })) @@ -93,7 +98,8 @@ pub async fn restart(#[context] ctx: RpcContext) -> Result<(), Error> { .await; ctx.shutdown .send(Some(Shutdown { - zfs_pool: ctx.zfs_pool_name.clone(), + datadir: ctx.datadir.clone(), + disk_guid: Some(ctx.disk_guid.clone()), restart: true, db_handle: Some(Arc::new(db)), })) diff --git a/build/initialization.sh b/build/initialization.sh index cfff30fb7..f8e2604ed 100755 --- a/build/initialization.sh +++ b/build/initialization.sh @@ -14,7 +14,6 @@ apt install -y \ avahi-daemon \ iotop \ bmon \ - zfsutils-linux \ exfat-utils \ sqlite3 \ wireless-tools \ @@ -37,7 +36,6 @@ touch /root/.docker/config.json docker run --privileged --rm tonistiigi/binfmt --install all docker network create -d bridge --subnet 172.18.0.1/16 start9 || true -echo '{ "storage-driver": "zfs" }' > /etc/docker/daemon.json mkdir -p /etc/embassy hostnamectl set-hostname "embassy" systemctl enable embassyd.service embassy-init.service