mirror of
https://github.com/uutils/coreutils
synced 2024-12-14 15:22:38 +00:00
Merge pull request #3960 from sylvestre/chroot-improv
Various improvements to chroot
This commit is contained in:
commit
6e74408834
3 changed files with 95 additions and 14 deletions
|
@ -6,13 +6,14 @@
|
||||||
// For the full copyright and license information, please view the LICENSE
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
// spell-checker:ignore (ToDO) NEWROOT Userspec pstatus
|
// spell-checker:ignore (ToDO) NEWROOT Userspec pstatus chdir
|
||||||
mod error;
|
mod error;
|
||||||
|
|
||||||
use crate::error::ChrootError;
|
use crate::error::ChrootError;
|
||||||
use clap::{crate_version, Arg, Command};
|
use clap::{crate_version, Arg, Command};
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::io::Error;
|
use std::io::Error;
|
||||||
|
use std::os::unix::prelude::OsStrExt;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::process;
|
use std::process;
|
||||||
use uucore::error::{set_exit_code, UClapError, UResult};
|
use uucore::error::{set_exit_code, UClapError, UResult};
|
||||||
|
@ -29,6 +30,7 @@ mod options {
|
||||||
pub const GROUPS: &str = "groups";
|
pub const GROUPS: &str = "groups";
|
||||||
pub const USERSPEC: &str = "userspec";
|
pub const USERSPEC: &str = "userspec";
|
||||||
pub const COMMAND: &str = "command";
|
pub const COMMAND: &str = "command";
|
||||||
|
pub const SKIP_CHDIR: &str = "skip-chdir";
|
||||||
}
|
}
|
||||||
|
|
||||||
#[uucore::main]
|
#[uucore::main]
|
||||||
|
@ -144,6 +146,15 @@ pub fn uu_app<'a>() -> Command<'a> {
|
||||||
)
|
)
|
||||||
.value_name("USER:GROUP"),
|
.value_name("USER:GROUP"),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::new(options::SKIP_CHDIR)
|
||||||
|
.long(options::SKIP_CHDIR)
|
||||||
|
.help(
|
||||||
|
"Use this option to not change the working directory \
|
||||||
|
to / after changing the root directory to newroot, \
|
||||||
|
i.e., inside the chroot.",
|
||||||
|
),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::COMMAND)
|
Arg::new(options::COMMAND)
|
||||||
.value_hint(clap::ValueHint::CommandName)
|
.value_hint(clap::ValueHint::CommandName)
|
||||||
|
@ -158,6 +169,7 @@ fn set_context(root: &Path, options: &clap::ArgMatches) -> UResult<()> {
|
||||||
let user_str = options.value_of(options::USER).unwrap_or_default();
|
let user_str = options.value_of(options::USER).unwrap_or_default();
|
||||||
let group_str = options.value_of(options::GROUP).unwrap_or_default();
|
let group_str = options.value_of(options::GROUP).unwrap_or_default();
|
||||||
let groups_str = options.value_of(options::GROUPS).unwrap_or_default();
|
let groups_str = options.value_of(options::GROUPS).unwrap_or_default();
|
||||||
|
let skip_chdir = options.contains_id(options::SKIP_CHDIR);
|
||||||
let userspec = match userspec_str {
|
let userspec = match userspec_str {
|
||||||
Some(u) => {
|
Some(u) => {
|
||||||
let s: Vec<&str> = u.split(':').collect();
|
let s: Vec<&str> = u.split(':').collect();
|
||||||
|
@ -175,7 +187,7 @@ fn set_context(root: &Path, options: &clap::ArgMatches) -> UResult<()> {
|
||||||
(userspec[0], userspec[1])
|
(userspec[0], userspec[1])
|
||||||
};
|
};
|
||||||
|
|
||||||
enter_chroot(root)?;
|
enter_chroot(root, skip_chdir)?;
|
||||||
|
|
||||||
set_groups_from_str(groups_str)?;
|
set_groups_from_str(groups_str)?;
|
||||||
set_main_group(group)?;
|
set_main_group(group)?;
|
||||||
|
@ -183,12 +195,20 @@ fn set_context(root: &Path, options: &clap::ArgMatches) -> UResult<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enter_chroot(root: &Path) -> UResult<()> {
|
fn enter_chroot(root: &Path, skip_chdir: bool) -> UResult<()> {
|
||||||
std::env::set_current_dir(root).unwrap();
|
|
||||||
let err = unsafe {
|
let err = unsafe {
|
||||||
chroot(CString::new(".").unwrap().as_bytes_with_nul().as_ptr() as *const libc::c_char)
|
chroot(
|
||||||
|
CString::new(root.as_os_str().as_bytes().to_vec())
|
||||||
|
.unwrap()
|
||||||
|
.as_bytes_with_nul()
|
||||||
|
.as_ptr() as *const libc::c_char,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
if err == 0 {
|
if err == 0 {
|
||||||
|
if !skip_chdir {
|
||||||
|
std::env::set_current_dir(root).unwrap();
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(ChrootError::CannotEnter(format!("{}", root.display()), Error::last_os_error()).into())
|
Err(ChrootError::CannotEnter(format!("{}", root.display()), Error::last_os_error()).into())
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// spell-checker:ignore (words) araba newroot userspec
|
// spell-checker:ignore (words) araba newroot userspec chdir pwd's
|
||||||
|
|
||||||
use crate::common::util::*;
|
use crate::common::util::*;
|
||||||
|
|
||||||
|
@ -112,15 +112,72 @@ fn test_default_shell() {
|
||||||
at.mkdir(dir);
|
at.mkdir(dir);
|
||||||
|
|
||||||
let shell = std::env::var("SHELL").unwrap_or_else(|_| "/bin/sh".to_string());
|
let shell = std::env::var("SHELL").unwrap_or_else(|_| "/bin/sh".to_string());
|
||||||
let _expected = format!(
|
let expected = format!(
|
||||||
"chroot: failed to run command '{}': No such file or directory",
|
"chroot: failed to run command '{}': No such file or directory",
|
||||||
shell
|
shell
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: [2021-09; jhscheer] uncomment if/when #2692 gets merged
|
if let Ok(result) = run_ucmd_as_root(&ts, &[dir]) {
|
||||||
// if let Ok(result) = run_ucmd_as_root(&ts, &[dir]) {
|
result.stderr_contains(expected);
|
||||||
// result.stderr_contains(expected);
|
} else {
|
||||||
// } else {
|
print!("TEST SKIPPED");
|
||||||
// print!("TEST SKIPPED");
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_chroot() {
|
||||||
|
let ts = TestScenario::new(util_name!());
|
||||||
|
let at = &ts.fixtures;
|
||||||
|
|
||||||
|
let dir = "CHROOT_DIR";
|
||||||
|
at.mkdir(dir);
|
||||||
|
if let Ok(result) = run_ucmd_as_root(&ts, &[dir, "whoami"]) {
|
||||||
|
result.success().no_stderr().stdout_is("root");
|
||||||
|
} else {
|
||||||
|
print!("Test skipped; requires root user");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(result) = run_ucmd_as_root(&ts, &[dir, "pwd"]) {
|
||||||
|
result.success().no_stderr().stdout_is("/");
|
||||||
|
} else {
|
||||||
|
print!("Test skipped; requires root user");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_chroot_skip_chdir() {
|
||||||
|
let ts = TestScenario::new(util_name!());
|
||||||
|
let at = &ts.fixtures;
|
||||||
|
|
||||||
|
let dir = "CHROOT_DIR";
|
||||||
|
at.mkdir(dir);
|
||||||
|
let env_cd = std::env::current_dir().unwrap();
|
||||||
|
if let Ok(result) = run_ucmd_as_root(&ts, &[dir, "--skip-chdir", "pwd"]) {
|
||||||
|
// Should return the same path
|
||||||
|
assert_eq!(
|
||||||
|
result.success().no_stderr().stdout_str(),
|
||||||
|
env_cd.to_str().unwrap()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
print!("Test skipped; requires root user");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_chroot_extra_arg() {
|
||||||
|
let ts = TestScenario::new(util_name!());
|
||||||
|
let at = &ts.fixtures;
|
||||||
|
|
||||||
|
let dir = "CHROOT_DIR";
|
||||||
|
at.mkdir(dir);
|
||||||
|
let env_cd = std::env::current_dir().unwrap();
|
||||||
|
// Verify that -P is pwd's and not chroot
|
||||||
|
if let Ok(result) = run_ucmd_as_root(&ts, &[dir, "pwd", "-P"]) {
|
||||||
|
assert_eq!(
|
||||||
|
result.success().no_stderr().stdout_str(),
|
||||||
|
env_cd.to_str().unwrap()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
print!("Test skipped; requires root user");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#
|
#
|
||||||
# UU_MAKE_PROFILE == 'debug' | 'release' ## build profile for *uutils* build; may be supplied by caller, defaults to 'debug'
|
# UU_MAKE_PROFILE == 'debug' | 'release' ## build profile for *uutils* build; may be supplied by caller, defaults to 'debug'
|
||||||
|
|
||||||
# spell-checker:ignore (paths) abmon deref discrim eacces getlimits getopt ginstall inacc infloop inotify reflink ; (misc) INT_OFLOW OFLOW baddecode submodules ; (vars/env) SRCDIR vdir rcexp
|
# spell-checker:ignore (paths) abmon deref discrim eacces getlimits getopt ginstall inacc infloop inotify reflink ; (misc) INT_OFLOW OFLOW baddecode submodules ; (vars/env) SRCDIR vdir rcexp xpart
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
@ -184,6 +184,10 @@ sed -i "s/ {ERR_SUBST=>\"s\/(unrecognized|unknown) option \[-' \]\*foobar\[' \]
|
||||||
|
|
||||||
# Remove the check whether a util was built. Otherwise tests against utils like "arch" are not run.
|
# Remove the check whether a util was built. Otherwise tests against utils like "arch" are not run.
|
||||||
sed -i "s|require_built_ |# require_built_ |g" init.cfg
|
sed -i "s|require_built_ |# require_built_ |g" init.cfg
|
||||||
|
# Some tests are executed with the "nobody" user.
|
||||||
|
# The check to verify if it works is based on the GNU coreutils version
|
||||||
|
# making it too restrictive for us
|
||||||
|
sed -i "s|\$PACKAGE_VERSION|[0-9]*|g" tests/rm/fail-2eperm.sh tests/mv/sticky-to-xpart.sh init.cfg
|
||||||
|
|
||||||
# usage_vs_getopt.sh is heavily modified as it runs all the binaries
|
# usage_vs_getopt.sh is heavily modified as it runs all the binaries
|
||||||
# with the option -/ is used, clap is returning a better error than GNU's. Adjust the GNU test
|
# with the option -/ is used, clap is returning a better error than GNU's. Adjust the GNU test
|
||||||
|
|
Loading…
Reference in a new issue