SHA256
Sawing progress
This commit is contained in:
Generated
+451
-548
File diff suppressed because it is too large
Load Diff
+4
-1
@@ -29,6 +29,9 @@ lto = true
|
|||||||
debug = false
|
debug = false
|
||||||
incremental = false
|
incremental = false
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
opt-level = 3
|
opt-level = "s"
|
||||||
strip = true
|
strip = true
|
||||||
|
|
||||||
|
|
||||||
|
[registry]
|
||||||
|
global-min-publish-age = "14 days"
|
||||||
|
|||||||
+10
-191
@@ -108,9 +108,12 @@ impl BlkDev {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_sys(name: &str) -> Option<Self> {
|
pub fn from_sys(name: &str) -> Option<Self> {
|
||||||
Self::list_sys_blk_dev().ok()?.iter().find(|v| v.name == name).cloned()
|
Self::list_sys_blk_dev()
|
||||||
|
.ok()?
|
||||||
|
.iter()
|
||||||
|
.find(|v| v.name == name)
|
||||||
|
.cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
@@ -231,6 +234,10 @@ impl PartInfo {
|
|||||||
role: None,
|
role: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn partlabel_dev(&self) -> Option<String> {
|
||||||
|
self.gpt_label.as_ref().and_then(|l| Some(format!("/dev/disk/by-partlabel/{}", l)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// getting list of device partitions
|
/// getting list of device partitions
|
||||||
@@ -416,7 +423,7 @@ impl PartLayout {
|
|||||||
FSType::VFAT,
|
FSType::VFAT,
|
||||||
Some("bee-efi".into()),
|
Some("bee-efi".into()),
|
||||||
Some("EFI".into()),
|
Some("EFI".into()),
|
||||||
Some("/efi".into()),
|
Some("/beefi".into()),
|
||||||
Some(PartRole::EFI),
|
Some(PartRole::EFI),
|
||||||
)
|
)
|
||||||
.add_part_reserve(
|
.add_part_reserve(
|
||||||
@@ -444,194 +451,6 @@ impl PartLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes parted command with given arguments
|
|
||||||
fn exec_parted(args: &Vec<&str>) -> Result<String, String> {
|
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
match Command::new("parted").args(args).output() {
|
|
||||||
Err(ex) => Err(ex.to_string()),
|
|
||||||
Ok(out) => {
|
|
||||||
if out.status.success() {
|
|
||||||
String::from_utf8(out.stdout).map_err(|ex| ex.to_string())
|
|
||||||
} else {
|
|
||||||
Err(out.status.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parted_get_last_part_num(dev: &BlkDev) -> Result<u32, String> {
|
|
||||||
// parted -m /dev/loop0 print
|
|
||||||
match exec_parted(&vec!["-m", dev.full_name().as_str(), "print"]) {
|
|
||||||
Ok(res) => {
|
|
||||||
match res.lines().last().and_then(|last_line| {
|
|
||||||
last_line
|
|
||||||
.split_once(':')
|
|
||||||
.and_then(|(first_part, _)| u32::from_str_radix(first_part, 10).ok())
|
|
||||||
}) {
|
|
||||||
Some(n) => Ok(n),
|
|
||||||
None => Err("Error parsing parted output".to_string()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(ex) => Err(ex),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// init gpt partition table
|
|
||||||
/// parted --script $install_device mklabel gpt
|
|
||||||
/// Its blocking function
|
|
||||||
pub fn parted_init_gpt_part_table(dev: &BlkDev) -> Result<(), String> {
|
|
||||||
exec_parted(&vec![
|
|
||||||
"--script",
|
|
||||||
dev.full_name().as_str(),
|
|
||||||
"mklabel",
|
|
||||||
"gpt",
|
|
||||||
])
|
|
||||||
.map(|_| ())
|
|
||||||
}
|
|
||||||
|
|
||||||
const LINUX_ROOT_X86_64_TYPE: &str = "4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709";
|
|
||||||
|
|
||||||
/// creating partition from PartInfo specification
|
|
||||||
/// It assumes that we add partitions sequentially to empty space (for part attributes settings)
|
|
||||||
/// Otherwise can produce bonkers layout
|
|
||||||
pub fn parted_mkpart(dev: &BlkDev, part: &PartInfo) -> Result<(), String> {
|
|
||||||
let part_end = part.start + part.size;
|
|
||||||
|
|
||||||
// gpt label should be in double qutes
|
|
||||||
let gpt_label = if let Some(s) = &part.gpt_label {
|
|
||||||
format!("\"{}\"", s)
|
|
||||||
} else {
|
|
||||||
"\"\"".to_string()
|
|
||||||
};
|
|
||||||
// creating partition
|
|
||||||
exec_parted(&vec![
|
|
||||||
"--script",
|
|
||||||
dev.full_name().as_str(),
|
|
||||||
"mkpart",
|
|
||||||
gpt_label.as_str(),
|
|
||||||
part.fs_type.to_parted_str(),
|
|
||||||
part.start.to_parted_bytes_str().as_str(),
|
|
||||||
part_end.to_parted_bytes_str().as_str(),
|
|
||||||
])?;
|
|
||||||
|
|
||||||
// set partition options depending on its role
|
|
||||||
if let Some(role) = &part.role {
|
|
||||||
// getting part number of last created partition (hopefully)
|
|
||||||
let part_id = parted_get_last_part_num(dev)?;
|
|
||||||
match role {
|
|
||||||
PartRole::EFI => {
|
|
||||||
exec_parted(&vec![
|
|
||||||
"--script",
|
|
||||||
dev.full_name().as_str(),
|
|
||||||
"set",
|
|
||||||
&part_id.to_string(),
|
|
||||||
"esp",
|
|
||||||
"on",
|
|
||||||
])?;
|
|
||||||
}
|
|
||||||
// parted --script $install_device type 3 4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709
|
|
||||||
PartRole::ROOT => {
|
|
||||||
exec_parted(&vec![
|
|
||||||
"--script",
|
|
||||||
dev.full_name().as_str(),
|
|
||||||
"type",
|
|
||||||
&part_id.to_string(),
|
|
||||||
LINUX_ROOT_X86_64_TYPE,
|
|
||||||
])?;
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// creating single drive bcachefs volume
|
|
||||||
pub fn bcachefs_format(
|
|
||||||
dev: &BlkDev,
|
|
||||||
compression: Option<String>,
|
|
||||||
password: Option<String>,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
// sudo bcachefs format --compression=lz4 --background_compression=lz4 --encrypted /dev/loop0
|
|
||||||
// --data_checksum=none - do not make sense on single dev volumes
|
|
||||||
// sudo bcachefs format --force --compression=lz4 --background_compression=lz4 --encrypted --passphrase_file=./test.pass /dev/loop0
|
|
||||||
use std::fs;
|
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
let mut arg = vec!["format", "--force", "--data_checksum=none"];
|
|
||||||
// if compression algorythm specified
|
|
||||||
if let Some(compresss_alg) = &compression {
|
|
||||||
arg.push(compresss_alg.as_str());
|
|
||||||
}
|
|
||||||
// if we want encrypted volume
|
|
||||||
if let Some(passw) = password {
|
|
||||||
// creating tmp file with password
|
|
||||||
fs::write("/tmp/bzzpsspass.txt", &passw).map_err(|ex| ex.to_string())?;
|
|
||||||
arg.push("--encrypted");
|
|
||||||
arg.push("--passphrase_file=/tmp/bzzpsspass.txt");
|
|
||||||
}
|
|
||||||
|
|
||||||
// add dev name at the end
|
|
||||||
let dev_name = dev.full_name();
|
|
||||||
arg.push(dev_name.as_str());
|
|
||||||
|
|
||||||
let out = Command::new("bcachefs")
|
|
||||||
.args(arg)
|
|
||||||
.output()
|
|
||||||
.map_err(|ex| ex.to_string())?;
|
|
||||||
|
|
||||||
// ignore any errors while deleting pass tmp file
|
|
||||||
let _ = fs::remove_file("/tmp/bzzpsspass.txt");
|
|
||||||
|
|
||||||
if out.status.success() {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(String::from_utf8(out.stderr).map_err(|ex| ex.to_string())?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// unlok encrypted bcache volume
|
|
||||||
pub fn bcachefs_unlock(dev: &BlkDev, pass: String) -> Result<(), String> {
|
|
||||||
use std::fs;
|
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
// bcachefs unlock --file=/tmp/bzzpsspass.txt /dev/loop0
|
|
||||||
let dev_name = dev.full_name();
|
|
||||||
let arg = vec!["unlock", "--file=/tmp/bzzpsspass.txt ", dev_name.as_str()];
|
|
||||||
// creating tmp file with password
|
|
||||||
fs::write("/tmp/bzzpsspass.txt", &pass).map_err(|ex| ex.to_string())?;
|
|
||||||
|
|
||||||
let out = Command::new("bcachefs")
|
|
||||||
.args(arg)
|
|
||||||
.output()
|
|
||||||
.map_err(|ex| ex.to_string())?;
|
|
||||||
// ignore any errors while deleting pass tmp file
|
|
||||||
let _ = fs::remove_file("/tmp/bzzpsspass.txt");
|
|
||||||
|
|
||||||
if out.status.success() {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(String::from_utf8(out.stderr).map_err(|ex| ex.to_string())?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// create subvolume
|
|
||||||
pub fn bcachefs_create_subvolume(vol_path: &str) -> Result<(), String> {
|
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
// sudo bcachefs subvolume create /mnt/test/nyan
|
|
||||||
let out = Command::new("bcachefs")
|
|
||||||
.args(["subvolume", "create", vol_path])
|
|
||||||
.output()
|
|
||||||
.map_err(|ex| ex.to_string())?;
|
|
||||||
if out.status.success() {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(String::from_utf8(out.stderr).map_err(|ex| ex.to_string())?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|||||||
@@ -0,0 +1,308 @@
|
|||||||
|
// <Kira Installer - universal Linux installer.>
|
||||||
|
// Copyright (C) <2026> <Kira Foundation>
|
||||||
|
|
||||||
|
// This program is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// This program is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/*
|
||||||
|
Functions for creating with disk layout
|
||||||
|
*/
|
||||||
|
|
||||||
|
use crate::kira_disk_layout::*;
|
||||||
|
use std::process::Command;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CommandFailed{
|
||||||
|
stderr: Vec<u8>,
|
||||||
|
cmd: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for CommandFailed {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(f, "Command failed! Command: {}; Stderr: {}", self.cmd, String::from_utf8(self.stderr.clone()).unwrap_or_else(|_|{"STDEER IS NOT VALID UTF-8".to_string()}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommandFailed {
|
||||||
|
pub fn new(cmd: &str, args: &Vec<&str>, stderr: Vec<u8>) -> Self {
|
||||||
|
Self {
|
||||||
|
stderr: stderr,
|
||||||
|
cmd: format!("{} {}", cmd, args.join(" ")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ExecCommandError {
|
||||||
|
IoError(std::io::Error),
|
||||||
|
FromUtf8Error(std::string::FromUtf8Error),
|
||||||
|
CommandFailed(CommandFailed),
|
||||||
|
Other(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Implement From for each wrapped error type
|
||||||
|
impl From<std::io::Error> for ExecCommandError {
|
||||||
|
fn from(err: std::io::Error) -> Self {
|
||||||
|
ExecCommandError::IoError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::string::FromUtf8Error> for ExecCommandError {
|
||||||
|
fn from(err: std::string::FromUtf8Error) -> Self {
|
||||||
|
ExecCommandError::FromUtf8Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<CommandFailed> for ExecCommandError {
|
||||||
|
fn from(err: CommandFailed) -> Self {
|
||||||
|
ExecCommandError::CommandFailed(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for ExecCommandError {
|
||||||
|
fn from(err: String) -> Self {
|
||||||
|
ExecCommandError::Other(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for ExecCommandError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
ExecCommandError::IoError(e) => write!(f, "IO Error: {}", e),
|
||||||
|
ExecCommandError::FromUtf8Error(e) => {
|
||||||
|
write!(f, "IO streem to String parse Error: {}", e)
|
||||||
|
}
|
||||||
|
ExecCommandError::CommandFailed(ex) => {
|
||||||
|
write!(f, "{}", ex)
|
||||||
|
}
|
||||||
|
ExecCommandError::Other(msg) => write!(f, "Error: {}", msg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement std::error::Error
|
||||||
|
impl std::error::Error for ExecCommandError {}
|
||||||
|
|
||||||
|
/// exec given sommand with arguments, capturing stderr in case or failure
|
||||||
|
fn exec_command(cmd: &str, args: &Vec<&str>) -> Result<(), ExecCommandError> {
|
||||||
|
let out = Command::new(cmd).args(args).output()?;
|
||||||
|
if out.status.success() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(CommandFailed::new(cmd, args, out.stderr).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// exec given sommand with arguments, returning stdout parsed to UTF-8 string
|
||||||
|
/// capturing stderr in case or failure
|
||||||
|
fn exec_command_out(cmd: &str, args: &Vec<&str>) -> Result<String, ExecCommandError> {
|
||||||
|
let out = Command::new(cmd).args(args).output()?;
|
||||||
|
if out.status.success() {
|
||||||
|
Ok(String::from_utf8(out.stdout)?)
|
||||||
|
} else {
|
||||||
|
Err(CommandFailed::new(cmd, args, out.stderr).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// sync system time with chrony
|
||||||
|
/// dinitctl start chrony
|
||||||
|
pub fn dinit_sync_time_chrony() -> Result<(), ExecCommandError> {
|
||||||
|
exec_command("dinitctl", &vec!["start", "chrony"])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Install base system
|
||||||
|
/// basestrap /mnt base base-devel dinit elogind-dinit
|
||||||
|
pub fn basestrap_dinit(root_dir: &str) -> Result<(), ExecCommandError> {
|
||||||
|
exec_command("basestrap", &vec![root_dir, "base", "base-devel", "dinit", "elogind-dinit"])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Executes parted command with given arguments
|
||||||
|
fn exec_parted(args: &Vec<&str>) -> Result<String, ExecCommandError> {
|
||||||
|
exec_command_out("parted", args)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parted_get_last_part_num(dev: &BlkDev) -> Result<u32, ExecCommandError> {
|
||||||
|
// parted -m /dev/loop0 print
|
||||||
|
let res = exec_parted(&vec!["-m", dev.full_name().as_str(), "print"])?;
|
||||||
|
|
||||||
|
match res.lines().last().and_then(|last_line| {
|
||||||
|
last_line
|
||||||
|
.split_once(':')
|
||||||
|
.and_then(|(first_part, _)| u32::from_str_radix(first_part, 10).ok())
|
||||||
|
}) {
|
||||||
|
Some(n) => Ok(n),
|
||||||
|
None => Err("Error parsing parted output".to_string().into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// init gpt partition table
|
||||||
|
/// parted --script $install_device mklabel gpt
|
||||||
|
/// Its blocking function
|
||||||
|
pub fn parted_init_gpt_part_table(dev: &BlkDev) -> Result<(), ExecCommandError> {
|
||||||
|
exec_parted(&vec![
|
||||||
|
"--script",
|
||||||
|
dev.full_name().as_str(),
|
||||||
|
"mklabel",
|
||||||
|
"gpt",
|
||||||
|
])
|
||||||
|
.map(|_| ())
|
||||||
|
}
|
||||||
|
|
||||||
|
const LINUX_ROOT_X86_64_TYPE: &str = "4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709";
|
||||||
|
|
||||||
|
/// creating partition from PartInfo specification
|
||||||
|
/// It assumes that we add partitions sequentially to empty space (for part attributes settings)
|
||||||
|
/// Otherwise can produce bonkers layout
|
||||||
|
pub fn parted_mkpart(dev: &BlkDev, part: &PartInfo) -> Result<(), ExecCommandError> {
|
||||||
|
let part_end = part.start + part.size;
|
||||||
|
|
||||||
|
// gpt label should be in double qutes
|
||||||
|
let gpt_label = if let Some(s) = &part.gpt_label {
|
||||||
|
format!("\"{}\"", s)
|
||||||
|
} else {
|
||||||
|
"\"\"".to_string()
|
||||||
|
};
|
||||||
|
// creating partition
|
||||||
|
exec_parted(&vec![
|
||||||
|
"--script",
|
||||||
|
dev.full_name().as_str(),
|
||||||
|
"mkpart",
|
||||||
|
gpt_label.as_str(),
|
||||||
|
part.fs_type.to_parted_str(),
|
||||||
|
part.start.to_parted_bytes_str().as_str(),
|
||||||
|
part_end.to_parted_bytes_str().as_str(),
|
||||||
|
])?;
|
||||||
|
|
||||||
|
// set partition options depending on its role
|
||||||
|
if let Some(role) = &part.role {
|
||||||
|
// getting part number of last created partition (hopefully)
|
||||||
|
let part_id = parted_get_last_part_num(dev)?;
|
||||||
|
match role {
|
||||||
|
PartRole::EFI => {
|
||||||
|
exec_parted(&vec![
|
||||||
|
"--script",
|
||||||
|
dev.full_name().as_str(),
|
||||||
|
"set",
|
||||||
|
&part_id.to_string(),
|
||||||
|
"esp",
|
||||||
|
"on",
|
||||||
|
])?;
|
||||||
|
}
|
||||||
|
// parted --script $install_device type 3 4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709
|
||||||
|
PartRole::ROOT => {
|
||||||
|
exec_parted(&vec![
|
||||||
|
"--script",
|
||||||
|
dev.full_name().as_str(),
|
||||||
|
"type",
|
||||||
|
&part_id.to_string(),
|
||||||
|
LINUX_ROOT_X86_64_TYPE,
|
||||||
|
])?;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// creating single drive bcachefs volume
|
||||||
|
pub fn bcachefs_format(
|
||||||
|
dev_name: &str,
|
||||||
|
compression: Option<String>,
|
||||||
|
password: Option<String>,
|
||||||
|
) -> Result<(), ExecCommandError> {
|
||||||
|
// sudo bcachefs format --compression=lz4 --background_compression=lz4 --encrypted /dev/loop0
|
||||||
|
// --data_checksum=none - do not make sense on single dev volumes
|
||||||
|
// sudo bcachefs format --force --compression=lz4 --background_compression=lz4 --encrypted --passphrase_file=./test.pass /dev/loop0
|
||||||
|
|
||||||
|
let mut arg = vec!["format", "--force", "--data_checksum=none"];
|
||||||
|
// if compression algorythm specified
|
||||||
|
if let Some(compresss_alg) = &compression {
|
||||||
|
arg.push(compresss_alg.as_str());
|
||||||
|
}
|
||||||
|
// if we want encrypted volume
|
||||||
|
if let Some(passw) = password {
|
||||||
|
// creating tmp file with password
|
||||||
|
fs::write("/tmp/bzzpsspass.txt", &passw)?;
|
||||||
|
arg.push("--encrypted");
|
||||||
|
arg.push("--passphrase_file=/tmp/bzzpsspass.txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
// add dev name at the end
|
||||||
|
arg.push(dev_name);
|
||||||
|
|
||||||
|
let out = exec_command("bcachefs", &arg);
|
||||||
|
|
||||||
|
// ignore any errors while deleting pass tmp file
|
||||||
|
let _ = fs::remove_file("/tmp/bzzpsspass.txt");
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
/// unlok encrypted bcache volume
|
||||||
|
pub fn bcachefs_unlock(dev_name: &str, pass: &str) -> Result<(), ExecCommandError> {
|
||||||
|
|
||||||
|
// bcachefs unlock --file=/tmp/bzzpsspass.txt /dev/loop0
|
||||||
|
let arg = vec!["unlock", "--file=/tmp/bzzpsspass.txt ", dev_name];
|
||||||
|
// creating tmp file with password
|
||||||
|
fs::write("/tmp/bzzpsspass.txt", &pass)?;
|
||||||
|
|
||||||
|
|
||||||
|
let out = exec_command("bcachefs", &arg);
|
||||||
|
// ignore any errors while deleting pass tmp file
|
||||||
|
let _ = fs::remove_file("/tmp/bzzpsspass.txt");
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
/// create subvolume
|
||||||
|
pub fn bcachefs_create_subvolume(vol_path: &str) -> Result<(), ExecCommandError> {
|
||||||
|
// sudo bcachefs subvolume create /mnt/test/nyan
|
||||||
|
exec_command("bcachefs", &vec!["subvolume", "create", vol_path])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// mount bcachefs
|
||||||
|
pub fn bcachefs_mount(dev_name: &str, mount_point: &str) -> Result<(), ExecCommandError> {
|
||||||
|
// sudo mount -t bcachefs /dev/loop0 /mnt/test
|
||||||
|
exec_command("mount", &vec!["-t", "bcachefs", dev_name, mount_point])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fatlabel(dev: &str, label: &str) -> Result<(), ExecCommandError> {
|
||||||
|
//fatlabel /dev/sda1 ESP
|
||||||
|
exec_command("fatlabel", &vec![dev, label])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mkfs_fat32(dev: &str, maybe_label: Option<&str>) -> Result<(), ExecCommandError> {
|
||||||
|
//mkfs.fat -F 32 /dev/sda1
|
||||||
|
exec_command("mkfs.fat", &vec!["-F", "32", dev])?;
|
||||||
|
|
||||||
|
// if need to set label
|
||||||
|
if let Some(label) = maybe_label {
|
||||||
|
fatlabel(dev, label)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// mkfs.fat -F 32 /dev/sda1
|
||||||
|
/// fatlabel /dev/sda1 ESP
|
||||||
|
pub fn mkfs_esp(dev: &str) -> Result<(), ExecCommandError> {
|
||||||
|
mkfs_fat32(dev, Some("ESP"))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn mkswap(dev: &str) -> Result<(), ExecCommandError> {
|
||||||
|
//mkswap -L SWAP /dev/sda4
|
||||||
|
exec_command("mkswap", &vec!["L", "SWAP", dev])
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use iced::{Color, Theme, color};
|
use iced::{Color, Theme, color};
|
||||||
|
use iced::widget::container;
|
||||||
|
|
||||||
// Function returns theme to be used by installer
|
// Function returns theme to be used by installer
|
||||||
pub fn main_theme() -> Theme {
|
pub fn main_theme() -> Theme {
|
||||||
@@ -38,6 +39,20 @@ pub fn change_lightnes(c: &Color, factor: f32) -> Color {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn dialog_background(_theme: &Theme) -> container::Style {
|
||||||
|
let mut back = _theme.palette().background.clone();
|
||||||
|
back.a = 0.8;
|
||||||
|
container::Style {
|
||||||
|
background: Some(iced::Background::Color(back)),
|
||||||
|
..container::Style::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn warning_dialog(_theme: &Theme) -> container::Style {
|
||||||
|
container::bordered_box(_theme).background(color!(0xDB5375))
|
||||||
|
}
|
||||||
|
|
||||||
// pub fn text_with_border(_theme: &Theme) -> container::Style {
|
// pub fn text_with_border(_theme: &Theme) -> container::Style {
|
||||||
|
|
||||||
// container::Style {
|
// container::Style {
|
||||||
|
|||||||
+1
-1
@@ -52,5 +52,5 @@
|
|||||||
"security.pass_middle": "normal security",
|
"security.pass_middle": "normal security",
|
||||||
"security.pass_hight": "hight security",
|
"security.pass_hight": "hight security",
|
||||||
"install.caption": "Now we will beee install Bzz Linux to your PC",
|
"install.caption": "Now we will beee install Bzz Linux to your PC",
|
||||||
"install.warning": "We a ready to start installation, this action will destroy all data on select drive!\nDo you want to proceed?"
|
"install.warning": "We a ready to start installation, this action will destroy all data on select drive!\n\nDo you want to proceed?"
|
||||||
}
|
}
|
||||||
+58
-12
@@ -35,6 +35,7 @@ use crate::stages::welcome::WelcomeStage;
|
|||||||
rust_i18n::i18n!("src/locales", fallback = "en");
|
rust_i18n::i18n!("src/locales", fallback = "en");
|
||||||
mod kira_color_bar;
|
mod kira_color_bar;
|
||||||
mod kira_disk_layout;
|
mod kira_disk_layout;
|
||||||
|
mod kira_system;
|
||||||
mod kira_logger;
|
mod kira_logger;
|
||||||
mod kira_scroll_list;
|
mod kira_scroll_list;
|
||||||
mod kira_size;
|
mod kira_size;
|
||||||
@@ -53,6 +54,7 @@ enum Views {
|
|||||||
Keyboard(stages::keyboard::KeyboardStage),
|
Keyboard(stages::keyboard::KeyboardStage),
|
||||||
Partition(stages::partition::PartitionStage),
|
Partition(stages::partition::PartitionStage),
|
||||||
Security(stages::security::SecurityStage),
|
Security(stages::security::SecurityStage),
|
||||||
|
Install(stages::install::InstallStage),
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Message {
|
enum Message {
|
||||||
@@ -65,6 +67,7 @@ enum Message {
|
|||||||
Keyboard(stages::keyboard::Message),
|
Keyboard(stages::keyboard::Message),
|
||||||
Partition(stages::partition::Message),
|
Partition(stages::partition::Message),
|
||||||
Security(stages::security::Message),
|
Security(stages::security::Message),
|
||||||
|
Install(stages::install::Message),
|
||||||
}
|
}
|
||||||
|
|
||||||
struct KiraState {
|
struct KiraState {
|
||||||
@@ -115,7 +118,8 @@ fn view(k_state: &KiraState) -> Element<'_, Message> {
|
|||||||
Views::Locale(locale_stage) => locale_stage.view().map(Message::Locale),
|
Views::Locale(locale_stage) => locale_stage.view().map(Message::Locale),
|
||||||
Views::Keyboard(keyboard_stage) => keyboard_stage.view().map(Message::Keyboard),
|
Views::Keyboard(keyboard_stage) => keyboard_stage.view().map(Message::Keyboard),
|
||||||
Views::Partition(partition_stage) => partition_stage.view().map(Message::Partition),
|
Views::Partition(partition_stage) => partition_stage.view().map(Message::Partition),
|
||||||
Views::Security(security_stage) => security_stage.view().map(Message::Security)
|
Views::Security(security_stage) => security_stage.view().map(Message::Security),
|
||||||
|
Views::Install(install_stage) => install_stage.view().map(Message::Install),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +142,10 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
match wlc_view.update(wlc_msg) {
|
match wlc_view.update(wlc_msg) {
|
||||||
StageAction::Next(welcome_res) => {
|
StageAction::Next(welcome_res) => {
|
||||||
k_state.config.config_trail.push(welcome_res);
|
k_state.config.config_trail.push(welcome_res);
|
||||||
let s_state = std::mem::replace(&mut k_state.current_view, Views::License(license::LicenseStage {}));
|
let s_state = std::mem::replace(
|
||||||
|
&mut k_state.current_view,
|
||||||
|
Views::License(license::LicenseStage {}),
|
||||||
|
);
|
||||||
k_state.stages_save.push(s_state);
|
k_state.stages_save.push(s_state);
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
@@ -155,7 +162,10 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
match action {
|
match action {
|
||||||
StageAction::Next(license_res) => {
|
StageAction::Next(license_res) => {
|
||||||
k_state.config.config_trail.push(license_res);
|
k_state.config.config_trail.push(license_res);
|
||||||
let s_state = std::mem::replace(&mut k_state.current_view, Views::Network(network::NetworkStage::new(&k_state.toml_config)));
|
let s_state = std::mem::replace(
|
||||||
|
&mut k_state.current_view,
|
||||||
|
Views::Network(network::NetworkStage::new(&k_state.toml_config)),
|
||||||
|
);
|
||||||
k_state.stages_save.push(s_state);
|
k_state.stages_save.push(s_state);
|
||||||
Task::done(Message::Network(network::Message::CheckNetwork))
|
Task::done(Message::Network(network::Message::CheckNetwork))
|
||||||
}
|
}
|
||||||
@@ -179,7 +189,10 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
network::UpdateResult::StageAction(action) => match action {
|
network::UpdateResult::StageAction(action) => match action {
|
||||||
StageAction::Next(network_res) => {
|
StageAction::Next(network_res) => {
|
||||||
k_state.config.config_trail.push(network_res);
|
k_state.config.config_trail.push(network_res);
|
||||||
let s_state = std::mem::replace(&mut k_state.current_view,Views::TimeZone(stages::timezone::TimeZoneStage::new()));
|
let s_state = std::mem::replace(
|
||||||
|
&mut k_state.current_view,
|
||||||
|
Views::TimeZone(stages::timezone::TimeZoneStage::new()),
|
||||||
|
);
|
||||||
k_state.stages_save.push(s_state);
|
k_state.stages_save.push(s_state);
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
@@ -201,7 +214,10 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
match action {
|
match action {
|
||||||
StageAction::Next(tz_res) => {
|
StageAction::Next(tz_res) => {
|
||||||
k_state.config.config_trail.push(tz_res);
|
k_state.config.config_trail.push(tz_res);
|
||||||
let s_state = std::mem::replace(&mut k_state.current_view,Views::Locale(stages::locale::LocaleStage::new(&k_state.config)));
|
let s_state = std::mem::replace(
|
||||||
|
&mut k_state.current_view,
|
||||||
|
Views::Locale(stages::locale::LocaleStage::new(&k_state.config)),
|
||||||
|
);
|
||||||
k_state.stages_save.push(s_state);
|
k_state.stages_save.push(s_state);
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
@@ -222,7 +238,10 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
match locale_view.update(locale_msg) {
|
match locale_view.update(locale_msg) {
|
||||||
StageAction::Next(locale_res) => {
|
StageAction::Next(locale_res) => {
|
||||||
k_state.config.config_trail.push(locale_res);
|
k_state.config.config_trail.push(locale_res);
|
||||||
let s_state = std::mem::replace(&mut k_state.current_view,Views::Keyboard(stages::keyboard::KeyboardStage::new()));
|
let s_state = std::mem::replace(
|
||||||
|
&mut k_state.current_view,
|
||||||
|
Views::Keyboard(stages::keyboard::KeyboardStage::new()),
|
||||||
|
);
|
||||||
k_state.stages_save.push(s_state);
|
k_state.stages_save.push(s_state);
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
@@ -244,7 +263,10 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
match stages::partition::PartitionStage::new(&k_state.toml_config) {
|
match stages::partition::PartitionStage::new(&k_state.toml_config) {
|
||||||
Ok(part_stage) => {
|
Ok(part_stage) => {
|
||||||
k_state.config.config_trail.push(keyboard_res);
|
k_state.config.config_trail.push(keyboard_res);
|
||||||
let s_state = std::mem::replace(&mut k_state.current_view,Views::Partition(part_stage));
|
let s_state = std::mem::replace(
|
||||||
|
&mut k_state.current_view,
|
||||||
|
Views::Partition(part_stage),
|
||||||
|
);
|
||||||
k_state.stages_save.push(s_state);
|
k_state.stages_save.push(s_state);
|
||||||
}
|
}
|
||||||
Err(ex) => {
|
Err(ex) => {
|
||||||
@@ -263,13 +285,16 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
} else {
|
} else {
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Message::Partition(partition_msg) => {
|
Message::Partition(partition_msg) => {
|
||||||
if let Views::Partition(partition_view) = &mut k_state.current_view {
|
if let Views::Partition(partition_view) = &mut k_state.current_view {
|
||||||
match partition_view.update(partition_msg) {
|
match partition_view.update(partition_msg) {
|
||||||
StageAction::Next(partition_res) => {
|
StageAction::Next(partition_res) => {
|
||||||
k_state.config.config_trail.push(partition_res);
|
k_state.config.config_trail.push(partition_res);
|
||||||
let s_state = std::mem::replace(&mut k_state.current_view,Views::Security(stages::security::SecurityStage::new()));
|
let s_state = std::mem::replace(
|
||||||
|
&mut k_state.current_view,
|
||||||
|
Views::Security(stages::security::SecurityStage::new()),
|
||||||
|
);
|
||||||
k_state.stages_save.push(s_state);
|
k_state.stages_save.push(s_state);
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
@@ -283,13 +308,17 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
} else {
|
} else {
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Message::Security(security_msg) => {
|
Message::Security(security_msg) => {
|
||||||
if let Views::Security(security_view) = &mut k_state.current_view {
|
if let Views::Security(security_view) = &mut k_state.current_view {
|
||||||
match security_view.update(security_msg) {
|
match security_view.update(security_msg) {
|
||||||
StageAction::Next(security_res) => {
|
StageAction::Next(security_res) => {
|
||||||
k_state.config.config_trail.push(security_res);
|
k_state.config.config_trail.push(security_res);
|
||||||
//let s_state = std::mem::replace(&mut k_state.current_view,
|
let s_state = std::mem::replace(
|
||||||
|
&mut k_state.current_view,
|
||||||
|
Views::Install(stages::install::InstallStage::new(&k_state.config)),
|
||||||
|
);
|
||||||
|
k_state.stages_save.push(s_state);
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
StageAction::Back => {
|
StageAction::Back => {
|
||||||
@@ -302,8 +331,25 @@ fn update(k_state: &mut KiraState, message: Message) -> Task<Message> {
|
|||||||
} else {
|
} else {
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
Message::Install(install_msg) => {
|
||||||
|
if let Views::Install(install_view) = &mut k_state.current_view {
|
||||||
|
let update_result = install_view.update(install_msg);
|
||||||
|
match update_result {
|
||||||
|
stages::install::Update::Task(t) => t.map(Message::Install),
|
||||||
|
stages::install::Update::StgAct(stage_action) => match stage_action {
|
||||||
|
StageAction::Back => {
|
||||||
|
k_state.config.config_trail.pop();
|
||||||
|
k_state.current_view = k_state.stages_save.pop().unwrap();
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
_ => Task::none(),
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+221
-45
@@ -18,27 +18,104 @@
|
|||||||
Actual installation script.
|
Actual installation script.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
kira_disk_layout::{self, BlkDev, PartLayout, PartRole},
|
||||||
|
kira_system::{self, ExecCommandError},
|
||||||
|
kira_size::KiraSize,
|
||||||
|
kira_theming,
|
||||||
|
stage::{KiraConfig, StageAction, StageResult},
|
||||||
|
stages::partition::PartitionStageResult,
|
||||||
|
};
|
||||||
|
use iced::{
|
||||||
|
Alignment, Task, futures::{self, SinkExt, StreamExt, TryStreamExt, channel::mpsc::SendError}, widget,
|
||||||
|
};
|
||||||
|
use iced::{Color, Length, futures::channel::mpsc::Sender, widget::container};
|
||||||
///
|
///
|
||||||
/// So. I am goind to make dir structure lile this
|
/// So. I am goind to make dir structure lile this
|
||||||
/// /bzz/root
|
/// /bzz/root
|
||||||
/// /bzz/home
|
/// /bzz/home
|
||||||
/// Mount root from it and binmount home
|
/// Mount root from it and binmount home
|
||||||
///
|
///
|
||||||
|
|
||||||
use log;
|
use log;
|
||||||
use crate::{
|
|
||||||
kira_disk_layout::{self, BlkDev, PartLayout}, kira_size::KiraSize, kira_theming, stage::{KiraConfig, StageAction, StageResult}
|
|
||||||
};
|
|
||||||
use iced::{Alignment, Task, futures::{self, SinkExt}, widget};
|
|
||||||
use rust_i18n::t;
|
use rust_i18n::t;
|
||||||
use iced::futures::channel::mpsc::{Sender};
|
|
||||||
|
/// Creates new GPT table and partitions from PartLayout
|
||||||
|
pub fn create_partitions(part_layout: &PartLayout) -> Result<(), ExecCommandError> {
|
||||||
|
kira_system::parted_init_gpt_part_table(&part_layout.dev)?;
|
||||||
|
for part in &part_layout.part_list {
|
||||||
|
kira_system::parted_mkpart(&part_layout.dev, part)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// inits bcachefs volume for installation to use
|
||||||
|
/// Formats device to bcachefs
|
||||||
|
/// Unlock it if encryption used
|
||||||
|
/// Mount to specified dir
|
||||||
|
/// creates subvolumes bzz/root bzz/home
|
||||||
|
pub fn bcachefs_init(
|
||||||
|
dev_name: &str,
|
||||||
|
compression: Option<String>,
|
||||||
|
password: Option<String>,
|
||||||
|
mnt_dir: &str,
|
||||||
|
) -> Result<(), ExecCommandError> {
|
||||||
|
|
||||||
|
// format device
|
||||||
|
kira_system::bcachefs_format(dev_name, compression, password.clone())?;
|
||||||
|
|
||||||
|
// if encryption used - unlock drive
|
||||||
|
if let Some(pass) = password {
|
||||||
|
kira_system::bcachefs_unlock(dev_name, pass.as_str())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
//mount drive
|
||||||
|
kira_system::bcachefs_mount(dev_name, mnt_dir)?;
|
||||||
|
|
||||||
|
// Creating bzz_snaps directory for root snapshoots
|
||||||
|
std::fs::create_dir(format!("{}{}", mnt_dir, "/bzz_snaps"))?;
|
||||||
|
|
||||||
|
// We want /var to bee simple dir so snapshoting root capture it
|
||||||
|
std::fs::create_dir(format!("{}{}", mnt_dir, "/var"))?;
|
||||||
|
|
||||||
|
// Creating boot directory for efi mounting
|
||||||
|
std::fs::create_dir_all(format!("{}{}", mnt_dir, "/bzz-boot/uefi"))?;
|
||||||
|
|
||||||
|
// create subvolumes
|
||||||
|
for vol_name in ["/home", "/tmp", "/var/tmp"] {
|
||||||
|
let vol_path = format!("{}{}", mnt_dir, vol_name);
|
||||||
|
kira_system::bcachefs_create_subvolume(vol_path.as_str())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_disk(part_layout: &PartLayout) -> Result<(), ExecCommandError> {
|
||||||
|
// use parted to create partition gpt layout
|
||||||
|
create_partitions(part_layout)?;
|
||||||
|
|
||||||
|
for part in &part_layout.part_list {
|
||||||
|
match part.role {
|
||||||
|
Some(PartRole::EFI) => {
|
||||||
|
if let Some(part_l) = part.partlabel_dev() {
|
||||||
|
kira_system::mkfs_esp(&part_l)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(PartRole::SWAP) => {
|
||||||
|
if let Some(part_l) = part.partlabel_dev() {
|
||||||
|
kira_system::mkswap(&part_l)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
enum InstallationState {
|
enum InstallationState {
|
||||||
UserConfirmDialog,
|
UserConfirmDialog,
|
||||||
Partitionning,
|
Installing,
|
||||||
CopyFiles,
|
|
||||||
Configuring,
|
|
||||||
Finish,
|
Finish,
|
||||||
Error,
|
Error,
|
||||||
}
|
}
|
||||||
@@ -48,19 +125,19 @@ pub struct InstallStage {
|
|||||||
state: InstallationState,
|
state: InstallationState,
|
||||||
progress: f32,
|
progress: f32,
|
||||||
progress_message: String,
|
progress_message: String,
|
||||||
kira_config: KiraConfig,
|
part_config: PartitionStageResult,
|
||||||
|
modal_backgroud: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
UserConfirms,
|
UserConfirms,
|
||||||
UserDenies,
|
UserDenies,
|
||||||
StartPartitioning,
|
Sucess,
|
||||||
UpdateProgress(Result<(f32, String), String>),
|
|
||||||
PartitioningFinish(Result<(), String>),
|
|
||||||
Back,
|
|
||||||
Cancel,
|
Cancel,
|
||||||
Finish,
|
Finish,
|
||||||
|
UpdateProgress(Result<(f32, String), String>),
|
||||||
|
Failure(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -69,42 +146,128 @@ pub enum Update {
|
|||||||
Task(Task<Message>),
|
Task(Task<Message>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn init_disk_async(part_layout: PartLayout) -> Result<(f32, String), ExecCommandError> {
|
||||||
|
use blocking;
|
||||||
|
blocking::unblock(move || init_disk(&part_layout)).await?;
|
||||||
|
Ok((1.1f32, "Preparing bcachefs filesystem...".into()))
|
||||||
|
}
|
||||||
|
|
||||||
fn partition_disk(disk_layout: PartLayout, password: Option<String>) -> impl futures::Stream<Item = Result<(f32, String), String>> {
|
async fn bcachefs_init_async(
|
||||||
|
dev_name: String,
|
||||||
|
compression: Option<String>,
|
||||||
|
password: Option<String>,
|
||||||
|
mnt_dir: String,
|
||||||
|
) -> Result<(f32, String), ExecCommandError> {
|
||||||
|
use blocking;
|
||||||
|
blocking::unblock(move || bcachefs_init(&dev_name, compression, password, &mnt_dir)).await?;
|
||||||
|
Ok((2.2f32, "Something next...".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn partition_disk(
|
||||||
|
disk_layout: PartLayout,
|
||||||
|
password: Option<String>,
|
||||||
|
compression: Option<String>,
|
||||||
|
) -> impl futures::Stream<Item = Result<(f32, String), ExecCommandError>> {
|
||||||
use blocking;
|
use blocking;
|
||||||
|
|
||||||
iced::stream::channel(100, |mut output: Sender<Result<(f32, String), String>>| async move {
|
iced::stream::channel(
|
||||||
for i in 0..=1000 {
|
100,
|
||||||
|
|mut output: Sender<Result<(f32, String), ExecCommandError>>| async move {
|
||||||
//sleep(Duration::from_millis(10)).await;
|
//sleep(Duration::from_millis(10)).await;
|
||||||
let dev = disk_layout.dev.clone();
|
let disk_layout_cloned = disk_layout.clone();
|
||||||
// creating gpt partition table
|
// creating gpt partition table
|
||||||
match output.send(blocking::unblock(move || kira_disk_layout::parted_init_gpt_part_table(&dev)).await.and(Ok((1.0f32, String::new())))).await {
|
match output.send(init_disk_async(disk_layout_cloned).await).await {
|
||||||
Err(ex) => {
|
Err(ex) => {
|
||||||
log::error!("Error creating GPT partition table: {}", ex.to_string());
|
log::error!("Error creating GPT partition table: {}", ex.to_string());
|
||||||
return;
|
return;
|
||||||
},
|
}
|
||||||
_ => ()
|
_ => (),
|
||||||
|
};
|
||||||
|
|
||||||
|
// creating partitions
|
||||||
|
let dev = disk_layout.dev.clone();
|
||||||
|
let part_list = disk_layout.part_list.clone();
|
||||||
|
match output
|
||||||
|
.send(
|
||||||
|
blocking::unblock(move || {
|
||||||
|
let mut res = Ok(());
|
||||||
|
for part in part_list {
|
||||||
|
match kira_system::parted_mkpart(&dev, &part) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(ex) => {
|
||||||
|
res = Err(ex);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.and(Ok((5.0f32, "Creating partitions".into()))),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Err(ex) => {
|
||||||
|
log::error!("Error creating GPT partition table: {}", ex.to_string());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
|
||||||
|
// creating bcachefs filesystem
|
||||||
|
// /dev/disk/by-partlabel/
|
||||||
|
let compression_cloned = compression.clone();
|
||||||
|
let password_cloned = password.clone();
|
||||||
|
match output
|
||||||
|
.send(
|
||||||
|
blocking::unblock(move || {
|
||||||
|
bcachefs_init(
|
||||||
|
"/dev/disk/by-partlabel/bee-root",
|
||||||
|
compression_cloned,
|
||||||
|
password_cloned,
|
||||||
|
"/mnt/bzzpss-install",
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.and(Ok((10.0f32, "Creating partition table".into()))),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Err(ex) => {
|
||||||
|
log::error!("Error creating GPT partition table: {}", ex.to_string());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if send fails (receiver dropped/cancelled)
|
// Check if send fails (receiver dropped/cancelled)
|
||||||
// if output.send(res).await.is_err() {
|
// if output.send(res).await.is_err() {
|
||||||
// return; // Exit early if UI is gone
|
// return; // Exit early if UI is gone
|
||||||
// }
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
// Optional: Send completion or error state here
|
// Optional: Send completion or error state here
|
||||||
// output.send(Message::WorkFailed).await.ok();
|
// output.send(Message::WorkFailed).await.ok();
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl InstallStage {
|
impl InstallStage {
|
||||||
pub fn new(config: &KiraConfig) -> Self {
|
pub fn new(config: &KiraConfig) -> Self {
|
||||||
|
let mut modal_backgroud = kira_theming::main_theme().palette().background;
|
||||||
|
modal_backgroud.a = 0.5;
|
||||||
Self {
|
Self {
|
||||||
|
modal_backgroud: modal_backgroud,
|
||||||
state: InstallationState::UserConfirmDialog,
|
state: InstallationState::UserConfirmDialog,
|
||||||
progress: 0.0,
|
progress: 0.0,
|
||||||
progress_message: String::new(),
|
progress_message: String::new(),
|
||||||
kira_config: config.clone(),
|
part_config: config
|
||||||
|
.config_trail
|
||||||
|
.iter()
|
||||||
|
.find_map(|v| match v {
|
||||||
|
StageResult::PartitionStageResult(res) => Some(res.clone()),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.expect("Partition stage result should be present in KiraConfig!"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,20 +280,18 @@ impl InstallStage {
|
|||||||
Message::UserDenies => Update::StgAct(StageAction::Back),
|
Message::UserDenies => Update::StgAct(StageAction::Back),
|
||||||
Message::UserConfirms => {
|
Message::UserConfirms => {
|
||||||
// starting installation process here
|
// starting installation process here
|
||||||
let part_data = self.kira_config.config_trail.iter().find_map(|v| match v {
|
log::info!("Starting disk partitioning.");
|
||||||
StageResult::PartitionStageResult(res) => Some(res.clone()),
|
|
||||||
_ => None,
|
|
||||||
}).unwrap();
|
|
||||||
self.state = InstallationState::Partitionning;
|
|
||||||
let disk_layout = part_data.part_layout.clone();
|
|
||||||
let disk_password = part_data.password.clone();
|
|
||||||
|
|
||||||
|
self.state = InstallationState::Installing;
|
||||||
Update::Task(Task::run(partition_disk(disk_layout, disk_password), Message::UpdateProgress))
|
let disk_layout = self.part_config.part_layout.clone();
|
||||||
|
let disk_password = self.part_config.password.clone();
|
||||||
|
let disk_compression = Some("lz4".to_string());
|
||||||
|
Update::Task(Task::run(
|
||||||
|
partition_disk(disk_layout, disk_password, disk_compression).map_err(|ex|ex.to_string()),
|
||||||
|
Message::UpdateProgress,
|
||||||
|
))
|
||||||
//Update::Task(Task::perform(, Message::PartitioningFinish))
|
//Update::Task(Task::perform(, Message::PartitioningFinish))
|
||||||
}
|
}
|
||||||
Message::StartPartitioning => Update::StgAct(StageAction::None),
|
|
||||||
Message::PartitioningFinish(res) => Update::StgAct(StageAction::None),
|
|
||||||
Message::UpdateProgress(maybe_msg) => {
|
Message::UpdateProgress(maybe_msg) => {
|
||||||
match maybe_msg {
|
match maybe_msg {
|
||||||
Ok((progress, msg)) => {
|
Ok((progress, msg)) => {
|
||||||
@@ -145,10 +306,13 @@ impl InstallStage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Update::StgAct(StageAction::None)
|
//Update::StgAct(StageAction::None)
|
||||||
|
}
|
||||||
},
|
Message::Failure(ex) => {
|
||||||
|
self.state = InstallationState::Error;
|
||||||
|
Update::StgAct(StageAction::Abort)
|
||||||
|
}
|
||||||
Message::Cancel => Update::StgAct(StageAction::Abort),
|
Message::Cancel => Update::StgAct(StageAction::Abort),
|
||||||
Message::Back => Update::StgAct(StageAction::Back),
|
Message::Sucess => Update::StgAct(StageAction::None),
|
||||||
Message::Finish => Update::StgAct(StageAction::None),
|
Message::Finish => Update::StgAct(StageAction::None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -161,12 +325,12 @@ impl InstallStage {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Embed the image bytes into the executable
|
// Embed the image bytes into the executable
|
||||||
let welcom_logo_handle = widget::image::Handle::from_bytes(kira_theming::get_logo_bytes());
|
let logo_handle = widget::image::Handle::from_bytes(kira_theming::get_logo_bytes());
|
||||||
|
|
||||||
let main_content = widget::column![
|
let main_content = widget::column![
|
||||||
widget::container(
|
widget::container(
|
||||||
widget::column![
|
widget::column![
|
||||||
widget::image(welcom_logo_handle)
|
widget::image(logo_handle)
|
||||||
.width(iced::Pixels(128.0))
|
.width(iced::Pixels(128.0))
|
||||||
.height(iced::Pixels(128.0)),
|
.height(iced::Pixels(128.0)),
|
||||||
widget::text(t!("install.caption")),
|
widget::text(t!("install.caption")),
|
||||||
@@ -193,9 +357,11 @@ impl InstallStage {
|
|||||||
|
|
||||||
if self.state == InstallationState::UserConfirmDialog {
|
if self.state == InstallationState::UserConfirmDialog {
|
||||||
page_stack
|
page_stack
|
||||||
.push(
|
.push(widget::opaque(
|
||||||
|
container(
|
||||||
|
container(
|
||||||
widget::column![
|
widget::column![
|
||||||
widget::text(t!("install.warning")),
|
widget::text(t!("install.warning")).align_x(Alignment::Center),
|
||||||
widget::row![
|
widget::row![
|
||||||
widget::button(widget::text(t!("button.no")))
|
widget::button(widget::text(t!("button.no")))
|
||||||
.on_press(Message::UserDenies),
|
.on_press(Message::UserDenies),
|
||||||
@@ -203,12 +369,22 @@ impl InstallStage {
|
|||||||
widget::button(widget::text(t!("button.yes")))
|
widget::button(widget::text(t!("button.yes")))
|
||||||
.on_press(Message::UserConfirms)
|
.on_press(Message::UserConfirms)
|
||||||
]
|
]
|
||||||
.width(iced::Length::Fill)
|
|
||||||
.align_y(Alignment::End)
|
|
||||||
.padding(10)
|
.padding(10)
|
||||||
]
|
]
|
||||||
|
.spacing(10)
|
||||||
|
.padding(10)
|
||||||
|
.width(512)
|
||||||
.align_x(Alignment::Center),
|
.align_x(Alignment::Center),
|
||||||
)
|
)
|
||||||
|
.style(kira_theming::warning_dialog),
|
||||||
|
)
|
||||||
|
.style(container::bordered_box)
|
||||||
|
.style(kira_theming::dialog_background)
|
||||||
|
.align_x(Alignment::Center)
|
||||||
|
.align_y(Alignment::Center)
|
||||||
|
.height(Length::Fill)
|
||||||
|
.width(Length::Fill),
|
||||||
|
))
|
||||||
.into()
|
.into()
|
||||||
} else {
|
} else {
|
||||||
page_stack.into()
|
page_stack.into()
|
||||||
|
|||||||
Reference in New Issue
Block a user