Skip to content

Commit 62deaf6

Browse files
committed
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.
1 parent 1be5541 commit 62deaf6

2 files changed

Lines changed: 61 additions & 11 deletions

File tree

src/updatedb/mod.rs

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use std::{
1414
};
1515

1616
use clap::{crate_version, value_parser, Arg, ArgAction, ArgMatches, Command};
17-
use uucore::error::UResult;
17+
use uucore::error::{strip_errno, UResult, USimpleError};
1818

1919
use crate::find::{find_main, Dependencies};
2020

@@ -323,22 +323,46 @@ fn do_updatedb(args: &[&str]) -> UResult<()> {
323323
let deps = CapturedDependencies::new(output.clone());
324324
find_main(find_args.as_slice(), &deps);
325325

326-
let mut writer = BufWriter::new(
327-
OpenOptions::new()
328-
.write(true)
329-
.truncate(true)
330-
.create(true)
331-
.open(config.output)?,
332-
);
326+
let output_path = config.output;
327+
let file = OpenOptions::new()
328+
.write(true)
329+
.truncate(true)
330+
.create(true)
331+
.open(&output_path)
332+
.map_err(|e| {
333+
USimpleError::new(
334+
1,
335+
format!(
336+
"cannot create '{}': {}",
337+
output_path.display(),
338+
strip_errno(&e)
339+
),
340+
)
341+
})?;
342+
let mut writer = BufWriter::new(file);
343+
344+
// strip the trailing "(os error N)" so write failures read like the create error above
345+
let write_err = |e: std::io::Error| {
346+
USimpleError::new(
347+
1,
348+
format!(
349+
"error writing '{}': {}",
350+
output_path.display(),
351+
strip_errno(&e)
352+
),
353+
)
354+
};
333355

334356
let output = output.borrow();
335357
let frcoder = Frcoder::new(output.as_slice(), config.db_format);
336-
writer.write_all(&frcoder.generate_header())?;
358+
writer
359+
.write_all(&frcoder.generate_header())
360+
.map_err(&write_err)?;
337361
for v in frcoder {
338-
writer.write_all(v.as_slice())?;
362+
writer.write_all(v.as_slice()).map_err(&write_err)?;
339363
}
340364

341-
writer.flush()?;
365+
writer.flush().map_err(&write_err)?;
342366

343367
Ok(())
344368
}

tests/db_tests.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,32 @@ fn test_updatedb_empty_prune() {
196196
.success();
197197
}
198198

199+
// when the output database can't be created, updatedb must report a clear error naming the path
200+
// and must not leak the raw "(os error N)" suffix
201+
#[test]
202+
fn test_updatedb_output_create_error() {
203+
let tmp = tempfile::tempdir().unwrap();
204+
// a path under a non-existent directory can't be created
205+
let bad_output = tmp.path().join("does-not-exist").join("db");
206+
let assert = Command::cargo_bin("updatedb")
207+
.expect("couldn't find updatedb binary")
208+
.args([
209+
"--localpaths=./test_data".to_string(),
210+
format!("--output={}", bad_output.display()),
211+
])
212+
.assert()
213+
.failure();
214+
let stderr = String::from_utf8_lossy(&assert.get_output().stderr);
215+
assert!(
216+
stderr.contains("cannot create") && stderr.contains(&bad_output.display().to_string()),
217+
"stderr did not name the un-creatable output path: {stderr:?}"
218+
);
219+
assert!(
220+
!stderr.contains("os error"),
221+
"stderr leaked the raw OS error: {stderr:?}"
222+
);
223+
}
224+
199225
// build a database from a temp tree with updatedb, then query it back with locate. This is the
200226
// only test that exercises the full pipeline (writer + reader) and is platform-independent.
201227
#[test]

0 commit comments

Comments
 (0)