From edfd57d5d510d4f39b34e0840c9a560ef8ad5a18d5a6629b657c3c86de359220 Mon Sep 17 00:00:00 2001 From: Kira Date: Tue, 19 May 2026 23:52:20 +0200 Subject: [PATCH] Working on partitions stuff related to issue #2 --- src/kira_disk_layout.rs | 302 ++++++++++++++++++++++++++++++++++++ src/main.rs | 1 + src/stages/keyboard/mod.rs | 2 +- src/stages/network/mod.rs | 1 - src/stages/partition/mod.rs | 3 +- src/stages/timezone/mod.rs | 2 +- src/stages/welcome/mod.rs | 3 +- 7 files changed, 307 insertions(+), 7 deletions(-) create mode 100644 src/kira_disk_layout.rs diff --git a/src/kira_disk_layout.rs b/src/kira_disk_layout.rs new file mode 100644 index 0000000..faae86d --- /dev/null +++ b/src/kira_disk_layout.rs @@ -0,0 +1,302 @@ +use std::collections::HashMap; + +pub fn format_bytes_size(size: u64) -> String { + let suffixs: [&str; 5] = ["KB", "MB", "GB", "TB", "PB"]; + + if size < 1000 { + format!("{}B", size) + } else { + let mut s = size as f64; + let mut idx = 0; + for i in 0..5 { + s = s / 1024.0; + idx = i; + if s < 1000.0 { + break; + } + } + format!("{:.2}{}", s, suffixs[idx]) + } +} + +/// +/// size - device size in bytes +/// sector size - default 4096 +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BlkDev { + name: String, + size: u64, + sector_size: u32, +} + +impl std::fmt::Display for BlkDev { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} ({})", &self.name, format_bytes_size(self.size)) + } +} + +impl BlkDev { + pub fn new(name: String, size: u64) -> Self { + Self { + name: name, + size: size, + sector_size: 4096, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum FSType { + VFAT, + XFS, + LUKS, + SWAP, + EXT4, + BTRFS, + BCACHEFS, + UNKNOWN, +} + +impl std::fmt::Display for FSType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::VFAT => write!(f, "vfat"), + Self::XFS => write!(f, "xfs"), + Self::LUKS => write!(f, "luks"), + Self::SWAP => write!(f, "swap"), + Self::EXT4 => write!(f, "ext4"), + Self::BTRFS => write!(f, "btrfs"), + Self::BCACHEFS => write!(f, "bcachefs"), + Self::UNKNOWN => write!(f, "unknown"), + } + } +} + +impl FSType { + pub fn from_str(s: &str) -> Self { + let s_low = s.to_lowercase(); + match s_low.as_str() { + "vfat" => Self::VFAT, + "xfs" => Self::XFS, + "crypto_luks" => Self::LUKS, + "swap" => Self::SWAP, + "ext4" => Self::EXT4, + "btrfs" => Self::BTRFS, + "bcachefs" => Self::BCACHEFS, + _ => Self::UNKNOWN, + } + } +} + +// fn part_type_to_color(t: &str) -> Color { +// match t { +// "vfat" => Color::from_rgb8(204, 121, 167), +// "xfs" => Color::from_rgb8(0, 158, 115), +// "crypto_LUKS" => Color::from_rgb8(240, 228, 66), +// "swap" => Color::from_rgb8(213, 94, 0), +// "ext4" => Color::from_rgb8(0, 114, 178), +// "btrfs" => Color::from_rgb8(86, 180, 233), +// _ => Color::from_rgb8(230, 159, 0), +// } +// } + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PartInfo { + name: String, + size: u64, + fs_type: FSType, + gpt_label: Option, + fs_label: Option, + mount_point: Option, +} + +impl std::fmt::Display for PartInfo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{} {} {}", + &self.name, + &self.fs_type, + format_bytes_size(self.size) + ) + } +} + +impl PartInfo { + pub fn from_hash_map(data: &HashMap) -> Option { + Some(Self { + name: data.get("NAME")?.clone(), + size: u64::from_str_radix(data.get("SIZE")?, 10).ok()?, + fs_type: FSType::from_str(data.get("FSTYPE")?), + gpt_label: data + .get("PARTLABEL") + .and_then(|v| if v.is_empty() { None } else { Some(v.clone()) }), + fs_label: data + .get("LABEL") + .and_then(|v| if v.is_empty() { None } else { Some(v.clone()) }), + mount_point: data + .get("MOUNTPOINT") + .and_then(|v| if v.is_empty() { None } else { Some(v.clone()) }), + }) + } +} + +/// getting list of device partitions +fn get_dev_part_blocking(dev_name: &str) -> Result, String> { + use std::process::Command; + + //"lsblk -o NAME,SIZE,FSTYPE -l -n /dev/nvme0n1" + // lsblk -o NAME,SIZE,FSTYPE,TYPE,PARTLABEL,LABEL,PARTFLAGS -b -P + + match Command::new("lsblk") + .args([ + "-o", + "NAME,SIZE,FSTYPE,TYPE,PARTLABEL,LABEL,MOUNTPOINT,PARTFLAGS", + "-b", + "-P", + 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() + .filter_map(|pair| { + pair.split_once("=").and_then(|kv| { + Some(( + kv.0.trim_matches('"').to_string(), + kv.1.trim_matches('"').to_string(), + )) + }) + }) + .collect::>() + }) + .filter(|mp| mp.get("TYPE").is_some_and(|v| v == "part")) + .filter_map(|mp| PartInfo::from_hash_map(&mp)) + .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()), + } +} + +/// +/// size - size in bytes +/// start - start byte of partition, inclusive index. +/// returns tuple of aligned size, start index, and end index +/// return none if aligment is not possibe (size of partition is less than align) +pub fn align_part(size: u64, start: u64, align: u64) -> Option<(u64, u64, u64)> { + if size < align { + println!("if size < align !!!!!!!"); + return None; + } + + let start_mod = start % align; + // increasing start if not align + let align_start = if start_mod > 0 { + start + (align - start_mod) + } else { + start + }; + + // decreasing end if not align + let part_end = align_start + size; + let end_mod = part_end % align; + println!("part_end: {} end_mod: {}", part_end, end_mod); + let align_end = part_end - end_mod - 1; + + let align_size = align_end - align_start + 1; + + if align_size < align { + println!("if align_size < align !!!!!!! align_size: {} align: {}",align_size,align); + return None; + } + + Some((align_size, align_start, align_end)) +} + +pub struct PartLayout { + dev: BlkDev, + empty_space: u64, + part_list: Vec, +} + +impl PartLayout { + pub fn new(dev: BlkDev) -> Self { + Self { + empty_space: dev.size, + dev: dev, + part_list: Vec::new(), + } + } + + pub fn read_from_dev(dev: &BlkDev) -> Result { + let part_list = get_dev_part_blocking(&dev.name)?; + let parts_size = part_list.iter().fold(0, |acc, part| acc + part.size); + Ok(Self { + dev: dev.clone(), + empty_space: dev.size - parts_size, + part_list: part_list, + }) + } + + pub fn new_table(mut self) -> Self { + self.empty_space = self.dev.size; + self.part_list = Vec::new(); + self + } + + pub fn add_part( + mut self, + part_size: u64, + fs_type: FSType, + gpt_label: Option, + fs_label: Option, + mount_point: Option, + ) -> Self { + self + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_align_part_fn() { + //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![ + (0, 10000, 4096, 0, 8191, 8192), + (5000, 20000, 4096, 8192, 24575, 16384), + (4096, 4096, 4096, 4096, 8191, 4096), + (1000000, 5000000, 1048576, 1048576, 5242879, 4194304), + (2097152, 1050000, 1048576, 2097152, 3145727, 1048576), + ]; + for test_data in joes_test_data { + let res = align_part(test_data.1,test_data.0,test_data.2).unwrap(); + let test_res = (test_data.5, test_data.3, test_data.4); + assert_eq!(res, test_res); + } + } +} + +// fn add_part(part_list: Vec, rem_size: u64, part_t: PartType, ) + +// fn gen_layout(dev: &BlkDev, swap_size: u64) -> Vec { + +// Vec::new() +// } diff --git a/src/main.rs b/src/main.rs index 1b9c877..fe3e013 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,6 +35,7 @@ rust_i18n::i18n!("src/locales", fallback = "en"); mod kira_theming; mod kira_scroll_list; mod kira_color_bar; +mod kira_disk_layout; mod stage; mod stages; diff --git a/src/stages/keyboard/mod.rs b/src/stages/keyboard/mod.rs index 2543b5b..9b96ac8 100644 --- a/src/stages/keyboard/mod.rs +++ b/src/stages/keyboard/mod.rs @@ -19,7 +19,7 @@ */ use crate::kira_scroll_list::{self, ListViewMessage}; -use crate::stage::{ConfigValue, StageAction, StageResult}; +use crate::stage::{StageAction, StageResult}; use iced::{Alignment, Length, widget}; use rust_i18n::t; use std::collections::HashMap; diff --git a/src/stages/network/mod.rs b/src/stages/network/mod.rs index 8c88659..b3c5cf4 100644 --- a/src/stages/network/mod.rs +++ b/src/stages/network/mod.rs @@ -22,7 +22,6 @@ use iced::{Alignment, Task, widget}; use iced_moving_picture::widget::apng; use rust_i18n::t; -use std::collections::HashMap; use toml::Table; use crate::stage; diff --git a/src/stages/partition/mod.rs b/src/stages/partition/mod.rs index f5156e3..8edcdf7 100644 --- a/src/stages/partition/mod.rs +++ b/src/stages/partition/mod.rs @@ -18,12 +18,11 @@ This is partition stage! */ -use crate::stage::{ConfigValue, StageAction, StageResult}; +use crate::stage::{StageAction, StageResult}; use crate::kira_color_bar; use iced::{Alignment, Color, Length, widget}; use rust_i18n::t; -use std::collections::HashMap; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SwapMode { diff --git a/src/stages/timezone/mod.rs b/src/stages/timezone/mod.rs index 0416527..1d568fb 100644 --- a/src/stages/timezone/mod.rs +++ b/src/stages/timezone/mod.rs @@ -18,7 +18,7 @@ This is TimeZone stage, used to select timezone */ -use crate::stage::{ConfigValue, StageAction, StageResult}; +use crate::stage::{StageAction, StageResult}; use iced::{Alignment, Length, widget}; use rust_i18n::t; use std::collections::HashMap; diff --git a/src/stages/welcome/mod.rs b/src/stages/welcome/mod.rs index bfad8ac..bd06716 100644 --- a/src/stages/welcome/mod.rs +++ b/src/stages/welcome/mod.rs @@ -20,10 +20,9 @@ */ -use crate::{stage::{ConfigValue, StageAction, StageResult}, kira_theming}; +use crate::{stage::{StageAction, StageResult}, kira_theming}; use iced::{Alignment, widget}; use rust_i18n::t; -use std::collections::HashMap; #[derive(Debug, Clone, PartialEq, Eq)] pub struct LocData {