Compare commits
328 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6afa6e5035 | |||
| 3559b6b969 | |||
| d90f84883e | |||
| e604868baf | |||
| 386b19566d | |||
| 4591fbabcc | |||
| ed6013c295 | |||
| e79c1be43e | |||
| b5b9bfd825 | |||
| 8c2b0f9de7 | |||
| 9800eac87c | |||
| e61506c50f | |||
| b29021c352 | |||
| 474ff08431 | |||
| f691afabab | |||
| f74e3fad19 | |||
| 3ef2cac56d | |||
| aa9c978f90 | |||
| a6fd457652 | |||
| 7806f87c56 | |||
| 70b8cb0e37 | |||
| 397974cb68 | |||
| 8765c51696 | |||
| 2d3b2ed38a | |||
| 1840ceac85 | |||
| 59d0872197 | |||
| 7f918e544e | |||
| 730eb9a606 | |||
| a790f1c228 | |||
| b33f3c1a80 | |||
| b0f90da7b8 | |||
| c4ed26e3d4 | |||
| 753256cfaf | |||
| cfc7938ecc | |||
| 15de2b4272 | |||
| fed265252d | |||
| d40619d66c | |||
| 64f46052db | |||
| eff771e640 | |||
| 397749f1f5 | |||
| 753c8af0e5 | |||
| 846e8446e0 | |||
| 385696d84a | |||
| ca9748f1ba | |||
| 7e697cfb3a | |||
| 86d2af6ca0 | |||
| 8ba8d6e15b | |||
| 3dba5827f2 | |||
| 433aa99660 | |||
| 990d9d343e | |||
| 8bc7c9d88d | |||
| 4fcd97e8da | |||
| 7a85862733 | |||
| 7308cfe259 | |||
| 58f161e019 | |||
| 693d9470f9 | |||
| 2a910e7058 | |||
| 5fad110e5e | |||
| 09531dfdc8 | |||
| e5233171ae | |||
| 8281316190 | |||
| 9594e87937 | |||
| bb9fa4ec7c | |||
| f30179a889 | |||
| 97dd77918e | |||
| 8e6a1c3aa4 | |||
| c35c9a88a9 | |||
| 43c1cd987f | |||
| 727c703bad | |||
| 0fa085c0f2 | |||
| 69de3d62a1 | |||
| c540ed8f9d | |||
| 7023dc0eb4 | |||
| 039ded92a4 | |||
| e74c9279cf | |||
| ee5a94dbe4 | |||
| b425b12cf5 | |||
| 432abd9249 | |||
| 01a3adb662 | |||
| 81e8bc09f8 | |||
| 5758ba854a | |||
| 64b047195d | |||
| 2730f1a70c | |||
| d82dd8998d | |||
| 721d49adfb | |||
| a86aec193d | |||
| 55a0bd1643 | |||
| 2c3afb4956 | |||
| 2c8c6bf30f | |||
| 0db88e3cb4 | |||
| 7ce72b7299 | |||
| 9e888c06fa | |||
| a4032c27b7 | |||
| fcc2dfb1c8 | |||
| d85eceac71 | |||
| a78377da19 | |||
| 2bf4865794 | |||
| d53435420b | |||
| 3a084e1c27 | |||
| e17550b6bd | |||
| 183e18be31 | |||
| 729c5631bc | |||
| 47282ba9dd | |||
| 7c359f1622 | |||
| 37cc477d5c | |||
| dc7d923e5a | |||
| 71ac2646bb | |||
| 17db563677 | |||
| cf83106610 | |||
| 0ee8e95f62 | |||
| b3e90be70b | |||
| 20fa660797 | |||
| eebccba932 | |||
| 4b9e9e17ab | |||
| 17263316d9 | |||
| 5c8cdd8950 | |||
| 5c52a2e80e | |||
| 7ccaccee4e | |||
| afc225c8d6 | |||
| 098fd8bbc9 | |||
| 66d66a537c | |||
| bfe70c409f | |||
| cb707c3ce4 | |||
| 9c3c5228df | |||
| caebc0d6f7 | |||
| 087810a252 | |||
| 34a832b43e | |||
| d352203794 | |||
| ad54d088d5 | |||
| a5534b8699 | |||
| e409c11e67 | |||
| 6895d9a9aa | |||
| 31d4816fcb | |||
| 2058101f61 | |||
| 69d4db511a | |||
| 52281ea9b5 | |||
| 7b9f9f663d | |||
| 7bcc07f357 | |||
| 5b7c64d587 | |||
| 248990b5ef | |||
| 2acf506d16 | |||
| f725079176 | |||
| 1c30f4a3c1 | |||
| 17219dab9f | |||
| 6dfcee04ad | |||
| dbfdd417f1 | |||
| 51f6efbaad | |||
| 3d417f7742 | |||
| a258044dcc | |||
| 9a1463dd11 | |||
| 7a7790e9bd | |||
| 09da116ccd | |||
| 6a4802d556 | |||
| 352407371c | |||
| 6f40e32d49 | |||
| b84146edd5 | |||
| adfee95484 | |||
| 1f4bbece4a | |||
| 5cdec46800 | |||
| c9e091be67 | |||
| aa1a982479 | |||
| b4f2f01d73 | |||
| 38f6aa70bd | |||
| 39f4b1c8b6 | |||
| ba2fc38a3f | |||
| f241649bba | |||
| 4821c55242 | |||
| cf9e2c9719 | |||
| 59e0f9e7ae | |||
| e0c909536c | |||
| cbdea61a61 | |||
| c5335d4aae | |||
| 2b34b31956 | |||
| d61b30de3f | |||
| 970cbfaef6 | |||
| 7a85c43974 | |||
| 7b35a76e49 | |||
| 363166ad1e | |||
| 3f306765ad | |||
| 20c0bdf60f | |||
| 46e14ea06f | |||
| 7e0f635d0e | |||
| 46e39bc109 | |||
| b04fcab423 | |||
| b23b2f6e5d | |||
| c4a7020e93 | |||
| 1411257784 | |||
| cfd556f3b3 | |||
| 257020852d | |||
| c91bea9452 | |||
| 41a656af92 | |||
| fadb0a1c1d | |||
| f710fa79db | |||
| b724e714d3 | |||
| c6d7cf16e1 | |||
| e24f5ef8e3 | |||
| fa836008c0 | |||
| 5e8e596d40 | |||
| 7b24f868b8 | |||
| 0b4f836f2a | |||
| 8d2d4cd62d | |||
| 9666c62036 | |||
| f103b5c4b9 | |||
| 4b1df136a6 | |||
| ddf59d8c7d | |||
| c3e679d5f1 | |||
| ad11b2adb2 | |||
| e8e4687c05 | |||
| 1ecb9b49fc | |||
| f1a806b288 | |||
| 4e7f7d8413 | |||
| be5eefd44f | |||
| 2b1107c856 | |||
| 2418286a29 | |||
| 0ed65d7d3d | |||
| f76771491c | |||
| f8ec4d28bc | |||
| e666636ab7 | |||
| 900f77e7f2 | |||
| adebf727dc | |||
| 9ea9a6ecac | |||
| 3c03576518 | |||
| 0c731043ac | |||
| 2c54a9b868 | |||
| b35e132153 | |||
| 41eba65942 | |||
| 1634877666 | |||
| ff2fdeca84 | |||
| 621a4dee2c | |||
| 84856f459d | |||
| 7f0a57108d | |||
| 70a2dc7532 | |||
| 224bb047dc | |||
| 150d65c31b | |||
| af5f64b807 | |||
| 03320a2025 | |||
| 03f996c70f | |||
| c22b1cf71b | |||
| 6a49914f0f | |||
| 3a1a257f0f | |||
| 1fccae679d | |||
| 7c5236fe35 | |||
| 3b0f59cbb0 | |||
| cd9e026705 | |||
| 9ff577792b | |||
| fbfc4edf6f | |||
| 505dec6069 | |||
| 89dcc2c060 | |||
| 7ef2567da6 | |||
| 5762840f79 | |||
| 39d68618ca | |||
| b75b1c2f86 | |||
| acb7bced58 | |||
| f30987b1e5 | |||
| 713359252a | |||
| f794ab2a66 | |||
| 40addc7020 | |||
| 8e88231ede | |||
| a262db6f25 | |||
| 78e9f77950 | |||
| 03fc74e21f | |||
| ed25899b0e | |||
| 49b7a5f427 | |||
| 7f38ffac1f | |||
| 33fa0af521 | |||
| 91c303df7d | |||
| 123350ae60 | |||
| be2e09472a | |||
| 03cae0ca37 | |||
| 8e39b2ea32 | |||
| 66b24d1116 | |||
| e58815f89c | |||
| ce6e96bd20 | |||
| 80add18dc9 | |||
| 1359501d3f | |||
| a1afbb144f | |||
| 999db45b0e | |||
| f6c5fd94a7 | |||
| 681fd3d0ef | |||
| 62412ef548 | |||
| 43e506c447 | |||
| ac759a17bd | |||
| a01dc9f37a | |||
| a82a577180 | |||
| a7b16dcb97 | |||
| 093ae7de55 | |||
| 25d6a0fec4 | |||
| 7ad7827497 | |||
| a4adb124d0 | |||
| 5cfe357821 | |||
| a76c8df867 | |||
| a9a1f0e76d | |||
| ed468af4c3 | |||
| 3efca19473 | |||
| c0bc46b954 | |||
| 2c0e14cadf | |||
| d73dd18f07 | |||
| 15d37cea36 | |||
| e47108a70e | |||
| d6f2936a53 | |||
| f99fac30ea | |||
| af1dd3b9fd | |||
| 31a60eb8a3 | |||
| 318e7bb89f | |||
| 8d6a4d7071 | |||
| 7e9f15e54f | |||
| c2bae24835 | |||
| 160a793017 | |||
| 7c7b30c16e | |||
| bb94edcb15 | |||
| e2aa3c3032 | |||
| bb56cee678 | |||
| 4e9d22ada4 | |||
| cfdb2a4968 | |||
| be092f3754 | |||
| eb2c0aafd4 | |||
| 5303301d3d | |||
| 9125d089fb | |||
| aeb9f89ec1 | |||
| 7d5baf798e | |||
| a56ec333ae | |||
| dd0a541bfe | |||
| a5773c3504 | |||
| e4db84e1d7 | |||
| 542ca6cb3d | |||
| 961983e788 | |||
| f666d06485 | |||
| 1abfd39b8d |
+4
-2
@@ -1,2 +1,4 @@
|
||||
Cargo.lock
|
||||
target
|
||||
pkg
|
||||
sysroot
|
||||
/target/
|
||||
/test.bin
|
||||
|
||||
+16
-9
@@ -1,17 +1,24 @@
|
||||
image: "redoxos/redoxer"
|
||||
image: "rust:latest"
|
||||
|
||||
stages:
|
||||
- build
|
||||
- lint
|
||||
- test
|
||||
|
||||
workflow:
|
||||
rules:
|
||||
- if: '$CI_COMMIT_BRANCH == "master" && $CI_PROJECT_NAMESPACE == "redox-os"'
|
||||
- if: '$CI_COMMIT_BRANCH == "master"'
|
||||
- if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"'
|
||||
|
||||
build:linux:
|
||||
stage: build
|
||||
script: cargo +nightly build
|
||||
fmt:
|
||||
stage: lint
|
||||
script:
|
||||
- rustup component add rustfmt
|
||||
- cargo fmt -- --check
|
||||
|
||||
build:redox:
|
||||
stage: build
|
||||
script: redoxer build
|
||||
cargo-test:
|
||||
stage: test
|
||||
script:
|
||||
- apt update && apt install -y fuse3 libfuse3-dev
|
||||
- cargo build --locked
|
||||
- cargo test
|
||||
- ./target/debug/redox_installer -c res/test.toml test.bin --no-mount
|
||||
|
||||
Generated
+2683
File diff suppressed because it is too large
Load Diff
+64
-16
@@ -1,25 +1,73 @@
|
||||
[package]
|
||||
name = "redox_syscall"
|
||||
version = "0.9.0+rb0.3.0"
|
||||
description = "A Rust library to access raw Redox system calls"
|
||||
name = "redox_installer"
|
||||
version = "0.2.42+rb0.3.0"
|
||||
description = "A Redox filesystem builder"
|
||||
license = "MIT"
|
||||
authors = ["Jeremy Soller <jackpot51@gmail.com>", "vasilito <adminpupkin@gmail.com>"]
|
||||
repository = "https://gitlab.redox-os.org/redox-os/syscall"
|
||||
documentation = "https://docs.rs/redox_syscall"
|
||||
repository = "https://gitlab.redox-os.org/redox-os/installer"
|
||||
default-run = "redox_installer"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
name = "syscall"
|
||||
[[bin]]
|
||||
name = "redox_installer"
|
||||
path = "src/bin/installer.rs"
|
||||
required-features = ["installer"]
|
||||
|
||||
[features]
|
||||
default = ["userspace"]
|
||||
rustc-dep-of-std = ["core", "bitflags/rustc-dep-of-std"]
|
||||
userspace = []
|
||||
std = []
|
||||
[[bin]]
|
||||
name = "redox_installer_tui"
|
||||
path = "src/bin/installer_tui.rs"
|
||||
required-features = ["installer"]
|
||||
|
||||
[lib]
|
||||
name = "redox_installer"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
bitflags = "2.4"
|
||||
core = { version = "1.0.0", optional = true, package = "rustc-std-workspace-core" }
|
||||
anyhow = "1"
|
||||
arg_parser = "0.1.0"
|
||||
fatfs = { version = "0.3.0", optional = true }
|
||||
fscommon = { version = "0.1.1", optional = true }
|
||||
gpt = { version = "3.0.0", optional = true }
|
||||
libc = "0.2.70"
|
||||
pkgar = { version = "0.2", optional = true }
|
||||
pkgar-core = { version = "0.2", optional = true }
|
||||
pkgar-keys = { version = "0.2", optional = true }
|
||||
rand = { version = "0.9", optional = true }
|
||||
redox-pkg = { version = "0.3", features = ["indicatif"], optional = true }
|
||||
redox_syscall = { path = "../syscall", optional = true }
|
||||
redoxfs = { path = "../redoxfs", optional = true, default-features = false, features = ["std", "log"] }
|
||||
rust-argon2 = { version = "3", optional = true }
|
||||
serde = "1"
|
||||
serde_derive = "1.0"
|
||||
termion = { version = "4", optional = true }
|
||||
toml = "0.8"
|
||||
uuid = { version = "1.4", features = ["v4"], optional = true }
|
||||
|
||||
[target.'cfg(loom)'.dev-dependencies]
|
||||
loom = "0.7"
|
||||
[target.'cfg(target_os = "redox")'.dependencies]
|
||||
libredox = { path = "../libredox" }
|
||||
ring = "=0.17.8"
|
||||
|
||||
[features]
|
||||
default = ["installer", "fuse"]
|
||||
installer = [
|
||||
"fatfs",
|
||||
"fscommon",
|
||||
"gpt",
|
||||
"pkgar",
|
||||
"pkgar-core",
|
||||
"pkgar-keys",
|
||||
"rand",
|
||||
"redox-pkg",
|
||||
"redox_syscall",
|
||||
"redoxfs",
|
||||
"rust-argon2",
|
||||
"termion",
|
||||
"uuid",
|
||||
]
|
||||
fuse = ["redoxfs/fuse"]
|
||||
|
||||
[patch.crates-io]
|
||||
# https://github.com/briansmith/ring/issues/1999
|
||||
ring = { git = "https://gitlab.redox-os.org/redox-os/ring.git", branch = "redox-0.17.8" }
|
||||
redox_syscall = { path = "../syscall" }
|
||||
libredox = { path = "../libredox" }
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
Copyright (c) 2017 Redox OS Developers
|
||||
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
Copyright (c) 2017 Redox OS
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
@@ -1,7 +1,50 @@
|
||||
# syscall
|
||||
# Redox OS installer
|
||||
|
||||
This crate contains the system call numbers and Rust wrappers for the inline Assembly code of system calls.
|
||||
The Redox installer will allow you to produce a Redox OS image. You will
|
||||
be able to specify:
|
||||
- Output device (raw image, ISO, QEMU, VirtualBox, drive)
|
||||
- Filesystem
|
||||
- Included packages
|
||||
- Method of installation (from source, from binary)
|
||||
- User accounts
|
||||
|
||||
[](./LICENSE)
|
||||
[](https://crates.io/crates/redox_syscall)
|
||||
[](https://docs.rs/redox_syscall)
|
||||
You will be prompted to install dependencies, based on your OS and method of
|
||||
installation. The easiest method is to install from binaries.
|
||||
|
||||
## Usage
|
||||
|
||||
It is recommended to compile with `cargo`, in release mode:
|
||||
```bash
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
By default, you will be prompted to supply configuration options. You can
|
||||
use the scripted mode by supplying a configuration file:
|
||||
```bash
|
||||
cargo run --release -- config/example.toml
|
||||
```
|
||||
An example configuration can be found in [config/example.toml](./config/example.toml).
|
||||
Unsuplied configuration will use the default. You can use the `general.prompt`
|
||||
setting to prompt when configuration is not set. Multiple configurations can
|
||||
be specified, they will be built in order.
|
||||
|
||||
## Embedding
|
||||
|
||||
The installer can also be used inside of other crates, as a library:
|
||||
|
||||
```toml
|
||||
# Cargo.toml
|
||||
[dependencies]
|
||||
redox_installer = "0.1"
|
||||
```
|
||||
|
||||
```rust
|
||||
// src/main.rs
|
||||
extern crate redox_installer;
|
||||
|
||||
fn main() {
|
||||
let mut config = redox_installer::Config::default();
|
||||
...
|
||||
redox_installer::install(config);
|
||||
}
|
||||
```
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
# This is the default configuration file
|
||||
|
||||
# General settings
|
||||
[general]
|
||||
# Do not prompt if settings are not defined
|
||||
prompt = false
|
||||
|
||||
# Package settings
|
||||
[packages]
|
||||
#acid = {}
|
||||
#autoconf = {}
|
||||
#automake = {}
|
||||
#bash = {}
|
||||
#binutils = {}
|
||||
#ca-certificates = {}
|
||||
#cargo = {}
|
||||
#contain = {}
|
||||
coreutils = {}
|
||||
#curl = {}
|
||||
#dash = {}
|
||||
#diffutils = {}
|
||||
drivers = {}
|
||||
extrautils = {}
|
||||
findutils = {}
|
||||
#games = {}
|
||||
#gawk = {}
|
||||
#gcc = {}
|
||||
#git = {}
|
||||
#gnu-binutils = {}
|
||||
#gnu-make = {}
|
||||
#installer = {}
|
||||
ion = {}
|
||||
#lua = {}
|
||||
#nasm = {}
|
||||
netstack = {}
|
||||
netutils = {}
|
||||
#newlib = {}
|
||||
#openssl = {}
|
||||
orbdata = {}
|
||||
orbital = {}
|
||||
orbterm = {}
|
||||
orbutils = {}
|
||||
pastel = {}
|
||||
#patch = {}
|
||||
#pixelcannon = {}
|
||||
pkgutils = {}
|
||||
ptyd = {}
|
||||
#python = {}
|
||||
randd = {}
|
||||
#redoxfs = {}
|
||||
#rust = {}
|
||||
#rustual-boy = {}
|
||||
#sed = {}
|
||||
smith = {}
|
||||
sodium = {}
|
||||
userutils = {}
|
||||
uutils = {}
|
||||
#xz = {}
|
||||
|
||||
# User settings
|
||||
[users.root]
|
||||
password = "password"
|
||||
uid = 0
|
||||
gid = 0
|
||||
name = "root"
|
||||
home = "/root"
|
||||
|
||||
[users.user]
|
||||
# Password is unset
|
||||
password = ""
|
||||
|
||||
[groups.sudo]
|
||||
gid = 1
|
||||
members = ["user"]
|
||||
|
||||
[[files]]
|
||||
path = "/etc/init.d/00_base"
|
||||
data = """
|
||||
pcid /etc/pcid/filesystem.toml
|
||||
randd
|
||||
ptyd
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/etc/init.d/10_net"
|
||||
data = """
|
||||
ethernetd
|
||||
ipd
|
||||
icmpd
|
||||
tcpd
|
||||
udpd
|
||||
dhcpd -b
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/etc/init.d/20_orbital"
|
||||
data = """
|
||||
orbital orblogin launcher
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/etc/init.d/30_console"
|
||||
data = """
|
||||
getty display/vesa:2
|
||||
getty debug: -J
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/etc/net/dns"
|
||||
data = """
|
||||
208.67.222.222
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/etc/net/ip"
|
||||
data = """
|
||||
10.0.2.15
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/etc/net/ip_router"
|
||||
data = """
|
||||
10.0.2.2
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/etc/net/ip_subnet"
|
||||
data = """
|
||||
255.255.255.0
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/etc/net/mac"
|
||||
data = """
|
||||
54-52-00-ab-cd-ef
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/etc/pkg.d/50_redox"
|
||||
data = "https://static.redox-os.org/pkg"
|
||||
|
||||
[[files]]
|
||||
path = "/etc/hostname"
|
||||
data = "redox"
|
||||
|
||||
[[files]]
|
||||
path = "/etc/issue"
|
||||
data = """
|
||||
########## Redox OS ##########
|
||||
# Login with the following: #
|
||||
# `user` #
|
||||
# `root`:`password` #
|
||||
##############################
|
||||
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/etc/motd"
|
||||
data = """
|
||||
Welcome to Redox OS!
|
||||
|
||||
"""
|
||||
|
||||
[[files]]
|
||||
path = "/usr"
|
||||
data = "/"
|
||||
symlink = true
|
||||
|
||||
[[files]]
|
||||
path = "/tmp"
|
||||
data = ""
|
||||
directory = true
|
||||
# 0o1777
|
||||
mode = 1023
|
||||
@@ -0,0 +1,24 @@
|
||||
# This is the default configuration file
|
||||
|
||||
# General settings
|
||||
[general]
|
||||
# Do not prompt if settings are not defined
|
||||
prompt = false
|
||||
|
||||
# Package settings
|
||||
[packages]
|
||||
binutils = {}
|
||||
coreutils = {}
|
||||
extrautils = {}
|
||||
ion = {}
|
||||
netutils = {}
|
||||
pkgutils = {}
|
||||
userutils = {}
|
||||
|
||||
# User settings
|
||||
[users.root]
|
||||
password = "password"
|
||||
uid = 0
|
||||
gid = 0
|
||||
name = "root"
|
||||
home = "/root"
|
||||
+411
@@ -0,0 +1,411 @@
|
||||
# Automatically generated by update.sh
|
||||
|
||||
include = []
|
||||
|
||||
[general]
|
||||
prompt = false
|
||||
filesystem_size = 256
|
||||
|
||||
[packages.base]
|
||||
|
||||
[packages.base-initfs]
|
||||
|
||||
[packages.bootloader]
|
||||
|
||||
[packages.ca-certificates]
|
||||
|
||||
[packages.coreutils]
|
||||
|
||||
[packages.extrautils]
|
||||
|
||||
[packages.findutils]
|
||||
|
||||
[packages.ion]
|
||||
|
||||
[packages.kernel]
|
||||
|
||||
[packages.kibi]
|
||||
|
||||
[packages.libgcc]
|
||||
|
||||
[packages.libstdcxx]
|
||||
|
||||
[packages.netdb]
|
||||
|
||||
[packages.netutils]
|
||||
|
||||
[packages.pkgutils]
|
||||
|
||||
[packages.relibc]
|
||||
|
||||
[packages.userutils]
|
||||
|
||||
[packages.uutils]
|
||||
|
||||
[[files]]
|
||||
path = "/usr/lib/init.d/00_base"
|
||||
data = """
|
||||
# clear and recreate tmpdir with 0o1777 permission
|
||||
rm -rf /tmp
|
||||
mkdir -m a=rwxt /tmp
|
||||
|
||||
ipcd
|
||||
ptyd
|
||||
nowait sudo --daemon
|
||||
"""
|
||||
symlink = false
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/usr/lib/init.d/00_drivers"
|
||||
data = """
|
||||
pcid-spawner /etc/pcid.d/
|
||||
"""
|
||||
symlink = false
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/etc/hostname"
|
||||
data = "redox"
|
||||
symlink = false
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/usr/lib/os-release"
|
||||
data = """
|
||||
PRETTY_NAME="Redox OS 0.9.0"
|
||||
NAME="Redox OS"
|
||||
VERSION_ID="0.9.0"
|
||||
VERSION="0.9.0"
|
||||
ID="redox-os"
|
||||
|
||||
HOME_URL="https://redox-os.org/"
|
||||
DOCUMENTATION_URL="https://redox-os.org/docs/"
|
||||
SUPPORT_URL="https://redox-os.org/community/"
|
||||
"""
|
||||
symlink = false
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/etc/os-release"
|
||||
data = "../usr/lib/os-release"
|
||||
symlink = true
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/etc/pkg.d/50_redox"
|
||||
data = "https://static.redox-os.org/pkg"
|
||||
symlink = false
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/usr"
|
||||
data = ""
|
||||
symlink = false
|
||||
directory = true
|
||||
mode = 493
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/usr/bin"
|
||||
data = ""
|
||||
symlink = false
|
||||
directory = true
|
||||
mode = 493
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/bin"
|
||||
data = "usr/bin"
|
||||
symlink = true
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/usr/include"
|
||||
data = ""
|
||||
symlink = false
|
||||
directory = true
|
||||
mode = 493
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/include"
|
||||
data = "usr/include"
|
||||
symlink = true
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/usr/lib"
|
||||
data = ""
|
||||
symlink = false
|
||||
directory = true
|
||||
mode = 493
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/lib"
|
||||
data = "usr/lib"
|
||||
symlink = true
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/usr/libexec"
|
||||
data = ""
|
||||
symlink = false
|
||||
directory = true
|
||||
mode = 493
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/usr/share"
|
||||
data = ""
|
||||
symlink = false
|
||||
directory = true
|
||||
mode = 493
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/share"
|
||||
data = "usr/share"
|
||||
symlink = true
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/usr/share/fonts"
|
||||
data = "../../ui/fonts"
|
||||
symlink = true
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/var"
|
||||
data = ""
|
||||
symlink = false
|
||||
directory = true
|
||||
mode = 493
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/var/cache"
|
||||
data = ""
|
||||
symlink = false
|
||||
directory = true
|
||||
mode = 493
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/var/lib"
|
||||
data = ""
|
||||
symlink = false
|
||||
directory = true
|
||||
mode = 493
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/var/lock"
|
||||
data = ""
|
||||
symlink = false
|
||||
directory = true
|
||||
mode = 1023
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/var/log"
|
||||
data = ""
|
||||
symlink = false
|
||||
directory = true
|
||||
mode = 493
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/var/run"
|
||||
data = ""
|
||||
symlink = false
|
||||
directory = true
|
||||
mode = 493
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/var/tmp"
|
||||
data = ""
|
||||
symlink = false
|
||||
directory = true
|
||||
mode = 1023
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/dev/null"
|
||||
data = "/scheme/null"
|
||||
symlink = true
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/dev/random"
|
||||
data = "/scheme/rand"
|
||||
symlink = true
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/dev/urandom"
|
||||
data = "/scheme/rand"
|
||||
symlink = true
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/dev/zero"
|
||||
data = "/scheme/zero"
|
||||
symlink = true
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/dev/tty"
|
||||
data = "libc:tty"
|
||||
symlink = true
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/dev/stdin"
|
||||
data = "libc:stdin"
|
||||
symlink = true
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/dev/stdout"
|
||||
data = "libc:stdout"
|
||||
symlink = true
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/dev/stderr"
|
||||
data = "libc:stderr"
|
||||
symlink = true
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/usr/lib/init.d/10_net"
|
||||
data = """
|
||||
smolnetd
|
||||
nowait dhcpd
|
||||
"""
|
||||
symlink = false
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/etc/net/dns"
|
||||
data = """
|
||||
9.9.9.9
|
||||
"""
|
||||
symlink = false
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/etc/net/ip"
|
||||
data = """
|
||||
10.0.2.15
|
||||
"""
|
||||
symlink = false
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/etc/net/ip_router"
|
||||
data = """
|
||||
10.0.2.2
|
||||
"""
|
||||
symlink = false
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/etc/net/ip_subnet"
|
||||
data = """
|
||||
255.255.255.0
|
||||
"""
|
||||
symlink = false
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[[files]]
|
||||
path = "/usr/lib/init.d/30_console"
|
||||
data = """
|
||||
inputd -A 2
|
||||
nowait getty 2
|
||||
nowait getty /scheme/debug -J
|
||||
"""
|
||||
symlink = false
|
||||
directory = false
|
||||
recursive_chown = false
|
||||
postinstall = false
|
||||
|
||||
[users.root]
|
||||
password = "password"
|
||||
uid = 0
|
||||
gid = 0
|
||||
name = "root"
|
||||
home = "/root"
|
||||
shell = "/usr/bin/ion"
|
||||
|
||||
[users.user]
|
||||
password = ""
|
||||
shell = "/usr/bin/ion"
|
||||
|
||||
[groups.sudo]
|
||||
gid = 1
|
||||
members = ["user"]
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
RES_PATH="$(dirname "$0")"
|
||||
|
||||
if [ -d "$1" ]
|
||||
then
|
||||
REDOX_PATH="$1"
|
||||
else
|
||||
echo "$0 [path to redox repository]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -x
|
||||
|
||||
# Update res/test.toml from the redoxer.toml template
|
||||
"${REDOX_PATH}/build/fstools/bin/redox_installer" \
|
||||
--config="${REDOX_PATH}/config/x86_64/minimal-net.toml" \
|
||||
--output-config="${RES_PATH}/test.toml"
|
||||
sed -i '1s/^/# Automatically generated by update.sh\n\n/' "${RES_PATH}/test.toml"
|
||||
@@ -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,134 @@
|
||||
extern crate arg_parser;
|
||||
extern crate redox_installer;
|
||||
extern crate serde;
|
||||
extern crate toml;
|
||||
|
||||
use std::path::Path;
|
||||
use std::{env, fs, process};
|
||||
|
||||
use arg_parser::ArgParser;
|
||||
|
||||
use redox_installer::{Config, PackageConfig};
|
||||
|
||||
const HELP_STR: &str = r#"
|
||||
redox_installer - Redox Installer.
|
||||
Refer to link below for filesystem config reference:
|
||||
https://doc.redox-os.org/book/configuration-settings.html
|
||||
|
||||
Using redox_installer as an installer:
|
||||
redox_installer <diskpath.img> [--config=file.toml] [--write-bootloader=file.img] [--live] [--no-mount] [--skip-partition]
|
||||
<diskpath.img> Disk file to write
|
||||
--config Path to filesystem config TOML
|
||||
--write-bootloader Path to write UEFI bootloader to in addition to the embedded ESP
|
||||
--skip-partition Skip writing GPT partition tables
|
||||
Use this only if you plan to use other partition tool
|
||||
--live Use bootloader configured for live disk
|
||||
--no-mount Use RedoxFS AR instead of FUSE to write files
|
||||
--cookbook Use local Redox OS build system rather than downloading packages
|
||||
|
||||
Using redox_installer as a configuration parser:
|
||||
redox_installer --config=file.toml [--list-packages|--filesystem-size|--output-config path]
|
||||
--list-packages List packages will be installed
|
||||
--filesystem-size Output filesystem size in MB
|
||||
--output-config Path to write the parsed config as another TOML
|
||||
"#;
|
||||
|
||||
fn main() {
|
||||
let mut parser = ArgParser::new(4)
|
||||
.add_opt("b", "cookbook")
|
||||
.add_opt("c", "config")
|
||||
.add_opt("o", "output-config")
|
||||
.add_opt("", "write-bootloader")
|
||||
.add_flag(&["skip-partition"])
|
||||
.add_flag(&["filesystem-size"])
|
||||
.add_flag(&["r", "repo-binary"]) // TODO: Remove
|
||||
.add_flag(&["l", "list-packages"])
|
||||
.add_flag(&["live"])
|
||||
.add_flag(&["no-mount"]);
|
||||
parser.parse(env::args());
|
||||
|
||||
let skip_partition = parser.found("skip-partition");
|
||||
|
||||
let mut config = if let Some(path) = parser.get_opt("config") {
|
||||
match Config::from_file(Path::new(&path)) {
|
||||
Ok(config) => config,
|
||||
Err(err) => {
|
||||
eprintln!("installer: {err}");
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
redox_installer::Config::default()
|
||||
};
|
||||
|
||||
// Get toml of merged config
|
||||
let merged_toml = toml::to_string_pretty(&config).unwrap();
|
||||
|
||||
// Just output merged config and exit
|
||||
if let Some(path) = parser.get_opt("output-config") {
|
||||
fs::write(path, merged_toml).unwrap();
|
||||
return;
|
||||
}
|
||||
|
||||
// Add filesystem.toml to config
|
||||
config.files.push(redox_installer::FileConfig {
|
||||
path: "filesystem.toml".to_string(),
|
||||
data: merged_toml,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
if skip_partition {
|
||||
config.general.skip_partitions = Some(true);
|
||||
}
|
||||
|
||||
if parser.found("filesystem-size") {
|
||||
println!("{}", config.general.filesystem_size.unwrap_or(0));
|
||||
} else if parser.found("list-packages") {
|
||||
// List the packages that should be fetched or built by the cookbook
|
||||
for (packagename, package) in &config.packages {
|
||||
match package {
|
||||
PackageConfig::Build(rule) if rule == "ignore" => {
|
||||
// skip this package
|
||||
}
|
||||
_ => {
|
||||
println!("{}", packagename);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let cookbook = if let Some(path) = parser.get_opt("cookbook") {
|
||||
if !Path::new(&path).is_dir() {
|
||||
eprintln!("installer: {}: cookbook not found", path);
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
Some(path)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if cookbook.is_some() {
|
||||
config.general.cookbook = cookbook;
|
||||
}
|
||||
if parser.found("live") {
|
||||
config.general.live_disk = Some(true);
|
||||
}
|
||||
if parser.found("no-mount") {
|
||||
config.general.no_mount = Some(true);
|
||||
}
|
||||
let write_bootloader = parser.get_opt("write-bootloader");
|
||||
if write_bootloader.is_some() {
|
||||
config.general.write_bootloader = write_bootloader;
|
||||
}
|
||||
|
||||
if let Some(path) = parser.args.first() {
|
||||
if let Err(err) = redox_installer::install(config, path) {
|
||||
eprintln!("installer: failed to install: {:?}", err);
|
||||
process::exit(1);
|
||||
}
|
||||
} else {
|
||||
eprint!("{}", HELP_STR);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,392 @@
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use pkgar::{ext::EntryExt, PackageHead};
|
||||
use pkgar_core::PackageSrc;
|
||||
use pkgar_keys::PublicKeyFile;
|
||||
use redox_installer::{try_fast_install, with_redoxfs_mount, with_whole_disk, Config, DiskOption};
|
||||
use std::{
|
||||
ffi::OsStr,
|
||||
fs,
|
||||
io::{self, Read, Write},
|
||||
os::unix::fs::{symlink, MetadataExt, OpenOptionsExt},
|
||||
path::{Path, PathBuf},
|
||||
process,
|
||||
};
|
||||
|
||||
// TODO: This is not the TUI a regular user would expect it does
|
||||
// 1. Linux: Implement disk listing, use "dd" to write into whole disk
|
||||
// 2. Allow partitioning to allow dual boot, possibly an integration with systemd-boot/grub
|
||||
// 3. Prompt everything (disk password, users, preconfigured packages, import from existing img)
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
fn disk_paths(_paths: &mut Vec<(PathBuf, u64)>) {}
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
fn disk_paths(paths: &mut Vec<(PathBuf, u64)>) {
|
||||
let mut schemes = Vec::new();
|
||||
match fs::read_dir("/scheme") {
|
||||
Ok(entries) => {
|
||||
for entry_res in entries {
|
||||
if let Ok(entry) = entry_res {
|
||||
if let Ok(file_name) = entry.file_name().into_string() {
|
||||
if file_name.starts_with("disk") {
|
||||
schemes.push(entry.path());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("redox_installer_tui: failed to list schemes: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
for scheme in schemes {
|
||||
if scheme.is_dir() {
|
||||
match fs::read_dir(&scheme) {
|
||||
Ok(entries) => {
|
||||
for entry_res in entries {
|
||||
if let Ok(entry) = entry_res {
|
||||
if let Ok(file_name) = entry.file_name().into_string() {
|
||||
if file_name.contains('p') {
|
||||
// Skip partitions
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Ok(metadata) = entry.metadata() {
|
||||
let size = metadata.len();
|
||||
if size > 0 {
|
||||
paths.push((entry.path(), size));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!(
|
||||
"redox_installer_tui: failed to list '{}': {}",
|
||||
scheme.display(),
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const KIB: u64 = 1024;
|
||||
const MIB: u64 = 1024 * KIB;
|
||||
const GIB: u64 = 1024 * MIB;
|
||||
const TIB: u64 = 1024 * GIB;
|
||||
|
||||
fn format_size(size: u64) -> String {
|
||||
if size >= 4 * TIB {
|
||||
format!("{:.1} TiB", size as f64 / TIB as f64)
|
||||
} else if size >= GIB {
|
||||
format!("{:.1} GiB", size as f64 / GIB as f64)
|
||||
} else if size >= MIB {
|
||||
format!("{:.1} MiB", size as f64 / MIB as f64)
|
||||
} else if size >= KIB {
|
||||
format!("{:.1} KiB", size as f64 / KIB as f64)
|
||||
} else {
|
||||
format!("{} B", size)
|
||||
}
|
||||
}
|
||||
|
||||
fn copy_file(src: &Path, dest: &Path, buf: &mut [u8]) -> Result<()> {
|
||||
if let Some(parent) = dest.parent() {
|
||||
// Parent may be a symlink
|
||||
if !parent.is_symlink() {
|
||||
match fs::create_dir_all(&parent) {
|
||||
Ok(()) => (),
|
||||
Err(err) => {
|
||||
bail!("failed to create directory {}: {}", parent.display(), err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let metadata = match fs::symlink_metadata(&src) {
|
||||
Ok(ok) => ok,
|
||||
Err(err) => {
|
||||
bail!("failed to read metadata of {}: {}", src.display(), err);
|
||||
}
|
||||
};
|
||||
|
||||
if metadata.file_type().is_symlink() {
|
||||
let real_src = match fs::read_link(&src) {
|
||||
Ok(ok) => ok,
|
||||
Err(err) => {
|
||||
bail!("failed to read link {}: {}", src.display(), err);
|
||||
}
|
||||
};
|
||||
|
||||
match symlink(&real_src, &dest) {
|
||||
Ok(()) => (),
|
||||
Err(err) => {
|
||||
bail!(
|
||||
"failed to copy link {} ({}) to {}: {}",
|
||||
src.display(),
|
||||
real_src.display(),
|
||||
dest.display(),
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let mut src_file = match fs::File::open(&src) {
|
||||
Ok(ok) => ok,
|
||||
Err(err) => {
|
||||
bail!("failed to open file {}: {}", src.display(), err);
|
||||
}
|
||||
};
|
||||
|
||||
let mut dest_file = match fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.mode(metadata.mode())
|
||||
.open(&dest)
|
||||
{
|
||||
Ok(ok) => ok,
|
||||
Err(err) => {
|
||||
bail!("failed to create file {}: {}", dest.display(), err);
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
let count = match src_file.read(buf) {
|
||||
Ok(ok) => ok,
|
||||
Err(err) => {
|
||||
bail!("failed to read file {}: {}", src.display(), err);
|
||||
}
|
||||
};
|
||||
|
||||
if count == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
match dest_file.write_all(&buf[..count]) {
|
||||
Ok(()) => (),
|
||||
Err(err) => {
|
||||
bail!("failed to write file {}: {}", dest.display(), err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn package_files(
|
||||
root_path: &Path,
|
||||
config: &mut Config,
|
||||
files: &mut Vec<String>,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
//TODO: Remove packages from config where all files are located (and have valid shasum?)
|
||||
config.packages.clear();
|
||||
|
||||
let pkey_path = "pkg/id_ed25519.pub.toml";
|
||||
let pkey = PublicKeyFile::open(&root_path.join(pkey_path))?.pkey;
|
||||
files.push(pkey_path.to_string());
|
||||
|
||||
for item_res in fs::read_dir(&root_path.join("pkg"))? {
|
||||
let item = item_res?;
|
||||
let pkg_path = item.path();
|
||||
if pkg_path.extension() == Some(OsStr::new("pkgar_head")) {
|
||||
let mut pkg = PackageHead::new(&pkg_path, &root_path, &pkey)?;
|
||||
for entry in pkg.read_entries()? {
|
||||
files.push(entry.check_path()?.to_str().unwrap().to_string());
|
||||
}
|
||||
files.push(
|
||||
pkg_path
|
||||
.strip_prefix(root_path)
|
||||
.unwrap()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn choose_disk() -> PathBuf {
|
||||
let mut paths = Vec::new();
|
||||
disk_paths(&mut paths);
|
||||
loop {
|
||||
for (i, (path, size)) in paths.iter().enumerate() {
|
||||
eprintln!(
|
||||
"\x1B[1m{}\x1B[0m: {}: {}",
|
||||
i + 1,
|
||||
path.display(),
|
||||
format_size(*size)
|
||||
);
|
||||
}
|
||||
|
||||
if paths.is_empty() {
|
||||
eprintln!("redox_installer_tui: no RedoxFS partition found");
|
||||
eprintln!("redox_installer_tui: this tool is used to overwrite unmounted RedoxFS disk in Redox OS");
|
||||
process::exit(1);
|
||||
} else {
|
||||
eprint!("Select a drive from 1 to {}: ", paths.len());
|
||||
|
||||
let mut line = String::new();
|
||||
match io::stdin().read_line(&mut line) {
|
||||
Ok(0) => {
|
||||
eprintln!("redox_installer_tui: failed to read line: end of input");
|
||||
process::exit(1);
|
||||
}
|
||||
Ok(_) => (),
|
||||
Err(err) => {
|
||||
eprintln!("redox_installer_tui: failed to read line: {}", err);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
match line.trim().parse::<usize>() {
|
||||
Ok(i) => {
|
||||
if i >= 1 && i <= paths.len() {
|
||||
break paths[i - 1].0.clone();
|
||||
} else {
|
||||
eprintln!("{} not from 1 to {}", i, paths.len());
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("invalid input: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let root_path = Path::new("/");
|
||||
|
||||
let disk_path = choose_disk();
|
||||
|
||||
let Ok(password_opt) = redox_installer::prompt_password(
|
||||
"redox_installer_tui: redoxfs password (empty for none)",
|
||||
"redox_installer_tui: confirm password",
|
||||
) else {
|
||||
process::exit(1);
|
||||
};
|
||||
|
||||
let instant = std::time::Instant::now();
|
||||
|
||||
let bootloader_bios = {
|
||||
let path = root_path.join("usr/lib/boot/bootloader.bios");
|
||||
if path.exists() {
|
||||
match fs::read(&path) {
|
||||
Ok(ok) => ok,
|
||||
Err(err) => {
|
||||
eprintln!(
|
||||
"redox_installer_tui: {}: failed to read: {}",
|
||||
path.display(),
|
||||
err
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
};
|
||||
|
||||
let bootloader_efi = {
|
||||
let path = root_path.join("usr/lib/boot/bootloader.efi");
|
||||
if path.exists() {
|
||||
match fs::read(&path) {
|
||||
Ok(ok) => ok,
|
||||
Err(err) => {
|
||||
eprintln!(
|
||||
"redox_installer_tui: {}: failed to read: {}",
|
||||
path.display(),
|
||||
err
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
};
|
||||
|
||||
let disk_option = DiskOption {
|
||||
bootloader_bios: &bootloader_bios,
|
||||
bootloader_efi: &bootloader_efi,
|
||||
password_opt: password_opt.as_ref().map(|x| x.as_bytes()),
|
||||
efi_partition_size: None,
|
||||
skip_partitions: false, // TODO?
|
||||
};
|
||||
let res = with_whole_disk(&disk_path, &disk_option, |mut fs| {
|
||||
// Fast install method via filesystem clone
|
||||
let mut last_percent = 0;
|
||||
if try_fast_install(&mut fs, move |used, used_old| {
|
||||
let percent = (used * 100) / used_old;
|
||||
if percent != last_percent {
|
||||
eprint!(
|
||||
"\r{}%: {} MB/{} MB",
|
||||
percent,
|
||||
used / 1000 / 1000,
|
||||
used_old / 1000 / 1000
|
||||
);
|
||||
last_percent = percent;
|
||||
}
|
||||
})? {
|
||||
eprintln!("\rfinished installing using fast mode");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Slow install method via file copy
|
||||
with_redoxfs_mount(fs, None, |mount_path| {
|
||||
let mut config: Config = Config::from_file(&root_path.join("filesystem.toml"))?;
|
||||
|
||||
// Copy filesystem.toml, which is not packaged
|
||||
let mut files = vec!["filesystem.toml".to_string()];
|
||||
|
||||
// Copy files from locally installed packages
|
||||
package_files(&root_path, &mut config, &mut files)
|
||||
// TODO: implement Error trait
|
||||
.map_err(|err| anyhow!("failed to read package files: {err}"))?;
|
||||
|
||||
// Perform config install (after packages have been converted to files)
|
||||
eprintln!("configuring system");
|
||||
let cookbook: Option<&'static str> = None;
|
||||
redox_installer::install_dir(config, mount_path, cookbook)
|
||||
.map_err(|err| io::Error::other(err))?;
|
||||
|
||||
// Sort and remove duplicates
|
||||
files.sort();
|
||||
files.dedup();
|
||||
|
||||
// Install files
|
||||
let mut buf = vec![0; 4 * MIB as usize];
|
||||
for (i, name) in files.iter().enumerate() {
|
||||
eprintln!("copy {} [{}/{}]", name, i, files.len());
|
||||
|
||||
let src = root_path.join(name);
|
||||
let dest = mount_path.join(name);
|
||||
copy_file(&src, &dest, &mut buf)?;
|
||||
}
|
||||
|
||||
eprintln!("finished installing, unmounting filesystem");
|
||||
|
||||
Ok(())
|
||||
})
|
||||
});
|
||||
|
||||
match res {
|
||||
Ok(()) => {
|
||||
eprintln!(
|
||||
"redox_installer_tui: installed successfully in {:?}",
|
||||
instant.elapsed()
|
||||
);
|
||||
process::exit(0);
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("redox_installer_tui: failed to install: {:?}", err);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
-391
@@ -1,391 +0,0 @@
|
||||
use super::{
|
||||
arch::*,
|
||||
data::{Map, Stat, StdFsCallMeta, TimeSpec},
|
||||
error::Result,
|
||||
flag::*,
|
||||
number::*,
|
||||
};
|
||||
|
||||
use core::mem;
|
||||
|
||||
/// Close a file descriptor
|
||||
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 into specified fd number
|
||||
pub fn dup_into(fd: usize, out: usize, buf: &[u8]) -> Result<usize> {
|
||||
unsafe { syscall4(SYS_DUP_INTO, fd, buf.as_ptr() as usize, buf.len(), out) }
|
||||
}
|
||||
|
||||
/// Copy and transform a file descriptor, letting the kernel allocate the new fd
|
||||
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>(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sync a file descriptor to its underlying medium
|
||||
pub fn fsync(fd: usize) -> Result<usize> {
|
||||
unsafe { syscall1(SYS_FSYNC, fd) }
|
||||
}
|
||||
|
||||
/// 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) }
|
||||
}
|
||||
|
||||
/// 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 into specified fd number
|
||||
pub fn openat_into<T: AsRef<str>>(
|
||||
fd: usize,
|
||||
out: usize,
|
||||
path: T,
|
||||
flags: usize,
|
||||
fcntl_flags: usize,
|
||||
) -> Result<usize> {
|
||||
let path = path.as_ref();
|
||||
unsafe {
|
||||
syscall6(
|
||||
SYS_OPENAT_INTO,
|
||||
fd,
|
||||
path.as_ptr() as usize,
|
||||
path.len(),
|
||||
flags,
|
||||
fcntl_flags,
|
||||
out,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Open a file at a specific path, letting the kernel allocate the new fd
|
||||
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 uid/gid filtering.
|
||||
/// Red Bear: legacy wrapper preserved for bootstrap namespace capability checks.
|
||||
pub fn openat_with_filter<T: AsRef<str>>(
|
||||
fd: usize,
|
||||
path: T,
|
||||
flags: usize,
|
||||
fcntl_flags: usize,
|
||||
uid: u32,
|
||||
gid: 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,
|
||||
uid as usize,
|
||||
gid as usize,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a file descriptor to another process via a socket
|
||||
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 usize,
|
||||
)
|
||||
}
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
unsafe {
|
||||
syscall4(SYS_SENDFD, receiver_socket, fd, flags, arg 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 a specific path with uid/gid filtering.
|
||||
/// Red Bear: legacy wrapper preserved for bootstrap namespace capability checks.
|
||||
pub fn unlinkat_with_filter<T: AsRef<str>>(
|
||||
fd: usize,
|
||||
path: T,
|
||||
flags: usize,
|
||||
uid: u32,
|
||||
gid: u32,
|
||||
) -> Result<usize> {
|
||||
let path = path.as_ref();
|
||||
unsafe {
|
||||
syscall6(
|
||||
SYS_UNLINKAT_WITH_FILTER,
|
||||
fd,
|
||||
path.as_ptr() as usize,
|
||||
path.len(),
|
||||
flags,
|
||||
uid as usize,
|
||||
gid 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) }
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
pub struct FileConfig {
|
||||
pub path: String,
|
||||
pub data: String,
|
||||
#[serde(default)]
|
||||
pub symlink: bool,
|
||||
#[serde(default)]
|
||||
pub directory: bool,
|
||||
pub mode: Option<u32>,
|
||||
pub uid: Option<u32>,
|
||||
pub gid: Option<u32>,
|
||||
#[serde(default)]
|
||||
pub recursive_chown: bool,
|
||||
#[serde(default)]
|
||||
pub postinstall: bool,
|
||||
}
|
||||
|
||||
impl FileConfig {
|
||||
pub fn new_file(path: String, data: String) -> FileConfig {
|
||||
FileConfig {
|
||||
path,
|
||||
data,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_directory(path: String) -> FileConfig {
|
||||
FileConfig {
|
||||
path,
|
||||
data: String::new(),
|
||||
directory: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_mod(&mut self, mode: u32, uid: u32, gid: u32) -> &mut FileConfig {
|
||||
self.mode = Some(mode);
|
||||
self.uid = Some(uid);
|
||||
self.gid = Some(gid);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_recursive_mod(&mut self, mode: u32, uid: u32, gid: u32) -> &mut FileConfig {
|
||||
self.with_mod(mode, uid, gid);
|
||||
self.recursive_chown = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for FileConfig {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.path)?;
|
||||
if self.symlink {
|
||||
write!(f, " -> {}", self.data)?;
|
||||
} else if self.directory {
|
||||
write!(f, " type=dir")?;
|
||||
if self.recursive_chown {
|
||||
write!(f, " chown=yes")?;
|
||||
}
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
" size={}B",
|
||||
arg_parser::to_human_readable_string(self.data.len() as u64)
|
||||
)?;
|
||||
if self.postinstall {
|
||||
write!(f, "!")?;
|
||||
}
|
||||
}
|
||||
if let Some(uid) = self.uid {
|
||||
write!(f, " uid={}", uid)?;
|
||||
}
|
||||
if let Some(uid) = self.uid {
|
||||
write!(f, " gid={}", uid)?;
|
||||
}
|
||||
if let Some(mode) = self.mode {
|
||||
write!(f, " mode={:3o}", mode)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
use anyhow::{Context, Result};
|
||||
use libc::{gid_t, uid_t};
|
||||
|
||||
use std::ffi::{CString, OsStr};
|
||||
use std::fs::{self, File};
|
||||
use std::io::{Error, Write};
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::os::unix::fs::{symlink, PermissionsExt};
|
||||
use std::path::Path;
|
||||
|
||||
fn chown<P: AsRef<Path>>(path: P, uid: uid_t, gid: gid_t, recursive: bool) -> Result<()> {
|
||||
let path = path.as_ref();
|
||||
|
||||
let c_path = CString::new(path.as_os_str().as_bytes()).unwrap();
|
||||
if unsafe { libc::chown(c_path.as_ptr(), uid, gid) } != 0 {
|
||||
return Err(Error::last_os_error().into());
|
||||
}
|
||||
|
||||
if recursive && path.is_dir() {
|
||||
for entry_res in fs::read_dir(path)? {
|
||||
let entry = entry_res?;
|
||||
chown(entry.path(), uid, gid, recursive)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// TODO: Rewrite impls
|
||||
impl crate::FileConfig {
|
||||
pub(crate) fn create<P: AsRef<Path>>(&self, prefix: P) -> Result<()> {
|
||||
let path = self.path.trim_start_matches('/');
|
||||
let target_file = prefix.as_ref().join(path);
|
||||
|
||||
if self.directory {
|
||||
println!("Create directory {}", target_file.display());
|
||||
fs::create_dir_all(&target_file)
|
||||
.with_context(|| format!("failed to create directory {}", target_file.display()))?;
|
||||
self.apply_perms(&target_file)?;
|
||||
return Ok(());
|
||||
} else if let Some(parent) = target_file.parent() {
|
||||
println!("Create file parent {}", parent.display());
|
||||
fs::create_dir_all(parent)
|
||||
.with_context(|| format!("failed to create file parent {}", parent.display()))?;
|
||||
}
|
||||
|
||||
if self.symlink {
|
||||
println!("Create symlink {} to {}", target_file.display(), self.data);
|
||||
if target_file.is_symlink() {
|
||||
fs::remove_file(&target_file).with_context(|| {
|
||||
format!("failed to remove old symlink {}", target_file.display())
|
||||
})?;
|
||||
}
|
||||
symlink(&OsStr::new(&self.data), &target_file).with_context(|| {
|
||||
format!(
|
||||
"failed to create symlink {} to {}",
|
||||
target_file.display(),
|
||||
self.data
|
||||
)
|
||||
})?;
|
||||
Ok(())
|
||||
} else {
|
||||
println!("Create file {}", target_file.display());
|
||||
let mut file = File::create(&target_file)
|
||||
.with_context(|| format!("failed to create file {}", target_file.display()))?;
|
||||
file.write_all(self.data.as_bytes())?;
|
||||
|
||||
self.apply_perms(target_file)
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_perms<P: AsRef<Path>>(&self, target: P) -> Result<()> {
|
||||
let path = target.as_ref();
|
||||
let mode = self
|
||||
.mode
|
||||
.unwrap_or_else(|| if self.directory { 0o0755 } else { 0o0644 });
|
||||
let uid = self.uid.unwrap_or(!0);
|
||||
let gid = self.gid.unwrap_or(!0);
|
||||
|
||||
// chmod
|
||||
fs::set_permissions(path, fs::Permissions::from_mode(mode))
|
||||
.with_context(|| format!("failed to set permissions on {}", path.display()))?;
|
||||
|
||||
// chown
|
||||
chown(path, uid, gid, self.recursive_chown)
|
||||
.with_context(|| format!("failed to chown {}", path.display()))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
pub struct GeneralConfig {
|
||||
/// Specify a path where cookbook exists, all packages will be installed locally
|
||||
pub cookbook: Option<String>,
|
||||
/// Allow prompts for missing information such as user password
|
||||
pub prompt: Option<bool>,
|
||||
/// Total filesystem size in MB
|
||||
pub filesystem_size: Option<u32>,
|
||||
/// EFI partition size in MB, default to 2MB
|
||||
pub efi_partition_size: Option<u32>,
|
||||
/// Skip disk partitioning, assume whole disk is a partition
|
||||
pub skip_partitions: Option<bool>,
|
||||
/// Set a plain text password to encrypt the disk
|
||||
pub encrypt_disk: Option<String>,
|
||||
/// Use live disk for bootloader config, default is false
|
||||
pub live_disk: Option<bool>,
|
||||
/// If set, write bootloader disk into this path
|
||||
pub write_bootloader: Option<String>,
|
||||
/// Use AR to write files instead of FUSE-based mount
|
||||
/// (bypasses FUSE, but slower and requires namespaced context such as "podman unshare")
|
||||
pub no_mount: Option<bool>,
|
||||
}
|
||||
|
||||
impl GeneralConfig {
|
||||
/// Merge two config, "other" is more dominant
|
||||
pub(super) fn merge(&mut self, other: GeneralConfig) {
|
||||
if let Some(cookbook) = other.cookbook {
|
||||
self.cookbook = Some(cookbook);
|
||||
}
|
||||
self.filesystem_size = other.filesystem_size.or(self.filesystem_size);
|
||||
self.efi_partition_size = other.efi_partition_size.or(self.efi_partition_size);
|
||||
self.skip_partitions = other.skip_partitions.or(self.skip_partitions);
|
||||
if let Some(encrypt_disk) = other.encrypt_disk {
|
||||
self.encrypt_disk = Some(encrypt_disk);
|
||||
}
|
||||
self.live_disk = other.live_disk.or(self.live_disk);
|
||||
if let Some(write_bootloader) = other.write_bootloader {
|
||||
self.write_bootloader = Some(write_bootloader);
|
||||
}
|
||||
self.no_mount = other.no_mount.or(self.no_mount);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::Display;
|
||||
use std::fs;
|
||||
use std::mem;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::bail;
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
|
||||
use crate::PackageConfig;
|
||||
|
||||
pub mod file;
|
||||
#[cfg(feature = "installer")]
|
||||
pub mod file_impl;
|
||||
pub mod general;
|
||||
pub mod package;
|
||||
pub mod user;
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
pub struct Config {
|
||||
#[serde(default)]
|
||||
pub include: Vec<PathBuf>,
|
||||
#[serde(default)]
|
||||
pub general: general::GeneralConfig,
|
||||
#[serde(default)]
|
||||
pub packages: BTreeMap<String, package::PackageConfig>,
|
||||
#[serde(default)]
|
||||
pub files: Vec<file::FileConfig>,
|
||||
#[serde(default)]
|
||||
pub users: BTreeMap<String, user::UserConfig>,
|
||||
#[serde(default)]
|
||||
pub groups: BTreeMap<String, user::GroupConfig>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
/// Load installer config from a TOML path
|
||||
pub fn from_file(path: &Path) -> Result<Self> {
|
||||
let mut config: Config = match fs::read_to_string(&path) {
|
||||
Ok(config_data) => match toml::from_str(&config_data) {
|
||||
Ok(config) => config,
|
||||
Err(err) => {
|
||||
bail!("failed to decode '{}': {}", path.display(), err);
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
bail!("failed to read '{}': {}", path.display(), err);
|
||||
}
|
||||
};
|
||||
|
||||
let config_dir = path.parent().unwrap();
|
||||
|
||||
let mut configs = mem::take(&mut config.include)
|
||||
.into_iter()
|
||||
.map(|path| {
|
||||
Config::from_file(&config_dir.join(&path))
|
||||
.with_context(|| format!("Importing from {}", path.display()))
|
||||
})
|
||||
.collect::<Result<Vec<Config>>>()?;
|
||||
configs.push(config); // Put ourself last to ensure that it overwrites anything else.
|
||||
|
||||
config = configs.remove(0);
|
||||
|
||||
for other_config in configs {
|
||||
config.merge(other_config);
|
||||
}
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
/// Load hardcoded install config to fetch bootloaders
|
||||
pub fn bootloader_config() -> Self {
|
||||
let mut bootloader_config = Config::default();
|
||||
// TODO: This is unused
|
||||
bootloader_config.files.push(file::FileConfig {
|
||||
path: "/etc/pkg.d/50_redox".to_string(),
|
||||
data: "https://static.redox-os.org/pkg".to_string(),
|
||||
..Default::default()
|
||||
});
|
||||
bootloader_config
|
||||
.packages
|
||||
.insert("bootloader".to_string(), PackageConfig::default());
|
||||
bootloader_config
|
||||
}
|
||||
|
||||
pub fn merge(&mut self, other: Config) {
|
||||
assert!(self.include.is_empty());
|
||||
assert!(other.include.is_empty());
|
||||
|
||||
let Config {
|
||||
include: _,
|
||||
general: other_general,
|
||||
packages: other_packages,
|
||||
files: other_files,
|
||||
users: other_users,
|
||||
groups: other_groups,
|
||||
} = other;
|
||||
|
||||
self.general.merge(other_general);
|
||||
|
||||
for (package, package_config) in other_packages {
|
||||
self.packages.insert(package, package_config);
|
||||
}
|
||||
|
||||
self.files.extend(other_files);
|
||||
|
||||
for (user, user_config) in other_users {
|
||||
self.users.insert(user, user_config);
|
||||
}
|
||||
|
||||
for (group, group_config) in other_groups {
|
||||
self.groups.insert(group, group_config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Config {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "files:")?;
|
||||
for file in &self.files {
|
||||
writeln!(f, "- {}", file)?;
|
||||
}
|
||||
writeln!(f, "users:")?;
|
||||
for (name, user) in &self.users {
|
||||
writeln!(f, "- {}:{}", name, user)?;
|
||||
}
|
||||
write!(f, "packages: ")?;
|
||||
for name in self.packages.keys() {
|
||||
write!(f, " {}", name)?;
|
||||
}
|
||||
writeln!(f, "")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum PackageConfig {
|
||||
Empty,
|
||||
Build(String),
|
||||
|
||||
// TODO: Sum type
|
||||
Spec {
|
||||
version: Option<String>,
|
||||
git: Option<String>,
|
||||
path: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Default for PackageConfig {
|
||||
fn default() -> Self {
|
||||
Self::Empty
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
pub struct UserConfig {
|
||||
pub password: Option<String>,
|
||||
pub uid: Option<u32>,
|
||||
pub gid: Option<u32>,
|
||||
pub name: Option<String>,
|
||||
pub home: Option<String>,
|
||||
pub shell: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
pub struct GroupConfig {
|
||||
pub gid: Option<u32>,
|
||||
// FIXME move this to the UserConfig struct as extra_groups
|
||||
pub members: Vec<String>,
|
||||
}
|
||||
|
||||
impl Display for UserConfig {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if let Some(uid) = &self.uid {
|
||||
write!(f, " uid={}", uid)?;
|
||||
}
|
||||
if let Some(gid) = &self.gid {
|
||||
write!(f, " gid={}", gid)?;
|
||||
}
|
||||
if let Some(name) = &self.name {
|
||||
write!(f, " name={}", name)?;
|
||||
}
|
||||
if let Some(home) = &self.home {
|
||||
write!(f, " home={}", home)?;
|
||||
}
|
||||
if let Some(shell) = &self.shell {
|
||||
write!(f, " shell={}", shell)?;
|
||||
}
|
||||
if self.password.as_ref().is_some_and(|s| !s.is_empty()) {
|
||||
write!(f, " password=yes")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
-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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
use std::{
|
||||
cmp,
|
||||
convert::TryInto,
|
||||
fs::{File, OpenOptions},
|
||||
io::{Read, Result, Seek, SeekFrom, Write},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DiskWrapper {
|
||||
disk: File,
|
||||
size: u64,
|
||||
block: Box<[u8]>,
|
||||
seek: u64,
|
||||
}
|
||||
|
||||
enum Buffer<'a> {
|
||||
Read(&'a mut [u8]),
|
||||
Write(&'a [u8]),
|
||||
}
|
||||
|
||||
impl DiskWrapper {
|
||||
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
|
||||
let disk = OpenOptions::new().read(true).write(true).open(path)?;
|
||||
let metadata = disk.metadata()?;
|
||||
let size = metadata.len();
|
||||
// TODO: get real block size: disk_metadata.blksize() works on disks but not image files
|
||||
let block_size = 512;
|
||||
let block = vec![0u8; block_size].into_boxed_slice();
|
||||
Ok(Self {
|
||||
disk,
|
||||
size,
|
||||
block,
|
||||
seek: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn block_size(&self) -> usize {
|
||||
self.block.len()
|
||||
}
|
||||
|
||||
pub fn size(&self) -> u64 {
|
||||
self.size
|
||||
}
|
||||
|
||||
fn io<'a>(&mut self, buf: &mut Buffer<'a>) -> Result<usize> {
|
||||
let buf_len = match buf {
|
||||
Buffer::Read(read) => read.len(),
|
||||
Buffer::Write(write) => write.len(),
|
||||
};
|
||||
let block_len: u64 = self.block.len().try_into().unwrap();
|
||||
|
||||
// Do aligned I/O quickly
|
||||
if self.seek % block_len == 0 && buf_len as u64 % block_len == 0 {
|
||||
self.disk.seek(SeekFrom::Start(self.seek))?;
|
||||
match buf {
|
||||
Buffer::Read(read) => self.disk.read_exact(read)?,
|
||||
Buffer::Write(write) => self.disk.write_all(write)?,
|
||||
}
|
||||
self.seek = self.seek.checked_add(buf_len.try_into().unwrap()).unwrap();
|
||||
return Ok(buf_len);
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
while i < buf_len {
|
||||
let block = self.seek / block_len;
|
||||
let offset: usize = (self.seek % block_len).try_into().unwrap();
|
||||
let remaining = buf_len.checked_sub(i).unwrap();
|
||||
let len = cmp::min(remaining, self.block.len().checked_sub(offset).unwrap());
|
||||
|
||||
self.disk
|
||||
.seek(SeekFrom::Start(block.checked_mul(block_len).unwrap()))?;
|
||||
self.disk.read_exact(&mut self.block)?;
|
||||
|
||||
match buf {
|
||||
Buffer::Read(read) => {
|
||||
read[i..i.checked_add(len).unwrap()]
|
||||
.copy_from_slice(&self.block[offset..offset.checked_add(len).unwrap()]);
|
||||
}
|
||||
Buffer::Write(write) => {
|
||||
self.block[offset..offset.checked_add(len).unwrap()]
|
||||
.copy_from_slice(&write[i..i.checked_add(len).unwrap()]);
|
||||
|
||||
self.disk
|
||||
.seek(SeekFrom::Start(block.checked_mul(block_len).unwrap()))?;
|
||||
self.disk.write_all(&mut self.block)?;
|
||||
}
|
||||
}
|
||||
|
||||
i = i.checked_add(len).unwrap();
|
||||
self.seek = self.seek.checked_add(len.try_into().unwrap()).unwrap();
|
||||
}
|
||||
|
||||
Ok(i)
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for DiskWrapper {
|
||||
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||
self.io(&mut Buffer::Read(buf))
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for DiskWrapper {
|
||||
fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
|
||||
let current: i64 = self.seek.try_into().unwrap();
|
||||
let end: i64 = self.size.try_into().unwrap();
|
||||
self.seek = match pos {
|
||||
SeekFrom::Start(offset) => cmp::min(self.size, offset),
|
||||
SeekFrom::End(offset) => cmp::max(0, cmp::min(end, end.wrapping_add(offset))) as u64,
|
||||
SeekFrom::Current(offset) => {
|
||||
cmp::max(0, cmp::min(end, current.wrapping_add(offset))) as u64
|
||||
}
|
||||
};
|
||||
Ok(self.seek)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for DiskWrapper {
|
||||
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||
self.io(&mut Buffer::Write(buf))
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<()> {
|
||||
self.disk.flush()
|
||||
}
|
||||
}
|
||||
-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 const 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)",
|
||||
];
|
||||
-573
@@ -1,573 +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;
|
||||
|
||||
/// Duplicate file descriptor with close-on-exec set (Red Bear extension; libredox ABI).
|
||||
pub const F_DUPFD_CLOEXEC: usize = 5;
|
||||
|
||||
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_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;
|
||||
|
||||
/// Atomically set close-on-exec when opening a file (Red Bear extension; libredox ABI).
|
||||
pub const O_CLOEXEC: usize = 0x0100_0000;
|
||||
|
||||
/// 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,
|
||||
Reserved1 = 3,
|
||||
Resize = 4,
|
||||
}
|
||||
impl FileTableVerb {
|
||||
pub fn try_from_raw(value: u8) -> Option<Self> {
|
||||
Some(match value {
|
||||
1 => Self::Close,
|
||||
2 => Self::Dup2,
|
||||
3 => Self::Reserved1,
|
||||
4 => Self::Resize,
|
||||
_ => 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;
|
||||
@@ -0,0 +1,855 @@
|
||||
use anyhow::Context;
|
||||
use anyhow::{bail, Result};
|
||||
use pkg::Library;
|
||||
use rand::{rngs::OsRng, TryRngCore};
|
||||
use redoxfs::{unmount_path, Disk, DiskIo, FileSystem, BLOCK_SIZE};
|
||||
use termion::input::TermRead;
|
||||
|
||||
use crate::config::file::FileConfig;
|
||||
use crate::config::package::PackageConfig;
|
||||
use crate::config::Config;
|
||||
use crate::disk_wrapper::DiskWrapper;
|
||||
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::BTreeMap,
|
||||
env, fs,
|
||||
io::{self, Seek, SeekFrom, Write},
|
||||
path::{Path, PathBuf},
|
||||
process,
|
||||
rc::Rc,
|
||||
sync::mpsc::channel,
|
||||
thread,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
pub struct DiskOption<'a> {
|
||||
pub bootloader_bios: &'a [u8],
|
||||
pub bootloader_efi: &'a [u8],
|
||||
pub password_opt: Option<&'a [u8]>,
|
||||
pub efi_partition_size: Option<u32>, //MiB
|
||||
pub skip_partitions: bool,
|
||||
}
|
||||
|
||||
fn get_target() -> String {
|
||||
// TODO: Configurable from filesystem config?
|
||||
env::var("TARGET").unwrap_or(
|
||||
option_env!("TARGET").map_or("x86_64-unknown-redox".to_string(), |x| x.to_string()),
|
||||
)
|
||||
}
|
||||
|
||||
/// Converts a password to a serialized argon2rs hash, understandable
|
||||
/// by redox_users. If the password is blank, the hash is blank.
|
||||
fn hash_password(password: &str) -> Result<String> {
|
||||
if !password.is_empty() {
|
||||
let salt = format!("{:X}", OsRng.try_next_u64()?);
|
||||
let config = argon2::Config::default();
|
||||
let hash = argon2::hash_encoded(password.as_bytes(), salt.as_bytes(), &config)?;
|
||||
Ok(hash)
|
||||
} else {
|
||||
Ok("".into())
|
||||
}
|
||||
}
|
||||
|
||||
fn syscall_error(err: syscall::Error) -> io::Error {
|
||||
io::Error::from_raw_os_error(err.errno)
|
||||
}
|
||||
|
||||
/// Returns a password collected from the user (plaintext)
|
||||
pub fn prompt_password(prompt: &str, confirm_prompt: &str) -> Result<Option<String>> {
|
||||
let stdin = io::stdin();
|
||||
let mut stdin = stdin.lock();
|
||||
let stdout = io::stdout();
|
||||
let mut stdout = stdout.lock();
|
||||
|
||||
for i in 0..3 {
|
||||
print!("{}", prompt);
|
||||
let mut password = stdin.read_passwd(&mut stdout)?;
|
||||
if let Some(password) = password.as_mut() {
|
||||
*password = password.trim().to_string();
|
||||
}
|
||||
password.take_if(|s| s.is_empty());
|
||||
|
||||
if password.is_none() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
print!("\n{}", confirm_prompt);
|
||||
let confirm_password = stdin.read_passwd(&mut stdout)?;
|
||||
|
||||
// Note: Actually comparing two Option<String> values
|
||||
if confirm_password == password {
|
||||
return Ok(password);
|
||||
} else if i < 2 {
|
||||
eprintln!("passwords do not match, please try again");
|
||||
}
|
||||
}
|
||||
bail!("passwords do not match, giving up");
|
||||
}
|
||||
|
||||
fn install_packages(config: &Config, dest: &Path, cookbook: Option<&str>) -> anyhow::Result<()> {
|
||||
let target = &get_target();
|
||||
|
||||
let packages: Vec<&String> = config
|
||||
.packages
|
||||
.iter()
|
||||
.filter_map(|(packagename, package)| match package {
|
||||
PackageConfig::Build(rule) if rule == "ignore" => None,
|
||||
_ => Some(packagename),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut library = if let Some(cookbook) = cookbook {
|
||||
let callback = pkg::callback::PlainCallback::new();
|
||||
let repo = Path::new(cookbook).join("repo");
|
||||
let pubkey = Path::new(cookbook).join("build");
|
||||
Library::new_local(
|
||||
repo,
|
||||
pubkey,
|
||||
dest.to_path_buf(),
|
||||
target,
|
||||
Rc::new(RefCell::new(callback)),
|
||||
)
|
||||
} else {
|
||||
let callback = pkg::callback::IndicatifCallback::new();
|
||||
Library::new_remote(
|
||||
&vec!["https://static.redox-os.org/pkg"],
|
||||
dest,
|
||||
target,
|
||||
Rc::new(RefCell::new(callback)),
|
||||
)
|
||||
}?;
|
||||
|
||||
let packages = pkg::PackageName::from_list(packages)?;
|
||||
library.install(packages)?;
|
||||
library.apply()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn install_dir(
|
||||
config: Config,
|
||||
output_dir: impl AsRef<Path>,
|
||||
cookbook: Option<&str>,
|
||||
) -> Result<()> {
|
||||
let output_dir = output_dir.as_ref();
|
||||
|
||||
let output_dir = output_dir.to_owned();
|
||||
|
||||
for file in &config.files {
|
||||
if !file.postinstall {
|
||||
file.create(&output_dir)?;
|
||||
}
|
||||
}
|
||||
|
||||
install_packages(&config, &output_dir, cookbook)?;
|
||||
|
||||
for file in &config.files {
|
||||
if file.postinstall {
|
||||
file.create(&output_dir)?;
|
||||
}
|
||||
}
|
||||
|
||||
let mut passwd = String::new();
|
||||
let mut shadow = String::new();
|
||||
let mut next_uid = 1000;
|
||||
let mut next_gid = 1000;
|
||||
|
||||
let mut groups = vec![];
|
||||
|
||||
for (username, user) in config.users {
|
||||
// plaintext
|
||||
let password = if let Some(password) = user.password {
|
||||
password
|
||||
} else if config.general.prompt.unwrap_or(true) {
|
||||
prompt_password(
|
||||
&format!("{}: enter password: ", username),
|
||||
&format!("{}: confirm password: ", username),
|
||||
)?
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
|
||||
let uid = user.uid.unwrap_or(next_uid);
|
||||
|
||||
if uid >= next_uid {
|
||||
next_uid = uid + 1;
|
||||
}
|
||||
|
||||
let gid = user.gid.unwrap_or(next_gid);
|
||||
|
||||
if gid >= next_gid {
|
||||
next_gid = gid + 1;
|
||||
}
|
||||
|
||||
let name = user.name.unwrap_or(username.clone());
|
||||
let home = user.home.unwrap_or(format!("/home/{}", username));
|
||||
let shell = user.shell.unwrap_or("/bin/ion".into());
|
||||
|
||||
println!("Adding user {username}:");
|
||||
if password.is_empty() {
|
||||
println!("\tPassword: unset");
|
||||
} else {
|
||||
println!("\tPassword: set");
|
||||
}
|
||||
println!("\tUID: {uid}");
|
||||
println!("\tGID: {gid}");
|
||||
println!("\tName: {name}");
|
||||
println!("\tHome: {home}");
|
||||
println!("\tShell: {shell}");
|
||||
|
||||
FileConfig::new_directory(home.clone())
|
||||
.with_recursive_mod(0o700, uid, gid)
|
||||
.create(&output_dir)?;
|
||||
|
||||
if uid >= 1000 {
|
||||
prepare_user_home(&output_dir, uid, gid, &home)?;
|
||||
}
|
||||
|
||||
let password = hash_password(&password)?;
|
||||
|
||||
passwd.push_str(&format!("{username};{uid};{gid};{name};{home};{shell}\n",));
|
||||
shadow.push_str(&format!("{username};{password}\n"));
|
||||
groups.push((username.clone(), gid, vec![username]));
|
||||
}
|
||||
|
||||
for (group, group_config) in config.groups {
|
||||
// FIXME this assumes there is no overlap between auto-created groups for users
|
||||
// and explicitly specified groups.
|
||||
let gid = group_config.gid.unwrap_or(next_gid);
|
||||
|
||||
if gid >= next_gid {
|
||||
next_gid = gid + 1;
|
||||
}
|
||||
|
||||
groups.push((group, gid, group_config.members));
|
||||
}
|
||||
|
||||
if !passwd.is_empty() {
|
||||
FileConfig::new_file("/etc/passwd".to_string(), passwd).create(&output_dir)?;
|
||||
}
|
||||
|
||||
if !shadow.is_empty() {
|
||||
FileConfig::new_file("/etc/shadow".to_string(), shadow)
|
||||
.with_mod(0o0600, 0, 0)
|
||||
.create(&output_dir)?;
|
||||
}
|
||||
|
||||
if !groups.is_empty() {
|
||||
let mut groups_data = String::new();
|
||||
|
||||
for (name, gid, members) in groups {
|
||||
use std::fmt::Write;
|
||||
writeln!(groups_data, "{name};x;{gid};{}", members.join(","))?;
|
||||
|
||||
println!("Adding group {name}:");
|
||||
println!("\tGID: {gid}");
|
||||
println!("\tMembers: {}", members.join(", "));
|
||||
}
|
||||
|
||||
FileConfig::new_file("/etc/group".to_string(), groups_data)
|
||||
.with_mod(0o0600, 0, 0)
|
||||
.create(&output_dir)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare_user_home(
|
||||
output_dir: &PathBuf,
|
||||
uid: u32,
|
||||
gid: u32,
|
||||
home: &String,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
for xdg_folder in &[
|
||||
"Desktop",
|
||||
"Documents",
|
||||
"Downloads",
|
||||
"Music",
|
||||
"Pictures",
|
||||
"Public",
|
||||
"Templates",
|
||||
"Videos",
|
||||
".config",
|
||||
".local",
|
||||
".local/share",
|
||||
".local/share/Trash",
|
||||
".local/share/Trash/info",
|
||||
] {
|
||||
FileConfig::new_directory(format!("{}/{}", home, xdg_folder))
|
||||
.with_mod(0o0700, uid, gid)
|
||||
.create(output_dir)?;
|
||||
}
|
||||
FileConfig::new_file(
|
||||
format!("{}/.config/user-dirs.dirs", home),
|
||||
r#"# Produced by redox installer
|
||||
XDG_DESKTOP_DIR="$HOME/Desktop"
|
||||
XDG_DOCUMENTS_DIR="$HOME/Documents"
|
||||
XDG_DOWNLOAD_DIR="$HOME/Downloads"
|
||||
XDG_MUSIC_DIR="$HOME/Music"
|
||||
XDG_PICTURES_DIR="$HOME/Pictures"
|
||||
XDG_PUBLICSHARE_DIR="$HOME/Public"
|
||||
XDG_TEMPLATES_DIR="$HOME/Templates"
|
||||
XDG_VIDEOS_DIR="$HOME/Videos"
|
||||
"#
|
||||
.to_string(),
|
||||
)
|
||||
.with_mod(0o0600, uid, gid)
|
||||
.create(output_dir)?;
|
||||
|
||||
let skel_dir = output_dir.join("etc/skel");
|
||||
if skel_dir.is_dir() {
|
||||
copy_dir_all(&skel_dir, home.clone(), output_dir, uid, gid)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy_dir_all(
|
||||
src: impl AsRef<Path>,
|
||||
dst: String,
|
||||
output_dir: &Path,
|
||||
uid: u32,
|
||||
gid: u32,
|
||||
) -> anyhow::Result<()> {
|
||||
if !Path::new(dst.as_str()).is_dir() {
|
||||
FileConfig::new_directory(dst.clone())
|
||||
.with_mod(0o0700, uid, gid)
|
||||
.create(&output_dir)?;
|
||||
}
|
||||
for entry in fs::read_dir(src)? {
|
||||
let entry = entry?;
|
||||
let file_type = entry.file_type()?;
|
||||
let dst_path = format!("{}/{}", dst, entry.file_name().display());
|
||||
if file_type.is_dir() {
|
||||
copy_dir_all(entry.path(), dst_path, output_dir, uid, gid)?;
|
||||
} else if file_type.is_file() {
|
||||
FileConfig::new_file(
|
||||
dst_path,
|
||||
fs::read_to_string(entry.path())
|
||||
.with_context(|| format!("Reading {}", entry.path().display()))?,
|
||||
)
|
||||
.with_mod(0o0600, uid, gid)
|
||||
.create(&output_dir)?;
|
||||
} else if file_type.is_symlink() {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn with_redoxfs<D, T, F>(disk: D, password_opt: Option<&[u8]>, callback: F) -> Result<T>
|
||||
where
|
||||
D: Disk + Send + 'static,
|
||||
F: FnOnce(FileSystem<D>) -> Result<T>,
|
||||
{
|
||||
let ctime = SystemTime::now().duration_since(UNIX_EPOCH)?;
|
||||
let fs = FileSystem::create(disk, password_opt, ctime.as_secs(), ctime.subsec_nanos())
|
||||
.map_err(syscall_error)?;
|
||||
callback(fs)
|
||||
}
|
||||
|
||||
fn decide_mount_path(mount_path: Option<&Path>) -> PathBuf {
|
||||
let mount_path = mount_path.map(|p| p.to_path_buf()).unwrap_or_else(|| {
|
||||
PathBuf::from(if cfg!(target_os = "redox") {
|
||||
format!("file.redox_installer_{}", process::id())
|
||||
} else {
|
||||
format!("/tmp/redox_installer_{}", process::id())
|
||||
})
|
||||
});
|
||||
mount_path
|
||||
}
|
||||
|
||||
pub fn with_redoxfs_mount<D, T, F>(
|
||||
fs: FileSystem<D>,
|
||||
mount_path: Option<&Path>,
|
||||
callback: F,
|
||||
) -> Result<T>
|
||||
where
|
||||
D: Disk + Send + 'static,
|
||||
F: FnOnce(&Path) -> Result<T>,
|
||||
{
|
||||
let mount_path = decide_mount_path(mount_path);
|
||||
|
||||
if cfg!(not(target_os = "redox")) && !mount_path.exists() {
|
||||
fs::create_dir(&mount_path)?;
|
||||
}
|
||||
|
||||
let (tx, rx) = channel();
|
||||
let join_handle = {
|
||||
let mount_path = mount_path.clone();
|
||||
thread::spawn(move || {
|
||||
let res = redoxfs::mount(fs, &mount_path, |real_path| {
|
||||
tx.send(Ok(real_path.to_owned())).unwrap();
|
||||
});
|
||||
match res {
|
||||
Ok(()) => (),
|
||||
Err(err) => {
|
||||
tx.send(Err(err)).unwrap();
|
||||
}
|
||||
};
|
||||
})
|
||||
};
|
||||
|
||||
let res = match rx.recv() {
|
||||
Ok(ok) => match ok {
|
||||
Ok(real_path) => callback(&real_path),
|
||||
Err(err) => return Err(err.into()),
|
||||
},
|
||||
Err(_) => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::NotConnected,
|
||||
"redoxfs thread did not send a result",
|
||||
)
|
||||
.into())
|
||||
}
|
||||
};
|
||||
|
||||
unmount_path(&mount_path.as_os_str().to_str().unwrap())?;
|
||||
|
||||
join_handle.join().unwrap();
|
||||
|
||||
if cfg!(not(target_os = "redox")) {
|
||||
fs::remove_dir_all(&mount_path)?;
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn with_redoxfs_ar<D, T, F>(
|
||||
mut fs: FileSystem<D>,
|
||||
mount_path: Option<&Path>,
|
||||
callback: F,
|
||||
) -> Result<T>
|
||||
where
|
||||
D: Disk + Send + 'static,
|
||||
F: FnOnce(&Path) -> Result<T>,
|
||||
{
|
||||
let mount_path = decide_mount_path(mount_path);
|
||||
|
||||
let res = callback(Path::new(&mount_path));
|
||||
|
||||
if res.is_ok() {
|
||||
let _end_block = fs
|
||||
.tx(|tx| {
|
||||
// Archive_at root node
|
||||
redoxfs::archive_at(tx, Path::new(&mount_path), redoxfs::TreePtr::root())
|
||||
.map_err(|err| syscall::Error::new(err.raw_os_error().unwrap()))?;
|
||||
|
||||
// Squash alloc log
|
||||
tx.sync(true)?;
|
||||
|
||||
let end_block = tx.header.size() / BLOCK_SIZE;
|
||||
/* TODO: Cut off any free blocks at the end of the filesystem
|
||||
let mut end_changed = true;
|
||||
while end_changed {
|
||||
end_changed = false;
|
||||
|
||||
let allocator = fs.allocator();
|
||||
let levels = allocator.levels();
|
||||
for level in 0..levels.len() {
|
||||
let level_size = 1 << level;
|
||||
for &block in levels[level].iter() {
|
||||
if block < end_block && block + level_size >= end_block {
|
||||
end_block = block;
|
||||
end_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Update header
|
||||
tx.header.size = (end_block * BLOCK_SIZE).into();
|
||||
tx.header_changed = true;
|
||||
tx.sync(false)?;
|
||||
|
||||
Ok(end_block)
|
||||
})
|
||||
.map_err(syscall_error)?;
|
||||
|
||||
// let size = (fs.block + end_block) * BLOCK_SIZE;
|
||||
// fs.disk.file.set_len(size)?;
|
||||
}
|
||||
|
||||
fs::remove_dir_all(&mount_path)?;
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub fn fetch_bootloaders(
|
||||
config: &Config,
|
||||
cookbook: Option<&str>,
|
||||
live: bool,
|
||||
) -> Result<(Vec<u8>, Vec<u8>)> {
|
||||
let bootloader_dir =
|
||||
PathBuf::from(format!("/tmp/redox_installer_bootloader_{}", process::id()));
|
||||
|
||||
if bootloader_dir.exists() {
|
||||
fs::remove_dir_all(&bootloader_dir)?;
|
||||
}
|
||||
|
||||
fs::create_dir(&bootloader_dir)?;
|
||||
|
||||
let mut bootloader_config = Config::bootloader_config();
|
||||
bootloader_config.general = config.general.clone();
|
||||
install_packages(&bootloader_config, &bootloader_dir, cookbook)?;
|
||||
|
||||
let boot_dir = bootloader_dir.join("usr/lib/boot");
|
||||
let bios_path = boot_dir.join(if live {
|
||||
"bootloader-live.bios"
|
||||
} else {
|
||||
"bootloader.bios"
|
||||
});
|
||||
let efi_path = boot_dir.join(if live {
|
||||
"bootloader-live.efi"
|
||||
} else {
|
||||
"bootloader.efi"
|
||||
});
|
||||
|
||||
let bios_data = if bios_path.exists() {
|
||||
fs::read(bios_path)?
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let efi_data = if efi_path.exists() {
|
||||
fs::read(efi_path)?
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
fs::remove_dir_all(&bootloader_dir)?;
|
||||
|
||||
Ok((bios_data, efi_data))
|
||||
}
|
||||
|
||||
//TODO: make bootloaders use Option, dynamically create BIOS and EFI partitions
|
||||
pub fn with_whole_disk<P, F, T>(disk_path: P, disk_option: &DiskOption, callback: F) -> Result<T>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
F: FnOnce(FileSystem<DiskIo<fscommon::StreamSlice<DiskWrapper>>>) -> Result<T>,
|
||||
{
|
||||
let target = get_target();
|
||||
|
||||
let bootloader_efi_name = match target.as_str() {
|
||||
"aarch64-unknown-redox" => "BOOTAA64.EFI",
|
||||
"i586-unknown-redox" | "i686-unknown-redox" => "BOOTIA32.EFI",
|
||||
"x86_64-unknown-redox" => "BOOTX64.EFI",
|
||||
"riscv64gc-unknown-redox" => "BOOTRISCV64.EFI",
|
||||
_ => {
|
||||
bail!("target '{target}' not supported");
|
||||
}
|
||||
};
|
||||
// Open disk and read metadata
|
||||
eprintln!("Opening disk {}", disk_path.as_ref().display());
|
||||
let mut disk_file = DiskWrapper::open(disk_path.as_ref())?;
|
||||
let disk_size = disk_file.size();
|
||||
let block_size = disk_file.block_size() as u64;
|
||||
|
||||
if disk_option.skip_partitions {
|
||||
return with_redoxfs(
|
||||
DiskIo(fscommon::StreamSlice::new(
|
||||
disk_file,
|
||||
0,
|
||||
disk_size.next_multiple_of(block_size),
|
||||
)?),
|
||||
disk_option.password_opt,
|
||||
callback,
|
||||
);
|
||||
}
|
||||
|
||||
let gpt_block_size = match block_size {
|
||||
512 => gpt::disk::LogicalBlockSize::Lb512,
|
||||
_ => {
|
||||
// TODO: support (and test) other block sizes
|
||||
bail!("block size {block_size} not supported");
|
||||
}
|
||||
};
|
||||
|
||||
// Calculate partition offsets
|
||||
let gpt_reserved = 34 * 512; // GPT always reserves 34 512-byte sectors
|
||||
let mibi = 1024 * 1024;
|
||||
|
||||
// First megabyte of the disk is reserved for BIOS partition, wich includes GPT tables
|
||||
let bios_start = gpt_reserved / block_size;
|
||||
let bios_end = (mibi / block_size) - 1;
|
||||
|
||||
// Second megabyte of the disk is reserved for EFI partition
|
||||
let efi_start = bios_end + 1;
|
||||
let efi_size = if let Some(size) = disk_option.efi_partition_size {
|
||||
size as u64
|
||||
} else {
|
||||
1
|
||||
};
|
||||
let efi_end = efi_start + (efi_size * mibi / block_size) - 1;
|
||||
|
||||
// The rest of the disk is RedoxFS, reserving the GPT table mirror at the end of disk
|
||||
let redoxfs_start = efi_end + 1;
|
||||
let redoxfs_end = ((((disk_size - gpt_reserved) / mibi) * mibi) / block_size) - 1;
|
||||
|
||||
// Format and install BIOS partition
|
||||
{
|
||||
// Write BIOS bootloader to disk
|
||||
eprintln!(
|
||||
"Write bootloader with size {:#x}",
|
||||
disk_option.bootloader_bios.len()
|
||||
);
|
||||
disk_file.seek(SeekFrom::Start(0))?;
|
||||
disk_file.write_all(&disk_option.bootloader_bios)?;
|
||||
|
||||
// Replace MBR tables with protective MBR
|
||||
// TODO: div_ceil
|
||||
let mbr_blocks = ((disk_size + block_size - 1) / block_size) - 1;
|
||||
eprintln!("Writing protective MBR with disk blocks {mbr_blocks:#x}");
|
||||
gpt::mbr::ProtectiveMBR::with_lb_size(mbr_blocks as u32)
|
||||
.update_conservative(&mut disk_file)?;
|
||||
|
||||
// Open disk, mark it as not initialized
|
||||
let mut gpt_disk = gpt::GptConfig::new()
|
||||
.initialized(false)
|
||||
.writable(true)
|
||||
.logical_block_size(gpt_block_size)
|
||||
.create_from_device(Box::new(&mut disk_file), None)?;
|
||||
|
||||
// Add BIOS boot partition
|
||||
let mut partitions = BTreeMap::new();
|
||||
let mut partition_id = 1;
|
||||
partitions.insert(
|
||||
partition_id,
|
||||
gpt::partition::Partition {
|
||||
part_type_guid: gpt::partition_types::BIOS,
|
||||
part_guid: uuid::Uuid::new_v4(),
|
||||
first_lba: bios_start,
|
||||
last_lba: bios_end,
|
||||
flags: 0, // TODO
|
||||
name: "BIOS".to_string(),
|
||||
},
|
||||
);
|
||||
partition_id += 1;
|
||||
|
||||
// Add EFI boot partition
|
||||
partitions.insert(
|
||||
partition_id,
|
||||
gpt::partition::Partition {
|
||||
part_type_guid: gpt::partition_types::EFI,
|
||||
part_guid: uuid::Uuid::new_v4(),
|
||||
first_lba: efi_start,
|
||||
last_lba: efi_end,
|
||||
flags: 0, // TODO
|
||||
name: "EFI".to_string(),
|
||||
},
|
||||
);
|
||||
partition_id += 1;
|
||||
|
||||
// Add RedoxFS partition
|
||||
partitions.insert(
|
||||
partition_id,
|
||||
gpt::partition::Partition {
|
||||
//TODO: Use REDOX_REDOXFS type (needs GPT crate changes)
|
||||
part_type_guid: gpt::partition_types::LINUX_FS,
|
||||
part_guid: uuid::Uuid::new_v4(),
|
||||
first_lba: redoxfs_start,
|
||||
last_lba: redoxfs_end,
|
||||
flags: 0,
|
||||
name: "REDOX".to_string(),
|
||||
},
|
||||
);
|
||||
|
||||
eprintln!("Writing GPT tables: {partitions:#?}");
|
||||
|
||||
// Initialize GPT table
|
||||
gpt_disk.update_partitions(partitions)?;
|
||||
|
||||
// Write partition layout, returning disk file
|
||||
gpt_disk.write()?;
|
||||
}
|
||||
|
||||
// Format and install EFI partition
|
||||
{
|
||||
let disk_efi_start = efi_start * block_size;
|
||||
let disk_efi_end = (efi_end + 1) * block_size;
|
||||
let mut disk_efi =
|
||||
fscommon::StreamSlice::new(&mut disk_file, disk_efi_start, disk_efi_end)?;
|
||||
|
||||
eprintln!(
|
||||
"Formatting EFI partition with size {:#x}",
|
||||
disk_efi_end - disk_efi_start
|
||||
);
|
||||
fatfs::format_volume(&mut disk_efi, fatfs::FormatVolumeOptions::new())?;
|
||||
|
||||
eprintln!("Opening EFI partition");
|
||||
let fs = fatfs::FileSystem::new(&mut disk_efi, fatfs::FsOptions::new())?;
|
||||
|
||||
eprintln!("Creating EFI directory");
|
||||
let root_dir = fs.root_dir();
|
||||
root_dir.create_dir("EFI")?;
|
||||
|
||||
eprintln!("Creating EFI/BOOT directory");
|
||||
let efi_dir = root_dir.open_dir("EFI")?;
|
||||
efi_dir.create_dir("BOOT")?;
|
||||
|
||||
eprintln!(
|
||||
"Writing EFI/BOOT/{} file with size {:#x}",
|
||||
bootloader_efi_name,
|
||||
disk_option.bootloader_efi.len()
|
||||
);
|
||||
let boot_dir = efi_dir.open_dir("BOOT")?;
|
||||
let mut file = boot_dir.create_file(bootloader_efi_name)?;
|
||||
file.truncate()?;
|
||||
file.write_all(&disk_option.bootloader_efi)?;
|
||||
}
|
||||
|
||||
// Format and install RedoxFS partition
|
||||
eprintln!(
|
||||
"Installing to RedoxFS partition with size {:#x}",
|
||||
(redoxfs_end - redoxfs_start) * block_size
|
||||
);
|
||||
let disk_redoxfs = DiskIo(fscommon::StreamSlice::new(
|
||||
disk_file,
|
||||
redoxfs_start * block_size,
|
||||
(redoxfs_end + 1) * block_size,
|
||||
)?);
|
||||
with_redoxfs(disk_redoxfs, disk_option.password_opt, callback)
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
pub fn try_fast_install<D: redoxfs::Disk, F: FnMut(u64, u64)>(
|
||||
_fs: &mut redoxfs::FileSystem<D>,
|
||||
_progress: F,
|
||||
) -> Result<bool> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// Try fast install using live disk memory
|
||||
#[cfg(target_os = "redox")]
|
||||
pub fn try_fast_install<D: redoxfs::Disk, F: FnMut(u64, u64)>(
|
||||
fs: &mut redoxfs::FileSystem<D>,
|
||||
mut progress: F,
|
||||
) -> Result<bool> {
|
||||
use libredox::{call::MmapArgs, flag};
|
||||
use std::os::fd::AsRawFd;
|
||||
use syscall::PAGE_SIZE;
|
||||
|
||||
let phys = env::var("DISK_LIVE_ADDR")
|
||||
.ok()
|
||||
.and_then(|x| usize::from_str_radix(&x, 16).ok())
|
||||
.unwrap_or(0);
|
||||
let size = env::var("DISK_LIVE_SIZE")
|
||||
.ok()
|
||||
.and_then(|x| usize::from_str_radix(&x, 16).ok())
|
||||
.unwrap_or(0);
|
||||
if phys == 0 || size == 0 {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let start = (phys / PAGE_SIZE) * PAGE_SIZE;
|
||||
let end = phys
|
||||
.checked_add(size)
|
||||
.context("phys + size overflow")?
|
||||
.next_multiple_of(PAGE_SIZE);
|
||||
let size = end - start;
|
||||
|
||||
let original = unsafe {
|
||||
//TODO: unmap this memory
|
||||
let file = fs::File::open("/scheme/memory/physical")?;
|
||||
let base = libredox::call::mmap(MmapArgs {
|
||||
fd: file.as_raw_fd() as usize,
|
||||
addr: core::ptr::null_mut(),
|
||||
offset: start as u64,
|
||||
length: size,
|
||||
prot: flag::PROT_READ,
|
||||
flags: flag::MAP_SHARED,
|
||||
})
|
||||
.map_err(|err| anyhow::anyhow!("failed to mmap livedisk: {}", err))?;
|
||||
|
||||
std::slice::from_raw_parts(base as *const u8, size)
|
||||
};
|
||||
|
||||
struct DiskLive {
|
||||
original: &'static [u8],
|
||||
}
|
||||
|
||||
impl redoxfs::Disk for DiskLive {
|
||||
unsafe fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> syscall::Result<usize> {
|
||||
let offset = (block * redoxfs::BLOCK_SIZE) as usize;
|
||||
if offset + buffer.len() > self.original.len() {
|
||||
return Err(syscall::Error::new(syscall::EINVAL));
|
||||
}
|
||||
buffer.copy_from_slice(&self.original[offset..offset + buffer.len()]);
|
||||
Ok(buffer.len())
|
||||
}
|
||||
|
||||
unsafe fn write_at(&mut self, _block: u64, _buffer: &[u8]) -> syscall::Result<usize> {
|
||||
Err(syscall::Error::new(syscall::EINVAL))
|
||||
}
|
||||
|
||||
fn size(&mut self) -> syscall::Result<u64> {
|
||||
Ok(self.original.len() as u64)
|
||||
}
|
||||
}
|
||||
|
||||
let mut fs_old = redoxfs::FileSystem::open(DiskLive { original }, None, None, false)?;
|
||||
let size_old = fs_old.header.size();
|
||||
let free_old = fs_old.allocator().free() * redoxfs::BLOCK_SIZE;
|
||||
let used_old = size_old - free_old;
|
||||
redoxfs::clone(&mut fs_old, fs, move |used| {
|
||||
progress(used, used_old);
|
||||
})?;
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn install_inner(config: Config, output: &Path) -> Result<()> {
|
||||
println!("Installing to {}:\n{}", output.display(), config);
|
||||
let cookbook = config.general.cookbook.clone();
|
||||
let cookbook = cookbook.as_ref().map(|p| p.as_str());
|
||||
if output.is_dir() {
|
||||
install_dir(config, output, cookbook)
|
||||
} else {
|
||||
if !output.is_file() {
|
||||
let fs_size = config.general.filesystem_size.unwrap_or(0) as u64;
|
||||
// arbitrary size approximately fit just for initfs
|
||||
if fs_size < 32 {
|
||||
bail!("Refusing to create image disk less than 32 MB");
|
||||
}
|
||||
eprintln!(
|
||||
"Creating a new file to {} with size {} MB",
|
||||
output.display(),
|
||||
fs_size
|
||||
);
|
||||
let file = fs::File::create(output)?;
|
||||
file.set_len(fs_size * 1024 * 1024)?;
|
||||
}
|
||||
let live = config.general.live_disk.unwrap_or(false);
|
||||
let password_opt = config.general.encrypt_disk.clone();
|
||||
let password_opt = password_opt.as_ref().map(|p| p.as_bytes());
|
||||
let (bootloader_bios, bootloader_efi) = fetch_bootloaders(&config, cookbook, live)?;
|
||||
if let Some(write_bootloader) = &config.general.write_bootloader {
|
||||
std::fs::write(write_bootloader, &bootloader_efi)?;
|
||||
}
|
||||
let disk_option = DiskOption {
|
||||
bootloader_bios: &bootloader_bios,
|
||||
bootloader_efi: &bootloader_efi,
|
||||
password_opt: password_opt,
|
||||
efi_partition_size: config.general.efi_partition_size,
|
||||
skip_partitions: config.general.skip_partitions.unwrap_or(false),
|
||||
};
|
||||
with_whole_disk(output, &disk_option, move |fs| {
|
||||
if config.general.no_mount.unwrap_or(false) {
|
||||
with_redoxfs_ar(fs, None, move |mount_path| {
|
||||
install_dir(config, mount_path, cookbook)
|
||||
})
|
||||
} else {
|
||||
with_redoxfs_mount(fs, None, move |mount_path| {
|
||||
install_dir(config, mount_path, cookbook)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Install RedoxFS into a new disk file, or a sysroot directory.
|
||||
/// This function assumes all interactive prompts resolved by the caller.
|
||||
pub fn install(config: Config, output: impl AsRef<Path>) -> Result<()> {
|
||||
install_inner(config, output.as_ref())
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
-51
@@ -1,53 +1,14 @@
|
||||
#![cfg_attr(not(any(feature = "std", test)), no_std)]
|
||||
#![allow(unexpected_cfgs)] // why does this even exist?
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate core;
|
||||
mod config;
|
||||
#[cfg(feature = "installer")]
|
||||
mod disk_wrapper;
|
||||
#[cfg(feature = "installer")]
|
||||
mod installer;
|
||||
#[cfg(feature = "installer")]
|
||||
pub use crate::installer::*;
|
||||
|
||||
pub use self::{arch::*, data::*, error::*, flag::*, io::*, number::*};
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[path = "arch/aarch64.rs"]
|
||||
mod arch;
|
||||
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
#[path = "arch/riscv64.rs"]
|
||||
mod arch;
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
#[path = "arch/x86.rs"]
|
||||
mod arch;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[path = "arch/x86_64.rs"]
|
||||
mod arch;
|
||||
|
||||
/// Function definitions
|
||||
#[cfg(feature = "userspace")]
|
||||
pub mod call;
|
||||
|
||||
#[cfg(feature = "userspace")]
|
||||
pub use call::*;
|
||||
|
||||
/// Complex structures that are used for some system calls
|
||||
pub mod data;
|
||||
|
||||
pub mod dirent;
|
||||
|
||||
/// All errors that can be generated by a system call
|
||||
pub mod error;
|
||||
|
||||
/// 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;
|
||||
pub use crate::config::file::FileConfig;
|
||||
pub use crate::config::package::PackageConfig;
|
||||
pub use crate::config::Config;
|
||||
|
||||
@@ -1,61 +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;
|
||||
|
||||
/// Upstream 0.9.0 `openat_into` (caller supplies target fd number).
|
||||
pub const SYS_OPENAT_INTO: usize = SYS_CLASS_PATH | SYS_RET_FILE | 987;
|
||||
/// Legacy Red Bear `openat` (kernel allocates a new fd).
|
||||
pub const SYS_OPENAT: usize = SYS_CLASS_PATH | SYS_RET_FILE | 7;
|
||||
/// Red Bear: preserve legacy SYS_OPENAT_WITH_FILTER (upstream 0.9.0 removed it; retained for kernel/userspace ABI).
|
||||
pub const SYS_OPENAT_WITH_FILTER: usize = SYS_CLASS_PATH | SYS_RET_FILE | 985;
|
||||
pub const SYS_UNLINKAT: usize = SYS_CLASS_PATH | 263;
|
||||
/// Red Bear: preserve legacy SYS_UNLINKAT_WITH_FILTER (upstream 0.9.0 removed it; retained for kernel/userspace ABI).
|
||||
pub const SYS_UNLINKAT_WITH_FILTER: usize = SYS_CLASS_PATH | 986;
|
||||
|
||||
pub const SYS_CLOSE: usize = SYS_CLASS_FILE | 6;
|
||||
pub const SYS_FCHMOD: usize = SYS_CLASS_FILE | 94;
|
||||
/// Upstream 0.9.0 `dup_into` (caller supplies target fd number).
|
||||
pub const SYS_DUP_INTO: usize = SYS_CLASS_FILE | SYS_RET_FILE | 988;
|
||||
/// Legacy Red Bear `dup` (kernel allocates a new fd).
|
||||
pub const SYS_DUP: usize = SYS_CLASS_FILE | SYS_RET_FILE | 41;
|
||||
pub const SYS_DUP2: usize = SYS_CLASS_FILE | SYS_RET_FILE | 63;
|
||||
/// Red Bear: preserve legacy SYS_SENDFD (upstream 0.9.0 removed it; retained for kernel/userspace ABI).
|
||||
pub const SYS_SENDFD: usize = SYS_CLASS_FILE | 34;
|
||||
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_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;
|
||||
|
||||
// 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_FSYNC: usize = SYS_CLASS_FILE | 118;
|
||||
|
||||
pub const SYS_CLOCK_GETTIME: usize = 265;
|
||||
pub const SYS_FUTEX: usize = 240;
|
||||
pub const SYS_MPROTECT: usize = 125;
|
||||
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();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
IMAGE=test.bin
|
||||
|
||||
QEMU_ARGS=(
|
||||
-cpu max
|
||||
-machine q35
|
||||
-m 2048
|
||||
-smp 4
|
||||
-serial mon:stdio
|
||||
-netdev user,id=net0
|
||||
-device e1000,netdev=net0
|
||||
)
|
||||
|
||||
if [ -e /dev/kvm ]
|
||||
then
|
||||
QEMU_ARGS+=(-accel kvm)
|
||||
fi
|
||||
|
||||
set -ex
|
||||
|
||||
cargo build --release
|
||||
|
||||
rm -f "${IMAGE}"
|
||||
fallocate -l 1GiB "${IMAGE}"
|
||||
|
||||
target/release/redox_installer -c res/test.toml "${IMAGE}"
|
||||
|
||||
qemu-system-x86_64 "${QEMU_ARGS[@]}" -drive "file=${IMAGE},format=raw"
|
||||
Reference in New Issue
Block a user