Skip to content

Commit

Permalink
chroot: implement --skip-chdir
Browse files Browse the repository at this point in the history
  • Loading branch information
sylvestre committed Sep 20, 2022
1 parent 58cfc7f commit e86963b
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 5 deletions.
34 changes: 30 additions & 4 deletions src/uu/chroot/src/chroot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
// For the full copyright and license information, please view the LICENSE
// 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;

use crate::error::ChrootError;
use clap::{crate_version, Arg, Command};
use std::ffi::CString;
use std::io::Error;
use std::path::Path;
use std::path::{Path, PathBuf};
use std::process;
use uucore::error::{set_exit_code, UClapError, UResult};
use uucore::libc::{self, chroot, setgid, setgroups, setuid};
Expand All @@ -29,6 +29,7 @@ mod options {
pub const GROUPS: &str = "groups";
pub const USERSPEC: &str = "userspec";
pub const COMMAND: &str = "command";
pub const SKIP_CHDIR: &str = "skip-chdir";
}

#[uucore::main]
Expand Down Expand Up @@ -143,6 +144,15 @@ pub fn uu_app<'a>() -> Command<'a> {
)
.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::new(options::COMMAND)
.value_hint(clap::ValueHint::CommandName)
Expand All @@ -157,6 +167,7 @@ fn set_context(root: &Path, options: &clap::ArgMatches) -> UResult<()> {
let user_str = options.value_of(options::USER).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 skip_chdir = options.contains_id(options::SKIP_CHDIR);
let userspec = match userspec_str {
Some(u) => {
let s: Vec<&str> = u.split(':').collect();
Expand All @@ -174,20 +185,35 @@ fn set_context(root: &Path, options: &clap::ArgMatches) -> UResult<()> {
(userspec[0], userspec[1])
};

enter_chroot(root)?;
enter_chroot(root, skip_chdir)?;

set_groups_from_str(groups_str)?;
set_main_group(group)?;
set_user(user)?;
Ok(())
}

fn enter_chroot(root: &Path) -> UResult<()> {
fn enter_chroot(root: &Path, skip_chdir: bool) -> UResult<()> {
let cwd = if skip_chdir {
// We want to keep the current working directory
// So, we store the current path
std::env::current_dir().unwrap()
} else {
// Empty path, we won't use it
PathBuf::from("")
};

std::env::set_current_dir(root).unwrap();
let err = unsafe {
chroot(CString::new(".").unwrap().as_bytes_with_nul().as_ptr() as *const libc::c_char)
};
if err == 0 {
// We have successful chroot
// Set the previous path back
if skip_chdir {
std::env::set_current_dir(cwd).unwrap();
}

Ok(())
} else {
Err(ChrootError::CannotEnter(format!("{}", root.display()), Error::last_os_error()).into())
Expand Down
21 changes: 20 additions & 1 deletion tests/by-util/test_chroot.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// spell-checker:ignore (words) araba newroot userspec
// spell-checker:ignore (words) araba newroot userspec chdir

use crate::common::util::*;

Expand Down Expand Up @@ -143,3 +143,22 @@ fn test_chroot() {
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");
}
}

0 comments on commit e86963b

Please sign in to comment.