mirror of
https://github.com/philmmanjaro/alma.git
synced 2025-12-06 19:29:21 +01:00
Loop device support
This commit is contained in:
19
src/args.rs
19
src/args.rs
@@ -1,6 +1,11 @@
|
||||
use byte_unit::Byte;
|
||||
use std::path::PathBuf;
|
||||
use structopt::StructOpt;
|
||||
|
||||
fn parse_bytes(src: &str) -> Result<Byte, &'static str> {
|
||||
Byte::from_string(src).map_err(|_| "Invalid image size")
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
#[structopt(name = "alma", about = "Arch Linux Mobile Appliance")]
|
||||
pub struct App {
|
||||
@@ -26,9 +31,9 @@ pub enum Command {
|
||||
|
||||
#[derive(StructOpt)]
|
||||
pub struct CreateCommand {
|
||||
/// Path starting with /dev/disk/by-id for the USB drive
|
||||
/// Either a path to a removable block device or a nonexiting file if --image is specified
|
||||
#[structopt(parse(from_os_str))]
|
||||
pub block_device: PathBuf,
|
||||
pub path: PathBuf,
|
||||
|
||||
/// Additional pacakges to install
|
||||
#[structopt(short = "p", long = "extra-packages", value_name = "package")]
|
||||
@@ -43,8 +48,16 @@ pub struct CreateCommand {
|
||||
pub encrypted_root: bool,
|
||||
|
||||
/// Path to preset files
|
||||
#[structopt(long = "presets")]
|
||||
#[structopt(long = "presets", value_name = "preset")]
|
||||
pub presets: Vec<PathBuf>,
|
||||
|
||||
/// Create an image with a certain size in the given path instead of using an actual block device
|
||||
#[structopt(
|
||||
long = "image",
|
||||
parse(try_from_str = "parse_bytes"),
|
||||
value_name = "size"
|
||||
)]
|
||||
pub image: Option<Byte>,
|
||||
}
|
||||
|
||||
#[derive(StructOpt)]
|
||||
|
||||
@@ -82,6 +82,12 @@ pub enum ErrorKind {
|
||||
|
||||
#[fail(display = "Error executing preset script")]
|
||||
PresetScript,
|
||||
|
||||
#[fail(display = "Error creating the image")]
|
||||
Image,
|
||||
|
||||
#[fail(display = "Error setting up a loop device: {}", _0)]
|
||||
Losetup(String),
|
||||
}
|
||||
|
||||
impl Fail for Error {
|
||||
|
||||
34
src/main.rs
34
src/main.rs
@@ -10,6 +10,7 @@ use crate::error::*;
|
||||
use crate::process::CommandExt;
|
||||
use crate::storage::*;
|
||||
use crate::tool::Tool;
|
||||
use byte_unit::Byte;
|
||||
use failure::{Fail, ResultExt};
|
||||
use log::{debug, error, info, warn};
|
||||
use nix::sys::signal;
|
||||
@@ -75,6 +76,21 @@ fn fix_fstab(fstab: &str) -> String {
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
fn create_image(path: &Path, size: Byte) -> Result<LoopDevice, Error> {
|
||||
{
|
||||
let file = fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(path)
|
||||
.context(ErrorKind::Image)?;
|
||||
|
||||
file.set_len(size.get_bytes() as u64)
|
||||
.context(ErrorKind::Image)?;
|
||||
}
|
||||
|
||||
LoopDevice::create(path)
|
||||
}
|
||||
|
||||
fn create(command: CreateCommand) -> Result<(), Error> {
|
||||
let presets = presets::Presets::load(&command.presets)?;
|
||||
|
||||
@@ -95,7 +111,21 @@ fn create(command: CreateCommand) -> Result<(), Error> {
|
||||
None
|
||||
};
|
||||
|
||||
let storage_device = storage::StorageDevice::from_path(command.block_device)?;
|
||||
let image_loop = if let Some(size) = command.image {
|
||||
Some(create_image(&command.path, size)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let storage_device = storage::StorageDevice::from_path(
|
||||
image_loop
|
||||
.as_ref()
|
||||
.map(|loop_dev| {
|
||||
info!("Using loop device at {}", loop_dev.path().display());
|
||||
loop_dev.path()
|
||||
})
|
||||
.unwrap_or(&command.path),
|
||||
)?;
|
||||
let mount_point = tempdir().context(ErrorKind::TmpDirError)?;
|
||||
let disk_path = storage_device.path();
|
||||
|
||||
@@ -300,7 +330,7 @@ fn chroot(command: ChrootCommand) -> Result<(), Error> {
|
||||
None
|
||||
};
|
||||
|
||||
let storage_device = storage::StorageDevice::from_path(command.block_device)?;
|
||||
let storage_device = storage::StorageDevice::from_path(&command.block_device)?;
|
||||
let mount_point = tempdir().context(ErrorKind::TmpDirError)?;
|
||||
|
||||
let boot_partition = storage_device.get_partition(BOOT_PARTITION_INDEX)?;
|
||||
|
||||
52
src/storage/loop_device.rs
Normal file
52
src/storage/loop_device.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
use crate::error::{Error, ErrorKind};
|
||||
use crate::tool::Tool;
|
||||
use failure::ResultExt;
|
||||
use log::info;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct LoopDevice {
|
||||
path: PathBuf,
|
||||
losetup: Tool,
|
||||
}
|
||||
|
||||
impl LoopDevice {
|
||||
pub fn create(file: &Path) -> Result<Self, Error> {
|
||||
let losetup = Tool::find("losetup")?;
|
||||
let output = losetup
|
||||
.execute()
|
||||
.args(&["--find", "-P", "--show"])
|
||||
.arg(file)
|
||||
.output()
|
||||
.context(ErrorKind::Image)?;
|
||||
|
||||
if !output.status.success() {
|
||||
Err(ErrorKind::Losetup(
|
||||
String::from_utf8(output.stderr).unwrap(),
|
||||
))?
|
||||
}
|
||||
|
||||
Ok(LoopDevice {
|
||||
path: PathBuf::from(String::from_utf8(output.stdout).unwrap().trim()),
|
||||
losetup,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn path(&self) -> &Path {
|
||||
&self.path
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for LoopDevice {
|
||||
fn drop(&mut self) {
|
||||
info!("Detaching loop device {}", self.path.display());
|
||||
self.losetup
|
||||
.execute()
|
||||
.arg("-d")
|
||||
.arg(&self.path)
|
||||
.spawn()
|
||||
.unwrap()
|
||||
.wait()
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
mod crypt;
|
||||
mod filesystem;
|
||||
mod loop_device;
|
||||
mod markers;
|
||||
mod mount_stack;
|
||||
mod partition;
|
||||
@@ -7,6 +8,7 @@ mod storage_device;
|
||||
|
||||
pub use crypt::EncryptedDevice;
|
||||
pub use filesystem::{Filesystem, FilesystemType};
|
||||
pub use loop_device::LoopDevice;
|
||||
pub use markers::BlockDevice;
|
||||
pub use mount_stack::MountStack;
|
||||
pub use storage_device::StorageDevice;
|
||||
|
||||
@@ -4,16 +4,18 @@ use crate::error::{Error, ErrorKind};
|
||||
use failure::ResultExt;
|
||||
use log::debug;
|
||||
use std::fs::read_to_string;
|
||||
use std::marker::PhantomData;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StorageDevice {
|
||||
pub struct StorageDevice<'a> {
|
||||
name: String,
|
||||
path: PathBuf,
|
||||
origin: PhantomData<&'a Origin>,
|
||||
}
|
||||
|
||||
impl StorageDevice {
|
||||
pub fn from_path(path: PathBuf) -> Result<Self, Error> {
|
||||
impl<'a> StorageDevice<'a> {
|
||||
pub fn from_path(path: &'a Path) -> Result<Self, Error> {
|
||||
let real_path = path.canonicalize().context(ErrorKind::DeviceQuery)?;
|
||||
let device_name = real_path
|
||||
.file_name()
|
||||
@@ -30,6 +32,7 @@ impl StorageDevice {
|
||||
let _self = Self {
|
||||
name: device_name,
|
||||
path: real_path,
|
||||
origin: PhantomData,
|
||||
};
|
||||
if !(_self.is_removable_device()? || _self.is_loop_device()) {
|
||||
return Err(ErrorKind::DangerousDevice)?;
|
||||
@@ -78,10 +81,10 @@ impl StorageDevice {
|
||||
}
|
||||
}
|
||||
|
||||
impl BlockDevice for StorageDevice {
|
||||
impl<'a> BlockDevice for StorageDevice<'a> {
|
||||
fn path(&self) -> &Path {
|
||||
&self.path
|
||||
}
|
||||
}
|
||||
|
||||
impl Origin for StorageDevice {}
|
||||
impl<'a> Origin for StorageDevice<'a> {}
|
||||
|
||||
Reference in New Issue
Block a user