mirror of
https://github.com/uutils/findutils.git
synced 2026-06-10 15:48:30 -07:00
e2d84e98d5
`locate -d <file> <pattern>` panicked (slice index underflow, exit 101) when the database file is exactly one byte: `check_db` consumes that byte with `read_exact`, then `read_until(b'\0', ...)` hits EOF and appends nothing, leaving `buf` empty. `&buf[..buf.len() - 1]` then underflows `buf.len() - 1` to `usize::MAX` and slices out of bounds. Strip the trailing nul with `strip_suffix(b"\0")` (falling back to the whole buffer when `read_until` stopped at EOF without one) instead of slicing `buf.len() - 1`. An empty buffer now yields `None` (unrecognized format), so the too-short database is skipped like any other invalid one and locate exits 1 instead of crashing. Adds a regression test.
269 lines
7.4 KiB
Rust
269 lines
7.4 KiB
Rust
// Use of this source code is governed by a MIT-style
|
|
// license that can be found in the LICENSE file or at
|
|
// https://opensource.org/licenses/MIT.
|
|
|
|
mod common;
|
|
|
|
use std::process::Command;
|
|
|
|
use assert_cmd::{assert::OutputAssertExt, cargo::CommandCargoExt};
|
|
use rstest::rstest;
|
|
|
|
const DB_FLAG: &str = "--database=test_data/db/test_data_db";
|
|
const INVALID_DB_FLAG: &str = "--database=test_data/db/invalid_db";
|
|
const OLD_DB_FLAG: &str = "--database=test_data/db/old_db";
|
|
|
|
#[test]
|
|
fn test_locate_no_matches() {
|
|
Command::cargo_bin("locate")
|
|
.expect("couldn't find locate binary")
|
|
.args(["usr", DB_FLAG])
|
|
.assert()
|
|
.failure();
|
|
}
|
|
|
|
#[test]
|
|
fn test_locate_match() {
|
|
Command::cargo_bin("locate")
|
|
.expect("couldn't find locate binary")
|
|
.args(["test_data", DB_FLAG])
|
|
.assert()
|
|
.success();
|
|
}
|
|
|
|
#[test]
|
|
fn test_locate_no_matches_basename() {
|
|
Command::cargo_bin("locate")
|
|
.expect("couldn't find locate binary")
|
|
.args(["test_data1234567890", "--basename", DB_FLAG])
|
|
.assert()
|
|
.failure();
|
|
}
|
|
|
|
#[test]
|
|
fn test_locate_match_basename() {
|
|
Command::cargo_bin("locate")
|
|
.expect("couldn't find locate binary")
|
|
.args(["abbbc", "--basename", DB_FLAG])
|
|
.assert()
|
|
.success();
|
|
}
|
|
|
|
#[test]
|
|
fn test_locate_existing() {
|
|
Command::cargo_bin("locate")
|
|
.expect("couldn't find locate binary")
|
|
.args(["abbbc", "--existing", DB_FLAG])
|
|
.assert()
|
|
.success();
|
|
}
|
|
|
|
#[test]
|
|
fn test_locate_non_existing() {
|
|
Command::cargo_bin("locate")
|
|
.expect("couldn't find locate binary")
|
|
.args(["abbbc", "--non-existing", DB_FLAG])
|
|
.assert()
|
|
.failure();
|
|
}
|
|
|
|
#[test]
|
|
fn test_locate_statistics() {
|
|
Command::cargo_bin("locate")
|
|
.expect("couldn't find locate binary")
|
|
.args(["", "--statistics", DB_FLAG])
|
|
.assert()
|
|
.success();
|
|
}
|
|
|
|
#[rstest]
|
|
#[case("emacs")]
|
|
#[case("grep")]
|
|
#[case("posix-basic")]
|
|
#[case("posix-extended")]
|
|
fn test_locate_regex(#[case] input: &str) {
|
|
Command::cargo_bin("locate")
|
|
.expect("couldn't find locate binary")
|
|
.args([
|
|
"abbbc",
|
|
"--regex",
|
|
format!("--regextype={input}").as_str(),
|
|
DB_FLAG,
|
|
])
|
|
.assert()
|
|
.success();
|
|
}
|
|
|
|
#[test]
|
|
fn test_locate_all() {
|
|
Command::cargo_bin("locate")
|
|
.expect("couldn't find locate binary")
|
|
.args(["abb", "bbc", "--all", DB_FLAG])
|
|
.assert()
|
|
.success();
|
|
}
|
|
|
|
#[test]
|
|
fn test_locate_all_regex() {
|
|
Command::cargo_bin("locate")
|
|
.expect("couldn't find locate binary")
|
|
.args(["abb", "b*c", "--all", "--regex", DB_FLAG])
|
|
.assert()
|
|
.success();
|
|
}
|
|
|
|
#[test]
|
|
fn test_locate_invalid_db() {
|
|
Command::cargo_bin("locate")
|
|
.expect("couldn't find locate binary")
|
|
.args(["test_data", INVALID_DB_FLAG])
|
|
.assert()
|
|
.failure();
|
|
}
|
|
|
|
#[test]
|
|
fn test_locate_outdated_db() {
|
|
Command::cargo_bin("locate")
|
|
.expect("couldn't find locate binary")
|
|
.args(["test_data", OLD_DB_FLAG])
|
|
.assert()
|
|
.success();
|
|
}
|
|
|
|
#[test]
|
|
fn test_locate_print_help() {
|
|
Command::cargo_bin("locate")
|
|
.expect("couldn't find locate binary")
|
|
.arg("--help")
|
|
.assert()
|
|
.success();
|
|
}
|
|
|
|
#[test]
|
|
fn test_locate_invalid_flag() {
|
|
Command::cargo_bin("locate")
|
|
.expect("couldn't find locate binary")
|
|
.arg("--unknown")
|
|
.assert()
|
|
.failure();
|
|
}
|
|
|
|
// an un-compilable regex should be reported as an error rather than silently matching nothing
|
|
#[test]
|
|
fn test_locate_invalid_regex() {
|
|
Command::cargo_bin("locate")
|
|
.expect("couldn't find locate binary")
|
|
.args(["[", "--regex", DB_FLAG])
|
|
.assert()
|
|
.failure();
|
|
}
|
|
|
|
#[test]
|
|
fn test_updatedb() {
|
|
let tmp = tempfile::tempdir().unwrap();
|
|
Command::cargo_bin("updatedb")
|
|
.expect("couldn't find updatedb binary")
|
|
.args([
|
|
"--localpaths=./test_data".to_string(),
|
|
format!("--output={}", tmp.path().join("db").display()),
|
|
])
|
|
.assert()
|
|
.success();
|
|
}
|
|
|
|
#[test]
|
|
fn test_updatedb_invalid_flag() {
|
|
Command::cargo_bin("updatedb")
|
|
.expect("couldn't find updatedb binary")
|
|
.args(["--unknown"])
|
|
.assert()
|
|
.failure();
|
|
}
|
|
|
|
// empty prunefs/prunepaths must not produce an invalid find expression (e.g. an empty `( )` group)
|
|
#[test]
|
|
fn test_updatedb_empty_prune() {
|
|
let tmp = tempfile::tempdir().unwrap();
|
|
Command::cargo_bin("updatedb")
|
|
.expect("couldn't find updatedb binary")
|
|
.args([
|
|
"--localpaths=./test_data".to_string(),
|
|
format!("--output={}", tmp.path().join("db").display()),
|
|
"--prunefs=".to_string(),
|
|
"--prunepaths=".to_string(),
|
|
])
|
|
.assert()
|
|
.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]
|
|
fn test_updatedb_locate_roundtrip() {
|
|
// create the tree under the target dir so the path has no spaces (updatedb splits
|
|
// --localpaths on whitespace, mirroring GNU's space-separated convention)
|
|
let dir = tempfile::tempdir_in(env!("CARGO_TARGET_TMPDIR")).unwrap();
|
|
let marker = "locate_roundtrip_marker";
|
|
std::fs::write(dir.path().join(marker), b"").unwrap();
|
|
let db = dir.path().join("db");
|
|
|
|
Command::cargo_bin("updatedb")
|
|
.expect("couldn't find updatedb binary")
|
|
.args([
|
|
format!("--localpaths={}", dir.path().display()),
|
|
format!("--output={}", db.display()),
|
|
])
|
|
.assert()
|
|
.success();
|
|
|
|
let assert = Command::cargo_bin("locate")
|
|
.expect("couldn't find locate binary")
|
|
.args([marker.to_string(), format!("--database={}", db.display())])
|
|
.assert()
|
|
.success();
|
|
let stdout = String::from_utf8_lossy(&assert.get_output().stdout);
|
|
assert!(
|
|
stdout.contains(marker),
|
|
"locate output did not contain the indexed file: {stdout:?}"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_locate_one_byte_db() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
let db = dir.path().join("db_1byte");
|
|
std::fs::write(&db, b"X").unwrap();
|
|
Command::cargo_bin("locate")
|
|
.expect("couldn't find locate binary")
|
|
.arg(format!("--database={}", db.display()))
|
|
.arg("foo")
|
|
.assert()
|
|
.code(1);
|
|
}
|