Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e913bacaa3 | |||
| e5c076b0ea | |||
| 3c84baebed | |||
| ea272706a7 | |||
| 5dc4a8364e |
+1
-2
@@ -1,2 +1 @@
|
|||||||
Cargo.lock
|
/target/
|
||||||
target
|
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
image: "redoxos/redoxer"
|
|
||||||
|
|
||||||
stages:
|
|
||||||
- build
|
|
||||||
|
|
||||||
workflow:
|
|
||||||
rules:
|
|
||||||
- if: '$CI_COMMIT_BRANCH == "master" && $CI_PROJECT_NAMESPACE == "redox-os"'
|
|
||||||
- if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"'
|
|
||||||
|
|
||||||
build:linux:
|
|
||||||
stage: build
|
|
||||||
script: cargo +nightly build
|
|
||||||
|
|
||||||
build:redox:
|
|
||||||
stage: build
|
|
||||||
script: redoxer build
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
language: rust
|
||||||
|
rust:
|
||||||
|
- nightly
|
||||||
|
sudo: false
|
||||||
|
notifications:
|
||||||
|
email: false
|
||||||
+76
-19
@@ -1,25 +1,82 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "redox_syscall"
|
name = "userutils"
|
||||||
version = "0.8.1"
|
version = "0.1.0+rb0.2.5"
|
||||||
description = "A Rust library to access raw Redox system calls"
|
authors = ["Jeremy Soller <jackpot51@gmail.com>", "vasilito <adminpupkin@gmail.com>"]
|
||||||
license = "MIT"
|
edition = "2024"
|
||||||
authors = ["Jeremy Soller <jackpot51@gmail.com>"]
|
|
||||||
repository = "https://gitlab.redox-os.org/redox-os/syscall"
|
|
||||||
documentation = "https://docs.rs/redox_syscall"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[lib]
|
[[bin]]
|
||||||
name = "syscall"
|
name = "id"
|
||||||
|
path = "src/bin/id.rs"
|
||||||
|
|
||||||
[features]
|
[[bin]]
|
||||||
default = ["userspace"]
|
name = "getty"
|
||||||
rustc-dep-of-std = ["core", "bitflags/rustc-dep-of-std"]
|
path = "src/bin/getty.rs"
|
||||||
userspace = []
|
|
||||||
std = []
|
[[bin]]
|
||||||
|
name = "groupadd"
|
||||||
|
path = "src/bin/groupadd.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "groupdel"
|
||||||
|
path = "src/bin/groupdel.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "groupmod"
|
||||||
|
path = "src/bin/groupmod.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "login"
|
||||||
|
path = "src/bin/login.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "passwd"
|
||||||
|
path = "src/bin/passwd.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "su"
|
||||||
|
path = "src/bin/su.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "sudo"
|
||||||
|
path = "src/bin/sudo.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "useradd"
|
||||||
|
path = "src/bin/useradd.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "userdel"
|
||||||
|
path = "src/bin/userdel.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "usermod"
|
||||||
|
path = "src/bin/usermod.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "2.4"
|
clap = "2.33.0"
|
||||||
core = { version = "1.0.0", optional = true, package = "rustc-std-workspace-core" }
|
extra = { git = "https://gitlab.redox-os.org/redox-os/libextra.git" }
|
||||||
|
orbclient = "0.3.47"
|
||||||
|
plain = "0.2.3"
|
||||||
|
redox_liner = "0.5.2"
|
||||||
|
libredox = { path = "../libredox", features = ["mkns"] }
|
||||||
|
redox_termios = "0.1.3"
|
||||||
|
redox_event = "0.4.3"
|
||||||
|
redox-scheme = { path = "../redox-scheme" }
|
||||||
|
redox_syscall = { path = "../syscall" }
|
||||||
|
redox_users = "0.4.6"
|
||||||
|
termion = "4"
|
||||||
|
libc = "0.2"
|
||||||
|
serde = { version = "1.0.203", features = ["derive"] }
|
||||||
|
toml = "0.8.11"
|
||||||
|
ioslice = "0.6"
|
||||||
|
|
||||||
[target.'cfg(loom)'.dev-dependencies]
|
[target.'cfg(target_os = "redox")'.dependencies]
|
||||||
loom = "0.7"
|
redox-rt = { git = "https://gitlab.redox-os.org/redox-os/relibc", default-features = false }
|
||||||
|
|
||||||
|
[patch.crates-io]
|
||||||
|
redox_syscall = { path = "../syscall" }
|
||||||
|
libredox = { path = "../libredox" }
|
||||||
|
redox-scheme = { path = "../redox-scheme" }
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = true
|
||||||
|
|||||||
@@ -1,22 +1,21 @@
|
|||||||
Copyright (c) 2017 Redox OS Developers
|
The MIT License (MIT)
|
||||||
|
|
||||||
MIT License
|
Copyright (c) 2016 The Redox developers
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
a copy of this software and associated documentation files (the
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
"Software"), to deal in the Software without restriction, including
|
in the Software without restriction, including without limitation the rights
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
furnished to do so, subject to the following conditions:
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
The above copyright notice and this permission notice shall be included in all
|
||||||
included in all copies or substantial portions of the Software.
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
SOFTWARE.
|
||||||
|
|||||||
@@ -1,7 +1,24 @@
|
|||||||
# syscall
|
# Redox OS user and group utilities.
|
||||||
|
|
||||||
This crate contains the system call numbers and Rust wrappers for the inline Assembly code of system calls.
|
The `userutils` crate contains the utilities for dealing with users and groups in Redox OS.
|
||||||
|
They are heavily influenced by UNIX and are, when needed, tailored to specific Redox use cases.
|
||||||
|
|
||||||
[](./LICENSE)
|
These implementations strive to be as simple as possible drawing particular
|
||||||
[](https://crates.io/crates/redox_syscall)
|
inspiration by BSD systems. They are indeed small, by choice.
|
||||||
[](https://docs.rs/redox_syscall)
|
|
||||||
|
[](https://travis-ci.org/redox-os/userutils)
|
||||||
|
|
||||||
|
**Currently included:**
|
||||||
|
|
||||||
|
- `getty`: Used by `init(8)` to open and initialize the TTY line, read a login name and invoke `login(1)`.
|
||||||
|
- `id`: Displays user identity.
|
||||||
|
- `login`: Allows users to login into the system
|
||||||
|
- `passwd`: Allows users to modify their passwords.
|
||||||
|
- `su`: Allows users to substitute identity.
|
||||||
|
- `sudo`: Enables users to execute a command as another user.
|
||||||
|
- `useradd`: Add a user
|
||||||
|
- `usermod`: Modify user information
|
||||||
|
- `userdel`: Delete a user
|
||||||
|
- `groupadd`: Add a user group
|
||||||
|
- `groupmod`: Modify group information
|
||||||
|
- `groupdel`: Remove a user group
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
########## Red Bear OS #########
|
||||||
|
# Login with the following: #
|
||||||
|
# `user` #
|
||||||
|
# `root`:`password` #
|
||||||
|
################################
|
||||||
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
########## Redox OS ##########
|
||||||
|
# Login with the following: #
|
||||||
|
# `user` #
|
||||||
|
# `root`:`password` #
|
||||||
|
##############################
|
||||||
|
|
||||||
@@ -1,207 +0,0 @@
|
|||||||
use core::{
|
|
||||||
mem,
|
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
slice,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::error::{Error, Result};
|
|
||||||
|
|
||||||
pub const PAGE_SIZE: usize = 4096;
|
|
||||||
/// Size of the metadata region used to transfer information from the kernel to the bootstrapper.
|
|
||||||
pub const KERNEL_METADATA_SIZE: usize = 4 * PAGE_SIZE;
|
|
||||||
|
|
||||||
#[cfg(feature = "userspace")]
|
|
||||||
macro_rules! syscall {
|
|
||||||
($($name:ident($a:ident, $($b:ident, $($c:ident, $($d:ident, $($e:ident, $($f:ident, $($g:ident, )?)?)?)?)?)?);)+) => {
|
|
||||||
$(
|
|
||||||
pub unsafe fn $name($a: usize, $($b: usize, $($c: usize, $($d: usize, $($e: usize, $($f: usize, $($g: usize)?)?)?)?)?)?) -> Result<usize> {
|
|
||||||
let ret: usize;
|
|
||||||
|
|
||||||
core::arch::asm!(
|
|
||||||
"svc 0",
|
|
||||||
in("x8") $a,
|
|
||||||
$(
|
|
||||||
in("x0") $b,
|
|
||||||
$(
|
|
||||||
in("x1") $c,
|
|
||||||
$(
|
|
||||||
in("x2") $d,
|
|
||||||
$(
|
|
||||||
in("x3") $e,
|
|
||||||
$(
|
|
||||||
in("x4") $f,
|
|
||||||
$(
|
|
||||||
in("x5") $g,
|
|
||||||
)?
|
|
||||||
)?
|
|
||||||
)?
|
|
||||||
)?
|
|
||||||
)?
|
|
||||||
)?
|
|
||||||
lateout("x0") ret,
|
|
||||||
options(nostack),
|
|
||||||
);
|
|
||||||
|
|
||||||
Error::demux(ret)
|
|
||||||
}
|
|
||||||
)+
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "userspace")]
|
|
||||||
syscall! {
|
|
||||||
syscall0(a,);
|
|
||||||
syscall1(a, b,);
|
|
||||||
syscall2(a, b, c,);
|
|
||||||
syscall3(a, b, c, d,);
|
|
||||||
syscall4(a, b, c, d, e,);
|
|
||||||
syscall5(a, b, c, d, e, f,);
|
|
||||||
syscall6(a, b, c, d, e, f, g,);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct IntRegisters {
|
|
||||||
pub x30: usize,
|
|
||||||
pub x29: usize,
|
|
||||||
pub x28: usize,
|
|
||||||
pub x27: usize,
|
|
||||||
pub x26: usize,
|
|
||||||
pub x25: usize,
|
|
||||||
pub x24: usize,
|
|
||||||
pub x23: usize,
|
|
||||||
pub x22: usize,
|
|
||||||
pub x21: usize,
|
|
||||||
pub x20: usize,
|
|
||||||
pub x19: usize,
|
|
||||||
pub x18: usize,
|
|
||||||
pub x17: usize,
|
|
||||||
pub x16: usize,
|
|
||||||
pub x15: usize,
|
|
||||||
pub x14: usize,
|
|
||||||
pub x13: usize,
|
|
||||||
pub x12: usize,
|
|
||||||
pub x11: usize,
|
|
||||||
pub x10: usize,
|
|
||||||
pub x9: usize,
|
|
||||||
pub x8: usize,
|
|
||||||
pub x7: usize,
|
|
||||||
pub x6: usize,
|
|
||||||
pub x5: usize,
|
|
||||||
pub x4: usize,
|
|
||||||
pub x3: usize,
|
|
||||||
pub x2: usize,
|
|
||||||
pub x1: usize,
|
|
||||||
pub x0: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for IntRegisters {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts(
|
|
||||||
self as *const IntRegisters as *const u8,
|
|
||||||
mem::size_of::<IntRegisters>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for IntRegisters {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(
|
|
||||||
self as *mut IntRegisters as *mut u8,
|
|
||||||
mem::size_of::<IntRegisters>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
#[repr(C, packed)]
|
|
||||||
pub struct FloatRegisters {
|
|
||||||
pub fp_simd_regs: [u128; 32],
|
|
||||||
pub fpsr: u32,
|
|
||||||
pub fpcr: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for FloatRegisters {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts(
|
|
||||||
self as *const FloatRegisters as *const u8,
|
|
||||||
mem::size_of::<FloatRegisters>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for FloatRegisters {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(
|
|
||||||
self as *mut FloatRegisters as *mut u8,
|
|
||||||
mem::size_of::<FloatRegisters>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
#[repr(C, packed)]
|
|
||||||
pub struct EnvRegisters {
|
|
||||||
pub tpidr_el0: usize,
|
|
||||||
pub tpidrro_el0: usize,
|
|
||||||
}
|
|
||||||
impl Deref for EnvRegisters {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts(
|
|
||||||
self as *const EnvRegisters as *const u8,
|
|
||||||
mem::size_of::<EnvRegisters>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for EnvRegisters {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(
|
|
||||||
self as *mut EnvRegisters as *mut u8,
|
|
||||||
mem::size_of::<EnvRegisters>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
#[repr(C, packed)]
|
|
||||||
pub struct Exception {
|
|
||||||
pub kind: usize,
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
impl Deref for Exception {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts(
|
|
||||||
self as *const Exception as *const u8,
|
|
||||||
mem::size_of::<Exception>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for Exception {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(
|
|
||||||
self as *mut Exception as *mut u8,
|
|
||||||
mem::size_of::<Exception>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,206 +0,0 @@
|
|||||||
use super::error::{Error, Result};
|
|
||||||
use core::arch::asm;
|
|
||||||
use core::{
|
|
||||||
mem,
|
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
slice,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const PAGE_SIZE: usize = 4096;
|
|
||||||
/// Size of the metadata region used to transfer information from the kernel to the bootstrapper.
|
|
||||||
pub const KERNEL_METADATA_SIZE: usize = 4 * PAGE_SIZE;
|
|
||||||
|
|
||||||
#[cfg(feature = "userspace")]
|
|
||||||
macro_rules! syscall {
|
|
||||||
($($name:ident($a:ident, $($b:ident, $($c:ident, $($d:ident, $($e:ident, $($f:ident, $($g:ident, )?)?)?)?)?)?);)+) => {
|
|
||||||
$(
|
|
||||||
pub unsafe fn $name($a: usize, $($b: usize, $($c: usize, $($d: usize, $($e: usize, $($f: usize, $($g: usize)?)?)?)?)?)?) -> Result<usize> {
|
|
||||||
let ret: usize;
|
|
||||||
|
|
||||||
asm!(
|
|
||||||
"ecall",
|
|
||||||
in("a7") $a,
|
|
||||||
$(
|
|
||||||
in("a0") $b,
|
|
||||||
$(
|
|
||||||
in("a1") $c,
|
|
||||||
$(
|
|
||||||
in("a2") $d,
|
|
||||||
$(
|
|
||||||
in("a3") $e,
|
|
||||||
$(
|
|
||||||
in("a4") $f,
|
|
||||||
$(
|
|
||||||
in("a5") $g,
|
|
||||||
)?
|
|
||||||
)?
|
|
||||||
)?
|
|
||||||
)?
|
|
||||||
)?
|
|
||||||
)?
|
|
||||||
lateout("a0") ret,
|
|
||||||
options(nostack),
|
|
||||||
);
|
|
||||||
|
|
||||||
Error::demux(ret)
|
|
||||||
}
|
|
||||||
)+
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "userspace")]
|
|
||||||
syscall! {
|
|
||||||
syscall0(a,);
|
|
||||||
syscall1(a, b,);
|
|
||||||
syscall2(a, b, c,);
|
|
||||||
syscall3(a, b, c, d,);
|
|
||||||
syscall4(a, b, c, d, e,);
|
|
||||||
syscall5(a, b, c, d, e, f,);
|
|
||||||
syscall6(a, b, c, d, e, f, g,);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct IntRegisters {
|
|
||||||
pub pc: usize,
|
|
||||||
pub x31: usize,
|
|
||||||
pub x30: usize,
|
|
||||||
pub x29: usize,
|
|
||||||
pub x28: usize,
|
|
||||||
pub x27: usize,
|
|
||||||
pub x26: usize,
|
|
||||||
pub x25: usize,
|
|
||||||
pub x24: usize,
|
|
||||||
pub x23: usize,
|
|
||||||
pub x22: usize,
|
|
||||||
pub x21: usize,
|
|
||||||
pub x20: usize,
|
|
||||||
pub x19: usize,
|
|
||||||
pub x18: usize,
|
|
||||||
pub x17: usize,
|
|
||||||
pub x16: usize,
|
|
||||||
pub x15: usize,
|
|
||||||
pub x14: usize,
|
|
||||||
pub x13: usize,
|
|
||||||
pub x12: usize,
|
|
||||||
pub x11: usize,
|
|
||||||
pub x10: usize,
|
|
||||||
pub x9: usize,
|
|
||||||
pub x8: usize,
|
|
||||||
pub x7: usize,
|
|
||||||
pub x6: usize,
|
|
||||||
pub x5: usize,
|
|
||||||
// x4(tp) is in env
|
|
||||||
// x3(gp) is a platform scratch register
|
|
||||||
pub x2: usize,
|
|
||||||
pub x1: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for IntRegisters {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts(
|
|
||||||
self as *const IntRegisters as *const u8,
|
|
||||||
mem::size_of::<IntRegisters>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for IntRegisters {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(
|
|
||||||
self as *mut IntRegisters as *mut u8,
|
|
||||||
mem::size_of::<IntRegisters>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
#[repr(C, packed)]
|
|
||||||
pub struct FloatRegisters {
|
|
||||||
pub fregs: [u64; 32],
|
|
||||||
pub fcsr: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for FloatRegisters {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts(
|
|
||||||
self as *const FloatRegisters as *const u8,
|
|
||||||
mem::size_of::<FloatRegisters>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for FloatRegisters {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(
|
|
||||||
self as *mut FloatRegisters as *mut u8,
|
|
||||||
mem::size_of::<FloatRegisters>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
#[repr(packed)]
|
|
||||||
pub struct EnvRegisters {
|
|
||||||
pub tp: usize,
|
|
||||||
}
|
|
||||||
impl Deref for EnvRegisters {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts(
|
|
||||||
self as *const EnvRegisters as *const u8,
|
|
||||||
mem::size_of::<EnvRegisters>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for EnvRegisters {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(
|
|
||||||
self as *mut EnvRegisters as *mut u8,
|
|
||||||
mem::size_of::<EnvRegisters>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
#[repr(C, packed)]
|
|
||||||
pub struct Exception {
|
|
||||||
pub kind: usize,
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
impl Deref for Exception {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts(
|
|
||||||
self as *const Exception as *const u8,
|
|
||||||
mem::size_of::<Exception>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for Exception {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(
|
|
||||||
self as *mut Exception as *mut u8,
|
|
||||||
mem::size_of::<Exception>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-288
@@ -1,288 +0,0 @@
|
|||||||
use core::{
|
|
||||||
arch::asm,
|
|
||||||
mem,
|
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
slice,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::error::{Error, Result};
|
|
||||||
|
|
||||||
pub const PAGE_SIZE: usize = 4096;
|
|
||||||
/// Size of the metadata region used to transfer information from the kernel to the bootstrapper.
|
|
||||||
pub const KERNEL_METADATA_SIZE: usize = 4 * PAGE_SIZE;
|
|
||||||
|
|
||||||
#[cfg(feature = "userspace")]
|
|
||||||
macro_rules! syscall {
|
|
||||||
($($name:ident($a:ident, $($b:ident, $($c:ident, $($d:ident, $($e:ident, $($f:ident, )?)?)?)?)?);)+) => {
|
|
||||||
$(
|
|
||||||
pub unsafe fn $name(mut $a: usize, $($b: usize, $($c: usize, $($d: usize, $($e: usize, $($f: usize)?)?)?)?)?) -> Result<usize> {
|
|
||||||
asm!(
|
|
||||||
"int 0x80",
|
|
||||||
inout("eax") $a,
|
|
||||||
$(
|
|
||||||
in("ebx") $b,
|
|
||||||
$(
|
|
||||||
in("ecx") $c,
|
|
||||||
$(
|
|
||||||
in("edx") $d,
|
|
||||||
$(
|
|
||||||
in("esi") $e,
|
|
||||||
$(
|
|
||||||
in("edi") $f,
|
|
||||||
)?
|
|
||||||
)?
|
|
||||||
)?
|
|
||||||
)?
|
|
||||||
)?
|
|
||||||
options(nostack),
|
|
||||||
);
|
|
||||||
|
|
||||||
Error::demux($a)
|
|
||||||
}
|
|
||||||
)+
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "userspace")]
|
|
||||||
syscall! {
|
|
||||||
syscall0(a,);
|
|
||||||
syscall1(a, b,);
|
|
||||||
syscall2(a, b, c,);
|
|
||||||
syscall3(a, b, c, d,);
|
|
||||||
// Must be done custom because LLVM reserves ESI
|
|
||||||
//syscall4(a, b, c, d, e,);
|
|
||||||
//syscall5(a, b, c, d, e, f,);
|
|
||||||
//syscall6(a, b, c, d, e, f, g,);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "userspace")]
|
|
||||||
pub unsafe fn syscall4(mut a: usize, b: usize, c: usize, d: usize, e: usize) -> Result<usize> {
|
|
||||||
asm!(
|
|
||||||
"xchg esi, {e}
|
|
||||||
int 0x80
|
|
||||||
xchg esi, {e}",
|
|
||||||
e = in(reg) e,
|
|
||||||
inout("eax") a,
|
|
||||||
in("ebx") b,
|
|
||||||
in("ecx") c,
|
|
||||||
in("edx") d,
|
|
||||||
options(nostack),
|
|
||||||
);
|
|
||||||
|
|
||||||
Error::demux(a)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "userspace")]
|
|
||||||
pub unsafe fn syscall5(
|
|
||||||
mut a: usize,
|
|
||||||
b: usize,
|
|
||||||
c: usize,
|
|
||||||
d: usize,
|
|
||||||
e: usize,
|
|
||||||
f: usize,
|
|
||||||
) -> Result<usize> {
|
|
||||||
asm!(
|
|
||||||
"xchg esi, {e}
|
|
||||||
int 0x80
|
|
||||||
xchg esi, {e}",
|
|
||||||
e = in(reg) e,
|
|
||||||
inout("eax") a,
|
|
||||||
in("ebx") b,
|
|
||||||
in("ecx") c,
|
|
||||||
in("edx") d,
|
|
||||||
in("edi") f,
|
|
||||||
options(nostack),
|
|
||||||
);
|
|
||||||
|
|
||||||
Error::demux(a)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "userspace")]
|
|
||||||
pub unsafe fn syscall6(
|
|
||||||
mut a: usize,
|
|
||||||
b: usize,
|
|
||||||
c: usize,
|
|
||||||
d: usize,
|
|
||||||
e: usize,
|
|
||||||
f: usize,
|
|
||||||
g: usize,
|
|
||||||
) -> Result<usize> {
|
|
||||||
#[repr(C)]
|
|
||||||
struct PackedArgs {
|
|
||||||
arg4: usize,
|
|
||||||
arg6: usize,
|
|
||||||
nr: usize,
|
|
||||||
}
|
|
||||||
let args = PackedArgs {
|
|
||||||
arg4: e,
|
|
||||||
arg6: g,
|
|
||||||
nr: a,
|
|
||||||
};
|
|
||||||
let args_ptr = &args as *const PackedArgs;
|
|
||||||
asm!(
|
|
||||||
"push ebp",
|
|
||||||
"push esi",
|
|
||||||
"mov esi, [eax + 0]", // arg4 -> esi
|
|
||||||
"mov ebp, [eax + 4]", // arg6 -> ebp
|
|
||||||
"mov eax, [eax + 8]", // nr -> eax
|
|
||||||
"int 0x80",
|
|
||||||
"pop esi",
|
|
||||||
"pop ebp",
|
|
||||||
inout("eax") args_ptr => a,
|
|
||||||
in("ebx") b,
|
|
||||||
in("ecx") c,
|
|
||||||
in("edx") d,
|
|
||||||
in("edi") f,
|
|
||||||
options(nostack),
|
|
||||||
);
|
|
||||||
|
|
||||||
Error::demux(a)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct IntRegisters {
|
|
||||||
// TODO: Some of these don't get set by Redox yet. Should they?
|
|
||||||
pub ebp: usize,
|
|
||||||
pub esi: usize,
|
|
||||||
pub edi: usize,
|
|
||||||
pub ebx: usize,
|
|
||||||
pub eax: usize,
|
|
||||||
pub ecx: usize,
|
|
||||||
pub edx: usize,
|
|
||||||
// pub orig_rax: usize,
|
|
||||||
pub eip: usize,
|
|
||||||
pub cs: usize,
|
|
||||||
pub eflags: usize,
|
|
||||||
pub esp: usize,
|
|
||||||
pub ss: usize,
|
|
||||||
// pub fs_base: usize,
|
|
||||||
// pub gs_base: usize,
|
|
||||||
// pub ds: usize,
|
|
||||||
// pub es: usize,
|
|
||||||
pub fs: usize,
|
|
||||||
// pub gs: usize
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for IntRegisters {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts(
|
|
||||||
self as *const IntRegisters as *const u8,
|
|
||||||
mem::size_of::<IntRegisters>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for IntRegisters {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(
|
|
||||||
self as *mut IntRegisters as *mut u8,
|
|
||||||
mem::size_of::<IntRegisters>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
#[repr(C, packed)]
|
|
||||||
pub struct FloatRegisters {
|
|
||||||
pub fcw: u16,
|
|
||||||
pub fsw: u16,
|
|
||||||
pub ftw: u8,
|
|
||||||
pub _reserved: u8,
|
|
||||||
pub fop: u16,
|
|
||||||
pub fip: u64,
|
|
||||||
pub fdp: u64,
|
|
||||||
pub mxcsr: u32,
|
|
||||||
pub mxcsr_mask: u32,
|
|
||||||
pub st_space: [u128; 8],
|
|
||||||
pub xmm_space: [u128; 16],
|
|
||||||
// TODO: YMM/ZMM
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for FloatRegisters {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts(
|
|
||||||
self as *const FloatRegisters as *const u8,
|
|
||||||
mem::size_of::<FloatRegisters>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for FloatRegisters {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(
|
|
||||||
self as *mut FloatRegisters as *mut u8,
|
|
||||||
mem::size_of::<FloatRegisters>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
#[repr(C, packed)]
|
|
||||||
pub struct EnvRegisters {
|
|
||||||
pub fsbase: u32,
|
|
||||||
pub gsbase: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for EnvRegisters {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts(
|
|
||||||
self as *const EnvRegisters as *const u8,
|
|
||||||
mem::size_of::<EnvRegisters>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for EnvRegisters {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(
|
|
||||||
self as *mut EnvRegisters as *mut u8,
|
|
||||||
mem::size_of::<EnvRegisters>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
#[repr(C, packed)]
|
|
||||||
pub struct Exception {
|
|
||||||
pub kind: usize,
|
|
||||||
pub code: usize,
|
|
||||||
pub address: usize,
|
|
||||||
}
|
|
||||||
impl Deref for Exception {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts(
|
|
||||||
self as *const Exception as *const u8,
|
|
||||||
mem::size_of::<Exception>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for Exception {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(
|
|
||||||
self as *mut Exception as *mut u8,
|
|
||||||
mem::size_of::<Exception>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,194 +0,0 @@
|
|||||||
use core::{
|
|
||||||
mem,
|
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
slice,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const PAGE_SIZE: usize = 4096;
|
|
||||||
/// Size of the metadata region used to transfer information from the kernel to the bootstrapper.
|
|
||||||
pub const KERNEL_METADATA_SIZE: usize = 4 * PAGE_SIZE;
|
|
||||||
|
|
||||||
#[cfg(feature = "userspace")]
|
|
||||||
macro_rules! syscall {
|
|
||||||
($($name:ident($a:ident, $($b:ident, $($c:ident, $($d:ident, $($e:ident, $($f:ident, $($g:ident, )?)?)?)?)?)?);)+) => {
|
|
||||||
$(
|
|
||||||
pub unsafe fn $name(mut $a: usize, $($b: usize, $($c: usize, $($d: usize, $($e: usize, $($f: usize, $($g: usize)?)?)?)?)?)?) -> crate::error::Result<usize> {
|
|
||||||
core::arch::asm!(
|
|
||||||
"syscall",
|
|
||||||
inout("rax") $a,
|
|
||||||
$(
|
|
||||||
in("rdi") $b,
|
|
||||||
$(
|
|
||||||
in("rsi") $c,
|
|
||||||
$(
|
|
||||||
in("rdx") $d,
|
|
||||||
$(
|
|
||||||
in("r10") $e,
|
|
||||||
$(
|
|
||||||
in("r8") $f,
|
|
||||||
$(
|
|
||||||
in("r9") $g,
|
|
||||||
)?
|
|
||||||
)?
|
|
||||||
)?
|
|
||||||
)?
|
|
||||||
)?
|
|
||||||
)?
|
|
||||||
out("rcx") _,
|
|
||||||
out("r11") _,
|
|
||||||
options(nostack),
|
|
||||||
);
|
|
||||||
|
|
||||||
crate::error::Error::demux($a)
|
|
||||||
}
|
|
||||||
)+
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "userspace")]
|
|
||||||
syscall! {
|
|
||||||
syscall0(a,);
|
|
||||||
syscall1(a, b,);
|
|
||||||
syscall2(a, b, c,);
|
|
||||||
syscall3(a, b, c, d,);
|
|
||||||
syscall4(a, b, c, d, e,);
|
|
||||||
syscall5(a, b, c, d, e, f,);
|
|
||||||
syscall6(a, b, c, d, e, f, g,);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct IntRegisters {
|
|
||||||
pub r15: usize,
|
|
||||||
pub r14: usize,
|
|
||||||
pub r13: usize,
|
|
||||||
pub r12: usize,
|
|
||||||
pub rbp: usize,
|
|
||||||
pub rbx: usize,
|
|
||||||
pub r11: usize,
|
|
||||||
pub r10: usize,
|
|
||||||
pub r9: usize,
|
|
||||||
pub r8: usize,
|
|
||||||
pub rax: usize,
|
|
||||||
pub rcx: usize,
|
|
||||||
pub rdx: usize,
|
|
||||||
pub rsi: usize,
|
|
||||||
pub rdi: usize,
|
|
||||||
pub rip: usize,
|
|
||||||
pub cs: usize,
|
|
||||||
pub rflags: usize,
|
|
||||||
pub rsp: usize,
|
|
||||||
pub ss: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for IntRegisters {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe { slice::from_raw_parts(self as *const Self as *const u8, mem::size_of::<Self>()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for IntRegisters {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe { slice::from_raw_parts_mut(self as *mut Self as *mut u8, mem::size_of::<Self>()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
#[repr(C, packed)]
|
|
||||||
pub struct FloatRegisters {
|
|
||||||
pub fcw: u16,
|
|
||||||
pub fsw: u16,
|
|
||||||
pub ftw: u8,
|
|
||||||
pub _reserved: u8,
|
|
||||||
pub fop: u16,
|
|
||||||
pub fip: u64,
|
|
||||||
pub fdp: u64,
|
|
||||||
pub mxcsr: u32,
|
|
||||||
pub mxcsr_mask: u32,
|
|
||||||
pub st_space: [u128; 8],
|
|
||||||
pub xmm_space: [u128; 16],
|
|
||||||
// TODO: YMM/ZMM
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for FloatRegisters {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts(
|
|
||||||
self as *const FloatRegisters as *const u8,
|
|
||||||
mem::size_of::<FloatRegisters>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for FloatRegisters {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(
|
|
||||||
self as *mut FloatRegisters as *mut u8,
|
|
||||||
mem::size_of::<FloatRegisters>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
#[repr(C, packed)]
|
|
||||||
pub struct EnvRegisters {
|
|
||||||
pub fsbase: u64,
|
|
||||||
pub gsbase: u64,
|
|
||||||
// TODO: PKRU?
|
|
||||||
}
|
|
||||||
impl Deref for EnvRegisters {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts(
|
|
||||||
self as *const EnvRegisters as *const u8,
|
|
||||||
mem::size_of::<EnvRegisters>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for EnvRegisters {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(
|
|
||||||
self as *mut EnvRegisters as *mut u8,
|
|
||||||
mem::size_of::<EnvRegisters>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
#[repr(C, packed)]
|
|
||||||
pub struct Exception {
|
|
||||||
pub kind: usize,
|
|
||||||
pub code: usize,
|
|
||||||
pub address: usize,
|
|
||||||
}
|
|
||||||
impl Deref for Exception {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts(
|
|
||||||
self as *const Exception as *const u8,
|
|
||||||
mem::size_of::<Exception>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for Exception {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(
|
|
||||||
self as *mut Exception as *mut u8,
|
|
||||||
mem::size_of::<Exception>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,285 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate clap;
|
||||||
|
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{self, ErrorKind, Read, Stderr, Write};
|
||||||
|
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||||
|
use std::process::{Child, Command, Stdio};
|
||||||
|
use std::str;
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
use event::{EventFlags, RawEventQueue};
|
||||||
|
use extra::io::fail;
|
||||||
|
use libredox::call as redox;
|
||||||
|
use libredox::errno::EAGAIN;
|
||||||
|
use libredox::flag;
|
||||||
|
|
||||||
|
const _MAN_PAGE: &'static str = /* @MANSTART{getty} */
|
||||||
|
r#"
|
||||||
|
NAME
|
||||||
|
getty - set terminal mode
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
getty [-J | --noclear | -C | --contain ] tty
|
||||||
|
getty [ -h | --help ]
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
The getty utility is called by init(8) to open and initialize the tty line,
|
||||||
|
read a login name, and invoke login(1).
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
|
||||||
|
-h, --help
|
||||||
|
Display this help and exit.
|
||||||
|
|
||||||
|
-J, --noclear
|
||||||
|
Do not clear the screen before forking login(1).
|
||||||
|
|
||||||
|
-C, --contain
|
||||||
|
Run contain_login instead of login
|
||||||
|
|
||||||
|
AUTHOR
|
||||||
|
Written by Jeremy Soller.
|
||||||
|
"#; /* @MANEND */
|
||||||
|
|
||||||
|
const DEFAULT_COLS: u16 = 80;
|
||||||
|
const DEFAULT_LINES: u16 = 30;
|
||||||
|
|
||||||
|
pub fn handle(
|
||||||
|
event_queue: &mut RawEventQueue,
|
||||||
|
tty_fd: RawFd,
|
||||||
|
master_fd: RawFd,
|
||||||
|
process: &mut Child,
|
||||||
|
) {
|
||||||
|
// tty_fd => Display
|
||||||
|
// master_fd => PTY
|
||||||
|
|
||||||
|
let handle_event = |event_id: usize| {
|
||||||
|
if event_id as RawFd == tty_fd {
|
||||||
|
let mut packet = [0; 4096];
|
||||||
|
loop {
|
||||||
|
let count = match redox::read(tty_fd as usize, &mut packet) {
|
||||||
|
Ok(0) => return,
|
||||||
|
Ok(count) => count,
|
||||||
|
Err(ref err) if err.errno() == EAGAIN => break,
|
||||||
|
Err(_) => panic!("getty: failed to read from TTY"),
|
||||||
|
};
|
||||||
|
redox::write(master_fd as usize, &packet[..count])
|
||||||
|
.expect("getty: failed to write master PTY");
|
||||||
|
}
|
||||||
|
} else if event_id as RawFd == master_fd {
|
||||||
|
let mut packet = [0; 4096];
|
||||||
|
loop {
|
||||||
|
let count = match redox::read(master_fd as usize, &mut packet) {
|
||||||
|
Ok(0) => return,
|
||||||
|
Ok(count) => count,
|
||||||
|
Err(ref err) if err.errno() == EAGAIN => break,
|
||||||
|
Err(_) => panic!("getty: failed to read from master TTY"),
|
||||||
|
};
|
||||||
|
redox::write(tty_fd as usize, &packet[1..count])
|
||||||
|
.expect("getty: failed to write to TTY");
|
||||||
|
if packet[0] & 1 == 1 {
|
||||||
|
let _ = redox::fsync(tty_fd as usize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
handle_event(tty_fd as usize);
|
||||||
|
handle_event(master_fd as usize);
|
||||||
|
|
||||||
|
'events: loop {
|
||||||
|
let sys_event = event_queue
|
||||||
|
.next()
|
||||||
|
.expect("getty: event queue stopped")
|
||||||
|
.expect("getty: failed to read event file");
|
||||||
|
handle_event(sys_event.fd);
|
||||||
|
|
||||||
|
match process.try_wait() {
|
||||||
|
Ok(status) => match status {
|
||||||
|
Some(_code) => break 'events,
|
||||||
|
None => (),
|
||||||
|
},
|
||||||
|
Err(err) => match err.kind() {
|
||||||
|
ErrorKind::WouldBlock => (),
|
||||||
|
_ => panic!("getty: failed to wait on child: {:?}", err),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = process.kill();
|
||||||
|
process.wait().expect("getty: failed to wait on login");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getpty(columns: u16, lines: u16) -> (RawFd, String) {
|
||||||
|
let master = redox::open(
|
||||||
|
"/scheme/pty",
|
||||||
|
flag::O_CLOEXEC | flag::O_RDWR | flag::O_CREAT | flag::O_NONBLOCK,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.expect("getty: failed to create PTY");
|
||||||
|
|
||||||
|
if let Ok(winsize_fd) = redox::dup(master, b"winsize") {
|
||||||
|
let _ = redox::write(
|
||||||
|
winsize_fd,
|
||||||
|
&redox_termios::Winsize {
|
||||||
|
ws_row: lines,
|
||||||
|
ws_col: columns,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let _ = redox::close(winsize_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buf: [u8; 4096] = [0; 4096];
|
||||||
|
let count = redox::fpath(master, &mut buf).unwrap();
|
||||||
|
(master as RawFd, unsafe {
|
||||||
|
String::from_utf8_unchecked(Vec::from(&buf[..count]))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// termion cursor_pos prone to error and does not work on nonblocking files
|
||||||
|
fn tty_cursor_pos(tty: &mut File) -> Result<(u16, u16), Box<dyn Error>> {
|
||||||
|
write!(tty, "\x1B[6n")?;
|
||||||
|
tty.flush()?;
|
||||||
|
|
||||||
|
let timeout = Duration::from_millis(500);
|
||||||
|
let instant = Instant::now();
|
||||||
|
let mut data = String::new();
|
||||||
|
while instant.elapsed() < timeout {
|
||||||
|
let mut bytes = [0];
|
||||||
|
match tty.read(&mut bytes) {
|
||||||
|
Ok(count) => if count == 1 {
|
||||||
|
let c = bytes[0] as char;
|
||||||
|
if c == 'R' {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
data.push(c);
|
||||||
|
},
|
||||||
|
Err(err) => if err.kind() != ErrorKind::WouldBlock {
|
||||||
|
return Err(err.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.is_empty() {
|
||||||
|
return Err("cursor position timed out".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let beg = data.rfind('[').ok_or("failed to find [")?;
|
||||||
|
let coords: String = data.chars().skip(beg + 1).collect();
|
||||||
|
let mut nums = coords.split(';');
|
||||||
|
|
||||||
|
let row = nums.next().ok_or("failed to find row")?.parse::<u16>()?;
|
||||||
|
let col = nums.next().ok_or("failed to find col")?.parse::<u16>()?;
|
||||||
|
|
||||||
|
Ok((col, row))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tty_columns_lines(tty: &mut File) -> Result<(u16, u16), Box<dyn Error>> {
|
||||||
|
write!(tty, "{}", termion::cursor::Save)?;
|
||||||
|
tty.flush()?;
|
||||||
|
|
||||||
|
write!(tty, "{}", termion::cursor::Goto(999, 999))?;
|
||||||
|
tty.flush()?;
|
||||||
|
|
||||||
|
let res = tty_cursor_pos(tty);
|
||||||
|
|
||||||
|
write!(tty, "{}", termion::cursor::Restore)?;
|
||||||
|
tty.flush()?;
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn daemon(tty: &mut File, clear: bool, contain: bool, stderr: &mut Stderr) {
|
||||||
|
let (columns, lines) = tty_columns_lines(tty).unwrap_or((DEFAULT_COLS, DEFAULT_LINES));
|
||||||
|
let tty_fd = tty.as_raw_fd();
|
||||||
|
|
||||||
|
let (master_fd, pty) = getpty(columns, lines);
|
||||||
|
|
||||||
|
let mut event_queue = event::RawEventQueue::new().expect("getty: failed to open event queue");
|
||||||
|
|
||||||
|
event_queue
|
||||||
|
.subscribe(tty_fd as usize, 0, EventFlags::READ)
|
||||||
|
.expect("getty: failed to fevent TTY");
|
||||||
|
|
||||||
|
event_queue
|
||||||
|
.subscribe(master_fd as usize, 0, EventFlags::READ)
|
||||||
|
.expect("getty: failed to fevent master PTY");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if clear {
|
||||||
|
let _ = redox::write(tty_fd as usize, b"\x1Bc");
|
||||||
|
}
|
||||||
|
let _ = redox::fsync(tty_fd as usize);
|
||||||
|
|
||||||
|
let slave_stdin = redox::open(&pty, flag::O_CLOEXEC | flag::O_RDONLY, 0)
|
||||||
|
.expect("getty: failed to open slave stdin");
|
||||||
|
let slave_stdout = redox::open(&pty, flag::O_CLOEXEC | flag::O_WRONLY, 0)
|
||||||
|
.expect("getty: failed to open slave stdout");
|
||||||
|
let slave_stderr = redox::open(&pty, flag::O_CLOEXEC | flag::O_WRONLY, 0)
|
||||||
|
.expect("getty: failed to open slave stderr");
|
||||||
|
|
||||||
|
let mut command = if contain {
|
||||||
|
Command::new("contain_login")
|
||||||
|
} else {
|
||||||
|
Command::new("login")
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
command
|
||||||
|
.stdin(Stdio::from_raw_fd(slave_stdin as RawFd))
|
||||||
|
.stdout(Stdio::from_raw_fd(slave_stdout as RawFd))
|
||||||
|
.stderr(Stdio::from_raw_fd(slave_stderr as RawFd))
|
||||||
|
.env("TERM", "xterm-256color")
|
||||||
|
.env("TTY", &pty);
|
||||||
|
}
|
||||||
|
|
||||||
|
match command.spawn() {
|
||||||
|
Ok(mut process) => {
|
||||||
|
handle(&mut event_queue, tty_fd, master_fd, &mut process);
|
||||||
|
}
|
||||||
|
Err(err) => fail(&format!("getty: failed to execute login: {}", err), stderr),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
let mut stderr = io::stderr();
|
||||||
|
|
||||||
|
let args = clap_app!(getty =>
|
||||||
|
(author: "Jeremy Soller")
|
||||||
|
(about: "Set terminal mode")
|
||||||
|
(@arg TTY: +required "")
|
||||||
|
(@arg NO_CLEAR: -J --("no-clear") "Do not clear the screen before forking")
|
||||||
|
(@arg CONTAIN: -C --("contain") "Run contain_login instead of login")
|
||||||
|
)
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
|
let clear = !args.is_present("NO_CLEAR");
|
||||||
|
|
||||||
|
let contain = args.is_present("CONTAIN");
|
||||||
|
|
||||||
|
let vt = args.value_of("TTY").unwrap();
|
||||||
|
|
||||||
|
let buf: String;
|
||||||
|
let vt_path = if vt.parse::<usize>().is_ok() {
|
||||||
|
buf = format!("/scheme/fbcon/{vt}");
|
||||||
|
&*buf
|
||||||
|
} else {
|
||||||
|
vt
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut tty = match redox::open(
|
||||||
|
&vt_path,
|
||||||
|
flag::O_CLOEXEC | flag::O_RDWR | flag::O_NONBLOCK,
|
||||||
|
0,
|
||||||
|
) {
|
||||||
|
Ok(fd) => unsafe { File::from_raw_fd(fd as RawFd) },
|
||||||
|
Err(err) => fail(
|
||||||
|
&format!("getty: failed to open TTY {}: {}", vt_path, err),
|
||||||
|
&mut stderr,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
daemon(&mut tty, clear, contain, &mut stderr);
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate clap;
|
||||||
|
|
||||||
|
use extra::option::OptionalExt;
|
||||||
|
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
|
use redox_users::{All, AllGroups, Config, Error, GroupBuilder};
|
||||||
|
|
||||||
|
const _MAN_PAGE: &'static str = /* @MANSTART{groupadd} */
|
||||||
|
r#"
|
||||||
|
NAME
|
||||||
|
groupadd - add a user group
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
groupadd [ -f | --force ] GROUP
|
||||||
|
groupadd [ -h | --help ]
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
The groupadd utility adds a new user group using values
|
||||||
|
passed on the command line and system defaults.
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
-f, --force
|
||||||
|
Simply forces the exit status of the program to 0
|
||||||
|
even if the group already exists. A message is still
|
||||||
|
printed to stdout.
|
||||||
|
|
||||||
|
-g, --gid GID
|
||||||
|
The group id to use. This value must not be used and must
|
||||||
|
be non-negative. The default is to pick the smallest available
|
||||||
|
group id (between values defined in redox_users).
|
||||||
|
|
||||||
|
-h, --help
|
||||||
|
Display this help and exit.
|
||||||
|
|
||||||
|
AUTHOR
|
||||||
|
Written by Wesley Hershberger.
|
||||||
|
"#; /* @MANEND */
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args = clap_app!(groupadd =>
|
||||||
|
(author: "Wesley Hershberger")
|
||||||
|
(about: "Add groups based on the system's redox_users backend")
|
||||||
|
(@arg GROUP: +required "Add group GROUP")
|
||||||
|
(@arg FORCE: -f --force "Force the status of the program to be 0 even if the group exists")
|
||||||
|
(@arg GID: -g --gid +takes_value "Group id. Positive integer and must not be in use")
|
||||||
|
)
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
|
let mut sys_groups = AllGroups::new(Config::default().writeable(true)).unwrap_or_exit(1);
|
||||||
|
|
||||||
|
let groupname = args.value_of("GROUP").unwrap();
|
||||||
|
|
||||||
|
let gid = match args.value_of("GID") {
|
||||||
|
Some(gid) => {
|
||||||
|
let id = gid.parse::<usize>().unwrap_or_exit(1);
|
||||||
|
if let Some(_group) = sys_groups.get_by_id(id) {
|
||||||
|
eprintln!("groupadd: group already exists");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
id
|
||||||
|
}
|
||||||
|
None => sys_groups.get_unique_id().unwrap_or_else(|| {
|
||||||
|
eprintln!("groupadd: no available gid");
|
||||||
|
exit(1);
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
let group = GroupBuilder::new(groupname).gid(gid);
|
||||||
|
match sys_groups.add_group(group) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(Error::GroupAlreadyExists) if args.is_present("FORCE") => {
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("groupadd: {}", err);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sys_groups.save().unwrap_or_exit(1);
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate clap;
|
||||||
|
|
||||||
|
use extra::option::OptionalExt;
|
||||||
|
use redox_users::{All, AllGroups, Config};
|
||||||
|
|
||||||
|
const _MAN_PAGE: &'static str = /* @MANSTART{groupdel} */
|
||||||
|
r#"
|
||||||
|
NAME
|
||||||
|
groupdel - modify system files to delete groups
|
||||||
|
|
||||||
|
SYNOPSYS
|
||||||
|
groupdel [ options ] GROUP
|
||||||
|
groupdel [ -h | --help ]
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
groupdel removes groups from whatever backend is employed by
|
||||||
|
the system's redox_users.
|
||||||
|
|
||||||
|
Note that you should not remove a primary user group before
|
||||||
|
removing the user. It is also generally wise not to remove
|
||||||
|
groups that still own files on the system.
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
-h, --help
|
||||||
|
Print this help page and exit.
|
||||||
|
|
||||||
|
AUTHORS
|
||||||
|
Wesley Hershberger.
|
||||||
|
"#; /* @MANEND */
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let matches = clap_app!(groupdel =>
|
||||||
|
(author: "Wesley Hershberger")
|
||||||
|
(about: "Removes a group from the system using redox_users")
|
||||||
|
(@arg GROUP: +required "Removes group GROUP")
|
||||||
|
)
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
|
let group = matches.value_of("GROUP").unwrap();
|
||||||
|
|
||||||
|
let mut sys_groups = AllGroups::new(Config::default().writeable(true)).unwrap_or_exit(1);
|
||||||
|
|
||||||
|
sys_groups.remove_by_name(group.to_string());
|
||||||
|
|
||||||
|
sys_groups.save().unwrap_or_exit(1);
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate clap;
|
||||||
|
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
|
use extra::option::OptionalExt;
|
||||||
|
use redox_users::{All, AllGroups, AllUsers, Config};
|
||||||
|
|
||||||
|
const _MAN_PAGE: &'static str = /* @MANSTART{groupmod} */
|
||||||
|
r#"
|
||||||
|
NAME
|
||||||
|
groupmod - modify group information
|
||||||
|
|
||||||
|
SYNOPSYS
|
||||||
|
groupmod [ options ] GROUP
|
||||||
|
groupmod [ -h | --help ]
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
groupmod modifies a user group GROUP in the system's
|
||||||
|
redox_users backend.
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
-h, --help
|
||||||
|
Print this help page and exit.
|
||||||
|
|
||||||
|
-g, --gid GID
|
||||||
|
Change GROUP's group id. GID must be a non-negative
|
||||||
|
decimal integer.
|
||||||
|
|
||||||
|
Files with GROUP's old gid will not be updated.
|
||||||
|
|
||||||
|
User's who use the old gid as their primary gid will
|
||||||
|
be updated.
|
||||||
|
|
||||||
|
-n, --name NAME
|
||||||
|
The name of the group will be set to NAME
|
||||||
|
|
||||||
|
AUTHORS
|
||||||
|
Wesley Hershberger.
|
||||||
|
"#; /* @MANEND */
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args = clap_app!(groupmod =>
|
||||||
|
(author: "Wesley Hershberger")
|
||||||
|
(about: "Modify users according to the system's redox_users backend")
|
||||||
|
(@arg GROUP: +required "Modify GROUP")
|
||||||
|
(@arg GID: -g --gid +takes_value "Change GROUP's group id. See man page for details")
|
||||||
|
(@arg NAME: -n --name +takes_value "Change GROUP's name")
|
||||||
|
)
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
|
let groupname = args.value_of("GROUP").unwrap();
|
||||||
|
|
||||||
|
let mut sys_groups = AllGroups::new(Config::default().writeable(true)).unwrap_or_exit(1);
|
||||||
|
{
|
||||||
|
let group = sys_groups.get_mut_by_name(groupname).unwrap_or_else(|| {
|
||||||
|
eprintln!("groupmod: group not found: {}", groupname);
|
||||||
|
exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(gid) = args.value_of("GID") {
|
||||||
|
let gid = gid.parse::<usize>().unwrap_or_exit(1);
|
||||||
|
// Update users
|
||||||
|
let mut sys_users =
|
||||||
|
AllUsers::authenticator(Config::default().writeable(true)).unwrap_or_exit(1);
|
||||||
|
for user in sys_users.iter_mut() {
|
||||||
|
if user.gid == group.gid {
|
||||||
|
user.gid = gid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sys_users.save().unwrap_or_exit(1);
|
||||||
|
group.gid = gid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(name) = args.value_of("NAME") {
|
||||||
|
group.group = name.to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sys_groups.save().unwrap_or_exit(1);
|
||||||
|
}
|
||||||
+163
@@ -0,0 +1,163 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate clap;
|
||||||
|
|
||||||
|
use std::env::args;
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
|
use extra::option::OptionalExt;
|
||||||
|
use redox_users::{All, AllGroups, AllUsers, Config, get_egid, get_euid, get_gid, get_uid};
|
||||||
|
|
||||||
|
const _MAN_PAGE: &'static str = /* @MANSTART{id} */
|
||||||
|
r#"
|
||||||
|
NAME
|
||||||
|
id - display user identity
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
id
|
||||||
|
id -g [-nr]
|
||||||
|
id -u [-nr]
|
||||||
|
id [ -h | --help ]
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
The id utility displays the user and group names and numeric IDs, of
|
||||||
|
the calling process, to the standard output.
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
-G, --groups
|
||||||
|
Display the different group IDs (effective and real) as white-space
|
||||||
|
separated numbers, in no particular order.
|
||||||
|
|
||||||
|
-g, --group
|
||||||
|
Display the effective group ID as a number.
|
||||||
|
|
||||||
|
-n, --name
|
||||||
|
Display the name of the user or group ID for the -g and -u options
|
||||||
|
instead of the number.
|
||||||
|
|
||||||
|
-u, --user
|
||||||
|
Display the effective user ID as a number.
|
||||||
|
|
||||||
|
-a
|
||||||
|
Ignored for compatibility with other id implementations.
|
||||||
|
|
||||||
|
-r, --real
|
||||||
|
Display the real ID for the -g and -u options instead of the effective ID.
|
||||||
|
|
||||||
|
-h, --help
|
||||||
|
Display help and exit.
|
||||||
|
|
||||||
|
AUTHOR
|
||||||
|
Written by Jose Narvaez.
|
||||||
|
"#; /* @MANEND */
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
let app = clap_app!(id =>
|
||||||
|
(author: "Jose Narvaez")
|
||||||
|
(about: "Get user and group information about the current user")
|
||||||
|
(@arg IGNORE: -a "Ignored for compatibility with other impls of id")
|
||||||
|
(@arg GROUPS: -G --groups conflicts_with[selector modifier] "Display current user's real and effective group id's")
|
||||||
|
(@group selector =>
|
||||||
|
(@arg GROUP: -g --group "Display current user's effective group id")
|
||||||
|
(@arg USER: -u --user "Display the effective userid")
|
||||||
|
)
|
||||||
|
(@group modifier =>
|
||||||
|
(@arg NAME: -n --name requires[selector] "Display names of groups/users instead of ids (use with -g or -u)")
|
||||||
|
(@arg REAL: -r --real requires[selector] "Display real id's instead of effective ids (use with -g and -u)")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
let args = match &*args().nth(0).unwrap_or(String::new()) {
|
||||||
|
"whoami" => app.get_matches_from(["id", "-un"].iter()),
|
||||||
|
_ => app.get_matches(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Display the different group IDs (effective and real)
|
||||||
|
// as white-space separated numbers, in no particular order.
|
||||||
|
if args.is_present("GROUPS") {
|
||||||
|
let egid = get_egid().unwrap_or_exit(1);
|
||||||
|
|
||||||
|
let gid = get_gid().unwrap_or_exit(1);
|
||||||
|
|
||||||
|
println!("{} {}", egid, gid);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display effective/real process user ID UNIX user name
|
||||||
|
if args.is_present("USER") && args.is_present("NAME") {
|
||||||
|
// Did they pass -r? If so, we show the real
|
||||||
|
let uid = if args.is_present("REAL") {
|
||||||
|
get_uid()
|
||||||
|
} else {
|
||||||
|
get_euid()
|
||||||
|
}
|
||||||
|
.unwrap_or_exit(1);
|
||||||
|
|
||||||
|
let users = AllUsers::basic(Config::default()).unwrap_or_exit(1);
|
||||||
|
let user = users.get_by_id(uid).unwrap_or_exit(1);
|
||||||
|
|
||||||
|
println!("{}", user.user);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display real user ID
|
||||||
|
if args.is_present("USER") && args.is_present("REAL") {
|
||||||
|
let uid = get_uid().unwrap_or_exit(1);
|
||||||
|
|
||||||
|
println!("{}", uid);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display effective user ID
|
||||||
|
if args.is_present("USER") {
|
||||||
|
let euid = get_euid().unwrap_or_exit(1);
|
||||||
|
|
||||||
|
println!("{}", euid);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display effective/real process group ID UNIX group name
|
||||||
|
if args.is_present("GROUP") && args.is_present("NAME") {
|
||||||
|
// Did they pass -r? If so we show the real one
|
||||||
|
let gid = if args.is_present("REAL") {
|
||||||
|
get_gid()
|
||||||
|
} else {
|
||||||
|
get_egid()
|
||||||
|
}
|
||||||
|
.unwrap_or_exit(1);
|
||||||
|
|
||||||
|
let groups = AllGroups::new(Config::default()).unwrap_or_exit(1);
|
||||||
|
let group = groups.get_by_id(gid).unwrap_or_exit(1);
|
||||||
|
|
||||||
|
println!("{}", group.group);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display the real group ID
|
||||||
|
if args.is_present("GROUP") && args.is_present("REAL") {
|
||||||
|
let gid = get_gid().unwrap_or_exit(1);
|
||||||
|
|
||||||
|
println!("{}", gid);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display effective group ID
|
||||||
|
if args.is_present("GROUP") {
|
||||||
|
let egid = get_egid().unwrap_or_exit(1);
|
||||||
|
|
||||||
|
println!("{}", egid);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We get everything we can and show
|
||||||
|
let euid = get_euid().unwrap_or_exit(1);
|
||||||
|
let egid = get_egid().unwrap_or_exit(1);
|
||||||
|
|
||||||
|
let users = AllUsers::basic(Config::default()).unwrap_or_exit(1);
|
||||||
|
let groups = AllGroups::new(Config::default()).unwrap_or_exit(1);
|
||||||
|
|
||||||
|
let user = users.get_by_id(euid).unwrap_or_exit(1);
|
||||||
|
let group = groups.get_by_id(egid).unwrap_or_exit(1);
|
||||||
|
|
||||||
|
println!("uid={}({}) gid={}({})", euid, user.user, egid, group.group);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate clap;
|
||||||
|
|
||||||
|
use libredox::error::Result;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{self, Write};
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
use extra::option::OptionalExt;
|
||||||
|
use redox_users::{All, AllUsers, Config, User};
|
||||||
|
use termion::input::TermRead;
|
||||||
|
use userutils::spawn_shell;
|
||||||
|
|
||||||
|
const _MAN_PAGE: &'static str = /* @MANSTART{login} */
|
||||||
|
r#"
|
||||||
|
NAME
|
||||||
|
login - log into the computer
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
login
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
The login utility logs users (and pseudo-users) into the computer system.
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
|
||||||
|
-h --help
|
||||||
|
Display help info and exit.
|
||||||
|
|
||||||
|
AUTHOR
|
||||||
|
Written by Jeremy Soller, Jose Narvaez.
|
||||||
|
"#; /* @MANEND */
|
||||||
|
|
||||||
|
const ISSUE_FILE: &'static str = "/etc/issue";
|
||||||
|
const MOTD_FILE: &'static str = "/etc/motd";
|
||||||
|
|
||||||
|
// TODO: Move to redox_users once the definition solidifies.
|
||||||
|
const DEFAULT_SCHEMES: [&'static str; 26] = [
|
||||||
|
// Kernel schemes
|
||||||
|
"debug",
|
||||||
|
"event",
|
||||||
|
"memory",
|
||||||
|
"pipe",
|
||||||
|
"serio",
|
||||||
|
"irq",
|
||||||
|
"time",
|
||||||
|
"sys",
|
||||||
|
// Base schemes
|
||||||
|
"rand",
|
||||||
|
"null",
|
||||||
|
"zero",
|
||||||
|
"log",
|
||||||
|
// Network schemes
|
||||||
|
"ip",
|
||||||
|
"icmp",
|
||||||
|
"tcp",
|
||||||
|
"udp",
|
||||||
|
// IPC schemes
|
||||||
|
"shm",
|
||||||
|
"chan",
|
||||||
|
"uds_stream",
|
||||||
|
"uds_dgram",
|
||||||
|
// File schemes
|
||||||
|
"file",
|
||||||
|
// Display schemes
|
||||||
|
"display.vesa",
|
||||||
|
"display*",
|
||||||
|
// Other schemes
|
||||||
|
"pty",
|
||||||
|
"sudo",
|
||||||
|
"audio",
|
||||||
|
];
|
||||||
|
pub fn apply_login_schemes(
|
||||||
|
user: &User<redox_users::auth::Full>,
|
||||||
|
default_schemes: &[&str],
|
||||||
|
) -> Result<libredox::Fd> {
|
||||||
|
let schemes = match load_config_schemes(user) {
|
||||||
|
Some(s) => s,
|
||||||
|
_ => default_schemes.iter().map(|s| s.to_string()).collect(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut names: Vec<ioslice::IoSlice> = Vec::with_capacity(schemes.len());
|
||||||
|
for scheme in schemes.iter() {
|
||||||
|
names.push(ioslice::IoSlice::new(scheme.as_bytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let ns_fd = libredox::call::mkns(&names)?;
|
||||||
|
let before_ns_fd = libredox::Fd::new(libredox::call::setns(ns_fd)?);
|
||||||
|
|
||||||
|
Ok(before_ns_fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_config_schemes(user: &User<redox_users::auth::Full>) -> Option<Vec<String>> {
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
const LOGIN_SCHEMES_FILE: &'static str = "/etc/login_schemes.toml";
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
struct UserSchemeConfig {
|
||||||
|
pub schemes: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
struct LoginConfig {
|
||||||
|
#[serde(rename = "user_schemes")]
|
||||||
|
pub user_schemes: BTreeMap<String, UserSchemeConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let config_str = fs::read_to_string(LOGIN_SCHEMES_FILE).ok()?;
|
||||||
|
let config: LoginConfig = toml::from_str(&config_str).ok()?;
|
||||||
|
|
||||||
|
config
|
||||||
|
.user_schemes
|
||||||
|
.get(&user.user)
|
||||||
|
.map(|cfg| cfg.schemes.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
let mut stdout = io::stdout();
|
||||||
|
let mut stderr = io::stderr();
|
||||||
|
|
||||||
|
let _args = clap_app!(login =>
|
||||||
|
(author: "Jeremy Soller, Jose Narvaez")
|
||||||
|
(about: "Login as a user")
|
||||||
|
)
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
|
if let Ok(mut issue) = File::open(ISSUE_FILE) {
|
||||||
|
io::copy(&mut issue, &mut stdout).r#try(&mut stderr);
|
||||||
|
stdout.flush().r#try(&mut stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let user = liner::Context::new()
|
||||||
|
.read_line(
|
||||||
|
liner::Prompt::from("\x1B[1mRed Bear login:\x1B[0m "),
|
||||||
|
None,
|
||||||
|
&mut liner::BasicCompleter::new(Vec::<String>::new()),
|
||||||
|
)
|
||||||
|
.r#try(&mut stderr);
|
||||||
|
|
||||||
|
if !user.is_empty() {
|
||||||
|
let stdin = io::stdin();
|
||||||
|
let mut stdin = stdin.lock();
|
||||||
|
let sys_users = AllUsers::authenticator(Config::default()).unwrap_or_exit(1);
|
||||||
|
|
||||||
|
match sys_users.get_by_name(user) {
|
||||||
|
None => {
|
||||||
|
stdout.write(b"\nLogin incorrect\n").r#try(&mut stderr);
|
||||||
|
stdout.write(b"\n").r#try(&mut stderr);
|
||||||
|
stdout.flush().r#try(&mut stderr);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Some(user) => {
|
||||||
|
if user.is_passwd_blank() {
|
||||||
|
if let Ok(mut motd) = File::open(MOTD_FILE) {
|
||||||
|
io::copy(&mut motd, &mut stdout).r#try(&mut stderr);
|
||||||
|
stdout.flush().r#try(&mut stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
let before_ns_fd =
|
||||||
|
apply_login_schemes(user, &DEFAULT_SCHEMES).unwrap_or_exit(1);
|
||||||
|
|
||||||
|
let _ = syscall::fcntl(
|
||||||
|
before_ns_fd.raw(),
|
||||||
|
syscall::F_SETFD,
|
||||||
|
syscall::O_CLOEXEC,
|
||||||
|
);
|
||||||
|
spawn_shell(user).unwrap_or_exit(1);
|
||||||
|
let _ = syscall::fcntl(before_ns_fd.raw(), syscall::F_SETFD, 0);
|
||||||
|
let _ = libredox::call::close(
|
||||||
|
libredox::call::setns(before_ns_fd.into_raw()).unwrap_or_exit(1),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout
|
||||||
|
.write_all(b"\x1B[1mpassword:\x1B[0m ")
|
||||||
|
.r#try(&mut stderr);
|
||||||
|
stdout.flush().r#try(&mut stderr);
|
||||||
|
if let Some(password) = stdin.read_passwd(&mut stdout).r#try(&mut stderr) {
|
||||||
|
stdout.write(b"\n").r#try(&mut stderr);
|
||||||
|
stdout.flush().r#try(&mut stderr);
|
||||||
|
|
||||||
|
if user.verify_passwd(&password) {
|
||||||
|
if let Ok(mut motd) = File::open(MOTD_FILE) {
|
||||||
|
io::copy(&mut motd, &mut stdout).r#try(&mut stderr);
|
||||||
|
stdout.flush().r#try(&mut stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
spawn_shell(user).unwrap_or_exit(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stdout.write(b"\n").r#try(&mut stderr);
|
||||||
|
stdout.flush().r#try(&mut stderr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate clap;
|
||||||
|
|
||||||
|
use std::io;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
|
use extra::option::OptionalExt;
|
||||||
|
use libredox::flag::O_CLOEXEC;
|
||||||
|
use libredox::errno::EPERM;
|
||||||
|
use redox_users::{All, AllUsers, Config, get_uid};
|
||||||
|
use termion::input::TermRead;
|
||||||
|
|
||||||
|
const _MAN_PAGE: &'static str = /* @MANSTART{passwd} */
|
||||||
|
r#"
|
||||||
|
NAME
|
||||||
|
passwd - modify a user's password
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
passwd [ LOGIN ]
|
||||||
|
passwd [ -h | --help ]
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
The passwd utility changes the user's local password. If the user is not
|
||||||
|
the super-user, passwd first prompts for the current password and will
|
||||||
|
not continue unless the correct password is entered.
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
|
||||||
|
-h, --help
|
||||||
|
Display this help and exit.
|
||||||
|
|
||||||
|
-l, --lock
|
||||||
|
Lock the password of the named account. This changes the stored password
|
||||||
|
hash so that it matches no encrypted value ("!")
|
||||||
|
|
||||||
|
Users with locked passwords are not allowed to change their password.
|
||||||
|
|
||||||
|
AUTHOR
|
||||||
|
Written by Jeremy Soller, Jose Narvaez.
|
||||||
|
"#; /* @MANEND */
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut stdin = io::stdin().lock();
|
||||||
|
let mut stdout = io::stdout().lock();
|
||||||
|
let mut stderr = io::stderr();
|
||||||
|
|
||||||
|
let args = clap_app!(passwd =>
|
||||||
|
(author: "Jeremy Soller, Jose Narvaez")
|
||||||
|
(about: "Set user passwords")
|
||||||
|
(@arg LOGIN: "Apply to login. Sets password for current user if not supplied")
|
||||||
|
(@arg LOCK: -l --lock "Lock the password for an account (no login)")
|
||||||
|
)
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
|
if args.is_present("LOCK") {
|
||||||
|
if get_uid().unwrap_or_exit(1) != 0 {
|
||||||
|
eprintln!("passwd: only root is allowed to lock accounts");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut users =
|
||||||
|
AllUsers::authenticator(Config::default().writeable(true)).unwrap_or_exit(1);
|
||||||
|
|
||||||
|
let Some(login) = args.value_of("LOGIN") else {
|
||||||
|
eprintln!("passwd: no account specified to lock");
|
||||||
|
exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
let user = users.get_mut_by_name(login).unwrap_or_else(|| {
|
||||||
|
eprintln!("passwd: user does not exist: {}", login);
|
||||||
|
exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
user.unset_passwd();
|
||||||
|
users.save().unwrap_or_exit(1);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let uid = get_uid().unwrap_or_exit(1);
|
||||||
|
|
||||||
|
if uid == 0 {
|
||||||
|
let mut users =
|
||||||
|
AllUsers::authenticator(Config::default().writeable(true)).unwrap_or_exit(1);
|
||||||
|
|
||||||
|
let user = find_user(&args, &mut users);
|
||||||
|
|
||||||
|
let msg = format!("changing password for '{}' \n", user.user);
|
||||||
|
stdout.write_all(&msg.as_bytes()).r#try(&mut stderr);
|
||||||
|
stdout.flush().r#try(&mut stderr);
|
||||||
|
|
||||||
|
let new_password = ask_new_password(stdin, stdout, stderr);
|
||||||
|
|
||||||
|
user.set_passwd(&new_password).unwrap_or_exit(1);
|
||||||
|
users.save().unwrap_or_exit(1);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut users = AllUsers::basic(Config::default()).unwrap_or_exit(1);
|
||||||
|
|
||||||
|
let user = find_user(&args, &mut users);
|
||||||
|
|
||||||
|
if user.uid != uid {
|
||||||
|
eprintln!(
|
||||||
|
"passwd: you do not have permission to set the password of '{}'",
|
||||||
|
user.user
|
||||||
|
);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let msg = format!("changing password for '{}' \n", user.user);
|
||||||
|
stdout.write_all(&msg.as_bytes()).r#try(&mut stderr);
|
||||||
|
stdout.flush().r#try(&mut stderr);
|
||||||
|
|
||||||
|
drop(users); // Unlock /etc/passwd
|
||||||
|
|
||||||
|
stdout.write_all(b"current password: ").r#try(&mut stderr);
|
||||||
|
stdout.flush().r#try(&mut stderr);
|
||||||
|
|
||||||
|
let file = libredox::call::open("/scheme/sudo/passwd", O_CLOEXEC, 0).unwrap();
|
||||||
|
|
||||||
|
if let Some(password) = stdin.read_passwd(&mut stdout).r#try(&mut stderr) {
|
||||||
|
stdout.write(b"\n").r#try(&mut stderr);
|
||||||
|
stdout.flush().r#try(&mut stderr);
|
||||||
|
|
||||||
|
match libredox::call::write(file, password.as_bytes()) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(err) if err.errno() == EPERM => {
|
||||||
|
eprintln!("passwd: incorrect current password");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
Err(err) => panic!("{err}"),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
eprintln!("passwd: incorrect current password");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_password = ask_new_password(stdin, stdout, stderr);
|
||||||
|
|
||||||
|
match libredox::call::write(file, new_password.as_bytes()) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(err) if err.errno() == EPERM => {
|
||||||
|
eprintln!("passwd: invalid new password");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
Err(err) => panic!("{err}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_user<'a, T: Default>(
|
||||||
|
args: &clap::ArgMatches<'_>,
|
||||||
|
users: &'a mut AllUsers<T>,
|
||||||
|
) -> &'a mut redox_users::User<T> {
|
||||||
|
let uid = get_uid().unwrap_or_exit(1);
|
||||||
|
match args.value_of("LOGIN") {
|
||||||
|
Some(login) => users.get_mut_by_name(login).unwrap_or_else(|| {
|
||||||
|
eprintln!("passwd: user does not exist: {}", login);
|
||||||
|
exit(1);
|
||||||
|
}),
|
||||||
|
None => users.get_mut_by_id(uid).unwrap_or_else(|| {
|
||||||
|
eprintln!("passwd: you do not exist");
|
||||||
|
exit(1);
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ask_new_password(
|
||||||
|
mut stdin: io::StdinLock<'_>,
|
||||||
|
mut stdout: io::StdoutLock<'_>,
|
||||||
|
mut stderr: io::Stderr,
|
||||||
|
) -> String {
|
||||||
|
stdout.write_all(b"new password: ").r#try(&mut stderr);
|
||||||
|
stdout.flush().r#try(&mut stderr);
|
||||||
|
let Some(new_password) = stdin.read_passwd(&mut stdout).r#try(&mut stderr) else {
|
||||||
|
eprintln!("passwd: no new password provided");
|
||||||
|
exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
stdout.write(b"\nconfirm password: ").r#try(&mut stderr);
|
||||||
|
stdout.flush().r#try(&mut stderr);
|
||||||
|
let Some(confirm_password) = stdin.read_passwd(&mut stdout).r#try(&mut stderr) else {
|
||||||
|
eprintln!("\npasswd: no confirm password provided");
|
||||||
|
exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
stdout.write(b"\n").r#try(&mut stderr);
|
||||||
|
stdout.flush().r#try(&mut stderr);
|
||||||
|
|
||||||
|
if new_password != confirm_password {
|
||||||
|
eprintln!("passwd: new password does not match confirm password");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
new_password
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate clap;
|
||||||
|
|
||||||
|
use std::io::{self, Write};
|
||||||
|
use std::process::exit;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
use extra::option::OptionalExt;
|
||||||
|
use libredox::flag::O_CLOEXEC;
|
||||||
|
use redox_users::{All, AllUsers, Config, get_uid};
|
||||||
|
use syscall::EPERM;
|
||||||
|
use termion::input::TermRead;
|
||||||
|
use userutils::spawn_shell;
|
||||||
|
|
||||||
|
const _MAN_PAGE: &'static str = /* @MANSTART{su} */
|
||||||
|
r#"
|
||||||
|
NAME
|
||||||
|
su - substitute user identity
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
su [ user ]
|
||||||
|
su [ -h | --help ]
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
The su utility requests appropriate user credentials via PAM and switches to
|
||||||
|
that user ID (the default user is the superuser). A shell is then executed.
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
|
||||||
|
-h, --help
|
||||||
|
Display this help and exit.
|
||||||
|
|
||||||
|
AUTHOR
|
||||||
|
Written by Jeremy Soller, Jose Narvaez.
|
||||||
|
"#; /* @MANEND */
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
let stdin = io::stdin();
|
||||||
|
let mut stdin = stdin.lock();
|
||||||
|
let stdout = io::stdout();
|
||||||
|
let mut stdout = stdout.lock();
|
||||||
|
let mut stderr = io::stderr();
|
||||||
|
|
||||||
|
let args = clap_app!(su =>
|
||||||
|
(author: "Jeremy Soller, Jose Narvaez")
|
||||||
|
(about: "substitue user identity")
|
||||||
|
(@arg LOGIN: "Login as LOGIN. Default is \'root\'")
|
||||||
|
)
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
|
let target_user = args.value_of("LOGIN").unwrap_or("root");
|
||||||
|
|
||||||
|
let uid = get_uid().unwrap_or_exit(1);
|
||||||
|
|
||||||
|
let users = AllUsers::basic(Config::default()).unwrap_or_exit(1);
|
||||||
|
let user = users.get_by_name(&target_user).unwrap_or_exit(1);
|
||||||
|
|
||||||
|
// If the user executing su is root, then they can do anything without a password.
|
||||||
|
// Same if the user we're being asked to login as doesn't have a password.
|
||||||
|
if uid == 0 {
|
||||||
|
writeln!(stdout).unwrap_or_exit(1);
|
||||||
|
exit(spawn_shell(user).unwrap_or_exit(1));
|
||||||
|
} else {
|
||||||
|
let file = libredox::call::open("/scheme/sudo/su", O_CLOEXEC, 0).unwrap();
|
||||||
|
|
||||||
|
write!(stdout, "password: ").unwrap_or_exit(1);
|
||||||
|
stdout.flush().unwrap_or_exit(1);
|
||||||
|
|
||||||
|
// Read the password, reading an empty string if CTRL-d is specified
|
||||||
|
let password = stdin
|
||||||
|
.read_passwd(&mut stdout)
|
||||||
|
.r#try(&mut stderr)
|
||||||
|
.unwrap_or(String::new());
|
||||||
|
|
||||||
|
match libredox::call::write(file, password.as_bytes()) {
|
||||||
|
Ok(_) => exit(spawn_shell(user).unwrap_or_exit(1)),
|
||||||
|
Err(err) if err.errno() == EPERM => {
|
||||||
|
writeln!(stderr, "su: authentication failed").unwrap_or_exit(1);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
Err(err) => panic!("{err}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+402
@@ -0,0 +1,402 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::env;
|
||||||
|
use std::io::{self, Write};
|
||||||
|
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd};
|
||||||
|
use std::os::unix::process::CommandExt;
|
||||||
|
use std::process::{Command, exit};
|
||||||
|
|
||||||
|
use extra::option::OptionalExt;
|
||||||
|
use libredox::flag::O_CLOEXEC;
|
||||||
|
use libredox::protocol::ProcCall;
|
||||||
|
use redox_rt::sys::proc_call;
|
||||||
|
use redox_scheme::scheme::{SchemeSync, register_sync_scheme};
|
||||||
|
use redox_scheme::{
|
||||||
|
CallerCtx, OpenResult, RequestKind, Response, SendFdRequest, SignalBehavior, Socket,
|
||||||
|
};
|
||||||
|
use redox_users::{All, AllGroups, AllUsers, Config, get_uid};
|
||||||
|
use syscall::error::*;
|
||||||
|
use syscall::flag::*;
|
||||||
|
use syscall::schemev2::NewFdFlags;
|
||||||
|
use termion::input::TermRead;
|
||||||
|
|
||||||
|
const MAX_ATTEMPTS: u16 = 3;
|
||||||
|
const _MAN_PAGE: &'static str = /* @MANSTART{sudo} */
|
||||||
|
r#"
|
||||||
|
NAME
|
||||||
|
sudo - execute a command as another user
|
||||||
|
|
||||||
|
SYNOPSIS
|
||||||
|
sudo command
|
||||||
|
sudo [ -h | --help ]
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
The sudo utility allows a permitted user to execute a command as the
|
||||||
|
superuser or another user, as specified by the security policy.
|
||||||
|
|
||||||
|
EXIT STATUS
|
||||||
|
Upon successful execution of a command, the exit status from sudo will
|
||||||
|
be the exit status of the program that was executed. In case of error
|
||||||
|
the exit status will be >0.
|
||||||
|
|
||||||
|
AUTHOR
|
||||||
|
Written by Jeremy Soller, Jose Narvaez, bjorn3.
|
||||||
|
"#; /* @MANEND */
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if env::args().nth(1).as_deref() == Some("--daemon") {
|
||||||
|
daemon_main();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut args = env::args().skip(1);
|
||||||
|
let cmd = args.next().unwrap_or_else(|| {
|
||||||
|
eprintln!("sudo: no command provided");
|
||||||
|
exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
let users = AllUsers::basic(Config::default()).unwrap_or_exit(1);
|
||||||
|
let uid = get_uid().unwrap_or_exit(1);
|
||||||
|
let user = users.get_by_id(uid).unwrap_or_exit(1);
|
||||||
|
|
||||||
|
if uid == 0 {
|
||||||
|
// We are root already. No need to elevate privileges
|
||||||
|
run_command_as_root(&cmd, &args.collect());
|
||||||
|
}
|
||||||
|
|
||||||
|
let file = libredox::call::open("/scheme/sudo", O_CLOEXEC, 0).unwrap();
|
||||||
|
|
||||||
|
let mut attempts = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
print!("[sudo] password for {}: ", user.user);
|
||||||
|
let _ = io::stdout().flush();
|
||||||
|
|
||||||
|
match io::stdin().read_passwd(&mut io::stdout()).unwrap() {
|
||||||
|
Some(password) => {
|
||||||
|
println!();
|
||||||
|
|
||||||
|
match libredox::call::write(file, password.as_bytes()) {
|
||||||
|
Ok(_) => break,
|
||||||
|
Err(err) if err.errno() == EPERM => {
|
||||||
|
attempts += 1;
|
||||||
|
eprintln!(
|
||||||
|
"sudo: incorrect password or not in sudo group ({}/{})",
|
||||||
|
attempts, MAX_ATTEMPTS,
|
||||||
|
);
|
||||||
|
if attempts >= MAX_ATTEMPTS {
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => panic!("{err}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
println!();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME move to libredox
|
||||||
|
unsafe extern "C" {
|
||||||
|
safe fn redox_cur_procfd_v0() -> usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Elevate privileges of our own process with help from the sudo daemon
|
||||||
|
syscall::sendfd(
|
||||||
|
file,
|
||||||
|
syscall::dup(redox_cur_procfd_v0(), &[]).unwrap(),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// FIXME perhaps keep the original namespace available in a subdirectory of the namespace we switch to?
|
||||||
|
let ns = syscall::openat(file, "ns", 0, syscall::O_CLOEXEC).unwrap();
|
||||||
|
libredox::call::setns(ns).unwrap();
|
||||||
|
|
||||||
|
run_command_as_root(&cmd, &args.collect());
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Policy {
|
||||||
|
Deny,
|
||||||
|
Authenticate,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn policy_for_user(uid: u32) -> Policy {
|
||||||
|
let users = AllUsers::authenticator(Config::default()).unwrap_or_exit(1);
|
||||||
|
let groups = AllGroups::new(Config::default()).unwrap_or_exit(1);
|
||||||
|
|
||||||
|
let user = users.get_by_id(uid as usize).unwrap_or_exit(1);
|
||||||
|
|
||||||
|
let sudo_group = groups.get_by_name("sudo").unwrap_or_exit(1);
|
||||||
|
if !sudo_group.users.iter().any(|name| name == &user.user) {
|
||||||
|
return Policy::Deny;
|
||||||
|
}
|
||||||
|
|
||||||
|
Policy::Authenticate
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_command_as_root(cmd: &str, args: &Vec<String>) -> ! {
|
||||||
|
let mut command = Command::new(&cmd);
|
||||||
|
for arg in args {
|
||||||
|
command.arg(&arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
command.uid(0);
|
||||||
|
command.gid(0);
|
||||||
|
command.env("USER", "root");
|
||||||
|
command.env("UID", "0");
|
||||||
|
command.env("GROUPS", "0");
|
||||||
|
|
||||||
|
let err = command.exec();
|
||||||
|
|
||||||
|
eprintln!("sudo: failed to execute {}: {}", cmd, err);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Scheme {
|
||||||
|
next_fd: usize,
|
||||||
|
handles: HashMap<usize, Handle>,
|
||||||
|
}
|
||||||
|
enum Handle {
|
||||||
|
AwaitingPassword { uid: u32 },
|
||||||
|
AwaitingRootPassword,
|
||||||
|
AwaitingContextFd,
|
||||||
|
AwaitingNamespaceFetch { ns: libredox::Fd },
|
||||||
|
|
||||||
|
AwaitingPasswordForPasswd { uid: u32 },
|
||||||
|
AwaitingNewPassword { uid: u32 },
|
||||||
|
|
||||||
|
Placeholder,
|
||||||
|
|
||||||
|
SchemeRoot,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SchemeSync for Scheme {
|
||||||
|
fn scheme_root(&mut self) -> Result<usize> {
|
||||||
|
let fd = self.next_fd;
|
||||||
|
self.next_fd = self.next_fd.checked_add(1).ok_or(Error::new(EMFILE))?;
|
||||||
|
self.handles.insert(fd, Handle::SchemeRoot);
|
||||||
|
Ok(fd)
|
||||||
|
}
|
||||||
|
fn openat(
|
||||||
|
&mut self,
|
||||||
|
dirfd: usize,
|
||||||
|
path: &str,
|
||||||
|
_flags: usize,
|
||||||
|
_fcntl_flags: u32,
|
||||||
|
ctx: &CallerCtx,
|
||||||
|
) -> Result<OpenResult> {
|
||||||
|
let handle = match self.handles.get_mut(&dirfd).ok_or(Error::new(EBADF))? {
|
||||||
|
Handle::SchemeRoot => match path {
|
||||||
|
"" => Handle::AwaitingPassword { uid: ctx.uid },
|
||||||
|
"su" => Handle::AwaitingRootPassword,
|
||||||
|
"passwd" => Handle::AwaitingPasswordForPasswd { uid: ctx.uid },
|
||||||
|
_ => return Err(Error::new(ENOENT)),
|
||||||
|
},
|
||||||
|
Handle::AwaitingNamespaceFetch { .. } => {
|
||||||
|
if path != "ns" {
|
||||||
|
return Err(Error::new(ENOENT));
|
||||||
|
}
|
||||||
|
let ns = match self.handles.insert(dirfd, Handle::Placeholder).unwrap() {
|
||||||
|
Handle::AwaitingNamespaceFetch { ns } => ns,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
return Ok(OpenResult::OtherScheme { fd: ns.into_raw() });
|
||||||
|
}
|
||||||
|
_ => return Err(Error::new(EINVAL)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let fd = self.next_fd;
|
||||||
|
self.next_fd = self.next_fd.checked_add(1).ok_or(Error::new(EMFILE))?;
|
||||||
|
self.handles.insert(fd, handle);
|
||||||
|
|
||||||
|
Ok(OpenResult::ThisScheme {
|
||||||
|
number: fd,
|
||||||
|
flags: NewFdFlags::empty(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(
|
||||||
|
&mut self,
|
||||||
|
id: usize,
|
||||||
|
buf: &[u8],
|
||||||
|
_off: u64,
|
||||||
|
_flags: u32,
|
||||||
|
_ctx: &CallerCtx,
|
||||||
|
) -> Result<usize> {
|
||||||
|
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||||
|
|
||||||
|
let validate_utf8 = |buf| std::str::from_utf8(buf).map_err(|_| Error::new(EINVAL));
|
||||||
|
|
||||||
|
match std::mem::replace(handle, Handle::Placeholder) {
|
||||||
|
Handle::AwaitingPassword { uid } => {
|
||||||
|
let users = AllUsers::authenticator(Config::default()).unwrap_or_exit(1);
|
||||||
|
let user = users.get_by_id(uid as usize).unwrap_or_exit(1);
|
||||||
|
|
||||||
|
match policy_for_user(uid) {
|
||||||
|
Policy::Deny => {
|
||||||
|
*handle = Handle::AwaitingPassword { uid };
|
||||||
|
return Err(Error::new(EPERM));
|
||||||
|
}
|
||||||
|
Policy::Authenticate => {
|
||||||
|
let password = validate_utf8(buf)?;
|
||||||
|
if user.verify_passwd(&password) {
|
||||||
|
*handle = Handle::AwaitingContextFd
|
||||||
|
} else {
|
||||||
|
*handle = Handle::AwaitingPassword { uid };
|
||||||
|
return Err(Error::new(EPERM));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Handle::AwaitingRootPassword => {
|
||||||
|
let users = AllUsers::authenticator(Config::default()).unwrap_or_exit(1);
|
||||||
|
let user = users.get_by_id(0).unwrap_or_exit(1);
|
||||||
|
|
||||||
|
let password = validate_utf8(buf)?;
|
||||||
|
if user.verify_passwd(&password) {
|
||||||
|
*handle = Handle::AwaitingContextFd
|
||||||
|
} else {
|
||||||
|
*handle = Handle::AwaitingRootPassword;
|
||||||
|
return Err(Error::new(EPERM));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Handle::AwaitingContextFd => {
|
||||||
|
*handle = Handle::AwaitingContextFd;
|
||||||
|
return Err(Error::new(EINVAL));
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle::AwaitingPasswordForPasswd { uid } => {
|
||||||
|
let users =
|
||||||
|
AllUsers::authenticator(Config::default()).map_err(|_| Error::new(ENOLCK))?;
|
||||||
|
let user = users.get_by_id(uid as usize).ok_or(Error::new(EEXIST))?;
|
||||||
|
|
||||||
|
let password = validate_utf8(buf)?;
|
||||||
|
if user.verify_passwd(&password) {
|
||||||
|
*handle = Handle::AwaitingNewPassword { uid }
|
||||||
|
} else {
|
||||||
|
*handle = Handle::AwaitingPasswordForPasswd { uid };
|
||||||
|
return Err(Error::new(EPERM));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Handle::AwaitingNewPassword { uid } => {
|
||||||
|
let mut users = AllUsers::authenticator(Config::default().writeable(true))
|
||||||
|
.map_err(|_| Error::new(ENOLCK))?;
|
||||||
|
let user = users
|
||||||
|
.get_mut_by_id(uid as usize)
|
||||||
|
.ok_or(Error::new(EEXIST))?;
|
||||||
|
|
||||||
|
let new_password = validate_utf8(buf)?;
|
||||||
|
if user.set_passwd(&new_password).is_ok() {
|
||||||
|
users.save().map_err(|_| Error::new(ENOLCK))?;
|
||||||
|
*handle = Handle::Placeholder
|
||||||
|
} else {
|
||||||
|
*handle = Handle::AwaitingNewPassword { uid };
|
||||||
|
return Err(Error::new(EPERM));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle::AwaitingNamespaceFetch { .. } => {
|
||||||
|
eprintln!("sudo: found namespace fetch handle with ID {id}");
|
||||||
|
return Err(Error::new(EBADFD));
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle::Placeholder => {
|
||||||
|
eprintln!("sudo: found placeholder handle with ID {id}");
|
||||||
|
return Err(Error::new(EBADFD));
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle::SchemeRoot => {
|
||||||
|
eprintln!("sudo: found Scheme root handle with ID {id}");
|
||||||
|
return Err(Error::new(EBADFD));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Scheme {
|
||||||
|
fn on_close(&mut self, id: usize) {
|
||||||
|
self.handles.remove(&id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_sendfd(&mut self, socket: &Socket, req: &SendFdRequest) -> Result<usize> {
|
||||||
|
let handle = self.handles.get_mut(&req.id()).ok_or(Error::new(EBADF))?;
|
||||||
|
match std::mem::replace(handle, Handle::Placeholder) {
|
||||||
|
Handle::AwaitingContextFd => {
|
||||||
|
let mut proc_fd = usize::MAX;
|
||||||
|
req.obtain_fd(
|
||||||
|
socket,
|
||||||
|
FobtainFdFlags::empty(),
|
||||||
|
std::slice::from_mut(&mut proc_fd),
|
||||||
|
)?;
|
||||||
|
let proc_fd = unsafe { OwnedFd::from_raw_fd(proc_fd as RawFd) };
|
||||||
|
|
||||||
|
let [ruid, euid, suid] = [0, 0, 0];
|
||||||
|
let [rgid, egid, sgid] = [0, 0, 0];
|
||||||
|
let mut payload = [0; size_of::<u32>() * 6];
|
||||||
|
plain::slice_from_mut_bytes(&mut payload)
|
||||||
|
.unwrap()
|
||||||
|
.copy_from_slice(&[ruid, euid, suid, rgid, egid, sgid]);
|
||||||
|
|
||||||
|
if let Err(err) = proc_call(
|
||||||
|
proc_fd.as_raw_fd() as usize,
|
||||||
|
&mut payload,
|
||||||
|
CallFlags::empty(),
|
||||||
|
&[ProcCall::SetResugid as u64],
|
||||||
|
) {
|
||||||
|
eprintln!("failed to setresugid: {err}");
|
||||||
|
}
|
||||||
|
|
||||||
|
*handle = Handle::AwaitingNamespaceFetch {
|
||||||
|
ns: libredox::Fd::new(
|
||||||
|
syscall::dup(libredox::call::getns().unwrap(), b"").unwrap(),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
old => {
|
||||||
|
*handle = old;
|
||||||
|
return Err(Error::new(EBADF));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn daemon_main() -> ! {
|
||||||
|
// TODO: Linux kernel audit-like logging?
|
||||||
|
let socket = Socket::create().expect("failed to open scheme socket");
|
||||||
|
|
||||||
|
let mut scheme = Scheme {
|
||||||
|
next_fd: 1,
|
||||||
|
handles: HashMap::new(),
|
||||||
|
};
|
||||||
|
let mut scheme_state = redox_scheme::scheme::SchemeState::new();
|
||||||
|
|
||||||
|
register_sync_scheme(&socket, "sudo", &mut scheme)
|
||||||
|
.expect("failed to register sudo scheme to namespace");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let Some(req) = socket
|
||||||
|
.next_request(SignalBehavior::Restart)
|
||||||
|
.expect("failed to get request")
|
||||||
|
else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = match req.kind() {
|
||||||
|
RequestKind::Call(call) => call.handle_sync(&mut scheme, &mut scheme_state),
|
||||||
|
RequestKind::SendFd(req) => Response::new(scheme.on_sendfd(&socket, &req), req),
|
||||||
|
RequestKind::OnClose { id } => {
|
||||||
|
scheme.on_close(id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
socket
|
||||||
|
.write_response(response, SignalBehavior::Restart)
|
||||||
|
.expect("sudo: scheme write failed");
|
||||||
|
}
|
||||||
|
std::process::exit(0)
|
||||||
|
}
|
||||||
@@ -0,0 +1,205 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate clap;
|
||||||
|
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
|
use extra::option::OptionalExt;
|
||||||
|
use redox_users::{All, AllGroups, AllUsers, Config, GroupBuilder, UserBuilder};
|
||||||
|
use userutils::create_user_dir;
|
||||||
|
|
||||||
|
const _MAN_PAGE: &'static str = /* @MANSTART{useradd} */
|
||||||
|
r#"
|
||||||
|
NAME
|
||||||
|
useradd - add a new user
|
||||||
|
|
||||||
|
SYNOPSYS
|
||||||
|
useradd [ options ] LOGIN
|
||||||
|
useradd [ -h | --help ]
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
The useradd utility creates a new user based on
|
||||||
|
system defaults and values passed on the command line.
|
||||||
|
|
||||||
|
Useradd creates a new group for the user by default and
|
||||||
|
can also be instructed to create the user's home directory.
|
||||||
|
|
||||||
|
Note that useradd creates a new user with the password
|
||||||
|
unset (no login). This is better documented with the
|
||||||
|
redox_users crate.
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
-h, --help
|
||||||
|
Display this help and exit.
|
||||||
|
|
||||||
|
-c, --comment
|
||||||
|
Any text string, usually used as the user's full name.
|
||||||
|
Historically known as the GECOS field
|
||||||
|
|
||||||
|
-d, --home-dir HOME_DIR
|
||||||
|
The new user will be created with HOME_DIR as their home
|
||||||
|
directory. The default value is LOGIN prepended with "/home/".
|
||||||
|
This flag DOES NOT create the home directory. See --create-home
|
||||||
|
|
||||||
|
-g, --gid GID
|
||||||
|
The group id to use when creating the default login group. This value
|
||||||
|
must not be in use and must be non-negative. The default is to pick the
|
||||||
|
smallest available group id between values defined in redox_users.
|
||||||
|
|
||||||
|
-m, --create-home
|
||||||
|
Creates the user's home directory if it does not already exist.
|
||||||
|
|
||||||
|
This option is not enabled by default. This option must be specified
|
||||||
|
for a home directory to be created. If not set, the user's home dir is
|
||||||
|
set to "/"
|
||||||
|
|
||||||
|
-N, --no-user-group
|
||||||
|
Do not attempt to create the user's user group. Instead, the groupid
|
||||||
|
is set to 99 ("nobody"). -N and -g are mutually exclusive.
|
||||||
|
|
||||||
|
-s, --shell SHELL
|
||||||
|
The path to the user's default login shell. If not specified, the
|
||||||
|
default shell is set as "/bin/ion"
|
||||||
|
|
||||||
|
-u, --uid UID
|
||||||
|
The user id to use. This value must not be in use and must be
|
||||||
|
non-negative. The default is to pick the smallest available
|
||||||
|
user id between the defaults defined in redox_users
|
||||||
|
|
||||||
|
AUTHORS
|
||||||
|
Written by Wesley Hershberger.
|
||||||
|
"#; /* @MANEND */
|
||||||
|
const DEFAULT_SHELL: &'static str = "/bin/ion";
|
||||||
|
const DEFAULT_HOME: &'static str = "/home";
|
||||||
|
const DEFAULT_NO_GROUP: &'static str = "nobody";
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args = clap_app!(useradd =>
|
||||||
|
(author: "Wesley Hershberger")
|
||||||
|
(about: "Add users based on the system's redox_users backend")
|
||||||
|
(@arg LOGIN:
|
||||||
|
+required
|
||||||
|
"Add user LOGIN")
|
||||||
|
(@arg COMMENT:
|
||||||
|
-c --comment
|
||||||
|
+takes_value
|
||||||
|
"Set user description (GECOS field)")
|
||||||
|
(@arg HOME_DIR:
|
||||||
|
-d --("home-dir")
|
||||||
|
+takes_value
|
||||||
|
"Set LOGIN's home dir to HOME_DIR (does not create directory)")
|
||||||
|
(@arg CREATE_HOME:
|
||||||
|
-m --("create-home")
|
||||||
|
"Create the user's home directory")
|
||||||
|
(@arg SHELL:
|
||||||
|
-s --shell
|
||||||
|
+takes_value
|
||||||
|
"Set user's default login shell")
|
||||||
|
(@arg GID:
|
||||||
|
-g --gid
|
||||||
|
+takes_value
|
||||||
|
"Set LOGIN's primary group id. Positive integer and must not be in use.")
|
||||||
|
(@arg NO_USER_GROUP:
|
||||||
|
-N --("no-user-group")
|
||||||
|
conflicts_with[GID]
|
||||||
|
"Do not create primary user group (set gid to 99, \"nobody\")")
|
||||||
|
(@arg UID:
|
||||||
|
-u --uid
|
||||||
|
+takes_value
|
||||||
|
"Set LOGIN's user id. Positive ineger and must not be in use.")
|
||||||
|
)
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
|
// unwrap is safe because of "+required". clap-rs is cool...
|
||||||
|
let login = args.value_of("LOGIN").unwrap();
|
||||||
|
|
||||||
|
let mut sys_users =
|
||||||
|
AllUsers::authenticator(Config::default().writeable(true)).unwrap_or_exit(1);
|
||||||
|
let mut sys_groups = AllGroups::new(Config::default().writeable(true)).unwrap_or_exit(1);
|
||||||
|
|
||||||
|
let uid = match args.value_of("UID") {
|
||||||
|
Some(uid) => {
|
||||||
|
let id = uid.parse::<usize>().unwrap_or_exit(1);
|
||||||
|
if let Some(_user) = sys_users.get_by_id(id) {
|
||||||
|
eprintln!("useradd: userid already in use: {}", id);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
id
|
||||||
|
}
|
||||||
|
None => sys_users.get_unique_id().unwrap_or_else(|| {
|
||||||
|
eprintln!("useradd: no available uid");
|
||||||
|
exit(1);
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
let gid = if args.is_present("NO_USER_GROUP") {
|
||||||
|
let nobody = sys_groups
|
||||||
|
.get_mut_by_name(DEFAULT_NO_GROUP)
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
eprintln!("useradd: group not found: {}", DEFAULT_NO_GROUP);
|
||||||
|
exit(1)
|
||||||
|
});
|
||||||
|
nobody.users.push(login.to_string());
|
||||||
|
99
|
||||||
|
} else {
|
||||||
|
let id = match args.value_of("GID") {
|
||||||
|
Some(id) => {
|
||||||
|
let id = id.parse::<usize>().unwrap_or_exit(1);
|
||||||
|
if let Some(_group) = sys_groups.get_by_id(id) {
|
||||||
|
eprintln!("useradd: group already exists with gid: {}", id);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
id
|
||||||
|
}
|
||||||
|
None => sys_groups.get_unique_id().unwrap_or_else(|| {
|
||||||
|
eprintln!("useradd: no available gid");
|
||||||
|
exit(1);
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
sys_groups
|
||||||
|
.add_group(GroupBuilder::new(login).gid(id).user(login))
|
||||||
|
.unwrap_or_else(|err| {
|
||||||
|
eprintln!("useradd: {}: {}", err, login);
|
||||||
|
exit(1);
|
||||||
|
});
|
||||||
|
id
|
||||||
|
};
|
||||||
|
|
||||||
|
let gecos = args.value_of("COMMENT").unwrap_or(login);
|
||||||
|
|
||||||
|
//Ugly way to satisfy the borrow checker...
|
||||||
|
let mut sys_homes = String::from(DEFAULT_HOME);
|
||||||
|
let userhome = args.value_of("HOME_DIR").unwrap_or_else(|| {
|
||||||
|
if args.is_present("CREATE_HOME") {
|
||||||
|
sys_homes.push_str("/");
|
||||||
|
sys_homes.push_str(&login);
|
||||||
|
sys_homes.as_str()
|
||||||
|
} else {
|
||||||
|
"/"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let shell = args.value_of("SHELL").unwrap_or(DEFAULT_SHELL);
|
||||||
|
|
||||||
|
let user = UserBuilder::new(login)
|
||||||
|
.uid(uid)
|
||||||
|
.gid(gid)
|
||||||
|
.name(gecos)
|
||||||
|
.home(userhome)
|
||||||
|
.shell(shell);
|
||||||
|
sys_users.add_user(user).unwrap_or_else(|err| {
|
||||||
|
eprintln!("useradd: {}: {}", err, login);
|
||||||
|
exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Make sure to try and create the user/groups before we create
|
||||||
|
// their home, that way we get a permissions error that makes
|
||||||
|
// more sense
|
||||||
|
sys_groups.save().unwrap_or_exit(1);
|
||||||
|
sys_users.save().unwrap_or_exit(1);
|
||||||
|
|
||||||
|
if args.is_present("CREATE_HOME") {
|
||||||
|
//Shouldn't ever error...
|
||||||
|
let user = sys_users.get_by_id(uid).unwrap_or_exit(1);
|
||||||
|
create_user_dir(user, userhome).unwrap_or_exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate clap;
|
||||||
|
|
||||||
|
use std::fs::remove_dir;
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
|
use extra::option::OptionalExt;
|
||||||
|
use redox_users::{All, AllGroups, AllUsers, Config};
|
||||||
|
use userutils::AllGroupsExt;
|
||||||
|
|
||||||
|
const _MAN_PAGE: &'static str = /* @MANSTART{userdel} */
|
||||||
|
r#"
|
||||||
|
NAME
|
||||||
|
userdel - modify system files to delete users
|
||||||
|
|
||||||
|
SYNOPSYS
|
||||||
|
userdel [ options ] LOGIN
|
||||||
|
userdel [ -h | --help ]
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
userdel removes users from whatever backend is employed by
|
||||||
|
the system's redox_users. The utility removes the user from
|
||||||
|
all groups of which they are a member.
|
||||||
|
|
||||||
|
It can also be used to manage removal of home directories.
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
-h, --help
|
||||||
|
Print this help page and exit.
|
||||||
|
|
||||||
|
-r, --remove
|
||||||
|
The user's home directory and all files inside will be
|
||||||
|
removed.
|
||||||
|
|
||||||
|
AUTHORS
|
||||||
|
Wesley Hershberger.
|
||||||
|
"#; /* @MANEND */
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args = clap_app!(userdel =>
|
||||||
|
(author: "Wesley Hershberger")
|
||||||
|
(about: "Removes system users using redox_users")
|
||||||
|
(@arg LOGIN: +required "Remove user LOGIN")
|
||||||
|
(@arg REMOVE: -r --remove "Remove the user's home and all files and directories inside")
|
||||||
|
)
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
|
let login = args.value_of("LOGIN").unwrap();
|
||||||
|
|
||||||
|
let mut sys_users =
|
||||||
|
AllUsers::authenticator(Config::default().writeable(true)).unwrap_or_exit(1);
|
||||||
|
let mut sys_groups = AllGroups::new(Config::default().writeable(true)).unwrap_or_exit(1);
|
||||||
|
{
|
||||||
|
sys_groups.remove_user_from_all_groups(login);
|
||||||
|
|
||||||
|
if args.is_present("REMOVE") {
|
||||||
|
let user = sys_users.get_by_name(login).unwrap_or_else(|| {
|
||||||
|
eprintln!("userdel: user does not exist: {}", login);
|
||||||
|
exit(1);
|
||||||
|
});
|
||||||
|
remove_dir(&user.home).unwrap_or_exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sys_users.remove_by_name(login.to_string());
|
||||||
|
|
||||||
|
sys_groups.save().unwrap_or_exit(1);
|
||||||
|
sys_users.save().unwrap_or_exit(1);
|
||||||
|
}
|
||||||
@@ -0,0 +1,196 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate clap;
|
||||||
|
|
||||||
|
use std::fs::{remove_dir, rename};
|
||||||
|
use std::process::exit;
|
||||||
|
|
||||||
|
use extra::option::OptionalExt;
|
||||||
|
use redox_users::{All, AllGroups, AllUsers, Config};
|
||||||
|
use userutils::{AllGroupsExt, create_user_dir};
|
||||||
|
|
||||||
|
const _MAN_PAGE: &'static str = /* @MANSTART{usermod} */
|
||||||
|
r#"
|
||||||
|
NAME
|
||||||
|
usermod - modify user information
|
||||||
|
|
||||||
|
SYNOPSYS
|
||||||
|
usermod [ options ] LOGIN
|
||||||
|
usermod [ -h | --help ]
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
The usermod utility can be used to modify user information.
|
||||||
|
|
||||||
|
This utility uses the redox_users API, so the backend is whatever
|
||||||
|
backend in use on the system for that API at the time.
|
||||||
|
|
||||||
|
See passwd for setting user passwords.
|
||||||
|
|
||||||
|
OPTIONS
|
||||||
|
-h, --help
|
||||||
|
Display this help and exit.
|
||||||
|
|
||||||
|
-c, --comment COMMENT
|
||||||
|
The comment field (or GECOS, historically) for the user. This
|
||||||
|
is typically the full name of the user, although sometimes it
|
||||||
|
includes an e-mail.
|
||||||
|
|
||||||
|
-d, --home-dir HOME_DIR
|
||||||
|
Sets the home directory to HOME_DIR and creates the directory.
|
||||||
|
See -m for move
|
||||||
|
|
||||||
|
-m, --move-home
|
||||||
|
Moves the the user's old home directory into the home directory
|
||||||
|
specified by --home-dir. Has no effect if passed without --home-dir
|
||||||
|
|
||||||
|
-G, --append-groups GROUP[,GROUP, ...]
|
||||||
|
Add this user to GROUP groups. This does not remove the user from
|
||||||
|
any group of which they are already a member.
|
||||||
|
|
||||||
|
-S, --set-groups GROUP[,GROUP, ...]
|
||||||
|
Remove the user from all groups of which they are a part and add
|
||||||
|
them to GROUP groups.
|
||||||
|
|
||||||
|
-g, --gid GID
|
||||||
|
Set the user's primary group id. If the group does not exist,
|
||||||
|
a warning is issued and no changes are applied.
|
||||||
|
|
||||||
|
-l, --login NEW_LOGIN
|
||||||
|
Set the new login name for the user. Must not be in use.
|
||||||
|
|
||||||
|
-s, --shell SHELL
|
||||||
|
Set the user's login shell as SHELL. This must be a full path.
|
||||||
|
|
||||||
|
-u, --uid UID
|
||||||
|
Set the user's user id. If another user's userid is the same as
|
||||||
|
UID, a warning is issued and no changes are applied. Note that
|
||||||
|
changing the value of the user's userid may have unexpected consequences.
|
||||||
|
|
||||||
|
AUTHORS
|
||||||
|
Written by Wesley Hershberger.
|
||||||
|
"#; /* @MANEND */
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args = clap_app!(usermod =>
|
||||||
|
(author: "Wesley Hershberger")
|
||||||
|
(about: "Modify users according to the system's redox_users backend")
|
||||||
|
(@arg LOGIN:
|
||||||
|
+required
|
||||||
|
"Apply modifications to LOGIN")
|
||||||
|
(@arg COMMENT:
|
||||||
|
-c --comment
|
||||||
|
+takes_value
|
||||||
|
"Set LOGIN's description (GECOS field)")
|
||||||
|
(@arg HOME_DIR:
|
||||||
|
-d --("home-dir")
|
||||||
|
+takes_value
|
||||||
|
"Create and set LOGIN's home directory")
|
||||||
|
(@arg MOVE_HOME:
|
||||||
|
-m --("move-home")
|
||||||
|
requires[HOME_DIR]
|
||||||
|
"Move LOGIN's old home to HOME_DIR (see --home-dir) instead of creating it. Requires -d")
|
||||||
|
(@arg APPEND_GROUPS:
|
||||||
|
-G --("append-groups")
|
||||||
|
+takes_value conflicts_with[SET_GROUPS]
|
||||||
|
"Add user to groups specified (comma separated list, see man page)")
|
||||||
|
(@arg SET_GROUPS:
|
||||||
|
-S --("set-groups")
|
||||||
|
+takes_value conflicts_with[APPEND_GROUPS]
|
||||||
|
"Set LOGIN's groups as specified (truncates existing, see man page)")
|
||||||
|
(@arg GID:
|
||||||
|
-g --gid
|
||||||
|
+takes_value
|
||||||
|
"Set LOGIN's primary group id. Group must exist")
|
||||||
|
(@arg NEW_LOGIN:
|
||||||
|
-l --login
|
||||||
|
+takes_value
|
||||||
|
"Set LOGIN's name to NEW_LOGIN")
|
||||||
|
(@arg SHELL:
|
||||||
|
-s --shell
|
||||||
|
+takes_value
|
||||||
|
"Set LOGIN's default login shell")
|
||||||
|
(@arg UID:
|
||||||
|
-u --uid
|
||||||
|
+takes_value
|
||||||
|
"Set LOGIN's user id. See man page for details")
|
||||||
|
).get_matches();
|
||||||
|
|
||||||
|
let login = args.value_of("LOGIN").unwrap();
|
||||||
|
|
||||||
|
//TODO: Does not always need shadowfile access
|
||||||
|
let mut sys_users =
|
||||||
|
AllUsers::authenticator(Config::default().writeable(true)).unwrap_or_exit(1);
|
||||||
|
let mut sys_groups;
|
||||||
|
|
||||||
|
if let Some(new_groups) = args.value_of("SET_GROUPS") {
|
||||||
|
sys_groups = AllGroups::new(Config::default().writeable(true)).unwrap_or_exit(1);
|
||||||
|
sys_groups.remove_user_from_all_groups(login);
|
||||||
|
sys_groups
|
||||||
|
.add_user_to_groups(login, new_groups.split(',').collect())
|
||||||
|
.unwrap_or_exit(1);
|
||||||
|
sys_groups.save().unwrap_or_exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(new_groups) = args.value_of("APPEND_GROUPS") {
|
||||||
|
sys_groups = AllGroups::new(Config::default().writeable(true)).unwrap_or_exit(1);
|
||||||
|
sys_groups
|
||||||
|
.add_user_to_groups(login, new_groups.split(',').collect())
|
||||||
|
.unwrap_or_exit(1);
|
||||||
|
sys_groups.save().unwrap_or_exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let uid = args.value_of("UID").map(|uid| {
|
||||||
|
let uid = uid.parse::<usize>().unwrap_or_exit(1);
|
||||||
|
if let Some(_user) = sys_users.get_by_id(uid) {
|
||||||
|
eprintln!("usermod: userid already in use: {}", uid);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
uid
|
||||||
|
});
|
||||||
|
|
||||||
|
{
|
||||||
|
let user = sys_users.get_mut_by_name(&login).unwrap_or_else(|| {
|
||||||
|
eprintln!("usermod: user \"{}\" not found", login);
|
||||||
|
exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(gecos) = args.value_of("COMMENT") {
|
||||||
|
user.name = gecos.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(new_login) = args.value_of("NEW_LOGIN") {
|
||||||
|
user.user = new_login.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(shell) = args.value_of("SHELL") {
|
||||||
|
user.shell = shell.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(home) = args.value_of("HOME_DIR") {
|
||||||
|
if args.is_present("MOVE_HOME") {
|
||||||
|
rename(&user.home, &home).unwrap_or_exit(1);
|
||||||
|
} else {
|
||||||
|
create_user_dir(user, &home).unwrap_or_exit(1);
|
||||||
|
remove_dir(&user.home).unwrap_or_exit(1);
|
||||||
|
}
|
||||||
|
user.home = home.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(uid) = uid {
|
||||||
|
user.uid = uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(gid) = args.value_of("GID") {
|
||||||
|
sys_groups = AllGroups::new(Config::default()).unwrap_or_exit(1);
|
||||||
|
let gid = gid.parse::<usize>().unwrap_or_exit(1);
|
||||||
|
|
||||||
|
if let Some(_group) = sys_groups.get_by_id(gid) {
|
||||||
|
user.gid = gid;
|
||||||
|
} else {
|
||||||
|
eprintln!("usermod: no group found for id: {}", gid);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sys_users.save().unwrap_or_exit(1);
|
||||||
|
}
|
||||||
-410
@@ -1,410 +0,0 @@
|
|||||||
use super::{
|
|
||||||
arch::*,
|
|
||||||
data::{Map, Stat, StatVfs, StdFsCallMeta, TimeSpec},
|
|
||||||
error::Result,
|
|
||||||
flag::*,
|
|
||||||
number::*,
|
|
||||||
};
|
|
||||||
|
|
||||||
use core::mem;
|
|
||||||
|
|
||||||
/// Close a file
|
|
||||||
pub fn close(fd: usize) -> Result<usize> {
|
|
||||||
unsafe { syscall1(SYS_CLOSE, fd) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the current system time
|
|
||||||
pub fn clock_gettime(clock: usize, tp: &mut TimeSpec) -> Result<usize> {
|
|
||||||
unsafe { syscall2(SYS_CLOCK_GETTIME, clock, tp as *mut TimeSpec as usize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Copy and transform a file descriptor
|
|
||||||
pub fn dup(fd: usize, buf: &[u8]) -> Result<usize> {
|
|
||||||
unsafe { syscall3(SYS_DUP, fd, buf.as_ptr() as usize, buf.len()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Copy and transform a file descriptor
|
|
||||||
pub fn dup2(fd: usize, newfd: usize, buf: &[u8]) -> Result<usize> {
|
|
||||||
unsafe { syscall4(SYS_DUP2, fd, newfd, buf.as_ptr() as usize, buf.len()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Change file permissions
|
|
||||||
pub fn fchmod(fd: usize, mode: u16) -> Result<usize> {
|
|
||||||
unsafe { syscall2(SYS_FCHMOD, fd, mode as usize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Change file ownership
|
|
||||||
pub fn fchown(fd: usize, uid: u32, gid: u32) -> Result<usize> {
|
|
||||||
unsafe { syscall3(SYS_FCHOWN, fd, uid as usize, gid as usize) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Change file descriptor flags
|
|
||||||
pub fn fcntl(fd: usize, cmd: usize, arg: usize) -> Result<usize> {
|
|
||||||
unsafe { syscall3(SYS_FCNTL, fd, cmd, arg) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Map a file into memory, but with the ability to set the address to map into, either as a hint
|
|
||||||
/// or as a requirement of the map.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// `EACCES` - the file descriptor was not open for reading
|
|
||||||
/// `EBADF` - if the file descriptor was invalid
|
|
||||||
/// `ENODEV` - mmapping was not supported
|
|
||||||
/// `EINVAL` - invalid combination of flags
|
|
||||||
/// `EEXIST` - if [`MapFlags::MAP_FIXED`] was set, and the address specified was already in use.
|
|
||||||
///
|
|
||||||
pub unsafe fn fmap(fd: usize, map: &Map) -> Result<usize> {
|
|
||||||
syscall3(
|
|
||||||
SYS_FMAP,
|
|
||||||
fd,
|
|
||||||
map as *const Map as usize,
|
|
||||||
mem::size_of::<Map>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unmap whole (or partial) continous memory-mapped files
|
|
||||||
pub unsafe fn funmap(addr: usize, len: usize) -> Result<usize> {
|
|
||||||
syscall2(SYS_FUNMAP, addr, len)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieve the canonical path of a file
|
|
||||||
pub fn fpath(fd: usize, buf: &mut [u8]) -> Result<usize> {
|
|
||||||
unsafe { syscall3(SYS_FPATH, fd, buf.as_mut_ptr() as usize, buf.len()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a link to a file
|
|
||||||
pub fn flink<T: AsRef<str>>(fd: usize, path: T) -> Result<usize> {
|
|
||||||
let path = path.as_ref();
|
|
||||||
unsafe { syscall3(SYS_FLINK, fd, path.as_ptr() as usize, path.len()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Rename a file
|
|
||||||
pub fn frename<T: AsRef<str>>(fd: usize, path: T) -> Result<usize> {
|
|
||||||
let path = path.as_ref();
|
|
||||||
unsafe { syscall3(SYS_FRENAME, fd, path.as_ptr() as usize, path.len()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get metadata about a file
|
|
||||||
pub fn fstat(fd: usize, stat: &mut Stat) -> Result<usize> {
|
|
||||||
unsafe {
|
|
||||||
syscall3(
|
|
||||||
SYS_FSTAT,
|
|
||||||
fd,
|
|
||||||
stat as *mut Stat as usize,
|
|
||||||
mem::size_of::<Stat>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get metadata about a filesystem
|
|
||||||
pub fn fstatvfs(fd: usize, stat: &mut StatVfs) -> Result<usize> {
|
|
||||||
unsafe {
|
|
||||||
syscall3(
|
|
||||||
SYS_FSTATVFS,
|
|
||||||
fd,
|
|
||||||
stat as *mut StatVfs as usize,
|
|
||||||
mem::size_of::<StatVfs>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sync a file descriptor to its underlying medium
|
|
||||||
pub fn fsync(fd: usize) -> Result<usize> {
|
|
||||||
unsafe { syscall1(SYS_FSYNC, fd) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Truncate or extend a file to a specified length
|
|
||||||
pub fn ftruncate(fd: usize, len: usize) -> Result<usize> {
|
|
||||||
unsafe { syscall2(SYS_FTRUNCATE, fd, len) }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Change modify and/or access times
|
|
||||||
pub fn futimens(fd: usize, times: &[TimeSpec]) -> Result<usize> {
|
|
||||||
unsafe {
|
|
||||||
syscall3(
|
|
||||||
SYS_FUTIMENS,
|
|
||||||
fd,
|
|
||||||
times.as_ptr() as usize,
|
|
||||||
mem::size_of_val(times),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fast userspace mutex
|
|
||||||
pub unsafe fn futex(
|
|
||||||
addr: *mut i32,
|
|
||||||
op: usize,
|
|
||||||
val: i32,
|
|
||||||
val2: usize,
|
|
||||||
addr2: *mut i32,
|
|
||||||
) -> Result<usize> {
|
|
||||||
syscall5(
|
|
||||||
SYS_FUTEX,
|
|
||||||
addr as usize,
|
|
||||||
op,
|
|
||||||
(val as isize) as usize,
|
|
||||||
val2,
|
|
||||||
addr2 as usize,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Seek to `offset` bytes in a file descriptor
|
|
||||||
pub fn lseek(fd: usize, offset: isize, whence: usize) -> Result<usize> {
|
|
||||||
unsafe { syscall3(SYS_LSEEK, fd, offset as usize, whence) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Make a new scheme namespace
|
|
||||||
pub fn mkns(schemes: &[[usize; 2]]) -> Result<usize> {
|
|
||||||
unsafe { syscall2(SYS_MKNS, schemes.as_ptr() as usize, schemes.len()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Change mapping flags
|
|
||||||
pub unsafe fn mprotect(addr: usize, size: usize, flags: MapFlags) -> Result<usize> {
|
|
||||||
syscall3(SYS_MPROTECT, addr, size, flags.bits())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sleep for the time specified in `req`
|
|
||||||
pub fn nanosleep(req: &TimeSpec, rem: &mut TimeSpec) -> Result<usize> {
|
|
||||||
unsafe {
|
|
||||||
syscall2(
|
|
||||||
SYS_NANOSLEEP,
|
|
||||||
req as *const TimeSpec as usize,
|
|
||||||
rem as *mut TimeSpec as usize,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Open a file at a specific path
|
|
||||||
pub fn openat<T: AsRef<str>>(
|
|
||||||
fd: usize,
|
|
||||||
path: T,
|
|
||||||
flags: usize,
|
|
||||||
fcntl_flags: usize,
|
|
||||||
) -> Result<usize> {
|
|
||||||
let path = path.as_ref();
|
|
||||||
unsafe {
|
|
||||||
syscall5(
|
|
||||||
SYS_OPENAT,
|
|
||||||
fd,
|
|
||||||
path.as_ptr() as usize,
|
|
||||||
path.len(),
|
|
||||||
flags,
|
|
||||||
fcntl_flags,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Open a file at a specific path with filter
|
|
||||||
pub fn openat_with_filter<T: AsRef<str>>(
|
|
||||||
fd: usize,
|
|
||||||
path: T,
|
|
||||||
flags: usize,
|
|
||||||
fcntl_flags: usize,
|
|
||||||
euid: u32,
|
|
||||||
egid: u32,
|
|
||||||
) -> Result<usize> {
|
|
||||||
let path = path.as_ref();
|
|
||||||
unsafe {
|
|
||||||
syscall6(
|
|
||||||
SYS_OPENAT_WITH_FILTER,
|
|
||||||
fd,
|
|
||||||
path.as_ptr() as usize,
|
|
||||||
path.len(),
|
|
||||||
flags | fcntl_flags,
|
|
||||||
// NOTE: Short-term solution to allow namespace management.
|
|
||||||
// In the long term, we need to figure out how we should best handle
|
|
||||||
// Unix permissions using capabilities.
|
|
||||||
euid as usize,
|
|
||||||
egid as usize,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove a file at at specific path
|
|
||||||
pub fn unlinkat<T: AsRef<str>>(fd: usize, path: T, flags: usize) -> Result<usize> {
|
|
||||||
let path = path.as_ref();
|
|
||||||
unsafe { syscall4(SYS_UNLINKAT, fd, path.as_ptr() as usize, path.len(), flags) }
|
|
||||||
}
|
|
||||||
/// Remove a file at at specific path with filter
|
|
||||||
pub fn unlinkat_with_filter<T: AsRef<str>>(
|
|
||||||
fd: usize,
|
|
||||||
path: T,
|
|
||||||
flags: usize,
|
|
||||||
euid: u32,
|
|
||||||
egid: u32,
|
|
||||||
) -> Result<usize> {
|
|
||||||
let path = path.as_ref();
|
|
||||||
unsafe {
|
|
||||||
syscall6(
|
|
||||||
SYS_UNLINKAT_WITH_FILTER,
|
|
||||||
fd,
|
|
||||||
path.as_ptr() as usize,
|
|
||||||
path.len(),
|
|
||||||
flags,
|
|
||||||
// NOTE: Short-term solution to allow namespace management.
|
|
||||||
// In the long term, we need to figure out how we should best handle
|
|
||||||
// Unix permissions using capabilities.
|
|
||||||
euid as usize,
|
|
||||||
egid as usize,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read from a file descriptor into a buffer
|
|
||||||
pub fn read(fd: usize, buf: &mut [u8]) -> Result<usize> {
|
|
||||||
unsafe { syscall3(SYS_READ, fd, buf.as_mut_ptr() as usize, buf.len()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write a buffer to a file descriptor
|
|
||||||
///
|
|
||||||
/// The kernel will attempt to write the bytes in `buf` to the file descriptor `fd`, returning
|
|
||||||
/// either an `Err`, explained below, or `Ok(count)` where `count` is the number of bytes which
|
|
||||||
/// were written.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// * `EAGAIN` - the file descriptor was opened with `O_NONBLOCK` and writing would block
|
|
||||||
/// * `EBADF` - the file descriptor is not valid or is not open for writing
|
|
||||||
/// * `EFAULT` - `buf` does not point to the process's addressible memory
|
|
||||||
/// * `EIO` - an I/O error occurred
|
|
||||||
/// * `ENOSPC` - the device containing the file descriptor has no room for data
|
|
||||||
/// * `EPIPE` - the file descriptor refers to a pipe or socket whose reading end is closed
|
|
||||||
pub fn write(fd: usize, buf: &[u8]) -> Result<usize> {
|
|
||||||
unsafe { syscall3(SYS_WRITE, fd, buf.as_ptr() as usize, buf.len()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Yield the process's time slice to the kernel
|
|
||||||
///
|
|
||||||
/// This function will return Ok(0) on success
|
|
||||||
pub fn sched_yield() -> Result<usize> {
|
|
||||||
unsafe { syscall0(SYS_YIELD) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Send a file descriptor `fd`, handled by the scheme providing `receiver_socket`. `flags` is
|
|
||||||
/// currently unused (must be zero), and `arg` is included in the scheme call.
|
|
||||||
///
|
|
||||||
/// The scheme can return an arbitrary value.
|
|
||||||
pub fn sendfd(receiver_socket: usize, fd: usize, flags: usize, arg: u64) -> Result<usize> {
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
|
||||||
unsafe {
|
|
||||||
syscall5(
|
|
||||||
SYS_SENDFD,
|
|
||||||
receiver_socket,
|
|
||||||
fd,
|
|
||||||
flags,
|
|
||||||
arg as u32 as usize,
|
|
||||||
(arg >> 32) as u32 as usize,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
|
||||||
unsafe {
|
|
||||||
syscall4(SYS_SENDFD, receiver_socket, fd, flags, arg as usize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Call {
|
|
||||||
unsafe fn raw_call(
|
|
||||||
&self,
|
|
||||||
payload_ptr: *const u8,
|
|
||||||
len: usize,
|
|
||||||
flags: CallFlags,
|
|
||||||
metadata: &[u64],
|
|
||||||
) -> Result<usize>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Call for usize {
|
|
||||||
unsafe fn raw_call(
|
|
||||||
&self,
|
|
||||||
payload_ptr: *const u8,
|
|
||||||
len: usize,
|
|
||||||
flags: CallFlags,
|
|
||||||
metadata: &[u64],
|
|
||||||
) -> Result<usize> {
|
|
||||||
unsafe {
|
|
||||||
syscall5(
|
|
||||||
SYS_CALL,
|
|
||||||
*self,
|
|
||||||
payload_ptr as usize,
|
|
||||||
len,
|
|
||||||
metadata.len() | flags.bits(),
|
|
||||||
metadata.as_ptr() as usize,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Call for &[usize] {
|
|
||||||
unsafe fn raw_call(
|
|
||||||
&self,
|
|
||||||
payload_ptr: *const u8,
|
|
||||||
len: usize,
|
|
||||||
flags: CallFlags,
|
|
||||||
metadata: &[u64],
|
|
||||||
) -> Result<usize> {
|
|
||||||
let combined_flags = flags | CallFlags::MULTIPLE_FDS;
|
|
||||||
unsafe {
|
|
||||||
syscall6(
|
|
||||||
SYS_CALL,
|
|
||||||
self.as_ptr() as usize,
|
|
||||||
payload_ptr as usize,
|
|
||||||
len,
|
|
||||||
metadata.len() | combined_flags.bits(),
|
|
||||||
metadata.as_ptr() as usize,
|
|
||||||
self.len() * mem::size_of::<usize>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// SYS_CALL interface, read-only variant
|
|
||||||
pub fn call_ro<T: Call>(
|
|
||||||
fd: T,
|
|
||||||
payload: &mut [u8],
|
|
||||||
flags: CallFlags,
|
|
||||||
metadata: &[u64],
|
|
||||||
) -> Result<usize> {
|
|
||||||
unsafe {
|
|
||||||
fd.raw_call(
|
|
||||||
payload.as_mut_ptr(),
|
|
||||||
payload.len(),
|
|
||||||
flags | CallFlags::READ,
|
|
||||||
metadata,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// SYS_CALL interface, write-only variant
|
|
||||||
pub fn call_wo<T: Call>(
|
|
||||||
fd: T,
|
|
||||||
payload: &[u8],
|
|
||||||
flags: CallFlags,
|
|
||||||
metadata: &[u64],
|
|
||||||
) -> Result<usize> {
|
|
||||||
unsafe {
|
|
||||||
fd.raw_call(
|
|
||||||
payload.as_ptr(),
|
|
||||||
payload.len(),
|
|
||||||
flags | CallFlags::WRITE,
|
|
||||||
metadata,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// SYS_CALL interface, read-write variant
|
|
||||||
pub fn call_rw<T: Call>(
|
|
||||||
fd: T,
|
|
||||||
payload: &mut [u8],
|
|
||||||
flags: CallFlags,
|
|
||||||
metadata: &[u64],
|
|
||||||
) -> Result<usize> {
|
|
||||||
unsafe {
|
|
||||||
fd.raw_call(
|
|
||||||
payload.as_mut_ptr(),
|
|
||||||
payload.len(),
|
|
||||||
flags | CallFlags::READ | CallFlags::WRITE,
|
|
||||||
metadata,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn std_fs_call<T: Call>(fd: T, payload: &mut [u8], metadata: &StdFsCallMeta) -> Result<usize> {
|
|
||||||
call_rw(fd, payload, CallFlags::STD_FS, metadata)
|
|
||||||
}
|
|
||||||
-501
@@ -1,501 +0,0 @@
|
|||||||
use core::{
|
|
||||||
mem,
|
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
slice,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::flag::{EventFlags, MapFlags, PtraceFlags, StdFsCallKind};
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Event {
|
|
||||||
pub id: usize,
|
|
||||||
pub flags: EventFlags,
|
|
||||||
pub data: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for Event {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe { slice::from_raw_parts(self as *const Event as *const u8, mem::size_of::<Event>()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for Event {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe { slice::from_raw_parts_mut(self as *mut Event as *mut u8, mem::size_of::<Event>()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct ITimerSpec {
|
|
||||||
pub it_interval: TimeSpec,
|
|
||||||
pub it_value: TimeSpec,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for ITimerSpec {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts(
|
|
||||||
self as *const ITimerSpec as *const u8,
|
|
||||||
mem::size_of::<ITimerSpec>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for ITimerSpec {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(
|
|
||||||
self as *mut ITimerSpec as *mut u8,
|
|
||||||
mem::size_of::<ITimerSpec>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct OldMap {
|
|
||||||
pub offset: usize,
|
|
||||||
pub size: usize,
|
|
||||||
pub flags: MapFlags,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for OldMap {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts(self as *const OldMap as *const u8, mem::size_of::<OldMap>())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for OldMap {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(self as *mut OldMap as *mut u8, mem::size_of::<OldMap>())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Map {
|
|
||||||
/// The offset inside the file that is being mapped.
|
|
||||||
pub offset: usize,
|
|
||||||
|
|
||||||
/// The size of the memory map.
|
|
||||||
pub size: usize,
|
|
||||||
|
|
||||||
/// Contains both prot and map flags.
|
|
||||||
pub flags: MapFlags,
|
|
||||||
|
|
||||||
/// Functions as a hint to where in the virtual address space of the running process, to place
|
|
||||||
/// the memory map. If [`MapFlags::MAP_FIXED`] is set, then this address must be the address to
|
|
||||||
/// map to.
|
|
||||||
pub address: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for Map {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe { slice::from_raw_parts(self as *const Map as *const u8, mem::size_of::<Map>()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for Map {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe { slice::from_raw_parts_mut(self as *mut Map as *mut u8, mem::size_of::<Map>()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Stat {
|
|
||||||
pub st_dev: u64,
|
|
||||||
pub st_ino: u64,
|
|
||||||
pub st_mode: u16,
|
|
||||||
pub st_nlink: u32,
|
|
||||||
pub st_uid: u32,
|
|
||||||
pub st_gid: u32,
|
|
||||||
pub st_size: u64,
|
|
||||||
pub st_blksize: u32,
|
|
||||||
pub st_blocks: u64,
|
|
||||||
pub st_mtime: u64,
|
|
||||||
pub st_mtime_nsec: u32,
|
|
||||||
pub st_atime: u64,
|
|
||||||
pub st_atime_nsec: u32,
|
|
||||||
pub st_ctime: u64,
|
|
||||||
pub st_ctime_nsec: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for Stat {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe { slice::from_raw_parts(self as *const Stat as *const u8, mem::size_of::<Stat>()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for Stat {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe { slice::from_raw_parts_mut(self as *mut Stat as *mut u8, mem::size_of::<Stat>()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct StatVfs {
|
|
||||||
pub f_bsize: u32,
|
|
||||||
pub f_blocks: u64,
|
|
||||||
pub f_bfree: u64,
|
|
||||||
pub f_bavail: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for StatVfs {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts(
|
|
||||||
self as *const StatVfs as *const u8,
|
|
||||||
mem::size_of::<StatVfs>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for StatVfs {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(self as *mut StatVfs as *mut u8, mem::size_of::<StatVfs>())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
|
||||||
#[repr(C, packed)]
|
|
||||||
pub struct StdFsCallMeta {
|
|
||||||
pub kind: u8, // enum StdFsCallKind
|
|
||||||
_rsvd: [u8; 7],
|
|
||||||
pub arg1: u64,
|
|
||||||
pub arg2: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StdFsCallMeta {
|
|
||||||
pub fn new(kind: StdFsCallKind, arg1: u64, arg2: u64) -> Self {
|
|
||||||
Self {
|
|
||||||
kind: kind as u8,
|
|
||||||
_rsvd: [0; 7],
|
|
||||||
arg1,
|
|
||||||
arg2,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for StdFsCallMeta {
|
|
||||||
type Target = [u64];
|
|
||||||
fn deref(&self) -> &[u64] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts(
|
|
||||||
self as *const StdFsCallMeta as *const u64,
|
|
||||||
mem::size_of::<StdFsCallMeta>() / mem::size_of::<u64>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for StdFsCallMeta {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u64] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(
|
|
||||||
self as *mut StdFsCallMeta as *mut u64,
|
|
||||||
mem::size_of::<StdFsCallMeta>() / mem::size_of::<u64>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct TimeSpec {
|
|
||||||
pub tv_sec: i64,
|
|
||||||
pub tv_nsec: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
const NANOS_PER_SEC: u128 = 1_000_000_000;
|
|
||||||
|
|
||||||
impl TimeSpec {
|
|
||||||
pub fn from_nanos(nanos: u128) -> Self {
|
|
||||||
Self {
|
|
||||||
tv_sec: i64::try_from(nanos / NANOS_PER_SEC).unwrap_or(i64::MAX),
|
|
||||||
tv_nsec: (nanos % NANOS_PER_SEC) as i32, // guaranteed to never overflow
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn to_nanos(&self) -> u128 {
|
|
||||||
self.tv_sec as u128 * NANOS_PER_SEC + self.tv_nsec as u128
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for TimeSpec {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts(
|
|
||||||
self as *const TimeSpec as *const u8,
|
|
||||||
mem::size_of::<TimeSpec>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for TimeSpec {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(self as *mut TimeSpec as *mut u8, mem::size_of::<TimeSpec>())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct PtraceEvent {
|
|
||||||
pub cause: PtraceFlags,
|
|
||||||
pub a: usize,
|
|
||||||
pub b: usize,
|
|
||||||
pub c: usize,
|
|
||||||
pub d: usize,
|
|
||||||
pub e: usize,
|
|
||||||
pub f: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for PtraceEvent {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts(
|
|
||||||
self as *const PtraceEvent as *const u8,
|
|
||||||
mem::size_of::<PtraceEvent>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for PtraceEvent {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(
|
|
||||||
self as *mut PtraceEvent as *mut u8,
|
|
||||||
mem::size_of::<PtraceEvent>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! ptrace_event {
|
|
||||||
($cause:expr $(, $a:expr $(, $b:expr $(, $c:expr)?)?)?) => {
|
|
||||||
$crate::data::PtraceEvent {
|
|
||||||
cause: $cause,
|
|
||||||
$(a: $a,
|
|
||||||
$(b: $b,
|
|
||||||
$(c: $c,)?
|
|
||||||
)?
|
|
||||||
)?
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bitflags::bitflags! {
|
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy, Default)]
|
|
||||||
pub struct GrantFlags: usize {
|
|
||||||
const GRANT_READ = 0x0000_0001;
|
|
||||||
const GRANT_WRITE = 0x0000_0002;
|
|
||||||
const GRANT_EXEC = 0x0000_0004;
|
|
||||||
|
|
||||||
const GRANT_SHARED = 0x0000_0008;
|
|
||||||
const GRANT_LAZY = 0x0000_0010;
|
|
||||||
const GRANT_SCHEME = 0x0000_0020;
|
|
||||||
const GRANT_PHYS = 0x0000_0040;
|
|
||||||
const GRANT_PINNED = 0x0000_0080;
|
|
||||||
const GRANT_PHYS_CONTIGUOUS = 0x0000_0100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl GrantFlags {
|
|
||||||
#[deprecated = "use the safe `from_bits_retain` method instead"]
|
|
||||||
pub unsafe fn from_bits_unchecked(bits: usize) -> Self {
|
|
||||||
Self::from_bits_retain(bits)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct GrantDesc {
|
|
||||||
pub base: usize,
|
|
||||||
pub size: usize,
|
|
||||||
pub flags: GrantFlags,
|
|
||||||
pub offset: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for GrantDesc {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts(
|
|
||||||
self as *const GrantDesc as *const u8,
|
|
||||||
mem::size_of::<GrantDesc>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for GrantDesc {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(
|
|
||||||
self as *mut GrantDesc as *mut u8,
|
|
||||||
mem::size_of::<GrantDesc>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct SetSighandlerData {
|
|
||||||
pub user_handler: usize,
|
|
||||||
pub excp_handler: usize,
|
|
||||||
pub thread_control_addr: usize,
|
|
||||||
pub proc_control_addr: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for SetSighandlerData {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe { slice::from_raw_parts(self as *const Self as *const u8, mem::size_of::<Self>()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for SetSighandlerData {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe { slice::from_raw_parts_mut(self as *mut Self as *mut u8, mem::size_of::<Self>()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub use crate::sigabi::*;
|
|
||||||
|
|
||||||
/// UNSTABLE
|
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct ProcSchemeAttrs {
|
|
||||||
pub pid: u32,
|
|
||||||
pub euid: u32,
|
|
||||||
pub egid: u32,
|
|
||||||
pub prio: u32,
|
|
||||||
pub debug_name: [u8; 32],
|
|
||||||
}
|
|
||||||
impl Deref for ProcSchemeAttrs {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe { slice::from_raw_parts(self as *const Self as *const u8, mem::size_of::<Self>()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl DerefMut for ProcSchemeAttrs {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(
|
|
||||||
self as *mut ProcSchemeAttrs as *mut u8,
|
|
||||||
mem::size_of::<ProcSchemeAttrs>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct CtxtStsBuf {
|
|
||||||
pub status: usize,
|
|
||||||
pub excp: crate::Exception,
|
|
||||||
}
|
|
||||||
impl Deref for CtxtStsBuf {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe { slice::from_raw_parts(self as *const Self as *const u8, mem::size_of::<Self>()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl DerefMut for CtxtStsBuf {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(
|
|
||||||
self as *mut CtxtStsBuf as *mut u8,
|
|
||||||
mem::size_of::<CtxtStsBuf>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct NewFdParams {
|
|
||||||
pub offset: u64,
|
|
||||||
pub number: usize,
|
|
||||||
pub flags: usize,
|
|
||||||
pub internal_flags: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
pub enum GlobalSchemes {
|
|
||||||
Debug = 1,
|
|
||||||
Event = 2,
|
|
||||||
Memory = 3,
|
|
||||||
Pipe = 4,
|
|
||||||
Serio = 5,
|
|
||||||
Irq = 6,
|
|
||||||
Time = 7,
|
|
||||||
Sys = 8,
|
|
||||||
Proc = 9,
|
|
||||||
Acpi = 10,
|
|
||||||
Dtb = 11,
|
|
||||||
}
|
|
||||||
impl GlobalSchemes {
|
|
||||||
pub fn try_from_raw(raw: u8) -> Option<Self> {
|
|
||||||
match raw {
|
|
||||||
1 => Some(Self::Debug),
|
|
||||||
2 => Some(Self::Event),
|
|
||||||
3 => Some(Self::Memory),
|
|
||||||
4 => Some(Self::Pipe),
|
|
||||||
5 => Some(Self::Serio),
|
|
||||||
6 => Some(Self::Irq),
|
|
||||||
7 => Some(Self::Time),
|
|
||||||
8 => Some(Self::Sys),
|
|
||||||
9 => Some(Self::Proc),
|
|
||||||
10 => Some(Self::Acpi),
|
|
||||||
11 => Some(Self::Dtb),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn as_str(&self) -> &'static str {
|
|
||||||
match self {
|
|
||||||
Self::Debug => "debug",
|
|
||||||
Self::Event => "event",
|
|
||||||
Self::Memory => "memory",
|
|
||||||
Self::Pipe => "pipe",
|
|
||||||
Self::Serio => "serio",
|
|
||||||
Self::Irq => "irq",
|
|
||||||
Self::Time => "time",
|
|
||||||
Self::Sys => "sys",
|
|
||||||
Self::Proc => "kernel.proc",
|
|
||||||
Self::Acpi => "kernel.acpi",
|
|
||||||
Self::Dtb => "kernel.dtb",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
|
||||||
pub struct KernelSchemeInfo {
|
|
||||||
pub scheme_id: u8,
|
|
||||||
pub fd: usize,
|
|
||||||
}
|
|
||||||
-231
@@ -1,231 +0,0 @@
|
|||||||
use core::{
|
|
||||||
mem::size_of,
|
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
slice,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
error::{Error, Result, EINVAL},
|
|
||||||
ENAMETOOLONG,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
#[repr(packed)]
|
|
||||||
pub struct DirentHeader {
|
|
||||||
pub inode: u64,
|
|
||||||
/// A filesystem-specific opaque value used to uniquely identify directory entries. This value,
|
|
||||||
/// in the last returned entry from a SYS_GETDENTS invocation, shall be passed to the next
|
|
||||||
/// call.
|
|
||||||
pub next_opaque_id: u64,
|
|
||||||
// This struct intentionally does not include a "next" offset field, unlike Linux, to easily
|
|
||||||
// guarantee the iterator will be reasonably deterministic, even if the scheme is adversarial.
|
|
||||||
pub record_len: u16,
|
|
||||||
/// A `DirentKind`.
|
|
||||||
///
|
|
||||||
/// May not be directly available (Unspecified), and if so needs to be looked using fstat.
|
|
||||||
pub kind: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for DirentHeader {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe { slice::from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for DirentHeader {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe { slice::from_raw_parts_mut(self as *mut Self as *mut u8, size_of::<Self>()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: Must match relibc/include/bits/dirent.h
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
#[repr(u8)]
|
|
||||||
pub enum DirentKind {
|
|
||||||
#[default]
|
|
||||||
Unspecified = 0,
|
|
||||||
|
|
||||||
CharDev = 2,
|
|
||||||
Directory = 4,
|
|
||||||
BlockDev = 6,
|
|
||||||
Regular = 8,
|
|
||||||
Symlink = 10,
|
|
||||||
Socket = 12,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DirentKind {
|
|
||||||
// TODO: derive(FromPrimitive)
|
|
||||||
pub fn try_from_raw(raw: u8) -> Option<Self> {
|
|
||||||
Some(match raw {
|
|
||||||
0 => Self::Unspecified,
|
|
||||||
|
|
||||||
2 => Self::CharDev,
|
|
||||||
4 => Self::Directory,
|
|
||||||
6 => Self::BlockDev,
|
|
||||||
8 => Self::Regular,
|
|
||||||
10 => Self::Symlink,
|
|
||||||
12 => Self::Socket,
|
|
||||||
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DirentIter<'a>(&'a [u8]);
|
|
||||||
|
|
||||||
impl<'a> DirentIter<'a> {
|
|
||||||
pub const fn new(buffer: &'a [u8]) -> Self {
|
|
||||||
Self(buffer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Invalid;
|
|
||||||
|
|
||||||
impl<'a> Iterator for DirentIter<'a> {
|
|
||||||
type Item = Result<(&'a DirentHeader, &'a [u8]), Invalid>;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
if self.0.len() < size_of::<DirentHeader>() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let header = unsafe { &*(self.0.as_ptr().cast::<DirentHeader>()) };
|
|
||||||
if self.0.len() < usize::from(header.record_len) {
|
|
||||||
return Some(Err(Invalid));
|
|
||||||
}
|
|
||||||
let (this, remaining) = self.0.split_at(usize::from(header.record_len));
|
|
||||||
self.0 = remaining;
|
|
||||||
|
|
||||||
let name_and_nul = &this[size_of::<DirentHeader>()..];
|
|
||||||
let name = &name_and_nul[..name_and_nul.len() - 1];
|
|
||||||
|
|
||||||
Some(Ok((header, name)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct DirentBuf<B> {
|
|
||||||
buffer: B,
|
|
||||||
|
|
||||||
// Exists in order to allow future extensions to the DirentHeader struct.
|
|
||||||
|
|
||||||
// TODO: Might add an upper bound to protect against cache miss DoS. The kernel currently
|
|
||||||
// forbids any other value than size_of::<DirentHeader>().
|
|
||||||
header_size: u16,
|
|
||||||
|
|
||||||
written: usize,
|
|
||||||
}
|
|
||||||
/// Abstraction between &mut [u8] and the kernel's UserSliceWo.
|
|
||||||
pub trait Buffer<'a>: Sized + 'a {
|
|
||||||
fn empty() -> Self;
|
|
||||||
fn length(&self) -> usize;
|
|
||||||
|
|
||||||
/// Split all of `self` into two disjoint contiguous subbuffers of lengths `index` and `length
|
|
||||||
/// - index` respectively.
|
|
||||||
///
|
|
||||||
/// Returns None if and only if `index > length`.
|
|
||||||
fn split_at(self, index: usize) -> Option<[Self; 2]>;
|
|
||||||
|
|
||||||
/// Copy from `src`, lengths must match exactly.
|
|
||||||
///
|
|
||||||
/// Allowed to overwrite subsequent buffer space, for performance reasons. Can be changed in
|
|
||||||
/// the future if too restrictive.
|
|
||||||
fn copy_from_slice_exact(self, src: &[u8]) -> Result<()>;
|
|
||||||
|
|
||||||
/// Write zeroes to this part of the buffer.
|
|
||||||
///
|
|
||||||
/// Allowed to overwrite subsequent buffer space, for performance reasons. Can be changed in
|
|
||||||
/// the future if too restrictive.
|
|
||||||
fn zero_out(self) -> Result<()>;
|
|
||||||
}
|
|
||||||
impl<'a> Buffer<'a> for &'a mut [u8] {
|
|
||||||
fn empty() -> Self {
|
|
||||||
&mut []
|
|
||||||
}
|
|
||||||
fn length(&self) -> usize {
|
|
||||||
self.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn split_at(self, index: usize) -> Option<[Self; 2]> {
|
|
||||||
self.split_at_mut_checked(index).map(|(a, b)| [a, b])
|
|
||||||
}
|
|
||||||
fn copy_from_slice_exact(self, src: &[u8]) -> Result<()> {
|
|
||||||
self.copy_from_slice(src);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
fn zero_out(self) -> Result<()> {
|
|
||||||
self.fill(0);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct DirEntry<'name> {
|
|
||||||
pub inode: u64,
|
|
||||||
pub next_opaque_id: u64,
|
|
||||||
pub name: &'name str,
|
|
||||||
pub kind: DirentKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, B: Buffer<'a>> DirentBuf<B> {
|
|
||||||
pub fn new(buffer: B, header_size: u16) -> Option<Self> {
|
|
||||||
if usize::from(header_size) < size_of::<DirentHeader>() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(Self {
|
|
||||||
buffer,
|
|
||||||
header_size,
|
|
||||||
written: 0,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
pub fn entry(&mut self, entry: DirEntry<'_>) -> Result<()> {
|
|
||||||
let name16 = u16::try_from(entry.name.len()).map_err(|_| Error::new(EINVAL))?;
|
|
||||||
let record_align = align_of::<*const DirentHeader>();
|
|
||||||
let record_len = self
|
|
||||||
.header_size
|
|
||||||
.checked_add(name16)
|
|
||||||
// XXX: NUL byte. Unfortunately this is probably the only performant way to be
|
|
||||||
// compatible with C.
|
|
||||||
.and_then(|l| l.checked_add(1))
|
|
||||||
// Align length so next header is aligned
|
|
||||||
.and_then(|l| l.checked_next_multiple_of(record_align as u16))
|
|
||||||
.ok_or(Error::new(ENAMETOOLONG))?;
|
|
||||||
|
|
||||||
let [this, remaining] = core::mem::replace(&mut self.buffer, B::empty())
|
|
||||||
.split_at(usize::from(record_len))
|
|
||||||
.ok_or(Error::new(EINVAL))?;
|
|
||||||
|
|
||||||
let [this_header_variable, this_name_and_nul] = this
|
|
||||||
.split_at(usize::from(self.header_size))
|
|
||||||
.expect("already know header_size + ... >= header_size");
|
|
||||||
|
|
||||||
let [this_name, this_name_nul] = this_name_and_nul
|
|
||||||
.split_at(usize::from(name16))
|
|
||||||
.expect("already know name.len() <= name.len() + 1");
|
|
||||||
|
|
||||||
// Every write here is currently sequential, allowing the buffer trait to do optimizations
|
|
||||||
// where subbuffer writes are out-of-bounds (but inside the total buffer).
|
|
||||||
|
|
||||||
let [this_header, this_header_extra] = this_header_variable
|
|
||||||
.split_at(size_of::<DirentHeader>())
|
|
||||||
.expect("already checked header_size <= size_of Header");
|
|
||||||
|
|
||||||
this_header.copy_from_slice_exact(&DirentHeader {
|
|
||||||
record_len,
|
|
||||||
next_opaque_id: entry.next_opaque_id,
|
|
||||||
inode: entry.inode,
|
|
||||||
kind: entry.kind as u8,
|
|
||||||
})?;
|
|
||||||
this_header_extra.zero_out()?;
|
|
||||||
this_name.copy_from_slice_exact(entry.name.as_bytes())?;
|
|
||||||
this_name_nul.zero_out()?;
|
|
||||||
|
|
||||||
self.written += usize::from(record_len);
|
|
||||||
self.buffer = remaining;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn finalize(self) -> usize {
|
|
||||||
self.written
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-327
@@ -1,327 +0,0 @@
|
|||||||
use core::{fmt, result};
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
|
||||||
pub struct Error {
|
|
||||||
pub errno: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Result<T, E = Error> = result::Result<T, E>;
|
|
||||||
|
|
||||||
impl Error {
|
|
||||||
pub fn new(errno: i32) -> Error {
|
|
||||||
Error { errno }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mux(result: Result<usize>) -> usize {
|
|
||||||
match result {
|
|
||||||
Ok(value) => value,
|
|
||||||
Err(error) => -error.errno as usize,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn demux(value: usize) -> Result<usize> {
|
|
||||||
let errno = -(value as i32);
|
|
||||||
if errno >= 1 && errno < STR_ERROR.len() as i32 {
|
|
||||||
Err(Error::new(errno))
|
|
||||||
} else {
|
|
||||||
Ok(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn text(&self) -> &'static str {
|
|
||||||
STR_ERROR
|
|
||||||
.get(self.errno as usize)
|
|
||||||
.map(|&x| x)
|
|
||||||
.unwrap_or("Unknown Error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
|
|
||||||
f.write_str(self.text())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
|
|
||||||
f.write_str(self.text())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
impl std::error::Error for Error {}
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
impl From<Error> for std::io::Error {
|
|
||||||
fn from(value: Error) -> Self {
|
|
||||||
std::io::Error::from_raw_os_error(value.errno)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const EPERM: i32 = 1; /* Operation not permitted */
|
|
||||||
pub const ENOENT: i32 = 2; /* No such file or directory */
|
|
||||||
pub const ESRCH: i32 = 3; /* No such process */
|
|
||||||
pub const EINTR: i32 = 4; /* Interrupted system call */
|
|
||||||
pub const EIO: i32 = 5; /* I/O error */
|
|
||||||
pub const ENXIO: i32 = 6; /* No such device or address */
|
|
||||||
pub const E2BIG: i32 = 7; /* Argument list too long */
|
|
||||||
pub const ENOEXEC: i32 = 8; /* Exec format error */
|
|
||||||
pub const EBADF: i32 = 9; /* Bad file number */
|
|
||||||
pub const ECHILD: i32 = 10; /* No child processes */
|
|
||||||
pub const EAGAIN: i32 = 11; /* Try again */
|
|
||||||
pub const ENOMEM: i32 = 12; /* Out of memory */
|
|
||||||
pub const EACCES: i32 = 13; /* Permission denied */
|
|
||||||
pub const EFAULT: i32 = 14; /* Bad address */
|
|
||||||
pub const ENOTBLK: i32 = 15; /* Block device required */
|
|
||||||
pub const EBUSY: i32 = 16; /* Device or resource busy */
|
|
||||||
pub const EEXIST: i32 = 17; /* File exists */
|
|
||||||
pub const EXDEV: i32 = 18; /* Cross-device link */
|
|
||||||
pub const ENODEV: i32 = 19; /* No such device */
|
|
||||||
pub const ENOTDIR: i32 = 20; /* Not a directory */
|
|
||||||
pub const EISDIR: i32 = 21; /* Is a directory */
|
|
||||||
pub const EINVAL: i32 = 22; /* Invalid argument */
|
|
||||||
pub const ENFILE: i32 = 23; /* File table overflow */
|
|
||||||
pub const EMFILE: i32 = 24; /* Too many open files */
|
|
||||||
pub const ENOTTY: i32 = 25; /* Not a typewriter */
|
|
||||||
pub const ETXTBSY: i32 = 26; /* Text file busy */
|
|
||||||
pub const EFBIG: i32 = 27; /* File too large */
|
|
||||||
pub const ENOSPC: i32 = 28; /* No space left on device */
|
|
||||||
pub const ESPIPE: i32 = 29; /* Illegal seek */
|
|
||||||
pub const EROFS: i32 = 30; /* Read-only file system */
|
|
||||||
pub const EMLINK: i32 = 31; /* Too many links */
|
|
||||||
pub const EPIPE: i32 = 32; /* Broken pipe */
|
|
||||||
pub const EDOM: i32 = 33; /* Math argument out of domain of func */
|
|
||||||
pub const ERANGE: i32 = 34; /* Math result not representable */
|
|
||||||
pub const EDEADLK: i32 = 35; /* Resource deadlock would occur */
|
|
||||||
pub const ENAMETOOLONG: i32 = 36; /* File name too long */
|
|
||||||
pub const ENOLCK: i32 = 37; /* No record locks available */
|
|
||||||
pub const ENOSYS: i32 = 38; /* Function not implemented */
|
|
||||||
pub const ENOTEMPTY: i32 = 39; /* Directory not empty */
|
|
||||||
pub const ELOOP: i32 = 40; /* Too many symbolic links encountered */
|
|
||||||
pub const EWOULDBLOCK: i32 = 41; /* Operation would block */
|
|
||||||
pub const ENOMSG: i32 = 42; /* No message of desired type */
|
|
||||||
pub const EIDRM: i32 = 43; /* Identifier removed */
|
|
||||||
pub const ECHRNG: i32 = 44; /* Channel number out of range */
|
|
||||||
pub const EL2NSYNC: i32 = 45; /* Level 2 not synchronized */
|
|
||||||
pub const EL3HLT: i32 = 46; /* Level 3 halted */
|
|
||||||
pub const EL3RST: i32 = 47; /* Level 3 reset */
|
|
||||||
pub const ELNRNG: i32 = 48; /* Link number out of range */
|
|
||||||
pub const EUNATCH: i32 = 49; /* Protocol driver not attached */
|
|
||||||
pub const ENOCSI: i32 = 50; /* No CSI structure available */
|
|
||||||
pub const EL2HLT: i32 = 51; /* Level 2 halted */
|
|
||||||
pub const EBADE: i32 = 52; /* Invalid exchange */
|
|
||||||
pub const EBADR: i32 = 53; /* Invalid request descriptor */
|
|
||||||
pub const EXFULL: i32 = 54; /* Exchange full */
|
|
||||||
pub const ENOANO: i32 = 55; /* No anode */
|
|
||||||
pub const EBADRQC: i32 = 56; /* Invalid request code */
|
|
||||||
pub const EBADSLT: i32 = 57; /* Invalid slot */
|
|
||||||
pub const EDEADLOCK: i32 = 58; /* Resource deadlock would occur */
|
|
||||||
pub const EBFONT: i32 = 59; /* Bad font file format */
|
|
||||||
pub const ENOSTR: i32 = 60; /* Device not a stream */
|
|
||||||
pub const ENODATA: i32 = 61; /* No data available */
|
|
||||||
pub const ETIME: i32 = 62; /* Timer expired */
|
|
||||||
pub const ENOSR: i32 = 63; /* Out of streams resources */
|
|
||||||
pub const ENONET: i32 = 64; /* Machine is not on the network */
|
|
||||||
pub const ENOPKG: i32 = 65; /* Package not installed */
|
|
||||||
pub const EREMOTE: i32 = 66; /* Object is remote */
|
|
||||||
pub const ENOLINK: i32 = 67; /* Link has been severed */
|
|
||||||
pub const EADV: i32 = 68; /* Advertise error */
|
|
||||||
pub const ESRMNT: i32 = 69; /* Srmount error */
|
|
||||||
pub const ECOMM: i32 = 70; /* Communication error on send */
|
|
||||||
pub const EPROTO: i32 = 71; /* Protocol error */
|
|
||||||
pub const EMULTIHOP: i32 = 72; /* Multihop attempted */
|
|
||||||
pub const EDOTDOT: i32 = 73; /* RFS specific error */
|
|
||||||
pub const EBADMSG: i32 = 74; /* Not a data message */
|
|
||||||
pub const EOVERFLOW: i32 = 75; /* Value too large for defined data type */
|
|
||||||
pub const ENOTUNIQ: i32 = 76; /* Name not unique on network */
|
|
||||||
pub const EBADFD: i32 = 77; /* File descriptor in bad state */
|
|
||||||
pub const EREMCHG: i32 = 78; /* Remote address changed */
|
|
||||||
pub const ELIBACC: i32 = 79; /* Can not access a needed shared library */
|
|
||||||
pub const ELIBBAD: i32 = 80; /* Accessing a corrupted shared library */
|
|
||||||
pub const ELIBSCN: i32 = 81; /* .lib section in a.out corrupted */
|
|
||||||
pub const ELIBMAX: i32 = 82; /* Attempting to link in too many shared libraries */
|
|
||||||
pub const ELIBEXEC: i32 = 83; /* Cannot exec a shared library directly */
|
|
||||||
pub const EILSEQ: i32 = 84; /* Illegal byte sequence */
|
|
||||||
pub const ERESTART: i32 = 85; /* Interrupted system call should be restarted */
|
|
||||||
pub const ESTRPIPE: i32 = 86; /* Streams pipe error */
|
|
||||||
pub const EUSERS: i32 = 87; /* Too many users */
|
|
||||||
pub const ENOTSOCK: i32 = 88; /* Socket operation on non-socket */
|
|
||||||
pub const EDESTADDRREQ: i32 = 89; /* Destination address required */
|
|
||||||
pub const EMSGSIZE: i32 = 90; /* Message too long */
|
|
||||||
pub const EPROTOTYPE: i32 = 91; /* Protocol wrong type for socket */
|
|
||||||
pub const ENOPROTOOPT: i32 = 92; /* Protocol not available */
|
|
||||||
pub const EPROTONOSUPPORT: i32 = 93; /* Protocol not supported */
|
|
||||||
pub const ESOCKTNOSUPPORT: i32 = 94; /* Socket type not supported */
|
|
||||||
pub const EOPNOTSUPP: i32 = 95; /* Operation not supported on transport endpoint */
|
|
||||||
pub const EPFNOSUPPORT: i32 = 96; /* Protocol family not supported */
|
|
||||||
pub const EAFNOSUPPORT: i32 = 97; /* Address family not supported by protocol */
|
|
||||||
pub const EADDRINUSE: i32 = 98; /* Address already in use */
|
|
||||||
pub const EADDRNOTAVAIL: i32 = 99; /* Cannot assign requested address */
|
|
||||||
pub const ENETDOWN: i32 = 100; /* Network is down */
|
|
||||||
pub const ENETUNREACH: i32 = 101; /* Network is unreachable */
|
|
||||||
pub const ENETRESET: i32 = 102; /* Network dropped connection because of reset */
|
|
||||||
pub const ECONNABORTED: i32 = 103; /* Software caused connection abort */
|
|
||||||
pub const ECONNRESET: i32 = 104; /* Connection reset by peer */
|
|
||||||
pub const ENOBUFS: i32 = 105; /* No buffer space available */
|
|
||||||
pub const EISCONN: i32 = 106; /* Transport endpoint is already connected */
|
|
||||||
pub const ENOTCONN: i32 = 107; /* Transport endpoint is not connected */
|
|
||||||
pub const ESHUTDOWN: i32 = 108; /* Cannot send after transport endpoint shutdown */
|
|
||||||
pub const ETOOMANYREFS: i32 = 109; /* Too many references: cannot splice */
|
|
||||||
pub const ETIMEDOUT: i32 = 110; /* Connection timed out */
|
|
||||||
pub const ECONNREFUSED: i32 = 111; /* Connection refused */
|
|
||||||
pub const EHOSTDOWN: i32 = 112; /* Host is down */
|
|
||||||
pub const EHOSTUNREACH: i32 = 113; /* No route to host */
|
|
||||||
pub const EALREADY: i32 = 114; /* Operation already in progress */
|
|
||||||
pub const EINPROGRESS: i32 = 115; /* Operation now in progress */
|
|
||||||
pub const ESTALE: i32 = 116; /* Stale NFS file handle */
|
|
||||||
pub const EUCLEAN: i32 = 117; /* Structure needs cleaning */
|
|
||||||
pub const ENOTNAM: i32 = 118; /* Not a XENIX named type file */
|
|
||||||
pub const ENAVAIL: i32 = 119; /* No XENIX semaphores available */
|
|
||||||
pub const EISNAM: i32 = 120; /* Is a named type file */
|
|
||||||
pub const EREMOTEIO: i32 = 121; /* Remote I/O error */
|
|
||||||
pub const EDQUOT: i32 = 122; /* Quota exceeded */
|
|
||||||
pub const ENOMEDIUM: i32 = 123; /* No medium found */
|
|
||||||
pub const EMEDIUMTYPE: i32 = 124; /* Wrong medium type */
|
|
||||||
pub const ECANCELED: i32 = 125; /* Operation Canceled */
|
|
||||||
pub const ENOKEY: i32 = 126; /* Required key not available */
|
|
||||||
pub const EKEYEXPIRED: i32 = 127; /* Key has expired */
|
|
||||||
pub const EKEYREVOKED: i32 = 128; /* Key has been revoked */
|
|
||||||
pub const EKEYREJECTED: i32 = 129; /* Key was rejected by service */
|
|
||||||
pub const EOWNERDEAD: i32 = 130; /* Owner died */
|
|
||||||
pub const ENOTRECOVERABLE: i32 = 131; /* State not recoverable */
|
|
||||||
pub const ERSVD: i32 = 132; /* Reserved (formerly "scheme-kernel message code") */
|
|
||||||
|
|
||||||
pub static STR_ERROR: [&'static str; 133] = [
|
|
||||||
"Success",
|
|
||||||
"Operation not permitted",
|
|
||||||
"No such file or directory",
|
|
||||||
"No such process",
|
|
||||||
"Interrupted system call",
|
|
||||||
"I/O error",
|
|
||||||
"No such device or address",
|
|
||||||
"Argument list too long",
|
|
||||||
"Exec format error",
|
|
||||||
"Bad file number",
|
|
||||||
"No child processes",
|
|
||||||
"Try again",
|
|
||||||
"Out of memory",
|
|
||||||
"Permission denied",
|
|
||||||
"Bad address",
|
|
||||||
"Block device required",
|
|
||||||
"Device or resource busy",
|
|
||||||
"File exists",
|
|
||||||
"Cross-device link",
|
|
||||||
"No such device",
|
|
||||||
"Not a directory",
|
|
||||||
"Is a directory",
|
|
||||||
"Invalid argument",
|
|
||||||
"File table overflow",
|
|
||||||
"Too many open files",
|
|
||||||
"Not a typewriter",
|
|
||||||
"Text file busy",
|
|
||||||
"File too large",
|
|
||||||
"No space left on device",
|
|
||||||
"Illegal seek",
|
|
||||||
"Read-only file system",
|
|
||||||
"Too many links",
|
|
||||||
"Broken pipe",
|
|
||||||
"Math argument out of domain of func",
|
|
||||||
"Math result not representable",
|
|
||||||
"Resource deadlock would occur",
|
|
||||||
"File name too long",
|
|
||||||
"No record locks available",
|
|
||||||
"Function not implemented",
|
|
||||||
"Directory not empty",
|
|
||||||
"Too many symbolic links encountered",
|
|
||||||
"Operation would block",
|
|
||||||
"No message of desired type",
|
|
||||||
"Identifier removed",
|
|
||||||
"Channel number out of range",
|
|
||||||
"Level 2 not synchronized",
|
|
||||||
"Level 3 halted",
|
|
||||||
"Level 3 reset",
|
|
||||||
"Link number out of range",
|
|
||||||
"Protocol driver not attached",
|
|
||||||
"No CSI structure available",
|
|
||||||
"Level 2 halted",
|
|
||||||
"Invalid exchange",
|
|
||||||
"Invalid request descriptor",
|
|
||||||
"Exchange full",
|
|
||||||
"No anode",
|
|
||||||
"Invalid request code",
|
|
||||||
"Invalid slot",
|
|
||||||
"Resource deadlock would occur",
|
|
||||||
"Bad font file format",
|
|
||||||
"Device not a stream",
|
|
||||||
"No data available",
|
|
||||||
"Timer expired",
|
|
||||||
"Out of streams resources",
|
|
||||||
"Machine is not on the network",
|
|
||||||
"Package not installed",
|
|
||||||
"Object is remote",
|
|
||||||
"Link has been severed",
|
|
||||||
"Advertise error",
|
|
||||||
"Srmount error",
|
|
||||||
"Communication error on send",
|
|
||||||
"Protocol error",
|
|
||||||
"Multihop attempted",
|
|
||||||
"RFS specific error",
|
|
||||||
"Not a data message",
|
|
||||||
"Value too large for defined data type",
|
|
||||||
"Name not unique on network",
|
|
||||||
"File descriptor in bad state",
|
|
||||||
"Remote address changed",
|
|
||||||
"Can not access a needed shared library",
|
|
||||||
"Accessing a corrupted shared library",
|
|
||||||
".lib section in a.out corrupted",
|
|
||||||
"Attempting to link in too many shared libraries",
|
|
||||||
"Cannot exec a shared library directly",
|
|
||||||
"Illegal byte sequence",
|
|
||||||
"Interrupted system call should be restarted",
|
|
||||||
"Streams pipe error",
|
|
||||||
"Too many users",
|
|
||||||
"Socket operation on non-socket",
|
|
||||||
"Destination address required",
|
|
||||||
"Message too long",
|
|
||||||
"Protocol wrong type for socket",
|
|
||||||
"Protocol not available",
|
|
||||||
"Protocol not supported",
|
|
||||||
"Socket type not supported",
|
|
||||||
"Operation not supported on transport endpoint",
|
|
||||||
"Protocol family not supported",
|
|
||||||
"Address family not supported by protocol",
|
|
||||||
"Address already in use",
|
|
||||||
"Cannot assign requested address",
|
|
||||||
"Network is down",
|
|
||||||
"Network is unreachable",
|
|
||||||
"Network dropped connection because of reset",
|
|
||||||
"Software caused connection abort",
|
|
||||||
"Connection reset by peer",
|
|
||||||
"No buffer space available",
|
|
||||||
"Transport endpoint is already connected",
|
|
||||||
"Transport endpoint is not connected",
|
|
||||||
"Cannot send after transport endpoint shutdown",
|
|
||||||
"Too many references: cannot splice",
|
|
||||||
"Connection timed out",
|
|
||||||
"Connection refused",
|
|
||||||
"Host is down",
|
|
||||||
"No route to host",
|
|
||||||
"Operation already in progress",
|
|
||||||
"Operation now in progress",
|
|
||||||
"Stale NFS file handle",
|
|
||||||
"Structure needs cleaning",
|
|
||||||
"Not a XENIX named type file",
|
|
||||||
"No XENIX semaphores available",
|
|
||||||
"Is a named type file",
|
|
||||||
"Remote I/O error",
|
|
||||||
"Quota exceeded",
|
|
||||||
"No medium found",
|
|
||||||
"Wrong medium type",
|
|
||||||
"Operation Canceled",
|
|
||||||
"Required key not available",
|
|
||||||
"Key has expired",
|
|
||||||
"Key has been revoked",
|
|
||||||
"Key was rejected by service",
|
|
||||||
"Owner died",
|
|
||||||
"State not recoverable",
|
|
||||||
"Reserved (formerly scheme-kernel message code)",
|
|
||||||
];
|
|
||||||
-567
@@ -1,567 +0,0 @@
|
|||||||
use bitflags::bitflags as inner_bitflags;
|
|
||||||
use core::{mem, ops::Deref, slice};
|
|
||||||
|
|
||||||
macro_rules! bitflags {
|
|
||||||
(
|
|
||||||
$(#[$outer:meta])*
|
|
||||||
pub struct $BitFlags:ident: $T:ty {
|
|
||||||
$(
|
|
||||||
$(#[$inner:ident $($args:tt)*])*
|
|
||||||
const $Flag:ident = $value:expr;
|
|
||||||
)+
|
|
||||||
}
|
|
||||||
) => {
|
|
||||||
// First, use the inner bitflags
|
|
||||||
inner_bitflags! {
|
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy, Default)]
|
|
||||||
$(#[$outer])*
|
|
||||||
pub struct $BitFlags: $T {
|
|
||||||
$(
|
|
||||||
$(#[$inner $($args)*])*
|
|
||||||
const $Flag = $value;
|
|
||||||
)+
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl $BitFlags {
|
|
||||||
#[deprecated = "use the safe `from_bits_retain` method instead"]
|
|
||||||
pub unsafe fn from_bits_unchecked(bits: $T) -> Self {
|
|
||||||
Self::from_bits_retain(bits)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Secondly, re-export all inner constants
|
|
||||||
// (`pub use self::Struct::*` doesn't work)
|
|
||||||
$(
|
|
||||||
$(#[$inner $($args)*])*
|
|
||||||
pub const $Flag: $BitFlags = $BitFlags::$Flag;
|
|
||||||
)+
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const CLOCK_REALTIME: usize = 1;
|
|
||||||
pub const CLOCK_MONOTONIC: usize = 4;
|
|
||||||
|
|
||||||
bitflags! {
|
|
||||||
pub struct EventFlags: usize {
|
|
||||||
const EVENT_NONE = 0;
|
|
||||||
const EVENT_READ = 1;
|
|
||||||
const EVENT_WRITE = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const F_DUPFD: usize = 0;
|
|
||||||
pub const F_GETFD: usize = 1;
|
|
||||||
pub const F_SETFD: usize = 2;
|
|
||||||
pub const F_GETFL: usize = 3;
|
|
||||||
pub const F_SETFL: usize = 4;
|
|
||||||
pub const F_DUPFD_CLOEXEC: usize = 1030;
|
|
||||||
|
|
||||||
pub const FUTEX_WAIT: usize = 0;
|
|
||||||
pub const FUTEX_WAKE: usize = 1;
|
|
||||||
pub const FUTEX_REQUEUE: usize = 2;
|
|
||||||
pub const FUTEX_WAIT64: usize = 3;
|
|
||||||
|
|
||||||
// TODO: Split SendFdFlags into caller flags and flags that the scheme receives?
|
|
||||||
bitflags::bitflags! {
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct SendFdFlags: usize {
|
|
||||||
/// If set, the kernel will enforce that the file descriptors are exclusively owned.
|
|
||||||
///
|
|
||||||
/// That is, there will no longer exist any other reference to those FDs when removed from
|
|
||||||
/// the file table (sendfd always removes the FDs from the file table, but without this
|
|
||||||
/// flag, it can be retained by SYS_DUPing them first).
|
|
||||||
const EXCLUSIVE = 1;
|
|
||||||
|
|
||||||
/// If set, the file descriptors will be cloned and *not* removed from the sender's file table.
|
|
||||||
/// By default, `SYS_SENDFD` moves the file descriptors, removing them from the sender.
|
|
||||||
const CLONE = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bitflags::bitflags! {
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct FobtainFdFlags: usize {
|
|
||||||
/// If set, the SYS_CALL payload specifies the destination file descriptor slots, otherwise the lowest
|
|
||||||
/// available slots will be selected, and placed in the usize pointed to by SYS_CALL
|
|
||||||
/// payload.
|
|
||||||
const MANUAL_FD = 1;
|
|
||||||
|
|
||||||
/// If set, the file descriptors received are guaranteed to be exclusively owned (by the file
|
|
||||||
/// table the obtainer is running in).
|
|
||||||
const EXCLUSIVE = 2;
|
|
||||||
|
|
||||||
/// If set, the file descriptors received will be placed into the *upper* file table.
|
|
||||||
const UPPER_TBL = 4;
|
|
||||||
|
|
||||||
/// If set, the received file descriptors are marked as close-on-exec.
|
|
||||||
const CLOEXEC = 8;
|
|
||||||
|
|
||||||
// No, cloexec won't be stored in the kernel in the future, when the stable ABI is moved to
|
|
||||||
// relibc, so no flag for that!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bitflags::bitflags! {
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct RecvFdFlags: usize {
|
|
||||||
/// If set, the SYS_CALL payload specifies the destination file descriptor slots, otherwise the lowest
|
|
||||||
/// available slots will be selected, and placed in the usize pointed to by SYS_CALL
|
|
||||||
/// payload.
|
|
||||||
const MANUAL_FD = 1;
|
|
||||||
|
|
||||||
/// If set, the file descriptors received will be placed into the *upper* file table.
|
|
||||||
const UPPER_TBL = 2;
|
|
||||||
|
|
||||||
/// If set, the received file descriptors are marked as close-on-exec.
|
|
||||||
const CLOEXEC = 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bitflags::bitflags! {
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct FmoveFdFlags: usize {
|
|
||||||
/// If set, the kernel will enforce that the file descriptors are exclusively owned.
|
|
||||||
///
|
|
||||||
/// That is, there will no longer exist any other reference to those FDs when removed from
|
|
||||||
/// the file table (SYS_CALL always removes the FDs from the file table, but without this
|
|
||||||
/// flag, it can be retained by SYS_DUPing them first).
|
|
||||||
const EXCLUSIVE = 1;
|
|
||||||
|
|
||||||
/// If set, the file descriptors will be cloned and *not* removed from the sender's file table.
|
|
||||||
/// By default, sendfd moves the file descriptors, removing them from the sender.
|
|
||||||
const CLONE = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bitflags! {
|
|
||||||
pub struct MapFlags: usize {
|
|
||||||
// TODO: Downgrade PROT_NONE to global constant? (bitflags specifically states zero flags
|
|
||||||
// can cause buggy behavior).
|
|
||||||
const PROT_NONE = 0x0000_0000;
|
|
||||||
|
|
||||||
const PROT_EXEC = 0x0001_0000;
|
|
||||||
const PROT_WRITE = 0x0002_0000;
|
|
||||||
const PROT_READ = 0x0004_0000;
|
|
||||||
|
|
||||||
const MAP_SHARED = 0x0001;
|
|
||||||
const MAP_PRIVATE = 0x0002;
|
|
||||||
|
|
||||||
const MAP_FIXED = 0x0004;
|
|
||||||
const MAP_FIXED_NOREPLACE = 0x000C;
|
|
||||||
|
|
||||||
/// For *userspace-backed mmaps*, return from the mmap call before all pages have been
|
|
||||||
/// provided by the scheme. This requires the scheme to be trusted, as the current context
|
|
||||||
/// can block indefinitely, if the scheme does not respond to the page fault handler's
|
|
||||||
/// request, as it tries to map the page by requesting it from the scheme.
|
|
||||||
///
|
|
||||||
/// In some cases however, such as the program loader, the data needs to be trusted as much
|
|
||||||
/// with or without MAP_LAZY, and if so, mapping lazily will not cause insecureness by
|
|
||||||
/// itself.
|
|
||||||
///
|
|
||||||
/// For kernel-backed mmaps, this flag has no effect at all. It is unspecified whether
|
|
||||||
/// kernel mmaps are lazy or not.
|
|
||||||
const MAP_LAZY = 0x0010;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bitflags! {
|
|
||||||
pub struct MunmapFlags: usize {
|
|
||||||
/// Indicates whether the funmap call must implicitly do an msync, for the changes to
|
|
||||||
/// become visible later.
|
|
||||||
///
|
|
||||||
/// This flag will currently be set if and only if MAP_SHARED | PROT_WRITE are set.
|
|
||||||
const NEEDS_SYNC = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_TYPE: u16 = 0xF000;
|
|
||||||
pub const MODE_DIR: u16 = 0x4000;
|
|
||||||
pub const MODE_FILE: u16 = 0x8000;
|
|
||||||
pub const MODE_SYMLINK: u16 = 0xA000;
|
|
||||||
pub const MODE_FIFO: u16 = 0x1000;
|
|
||||||
pub const MODE_CHR: u16 = 0x2000;
|
|
||||||
pub const MODE_SOCK: u16 = 0xC000;
|
|
||||||
|
|
||||||
pub const MODE_PERM: u16 = 0x0FFF;
|
|
||||||
pub const MODE_SETUID: u16 = 0o4000;
|
|
||||||
pub const MODE_SETGID: u16 = 0o2000;
|
|
||||||
|
|
||||||
pub const O_RDONLY: usize = 0x0001_0000;
|
|
||||||
pub const O_WRONLY: usize = 0x0002_0000;
|
|
||||||
pub const O_RDWR: usize = 0x0003_0000;
|
|
||||||
pub const O_NONBLOCK: usize = 0x0004_0000;
|
|
||||||
pub const O_APPEND: usize = 0x0008_0000;
|
|
||||||
pub const O_SHLOCK: usize = 0x0010_0000;
|
|
||||||
pub const O_EXLOCK: usize = 0x0020_0000;
|
|
||||||
pub const O_ASYNC: usize = 0x0040_0000;
|
|
||||||
pub const O_FSYNC: usize = 0x0080_0000;
|
|
||||||
pub const O_CLOEXEC: usize = 0x0100_0000;
|
|
||||||
pub const O_CREAT: usize = 0x0200_0000;
|
|
||||||
pub const O_TRUNC: usize = 0x0400_0000;
|
|
||||||
pub const O_EXCL: usize = 0x0800_0000;
|
|
||||||
pub const O_DIRECTORY: usize = 0x1000_0000;
|
|
||||||
pub const O_STAT: usize = 0x2000_0000;
|
|
||||||
pub const O_SYMLINK: usize = 0x4000_0000;
|
|
||||||
pub const O_NOFOLLOW: usize = 0x8000_0000;
|
|
||||||
pub const O_ACCMODE: usize = O_RDONLY | O_WRONLY | O_RDWR;
|
|
||||||
pub const O_FCNTL_MASK: usize = O_NONBLOCK | O_APPEND | O_ASYNC | O_FSYNC;
|
|
||||||
|
|
||||||
/// Remove directory instead of unlinking file.
|
|
||||||
pub const AT_REMOVEDIR: usize = 0x200;
|
|
||||||
|
|
||||||
// The top 48 bits of PTRACE_* are reserved, for now
|
|
||||||
|
|
||||||
// NOT ABI STABLE!
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
|
||||||
#[repr(usize)]
|
|
||||||
pub enum ContextStatus {
|
|
||||||
Runnable,
|
|
||||||
Blocked,
|
|
||||||
NotYetStarted,
|
|
||||||
Dead,
|
|
||||||
ForceKilled,
|
|
||||||
Stopped,
|
|
||||||
UnhandledExcp,
|
|
||||||
#[default]
|
|
||||||
Other, // reserved
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
||||||
#[repr(usize)]
|
|
||||||
pub enum ContextVerb {
|
|
||||||
Stop = 1,
|
|
||||||
Unstop = 2,
|
|
||||||
Interrupt = 3,
|
|
||||||
ForceKill = usize::MAX,
|
|
||||||
}
|
|
||||||
impl ContextVerb {
|
|
||||||
pub fn try_from_raw(raw: usize) -> Option<Self> {
|
|
||||||
Some(match raw {
|
|
||||||
1 => Self::Stop,
|
|
||||||
2 => Self::Unstop,
|
|
||||||
3 => Self::Interrupt,
|
|
||||||
usize::MAX => Self::ForceKill,
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOT ABI STABLE!
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
||||||
#[repr(u8)]
|
|
||||||
pub enum AddrSpaceVerb {
|
|
||||||
MmapMin = 255,
|
|
||||||
}
|
|
||||||
impl AddrSpaceVerb {
|
|
||||||
pub fn try_from_raw(verb: u8) -> Option<Self> {
|
|
||||||
Some(match verb {
|
|
||||||
255 => Self::MmapMin,
|
|
||||||
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOT ABI STABLE!
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
||||||
#[repr(u8)]
|
|
||||||
pub enum ProcSchemeVerb {
|
|
||||||
RegsInt = 250,
|
|
||||||
RegsFloat = 251,
|
|
||||||
RegsEnv = 252,
|
|
||||||
SchedAffinity = 253,
|
|
||||||
Start = 254,
|
|
||||||
Iopl = 255,
|
|
||||||
}
|
|
||||||
impl ProcSchemeVerb {
|
|
||||||
pub fn try_from_raw(verb: u8) -> Option<Self> {
|
|
||||||
Some(match verb {
|
|
||||||
250 => Self::RegsInt,
|
|
||||||
251 => Self::RegsFloat,
|
|
||||||
252 => Self::RegsEnv,
|
|
||||||
253 => Self::SchedAffinity,
|
|
||||||
254 => Self::Start,
|
|
||||||
255 => Self::Iopl,
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
||||||
pub enum FileTableVerb {
|
|
||||||
Close = 1,
|
|
||||||
Dup2 = 2,
|
|
||||||
CloseCloExec = 3,
|
|
||||||
}
|
|
||||||
impl FileTableVerb {
|
|
||||||
pub fn try_from_raw(value: u8) -> Option<Self> {
|
|
||||||
Some(match value {
|
|
||||||
1 => Self::Close,
|
|
||||||
2 => Self::Dup2,
|
|
||||||
3 => Self::CloseCloExec,
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOT ABI-STABLE!
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
||||||
#[repr(u64)]
|
|
||||||
pub enum AcpiVerb {
|
|
||||||
// copies the rsdt/xsdt to the payload buffer (the number of bytes that fit), and returns the
|
|
||||||
// rsdt/xsdt length regardless
|
|
||||||
ReadRxsdt = 1,
|
|
||||||
// no payload, just returns 0 or 1
|
|
||||||
CheckShutdown = 2,
|
|
||||||
/// Red Bear OS extension (Phase I): acpid requests the kernel
|
|
||||||
/// enter s2idle (Modern Standby / S0ix). The kernel sets
|
|
||||||
/// `S2IDLE_REQUESTED`; the idle path calls `mwait_loop()`. Read
|
|
||||||
/// payload (1 byte) returns the *previous* value of the flag.
|
|
||||||
/// Write payload is opaque (ignored by current kernel).
|
|
||||||
/// Mirrors Linux 7.1 `s2idle_enter()` in
|
|
||||||
/// `kernel/power/suspend.c:91`. Hardware-agnostic — works on
|
|
||||||
/// any platform with Modern Standby firmware (Dell, HP, Lenovo,
|
|
||||||
/// LG Gram, etc.), not just LG Gram.
|
|
||||||
EnterS2Idle = 3,
|
|
||||||
/// Red Bear OS extension (Phase I): acpid signals s2idle
|
|
||||||
/// exit. Kernel clears `S2IDLE_REQUESTED`. Read payload (1
|
|
||||||
/// byte) always returns 0. Mirrors Linux 7.1 `s2idle_wake()` in
|
|
||||||
/// `kernel/power/suspend.c:133`. Hardware-agnostic.
|
|
||||||
ExitS2Idle = 4,
|
|
||||||
/// Red Bear OS extension (Phase II.X.W): acpid writes the
|
|
||||||
/// 64-bit kernel S3 resume trampoline address to
|
|
||||||
/// FACS.xfirmware_waking_vector. The kernel's
|
|
||||||
/// `arch/x86_shared/s3_resume.rs` trampoline is at this
|
|
||||||
/// address; the platform firmware jumps to it on S3 wake.
|
|
||||||
/// Write payload: 8-byte little-endian u64 (the trampoline
|
|
||||||
/// address). Read payload: 0 (no error condition; this verb
|
|
||||||
/// is one-way). Mirrors Linux 7.1's
|
|
||||||
/// `acpi_set_firmware_waking_vector` in ACPICA.
|
|
||||||
/// Hardware-agnostic: works on any x86_64 system with
|
|
||||||
/// standard ACPI S3 support (Dell, HP, Lenovo, LG Gram 14).
|
|
||||||
SetS3WakingVector = 5,
|
|
||||||
/// Red Bear OS extension (Phase II.X.W): acpid requests
|
|
||||||
/// the kernel to enter S3. The kernel's kstop handler
|
|
||||||
/// dispatches on the "s3" string arg, dispatches on the
|
|
||||||
/// SLP_TYP byte, and does the PM1 register write. The
|
|
||||||
/// acpid has already done the AML prep (`_TTS(3)`, `_PTS(3)`,
|
|
||||||
/// `_SST(3)`) and written the trampoline address to FACS
|
|
||||||
/// via `SetS3WakingVector`. No payload needed.
|
|
||||||
/// Mirrors Linux 7.1's `enter_sleep_state` /
|
|
||||||
/// `acpi_hw_legacy_sleep` for S3.
|
|
||||||
EnterS3 = 6,
|
|
||||||
}
|
|
||||||
impl AcpiVerb {
|
|
||||||
pub const fn try_from_raw(value: u64) -> Option<Self> {
|
|
||||||
Some(match value {
|
|
||||||
1 => Self::ReadRxsdt,
|
|
||||||
2 => Self::CheckShutdown,
|
|
||||||
3 => Self::EnterS2Idle,
|
|
||||||
4 => Self::ExitS2Idle,
|
|
||||||
5 => Self::SetS3WakingVector,
|
|
||||||
6 => Self::EnterS3,
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
||||||
#[repr(usize)]
|
|
||||||
pub enum SchemeSocketCall {
|
|
||||||
ObtainFd = 0,
|
|
||||||
MoveFd = 1,
|
|
||||||
}
|
|
||||||
impl SchemeSocketCall {
|
|
||||||
pub fn try_from_raw(raw: usize) -> Option<Self> {
|
|
||||||
Some(match raw {
|
|
||||||
0 => Self::ObtainFd,
|
|
||||||
1 => Self::MoveFd,
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
||||||
#[repr(usize)]
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub enum FsCall {
|
|
||||||
Connect = 0,
|
|
||||||
}
|
|
||||||
impl FsCall {
|
|
||||||
pub fn try_from_raw(raw: usize) -> Option<Self> {
|
|
||||||
Some(match raw {
|
|
||||||
0 => Self::Connect,
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bitflags! {
|
|
||||||
pub struct PtraceFlags: u64 {
|
|
||||||
/// Stop before a syscall is handled. Send PTRACE_FLAG_IGNORE to not
|
|
||||||
/// handle the syscall.
|
|
||||||
const PTRACE_STOP_PRE_SYSCALL = 0x0000_0000_0000_0001;
|
|
||||||
/// Stop after a syscall is handled.
|
|
||||||
const PTRACE_STOP_POST_SYSCALL = 0x0000_0000_0000_0002;
|
|
||||||
/// Stop after exactly one instruction. TODO: This may not handle
|
|
||||||
/// fexec/signal boundaries. Should it?
|
|
||||||
const PTRACE_STOP_SINGLESTEP = 0x0000_0000_0000_0004;
|
|
||||||
/// Stop before a signal is handled. Send PTRACE_FLAG_IGNORE to not
|
|
||||||
/// handle signal.
|
|
||||||
const PTRACE_STOP_SIGNAL = 0x0000_0000_0000_0008;
|
|
||||||
/// Stop on a software breakpoint, such as the int3 instruction for
|
|
||||||
/// x86_64.
|
|
||||||
const PTRACE_STOP_BREAKPOINT = 0x0000_0000_0000_0010;
|
|
||||||
/// Stop just before exiting for good.
|
|
||||||
const PTRACE_STOP_EXIT = 0x0000_0000_0000_0020;
|
|
||||||
|
|
||||||
const PTRACE_STOP_MASK = 0x0000_0000_0000_00FF;
|
|
||||||
|
|
||||||
|
|
||||||
/// Sent when a child is cloned, giving you the opportunity to trace it.
|
|
||||||
/// If you don't catch this, the child is started as normal.
|
|
||||||
const PTRACE_EVENT_CLONE = 0x0000_0000_0000_0100;
|
|
||||||
|
|
||||||
/// Sent when current-addrspace is changed, allowing the tracer to reopen the memory file.
|
|
||||||
const PTRACE_EVENT_ADDRSPACE_SWITCH = 0x0000_0000_0000_0200;
|
|
||||||
|
|
||||||
const PTRACE_EVENT_MASK = 0x0000_0000_0000_0F00;
|
|
||||||
|
|
||||||
/// Special meaning, depending on the event. Usually, when fired before
|
|
||||||
/// an action, it will skip performing that action.
|
|
||||||
const PTRACE_FLAG_IGNORE = 0x0000_0000_0000_1000;
|
|
||||||
|
|
||||||
const PTRACE_FLAG_MASK = 0x0000_0000_0000_F000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Deref for PtraceFlags {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
// Same as to_ne_bytes but in-place
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts(&self.bits() as *const _ as *const u8, mem::size_of::<u64>())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const SEEK_SET: usize = 0;
|
|
||||||
pub const SEEK_CUR: usize = 1;
|
|
||||||
pub const SEEK_END: usize = 2;
|
|
||||||
|
|
||||||
pub const SIGCHLD: usize = 17;
|
|
||||||
pub const SIGTSTP: usize = 20;
|
|
||||||
pub const SIGTTIN: usize = 21;
|
|
||||||
pub const SIGTTOU: usize = 22;
|
|
||||||
|
|
||||||
pub const ADDRSPACE_OP_MMAP: usize = 0;
|
|
||||||
pub const ADDRSPACE_OP_MUNMAP: usize = 1;
|
|
||||||
pub const ADDRSPACE_OP_MPROTECT: usize = 2;
|
|
||||||
pub const ADDRSPACE_OP_TRANSFER: usize = 3;
|
|
||||||
|
|
||||||
bitflags! {
|
|
||||||
pub struct MremapFlags: usize {
|
|
||||||
const FIXED = 1;
|
|
||||||
const FIXED_REPLACE = 3;
|
|
||||||
/// Alias's memory region at `old_address` to `new_address` such that both regions share
|
|
||||||
/// the same frames.
|
|
||||||
const KEEP_OLD = 1 << 2;
|
|
||||||
// TODO: MAYMOVE, DONTUNMAP
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bitflags! {
|
|
||||||
pub struct RwFlags: u32 {
|
|
||||||
const NONBLOCK = 1;
|
|
||||||
const APPEND = 2;
|
|
||||||
// TODO: sync/dsync
|
|
||||||
// TODO: O_DIRECT?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bitflags! {
|
|
||||||
pub struct SigcontrolFlags: usize {
|
|
||||||
/// Prevents the kernel from jumping the context to the signal trampoline, but otherwise
|
|
||||||
/// has absolutely no effect on which signals are blocked etc. Meant to be used for
|
|
||||||
/// short-lived critical sections inside libc.
|
|
||||||
const INHIBIT_DELIVERY = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bitflags! {
|
|
||||||
pub struct CallFlags: usize {
|
|
||||||
// reserved
|
|
||||||
const RSVD0 = 1 << 0;
|
|
||||||
const RSVD1 = 1 << 1;
|
|
||||||
const RSVD2 = 1 << 2;
|
|
||||||
const RSVD3 = 1 << 3;
|
|
||||||
const RSVD4 = 1 << 4;
|
|
||||||
const RSVD5 = 1 << 5;
|
|
||||||
const RSVD6 = 1 << 6;
|
|
||||||
const RSVD7 = 1 << 7;
|
|
||||||
|
|
||||||
/// Remove the fd from the caller's file table before sending the message.
|
|
||||||
const CONSUME = 1 << 8;
|
|
||||||
|
|
||||||
const WRITE = 1 << 9;
|
|
||||||
const READ = 1 << 10;
|
|
||||||
|
|
||||||
/// Indicates the request is a bulk fd passing request.
|
|
||||||
const FD = 1 << 11;
|
|
||||||
/// Flags for the fd passing request.
|
|
||||||
const FD_EXCLUSIVE = 1 << 12;
|
|
||||||
const FD_CLONE = 1 << 13;
|
|
||||||
const FD_UPPER = 1 << 14;
|
|
||||||
const FD_CLOEXEC = 1 << 15;
|
|
||||||
|
|
||||||
/// Call is a standard fs call, with metadata defined in `StdFsCallMeta`
|
|
||||||
const STD_FS = 1 << 16;
|
|
||||||
|
|
||||||
/// Call is taking multiple fds as an argument
|
|
||||||
const MULTIPLE_FDS = 1 << 17;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
pub enum StdFsCallKind {
|
|
||||||
// TODO: remove old syscalls
|
|
||||||
Fchmod = 1,
|
|
||||||
Fchown = 2,
|
|
||||||
Getdents = 3,
|
|
||||||
Fstat = 4,
|
|
||||||
Fstatvfs = 5,
|
|
||||||
Fsync = 6,
|
|
||||||
Ftruncate = 7,
|
|
||||||
Futimens = 8,
|
|
||||||
// 9 reserved in fscall RFC
|
|
||||||
// Unlinkat = 10,
|
|
||||||
Relpathat = 11,
|
|
||||||
Lock = 12,
|
|
||||||
Unlock = 13,
|
|
||||||
GetLock = 14,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StdFsCallKind {
|
|
||||||
pub fn try_from_raw(raw: u8) -> Option<Self> {
|
|
||||||
use StdFsCallKind::*;
|
|
||||||
|
|
||||||
// TODO: Use a library where this match can be automated.
|
|
||||||
Some(match raw {
|
|
||||||
1 => Fchmod,
|
|
||||||
2 => Fchown,
|
|
||||||
3 => Getdents,
|
|
||||||
4 => Fstat,
|
|
||||||
5 => Fstatvfs,
|
|
||||||
6 => Fsync,
|
|
||||||
7 => Ftruncate,
|
|
||||||
8 => Futimens,
|
|
||||||
// 9 reserved in fscall RFC
|
|
||||||
// 10 => Unlinkat,
|
|
||||||
11 => Relpathat,
|
|
||||||
12 => Lock,
|
|
||||||
13 => Unlock,
|
|
||||||
14 => GetLock,
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The tag for the fd number in the upper file descriptor table.
|
|
||||||
pub const UPPER_FDTBL_TAG: usize = 1 << (usize::BITS - 2);
|
|
||||||
|
|
||||||
/// The identifier for registering event timeout
|
|
||||||
pub const EVENT_TIMEOUT_ID: usize = usize::MAX - 2;
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
use core::{
|
|
||||||
cmp::PartialEq,
|
|
||||||
ops::{BitAnd, BitOr, Not},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub trait Io {
|
|
||||||
type Value: Copy
|
|
||||||
+ PartialEq
|
|
||||||
+ BitAnd<Output = Self::Value>
|
|
||||||
+ BitOr<Output = Self::Value>
|
|
||||||
+ Not<Output = Self::Value>;
|
|
||||||
|
|
||||||
fn read(&self) -> Self::Value;
|
|
||||||
fn write(&mut self, value: Self::Value);
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn readf(&self, flags: Self::Value) -> bool {
|
|
||||||
(self.read() & flags) as Self::Value == flags
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn writef(&mut self, flags: Self::Value, value: bool) {
|
|
||||||
let tmp: Self::Value = match value {
|
|
||||||
true => self.read() | flags,
|
|
||||||
false => self.read() & !flags,
|
|
||||||
};
|
|
||||||
self.write(tmp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ReadOnly<I> {
|
|
||||||
inner: I,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> ReadOnly<I> {
|
|
||||||
pub const fn new(inner: I) -> ReadOnly<I> {
|
|
||||||
ReadOnly { inner: inner }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I: Io> ReadOnly<I> {
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn read(&self) -> I::Value {
|
|
||||||
self.inner.read()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn readf(&self, flags: I::Value) -> bool {
|
|
||||||
self.inner.readf(flags)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct WriteOnly<I> {
|
|
||||||
inner: I,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I> WriteOnly<I> {
|
|
||||||
pub const fn new(inner: I) -> WriteOnly<I> {
|
|
||||||
WriteOnly { inner: inner }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I: Io> WriteOnly<I> {
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn write(&mut self, value: I::Value) {
|
|
||||||
self.inner.write(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn writef(&mut self, flags: I::Value, value: bool) {
|
|
||||||
self.inner.writef(flags, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-165
@@ -1,165 +0,0 @@
|
|||||||
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
|
||||||
use core::ops::{BitAnd, BitOr, Not};
|
|
||||||
use core::{mem::MaybeUninit, ptr};
|
|
||||||
|
|
||||||
use super::io::Io;
|
|
||||||
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct Mmio<T> {
|
|
||||||
value: MaybeUninit<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Mmio<T> {
|
|
||||||
pub unsafe fn zeroed() -> Self {
|
|
||||||
Self {
|
|
||||||
value: MaybeUninit::zeroed(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub unsafe fn uninit() -> Self {
|
|
||||||
Self {
|
|
||||||
value: MaybeUninit::uninit(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub const fn from(value: T) -> Self {
|
|
||||||
Self {
|
|
||||||
value: MaybeUninit::new(value),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generic implementation (WARNING: requires aligned pointers!)
|
|
||||||
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
|
||||||
impl<T> Io for Mmio<T>
|
|
||||||
where
|
|
||||||
T: Copy + PartialEq + BitAnd<Output = T> + BitOr<Output = T> + Not<Output = T>,
|
|
||||||
{
|
|
||||||
type Value = T;
|
|
||||||
|
|
||||||
fn read(&self) -> T {
|
|
||||||
unsafe { ptr::read_volatile(ptr::addr_of!(self.value).cast::<T>()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&mut self, value: T) {
|
|
||||||
unsafe { ptr::write_volatile(ptr::addr_of_mut!(self.value).cast::<T>(), value) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// x86 u8 implementation
|
|
||||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
|
||||||
impl Io for Mmio<u8> {
|
|
||||||
type Value = u8;
|
|
||||||
|
|
||||||
fn read(&self) -> Self::Value {
|
|
||||||
unsafe {
|
|
||||||
let value: Self::Value;
|
|
||||||
let ptr: *const Self::Value = ptr::addr_of!(self.value).cast::<Self::Value>();
|
|
||||||
core::arch::asm!(
|
|
||||||
"mov {}, [{}]",
|
|
||||||
out(reg_byte) value,
|
|
||||||
in(reg) ptr
|
|
||||||
);
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&mut self, value: Self::Value) {
|
|
||||||
unsafe {
|
|
||||||
let ptr: *mut Self::Value = ptr::addr_of_mut!(self.value).cast::<Self::Value>();
|
|
||||||
core::arch::asm!(
|
|
||||||
"mov [{}], {}",
|
|
||||||
in(reg) ptr,
|
|
||||||
in(reg_byte) value,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// x86 u16 implementation
|
|
||||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
|
||||||
impl Io for Mmio<u16> {
|
|
||||||
type Value = u16;
|
|
||||||
|
|
||||||
fn read(&self) -> Self::Value {
|
|
||||||
unsafe {
|
|
||||||
let value: Self::Value;
|
|
||||||
let ptr: *const Self::Value = ptr::addr_of!(self.value).cast::<Self::Value>();
|
|
||||||
core::arch::asm!(
|
|
||||||
"mov {:x}, [{}]",
|
|
||||||
out(reg) value,
|
|
||||||
in(reg) ptr
|
|
||||||
);
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&mut self, value: Self::Value) {
|
|
||||||
unsafe {
|
|
||||||
let ptr: *mut Self::Value = ptr::addr_of_mut!(self.value).cast::<Self::Value>();
|
|
||||||
core::arch::asm!(
|
|
||||||
"mov [{}], {:x}",
|
|
||||||
in(reg) ptr,
|
|
||||||
in(reg) value,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// x86 u32 implementation
|
|
||||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
|
||||||
impl Io for Mmio<u32> {
|
|
||||||
type Value = u32;
|
|
||||||
|
|
||||||
fn read(&self) -> Self::Value {
|
|
||||||
unsafe {
|
|
||||||
let value: Self::Value;
|
|
||||||
let ptr: *const Self::Value = ptr::addr_of!(self.value).cast::<Self::Value>();
|
|
||||||
core::arch::asm!(
|
|
||||||
"mov {:e}, [{}]",
|
|
||||||
out(reg) value,
|
|
||||||
in(reg) ptr
|
|
||||||
);
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&mut self, value: Self::Value) {
|
|
||||||
unsafe {
|
|
||||||
let ptr: *mut Self::Value = ptr::addr_of_mut!(self.value).cast::<Self::Value>();
|
|
||||||
core::arch::asm!(
|
|
||||||
"mov [{}], {:e}",
|
|
||||||
in(reg) ptr,
|
|
||||||
in(reg) value,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// x86 u64 implementation (x86_64 only)
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
impl Io for Mmio<u64> {
|
|
||||||
type Value = u64;
|
|
||||||
|
|
||||||
fn read(&self) -> Self::Value {
|
|
||||||
unsafe {
|
|
||||||
let value: Self::Value;
|
|
||||||
let ptr: *const Self::Value = ptr::addr_of!(self.value).cast::<Self::Value>();
|
|
||||||
core::arch::asm!(
|
|
||||||
"mov {:r}, [{}]",
|
|
||||||
out(reg) value,
|
|
||||||
in(reg) ptr
|
|
||||||
);
|
|
||||||
value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(&mut self, value: Self::Value) {
|
|
||||||
unsafe {
|
|
||||||
let ptr: *mut Self::Value = ptr::addr_of_mut!(self.value).cast::<Self::Value>();
|
|
||||||
core::arch::asm!(
|
|
||||||
"mov [{}], {:r}",
|
|
||||||
in(reg) ptr,
|
|
||||||
in(reg) value,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
//! I/O functions
|
|
||||||
|
|
||||||
pub use self::{io::*, mmio::*};
|
|
||||||
|
|
||||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
|
||||||
pub use self::pio::*;
|
|
||||||
|
|
||||||
mod io;
|
|
||||||
mod mmio;
|
|
||||||
|
|
||||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
|
||||||
mod pio;
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
use core::{arch::asm, marker::PhantomData};
|
|
||||||
|
|
||||||
use super::io::Io;
|
|
||||||
|
|
||||||
/// Generic PIO
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct Pio<T> {
|
|
||||||
port: u16,
|
|
||||||
value: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Pio<T> {
|
|
||||||
/// Create a PIO from a given port
|
|
||||||
pub const fn new(port: u16) -> Self {
|
|
||||||
Pio::<T> {
|
|
||||||
port,
|
|
||||||
value: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read/Write for byte PIO
|
|
||||||
impl Io for Pio<u8> {
|
|
||||||
type Value = u8;
|
|
||||||
|
|
||||||
/// Read
|
|
||||||
#[inline(always)]
|
|
||||||
fn read(&self) -> u8 {
|
|
||||||
let value: u8;
|
|
||||||
unsafe {
|
|
||||||
asm!("in al, dx", in("dx") self.port, out("al") value, options(nostack, nomem, preserves_flags));
|
|
||||||
}
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write
|
|
||||||
#[inline(always)]
|
|
||||||
fn write(&mut self, value: u8) {
|
|
||||||
unsafe {
|
|
||||||
asm!("out dx, al", in("dx") self.port, in("al") value, options(nostack, nomem, preserves_flags));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read/Write for word PIO
|
|
||||||
impl Io for Pio<u16> {
|
|
||||||
type Value = u16;
|
|
||||||
|
|
||||||
/// Read
|
|
||||||
#[inline(always)]
|
|
||||||
fn read(&self) -> u16 {
|
|
||||||
let value: u16;
|
|
||||||
unsafe {
|
|
||||||
asm!("in ax, dx", in("dx") self.port, out("ax") value, options(nostack, nomem, preserves_flags));
|
|
||||||
}
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write
|
|
||||||
#[inline(always)]
|
|
||||||
fn write(&mut self, value: u16) {
|
|
||||||
unsafe {
|
|
||||||
asm!("out dx, ax", in("dx") self.port, in("ax") value, options(nostack, nomem, preserves_flags));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read/Write for doubleword PIO
|
|
||||||
impl Io for Pio<u32> {
|
|
||||||
type Value = u32;
|
|
||||||
|
|
||||||
/// Read
|
|
||||||
#[inline(always)]
|
|
||||||
fn read(&self) -> u32 {
|
|
||||||
let value: u32;
|
|
||||||
unsafe {
|
|
||||||
asm!("in eax, dx", in("dx") self.port, out("eax") value, options(nostack, nomem, preserves_flags));
|
|
||||||
}
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write
|
|
||||||
#[inline(always)]
|
|
||||||
fn write(&mut self, value: u32) {
|
|
||||||
unsafe {
|
|
||||||
asm!("out dx, eax", in("dx") self.port, in("eax") value, options(nostack, nomem, preserves_flags));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+83
-44
@@ -1,53 +1,92 @@
|
|||||||
#![cfg_attr(not(any(feature = "std", test)), no_std)]
|
//! Redox OS user and group utilities.
|
||||||
#![allow(unexpected_cfgs)] // why does this even exist?
|
//!
|
||||||
|
//! The `userutils` crate contains the utilities for dealing with users and groups in Redox OS.
|
||||||
|
//! They are heavily influenced by UNIX and are, when needed, tailored to specific Redox use cases.
|
||||||
|
//!
|
||||||
|
//! These implementations strive to be as simple as possible drawing particular
|
||||||
|
//! inspiration by BSD systems. They are indeed small, by choice.
|
||||||
|
//!
|
||||||
|
//! The included utilities are:
|
||||||
|
//!
|
||||||
|
//! - `getty`: Used by `init(8)` to open and initialize the TTY line, read a login name and invoke `login(1)`.
|
||||||
|
//! - `id`: Displays user identity.
|
||||||
|
//! - `login`: Allows users to into the system.
|
||||||
|
//! - `passwd`: Allows users to modify their passwords.
|
||||||
|
//! - `su`: Allows users to substitute identity.
|
||||||
|
//! - `sudo`: Enables users to execute a command as another user.
|
||||||
|
//! - `whoami`: Display effective user ID.
|
||||||
|
|
||||||
#[cfg(test)]
|
use std::io::Result as IoResult;
|
||||||
extern crate core;
|
|
||||||
|
|
||||||
pub use self::{arch::*, data::*, error::*, flag::*, io::*, number::*};
|
use libredox::call::{fchown, open};
|
||||||
|
use libredox::error::Result as SysResult;
|
||||||
|
use libredox::flag::{O_CLOEXEC, O_CREAT, O_DIRECTORY};
|
||||||
|
use redox_users::{All, AllGroups, Error, Result, User, auth};
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
const DEFAULT_MODE: u16 = 0o700;
|
||||||
#[path = "arch/aarch64.rs"]
|
|
||||||
mod arch;
|
|
||||||
|
|
||||||
#[cfg(target_arch = "riscv64")]
|
// Not the prettiest thing in the world, but some functionality here makes
|
||||||
#[path = "arch/riscv64.rs"]
|
// some of the utils much less gross
|
||||||
mod arch;
|
pub trait AllGroupsExt {
|
||||||
|
fn add_user_to_groups(&mut self, login: &str, groups: Vec<&str>) -> Result<()>;
|
||||||
|
fn remove_user_from_all_groups(&mut self, login: &str);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "x86")]
|
impl AllGroupsExt for AllGroups {
|
||||||
#[path = "arch/x86.rs"]
|
// new_groups is a comma separated list of groupnames
|
||||||
mod arch;
|
fn add_user_to_groups(&mut self, login: &str, new_groups: Vec<&str>) -> Result<()> {
|
||||||
|
for groupname in new_groups {
|
||||||
|
let group = match self.get_mut_by_name(groupname) {
|
||||||
|
Some(group) => group,
|
||||||
|
None => return Err(Error::UserNotFound),
|
||||||
|
};
|
||||||
|
group.users.push(login.to_string());
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
/// Remove a user from all groups of which they are a member
|
||||||
#[path = "arch/x86_64.rs"]
|
fn remove_user_from_all_groups(&mut self, login: &str) {
|
||||||
mod arch;
|
for group in self.iter_mut() {
|
||||||
|
let op_pos = group.users.iter().position(|username| username == login);
|
||||||
|
if let Some(indx) = op_pos {
|
||||||
|
group.users.remove(indx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Function definitions
|
/// Spawns a shell for the given `User`.
|
||||||
#[cfg(feature = "userspace")]
|
///
|
||||||
pub mod call;
|
/// This function wraps the shell_cmd function of the User struct
|
||||||
|
/// from redox_users and manages the child process. It is a blocking
|
||||||
|
/// operation.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use redox_users::AllUsers;
|
||||||
|
///
|
||||||
|
/// let sys_users = AllUsers::new().unwrap();
|
||||||
|
/// let user = sys_users.get_by_name("goyox86");
|
||||||
|
/// spawn_shell(user).unwrap();
|
||||||
|
/// ```
|
||||||
|
pub fn spawn_shell<T: Default>(user: &User<T>) -> IoResult<i32> {
|
||||||
|
let mut command = user.shell_cmd();
|
||||||
|
|
||||||
#[cfg(feature = "userspace")]
|
let mut child = command.spawn()?;
|
||||||
pub use call::*;
|
match child.wait()?.code() {
|
||||||
|
Some(code) => Ok(code),
|
||||||
|
None => Ok(1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Complex structures that are used for some system calls
|
/// Creates a directory with 700 user:user permissions
|
||||||
pub mod data;
|
pub fn create_user_dir<T>(user: &User<auth::Full>, dir: T) -> SysResult<()>
|
||||||
|
where
|
||||||
pub mod dirent;
|
T: AsRef<str> + std::convert::AsRef<[u8]>,
|
||||||
|
{
|
||||||
/// All errors that can be generated by a system call
|
let fd = open(dir, O_CREAT | O_DIRECTORY | O_CLOEXEC, DEFAULT_MODE)?;
|
||||||
pub mod error;
|
fchown(fd, user.uid as u32, user.gid as u32)?;
|
||||||
|
Ok(())
|
||||||
/// Flags used as an argument to many system calls
|
}
|
||||||
pub mod flag;
|
|
||||||
|
|
||||||
/// Functions for low level hardware control
|
|
||||||
pub mod io;
|
|
||||||
|
|
||||||
/// Call numbers used by each system call
|
|
||||||
pub mod number;
|
|
||||||
|
|
||||||
/// ABI for shared memory based signals
|
|
||||||
pub mod sigabi;
|
|
||||||
|
|
||||||
/// V2 scheme format
|
|
||||||
pub mod schemev2;
|
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
pub const SYS_CLASS: usize = 0xF000_0000;
|
|
||||||
pub const SYS_CLASS_PATH: usize = 0x1000_0000;
|
|
||||||
pub const SYS_CLASS_FILE: usize = 0x2000_0000;
|
|
||||||
|
|
||||||
pub const SYS_ARG: usize = 0x0F00_0000;
|
|
||||||
pub const SYS_ARG_SLICE: usize = 0x0100_0000;
|
|
||||||
pub const SYS_ARG_MSLICE: usize = 0x0200_0000;
|
|
||||||
pub const SYS_ARG_PATH: usize = 0x0300_0000;
|
|
||||||
|
|
||||||
pub const SYS_RET: usize = 0x00F0_0000;
|
|
||||||
pub const SYS_RET_FILE: usize = 0x0010_0000;
|
|
||||||
|
|
||||||
pub const SYS_OPENAT: usize = SYS_CLASS_PATH | SYS_RET_FILE | 7;
|
|
||||||
pub const SYS_OPENAT_WITH_FILTER: usize = SYS_CLASS_PATH | SYS_RET_FILE | 985;
|
|
||||||
pub const SYS_UNLINKAT: usize = SYS_CLASS_PATH | 263;
|
|
||||||
pub const SYS_UNLINKAT_WITH_FILTER: usize = SYS_CLASS_PATH | 986;
|
|
||||||
|
|
||||||
pub const SYS_CLOSE: usize = SYS_CLASS_FILE | 6;
|
|
||||||
pub const SYS_DUP: usize = SYS_CLASS_FILE | SYS_RET_FILE | 41;
|
|
||||||
pub const SYS_DUP2: usize = SYS_CLASS_FILE | SYS_RET_FILE | 63;
|
|
||||||
pub const SYS_READ: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 3;
|
|
||||||
pub const SYS_READ2: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 35;
|
|
||||||
pub const SYS_WRITE: usize = SYS_CLASS_FILE | SYS_ARG_SLICE | 4;
|
|
||||||
pub const SYS_WRITE2: usize = SYS_CLASS_FILE | SYS_ARG_SLICE | 45;
|
|
||||||
pub const SYS_LSEEK: usize = SYS_CLASS_FILE | 19;
|
|
||||||
pub const SYS_FCHMOD: usize = SYS_CLASS_FILE | 94;
|
|
||||||
pub const SYS_FCHOWN: usize = SYS_CLASS_FILE | 207;
|
|
||||||
pub const SYS_FCNTL: usize = SYS_CLASS_FILE | 55;
|
|
||||||
pub const SYS_FEVENT: usize = SYS_CLASS_FILE | 927;
|
|
||||||
|
|
||||||
// SYS_CALL, fd, inout buf ptr, inout buf len, flags, metadata buf ptr, metadata buf len
|
|
||||||
// TODO: new number for SYS_CALL where flags are sent as 6th argument (using syscall6)
|
|
||||||
pub const SYS_CALL: usize = SYS_CLASS_FILE | SYS_ARG_SLICE | SYS_ARG_MSLICE | 0xCA11;
|
|
||||||
|
|
||||||
pub const SYS_SENDFD: usize = SYS_CLASS_FILE | 34;
|
|
||||||
pub const SYS_GETDENTS: usize = SYS_CLASS_FILE | 43;
|
|
||||||
|
|
||||||
// TODO: Rename FMAP/FUNMAP to MMAP/MUNMAP
|
|
||||||
pub const SYS_FMAP: usize = SYS_CLASS_FILE | SYS_ARG_SLICE | 900;
|
|
||||||
// TODO: SYS_FUNMAP should be SYS_CLASS_FILE
|
|
||||||
pub const SYS_FUNMAP: usize = SYS_CLASS_FILE | 92;
|
|
||||||
pub const SYS_MREMAP: usize = 155;
|
|
||||||
|
|
||||||
pub const SYS_FLINK: usize = SYS_CLASS_FILE | SYS_ARG_PATH | 9;
|
|
||||||
pub const SYS_FPATH: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 928;
|
|
||||||
pub const SYS_FRENAME: usize = SYS_CLASS_FILE | SYS_ARG_PATH | 38;
|
|
||||||
pub const SYS_FSTAT: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 28;
|
|
||||||
pub const SYS_FSTATVFS: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 100;
|
|
||||||
pub const SYS_FSYNC: usize = SYS_CLASS_FILE | 118;
|
|
||||||
pub const SYS_FTRUNCATE: usize = SYS_CLASS_FILE | 93;
|
|
||||||
pub const SYS_FUTIMENS: usize = SYS_CLASS_FILE | SYS_ARG_SLICE | 320;
|
|
||||||
|
|
||||||
pub const SYS_CLOCK_GETTIME: usize = 265;
|
|
||||||
pub const SYS_FUTEX: usize = 240;
|
|
||||||
pub const SYS_MPROTECT: usize = 125;
|
|
||||||
pub const SYS_MKNS: usize = 984;
|
|
||||||
pub const SYS_NANOSLEEP: usize = 162;
|
|
||||||
pub const SYS_YIELD: usize = 158;
|
|
||||||
-213
@@ -1,213 +0,0 @@
|
|||||||
use core::{
|
|
||||||
mem,
|
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
slice,
|
|
||||||
};
|
|
||||||
|
|
||||||
use bitflags::bitflags;
|
|
||||||
|
|
||||||
pub struct CallerCtx {
|
|
||||||
pub pid: usize,
|
|
||||||
pub uid: u32,
|
|
||||||
pub gid: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum OpenResult {
|
|
||||||
ThisScheme { number: usize },
|
|
||||||
OtherScheme { fd: usize },
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
pub struct Sqe {
|
|
||||||
pub opcode: u8,
|
|
||||||
pub sqe_flags: SqeFlags,
|
|
||||||
pub _rsvd: u16, // TODO: priority
|
|
||||||
pub tag: u32,
|
|
||||||
pub args: [u64; 6],
|
|
||||||
pub caller: u64,
|
|
||||||
}
|
|
||||||
impl Deref for Sqe {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe { slice::from_raw_parts(self as *const Sqe as *const u8, mem::size_of::<Sqe>()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for Sqe {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe { slice::from_raw_parts_mut(self as *mut Sqe as *mut u8, mem::size_of::<Sqe>()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bitflags! {
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
pub struct SqeFlags: u8 {
|
|
||||||
// If zero, the message is bidirectional, and the scheme is expected to pass the Ksmsg's
|
|
||||||
// tag field to the Skmsg. Some opcodes require this flag to be set.
|
|
||||||
const ONEWAY = 1;
|
|
||||||
|
|
||||||
// If this flag is set, index 0 of Sqe's args stores the IDs buffer address,
|
|
||||||
// and index 1 stores the IDs buffer length.
|
|
||||||
const MULTIPLE_IDS = 1 << 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Copy, Debug, Default)]
|
|
||||||
pub struct Cqe {
|
|
||||||
pub flags: u8, // bits 2:0 are CqeOpcode
|
|
||||||
pub extra_raw: [u8; 3],
|
|
||||||
pub tag: u32,
|
|
||||||
pub result: u64,
|
|
||||||
}
|
|
||||||
impl Deref for Cqe {
|
|
||||||
type Target = [u8];
|
|
||||||
fn deref(&self) -> &[u8] {
|
|
||||||
unsafe { slice::from_raw_parts(self as *const Cqe as *const u8, mem::size_of::<Cqe>()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DerefMut for Cqe {
|
|
||||||
fn deref_mut(&mut self) -> &mut [u8] {
|
|
||||||
unsafe { slice::from_raw_parts_mut(self as *mut Cqe as *mut u8, mem::size_of::<Cqe>()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bitflags! {
|
|
||||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
|
||||||
pub struct NewFdFlags: u8 {
|
|
||||||
const POSITIONED = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Cqe {
|
|
||||||
pub fn extra(&self) -> u32 {
|
|
||||||
u32::from_ne_bytes([self.extra_raw[0], self.extra_raw[1], self.extra_raw[2], 0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(u8)]
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
||||||
pub enum CqeOpcode {
|
|
||||||
RespondRegular,
|
|
||||||
RespondWithFd,
|
|
||||||
SendFevent, // no tag
|
|
||||||
ObtainFd,
|
|
||||||
RespondWithMultipleFds,
|
|
||||||
/// [`SchemeAsync::on_close`] and [`SchemeSync::on_close`] are only called when the last file
|
|
||||||
/// descriptor referring to the file description is closed. To implement traditional POSIX
|
|
||||||
/// advisory file locking, [`CqeOpcode::RespondAndNotifyOnDetach`] is used to notify the scheme
|
|
||||||
/// by sending a [`RequestKind::OnDetach`] request the next time the file description is
|
|
||||||
/// "detached" from a file descriptor. Not done by default to avoid unnecessary IPC.
|
|
||||||
RespondAndNotifyOnDetach,
|
|
||||||
// TODO: ProvideMmap
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CqeOpcode {
|
|
||||||
pub fn try_from_raw(raw: u8) -> Option<Self> {
|
|
||||||
// TODO: Use a library where this match can be automated.
|
|
||||||
Some(match raw {
|
|
||||||
0 => Self::RespondRegular,
|
|
||||||
1 => Self::RespondWithFd,
|
|
||||||
2 => Self::SendFevent,
|
|
||||||
3 => Self::ObtainFd,
|
|
||||||
4 => Self::RespondWithMultipleFds,
|
|
||||||
5 => Self::RespondAndNotifyOnDetach,
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// SqeOpcode
|
|
||||||
#[repr(u8)]
|
|
||||||
#[non_exhaustive]
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub enum Opcode {
|
|
||||||
Close = 3, // fd
|
|
||||||
Dup = 4, // old fd, buf_ptr, buf_len
|
|
||||||
Read = 5, // fd, buf_ptr, buf_len, TODO offset, TODO flags, _
|
|
||||||
Write = 6, // fd, buf_ptr, buf_len, TODO offset, TODO flags)
|
|
||||||
Fsize = 7, // fd
|
|
||||||
Fchmod = 8, // fd, new mode
|
|
||||||
Fchown = 9, // fd, new uid, new gid
|
|
||||||
Fcntl = 10, // fd, cmd, arg
|
|
||||||
Fevent = 11, // fd, requested mask
|
|
||||||
Sendfd = 12,
|
|
||||||
Fpath = 13, // fd, buf_ptr, buf_len
|
|
||||||
Frename = 14,
|
|
||||||
Fstat = 15, // fd, buf_ptr, buf_len
|
|
||||||
Fstatvfs = 16, // fd, buf_ptr, buf_len
|
|
||||||
Fsync = 17, // fd
|
|
||||||
Ftruncate = 18, // fd, new len
|
|
||||||
Futimens = 19, // fd, times_buf, times_len
|
|
||||||
|
|
||||||
MmapPrep = 20,
|
|
||||||
RequestMmap = 21,
|
|
||||||
Mremap = 22,
|
|
||||||
Munmap = 23,
|
|
||||||
Msync = 24, // TODO
|
|
||||||
|
|
||||||
Cancel = 25, // @tag
|
|
||||||
|
|
||||||
Getdents = 26,
|
|
||||||
CloseMsg = 27,
|
|
||||||
Call = 28,
|
|
||||||
|
|
||||||
OpenAt = 29, // fd, buf_ptr, buf_len, flags
|
|
||||||
Flink = 30,
|
|
||||||
Recvfd = 31,
|
|
||||||
|
|
||||||
UnlinkAt = 32, // fd, path_ptr, path_len (utf8), flags
|
|
||||||
StdFsCall = 33,
|
|
||||||
|
|
||||||
Detach = 34,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Opcode {
|
|
||||||
pub fn try_from_raw(raw: u8) -> Option<Self> {
|
|
||||||
use Opcode::*;
|
|
||||||
|
|
||||||
// TODO: Use a library where this match can be automated.
|
|
||||||
Some(match raw {
|
|
||||||
3 => Close,
|
|
||||||
4 => Dup,
|
|
||||||
5 => Read,
|
|
||||||
6 => Write,
|
|
||||||
7 => Fsize,
|
|
||||||
8 => Fchmod,
|
|
||||||
9 => Fchown,
|
|
||||||
10 => Fcntl,
|
|
||||||
11 => Fevent,
|
|
||||||
12 => Sendfd,
|
|
||||||
13 => Fpath,
|
|
||||||
14 => Frename,
|
|
||||||
15 => Fstat,
|
|
||||||
16 => Fstatvfs,
|
|
||||||
17 => Fsync,
|
|
||||||
18 => Ftruncate,
|
|
||||||
19 => Futimens,
|
|
||||||
|
|
||||||
20 => MmapPrep,
|
|
||||||
21 => RequestMmap,
|
|
||||||
22 => Mremap,
|
|
||||||
23 => Munmap,
|
|
||||||
24 => Msync,
|
|
||||||
|
|
||||||
25 => Cancel,
|
|
||||||
26 => Getdents,
|
|
||||||
27 => CloseMsg,
|
|
||||||
28 => Call,
|
|
||||||
|
|
||||||
29 => OpenAt,
|
|
||||||
30 => Flink,
|
|
||||||
31 => Recvfd,
|
|
||||||
|
|
||||||
32 => UnlinkAt,
|
|
||||||
33 => StdFsCall,
|
|
||||||
34 => Detach,
|
|
||||||
|
|
||||||
_ => return None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-340
@@ -1,340 +0,0 @@
|
|||||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
|
||||||
|
|
||||||
/// Signal runtime struct for the entire process
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[repr(C, align(4096))]
|
|
||||||
pub struct SigProcControl {
|
|
||||||
pub pending: AtomicU64,
|
|
||||||
pub actions: [RawAction; 64],
|
|
||||||
pub sender_infos: [AtomicU64; 32],
|
|
||||||
//pub queue: [RealtimeSig; 32], TODO
|
|
||||||
// qhead, qtail TODO
|
|
||||||
}
|
|
||||||
/*#[derive(Debug)]
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct RealtimeSig {
|
|
||||||
pub arg: NonatomicUsize,
|
|
||||||
}*/
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
#[repr(C, align(16))]
|
|
||||||
pub struct RawAction {
|
|
||||||
/// Only two MSBs are interesting for the kernel. If bit 63 is set, signal is ignored. If bit
|
|
||||||
/// 62 is set and the signal is SIGTSTP/SIGTTIN/SIGTTOU, it's equivalent to the action of
|
|
||||||
/// Stop.
|
|
||||||
pub first: AtomicU64,
|
|
||||||
/// Completely ignored by the kernel, but exists so userspace can (when 16-byte atomics exist)
|
|
||||||
/// atomically set both the handler, sigaction flags, and sigaction mask.
|
|
||||||
pub user_data: AtomicU64,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Signal runtime struct for a thread
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Sigcontrol {
|
|
||||||
// composed of [lo "pending" | lo "unmasked", hi "pending" | hi "unmasked"]
|
|
||||||
pub word: [AtomicU64; 2],
|
|
||||||
|
|
||||||
// lo = sender pid, hi = sender ruid
|
|
||||||
pub sender_infos: [AtomicU64; 32],
|
|
||||||
|
|
||||||
pub control_flags: SigatomicUsize,
|
|
||||||
|
|
||||||
pub saved_ip: NonatomicUsize, // rip/eip/pc
|
|
||||||
pub saved_archdep_reg: NonatomicUsize, // rflags(x64)/eflags(x86)/x0(aarch64)/t0(riscv64)
|
|
||||||
}
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub struct SenderInfo {
|
|
||||||
pub pid: u32,
|
|
||||||
pub ruid: u32,
|
|
||||||
}
|
|
||||||
impl SenderInfo {
|
|
||||||
#[inline]
|
|
||||||
pub fn raw(self) -> u64 {
|
|
||||||
u64::from(self.pid) | (u64::from(self.ruid) << 32)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub const fn from_raw(raw: u64) -> Self {
|
|
||||||
Self {
|
|
||||||
pid: raw as u32,
|
|
||||||
ruid: (raw >> 32) as u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Sigcontrol {
|
|
||||||
pub fn currently_pending_unblocked(&self, proc: &SigProcControl) -> u64 {
|
|
||||||
let proc_pending = proc.pending.load(Ordering::Relaxed);
|
|
||||||
let [w0, w1] = core::array::from_fn(|i| {
|
|
||||||
let w = self.word[i].load(Ordering::Relaxed);
|
|
||||||
((w | (proc_pending >> (i * 32))) & 0xffff_ffff) & (w >> 32)
|
|
||||||
});
|
|
||||||
//core::sync::atomic::fence(Ordering::Acquire);
|
|
||||||
w0 | (w1 << 32)
|
|
||||||
}
|
|
||||||
pub fn set_allowset(&self, new_allowset: u64) -> u64 {
|
|
||||||
//core::sync::atomic::fence(Ordering::Release);
|
|
||||||
let [w0, w1] = self.word.each_ref().map(|w| w.load(Ordering::Relaxed));
|
|
||||||
let old_a0 = w0 & 0xffff_ffff_0000_0000;
|
|
||||||
let old_a1 = w1 & 0xffff_ffff_0000_0000;
|
|
||||||
let new_a0 = (new_allowset & 0xffff_ffff) << 32;
|
|
||||||
let new_a1 = new_allowset & 0xffff_ffff_0000_0000;
|
|
||||||
|
|
||||||
let prev_w0 = self.word[0].fetch_add(new_a0.wrapping_sub(old_a0), Ordering::Relaxed);
|
|
||||||
let prev_w1 = self.word[0].fetch_add(new_a1.wrapping_sub(old_a1), Ordering::Relaxed);
|
|
||||||
//core::sync::atomic::fence(Ordering::Acquire);
|
|
||||||
let up0 = prev_w0 & (prev_w0 >> 32);
|
|
||||||
let up1 = prev_w1 & (prev_w1 >> 32);
|
|
||||||
|
|
||||||
up0 | (up1 << 32)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct SigatomicUsize(AtomicUsize);
|
|
||||||
|
|
||||||
impl SigatomicUsize {
|
|
||||||
#[inline]
|
|
||||||
pub fn load(&self, ordering: Ordering) -> usize {
|
|
||||||
let value = self.0.load(Ordering::Relaxed);
|
|
||||||
if ordering != Ordering::Relaxed {
|
|
||||||
core::sync::atomic::compiler_fence(ordering);
|
|
||||||
}
|
|
||||||
value
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn store(&self, value: usize, ordering: Ordering) {
|
|
||||||
if ordering != Ordering::Relaxed {
|
|
||||||
core::sync::atomic::compiler_fence(ordering);
|
|
||||||
}
|
|
||||||
self.0.store(value, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct NonatomicUsize(AtomicUsize);
|
|
||||||
|
|
||||||
impl NonatomicUsize {
|
|
||||||
#[inline]
|
|
||||||
pub const fn new(a: usize) -> Self {
|
|
||||||
Self(AtomicUsize::new(a))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn get(&self) -> usize {
|
|
||||||
self.0.load(Ordering::Relaxed)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set(&self, value: usize) {
|
|
||||||
self.0.store(value, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sig_bit(sig: usize) -> u64 {
|
|
||||||
1 << (sig - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Move to redox_rt?
|
|
||||||
impl SigProcControl {
|
|
||||||
/// Checks if `sig` should be ignored based on the current action flags.
|
|
||||||
///
|
|
||||||
/// * `sig` - The signal to check (e.g. `SIGCHLD`).
|
|
||||||
///
|
|
||||||
/// * `stop_or_continue` - Whether the signal is generated because a child
|
|
||||||
/// process stopped (`SIGSTOP`, `SIGTSTP`) or continued (`SIGCONT`). If
|
|
||||||
/// `true` and `sig` is `SIGCHLD`, the signal shall not be delivered if the
|
|
||||||
/// `SA_NOCLDSTOP` flag is set for `SIGCHLD`.
|
|
||||||
pub fn signal_will_ign(&self, sig: usize, stop_or_continue: bool) -> bool {
|
|
||||||
let flags = self.actions[sig - 1].first.load(Ordering::Relaxed);
|
|
||||||
let will_ign = flags & (1 << 63) != 0;
|
|
||||||
let sig_specific = flags & (1 << 62) != 0; // SA_NOCLDSTOP if sig == SIGCHLD
|
|
||||||
|
|
||||||
will_ign || (sig == SIGCHLD && stop_or_continue && sig_specific)
|
|
||||||
}
|
|
||||||
// TODO: Move to redox_rt?
|
|
||||||
pub fn signal_will_stop(&self, sig: usize) -> bool {
|
|
||||||
use crate::flag::*;
|
|
||||||
matches!(sig, SIGTSTP | SIGTTIN | SIGTTOU)
|
|
||||||
&& self.actions[sig - 1].first.load(Ordering::Relaxed) & (1 << 62) != 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "x86"))]
|
|
||||||
pub use core::sync::atomic::AtomicU64;
|
|
||||||
|
|
||||||
use crate::SIGCHLD;
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86")]
|
|
||||||
pub use self::atomic::AtomicU64;
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86")]
|
|
||||||
mod atomic {
|
|
||||||
use core::{cell::UnsafeCell, sync::atomic::Ordering};
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct AtomicU64(UnsafeCell<u64>);
|
|
||||||
|
|
||||||
unsafe impl Send for AtomicU64 {}
|
|
||||||
unsafe impl Sync for AtomicU64 {}
|
|
||||||
|
|
||||||
impl AtomicU64 {
|
|
||||||
pub const fn new(inner: u64) -> Self {
|
|
||||||
Self(UnsafeCell::new(inner))
|
|
||||||
}
|
|
||||||
pub fn compare_exchange(
|
|
||||||
&self,
|
|
||||||
old: u64,
|
|
||||||
new: u64,
|
|
||||||
_success: Ordering,
|
|
||||||
_failure: Ordering,
|
|
||||||
) -> Result<u64, u64> {
|
|
||||||
let old_hi = (old >> 32) as u32;
|
|
||||||
let old_lo = old as u32;
|
|
||||||
let new_hi = (new >> 32) as u32;
|
|
||||||
let new_lo = new as u32;
|
|
||||||
let mut out_hi;
|
|
||||||
let mut out_lo;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!("lock cmpxchg8b [{}]", in(reg) self.0.get(), inout("edx") old_hi => out_hi, inout("eax") old_lo => out_lo, in("ecx") new_hi, in("ebx") new_lo);
|
|
||||||
}
|
|
||||||
|
|
||||||
if old_hi == out_hi && old_lo == out_lo {
|
|
||||||
Ok(old)
|
|
||||||
} else {
|
|
||||||
Err(u64::from(out_lo) | (u64::from(out_hi) << 32))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn load(&self, ordering: Ordering) -> u64 {
|
|
||||||
match self.compare_exchange(0, 0, ordering, ordering) {
|
|
||||||
Ok(new) => new,
|
|
||||||
Err(new) => new,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn store(&self, new: u64, ordering: Ordering) {
|
|
||||||
let mut old = 0;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
match self.compare_exchange(old, new, ordering, Ordering::Relaxed) {
|
|
||||||
Ok(_) => break,
|
|
||||||
Err(new) => {
|
|
||||||
old = new;
|
|
||||||
core::hint::spin_loop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn fetch_update(
|
|
||||||
&self,
|
|
||||||
set_order: Ordering,
|
|
||||||
fetch_order: Ordering,
|
|
||||||
mut f: impl FnMut(u64) -> Option<u64>,
|
|
||||||
) -> Result<u64, u64> {
|
|
||||||
let mut old = self.load(fetch_order);
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let new = f(old).ok_or(old)?;
|
|
||||||
match self.compare_exchange(old, new, set_order, Ordering::Relaxed) {
|
|
||||||
Ok(_) => return Ok(new),
|
|
||||||
Err(changed) => {
|
|
||||||
old = changed;
|
|
||||||
core::hint::spin_loop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn fetch_or(&self, bits: u64, order: Ordering) -> u64 {
|
|
||||||
self.fetch_update(order, Ordering::Relaxed, |b| Some(b | bits))
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
pub fn fetch_and(&self, bits: u64, order: Ordering) -> u64 {
|
|
||||||
self.fetch_update(order, Ordering::Relaxed, |b| Some(b & bits))
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
pub fn fetch_add(&self, term: u64, order: Ordering) -> u64 {
|
|
||||||
self.fetch_update(order, Ordering::Relaxed, |b| Some(b.wrapping_add(term)))
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use std::sync::{
|
|
||||||
atomic::{AtomicU64, Ordering},
|
|
||||||
Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(not(loom))]
|
|
||||||
use std::{sync::Mutex, thread};
|
|
||||||
#[cfg(not(loom))]
|
|
||||||
fn model(f: impl FnOnce()) {
|
|
||||||
f()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(loom)]
|
|
||||||
use loom::{model, sync::Mutex, thread};
|
|
||||||
|
|
||||||
use crate::{RawAction, SigProcControl, Sigcontrol};
|
|
||||||
|
|
||||||
struct FakeThread {
|
|
||||||
ctl: Sigcontrol,
|
|
||||||
pctl: SigProcControl,
|
|
||||||
ctxt: Mutex<()>,
|
|
||||||
}
|
|
||||||
impl Default for FakeThread {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
ctl: Sigcontrol::default(),
|
|
||||||
pctl: SigProcControl {
|
|
||||||
pending: AtomicU64::new(0),
|
|
||||||
actions: core::array::from_fn(|_| RawAction::default()),
|
|
||||||
sender_infos: Default::default(),
|
|
||||||
},
|
|
||||||
ctxt: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn singlethread_mask() {
|
|
||||||
model(|| {
|
|
||||||
let fake_thread = Arc::new(FakeThread::default());
|
|
||||||
|
|
||||||
let thread = {
|
|
||||||
let fake_thread = Arc::clone(&fake_thread);
|
|
||||||
|
|
||||||
thread::spawn(move || {
|
|
||||||
fake_thread.ctl.set_allowset(!0);
|
|
||||||
{
|
|
||||||
let _g = fake_thread.ctxt.lock();
|
|
||||||
if fake_thread
|
|
||||||
.ctl
|
|
||||||
.currently_pending_unblocked(&fake_thread.pctl)
|
|
||||||
== 0
|
|
||||||
{
|
|
||||||
drop(_g);
|
|
||||||
thread::park();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
for sig in 1..=64 {
|
|
||||||
let _g = fake_thread.ctxt.lock();
|
|
||||||
|
|
||||||
let idx = sig - 1;
|
|
||||||
let bit = 1 << (idx % 32);
|
|
||||||
|
|
||||||
fake_thread.ctl.word[idx / 32].fetch_or(bit, Ordering::Relaxed);
|
|
||||||
let w = fake_thread.ctl.word[idx / 32].load(Ordering::Relaxed);
|
|
||||||
|
|
||||||
if w & (w >> 32) != 0 {
|
|
||||||
thread.thread().unpark();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
thread.join().unwrap();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user