From f68a66c4be26df6cdcd832251e88567ac6d9ed4b Mon Sep 17 00:00:00 2001 From: Matthew Jackson <1085847+MattJackson@users.noreply.github.com> Date: Tue, 23 Jun 2026 04:05:41 -0700 Subject: [PATCH] keydb: map missing home dir to NotFound, not keydb-parse error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit default_path returned Error::KeydbParse (E8004, rendered as 'failed to parse the keydb file') when HOME/USERPROFILE was unset. That misreports an environment failure — a process with no home directory, typically a stripped container or CI config — as a corrupt keydb file the code never read. Map it to an IoError(NotFound) in the I/O category instead, so no display path blames the keydb. Add a regression test. --- src/keydb.rs | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/keydb.rs b/src/keydb.rs index 50db5b8..6afe1f3 100644 --- a/src/keydb.rs +++ b/src/keydb.rs @@ -54,6 +54,18 @@ fn read_capped_to_string(reader: R) -> Result { /// `freemkv-keysources::keydb_search_paths`; this function is the single /// *write* default used by `save`/`update`, kept in lock-step with that crate's /// `default_keydb_path` for the same OS. +/// Build the error returned when no home directory can be determined +/// (`HOME`/`USERPROFILE` unset). This is an *environment* failure — the +/// process has no home dir, which typically signals a stripped container +/// or CI configuration — not a corrupt or unparseable keydb file. Map it +/// to a `NotFound` I/O error so display/remediation paths never claim a +/// keydb parse failure for a file that was never consulted. +fn no_home_dir() -> Error { + Error::IoError { + source: std::io::Error::from(std::io::ErrorKind::NotFound), + } +} + pub fn default_path() -> Result { #[cfg(windows)] { @@ -62,7 +74,7 @@ pub fn default_path() -> Result { return Ok(PathBuf::from(appdata).join("freemkv").join("keydb.cfg")); } } - let profile = std::env::var("USERPROFILE").map_err(|_| Error::KeydbParse)?; + let profile = std::env::var("USERPROFILE").map_err(|_| no_home_dir())?; Ok(PathBuf::from(profile) .join(".config") .join("freemkv") @@ -72,7 +84,7 @@ pub fn default_path() -> Result { { let home = std::env::var("HOME") .or_else(|_| std::env::var("USERPROFILE")) - .map_err(|_| Error::KeydbParse)?; + .map_err(|_| no_home_dir())?; Ok(PathBuf::from(home) .join(".config") .join("freemkv") @@ -410,6 +422,24 @@ mod tests { d } + // Regression: a missing home directory (HOME/USERPROFILE unset) is an + // *environment* failure, not a corrupt keydb. It must NOT surface as + // E8004 (KeydbParse → "failed to parse the keydb file"), which would + // blame a file that was never consulted. It maps to a NotFound I/O + // error in the 5xxx (I/O) category instead. + #[test] + fn no_home_dir_is_io_not_found_not_keydb_parse() { + let e = no_home_dir(); + match e { + Error::IoError { source } => { + assert_eq!(source.kind(), std::io::ErrorKind::NotFound); + } + other => panic!("expected IoError(NotFound), got {other:?}"), + } + // And explicitly: it is not the keydb-parse code. + assert_ne!(no_home_dir().code(), Error::KeydbParse.code()); + } + #[test] fn write_atomic_replaces_existing_and_leaves_no_temp() { let dir = scratch("atomic");