Files
start-os/appmgr/src/disk/main.rs
2022-01-21 20:35:52 -07:00

241 lines
6.4 KiB
Rust

use std::path::Path;
use tokio::process::Command;
use tracing::instrument;
use crate::util::Invoke;
use crate::{Error, ResultExt};
pub const PASSWORD_PATH: &'static str = "/etc/embassy/password";
pub const DEFAULT_PASSWORD: &'static str = "password";
// TODO: use IncorrectDisk / DiskNotAvailable / DiskCorrupted
#[instrument(skip(disks))]
pub async fn create<I: IntoIterator<Item = P>, P: AsRef<Path>>(
pool_name: &str,
disks: I,
password: &str,
) -> Result<String, Error> {
let guid = create_pool(pool_name, disks).await?;
create_fs(pool_name, password).await?;
export(pool_name).await?;
Ok(guid)
}
#[instrument(skip(datadir))]
pub async fn load<P: AsRef<Path>>(
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<I: IntoIterator<Item = P>, P: AsRef<Path>>(
pool_name: &str,
disks: I,
) -> Result<String, Error> {
let mut cmd = Command::new("zpool");
cmd.arg("create").arg("-f").arg(pool_name);
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())
}
#[instrument]
pub async fn create_fs(pool_name: &str, 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)
.await?;
Command::new("zfs")
.arg("create")
.arg("-o")
.arg("reservation=5G")
.arg(format!("{}/updates", pool_name))
.invoke(crate::ErrorKind::Zfs)
.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)
.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)
.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?;
Ok(())
}
#[instrument(skip(datadir))]
pub async fn mount<P: AsRef<Path>>(
pool_name: &str,
datadir: P,
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)
.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)
.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(())
}