diff --git a/src/alma.rs b/src/alma.rs deleted file mode 100644 index c5afda5..0000000 --- a/src/alma.rs +++ /dev/null @@ -1,54 +0,0 @@ -use super::block::BlockDevice; -use super::cryptsetup::EncryptedDevice; -use super::error::{Error, ErrorKind}; -use super::mountstack::{Filesystem, MountStack}; -use failure::ResultExt; -use log::{debug, info}; -use std::fs; -use std::path::{Path, PathBuf}; - -pub struct ALMA<'a> { - block: BlockDevice, - encrypted_root: Option>, -} - -impl<'a> ALMA<'a> { - pub fn new(block: BlockDevice, encrypted_root: Option>) -> Self { - Self { - block, - encrypted_root, - } - } - - pub fn mount<'b>(&self, path: &'b Path) -> Result, Error> { - let mut mount_stack = MountStack::new(); - - let root_device = if let Some(encrypted_root) = &self.encrypted_root { - PathBuf::from(encrypted_root.path()) - } else { - self.block.partition_device_path(3)? - }; - debug!("Root partition: {}", root_device.display()); - - info!("Mounting filesystems to {}", path.display()); - mount_stack - .mount(&root_device, path, Filesystem::Ext4, None) - .context(ErrorKind::Mounting)?; - - let boot_point = path.join("boot"); - if !boot_point.exists() { - fs::create_dir(&boot_point).context(ErrorKind::CreateBoot)?; - } - - mount_stack - .mount( - &self.block.partition_device_path(1)?, - boot_point, - Filesystem::Vfat, - None, - ) - .context(ErrorKind::Mounting)?; - - Ok(mount_stack) - } -} diff --git a/src/cryptsetup.rs b/src/cryptsetup.rs deleted file mode 100644 index e13ed8c..0000000 --- a/src/cryptsetup.rs +++ /dev/null @@ -1,68 +0,0 @@ -use super::error::{Error, ErrorKind}; -use super::process::CommandExt; -use super::tool::Tool; -use log::{debug, warn}; -use std::path::{Path, PathBuf}; - -pub struct EncryptedDevice<'a> { - cryptsetup: &'a Tool, - name: &'a str, - path: PathBuf, -} - -impl<'a> EncryptedDevice<'a> { - pub fn prepare(cryptsetup: &Tool, device: &Path) -> Result<(), Error> { - debug!("Preparing encrypted device in {}", device.display()); - cryptsetup - .execute() - .arg("luksFormat") - .arg("-q") - .arg(device) - .run(ErrorKind::LuksSetup)?; - - Ok(()) - } - - pub fn open( - cryptsetup: &'a Tool, - device: &Path, - name: &'a str, - ) -> Result, Error> { - debug!("Opening encrypted device {} as {}", device.display(), name); - cryptsetup - .execute() - .arg("open") - .arg(device) - .arg(name) - .run(ErrorKind::LuksOpen)?; - - Ok(Self { - cryptsetup, - name, - path: PathBuf::from("/dev/mapper").join(name), - }) - } - - fn _close(&mut self) -> Result<(), Error> { - debug!("Closing encrypted device {}", self.name); - self.cryptsetup - .execute() - .arg("close") - .arg(self.name) - .run(ErrorKind::LuksClose)?; - - Ok(()) - } - - pub fn path(&self) -> &Path { - &self.path - } -} - -impl<'a> Drop for EncryptedDevice<'a> { - fn drop(&mut self) { - if self._close().is_err() { - warn!("Error closing {}", self.name); - } - } -} diff --git a/src/main.rs b/src/main.rs index a0a7eb3..ff2096e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,11 @@ -mod alma; -mod block; -mod cryptsetup; mod error; -mod mountstack; mod process; +mod storage; mod tool; -use crate::alma::ALMA; -use crate::cryptsetup::EncryptedDevice; use crate::error::*; use crate::process::CommandExt; +use crate::storage::*; use crate::tool::Tool; use failure::{Fail, ResultExt}; use log::{debug, error, info, warn}; @@ -17,7 +13,7 @@ use nix::sys::signal; use simplelog::*; use std::fs; use std::io::Write; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process::exit; use std::thread; use std::time::Duration; @@ -89,6 +85,34 @@ struct ChrootCommand { command: Vec, } +fn mount<'a>( + mount_path: &Path, + boot_filesystem: &'a Filesystem, + root_filesystem: &'a Filesystem, +) -> Result, Error> { + let mut mount_stack = MountStack::new(); + debug!( + "Root partition: {}", + root_filesystem.block().path().display() + ); + + info!("Mounting filesystems to {}", mount_path.display()); + mount_stack + .mount(&root_filesystem, mount_path.into(), None) + .context(ErrorKind::Mounting)?; + + let boot_point = mount_path.join("boot"); + if !boot_point.exists() { + fs::create_dir(&boot_point).context(ErrorKind::CreateBoot)?; + } + + mount_stack + .mount(&boot_filesystem, boot_point, None) + .context(ErrorKind::Mounting)?; + + Ok(mount_stack) +} + fn fix_fstab(fstab: &str) -> String { fstab .lines() @@ -115,11 +139,9 @@ fn create(command: CreateCommand) -> Result<(), Error> { None }; - let block_device = block::BlockDevice::from_path(command.block_device)?; - + let storage_device = storage::StorageDevice::from_path(command.block_device)?; let mount_point = tempdir().context(ErrorKind::TmpDirError)?; - - let disk_path = block_device.device_path(); + let disk_path = storage_device.path(); info!("Partitioning the block device"); debug!("{:?}", disk_path); @@ -141,38 +163,31 @@ fn create(command: CreateCommand) -> Result<(), Error> { thread::sleep(Duration::from_millis(1000)); info!("Formatting filesystems"); - let boot_partition = block_device.partition_device_path(1)?; - mkfat - .execute() - .arg("-F32") - .arg(&boot_partition) - .run(ErrorKind::Formatting)?; + let boot_partition = storage_device.get_partition(1)?; + let boot_filesystem = Filesystem::format(&boot_partition, FilesystemType::Vfat, &mkfat)?; - let root_partition = block_device.partition_device_path(3)?; + let root_partition_base = storage_device.get_partition(3)?; let encrypted_root = if let Some(cryptsetup) = &cryptsetup { info!("Encrypting the root filesystem"); - EncryptedDevice::prepare(&cryptsetup, &root_partition)?; + EncryptedDevice::prepare(&cryptsetup, &root_partition_base)?; Some(EncryptedDevice::open( cryptsetup, - &root_partition, - "alma_root", + &root_partition_base, + "alma_root".into(), )?) } else { None }; - mkext4 - .execute() - .arg("-F") - .arg(if let Some(device) = &encrypted_root { - device.path() - } else { - &root_partition - }) - .run(ErrorKind::Formatting)?; + let root_partition = if let Some(e) = encrypted_root.as_ref() { + e as &BlockDevice + } else { + &root_partition_base as &BlockDevice + }; - let alma = ALMA::new(block_device, encrypted_root); - let mount_stack = alma.mount(mount_point.path())?; + let root_filesystem = Filesystem::format(root_partition, FilesystemType::Ext4, &mkext4)?; + + let mount_stack = mount(mount_point.path(), &boot_filesystem, &root_filesystem)?; info!("Bootstrapping system"); pacstrap @@ -246,7 +261,7 @@ fn create(command: CreateCommand) -> Result<(), Error> { let uuid = blkid .unwrap() .execute() - .arg(root_partition) + .arg(root_partition.path()) .args(&["-o", "value", "-s", "UUID"]) .run_text_output(ErrorKind::Partitioning)?; let trimmed = uuid.trim(); @@ -295,22 +310,31 @@ fn chroot(command: ChrootCommand) -> Result<(), Error> { None }; - let block_device = block::BlockDevice::from_path(command.block_device)?; - + let storage_device = storage::StorageDevice::from_path(command.block_device)?; let mount_point = tempdir().context(ErrorKind::TmpDirError)?; - let root_partition = block_device.partition_device_path(3)?; + + let boot_partition = storage_device.get_partition(1)?; + let boot_filesystem = Filesystem::from_partition(&boot_partition, FilesystemType::Vfat); + + let root_partition_base = storage_device.get_partition(3)?; let encrypted_root = if let Some(cryptsetup) = &cryptsetup { Some(EncryptedDevice::open( cryptsetup, - &root_partition, - "alma_root", + &root_partition_base, + "alma_root".into(), )?) } else { None }; - let alma = ALMA::new(block_device, encrypted_root); - let mount_stack = alma.mount(mount_point.path())?; + let root_partition = if let Some(e) = encrypted_root.as_ref() { + e as &BlockDevice + } else { + &root_partition_base as &BlockDevice + }; + let root_filesystem = Filesystem::from_partition(root_partition, FilesystemType::Ext4); + + let mount_stack = mount(mount_point.path(), &boot_filesystem, &root_filesystem)?; arch_chroot .execute() diff --git a/src/storage/crypt.rs b/src/storage/crypt.rs new file mode 100644 index 0000000..3270b00 --- /dev/null +++ b/src/storage/crypt.rs @@ -0,0 +1,80 @@ +use super::markers::BlockDevice; +use crate::error::{Error, ErrorKind}; +use crate::process::CommandExt; +use crate::tool::Tool; +use log::{debug, warn}; +use std::marker::PhantomData; +use std::path::{Path, PathBuf}; + +#[derive(Debug)] +pub struct EncryptedDevice<'t, 'o> { + cryptsetup: &'t Tool, + name: String, + path: PathBuf, + origin: PhantomData<&'o BlockDevice>, +} + +impl<'t, 'o> EncryptedDevice<'t, 'o> { + pub fn prepare(cryptsetup: &Tool, device: &BlockDevice) -> Result<(), Error> { + debug!("Preparing encrypted device in {}", device.path().display()); + cryptsetup + .execute() + .arg("luksFormat") + .arg("-q") + .arg(device.path()) + .run(ErrorKind::LuksSetup)?; + + Ok(()) + } + + pub fn open( + cryptsetup: &'t Tool, + device: &'o BlockDevice, + name: String, + ) -> Result, Error> { + debug!( + "Opening encrypted device {} as {}", + device.path().display(), + name + ); + cryptsetup + .execute() + .arg("open") + .arg(device.path()) + .arg(&name) + .run(ErrorKind::LuksOpen)?; + + let path = PathBuf::from("/dev/mapper").join(&name); + Ok(Self { + cryptsetup, + name, + path, + origin: PhantomData, + }) + } + + fn _close(&mut self) -> Result<(), Error> { + debug!("Closing encrypted device {}", self.name); + self.cryptsetup + .execute() + .arg("close") + .arg(&self.name) + .run(ErrorKind::LuksClose)?; + + Ok(()) + } +} + +impl<'t, 'o> Drop for EncryptedDevice<'t, 'o> { + fn drop(&mut self) { + if self._close().is_err() { + warn!("Error closing {}", self.name); + } + } +} + +impl<'t, 'o> BlockDevice for EncryptedDevice<'t, 'o> { + fn path(&self) -> &Path { + &self.path + } +} diff --git a/src/storage/filesystem.rs b/src/storage/filesystem.rs new file mode 100644 index 0000000..486ff0e --- /dev/null +++ b/src/storage/filesystem.rs @@ -0,0 +1,57 @@ +use super::markers::BlockDevice; +use crate::{ + error::{Error, ErrorKind}, + process::CommandExt, + tool::Tool, +}; + +#[derive(Debug, Clone, Copy)] +pub enum FilesystemType { + Ext4, + Vfat, +} + +impl FilesystemType { + pub fn to_mount_type(self) -> &'static str { + match self { + FilesystemType::Ext4 => "ext4", + FilesystemType::Vfat => "vfat", + } + } +} + +#[derive(Debug)] +pub struct Filesystem<'a> { + fs_type: FilesystemType, + block: &'a BlockDevice, +} + +impl<'a> Filesystem<'a> { + pub fn format( + block: &'a BlockDevice, + fs_type: FilesystemType, + mkfs: &Tool, + ) -> Result { + let mut command = mkfs.execute(); + match fs_type { + FilesystemType::Ext4 => command.arg("-F").arg(block.path()), + FilesystemType::Vfat => command.arg("-F32").arg(block.path()), + }; + + command.run(ErrorKind::Formatting)?; + + Ok(Self { fs_type, block }) + } + + pub fn from_partition(block: &'a BlockDevice, fs_type: FilesystemType) -> Self { + Self { fs_type, block } + } + + pub fn block(&self) -> &BlockDevice { + self.block + } + + pub fn fs_type(&self) -> FilesystemType { + self.fs_type + } +} diff --git a/src/storage/markers.rs b/src/storage/markers.rs new file mode 100644 index 0000000..8e18a7c --- /dev/null +++ b/src/storage/markers.rs @@ -0,0 +1,7 @@ +use std::path::Path; + +pub trait BlockDevice: std::fmt::Debug { + fn path(&self) -> &Path; +} + +pub trait Origin {} diff --git a/src/storage/mod.rs b/src/storage/mod.rs new file mode 100644 index 0000000..8f52bf6 --- /dev/null +++ b/src/storage/mod.rs @@ -0,0 +1,12 @@ +mod crypt; +mod filesystem; +mod markers; +mod mount_stack; +mod partition; +mod storage_device; + +pub use crypt::EncryptedDevice; +pub use filesystem::{Filesystem, FilesystemType}; +pub use markers::BlockDevice; +pub use mount_stack::MountStack; +pub use storage_device::StorageDevice; diff --git a/src/mountstack.rs b/src/storage/mount_stack.rs similarity index 60% rename from src/mountstack.rs rename to src/storage/mount_stack.rs index 35639fd..c77e0e2 100644 --- a/src/mountstack.rs +++ b/src/storage/mount_stack.rs @@ -1,52 +1,38 @@ -use super::error::{Error, ErrorKind}; +use super::Filesystem; +use crate::error::{Error, ErrorKind}; use failure::Fail; use log::{debug, warn}; use nix; use nix::mount::{mount, umount, MsFlags}; -use std::borrow::Cow; -use std::path::Path; - -#[derive(Debug, Clone, Copy)] -pub enum Filesystem { - Ext4, - Vfat, -} - -impl Filesystem { - fn to_type(self) -> &'static str { - match self { - Filesystem::Ext4 => "ext4", - Filesystem::Vfat => "vfat", - } - } -} +use std::marker::PhantomData; +use std::path::PathBuf; pub struct MountStack<'a> { - targets: Vec>, + targets: Vec, + filesystems: PhantomData>, } impl<'a> MountStack<'a> { pub fn new() -> Self { MountStack { targets: Vec::new(), + filesystems: PhantomData, } } #[must_use] - pub fn mount>>( + pub fn mount( &mut self, - source: &Path, - target: T, - filesystem: Filesystem, + filesystem: &'a Filesystem, + target: PathBuf, options: Option<&str>, ) -> nix::Result<()> { - let target = target.into(); - - debug!("Mounting {:?} ({:?}) to {:?}", source, filesystem, target); + let source = filesystem.block().path(); + debug!("Mounting {:?} to {:?}", filesystem, target); mount( Some(source), - target.as_ref(), - Some(filesystem.to_type()), + &target, + Some(filesystem.fs_type().to_mount_type()), MsFlags::MS_NOATIME, options, )?; @@ -59,7 +45,7 @@ impl<'a> MountStack<'a> { while let Some(target) = self.targets.pop() { debug!("Unmounting {}", target.display()); - if let Err(e) = umount(target.as_ref()) { + if let Err(e) = umount(&target) { warn!("Unable to umount {}: {}", target.display(), e); result = Err(Error::from(e.context(ErrorKind::UmountFailure))); }; diff --git a/src/storage/partition.rs b/src/storage/partition.rs new file mode 100644 index 0000000..38a0c13 --- /dev/null +++ b/src/storage/partition.rs @@ -0,0 +1,24 @@ +use super::markers::{BlockDevice, Origin}; +use std::marker::PhantomData; +use std::path::{Path, PathBuf}; + +#[derive(Debug)] +pub struct Partition<'a> { + path: PathBuf, + origin: PhantomData<&'a Origin>, +} + +impl<'a> Partition<'a> { + pub fn new(path: PathBuf) -> Self { + Self { + path, + origin: PhantomData, + } + } +} + +impl<'a> BlockDevice for Partition<'a> { + fn path(&self) -> &Path { + &self.path + } +} diff --git a/src/block.rs b/src/storage/storage_device.rs similarity index 71% rename from src/block.rs rename to src/storage/storage_device.rs index dc9503b..c62d72e 100644 --- a/src/block.rs +++ b/src/storage/storage_device.rs @@ -1,15 +1,18 @@ -use super::error::{Error, ErrorKind}; +use super::markers::{BlockDevice, Origin}; +use super::partition::Partition; +use crate::error::{Error, ErrorKind}; use failure::ResultExt; use log::debug; use std::fs::read_to_string; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; #[derive(Debug)] -pub struct BlockDevice { +pub struct StorageDevice { name: String, + path: PathBuf, } -impl BlockDevice { +impl StorageDevice { pub fn from_path(path: PathBuf) -> Result { let real_path = path.canonicalize().context(ErrorKind::DeviceQuery)?; let device_name = real_path @@ -24,8 +27,11 @@ impl BlockDevice { ); drop(path); - let _self = Self { name: device_name }; - if !(_self.is_removable()? || _self.is_loop_device()) { + let _self = Self { + name: device_name, + path: real_path, + }; + if !(_self.is_removable_device()? || _self.is_loop_device()) { return Err(ErrorKind::DangerousDevice)?; } @@ -38,7 +44,7 @@ impl BlockDevice { path } - fn is_removable(&self) -> Result { + fn is_removable_device(&self) -> Result { let mut path = self.sys_path(); path.push("removable"); @@ -55,13 +61,7 @@ impl BlockDevice { path.exists() } - pub fn device_path(&self) -> PathBuf { - let mut path = PathBuf::from("/dev"); - path.push(self.name.clone()); - path - } - - pub fn partition_device_path(&self, index: u8) -> Result { + pub fn get_partition(&self, index: u8) -> Result { let name = if self.name.chars().rev().next().unwrap().is_digit(10) { format!("{}p{}", self.name, index) } else { @@ -74,6 +74,14 @@ impl BlockDevice { if !path.exists() { return Err(ErrorKind::NoSuchPartition(index).into()); } - Ok(path) + Ok(Partition::new::(path)) } } + +impl BlockDevice for StorageDevice { + fn path(&self) -> &Path { + &self.path + } +} + +impl Origin for StorageDevice {}