From 3b086605cf007aba9bbbc771108536ca84db0f28c4159ae6b5a27fe43056bf6d Mon Sep 17 00:00:00 2001 From: kira Date: Mon, 28 Sep 2026 00:13:12 +0200 Subject: [PATCH] Work on making partitions start-end calculate properly. Specifically 2mb at start and -33 sectors GPT reserved at the end. --- src/kira_disk_layout.rs | 98 ++++++++++++++++++++++++++++++++--------- src/kira_system.rs | 53 +++++++++++++--------- 2 files changed, 108 insertions(+), 43 deletions(-) diff --git a/src/kira_disk_layout.rs b/src/kira_disk_layout.rs index 4a1ad4a..1f317cb 100644 --- a/src/kira_disk_layout.rs +++ b/src/kira_disk_layout.rs @@ -20,7 +20,7 @@ use crate::kira_size::KiraSize; use log; -use std::{collections::HashMap}; +use std::collections::HashMap; /// /// size - device size in bytes /// sector size - default 4096 @@ -117,20 +117,25 @@ impl BlkDev { } #[derive(Debug, Clone, PartialEq, Eq)] -pub struct BcachefsProp { +pub struct BcachefsProp { pub password: Option, pub comp_alg: Option, } impl BcachefsProp { pub fn new(password: Option, comp_alg: Option) -> Self { - Self { password:password, comp_alg: comp_alg } + Self { + password: password, + comp_alg: comp_alg, + } } pub fn default() -> Self { - Self { password: None, comp_alg: None } + Self { + password: None, + comp_alg: None, + } } - } #[derive(Debug, Clone, PartialEq, Eq)] @@ -208,7 +213,7 @@ impl FSType { pub enum PartRole { EFI, ROOT, - SWAP + SWAP, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -230,6 +235,22 @@ impl std::fmt::Display for PartInfo { } impl PartInfo { + /// + /// calculate par end byte as 0 starting bytes index (for parted and such) + /// + pub fn part_end(&self) -> KiraSize { + // important to shift here by 1 because disk index is starting from 0, and part end pointing to last byte of the partition. + (self.start + self.size) - KiraSize::new_b(1) + } + + /// + /// Returns zero based byte index of start of next (right after this one) partition + /// + pub fn next_part_start(&self) -> KiraSize { + // important to shift here by 1 because disk index is starting from 0, and part end pointing to last byte of the partition. + self.start + self.size + } + pub fn from_hash_map(data: &HashMap) -> Option { Some(Self { name: data.get("NAME")?.clone(), @@ -250,7 +271,9 @@ impl PartInfo { } pub fn partlabel_dev(&self) -> Option { - self.gpt_label.as_ref().and_then(|l| Some(format!("/dev/disk/by-partlabel/{}", l))) + self.gpt_label + .as_ref() + .and_then(|l| Some(format!("/dev/disk/by-partlabel/{}", l))) } } @@ -329,7 +352,7 @@ pub fn align_part(size: u64, start: u64, align: u64) -> Option<(u64, u64, u64)> // decreasing end if not align let part_end = align_start + size; let end_mod = part_end % align; - log::debug!("part_end: {} end_mod: {}", part_end, end_mod); + let align_end = part_end - end_mod - 1; let align_size = align_end - align_start + 1; @@ -342,7 +365,13 @@ pub fn align_part(size: u64, start: u64, align: u64) -> Option<(u64, u64, u64)> ); return None; } - + log::debug!( + "align_size: {}, align_start:{}, align_end: {} end_mod: {}", + align_size, + align_start, + align_end, + end_mod + ); Some((align_size, align_start, align_end)) } @@ -353,10 +382,16 @@ pub struct PartLayout { pub part_list: Vec, } +/// +/// GPT requires 33x512 sectors at the very end of the disk (16,896 bytes = 16.5 KiB): +/// 32 sectors — backup Partition Entry Array (128 entries × 128 bytes = 16,384 bytes) +/// 1 sector — backup GPT header impl PartLayout { pub fn new(dev: BlkDev) -> Self { + // 16896 - reserved space at the dev end for gpt + let usable_space = dev.size - KiraSize::new_b(16896); Self { - empty_space: dev.size, + empty_space: usable_space, dev: dev, part_list: Vec::new(), } @@ -374,11 +409,11 @@ impl PartLayout { }) } - pub fn new_table(mut self) -> Self { - self.empty_space = self.dev.size; - self.part_list = Vec::new(); - self - } + // 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, @@ -390,19 +425,29 @@ impl PartLayout { role: Option, ) -> Self { let part_start = if self.part_list.is_empty() { + // need to substract 2 mb offset from disk start. + self.empty_space = self.empty_space - KiraSize::new_mb(2); KiraSize::new_mb(2) } else { - self.dev.size - self.empty_space + // last part end + 1 + self.part_list.last().unwrap().next_part_start() }; - let (size, start, _) = + let (a_size, a_start, _) = align_part(part_size.b(), part_start.b(), self.dev.sector_size.b()).unwrap(); + log::debug!( + "PartLayout.add_part gpt_label{:?}, size_asked:{}, size_al:{}", + gpt_label, + part_size, + a_size + ); + let part_name = format!("{}p{}", self.dev.name, self.part_list.len() + 1); self.part_list.push(PartInfo { name: part_name, - size: KiraSize::new_b(size), - start: KiraSize::new_b(start), + size: KiraSize::new_b(a_size), + start: KiraSize::new_b(a_start), fs_type: fs_type, gpt_label: gpt_label, fs_label: fs_label, @@ -410,10 +455,14 @@ impl PartLayout { role: role, }); - self.empty_space = self.empty_space - KiraSize::new_b(size); + self.empty_space = self.empty_space - KiraSize::new_b(a_size); self } + /// + /// Use this to add partition of (disk_empty_space - reserve_space) + /// Specifically to leave some space at the end for SWAP and such + /// pub fn add_part_reserve( self, reserve_space: KiraSize, @@ -427,7 +476,13 @@ impl PartLayout { self.add_part(p_size, fs_type, gpt_label, fs_label, mount_point, role) } - pub fn gen_kira_layout(dev: BlkDev, efi_size: KiraSize, swap_size: KiraSize, maybe_password: Option, maybe_compr: Option) -> Self { + pub fn gen_kira_layout( + dev: BlkDev, + efi_size: KiraSize, + swap_size: KiraSize, + maybe_password: Option, + maybe_compr: Option, + ) -> Self { let mut res: PartLayout = PartLayout::new(dev); // UEFI partition @@ -465,7 +520,6 @@ impl PartLayout { } } - #[cfg(test)] mod tests { use rand::RngExt; diff --git a/src/kira_system.rs b/src/kira_system.rs index e3c3f41..63e60ec 100644 --- a/src/kira_system.rs +++ b/src/kira_system.rs @@ -19,18 +19,25 @@ */ use crate::kira_disk_layout::*; -use std::process::Command; +use crate::kira_size::KiraSize; use std::fs; +use std::process::Command; #[derive(Debug)] -pub struct CommandFailed{ +pub struct CommandFailed { stderr: Vec, 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()})) + 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() }) + ) } } @@ -124,7 +131,10 @@ pub fn dinit_sync_time_chrony() -> Result<(), ExecCommandError> { /// 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"]) + exec_command( + "basestrap", + &vec![root_dir, "base", "base-devel", "dinit", "elogind-dinit"], + ) } /// Executes parted command with given arguments @@ -151,12 +161,7 @@ fn parted_get_last_part_num(dev: &BlkDev) -> Result { /// Its blocking function pub fn parted_init_gpt_part_table(dev: &BlkDev) -> Result<(), ExecCommandError> { let dev_full_name = dev.full_name(); - let args = vec![ - "--script", - &dev_full_name, - "mklabel", - "gpt", - ]; + let args = vec!["--script", &dev_full_name, "mklabel", "gpt"]; exec_command("parted", &args) } @@ -166,7 +171,8 @@ const LINUX_ROOT_X86_64_TYPE: &str = "4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709"; /// 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; + // important to shift here by 1 because disk index is starting from 0, and part end pointing to last byte of the partition. + let part_end = part.part_end(); // gpt label should be in double qutes let gpt_label = if let Some(s) = &part.gpt_label { @@ -174,6 +180,14 @@ pub fn parted_mkpart(dev: &BlkDev, part: &PartInfo) -> Result<(), ExecCommandErr } else { "\"\"".to_string() }; + log::debug!( + "Creatin partition dev:{}, label:{}, fs_type:{}, start:{}, end:{}", + dev.full_name(), + gpt_label, + part.fs_type.to_parted_str(), + part.start.to_parted_bytes_str(), + part_end.to_parted_bytes_str() + ); // creating partition exec_parted(&vec![ "--script", @@ -218,7 +232,7 @@ pub fn parted_mkpart(dev: &BlkDev, part: &PartInfo) -> Result<(), ExecCommandErr } /// creating single drive bcachefs volume -/// compression: lz4, zstd +/// compression: lz4, zstd pub fn bcachefs_format( dev_name: &str, compression: Option, @@ -255,13 +269,11 @@ pub fn bcachefs_format( /// 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"); @@ -292,10 +304,10 @@ pub fn mkfs_fat32(dev: &str, maybe_label: Option<&str>) -> Result<(), ExecComman // if need to set label if let Some(label) = maybe_label { - fatlabel(dev, label) - } else { - Ok(()) - } + fatlabel(dev, label) + } else { + Ok(()) + } } /// mkfs.fat -F 32 /dev/sda1 @@ -304,8 +316,7 @@ 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 + //mkswap -L SWAP /dev/sda4 exec_command("mkswap", &vec!["L", "SWAP", dev]) -} \ No newline at end of file +}