From 2b2a5160fdcf9078118f0a0a93ae194b21ed1821 Mon Sep 17 00:00:00 2001 From: 2Shirt <2xShirt@gmail.com> Date: Sat, 15 Feb 2025 20:30:40 -0800 Subject: [PATCH] Add boot diag tasks --- boot_diags/src/app.rs | 188 +++++++++++++++++++++++++++++++++++----- boot_diags/src/diags.rs | 52 ++++++++++- deja_vu/src/app.rs | 10 +-- 3 files changed, 219 insertions(+), 31 deletions(-) diff --git a/boot_diags/src/app.rs b/boot_diags/src/app.rs index 509bcfd..1ac444d 100644 --- a/boot_diags/src/app.rs +++ b/boot_diags/src/app.rs @@ -13,7 +13,7 @@ // You should have received a copy of the GNU General Public License // along with Deja-Vu. If not, see . // -use crate::diags::Groups as DiagGroups; +use crate::diags; use core::{ action::Action, components::{ @@ -26,6 +26,7 @@ use core::{ system::{ boot::{self, SafeMode}, cpu::get_cpu_name, + disk::PartitionTableType, drivers, }, tasks::{Task, TaskResult, TaskType, Tasks}, @@ -34,6 +35,7 @@ use core::{ use std::{ env, iter::zip, + path::PathBuf, sync::{Arc, Mutex}, }; @@ -53,7 +55,7 @@ pub struct App { action_tx: mpsc::UnboundedSender, components: Vec>, config: Config, - diag_groups: DiagGroups, + diag_groups: diags::Groups, frame_rate: f64, last_tick_key_events: Vec, should_quit: bool, @@ -94,7 +96,7 @@ impl App { Box::new(popup::Popup::new()), ], config: Config::new()?, - diag_groups: DiagGroups::new(), + diag_groups: diags::Groups::new(), frame_rate, last_tick_key_events: Vec::new(), should_quit: false, @@ -180,13 +182,126 @@ impl App { self.list.select_first_item(); } Mode::BootScan => { - if self.tasks.idle() { - self.tasks.add(TaskType::Sleep); - } self.action_tx.send(Action::DisplayPopup( popup::Type::Info, String::from("Gathering info..."), ))?; + + // Get System32 path + let system32 = if cfg!(windows) { + if let Ok(path) = env::var("SYSTEMROOT") { + format!("{path}/System32") + } else { + self.action_tx.send(Action::Error(String::from( + "ERROR\n\n\nFailed to find SYSTEMROOT", + )))?; + return Ok(()); + } + } else { + String::from(".") + }; + + // Add tasks + let disk_list = self.clone.disk_list.lock().unwrap(); + if let Some(disk_index) = self.clone.disk_index_dest { + if let Some(disk) = disk_list.get(disk_index) { + let table_type = disk.part_type.clone(); + let letter_boot = disk.get_part_letter(self.clone.part_index_boot.unwrap()); + let letter_os = disk.get_part_letter(self.clone.part_index_os.unwrap()); + + // Safety check + if letter_boot.is_empty() || letter_os.is_empty() { + self.action_tx.send(Action::Error(String::from( + "ERROR\n\n\nFailed to get drive letters for the destination", + )))?; + return Ok(()); + } + + // BCD + self.tasks.add(TaskType::Command( + PathBuf::from(format!("{system32}/bcdedit.exe")), + vec![ + String::from("/store"), + format!( + "{letter_boot}{}\\Boot\\BCD", + if table_type == PartitionTableType::Guid { + "\\EFI\\Microsoft" + } else { + "" + } + ), + String::from("/enum"), + ], + )); + + // Bitlocker + self.tasks.add(TaskType::Command( + PathBuf::from(format!("{system32}/manage-bde.exe")), + vec![String::from("-status"), format!("{letter_os}:")], + )); + self.tasks.add(TaskType::Command( + PathBuf::from(format!("{system32}/manage-bde.exe")), + vec![ + String::from("-protectors"), + String::from("-get"), + format!("{letter_os}:"), + ], + )); + + // DISM Health + self.tasks.add(TaskType::Command( + PathBuf::from(format!("{system32}/dism.exe")), + vec![format!("{letter_os}:")], + )); + + // Filesystem Health + self.tasks.add(TaskType::Command( + PathBuf::from(format!("{system32}/chkdsk.exe")), + vec![format!("{letter_os}:")], + )); + + // Registry + self.tasks.add(TaskType::Command( + PathBuf::from(format!("{system32}/reg.exe")), + vec![ + String::from("load"), + String::from("HKLM\\TmpSoftware"), + format!("{letter_os}:\\Windows\\System32\\config\\SOFTWARE"), + ], + )); + self.tasks.add(TaskType::Command( + PathBuf::from(format!("{system32}/reg.exe")), + vec![ + String::from("load"), + String::from("HKLM\\TmpSystem"), + format!("{letter_os}:\\Windows\\System32\\config\\SYSTEM"), + ], + )); + self.tasks.add(TaskType::Command( + PathBuf::from(format!("{system32}/reg.exe")), + vec![ + String::from("load"), + String::from("HKU\\TmpDefault"), + format!("{letter_os}:\\Windows\\System32\\config\\DEFAULT"), + ], + )); + self.tasks.add(TaskType::Command( + PathBuf::from(format!("{system32}/reg.exe")), + vec![String::from("unload"), String::from("HKLM\\TmpSoftware")], + )); + self.tasks.add(TaskType::Command( + PathBuf::from(format!("{system32}/reg.exe")), + vec![String::from("unload"), String::from("HKLM\\TmpSystem")], + )); + self.tasks.add(TaskType::Command( + PathBuf::from(format!("{system32}/reg.exe")), + vec![String::from("unload"), String::from("HKU\\TmpDefault")], + )); + + // Files/Folders + // TODO: Check for critical folders (e.g. /Windows, /Windows/System32, etc) + } + } } Mode::InjectDrivers | Mode::InstallDrivers => self.clone.scan_drivers(), Mode::ScanDisks => { @@ -414,8 +529,9 @@ impl App { } fn handle_task(&mut self, task: &Task) -> Result<()> { + info!("Handling Task: {task:?}"); match self.cur_mode { - Mode::BootDiags => { + Mode::BootScan => { if let TaskType::Command(cmd_path, cmd_args) = &task.task_type { let mut cmd_name = ""; if let Some(path) = cmd_path.file_name() { @@ -423,9 +539,9 @@ impl App { cmd_name = cmd_str; } }; - match cmd_name { - "bcdedit" => { - // Boot config + let diag_type = diags::get_type(cmd_name); + match diag_type { + diags::Type::BootConfigData => { let title = "Boot Files"; if let Some(result) = &task.result { let passed: bool; @@ -449,18 +565,10 @@ impl App { .update(title.to_string(), passed, info.to_owned()); } } - "manage-bde" => { - // Bitlocker - } - "chkdsk" => { - // File system - } - "reg" => { - // Registry - } - "dir" => { - // Files/Folders - } + diags::Type::Bitlocker => {} + diags::Type::FileSystem => {} + diags::Type::Registry => {} + diags::Type::FilesAndFolders => {} _ => { warn!("Unrecognized command: {:?}", &cmd_path) } @@ -660,7 +768,19 @@ fn build_left_items(app: &App) -> Action { } } } - Mode::BootDiags | Mode::BootSetup => { + Mode::BootDiags => { + select_num = 0; + let (new_title, _) = get_mode_strings(app.cur_mode); + title = new_title; + app.diag_groups.get().iter().for_each(|group| { + items.push(if group.passed { + group.title.clone() + } else { + format!("{} - Issues detected!", group.title) + }); + }); + } + Mode::BootSetup => { select_num = 0; let (new_title, _) = get_mode_strings(app.cur_mode); title = new_title; @@ -715,6 +835,14 @@ fn build_right_items(app: &App) -> Action { )], line_colors: vec![Color::Reset], }, + DVLine { + line_parts: vec![String::from("-----")], + line_colors: vec![Color::Reset], + }, + DVLine { + line_parts: vec![format!("diag_groups: {:?}", &app.diag_groups)], + line_colors: vec![Color::Yellow], + }, DVLine { line_parts: vec![String::from("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")], line_colors: vec![Color::Reset], @@ -774,6 +902,20 @@ fn build_right_items(app: &App) -> Action { ]); }); } + Mode::BootDiags => { + app.diag_groups.get().iter().for_each(|group| { + let mut lines = Vec::new(); + group.info.iter().for_each(|text| { + text.lines().for_each(|line| { + lines.push(DVLine { + line_parts: vec![String::from(line)], + line_colors: vec![Color::Reset], + }); + }); + }); + items.push(lines); + }); + } Mode::InjectDrivers | Mode::InstallDrivers => { items.push(vec![DVLine { line_parts: vec![String::from("CPU")], diff --git a/boot_diags/src/diags.rs b/boot_diags/src/diags.rs index 33c5275..70b5741 100644 --- a/boot_diags/src/diags.rs +++ b/boot_diags/src/diags.rs @@ -15,21 +15,44 @@ use std::collections::HashMap; +pub enum Type { + Bitlocker, + BootConfigData, + FileSystem, + FilesAndFolders, + Registry, + Unknown, +} + +#[derive(Debug)] pub struct Groups { items: HashMap, + order: Vec, } impl Groups { pub fn new() -> Self { Groups { items: HashMap::new(), + order: Vec::new(), } } + pub fn get(&self) -> Vec<&Line> { + let mut lines = Vec::new(); + self.order.iter().for_each(|key| { + if let Some(line) = self.items.get(key) { + lines.push(line); + } + }); + lines + } + pub fn update(&mut self, title: String, passed: bool, info: String) { if let Some(line) = self.items.get_mut(&title) { line.update(passed, info); } else { + self.order.push(title.clone()); self.items.insert( title.clone(), Line { @@ -42,10 +65,11 @@ impl Groups { } } +#[derive(Clone, Debug)] pub struct Line { - title: String, - passed: bool, - info: Vec, + pub title: String, + pub passed: bool, + pub info: Vec, } impl Line { @@ -54,3 +78,25 @@ impl Line { self.info.push(info); } } + +pub fn get_type(cmd_name: &str) -> Type { + if cmd_name == "exa" { + return Type::BootConfigData; + } + if cmd_name == "bcdedit" { + return Type::BootConfigData; + } + if cmd_name == "dir" { + return Type::FilesAndFolders; + } + if cmd_name == "reg" { + return Type::Registry; + } + if cmd_name == "chkdsk" { + return Type::FileSystem; + } + if cmd_name == "manage-bde" { + return Type::Bitlocker; + } + Type::Unknown +} diff --git a/deja_vu/src/app.rs b/deja_vu/src/app.rs index 293be47..9c7a125 100644 --- a/deja_vu/src/app.rs +++ b/deja_vu/src/app.rs @@ -173,6 +173,11 @@ impl App { ))?; } Mode::PreClone => { + self.action_tx.send(Action::DisplayPopup( + popup::Type::Info, + String::from("Formatting destination disk"), + ))?; + // Get System32 path let system32 = if cfg!(windows) { if let Ok(path) = env::var("SYSTEMROOT") { @@ -187,11 +192,6 @@ impl App { String::from(".") }; - self.action_tx.send(Action::DisplayPopup( - popup::Type::Info, - String::from("Formatting destination disk"), - ))?; - // (Re)Enable volume mounting self.tasks.add(TaskType::Command( PathBuf::from(format!("{system32}/mountvol.exe")),