Compare commits

...

2 Commits

Author SHA256 Message Date
kira c6e7fa6069 More work on partition stuff #2, things looks much better now. 2026-05-21 08:39:11 +02:00
kira c0ae014eef align_part function now passing tests. #2 2026-05-20 20:04:45 +02:00
4 changed files with 215 additions and 224 deletions
Generated
+42 -3
View File
@@ -624,6 +624,17 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "chacha20"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
dependencies = [
"cfg-if",
"cpufeatures",
"rand_core 0.10.1",
]
[[package]] [[package]]
name = "clipboard-win" name = "clipboard-win"
version = "5.4.1" version = "5.4.1"
@@ -793,6 +804,15 @@ dependencies = [
"unicode-segmentation", "unicode-segmentation",
] ]
[[package]]
name = "cpufeatures"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.5.0" version = "1.5.0"
@@ -1322,6 +1342,7 @@ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"r-efi 6.0.0", "r-efi 6.0.0",
"rand_core 0.10.1",
"wasip2", "wasip2",
"wasip3", "wasip3",
] ]
@@ -2008,6 +2029,7 @@ dependencies = [
"blocking", "blocking",
"iced", "iced",
"iced_moving_picture", "iced_moving_picture",
"rand 0.10.1",
"rust-i18n", "rust-i18n",
"toml 1.1.2+spec-1.1.0", "toml 1.1.2+spec-1.1.0",
] ]
@@ -3104,7 +3126,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
dependencies = [ dependencies = [
"rand_chacha", "rand_chacha",
"rand_core", "rand_core 0.9.5",
]
[[package]]
name = "rand"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207"
dependencies = [
"chacha20",
"getrandom 0.4.2",
"rand_core 0.10.1",
] ]
[[package]] [[package]]
@@ -3114,7 +3147,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [ dependencies = [
"ppv-lite86", "ppv-lite86",
"rand_core", "rand_core 0.9.5",
] ]
[[package]] [[package]]
@@ -3126,6 +3159,12 @@ dependencies = [
"getrandom 0.3.4", "getrandom 0.3.4",
] ]
[[package]]
name = "rand_core"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
[[package]] [[package]]
name = "range-alloc" name = "range-alloc"
version = "0.1.5" version = "0.1.5"
@@ -3165,7 +3204,7 @@ dependencies = [
"num-traits", "num-traits",
"paste", "paste",
"profiling", "profiling",
"rand", "rand 0.9.4",
"rand_chacha", "rand_chacha",
"simd_helpers", "simd_helpers",
"thiserror 2.0.18", "thiserror 2.0.18",
+3
View File
@@ -10,6 +10,9 @@ iced_moving_picture = "0"
rust-i18n = "3" rust-i18n = "3"
toml = "1" toml = "1"
[dev-dependencies]
rand = "0.10"
[profile.release] [profile.release]
#lto = true #lto = true
codegen-units = 16 codegen-units = 16
+133 -15
View File
@@ -24,9 +24,9 @@ pub fn format_bytes_size(size: u64) -> String {
/// sector size - default 4096 /// sector size - default 4096
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct BlkDev { pub struct BlkDev {
name: String, pub name: String,
size: u64, pub size: u64,
sector_size: u32, pub sector_size: u32,
} }
impl std::fmt::Display for BlkDev { impl std::fmt::Display for BlkDev {
@@ -43,6 +43,57 @@ impl BlkDev {
sector_size: 4096, sector_size: 4096,
} }
} }
pub fn from_hash_map(data: &HashMap<String, String>) -> Option<Self> {
Some(Self {
name: data.get("NAME")?.clone(),
size: u64::from_str_radix(data.get("SIZE")?, 10).ok()?,
sector_size: 4096,
})
}
/// this is blocking function
pub fn list_sys_blk_dev() -> Result<Vec<Self>, String> {
use std::process::Command;
// lsblk -d -b -P -o NAME,SIZE,FSTYPE,LABEL,MOUNTPOINT
match Command::new("lsblk")
.args(["-d", "-b", "-P", "-o", "NAME,SIZE,FSTYPE,LABEL,MOUNTPOINT"])
.output()
{
Ok(cmd_output) => {
if cmd_output.status.success() {
match String::from_utf8(cmd_output.stdout) {
Ok(raw_str_list) => Ok(raw_str_list
.lines()
.map(|l| {
l.split_whitespace()
.filter_map(|pair| {
pair.split_once("=").and_then(|kv| {
Some((
kv.0.trim_matches('"').to_string(),
kv.1.trim_matches('"').to_string(),
))
})
})
.collect::<HashMap<String, String>>()
})
.filter(|mp| mp.get("MOUNTPOINT").is_some_and(|v| v.is_empty()))
.filter_map(|mp| Self::from_hash_map(&mp))
.collect()),
Err(ex) => Err(format!(
"Exception while converting disks info to UTF8 {}",
ex
)),
}
} else {
Err(format!(
"Error getting disk devices list: {}",
String::from_utf8(cmd_output.stderr).unwrap_or("UNKNOWN".into())
))
}
}
Err(ex) => Err(ex.to_string()),
}
}
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@@ -102,12 +153,13 @@ impl FSType {
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct PartInfo { pub struct PartInfo {
name: String, pub name: String,
size: u64, pub size: u64,
fs_type: FSType, pub start: u64,
gpt_label: Option<String>, pub fs_type: FSType,
fs_label: Option<String>, pub gpt_label: Option<String>,
mount_point: Option<String>, pub fs_label: Option<String>,
pub mount_point: Option<String>,
} }
impl std::fmt::Display for PartInfo { impl std::fmt::Display for PartInfo {
@@ -127,6 +179,7 @@ impl PartInfo {
Some(Self { Some(Self {
name: data.get("NAME")?.clone(), name: data.get("NAME")?.clone(),
size: u64::from_str_radix(data.get("SIZE")?, 10).ok()?, size: u64::from_str_radix(data.get("SIZE")?, 10).ok()?,
start: 0,
fs_type: FSType::from_str(data.get("FSTYPE")?), fs_type: FSType::from_str(data.get("FSTYPE")?),
gpt_label: data gpt_label: data
.get("PARTLABEL") .get("PARTLABEL")
@@ -222,7 +275,10 @@ pub fn align_part(size: u64, start: u64, align: u64) -> Option<(u64, u64, u64)>
let align_size = align_end - align_start + 1; let align_size = align_end - align_start + 1;
if align_size < align { if align_size < align {
println!("if align_size < align !!!!!!! align_size: {} align: {}",align_size,align); println!(
"if align_size < align !!!!!!! align_size: {} align: {}",
align_size, align
);
return None; return None;
} }
@@ -230,9 +286,9 @@ pub fn align_part(size: u64, start: u64, align: u64) -> Option<(u64, u64, u64)>
} }
pub struct PartLayout { pub struct PartLayout {
dev: BlkDev, pub dev: BlkDev,
empty_space: u64, pub empty_space: u64,
part_list: Vec<PartInfo>, pub part_list: Vec<PartInfo>,
} }
impl PartLayout { impl PartLayout {
@@ -244,7 +300,7 @@ impl PartLayout {
} }
} }
pub fn read_from_dev(dev: &BlkDev) -> Result<Self, String> { pub fn read_from_disk(dev: &BlkDev) -> Result<Self, String> {
let part_list = get_dev_part_blocking(&dev.name)?; let part_list = get_dev_part_blocking(&dev.name)?;
let parts_size = part_list.iter().fold(0, |acc, part| acc + part.size); let parts_size = part_list.iter().fold(0, |acc, part| acc + part.size);
Ok(Self { Ok(Self {
@@ -268,16 +324,50 @@ impl PartLayout {
fs_label: Option<String>, fs_label: Option<String>,
mount_point: Option<String>, mount_point: Option<String>,
) -> Self { ) -> Self {
let part_start = if self.part_list.is_empty() {
2u64 * 1024u64 * 1024u64
} else {
self.dev.size.checked_sub(self.empty_space).unwrap()
};
let (size, start, _) =
align_part(part_size, part_start, self.dev.sector_size as u64).unwrap();
self.part_list.push(PartInfo {
name: "_".into(),
size: size,
start: start,
fs_type: fs_type,
gpt_label: gpt_label,
fs_label: fs_label,
mount_point: mount_point,
});
self.empty_space = self.empty_space.checked_sub(size).unwrap();
self self
} }
pub fn add_part_reserve(
self,
reserve_space: u64,
fs_type: FSType,
gpt_label: Option<String>,
fs_label: Option<String>,
mount_point: Option<String>,
) -> Self {
let p_size = self.empty_space.checked_sub(reserve_space).unwrap();
self.add_part(p_size, fs_type, gpt_label, fs_label, mount_point)
}
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use rand::RngExt;
use super::*; use super::*;
#[test] #[test]
fn test_align_part_fn() { fn test_align_part_fn_joe() {
//START (S),SIZE (Z),ALIGNMENT (A),ALIGNED_START (Salign),ALIGNED_END (Ealign),Effektive Größe (Zalign) //START (S),SIZE (Z),ALIGNMENT (A),ALIGNED_START (Salign),ALIGNED_END (Ealign),Effektive Größe (Zalign)
let joes_test_data: Vec<(u64, u64, u64, u64, u64, u64)> = vec![ let joes_test_data: Vec<(u64, u64, u64, u64, u64, u64)> = vec![
(0, 10000, 4096, 0, 8191, 8192), (0, 10000, 4096, 0, 8191, 8192),
@@ -292,6 +382,34 @@ mod tests {
assert_eq!(res, test_res); assert_eq!(res, test_res);
} }
} }
#[test]
fn test_align_part_fn_rand() {
for ((size, start, align), test_res) in rand::rng()
.random_iter::<u32>()
.filter(|n| *n > 0)
.take(100)
.flat_map(|align| {
(0..u64::MAX)
.step_by(align as usize)
.take(1000)
.map(move |start_align| {
let mut rng = rand::rng();
let size_align = align as u64 * rng.random_range(1..u16::MAX) as u64;
let end_align = start_align + size_align - 1;
let start = start_align.saturating_sub(rng.random_range(0..align) as u64);
let size = size_align.saturating_add(rng.random_range(0..align) as u64);
(
(size, start, align as u64),
(size_align, start_align.clone(), end_align),
)
})
})
{
let res = align_part(size, start, align).unwrap();
assert_eq!(res, test_res);
}
}
} }
// fn add_part(part_list: Vec<PartInfo>, rem_size: u64, part_t: PartType, ) // fn add_part(part_list: Vec<PartInfo>, rem_size: u64, part_t: PartType, )
+36 -205
View File
@@ -21,6 +21,8 @@
use crate::stage::{StageAction, StageResult}; use crate::stage::{StageAction, StageResult};
use crate::kira_color_bar; use crate::kira_color_bar;
use crate::kira_disk_layout::{BlkDev,PartInfo, PartLayout, FSType};
use toml::Table;
use iced::{Alignment, Color, Length, widget}; use iced::{Alignment, Color, Length, widget};
use rust_i18n::t; use rust_i18n::t;
@@ -47,99 +49,26 @@ const SWAP_MODES: [SwapMode; 3] = [
SwapMode::SwapHibernate, SwapMode::SwapHibernate,
]; ];
#[derive(Debug, Clone, PartialEq, Eq)] fn part_type_to_color(t: &FSType) -> Color {
pub struct BlkDev {
name: String,
size_mb: u64,
}
impl std::fmt::Display for BlkDev {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{} ({})", &self.name, size_mb_to_text(self.size_mb))
}
}
impl BlkDev {
pub fn from_lsblk_out_vec(v: &Vec<String>) -> Self {
let name = v[0].clone();
let size = u64::from_str_radix(&v[3], 10).unwrap_or(0) / 1048576;
Self {
name: name,
size_mb: size,
}
}
}
fn part_type_to_color(t: &str) -> Color {
match t { match t {
"vfat" => Color::from_rgb8(204, 121, 167), FSType::VFAT => Color::from_rgb8(204, 121, 167),
"xfs" => Color::from_rgb8(0, 158, 115), FSType::XFS => Color::from_rgb8(0, 158, 115),
"crypto_LUKS" => Color::from_rgb8(240, 228, 66), FSType::LUKS => Color::from_rgb8(240, 228, 66),
"swap" => Color::from_rgb8(213, 94, 0), FSType::SWAP => Color::from_rgb8(213, 94, 0),
"ext4" => Color::from_rgb8(0, 114, 178), FSType::EXT4 => Color::from_rgb8(0, 114, 178),
"btrfs" => Color::from_rgb8(86, 180, 233), FSType::BTRFS => Color::from_rgb8(86, 180, 233),
_ => Color::from_rgb8(230, 159, 0), _ => Color::from_rgb8(230, 159, 0),
} }
} }
#[derive(Debug, Clone, PartialEq, Eq)] fn part_to_ct_params(part: &PartInfo, dev_size: u64) -> (String, u16, Color) {
pub struct PartInfo { let fill_ratio: u16 = ((part.size as f32 / dev_size as f32) * 12.0) as u16;
name: String,
part_type: String,
size_mb: u64,
}
fn size_mb_to_text(size_mb: u64) -> String {
let mut s = size_mb;
if s < 1000 {
format!("{}MB", s)
} else {
s = s / 1024;
if s < 1000 {
format!("{}GB", s)
} else {
s = s / 1024;
format!("{}TB", s)
}
}
}
impl std::fmt::Display for PartInfo {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{} {} {}",
&self.name,
self.part_type,
size_mb_to_text(self.size_mb)
)
}
}
impl PartInfo {
pub fn from_vect(v: &Vec<String>) -> Option<Self> {
if v.len() == 4 && v[3] == "part" {
let name = v[0].clone();
let size_mb = u64::from_str_radix(&v[1], 10).unwrap_or(0) / 1048576;
let part_type = v[2].clone();
Some(Self {
name: name,
part_type: part_type,
size_mb: size_mb,
})
} else {
None
}
}
pub fn to_ct_params(&self, dev_size_mb: u64) -> (String, u16, Color) {
let fill_ratio: u16 = ((self.size_mb as f32 / dev_size_mb as f32) * 12.0) as u16;
let fill_ratio = fill_ratio.max(1); let fill_ratio = fill_ratio.max(1);
let part_color = part_type_to_color(&self.part_type); let part_color = part_type_to_color(&part.fs_type);
(self.to_string(), fill_ratio, part_color) (part.to_string(), fill_ratio, part_color)
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct PartitionStage { pub struct PartitionStage {
device: Option<BlkDev>, device: Option<BlkDev>,
@@ -160,133 +89,35 @@ pub enum Message {
Back, Back,
} }
/// returns list of system block devices with their size in MB
pub fn get_disks_info_blocking() -> Result<Vec<BlkDev>, String> {
use std::process::Command;
match Command::new("lsblk").args(["-n", "-d", "-b"]).output() {
Ok(cmd_output) => {
if cmd_output.status.success() {
match String::from_utf8(cmd_output.stdout) {
Ok(raw_str_list) => Ok(raw_str_list
.lines()
.map(|l| {
l.split_whitespace()
.map(str::to_string)
.collect::<Vec<String>>()
})
.filter(|l_v| l_v.len() == 6)
.map(|dev_params| BlkDev::from_lsblk_out_vec(&dev_params))
.collect()),
Err(ex) => Err(format!(
"Exception while converting disks info to UTF8 {}",
ex
)),
}
} else {
Err(format!(
"Error getting disk devices list: {}",
String::from_utf8(cmd_output.stderr).unwrap_or("UNKNOWN".into())
))
}
}
Err(ex) => Err(ex.to_string()),
}
}
/// getting list of device partitions
pub fn get_dev_part_blocking(dev_name: &str) -> Result<Vec<PartInfo>, String> {
use std::process::Command;
//"lsblk -o NAME,SIZE,FSTYPE -l -n /dev/nvme0n1"
match Command::new("lsblk")
.args([
"-o",
"NAME,SIZE,FSTYPE,TYPE",
"-l",
"-n",
"-b",
format!("/dev/{}", dev_name).as_str(),
])
.output()
{
Ok(cmd_output) => {
if cmd_output.status.success() {
match String::from_utf8(cmd_output.stdout) {
Ok(raw_str_list) => Ok(raw_str_list
.lines()
.map(|l| {
l.split_whitespace()
.map(str::to_string)
.collect::<Vec<String>>()
})
.filter_map(|v| PartInfo::from_vect(&v))
.collect()),
Err(ex) => Err(format!(
"Exception while converting partition info to UTF8 {}",
ex
)),
}
} else {
Err(format!(
"Error getting device partitions list: {}",
String::from_utf8(cmd_output.stderr).unwrap_or("UNKNOWN".into())
))
}
}
Err(ex) => Err(ex.to_string()),
}
}
use toml::Table;
impl PartitionStage { impl PartitionStage {
pub fn gen_disk_layout_estimate(&self) -> Vec<PartInfo> { pub fn gen_disk_layout_estimate(&self) -> PartLayout {
let dev = self.device.clone().unwrap(); let dev = self.device.clone().unwrap();
let swap_size: u64 = match self.swap_mode { let swap_size: u64 = match self.swap_mode {
Some(SwapMode::NoSwap) => 0, Some(SwapMode::NoSwap) => 0,
Some(SwapMode::SwapHibernate) => 666, Some(SwapMode::SwapHibernate) => 666*1024*1024,
Some(SwapMode::SwapNoHibernate) => 1024, Some(SwapMode::SwapNoHibernate) => 1024*1024*1024,
_ => 1024, _ => 1024*1024*1024,
}; };
let mut res: Vec<PartInfo> = Vec::new(); let mut res: PartLayout = PartLayout::new(dev);
// UEFI partition // UEFI partition
res.push(PartInfo { res = res.add_part(256*1024*1024, FSType::VFAT, Some("kira-efi".into()), Some("EFI".into()), Some("/efi".into()))
name: "EFI".into(), .add_part(512*1024*1024, FSType::XFS, Some("kira-boot-a".into()), None, Some("/".into()))
part_type: "vfat".into(), .add_part_reserve(swap_size, FSType::XFS, Some("kira-home".into()), None, Some("/home".into()));
size_mb: 256,
});
// boot A and B
res.push(PartInfo {
name: "kira_boot_a".into(),
part_type: "xfs".into(),
size_mb: 512,
});
res.push(PartInfo {
name: "kira_boot_b".into(),
part_type: "xfs".into(),
size_mb: 512,
});
// user data
res.push(PartInfo {
name: "kira_data".into(),
part_type: "xfs".into(),
size_mb: dev.size_mb - 1024 - 256 - swap_size,
});
// swap // swap
if swap_size > 0 { if swap_size > 0 {
res.push(PartInfo { res = res.add_part(swap_size, FSType::SWAP, Some("kira-swap".into()), None, Some("SWAP".into()));
name: "".into(),
part_type: "swap".into(),
size_mb: swap_size,
});
} }
res res
} }
pub fn new(toml_config: &Table) -> Result<Self, String> { pub fn new(toml_config: &Table) -> Result<Self, String> {
let maybe_dev_list = get_disks_info_blocking(); let maybe_dev_list = BlkDev::list_sys_blk_dev();
let min_disk_size_mb = toml_config let min_disk_size_mb = toml_config
.get("partition") .get("partition")
@@ -295,13 +126,13 @@ impl PartitionStage {
.and_then(|v| v.as_integer()) .and_then(|v| v.as_integer())
.unwrap_or(8192); .unwrap_or(8192);
let min_disk_size_mb: u64 = min_disk_size_mb.try_into().unwrap_or(8192); let min_disk_size: u64 = min_disk_size_mb.try_into().unwrap_or(8192) * 1024 * 1024;
match maybe_dev_list { match maybe_dev_list {
Ok(dev_list) => { Ok(dev_list) => {
let acepted_dev: Vec<BlkDev> = dev_list let acepted_dev: Vec<BlkDev> = dev_list
.iter() .iter()
.filter(|v| v.size_mb > min_disk_size_mb) .filter(|v| v.size >= min_disk_size)
.cloned() .cloned()
.collect(); .collect();
@@ -341,11 +172,11 @@ impl PartitionStage {
pub fn update(&mut self, message: Message) -> StageAction { pub fn update(&mut self, message: Message) -> StageAction {
match message { match message {
Message::SelectDevice(dev) => { Message::SelectDevice(dev) => {
self.selected_disk_parts = match get_dev_part_blocking(&dev.name) { self.selected_disk_parts = match PartLayout::read_from_disk(&dev) {
Ok(dev_parts) => Some( Ok(dev_parts) => Some(
dev_parts dev_parts.part_list
.iter() .iter()
.map(|part_inf| part_inf.to_ct_params(dev.size_mb)) .map(|part_inf| part_to_ct_params(&part_inf, dev.size))
.collect(), .collect(),
), ),
Err(ex) => { Err(ex) => {
@@ -377,11 +208,11 @@ impl PartitionStage {
pub fn view(&self) -> iced::Element<'_, Message> { pub fn view(&self) -> iced::Element<'_, Message> {
let dev_parts_es: Option<Vec<(String, u16, Color)>> = if self.selected_disk_parts.is_some() let dev_parts_es: Option<Vec<(String, u16, Color)>> = if self.selected_disk_parts.is_some()
{ {
let p_l = self.gen_disk_layout_estimate();
Some( Some(
self.gen_disk_layout_estimate() p_l.part_list.iter()
.iter() .map(|part_info| part_to_ct_params(part_info, p_l.dev.size))
.map(|part_inf| part_inf.to_ct_params(self.device.clone().unwrap().size_mb)) .collect()
.collect(),
) )
} else { } else {
None None