Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,11 @@ jobs:
- uses: actions/checkout@v4
- name: Build
run: cargo build

test-windows:
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The job name 'test-windows' is inconsistent with the existing job name 'test', which runs on Linux. For consistency and clarity, consider renaming the existing job to 'test-linux' or renaming this job to something that better reflects it's a build-only job (like 'build-windows'), since neither job actually runs tests.

Copilot uses AI. Check for mistakes.
runs-on: windows-latest

steps:
- uses: actions/checkout@v4
- name: Build
run: cargo build
94 changes: 84 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 10 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,21 @@ derive_more = { version = "2.1.1", features = ["from", "into", "from_str", "add"
dirs = "6.0.0"
efivar = "2.0.0"
inquire = "0.9.2"
libc = "0.2.180"
miette = { version = "7.6.0", features = ["fancy"] }
serde = { version = "1.0.228", features = ["derive"] }
serde_yaml = "0.9"
system_shutdown = "4.1.0"
which = "8.0.0"

[target.'cfg(unix)'.dependencies]
libc = "0.2.180"

[target.'cfg(target_os = "linux")'.dependencies]
nix = { version = "0.31.1", features = ["fs", "user"] }

[target.'cfg(target_os = "windows")'.dependencies]
windows-sys = { version = "0.59", features = [
"Win32_Security",
"Win32_System_Threading",
"Win32_Foundation",
] }
6 changes: 6 additions & 0 deletions src/bin/it.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ fn main() {
Err(e) => {
eprintln!("Error: Failed to execute 'bootit' command: {}", e);
eprintln!("Make sure 'bootit' is installed and in your PATH.");

#[cfg(windows)]
eprintln!(
"\nIf you are not running as Administrator, try opening an elevated terminal."
);

std::process::exit(127);
}
};
Expand Down
10 changes: 8 additions & 2 deletions src/command/allow_non_admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,15 @@ pub fn allow_non_admin(it_path: Option<PathBuf>) -> miette::Result<()> {
println!("Non-admin users can now use it command to boot.");
}

#[cfg(not(target_os = "linux"))]
#[cfg(target_os = "windows")]
{
println!("Setting setuid bit is not supported on this operating system.");
println!("The setuid mechanism is not available on Windows.");
println!("Run bootit from an elevated terminal (right-click -> Run as administrator).");
}

#[cfg(not(any(target_os = "linux", target_os = "windows")))]
{
println!("allow-non-admin is not supported on this operating system.");
}

Ok(())
Expand Down
43 changes: 43 additions & 0 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,52 @@ pub fn check_privileges() -> miette::Result<()> {
));
}
}

#[cfg(windows)]
{
if !is_elevated() {
return Err(miette!(
"This program must be run as Administrator (try: run terminal as administrator)"
));
}
}

Ok(())
}

#[cfg(windows)]
fn is_elevated() -> bool {
use std::ptr::null_mut;
use windows_sys::Win32::Foundation::{CloseHandle, HANDLE};
use windows_sys::Win32::Security::{
GetTokenInformation, TokenElevation, TOKEN_ELEVATION, TOKEN_QUERY,
};
use windows_sys::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken};

unsafe {
let mut token_handle: HANDLE = null_mut();
if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token_handle) == 0 {
return false;
}

let mut elevation = TOKEN_ELEVATION { TokenIsElevated: 0 };
let mut return_length: u32 = 0;
let size = std::mem::size_of::<TOKEN_ELEVATION>() as u32;

let result = GetTokenInformation(
token_handle,
TokenElevation,
&mut elevation as *mut _ as *mut _,
size,
&mut return_length,
);

CloseHandle(token_handle);

result != 0 && elevation.TokenIsElevated != 0
Comment on lines +48 to +58
Copy link

Copilot AI Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If GetTokenInformation fails (returns 0), the handle is still closed properly, but the function returns false without distinguishing between "not elevated" and "failed to check elevation". This might be acceptable for this use case, but consider whether failed API calls should be logged or handled differently to aid debugging.

Copilot uses AI. Check for mistakes.
}
}

#[allow(unused)]
pub fn find_it() -> miette::Result<PathBuf> {
if let Ok(path) = which("it") {
Expand Down
Loading