diff --git a/.github/workflows/make.yml b/.github/workflows/make.yml index e654b55100..bba8903230 100644 --- a/.github/workflows/make.yml +++ b/.github/workflows/make.yml @@ -5,7 +5,7 @@ name: make # spell-checker:ignore (jargon) deps softprops toolchain # spell-checker:ignore (people) dawidd # spell-checker:ignore (shell/tools) nextest sccache zstd -# spell-checker:ignore (misc) bindir busytest defconfig DESTDIR manpages multisize runtest testsuite toybox uutils +# spell-checker:ignore (misc) bindir busytest defconfig DESTDIR manpages multisize runtest testsuite toybox userns uutils env: PROJECT_NAME: coreutils @@ -80,7 +80,11 @@ jobs: mv -t target/ target.cache/release 2>/dev/null || true - name: "`make nextest`" shell: bash - run: make nextest PROFILE=ci CARGOFLAGS="--hide-progress-bar" + run: | + # some test needs unshare + sudo sysctl -w kernel.unprivileged_userns_clone=1 || true + sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 || true + make nextest PROFILE=ci CARGOFLAGS="--hide-progress-bar" env: RUST_BACKTRACE: "1" - name: Upload test results to Codecov diff --git a/src/uu/id/src/id.rs b/src/uu/id/src/id.rs index 24b80e8eb4..7f4e93e79b 100644 --- a/src/uu/id/src/id.rs +++ b/src/uu/id/src/id.rs @@ -641,39 +641,18 @@ fn id_print(state: &State, groups: &[u32]) -> io::Result<()> { write!( lock, "uid={uid}({})", - entries::uid2usr(uid).unwrap_or_else(|_| { - show_error!( - "{}", - translate!("id-error-cannot-find-user-name", "uid" => uid) - ); - set_exit_code(1); - uid.to_string() - }) + entries::uid2usr(uid).unwrap_or_else(|_| { uid.to_string() }) )?; write!( lock, " gid={gid}({})", - entries::gid2grp(gid).unwrap_or_else(|_| { - show_error!( - "{}", - translate!("id-error-cannot-find-group-name", "gid" => gid) - ); - set_exit_code(1); - gid.to_string() - }) + entries::gid2grp(gid).unwrap_or_else(|_| { gid.to_string() }) )?; if !state.user_specified && (euid != uid) { write!( lock, " euid={euid}({})", - entries::uid2usr(euid).unwrap_or_else(|_| { - show_error!( - "{}", - translate!("id-error-cannot-find-user-name", "uid" => euid) - ); - set_exit_code(1); - euid.to_string() - }) + entries::uid2usr(euid).unwrap_or_else(|_| { euid.to_string() }) )?; } if !state.user_specified && (egid != gid) { @@ -681,14 +660,7 @@ fn id_print(state: &State, groups: &[u32]) -> io::Result<()> { write!( lock, " egid={egid}({})", - entries::gid2grp(egid).unwrap_or_else(|_| { - show_error!( - "{}", - translate!("id-error-cannot-find-group-name", "gid" => egid) - ); - set_exit_code(1); - egid.to_string() - }) + entries::gid2grp(egid).unwrap_or_else(|_| { egid.to_string() }) )?; } write!( @@ -698,14 +670,7 @@ fn id_print(state: &State, groups: &[u32]) -> io::Result<()> { .iter() .map(|&gr| format!( "{gr}({})", - entries::gid2grp(gr).unwrap_or_else(|_| { - show_error!( - "{}", - translate!("id-error-cannot-find-group-name", "gid" => gr) - ); - set_exit_code(1); - gr.to_string() - }) + entries::gid2grp(gr).unwrap_or_else(|_| { gr.to_string() }) )) .collect::>() .join(",") diff --git a/tests/by-util/test_id.rs b/tests/by-util/test_id.rs index dd2b977992..7b905674ca 100644 --- a/tests/by-util/test_id.rs +++ b/tests/by-util/test_id.rs @@ -51,6 +51,35 @@ fn test_id_no_specified_user() { .code_is(exp_result.code()); } +#[test] +#[cfg(target_os = "linux")] +fn test_id_nonexisting_ids() { + use uutests::{get_tests_binary, util::unshare_bin}; + + let unshare = unwrap_or_return!(unshare_bin()); + + let unshare_args = ["-U", "--map-user=10101", "--"]; + + let exp_result = Command::new(&unshare) + .args(unshare_args) + .arg(util_name!()) + .output() + .unwrap(); + + let result = Command::new(unshare) + .args(unshare_args) + .arg(get_tests_binary!()) + .arg(util_name!()) + .output() + .unwrap(); + + assert_eq!(result.status.code(), exp_result.status.code()); + + let exp_stderr = String::from_utf8_lossy(&exp_result.stderr); + let act_stderr = String::from_utf8_lossy(&result.stderr); + assert_eq!(act_stderr, exp_stderr); +} + #[test] fn test_id_single_user() { let test_users = [&whoami()[..]]; diff --git a/tests/uutests/src/lib/util.rs b/tests/uutests/src/lib/util.rs index 8286ecfea7..f2a315a813 100644 --- a/tests/uutests/src/lib/util.rs +++ b/tests/uutests/src/lib/util.rs @@ -3235,6 +3235,38 @@ pub fn run_ucmd_as_root_with_stdin_stdout( } } +/// Determines whether `unshare` can be used in the current setup, and returns the path to its binary. +/// +/// # Errors +/// If `unshare` is not available on the system, or cannot be run (e.g. in CI), this function +/// returns a String describing the reason. +pub fn unshare_bin() -> Result { + // Determine whether 'unshare' is available + let which_result = Command::new("which").arg("unshare").output()?; + if !which_result.status.success() { + return Err(io::Error::other("'unshare' not found on system")); + } + let unshare = String::from_utf8_lossy(&which_result.stdout) + .trim() + .to_string(); + + // Determine whether 'unshare' works as expected + let test_result = Command::new(&unshare) + .args(["-U", "--map-user=0", "--map-group=0", "--", "whoami"]) + .output()?; + if !test_result.status.success() { + let err = String::from_utf8_lossy(&test_result.stderr) + .trim() + .to_string(); + return Err(io::Error::other(format!("failed to run 'unshare': {err}"))); + } + if String::from_utf8_lossy(&test_result.stdout).trim() != "root" { + return Err(io::Error::other("'unshare' does not work as expected")); + } + + Ok(unshare) +} + /// Sanity checks for test utils #[cfg(test)] mod tests {