updatedb: clear error when the output database can't be created

Opening the output file with `?` surfaced a bare "No such file or
directory" with no indication of which file failed (easy to hit because the
default output path /usr/local/var/locatedb often doesn't exist).

Report it like GNU does, naming the path and the reason, e.g.
`cannot create '/usr/local/var/locatedb': No such file or directory`.
strip_errno() drops the trailing "(os error N)" noise.
This commit is contained in:
Sylvestre Ledru
2026-06-08 23:38:59 +02:00
parent 1be5541c74
commit 62deaf6e60
2 changed files with 61 additions and 11 deletions
+35 -11
View File
@@ -14,7 +14,7 @@ use std::{
};
use clap::{crate_version, value_parser, Arg, ArgAction, ArgMatches, Command};
use uucore::error::UResult;
use uucore::error::{strip_errno, UResult, USimpleError};
use crate::find::{find_main, Dependencies};
@@ -323,22 +323,46 @@ fn do_updatedb(args: &[&str]) -> UResult<()> {
let deps = CapturedDependencies::new(output.clone());
find_main(find_args.as_slice(), &deps);
let mut writer = BufWriter::new(
OpenOptions::new()
.write(true)
.truncate(true)
.create(true)
.open(config.output)?,
);
let output_path = config.output;
let file = OpenOptions::new()
.write(true)
.truncate(true)
.create(true)
.open(&output_path)
.map_err(|e| {
USimpleError::new(
1,
format!(
"cannot create '{}': {}",
output_path.display(),
strip_errno(&e)
),
)
})?;
let mut writer = BufWriter::new(file);
// strip the trailing "(os error N)" so write failures read like the create error above
let write_err = |e: std::io::Error| {
USimpleError::new(
1,
format!(
"error writing '{}': {}",
output_path.display(),
strip_errno(&e)
),
)
};
let output = output.borrow();
let frcoder = Frcoder::new(output.as_slice(), config.db_format);
writer.write_all(&frcoder.generate_header())?;
writer
.write_all(&frcoder.generate_header())
.map_err(&write_err)?;
for v in frcoder {
writer.write_all(v.as_slice())?;
writer.write_all(v.as_slice()).map_err(&write_err)?;
}
writer.flush()?;
writer.flush().map_err(&write_err)?;
Ok(())
}
+26
View File
@@ -196,6 +196,32 @@ fn test_updatedb_empty_prune() {
.success();
}
// when the output database can't be created, updatedb must report a clear error naming the path
// and must not leak the raw "(os error N)" suffix
#[test]
fn test_updatedb_output_create_error() {
let tmp = tempfile::tempdir().unwrap();
// a path under a non-existent directory can't be created
let bad_output = tmp.path().join("does-not-exist").join("db");
let assert = Command::cargo_bin("updatedb")
.expect("couldn't find updatedb binary")
.args([
"--localpaths=./test_data".to_string(),
format!("--output={}", bad_output.display()),
])
.assert()
.failure();
let stderr = String::from_utf8_lossy(&assert.get_output().stderr);
assert!(
stderr.contains("cannot create") && stderr.contains(&bad_output.display().to_string()),
"stderr did not name the un-creatable output path: {stderr:?}"
);
assert!(
!stderr.contains("os error"),
"stderr leaked the raw OS error: {stderr:?}"
);
}
// build a database from a temp tree with updatedb, then query it back with locate. This is the
// only test that exercises the full pipeline (writer + reader) and is platform-independent.
#[test]