diff --git a/src/uu/chroot/locales/en-US.ftl b/src/uu/chroot/locales/en-US.ftl index 24c109c406e..6b3c0a1d9c3 100644 --- a/src/uu/chroot/locales/en-US.ftl +++ b/src/uu/chroot/locales/en-US.ftl @@ -20,6 +20,7 @@ chroot-error-no-group-specified = no group specified for unknown uid: { $uid } chroot-error-no-such-user = invalid user chroot-error-no-such-group = invalid group chroot-error-no-such-directory = cannot change root directory to { $dir }: no such directory +chroot-error-cannot-stat = cannot change root directory to { $dir }: { $err } chroot-error-set-gid-failed = cannot set gid to { $gid }: { $err } chroot-error-set-groups-failed = cannot set groups: { $err } chroot-error-set-user-failed = cannot set user to { $user }: { $err } diff --git a/src/uu/chroot/locales/fr-FR.ftl b/src/uu/chroot/locales/fr-FR.ftl index caabd169b2b..13ee63c77b1 100644 --- a/src/uu/chroot/locales/fr-FR.ftl +++ b/src/uu/chroot/locales/fr-FR.ftl @@ -20,6 +20,7 @@ chroot-error-no-group-specified = aucun groupe spécifié pour l'uid inconnu : { chroot-error-no-such-user = utilisateur invalide chroot-error-no-such-group = groupe invalide chroot-error-no-such-directory = impossible de changer le répertoire racine vers { $dir } : aucun répertoire de ce type +chroot-error-cannot-stat = impossible de changer le répertoire racine vers { $dir } : { $err } chroot-error-set-gid-failed = impossible de définir le gid à { $gid } : { $err } chroot-error-set-groups-failed = impossible de définir les groupes : { $err } chroot-error-set-user-failed = impossible de définir l'utilisateur à { $user } : { $err } diff --git a/src/uu/chroot/src/chroot.rs b/src/uu/chroot/src/chroot.rs index 28b50c80ed9..b4ba95037cb 100644 --- a/src/uu/chroot/src/chroot.rs +++ b/src/uu/chroot/src/chroot.rs @@ -194,6 +194,14 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { options.chroot_target = resolved; } + // Probe the path with `metadata` so the *original* I/O error reaches the + // user. `Path::is_dir()` swallows that error and reports `false` for any + // failure — including `ENAMETOOLONG` (a 256-char path on Linux), which GNU + // surfaces as "File name too long". Without this, a too-long NEWROOT + // would be misreported as "no such directory". + if let Err(e) = std::fs::metadata(&options.newroot) { + return Err(ChrootError::CannotStat(options.newroot, e).into()); + } if !options.newroot.is_dir() { return Err(ChrootError::NoSuchDirectory(options.newroot).into()); } diff --git a/src/uu/chroot/src/error.rs b/src/uu/chroot/src/error.rs index 0592d72efad..39c8bbea52c 100644 --- a/src/uu/chroot/src/error.rs +++ b/src/uu/chroot/src/error.rs @@ -56,6 +56,10 @@ pub enum ChrootError { #[error("{}", translate!("chroot-error-no-such-directory", "dir" => _0.quote()))] NoSuchDirectory(PathBuf), + /// The given path could not be stat'd (e.g. ENAMETOOLONG, EACCES). + #[error("{}", translate!("chroot-error-cannot-stat", "dir" => _0.quote(), "err" => _1))] + CannotStat(PathBuf, #[source] Error), + /// The call to `setgid()` failed. #[error("{}", translate!("chroot-error-set-gid-failed", "gid" => _0, "err" => _1))] SetGidFailed(String, #[source] Error), diff --git a/tests/by-util/test_chroot.rs b/tests/by-util/test_chroot.rs index 66e10927740..840094f2dea 100644 --- a/tests/by-util/test_chroot.rs +++ b/tests/by-util/test_chroot.rs @@ -56,6 +56,22 @@ fn test_no_such_directory() { .stderr_is("chroot: cannot change root directory to 'a': no such directory\n"); } +#[test] +#[cfg(not(target_os = "android"))] +fn test_filename_too_long() { + // Regression for #13156: a NEWROOT longer than NAME_MAX (255 bytes on + // Linux) used to be misreported as "no such directory" because + // `Path::is_dir()` swallows ENAMETOOLONG. GNU surfaces the real error. + let long_name = "A".repeat(256); + let expected = format!( + "chroot: cannot change root directory to '{long_name}': File name too long (os error 63)\n" + ); + new_ucmd!() + .arg(&long_name) + .fails_with_code(125) + .stderr_is(expected); +} + #[test] fn test_multiple_group_args() { let ts = TestScenario::new(util_name!());