mirror of
https://github.com/uutils/findutils.git
synced 2026-06-10 15:48:30 -07:00
find: -ls: don't panic on an unmapped owner uid/gid
`find <file> -ls` panicked (exit 101) when the file's owning uid (or gid) had no `/etc/passwd` (`/etc/group`) entry: it resolved the name with `User::from_uid(...).unwrap().unwrap()`, and the inner unwrap aborts on the `Ok(None)` that an unmapped id returns. Unmapped owners are routine (extracted archives, NFS, deleted accounts, container images). Resolve the uid/gid via `uucore::entries::uid2usr`/`gid2grp`, falling back to the numeric id when there is no entry, matching GNU find (which prints the number). These helpers already serialize the non-thread-safe getpwuid/getgrgid behind a mutex, so reuse them rather than reimplementing the lookup. Add a -ls integration test that chowns a file to an unmapped id and checks the numeric fallback; it skips when the process lacks the privilege to do so.
This commit is contained in:
@@ -126,7 +126,6 @@ impl Ls {
|
||||
mut out: impl Write,
|
||||
print_error_message: bool,
|
||||
) {
|
||||
use nix::unistd::{Gid, Group, Uid, User};
|
||||
use std::os::unix::fs::{MetadataExt, PermissionsExt};
|
||||
|
||||
let metadata = file_info.metadata().unwrap();
|
||||
@@ -150,14 +149,13 @@ impl Ls {
|
||||
let permission =
|
||||
{ format_permissions(metadata.permissions().mode() as uucore::libc::mode_t) };
|
||||
let hard_links = metadata.nlink();
|
||||
let user = {
|
||||
let uid = metadata.uid();
|
||||
User::from_uid(Uid::from_raw(uid)).unwrap().unwrap().name
|
||||
};
|
||||
let group = {
|
||||
let gid = metadata.gid();
|
||||
Group::from_gid(Gid::from_raw(gid)).unwrap().unwrap().name
|
||||
};
|
||||
// Fall back to the numeric id when the uid/gid has no passwd/group entry
|
||||
// (unmapped owner) — matching GNU find, which never crashes. uucore::entries
|
||||
// serializes the non-thread-safe getpwuid/getgrgid behind a mutex.
|
||||
let user =
|
||||
uucore::entries::uid2usr(metadata.uid()).unwrap_or_else(|_| metadata.uid().to_string());
|
||||
let group =
|
||||
uucore::entries::gid2grp(metadata.gid()).unwrap_or_else(|_| metadata.gid().to_string());
|
||||
let size = metadata.size();
|
||||
let last_modified = {
|
||||
let system_time = metadata.modified().unwrap();
|
||||
|
||||
@@ -1018,6 +1018,37 @@ fn find_ls() {
|
||||
.no_stderr();
|
||||
}
|
||||
|
||||
// Regression test for uutils/findutils#717: `-ls` used to abort (exit 101) when
|
||||
// a file's owning uid/gid had no passwd/group entry; it must fall back to the
|
||||
// numeric id like GNU find. Creating such a file needs privilege to chown to an
|
||||
// unmapped id, so skip when that isn't available (e.g. non-root CI).
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn find_ls_unmapped_owner_renders_numeric_id() {
|
||||
use nix::unistd::{chown, Gid, Uid};
|
||||
|
||||
let temp_dir = Builder::new().prefix("find_ls_unmapped").tempdir().unwrap();
|
||||
let file = temp_dir.path().join("orphan");
|
||||
File::create(&file).unwrap();
|
||||
|
||||
// A uid/gid essentially never present in /etc/passwd or /etc/group.
|
||||
let unmapped = 4_000_000_000u32;
|
||||
if chown(
|
||||
&file,
|
||||
Some(Uid::from_raw(unmapped)),
|
||||
Some(Gid::from_raw(unmapped)),
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ucmd()
|
||||
.args(&[file.to_str().unwrap(), "-ls"])
|
||||
.succeeds()
|
||||
.stdout_contains(unmapped.to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn find_slashes() {
|
||||
|
||||
Reference in New Issue
Block a user