From 2934e23e412c1eb882270d833ea40a8eb9768a8b Mon Sep 17 00:00:00 2001 From: Robert M1 <50460704+githubrobbi@users.noreply.github.com> Date: Wed, 17 Jun 2026 17:27:49 -0700 Subject: [PATCH 1/2] fix(update): embed asInvoker manifest in uffs-update.exe (Windows elevation) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Windows every `uffs --update` op (doctor/acquire/apply) failed at "spawning uffs-update.exe → The requested operation requires elevation (os error 740)". Cause: Windows Installer Detection auto-flags executables whose NAME contains update/setup/install/patch as installers needing UAC — and uffs-update.exe had no application manifest to opt out. So the non-elevated uffs.exe couldn't launch it. Fix: give uffs-update the same treatment uffs.exe already has — an app.manifest declaring `requestedExecutionLevel level="asInvoker"` (plus longPathAware), embedded into the PE on Windows MSVC via a winresource build.rs. asInvoker is correct: the helper only rewrites files in the user's own install dir and never needs Administrator. Inert on non-Windows. Verified: the built windows-msvc uffs-update.exe carries `requestedExecutionLevel level="asInvoker"`. Host + Windows-MSVC clippy clean; uffs-update tests pass (46). Co-Authored-By: Claude Opus 4.8 --- crates/uffs-update/Cargo.toml | 5 ++++ crates/uffs-update/app.manifest | 41 +++++++++++++++++++++++++++++ crates/uffs-update/build.rs | 46 +++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 crates/uffs-update/app.manifest create mode 100644 crates/uffs-update/build.rs diff --git a/crates/uffs-update/Cargo.toml b/crates/uffs-update/Cargo.toml index 77a0d47d8..986d396ea 100644 --- a/crates/uffs-update/Cargo.toml +++ b/crates/uffs-update/Cargo.toml @@ -59,5 +59,10 @@ windows.workspace = true [target.'cfg(unix)'.dependencies] libc = "0.2" +# Embed `app.manifest` (asInvoker) into uffs-update.exe on Windows MSVC so the +# "update" name doesn't trip Installer Detection → forced UAC (os error 740). +[build-dependencies] +winresource.workspace = true + [lints] workspace = true diff --git a/crates/uffs-update/app.manifest b/crates/uffs-update/app.manifest new file mode 100644 index 000000000..af7b450f1 --- /dev/null +++ b/crates/uffs-update/app.manifest @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + true + + + + + + + + + + diff --git a/crates/uffs-update/build.rs b/crates/uffs-update/build.rs new file mode 100644 index 000000000..50a72b229 --- /dev/null +++ b/crates/uffs-update/build.rs @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) 2025-2026 SKY, LLC. + +// Build scripts run on the build host, not the shipping binary; the workspace +// deny-expect lint exists for runtime code. `expect` with a readable message is +// the idiomatic shape for build-script error reporting. +#![allow( + clippy::expect_used, + reason = "build scripts may panic on build-host failure; workspace deny-expect exists for runtime code" +)] + +//! Build script: embed `app.manifest` into `uffs-update.exe` on Windows MSVC. +//! +//! The manifest declares `asInvoker`, which is **required** to stop Windows' +//! Installer Detection heuristic from force-elevating this binary purely +//! because its name contains "update". Without it, `uffs.exe` (non-elevated) +//! cannot spawn `uffs-update.exe` — it fails with `ERROR_ELEVATION_REQUIRED` +//! (os error 740) — breaking every `uffs --update` operation on Windows. The +//! helper only rewrites files in the user's install dir; it never needs admin. +//! +//! Inert on non-Windows / non-MSVC targets (the helper ships windows-msvc). + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=app.manifest"); + println!("cargo:rerun-if-changed=../../assets/brand/icons/uffs.ico"); + + let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default(); + let target_env = std::env::var("CARGO_CFG_TARGET_ENV").unwrap_or_default(); + + if target_os == "windows" && target_env == "msvc" { + let mut res = winresource::WindowsResource::new(); + res.set_icon("../../assets/brand/icons/uffs.ico") + .set("ProductName", "UltraFastFileSearch") + .set("FileDescription", "UFFS self-update helper") + .set("CompanyName", "SKY, LLC.") + .set("LegalCopyright", "(c) 2025-2026 SKY, LLC. MPL-2.0.") + .set("OriginalFilename", "uffs-update.exe") + .set_manifest_file("app.manifest"); + + // Panic on failure is fine in a build script (host-only; the + // workspace deny-unwrap lint targets runtime code). + res.compile() + .expect("winresource: failed to embed uffs-update manifest"); + } +} From 6bab1c4bf99bbe0a15411534b6f868a25953da0c Mon Sep 17 00:00:00 2001 From: Robert M1 <50460704+githubrobbi@users.noreply.github.com> Date: Wed, 17 Jun 2026 17:35:49 -0700 Subject: [PATCH 2/2] =?UTF-8?q?chore:=20update=20Cargo.lock=20for=20the=20?= =?UTF-8?q?uffs-update=20=E2=86=92=20winresource=20build-dep=20edge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The asInvoker-manifest commit added winresource as a uffs-update build-dependency but didn't record the lockfile edge, so CI's `cargo fetch --locked` rejected the stale Cargo.lock. winresource was already in the graph (uffs-cli uses it), so no new crate — just the edge. Co-Authored-By: Claude Opus 4.8 --- Cargo.lock | 1 + 1 file changed, 1 insertion(+) diff --git a/Cargo.lock b/Cargo.lock index 62a747bd3..818eed386 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4688,6 +4688,7 @@ dependencies = [ "uffs-broker-protocol", "uffs-winsvc", "windows 0.62.2", + "winresource", ] [[package]]