1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2026-01-15 09:41:07 +00:00

chroot: return UResult from uumain() function

This commit is contained in:
Jeffrey Finkelstein 2021-12-24 12:47:29 -05:00
parent eb87ddbaf7
commit aacbfe681f
2 changed files with 131 additions and 70 deletions

View file

@ -7,15 +7,15 @@
// file that was distributed with this source code.
// spell-checker:ignore (ToDO) NEWROOT Userspec pstatus
mod error;
#[macro_use]
extern crate uucore;
use crate::error::ChrootError;
use clap::{crate_version, App, Arg};
use std::ffi::CString;
use std::io::Error;
use std::path::Path;
use std::process::Command;
use uucore::display::Quotable;
use uucore::error::{set_exit_code, UResult};
use uucore::libc::{self, chroot, setgid, setgroups, setuid};
use uucore::{entries, InvalidEncodingHandling};
@ -31,7 +31,8 @@ mod options {
pub const COMMAND: &str = "command";
}
pub fn uumain(args: impl uucore::Args) -> i32 {
#[uucore_procs::gen_uumain]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let args = args
.collect_str(InvalidEncodingHandling::ConvertLossy)
.accept_any();
@ -44,19 +45,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let newroot: &Path = match matches.value_of(options::NEWROOT) {
Some(v) => Path::new(v),
None => crash!(
1,
"Missing operand: NEWROOT\nTry '{} --help' for more information.",
uucore::execution_phrase()
),
None => return Err(ChrootError::MissingNewRoot.into()),
};
if !newroot.is_dir() {
crash!(
1,
"cannot change root directory to {}: no such directory",
newroot.quote()
);
return Err(ChrootError::NoSuchDirectory(format!("{}", newroot.display())).into());
}
let commands = match matches.values_of(options::COMMAND) {
@ -82,29 +75,20 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let chroot_args = &command[1..];
// NOTE: Tests can only trigger code beyond this point if they're invoked with root permissions
set_context(newroot, &matches);
set_context(newroot, &matches)?;
let pstatus = Command::new(chroot_command)
.args(chroot_args)
.status()
.unwrap_or_else(|e| {
// TODO: Exit status:
// 125 if chroot itself fails
// 126 if command is found but cannot be invoked
// 127 if command cannot be found
crash!(
1,
"failed to run command {}: {}",
command[0].to_string().quote(),
e
)
});
let pstatus = match Command::new(chroot_command).args(chroot_args).status() {
Ok(status) => status,
Err(e) => return Err(ChrootError::CommandFailed(command[0].to_string(), e).into()),
};
if pstatus.success() {
let code = if pstatus.success() {
0
} else {
pstatus.code().unwrap_or(-1)
}
};
set_exit_code(code);
Ok(())
}
pub fn uu_app() -> App<'static, 'static> {
@ -157,7 +141,7 @@ pub fn uu_app() -> App<'static, 'static> {
)
}
fn set_context(root: &Path, options: &clap::ArgMatches) {
fn set_context(root: &Path, options: &clap::ArgMatches) -> UResult<()> {
let userspec_str = options.value_of(options::USERSPEC);
let user_str = options.value_of(options::USER).unwrap_or_default();
let group_str = options.value_of(options::GROUP).unwrap_or_default();
@ -166,7 +150,7 @@ fn set_context(root: &Path, options: &clap::ArgMatches) {
Some(u) => {
let s: Vec<&str> = u.split(':').collect();
if s.len() != 2 || s.iter().any(|&spec| spec.is_empty()) {
crash!(1, "invalid userspec: {}", u.quote())
return Err(ChrootError::InvalidUserspec(u.to_string()).into());
};
s
}
@ -179,44 +163,40 @@ fn set_context(root: &Path, options: &clap::ArgMatches) {
(userspec[0], userspec[1])
};
enter_chroot(root);
enter_chroot(root)?;
set_groups_from_str(groups_str);
set_main_group(group);
set_user(user);
set_groups_from_str(groups_str)?;
set_main_group(group)?;
set_user(user)?;
Ok(())
}
fn enter_chroot(root: &Path) {
fn enter_chroot(root: &Path) -> UResult<()> {
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 {
crash!(
1,
"cannot chroot to {}: {}",
root.quote(),
Error::last_os_error()
)
};
if err == 0 {
Ok(())
} else {
Err(ChrootError::CannotEnter(format!("{}", root.display()), Error::last_os_error()).into())
}
}
fn set_main_group(group: &str) {
fn set_main_group(group: &str) -> UResult<()> {
if !group.is_empty() {
let group_id = match entries::grp2gid(group) {
Ok(g) => g,
_ => crash!(1, "no such group: {}", group.maybe_quote()),
_ => return Err(ChrootError::NoSuchGroup(group.to_string()).into()),
};
let err = unsafe { setgid(group_id) };
if err != 0 {
crash!(
1,
"cannot set gid to {}: {}",
group_id,
Error::last_os_error()
)
return Err(
ChrootError::SetGidFailed(group_id.to_string(), Error::last_os_error()).into(),
);
}
}
Ok(())
}
#[cfg(any(target_vendor = "apple", target_os = "freebsd"))]
@ -229,33 +209,33 @@ fn set_groups(groups: Vec<libc::gid_t>) -> libc::c_int {
unsafe { setgroups(groups.len() as libc::size_t, groups.as_ptr()) }
}
fn set_groups_from_str(groups: &str) {
fn set_groups_from_str(groups: &str) -> UResult<()> {
if !groups.is_empty() {
let groups_vec: Vec<libc::gid_t> = groups
.split(',')
.map(|x| match entries::grp2gid(x) {
let mut groups_vec = vec![];
for group in groups.split(',') {
let gid = match entries::grp2gid(group) {
Ok(g) => g,
_ => crash!(1, "no such group: {}", x),
})
.collect();
Err(_) => return Err(ChrootError::NoSuchGroup(group.to_string()).into()),
};
groups_vec.push(gid);
}
let err = set_groups(groups_vec);
if err != 0 {
crash!(1, "cannot set groups: {}", Error::last_os_error())
return Err(ChrootError::SetGroupsFailed(Error::last_os_error()).into());
}
}
Ok(())
}
fn set_user(user: &str) {
fn set_user(user: &str) -> UResult<()> {
if !user.is_empty() {
let user_id = entries::usr2uid(user).unwrap();
let err = unsafe { setuid(user_id as libc::uid_t) };
if err != 0 {
crash!(
1,
"cannot set user to {}: {}",
user.maybe_quote(),
Error::last_os_error()
)
return Err(
ChrootError::SetUserFailed(user.to_string(), Error::last_os_error()).into(),
);
}
}
Ok(())
}

View file

@ -0,0 +1,81 @@
// * This file is part of the uutils coreutils package.
// *
// * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code.
// spell-checker:ignore NEWROOT Userspec userspec
//! Errors returned by chroot.
use std::fmt::Display;
use std::io::Error;
use uucore::display::Quotable;
use uucore::error::UError;
/// Errors that can happen while executing chroot.
#[derive(Debug)]
pub enum ChrootError {
/// Failed to enter the specified directory.
CannotEnter(String, Error),
/// Failed to execute the specified command.
CommandFailed(String, Error),
/// The given user and group specification was invalid.
InvalidUserspec(String),
/// The new root directory was not given.
MissingNewRoot,
/// Failed to find the specified group.
NoSuchGroup(String),
/// The given directory does not exist.
NoSuchDirectory(String),
/// The call to `setgid()` failed.
SetGidFailed(String, Error),
/// The call to `setgroups()` failed.
SetGroupsFailed(Error),
/// The call to `setuid()` failed.
SetUserFailed(String, Error),
}
impl std::error::Error for ChrootError {}
impl UError for ChrootError {
// TODO: Exit status:
// 125 if chroot itself fails
// 126 if command is found but cannot be invoked
// 127 if command cannot be found
fn code(&self) -> i32 {
1
}
}
impl Display for ChrootError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
ChrootError::CannotEnter(s, e) => write!(f, "cannot chroot to {}: {}", s.quote(), e,),
ChrootError::CommandFailed(s, e) => {
write!(f, "failed to run command {}: {}", s.to_string().quote(), e,)
}
ChrootError::InvalidUserspec(s) => write!(f, "invalid userspec: {}", s.quote(),),
ChrootError::MissingNewRoot => write!(
f,
"Missing operand: NEWROOT\nTry '{} --help' for more information.",
uucore::execution_phrase(),
),
ChrootError::NoSuchGroup(s) => write!(f, "no such group: {}", s.maybe_quote(),),
ChrootError::NoSuchDirectory(s) => write!(
f,
"cannot change root directory to {}: no such directory",
s.quote(),
),
ChrootError::SetGidFailed(s, e) => write!(f, "cannot set gid to {}: {}", s, e),
ChrootError::SetGroupsFailed(e) => write!(f, "cannot set groups: {}", e),
ChrootError::SetUserFailed(s, e) => {
write!(f, "cannot set user to {}: {}", s.maybe_quote(), e)
}
}
}
}