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")),