Merge branch 'kek/chroot3' into 'master'

Add capability to set the default scheme

See merge request redox-os/relibc!510
This commit is contained in:
Jeremy Soller
2024-09-23 14:36:45 +00:00
10 changed files with 136 additions and 31 deletions
+1
View File
@@ -143,6 +143,7 @@ pub fn initialize_freestanding() {
page.tcb_ptr = page;
page.tcb_len = syscall::PAGE_SIZE;
page.tls_end = (page as *mut Tcb).cast();
*page.os_specific.thr_fd.get_mut() = None;
#[cfg(not(target_arch = "aarch64"))]
unsafe {
+15 -4
View File
@@ -45,6 +45,8 @@ pub struct InterpOverride {
pub struct ExtraInfo<'a> {
pub cwd: Option<&'a [u8]>,
// Default scheme for the process
pub default_scheme: Option<&'a [u8]>,
// POSIX states that while sigactions are reset, ignored sigactions will remain ignored.
pub sigignmask: u64,
// POSIX also states that the sigprocmask must be preserved across execs.
@@ -351,8 +353,9 @@ where
)?;
push(AT_PHENT)?;
let total_args_envs_auxvpointee_size =
total_args_envs_size + extrainfo.cwd.map_or(0, |s| s.len() + 1);
let total_args_envs_auxvpointee_size = total_args_envs_size
+ extrainfo.cwd.map_or(0, |s| s.len() + 1)
+ extrainfo.default_scheme.map_or(0, |s| s.len() + 1);
let args_envs_size_aligned = total_args_envs_auxvpointee_size.next_multiple_of(PAGE_SIZE);
let target_args_env_address =
find_free_target_addr(&tree, args_envs_size_aligned).ok_or(Error::new(ENOMEM))?;
@@ -392,10 +395,18 @@ where
if let Some(cwd) = extrainfo.cwd {
push(append(cwd)?)?;
push(AT_REDOX_INITIALCWD_PTR)?;
push(AT_REDOX_INITIAL_CWD_PTR)?;
push(cwd.len())?;
push(AT_REDOX_INITIALCWD_LEN)?;
push(AT_REDOX_INITIAL_CWD_LEN)?;
}
if let Some(default_scheme) = extrainfo.default_scheme {
push(append(default_scheme)?)?;
push(AT_REDOX_INITIAL_DEFAULT_SCHEME_PTR)?;
push(default_scheme.len())?;
push(AT_REDOX_INITIAL_DEFAULT_SCHEME_LEN)?;
}
#[cfg(target_pointer_width = "32")]
{
push((extrainfo.sigignmask >> 32) as usize)?;
+9 -1
View File
@@ -102,13 +102,21 @@ pub unsafe extern "C" fn chdir(path: *const c_char) -> c_int {
}
#[no_mangle]
pub extern "C" fn chroot(path: *const c_char) -> c_int {
pub unsafe extern "C" fn chroot(path: *const c_char) -> c_int {
// TODO: Implement
platform::ERRNO.set(crate::header::errno::EPERM);
-1
}
#[no_mangle]
pub unsafe extern "C" fn set_default_scheme(scheme: *const c_char) -> c_int {
let scheme = CStr::from_ptr(scheme);
Sys::set_default_scheme(scheme)
.map(|_| 0)
.or_minus_one_errno()
}
#[no_mangle]
pub unsafe extern "C" fn chown(path: *const c_char, owner: uid_t, group: gid_t) -> c_int {
let path = CStr::from_ptr(path);
+7 -2
View File
@@ -28,9 +28,9 @@ pub const AT_EXECFN: usize = 31; /* Filename of executable. */
#[cfg(target_os = "redox")]
// XXX: The name AT_CWD is already used in openat... for a completely different purpose.
pub const AT_REDOX_INITIALCWD_PTR: usize = 32;
pub const AT_REDOX_INITIAL_CWD_PTR: usize = 32;
#[cfg(target_os = "redox")]
pub const AT_REDOX_INITIALCWD_LEN: usize = 33;
pub const AT_REDOX_INITIAL_CWD_LEN: usize = 33;
#[cfg(target_os = "redox")]
pub const AT_REDOX_INHERITED_SIGIGNMASK: usize = 34;
@@ -40,3 +40,8 @@ pub const AT_REDOX_INHERITED_SIGIGNMASK_HI: usize = 35;
pub const AT_REDOX_INHERITED_SIGPROCMASK: usize = 36;
#[cfg(all(target_os = "redox", target_pointer_width = "32"))]
pub const AT_REDOX_INHERITED_SIGPROCMASK_HI: usize = 37;
#[cfg(target_os = "redox")]
pub const AT_REDOX_INITIAL_DEFAULT_SCHEME_PTR: usize = 38;
#[cfg(target_os = "redox")]
pub const AT_REDOX_INITIAL_DEFAULT_SCHEME_LEN: usize = 39;
+6 -2
View File
@@ -1,5 +1,5 @@
use crate::io::Write;
use core::{arch::asm, mem::offset_of, ptr};
use crate::{header::errno::EOPNOTSUPP, io::Write};
use core::{arch::asm, ptr};
use super::{types::*, Pal, ERRNO};
use crate::{
@@ -100,6 +100,10 @@ impl Pal for Sys {
e(unsafe { syscall!(CHDIR, path.as_ptr()) }) as c_int
}
fn set_default_scheme(scheme: CStr) -> Result<(), Errno> {
Err(Errno(EOPNOTSUPP))
}
fn chmod(path: CStr, mode: mode_t) -> c_int {
e(unsafe { syscall!(FCHMODAT, AT_FDCWD, path.as_ptr(), mode, 0) }) as c_int
}
+14 -3
View File
@@ -287,13 +287,24 @@ pub fn init(auxvs: Box<[[usize; 2]]>) {
use self::auxv_defs::*;
if let (Some(cwd_ptr), Some(cwd_len)) = (
get_auxv(&auxvs, AT_REDOX_INITIALCWD_PTR),
get_auxv(&auxvs, AT_REDOX_INITIALCWD_LEN),
get_auxv(&auxvs, AT_REDOX_INITIAL_CWD_PTR),
get_auxv(&auxvs, AT_REDOX_INITIAL_CWD_LEN),
) {
let cwd_bytes: &'static [u8] =
unsafe { core::slice::from_raw_parts(cwd_ptr as *const u8, cwd_len) };
if let Ok(cwd) = core::str::from_utf8(cwd_bytes) {
self::sys::path::setcwd_manual(cwd.into());
self::sys::path::set_cwd_manual(cwd.into());
}
}
if let (Some(scheme_ptr), Some(scheme_len)) = (
get_auxv(&auxvs, AT_REDOX_INITIAL_DEFAULT_SCHEME_PTR),
get_auxv(&auxvs, AT_REDOX_INITIAL_DEFAULT_SCHEME_LEN),
) {
let scheme_bytes: &'static [u8] =
unsafe { core::slice::from_raw_parts(scheme_ptr as *const u8, scheme_len) };
if let Ok(scheme) = core::str::from_utf8(scheme_bytes) {
self::sys::path::set_default_scheme_manual(scheme.into());
}
}
+5 -4
View File
@@ -3,7 +3,6 @@ use crate::{
c_str::CStr,
error::Errno,
header::{
dirent::dirent,
sys_resource::{rlimit, rusage},
sys_stat::stat,
sys_statvfs::statvfs,
@@ -33,6 +32,8 @@ pub trait Pal {
fn chdir(path: CStr) -> c_int;
fn set_default_scheme(scheme: CStr) -> Result<(), Errno>;
fn chmod(path: CStr, mode: mode_t) -> c_int;
fn chown(path: CStr, owner: uid_t, group: gid_t) -> c_int;
@@ -191,10 +192,10 @@ pub trait Pal {
fn pipe2(fildes: &mut [c_int], flags: c_int) -> c_int;
unsafe fn rlct_clone(stack: *mut usize) -> Result<crate::pthread::OsTid, Errno>;
unsafe fn rlct_kill(os_tid: crate::pthread::OsTid, signal: usize) -> Result<(), Errno>;
unsafe fn rlct_clone(stack: *mut usize) -> Result<pthread::OsTid, Errno>;
unsafe fn rlct_kill(os_tid: pthread::OsTid, signal: usize) -> Result<(), Errno>;
fn current_os_tid() -> crate::pthread::OsTid;
fn current_os_tid() -> pthread::OsTid;
fn read(fildes: c_int, buf: &mut [u8]) -> Result<ssize_t, Errno>;
fn pread(fildes: c_int, buf: &mut [u8], offset: off_t) -> Result<ssize_t, Errno>;
+5
View File
@@ -131,6 +131,9 @@ pub fn execve(
let wants_setugid = stat.st_mode & ((S_ISUID | S_ISGID) as u16) != 0;
let cwd: Box<[u8]> = super::path::clone_cwd().unwrap_or_default().into();
let default_scheme: Box<[u8]> = super::path::clone_default_scheme()
.unwrap_or_else(|| Box::from("file"))
.into();
// Count arguments
let mut len = 0;
@@ -295,6 +298,7 @@ pub fn execve(
let _ = syscall::write(*escalate_fd, &flatten_with_nul(args))?;
let _ = syscall::write(*escalate_fd, &flatten_with_nul(envs))?;
let _ = syscall::write(*escalate_fd, &cwd)?;
let _ = syscall::write(*escalate_fd, &default_scheme)?;
// Closing will notify the scheme, and from that point we will no longer have control
// over this process (unless it fails). We do this manually since drop cannot handle
@@ -310,6 +314,7 @@ pub fn execve(
let extrainfo = ExtraInfo {
cwd: Some(&cwd),
default_scheme: Some(&default_scheme),
sigignmask: 0,
sigprocmask,
};
+5
View File
@@ -175,6 +175,11 @@ impl Pal for Sys {
e(path::chdir(path).map(|()| 0)) as c_int
}
fn set_default_scheme(path: CStr) -> Result<(), Errno> {
let path = path.to_str().map_err(|_| Errno(EINVAL))?;
Ok(path::set_default_scheme(path)?)
}
fn chmod(path: CStr, mode: mode_t) -> c_int {
match File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC) {
Ok(file) => Self::fchmod(*file, mode),
+69 -15
View File
@@ -23,28 +23,21 @@ pub fn chdir(path: &str) -> Result<()> {
let _siglock = tmp_disable_signals();
let mut cwd_guard = CWD.lock();
let canonicalized =
canonicalize_using_cwd(cwd_guard.as_deref(), path).ok_or(Error::new(ENOENT))?;
let canon = canonicalize_using_cwd(cwd_guard.as_deref(), path).ok_or(Error::new(ENOENT))?;
let canon_with_scheme = canonicalize_with_cwd_internal(cwd_guard.as_deref(), path)?;
let fd = syscall::open(&canonicalized, O_STAT | O_CLOEXEC)?;
let fd = syscall::open(&canon_with_scheme, O_STAT | O_CLOEXEC)?;
let mut stat = Stat::default();
if syscall::fstat(fd, &mut stat).is_err() || (stat.st_mode & MODE_TYPE) != MODE_DIR {
return Err(Error::new(ENOTDIR));
}
let _ = syscall::close(fd);
*cwd_guard = Some(canonicalized.into_boxed_str());
// TODO: Check that the dir exists and is a directory.
*cwd_guard = Some(canon.into_boxed_str());
Ok(())
}
pub fn clone_cwd() -> Option<Box<str>> {
let _siglock = tmp_disable_signals();
CWD.lock().clone()
}
// TODO: MaybeUninit
pub fn getcwd(buf: &mut [u8]) -> Option<usize> {
let _siglock = tmp_disable_signals();
@@ -62,20 +55,81 @@ pub fn getcwd(buf: &mut [u8]) -> Option<usize> {
Some(cwd.len())
}
/// Sets the default scheme
///
/// By default absolute paths resolve to /scheme/file, calling this function
/// allows a different scheme to be used as the root. This property is inherited
/// by child processes.
///
/// Resets CWD to /.
pub fn set_default_scheme(scheme: &str) -> Result<()> {
let _siglock = tmp_disable_signals();
let mut cwd_guard = CWD.lock();
let mut default_scheme_guard = DEFAULT_SCHEME.lock();
*cwd_guard = None;
*default_scheme_guard = Some(scheme.into());
Ok(())
}
// TODO: How much of this logic should be in redox-path?
fn canonicalize_with_cwd_internal(cwd: Option<&str>, path: &str) -> Result<String> {
let path = canonicalize_using_cwd(cwd, path).ok_or(Error::new(ENOENT))?;
let standard_scheme = path == "/scheme" || path.starts_with("/scheme/");
let legacy_scheme = path
.split("/")
.next()
.map(|c| c.contains(":"))
.unwrap_or(false);
Ok(if standard_scheme || legacy_scheme {
path
} else {
let mut default_scheme_guard = DEFAULT_SCHEME.lock();
let default_scheme = default_scheme_guard.get_or_insert_with(|| Box::from("file"));
let mut result = format!("/scheme/{}{}", default_scheme, path);
// Trim trailing / to keep path canonical.
if result.as_bytes().last() == Some(&b'/') {
result.pop();
}
result
})
}
pub fn canonicalize(path: &str) -> Result<String> {
let _siglock = tmp_disable_signals();
let cwd = CWD.lock();
canonicalize_using_cwd(cwd.as_deref(), path).ok_or(Error::new(ENOENT))
let cwd_guard = CWD.lock();
canonicalize_with_cwd_internal(cwd_guard.as_deref(), path)
}
// TODO: arraystring?
static CWD: Mutex<Option<Box<str>>> = Mutex::new(None);
static DEFAULT_SCHEME: Mutex<Option<Box<str>>> = Mutex::new(None);
pub fn setcwd_manual(cwd: Box<str>) {
pub fn set_cwd_manual(cwd: Box<str>) {
let _siglock = tmp_disable_signals();
*CWD.lock() = Some(cwd);
}
pub fn set_default_scheme_manual(scheme: Box<str>) {
let _siglock = tmp_disable_signals();
*DEFAULT_SCHEME.lock() = Some(scheme)
}
pub fn clone_cwd() -> Option<Box<str>> {
let _siglock = tmp_disable_signals();
CWD.lock().clone()
}
pub fn clone_default_scheme() -> Option<Box<str>> {
let _siglock = tmp_disable_signals();
DEFAULT_SCHEME.lock().clone()
}
pub fn open(path: &str, flags: usize) -> Result<usize> {
// TODO: SYMLOOP_MAX
const MAX_LEVEL: usize = 64;
@@ -84,7 +138,7 @@ pub fn open(path: &str, flags: usize) -> Result<usize> {
let mut path = path;
for _ in 0..MAX_LEVEL {
let canon = canonicalize(path)?;
let canon = canonicalize_with_cwd_internal(CWD.lock().as_deref(), path)?;
let open_res = if canon.starts_with(libcscheme::LIBC_SCHEME) {
libcscheme::open(&canon, flags)