123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182 |
- use core::{mem, ptr, result::Result as CoreResult, slice, str};
- use core::arch::asm;
- use syscall::{
- self,
- data::{CloneInfo, Map, Stat as redox_stat, StatVfs as redox_statvfs, TimeSpec as redox_timespec},
- PtraceEvent, Result,
- };
- use crate::{
- c_str::{CStr, CString},
- fs::File,
- header::{
- dirent::dirent,
- errno::{EINVAL, EIO, ENOMEM, EPERM, ERANGE},
- fcntl,
- string::strlen,
- sys_mman::{MAP_ANONYMOUS, PROT_READ, PROT_WRITE},
- sys_random,
- sys_resource::{rlimit, RLIM_INFINITY},
- sys_stat::{stat, S_ISGID, S_ISUID},
- sys_statvfs::statvfs,
- sys_time::{timeval, timezone},
- sys_utsname::{utsname, UTSLENGTH},
- sys_wait,
- time::timespec,
- unistd::{F_OK, R_OK, W_OK, X_OK},
- },
- io::{self, prelude::*, BufReader, SeekFrom},
- };
- use super::{errno, types::*, Pal, Read};
- static mut BRK_CUR: *mut c_void = ptr::null_mut();
- static mut BRK_END: *mut c_void = ptr::null_mut();
- mod clone;
- mod epoll;
- mod exec;
- mod extra;
- mod ptrace;
- mod signal;
- mod socket;
- macro_rules! path_from_c_str {
- ($c_str:expr) => {{
- match $c_str.to_str() {
- Ok(ok) => ok,
- Err(err) => {
- unsafe {
- errno = EINVAL;
- }
- return -1;
- }
- }
- }};
- }
- pub fn e(sys: Result<usize>) -> usize {
- match sys {
- Ok(ok) => ok,
- Err(err) => {
- unsafe {
- errno = err.errno as c_int;
- }
- !0
- }
- }
- }
- fn flatten_with_nul<T>(iter: impl IntoIterator<Item = T>) -> Box<[u8]> where T: AsRef<[u8]> {
- let mut vec = Vec::new();
- for item in iter {
- vec.extend(item.as_ref());
- vec.push(b'\0');
- }
- vec.into_boxed_slice()
- }
- pub struct Sys;
- impl Pal for Sys {
- fn access(path: &CStr, mode: c_int) -> c_int {
- let fd = match File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC) {
- Ok(fd) => fd,
- Err(_) => return -1,
- };
- if mode == F_OK {
- return 0;
- }
- let mut stat = syscall::Stat::default();
- if e(syscall::fstat(*fd as usize, &mut stat)) == !0 {
- return -1;
- }
- let uid = e(syscall::getuid());
- if uid == !0 {
- return -1;
- }
- let gid = e(syscall::getgid());
- if gid == !0 {
- return -1;
- }
- let perms = if stat.st_uid as usize == uid {
- stat.st_mode >> (3 * 2 & 0o7)
- } else if stat.st_gid as usize == gid {
- stat.st_mode >> (3 * 1 & 0o7)
- } else {
- stat.st_mode & 0o7
- };
- if (mode & R_OK == R_OK && perms & 0o4 != 0o4)
- || (mode & W_OK == W_OK && perms & 0o2 != 0o2)
- || (mode & X_OK == X_OK && perms & 0o1 != 0o1)
- {
- unsafe {
- errno = EINVAL;
- }
- return -1;
- }
- 0
- }
- fn brk(addr: *mut c_void) -> *mut c_void {
- unsafe {
- // On first invocation, allocate a buffer for brk
- if BRK_CUR.is_null() {
- // 4 megabytes of RAM ought to be enough for anybody
- const BRK_MAX_SIZE: usize = 4 * 1024 * 1024;
- let allocated = Self::mmap(
- ptr::null_mut(),
- BRK_MAX_SIZE,
- PROT_READ | PROT_WRITE,
- MAP_ANONYMOUS,
- 0,
- 0,
- );
- if allocated == !0 as *mut c_void
- /* MAP_FAILED */
- {
- return !0 as *mut c_void;
- }
- BRK_CUR = allocated;
- BRK_END = (allocated as *mut u8).add(BRK_MAX_SIZE) as *mut c_void;
- }
- if addr.is_null() {
- // Lookup what previous brk() invocations have set the address to
- BRK_CUR
- } else if BRK_CUR <= addr && addr < BRK_END {
- // It's inside buffer, return
- BRK_CUR = addr;
- addr
- } else {
- // It was outside of valid range
- errno = ENOMEM;
- ptr::null_mut()
- }
- }
- }
- fn chdir(path: &CStr) -> c_int {
- let path = path_from_c_str!(path);
- e(syscall::chdir(path)) as c_int
- }
- 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),
- Err(_) => -1,
- }
- }
- fn chown(path: &CStr, owner: uid_t, group: gid_t) -> c_int {
- match File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC) {
- Ok(file) => Self::fchown(*file, owner, group),
- Err(_) => -1,
- }
- }
- fn clock_gettime(clk_id: clockid_t, tp: *mut timespec) -> c_int {
- let mut redox_tp = unsafe { redox_timespec::from(&*tp) };
- match e(syscall::clock_gettime(clk_id as usize, &mut redox_tp)) as c_int {
- -1 => -1,
- _ => {
- unsafe {
- (*tp).tv_sec = redox_tp.tv_sec;
- (*tp).tv_nsec = redox_tp.tv_nsec as i64;
- };
- 0
- }
- }
- }
- fn close(fd: c_int) -> c_int {
- e(syscall::close(fd as usize)) as c_int
- }
- fn dup(fd: c_int) -> c_int {
- e(syscall::dup(fd as usize, &[])) as c_int
- }
- fn dup2(fd1: c_int, fd2: c_int) -> c_int {
- e(syscall::dup2(fd1 as usize, fd2 as usize, &[])) as c_int
- }
- fn exit(status: c_int) -> ! {
- let _ = syscall::exit(status as usize);
- loop {}
- }
- unsafe fn execve(
- path: &CStr,
- mut argv: *const *mut c_char,
- mut envp: *const *mut c_char,
- ) -> c_int {
- // NOTE: We must omit O_CLOEXEC and close manually, otherwise it will be closed before we
- // have even read it!
- let mut file = match File::open(path, fcntl::O_RDONLY) {
- Ok(file) => file,
- Err(_) => return -1,
- };
- let fd = *file as usize;
- // With execve now being implemented in userspace, we need to check ourselves that this
- // file is actually executable. While checking for read permission is unnecessary as the
- // scheme will not allow us to read otherwise, the execute bit is completely unenforced. We
- // have the permission to mmap executable memory and fill it with the program even if it is
- // unset, so the best we can do is check that nothing is executed by accident.
- //
- // TODO: At some point we might have capabilities limiting the ability to allocate
- // executable memory, and in that case we might use the `escalate:` scheme as we already do
- // when the binary needs setuid/setgid.
- let mut stat = redox_stat::default();
- if e(syscall::fstat(fd, &mut stat)) == !0 {
- return -1;
- }
- let uid = e(syscall::getuid());
- if uid == !0 {
- return -1;
- }
- let gid = e(syscall::getuid());
- if gid == !0 {
- return -1;
- }
- let mode = if uid == stat.st_uid as usize {
- (stat.st_mode >> 3 * 2) & 0o7
- } else if gid == stat.st_gid as usize {
- (stat.st_mode >> 3 * 1) & 0o7
- } else {
- stat.st_mode & 0o7
- };
- if mode & 0o1 == 0o0 {
- errno = EPERM;
- return -1;
- }
- let wants_setugid = stat.st_mode & ((S_ISUID | S_ISGID) as u16) != 0;
- // Count arguments
- let mut len = 0;
- while !(*argv.add(len)).is_null() {
- len += 1;
- }
- let mut args: Vec<&[u8]> = Vec::with_capacity(len);
- // Read shebang (for example #!/bin/sh)
- let mut _interpreter_path = None;
- let is_interpreted = {
- let mut read = 0;
- let mut shebang = [0; 2];
- while read < 2 {
- match file.read(&mut shebang) {
- Ok(0) => break,
- Ok(i) => read += i,
- Err(_) => return -1,
- }
- }
- shebang == *b"#!"
- };
- // Since the fexec implementation is almost fully done in userspace, the kernel can no
- // longer set UID/GID accordingly, and this code checking for them before using
- // hypothetical interfaces to upgrade UID/GID, can not be trusted. So we ask the
- // `escalate:` scheme for help. Note that `escalate:` can be deliberately excluded from the
- // scheme namespace to deny privilege escalation (such as su/sudo/doas) for untrusted
- // processes.
- //
- // According to execve(2), Linux and most other UNIXes ignore setuid/setgid for interpreted
- // executables and thereby simply keep the privileges as is. For compatibility we do that
- // too.
- if is_interpreted {
- // So, this file is interpreted.
- // Then, read the actual interpreter:
- let mut interpreter = Vec::new();
- if BufReader::new(&mut file).read_until(b'\n', &mut interpreter).is_err() {
- return -1;
- }
- if interpreter.ends_with(&[b'\n']) {
- interpreter.pop().unwrap();
- }
- let cstring = match CString::new(interpreter) {
- Ok(cstring) => cstring,
- Err(_) => return -1,
- };
- file = match File::open(&cstring, fcntl::O_RDONLY) {
- Ok(file) => file,
- Err(_) => return -1,
- };
- // Make sure path is kept alive long enough, and push it to the arguments
- _interpreter_path = Some(cstring);
- let path_ref = _interpreter_path.as_ref().unwrap();
- args.push(path_ref.as_bytes());
- } else {
- if file.seek(SeekFrom::Start(0)).is_err() {
- return -1;
- }
- }
- let mut args_envs_size_without_nul = 0;
- // Arguments
- while !argv.read().is_null() {
- let arg = argv.read();
- let len = strlen(arg);
- args.push(core::slice::from_raw_parts(arg as *const u8, len));
- args_envs_size_without_nul += len;
- argv = argv.add(1);
- }
- // Environment variables
- let mut len = 0;
- while !envp.add(len).read().is_null() {
- len += 1;
- }
- let mut envs: Vec<&[u8]> = Vec::with_capacity(len);
- while !envp.read().is_null() {
- let env = envp.read();
- let len = strlen(env);
- envs.push(core::slice::from_raw_parts(env as *const u8, len));
- args_envs_size_without_nul += len;
- envp = envp.add(1);
- }
- // Close all O_CLOEXEC file descriptors. TODO: close_range?
- {
- // NOTE: This approach of implementing O_CLOEXEC will not work in multithreaded
- // scenarios. While execve() is undefined according to POSIX if there exist sibling
- // threads, it could still be allowed by keeping certain file descriptors and instead
- // set the active file table.
- let name = CStr::from_bytes_with_nul(b"thisproc:current/filetable\0").expect("string should be valid");
- let files_fd = match File::open(name, fcntl::O_RDONLY) {
- Ok(f) => f,
- Err(_) => return -1,
- };
- for line in BufReader::new(files_fd).lines() {
- let line = match line {
- Ok(l) => l,
- Err(_) => break,
- };
- let fd = match line.parse::<usize>() {
- Ok(f) => f,
- Err(_) => continue,
- };
- let flags = Self::fcntl(fd as c_int, fcntl::F_GETFD, 0);
- if flags != -1 {
- if flags & fcntl::O_CLOEXEC == fcntl::O_CLOEXEC {
- let _ = Self::close(fd as c_int);
- }
- }
- }
- }
- if !is_interpreted && wants_setugid {
- // Make sure the last file descriptor not covered by O_CLOEXEC is not leaked.
- drop(file);
- let name = CStr::from_bytes_with_nul(b"escalate:\0").expect("string should be valid");
- // We are now going to invoke `escalate:` rather than loading the program ourselves.
- let mut escalate_fd = match File::open(name, fcntl::O_WRONLY) {
- Ok(f) => f,
- Err(_) => return -1,
- };
- // First, we write the path.
- //
- // TODO: For improved security, use a hypothetical SYS_DUP_FORWARD syscall to give the
- // scheme our file descriptor. It can check through the kernel-overwritten stat.st_dev
- // field that it pertains to a "trusted" scheme (i.e. of at least the privilege the
- // new uid/gid has), although for now only root can open schemes. Passing a file
- // descriptor and not a path will allow escalated to run in a limited namespace.
- //
- // TODO: Plus, at this point fexecve is not implemented (but specified in
- // POSIX.1-2008), and to avoid bad syscalls such as fpath, passing a file descriptor
- // would be better.
- if escalate_fd.write_all(path.to_bytes()).is_err() {
- return -1;
- }
- // Second, we write the flattened args and envs with NUL characters separating
- // individual items. This can be copied directly into the new executable's memory.
- if escalate_fd.write_all(&flatten_with_nul(args)).is_err() {
- return -1;
- }
- if escalate_fd.write_all(&flatten_with_nul(envs)).is_err() {
- return -1;
- }
- // 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
- // errors.
- let fd = *escalate_fd as usize;
- core::mem::forget(escalate_fd);
- if let Err(err) = syscall::close(fd) {
- return e(Err(err)) as c_int;
- }
- unreachable!()
- } else {
- e(self::exec::fexec_impl(file, path.to_bytes(), &args, &envs, args_envs_size_without_nul)) as c_int
- }
- }
- fn fchdir(fd: c_int) -> c_int {
- let mut buf = [0; 4096];
- let res = e(syscall::fpath(fd as usize, &mut buf));
- if res == !0 {
- !0
- } else {
- match str::from_utf8(&buf[..res]) {
- Ok(path) => e(syscall::chdir(&path)) as c_int,
- Err(_) => {
- unsafe { errno = EINVAL };
- return -1;
- }
- }
- }
- }
- fn fchmod(fd: c_int, mode: mode_t) -> c_int {
- e(syscall::fchmod(fd as usize, mode as u16)) as c_int
- }
- fn fchown(fd: c_int, owner: uid_t, group: gid_t) -> c_int {
- e(syscall::fchown(fd as usize, owner as u32, group as u32)) as c_int
- }
- fn fcntl(fd: c_int, cmd: c_int, args: c_int) -> c_int {
- e(syscall::fcntl(fd as usize, cmd as usize, args as usize)) as c_int
- }
- fn flock(_fd: c_int, _operation: c_int) -> c_int {
- // TODO: Redox does not have file locking yet
- 0
- }
- fn fork() -> pid_t {
- e(clone::fork_impl()) as pid_t
- }
- fn fstat(fildes: c_int, buf: *mut stat) -> c_int {
- let mut redox_buf: redox_stat = redox_stat::default();
- match e(syscall::fstat(fildes as usize, &mut redox_buf)) {
- 0 => {
- if let Some(buf) = unsafe { buf.as_mut() } {
- buf.st_dev = redox_buf.st_dev as dev_t;
- buf.st_ino = redox_buf.st_ino as ino_t;
- buf.st_nlink = redox_buf.st_nlink as nlink_t;
- buf.st_mode = redox_buf.st_mode as mode_t;
- buf.st_uid = redox_buf.st_uid as uid_t;
- buf.st_gid = redox_buf.st_gid as gid_t;
- // TODO st_rdev
- buf.st_rdev = 0;
- buf.st_size = redox_buf.st_size as off_t;
- buf.st_blksize = redox_buf.st_blksize as blksize_t;
- buf.st_atim = timespec {
- tv_sec: redox_buf.st_atime as time_t,
- tv_nsec: redox_buf.st_atime_nsec as c_long,
- };
- buf.st_mtim = timespec {
- tv_sec: redox_buf.st_mtime as time_t,
- tv_nsec: redox_buf.st_mtime_nsec as c_long,
- };
- buf.st_ctim = timespec {
- tv_sec: redox_buf.st_ctime as time_t,
- tv_nsec: redox_buf.st_ctime_nsec as c_long,
- };
- }
- 0
- }
- _ => -1,
- }
- }
- fn fstatvfs(fildes: c_int, buf: *mut statvfs) -> c_int {
- let mut kbuf: redox_statvfs = redox_statvfs::default();
- match e(syscall::fstatvfs(fildes as usize, &mut kbuf)) {
- 0 => {
- unsafe {
- if !buf.is_null() {
- (*buf).f_bsize = kbuf.f_bsize as c_ulong;
- (*buf).f_frsize = kbuf.f_bsize as c_ulong;
- (*buf).f_blocks = kbuf.f_blocks;
- (*buf).f_bfree = kbuf.f_bfree;
- (*buf).f_bavail = kbuf.f_bavail;
- //TODO
- (*buf).f_files = 0;
- (*buf).f_ffree = 0;
- (*buf).f_favail = 0;
- (*buf).f_fsid = 0;
- (*buf).f_flag = 0;
- (*buf).f_namemax = 0;
- }
- }
- 0
- }
- _ => -1,
- }
- }
- fn fsync(fd: c_int) -> c_int {
- e(syscall::fsync(fd as usize)) as c_int
- }
- fn ftruncate(fd: c_int, len: off_t) -> c_int {
- e(syscall::ftruncate(fd as usize, len as usize)) as c_int
- }
- fn futex(addr: *mut c_int, op: c_int, val: c_int, val2: usize) -> c_int {
- match unsafe {
- syscall::futex(
- addr as *mut i32,
- op as usize,
- val as i32,
- val2,
- ptr::null_mut(),
- )
- } {
- Ok(success) => success as c_int,
- Err(err) => -(err.errno as c_int),
- }
- }
- fn futimens(fd: c_int, times: *const timespec) -> c_int {
- let times = [unsafe { redox_timespec::from(&*times) }, unsafe {
- redox_timespec::from(&*times.offset(1))
- }];
- e(syscall::futimens(fd as usize, ×)) as c_int
- }
- fn utimens(path: &CStr, times: *const timespec) -> c_int {
- match File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC) {
- Ok(file) => Self::futimens(*file, times),
- Err(_) => -1,
- }
- }
- fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char {
- let buf_slice = unsafe { slice::from_raw_parts_mut(buf as *mut u8, size as usize) };
- if !buf_slice.is_empty() {
- let nonnull_size = buf_slice.len() - 1;
- let read = e(syscall::getcwd(&mut buf_slice[..nonnull_size]));
- if read == !0 {
- ptr::null_mut()
- } else if read == nonnull_size {
- unsafe {
- errno = ERANGE;
- }
- ptr::null_mut()
- } else {
- for b in &mut buf_slice[read..] {
- *b = 0;
- }
- buf
- }
- } else {
- unsafe {
- errno = EINVAL;
- }
- ptr::null_mut()
- }
- }
- fn getdents(fd: c_int, mut dirents: *mut dirent, max_bytes: usize) -> c_int {
- //TODO: rewrite this code. Originally the *dirents = dirent { ... } stuff below caused
- // massive issues. This has been hacked around, but it still isn't perfect
- // Get initial reading position
- let mut read = match syscall::lseek(fd as usize, 0, syscall::SEEK_CUR) {
- Ok(pos) => pos as isize,
- Err(err) => return -err.errno,
- };
- let mut written = 0;
- let mut buf = [0; 1024];
- let mut name = [0; 256];
- let mut i = 0;
- let mut flush = |written: &mut usize, i: &mut usize, name: &mut [c_char; 256]| {
- if *i < name.len() {
- // Set NUL byte
- name[*i] = 0;
- }
- // Get size: full size - unused bytes
- if *written + mem::size_of::<dirent>() > max_bytes {
- // Seek back to after last read entry and return
- match syscall::lseek(fd as usize, read, syscall::SEEK_SET) {
- Ok(_) => return Some(*written as c_int),
- Err(err) => return Some(-err.errno),
- }
- }
- let size = mem::size_of::<dirent>() - name.len().saturating_sub(*i + 1);
- unsafe {
- //This is the offending code mentioned above
- *dirents = dirent {
- d_ino: 0,
- d_off: read as off_t,
- d_reclen: size as c_ushort,
- d_type: 0,
- d_name: *name,
- };
- dirents = (dirents as *mut u8).offset(size as isize) as *mut dirent;
- }
- read += *i as isize + /* newline */ 1;
- *written += size;
- *i = 0;
- None
- };
- loop {
- // Read a chunk from the directory
- let len = match syscall::read(fd as usize, &mut buf) {
- Ok(0) => {
- if i > 0 {
- if let Some(value) = flush(&mut written, &mut i, &mut name) {
- return value;
- }
- }
- return written as c_int;
- }
- Ok(n) => n,
- Err(err) => return -err.errno,
- };
- // Handle everything
- let mut start = 0;
- while start < len {
- let buf = &buf[start..len];
- // Copy everything up until a newline
- let newline = buf.iter().position(|&c| c == b'\n');
- let pre_len = newline.unwrap_or(buf.len());
- let post_len = newline.map(|i| i + 1).unwrap_or(buf.len());
- if i < pre_len {
- // Reserve space for NUL byte
- let name_len = name.len() - 1;
- let name = &mut name[i..name_len];
- let copy = pre_len.min(name.len());
- let buf = unsafe { slice::from_raw_parts(buf.as_ptr() as *const c_char, copy) };
- name[..copy].copy_from_slice(buf);
- }
- i += pre_len;
- start += post_len;
- // Write the directory entry
- if newline.is_some() {
- if let Some(value) = flush(&mut written, &mut i, &mut name) {
- return value;
- }
- }
- }
- }
- }
- fn getegid() -> gid_t {
- e(syscall::getegid()) as gid_t
- }
- fn geteuid() -> uid_t {
- e(syscall::geteuid()) as uid_t
- }
- fn getgid() -> gid_t {
- e(syscall::getgid()) as gid_t
- }
- fn getpagesize() -> usize {
- 4096
- }
- fn getpgid(pid: pid_t) -> pid_t {
- e(syscall::getpgid(pid as usize)) as pid_t
- }
- fn getpid() -> pid_t {
- e(syscall::getpid()) as pid_t
- }
- fn getppid() -> pid_t {
- e(syscall::getppid()) as pid_t
- }
- fn getrandom(buf: &mut [u8], flags: c_uint) -> ssize_t {
- //TODO: make this a system call?
- let path = if flags & sys_random::GRND_RANDOM != 0 {
- //TODO: /dev/random equivalent
- "rand:"
- } else {
- "rand:"
- };
- let mut open_flags = syscall::O_RDONLY | syscall::O_CLOEXEC;
- if flags & sys_random::GRND_NONBLOCK != 0 {
- open_flags |= syscall::O_NONBLOCK;
- }
- let fd = e(syscall::open(path, open_flags));
- if fd == !0 {
- return -1;
- }
- let res = e(syscall::read(fd, buf)) as ssize_t;
- let _ = syscall::close(fd);
- res
- }
- unsafe fn getrlimit(resource: c_int, rlim: *mut rlimit) -> c_int {
- //TODO
- if !rlim.is_null() {
- (*rlim).rlim_cur = RLIM_INFINITY;
- (*rlim).rlim_max = RLIM_INFINITY;
- }
- 0
- }
- fn gettid() -> pid_t {
- //TODO
- Self::getpid()
- }
- fn gettimeofday(tp: *mut timeval, tzp: *mut timezone) -> c_int {
- let mut redox_tp = redox_timespec::default();
- let err = e(syscall::clock_gettime(
- syscall::CLOCK_REALTIME,
- &mut redox_tp,
- )) as c_int;
- if err < 0 {
- return err;
- }
- unsafe {
- (*tp).tv_sec = redox_tp.tv_sec as time_t;
- (*tp).tv_usec = (redox_tp.tv_nsec / 1000) as suseconds_t;
- if !tzp.is_null() {
- (*tzp).tz_minuteswest = 0;
- (*tzp).tz_dsttime = 0;
- }
- }
- 0
- }
- fn getuid() -> uid_t {
- e(syscall::getuid()) as pid_t
- }
- fn lchown(path: &CStr, owner: uid_t, group: gid_t) -> c_int {
- // TODO: Is it correct for regular chown to use O_PATH? On Linux the meaning of that flag
- // is to forbid file operations, including fchown.
- // unlike chown, never follow symbolic links
- match File::open(path, fcntl::O_CLOEXEC | fcntl::O_NOFOLLOW) {
- Ok(file) => Self::fchown(*file, owner, group),
- Err(_) => -1,
- }
- }
- fn link(path1: &CStr, path2: &CStr) -> c_int {
- e(unsafe { syscall::link(path1.as_ptr() as *const u8, path2.as_ptr() as *const u8) })
- as c_int
- }
- fn lseek(fd: c_int, offset: off_t, whence: c_int) -> off_t {
- e(syscall::lseek(
- fd as usize,
- offset as isize,
- whence as usize,
- )) as off_t
- }
- fn mkdir(path: &CStr, mode: mode_t) -> c_int {
- match File::create(
- path,
- fcntl::O_DIRECTORY | fcntl::O_EXCL | fcntl::O_CLOEXEC,
- 0o777,
- ) {
- Ok(_fd) => 0,
- Err(_) => -1,
- }
- }
- fn mkfifo(path: &CStr, mode: mode_t) -> c_int {
- match File::create(
- path,
- fcntl::O_CREAT | fcntl::O_CLOEXEC,
- syscall::MODE_FIFO as mode_t | (mode & 0o777),
- ) {
- Ok(fd) => 0,
- Err(_) => -1,
- }
- }
- unsafe fn mlock(addr: *const c_void, len: usize) -> c_int {
- // Redox never swaps
- 0
- }
- fn mlockall(flags: c_int) -> c_int {
- // Redox never swaps
- 0
- }
- unsafe fn mmap(
- addr: *mut c_void,
- len: usize,
- prot: c_int,
- flags: c_int,
- fildes: c_int,
- off: off_t,
- ) -> *mut c_void {
- let map = Map {
- offset: off as usize,
- size: len,
- flags: syscall::MapFlags::from_bits_truncate(
- ((prot as usize) << 16) | ((flags as usize) & 0xFFFF),
- ),
- address: addr as usize,
- };
- if flags & MAP_ANONYMOUS == MAP_ANONYMOUS {
- e(syscall::fmap(!0, &map)) as *mut c_void
- } else {
- e(syscall::fmap(fildes as usize, &map)) as *mut c_void
- }
- }
- unsafe fn mprotect(addr: *mut c_void, len: usize, prot: c_int) -> c_int {
- e(syscall::mprotect(
- addr as usize,
- len,
- syscall::MapFlags::from_bits((prot as usize) << 16)
- .expect("mprotect: invalid bit pattern"),
- )) as c_int
- }
- unsafe fn msync(addr: *mut c_void, len: usize, flags: c_int) -> c_int {
- eprintln!("msync {:p} {:x} {:x}", addr, len, flags);
- e(Err(syscall::Error::new(syscall::ENOSYS))) as c_int
- /* TODO
- e(syscall::msync(
- addr as usize,
- len,
- flags
- )) as c_int
- */
- }
- unsafe fn munlock(addr: *const c_void, len: usize) -> c_int {
- // Redox never swaps
- 0
- }
- fn munlockall() -> c_int {
- // Redox never swaps
- 0
- }
- unsafe fn munmap(addr: *mut c_void, len: usize) -> c_int {
- if e(syscall::funmap(addr as usize, len)) == !0 {
- return !0;
- }
- 0
- }
- fn nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> c_int {
- let redox_rqtp = unsafe { redox_timespec::from(&*rqtp) };
- let mut redox_rmtp: redox_timespec;
- if rmtp.is_null() {
- redox_rmtp = redox_timespec::default();
- } else {
- redox_rmtp = unsafe { redox_timespec::from(&*rmtp) };
- }
- match e(syscall::nanosleep(&redox_rqtp, &mut redox_rmtp)) as c_int {
- -1 => -1,
- _ => {
- unsafe {
- if !rmtp.is_null() {
- (*rmtp).tv_sec = redox_rmtp.tv_sec;
- (*rmtp).tv_nsec = redox_rmtp.tv_nsec as i64;
- }
- }
- 0
- }
- }
- }
- fn open(path: &CStr, oflag: c_int, mode: mode_t) -> c_int {
- let path = path_from_c_str!(path);
- e(syscall::open(
- path,
- ((oflag as usize) & 0xFFFF_0000) | ((mode as usize) & 0xFFFF),
- )) as c_int
- }
- fn pipe2(fds: &mut [c_int], flags: c_int) -> c_int {
- let mut usize_fds: [usize; 2] = [0; 2];
- let res = e(syscall::pipe2(&mut usize_fds, flags as usize));
- fds[0] = usize_fds[0] as c_int;
- fds[1] = usize_fds[1] as c_int;
- res as c_int
- }
- #[cfg(target_arch = "aarch64")]
- unsafe fn pte_clone(stack: *mut usize) -> pid_t {
- //TODO: aarch64
- unimplemented!("pte_clone not implemented on aarch64");
- }
- #[cfg(target_arch = "x86")]
- unsafe fn pte_clone(stack: *mut usize) -> pid_t {
- //TODO: x86
- unimplemented!("pte_clone not implemented on x86");
- }
- #[cfg(target_arch = "x86_64")]
- unsafe fn pte_clone(stack: *mut usize) -> pid_t {
- e(clone::pte_clone_impl(stack)) as pid_t
- }
- fn read(fd: c_int, buf: &mut [u8]) -> ssize_t {
- e(syscall::read(fd as usize, buf)) as ssize_t
- }
- fn fpath(fildes: c_int, out: &mut [u8]) -> ssize_t {
- e(syscall::fpath(fildes as usize, out)) as ssize_t
- }
- fn readlink(pathname: &CStr, out: &mut [u8]) -> ssize_t {
- match File::open(pathname, fcntl::O_RDONLY | fcntl::O_SYMLINK | fcntl::O_CLOEXEC) {
- Ok(file) => Self::read(*file, out),
- Err(_) => return -1,
- }
- }
- fn rename(oldpath: &CStr, newpath: &CStr) -> c_int {
- let newpath = path_from_c_str!(newpath);
- match File::open(oldpath, fcntl::O_PATH | fcntl::O_CLOEXEC) {
- Ok(file) => e(syscall::frename(*file as usize, newpath)) as c_int,
- Err(_) => -1,
- }
- }
- fn rmdir(path: &CStr) -> c_int {
- let path = path_from_c_str!(path);
- e(syscall::rmdir(path)) as c_int
- }
- fn sched_yield() -> c_int {
- e(syscall::sched_yield()) as c_int
- }
- fn setpgid(pid: pid_t, pgid: pid_t) -> c_int {
- e(syscall::setpgid(pid as usize, pgid as usize)) as c_int
- }
- fn setregid(rgid: gid_t, egid: gid_t) -> c_int {
- e(syscall::setregid(rgid as usize, egid as usize)) as c_int
- }
- fn setreuid(ruid: uid_t, euid: uid_t) -> c_int {
- e(syscall::setreuid(ruid as usize, euid as usize)) as c_int
- }
- fn symlink(path1: &CStr, path2: &CStr) -> c_int {
- let mut file = match File::create(
- path2,
- fcntl::O_WRONLY | fcntl::O_SYMLINK | fcntl::O_CLOEXEC,
- 0o777,
- ) {
- Ok(ok) => ok,
- Err(_) => return -1,
- };
- if file.write(path1.to_bytes()).is_err() {
- return -1;
- }
- 0
- }
- fn umask(mask: mode_t) -> mode_t {
- e(syscall::umask(mask as usize)) as mode_t
- }
- fn uname(utsname: *mut utsname) -> c_int {
- fn gethostname(name: &mut [u8]) -> io::Result<()> {
- if name.is_empty() {
- return Ok(());
- }
- let mut file = File::open(
- &CString::new("/etc/hostname").unwrap(),
- fcntl::O_RDONLY | fcntl::O_CLOEXEC,
- )?;
- let mut read = 0;
- let name_len = name.len();
- loop {
- match file.read(&mut name[read..name_len - 1])? {
- 0 => break,
- n => read += n,
- }
- }
- name[read] = 0;
- Ok(())
- }
- fn inner(utsname: *mut utsname) -> CoreResult<(), i32> {
- match gethostname(unsafe {
- slice::from_raw_parts_mut(
- (*utsname).nodename.as_mut_ptr() as *mut u8,
- (*utsname).nodename.len(),
- )
- }) {
- Ok(_) => (),
- Err(_) => return Err(EIO),
- }
- let file_path = c_str!("sys:uname");
- let mut file = match File::open(file_path, fcntl::O_RDONLY | fcntl::O_CLOEXEC) {
- Ok(ok) => ok,
- Err(_) => return Err(EIO),
- };
- let mut lines = BufReader::new(&mut file).lines();
- let mut read_line = |dst: &mut [c_char]| {
- let line = match lines.next() {
- Some(Ok(l)) => match CString::new(l) {
- Ok(l) => l,
- Err(_) => return Err(EIO),
- },
- None | Some(Err(_)) => return Err(EIO),
- };
- let line_slice: &[c_char] = unsafe { mem::transmute(line.as_bytes_with_nul()) };
- if line_slice.len() <= UTSLENGTH {
- dst[..line_slice.len()].copy_from_slice(line_slice);
- Ok(())
- } else {
- Err(EIO)
- }
- };
- unsafe {
- read_line(&mut (*utsname).sysname)?;
- read_line(&mut (*utsname).release)?;
- read_line(&mut (*utsname).machine)?;
- // Version is not provided
- ptr::write_bytes((*utsname).version.as_mut_ptr(), 0, UTSLENGTH);
- // Redox doesn't provide domainname in sys:uname
- //read_line(&mut (*utsname).domainname)?;
- ptr::write_bytes((*utsname).domainname.as_mut_ptr(), 0, UTSLENGTH);
- }
- Ok(())
- }
- match inner(utsname) {
- Ok(()) => 0,
- Err(err) => unsafe {
- errno = err;
- -1
- },
- }
- }
- fn unlink(path: &CStr) -> c_int {
- let path = path_from_c_str!(path);
- e(syscall::unlink(path)) as c_int
- }
- fn waitpid(mut pid: pid_t, stat_loc: *mut c_int, options: c_int) -> pid_t {
- if pid == !0 {
- pid = 0;
- }
- let mut res = None;
- let mut status = 0;
- let inner = |status: &mut usize, flags| {
- syscall::waitpid(
- pid as usize,
- status,
- syscall::WaitFlags::from_bits(flags as usize)
- .expect("waitpid: invalid bit pattern"),
- )
- };
- // First, allow ptrace to handle waitpid
- // TODO: Handle special PIDs here (such as -1)
- let state = ptrace::init_state();
- let mut sessions = state.sessions.lock();
- if let Ok(session) = ptrace::get_session(&mut sessions, pid) {
- if options & sys_wait::WNOHANG != sys_wait::WNOHANG {
- let mut _event = PtraceEvent::default();
- let _ = (&mut &session.tracer).read(&mut _event);
- res = Some(e(inner(
- &mut status,
- options | sys_wait::WNOHANG | sys_wait::WUNTRACED,
- )));
- if res == Some(0) {
- // WNOHANG, just pretend ptrace SIGSTOP:ped this
- status = (syscall::SIGSTOP << 8) | 0x7f;
- assert!(syscall::wifstopped(status));
- assert_eq!(syscall::wstopsig(status), syscall::SIGSTOP);
- res = Some(pid as usize);
- }
- }
- }
- // If ptrace didn't impact this waitpid, proceed *almost* as
- // normal: We still need to add WUNTRACED, but we only return
- // it if (and only if) a ptrace traceme was activated during
- // the wait.
- let res = res.unwrap_or_else(|| loop {
- let res = e(inner(&mut status, options | sys_wait::WUNTRACED));
- // TODO: Also handle special PIDs here
- if !syscall::wifstopped(res) || ptrace::is_traceme(pid) {
- break res;
- }
- });
- // If stat_loc is non-null, set that and the return
- unsafe {
- if !stat_loc.is_null() {
- *stat_loc = status as c_int;
- }
- }
- res as pid_t
- }
- fn write(fd: c_int, buf: &[u8]) -> ssize_t {
- e(syscall::write(fd as usize, buf)) as ssize_t
- }
- fn verify() -> bool {
- // GETPID on Redox is 20, which is WRITEV on Linux
- e(unsafe { syscall::syscall5(syscall::number::SYS_GETPID, !0, !0, !0, !0, !0) }) != !0
- }
- }
|