From 7d301aea61acf18b08a794bb091d7eed7ff57938 Mon Sep 17 00:00:00 2001 From: Robert M1 <50460704+githubrobbi@users.noreply.github.com> Date: Fri, 19 Jun 2026 17:02:18 -0700 Subject: [PATCH] fix(cli): non-elevated daemon management when the broker is serving (Windows) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Windows the daemon-management elevation gate (`mutating_management_needs_elevation`) was hardcoded to `true`, so EVERY stop/restart needed an elevated shell — even with the Access Broker installed. That broke the non-elevated `uffs --update` on Windows: its quiesce drives `uffs --daemon stop`, the gate refused ("Daemon management commands require an elevated (Administrator) shell"), the daemon never stopped, and apply rolled back ("daemon did not stop within 20s"). It defeats the whole point of the broker (zero-UAC operation) and forced users into an elevated window to update. Fix: on Windows, require elevation only when the broker pipe is NOT serving. With the broker up the daemon runs non-elevated and a non-elevated caller can stop AND restart it (restart adopts broker handles — no UAC), so there's no risk of stranding a daemon that can't be brought back. Without the broker, a restart would need admin to re-read the MFT, so elevation is still required. Mirrors the Unix path, which already gates on PID-file ownership not a blanket `true`. The old `#[cfg(not(unix))]` `true` splits into `#[cfg(windows)]` (broker-aware) and `#[cfg(not(any(unix, windows)))]` (conservative `true`). Verified: macOS build + windows-msvc cross-build + windows clippy all clean. Co-Authored-By: Claude Opus 4.8 --- crates/uffs-cli/src/commands/daemon_mgmt.rs | 26 ++++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/crates/uffs-cli/src/commands/daemon_mgmt.rs b/crates/uffs-cli/src/commands/daemon_mgmt.rs index 7ddb8a0b3..0f599ba3b 100644 --- a/crates/uffs-cli/src/commands/daemon_mgmt.rs +++ b/crates/uffs-cli/src/commands/daemon_mgmt.rs @@ -24,7 +24,9 @@ use crate::commands::{daemon_load, daemon_tiering}; /// /// Read-only queries (`status`, `stats`, `status_drives`) are always /// permitted without elevation. `daemon start --elevate` is also -/// permitted on Windows; it opts in to an explicit UAC prompt. +/// permitted on Windows; it opts in to an explicit UAC prompt. On Windows, +/// mutating commands are also permitted un-elevated while the Access Broker is +/// serving (the daemon then runs non-elevated and is safely restartable). /// /// # Errors /// @@ -132,9 +134,8 @@ pub(crate) fn daemon(action: &DaemonAction) -> Result<()> { /// the daemon's uid; an unreadable/absent PID file means there is no daemon to /// protect → no elevation required. /// -/// On **Windows** (and any non-Unix target) `uffsd` runs elevated to read the -/// live MFT, so managing it always needs an elevated token — behaviour is -/// unchanged. +/// (Windows uses a broker-aware variant — see the `#[cfg(windows)]` impl +/// below.) #[cfg(unix)] fn mutating_management_needs_elevation() -> bool { daemon_owner_needs_elevation(&pid_file_path(), uffs_mft::current_euid()) @@ -152,8 +153,21 @@ fn daemon_owner_needs_elevation(pid_file: &std::path::Path, caller_euid: u32) -> std::fs::metadata(pid_file).is_ok_and(|meta| meta.uid() != caller_euid) } -/// Non-Unix: managing the (elevated) daemon always needs an elevated token. -#[cfg(not(unix))] +/// Windows: elevation is required only when the Access Broker pipe is NOT +/// serving. With the broker up the daemon runs non-elevated and a non-elevated +/// caller can stop AND restart it (restart adopts broker handles — no UAC), so +/// a non-elevated `uffs --update` can quiesce/restart it; without the broker a +/// restart needs admin for the MFT (mirrors the Unix PID-owner gate). +#[cfg(windows)] +fn mutating_management_needs_elevation() -> bool { + /// Short pipe probe — this gate runs once per management command. + const BROKER_GATE_PROBE_MS: u32 = 600; + !uffs_winsvc::pipe_serving(uffs_broker_protocol::PIPE_NAME, BROKER_GATE_PROBE_MS) +} + +/// Other non-Unix targets (WASM, bare-metal — not real deployments): keep the +/// conservative default of always requiring elevation. +#[cfg(not(any(unix, windows)))] const fn mutating_management_needs_elevation() -> bool { true }