Compare commits
520 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b78a791e27 | |||
| 90e0fe210a | |||
| c74e2864b0 | |||
| 96d435936d | |||
| a936d00e6c | |||
| 439c1f2905 | |||
| b199929813 | |||
| dc8413f4cb | |||
| 6ade863ea5 | |||
| d807dd3e4b | |||
| 065e22b7c4 | |||
| 57ad275d8f | |||
| 473b4baeb0 | |||
| 00ba438c95 | |||
| 6c1e2aa446 | |||
| 3acc7a240f | |||
| 47c9d242f5 | |||
| 6767faba13 | |||
| 44bbd27e1b | |||
| af493b9f4e | |||
| d03893a253 | |||
| 3ed2bee9bb | |||
| 1d1176de40 | |||
| 5b515c0c9b | |||
| f26c86b176 | |||
| b59677627c | |||
| 3d6f9cec6e | |||
| 0955762196 | |||
| 7f469db981 | |||
| 7dad335ba7 | |||
| eb50a95c9b | |||
| c4b51b9ca8 | |||
| 04dbcbcd91 | |||
| e75d311776 | |||
| 64a8ff46e1 | |||
| d00745b402 | |||
| c51642c8b6 | |||
| 61d52cf6a7 | |||
| 2360a346ac | |||
| 1e0119b3cb | |||
| ceeaf513b6 | |||
| f4a256ebcd | |||
| d47cd7d3f6 | |||
| 32d067d293 | |||
| 4726ebdd9d | |||
| 42db72daf2 | |||
| 57277f2041 | |||
| 9d4ae07987 | |||
| 7bd6189ca9 | |||
| 5778818bf6 | |||
| 0e01427cd8 | |||
| 57e05acef2 | |||
| a44303b4f6 | |||
| 2e1bca19ac | |||
| 2b8ea938ea | |||
| a2f3b55b68 | |||
| a59e7fca9b | |||
| c0777c08ac | |||
| 52b230fc67 | |||
| b8f2c63bdd | |||
| dc77e817e2 | |||
| 66f4766c6b | |||
| 644f747648 | |||
| 5c94db6f1a | |||
| 50683d5a7a | |||
| 54df280b02 | |||
| a7f61b12ad | |||
| 91c5ffb986 | |||
| 116d482d2b | |||
| b6700b94dc | |||
| fefd943c3f | |||
| ab92a083e8 | |||
| 5abce443a5 | |||
| 84288a2f5d | |||
| 2dee3bba4c | |||
| 5af2abde0a | |||
| 437d06ab8c | |||
| 5c54b189f9 | |||
| 9484534c76 | |||
| eea5c96137 | |||
| 5e044e88c0 | |||
| d7391bc6a8 | |||
| c796cc7413 | |||
| f37afb04bb | |||
| 74582a0917 | |||
| 5622165c92 | |||
| 8a46a6df39 | |||
| ae034662e6 | |||
| 0a1fd60d76 | |||
| fc4c2470a1 | |||
| 508feda5eb | |||
| 8591d96f67 | |||
| 2d61aceff9 | |||
| 25cc366a63 | |||
| 4af64e157a | |||
| 98286ce752 | |||
| 5828e12e85 | |||
| 0a14166c2f | |||
| d03aa518fe | |||
| b40fecf185 | |||
| ca7ea3c0c8 | |||
| a1c94311db | |||
| f30b363ce1 | |||
| 26dc32d710 | |||
| 0cb6f3cad5 | |||
| 1506ed50fc | |||
| 63217aa69a | |||
| dc25344b68 | |||
| bd6efe0c83 | |||
| aecd042afc | |||
| 291ce5edc0 | |||
| b959c34287 | |||
| 02685f5c70 | |||
| d0d5761b2d | |||
| 066e5f6e8a | |||
| 9e2e370c0b | |||
| 165d674d98 | |||
| 3d06dd39ba | |||
| 13642ab159 | |||
| 204abb9c5d | |||
| c403c1eb54 | |||
| 940b027514 | |||
| d17b938693 | |||
| fd0bb82b50 | |||
| 3170e8cd9c | |||
| 7e3915deb6 | |||
| 22f759aa4f | |||
| dc507f7e2a | |||
| 5ee3138d3d | |||
| 6bccac38c3 | |||
| 32aeff0cae | |||
| 1aa3142f21 | |||
| ec5f32ec38 | |||
| 84cf28b5d0 | |||
| 9531b20bf8 | |||
| 6abf533fdf | |||
| a0c734da01 | |||
| 90fbcab52e | |||
| 356ec3ffb1 | |||
| 9a0da927f9 | |||
| c2790a7168 | |||
| 947ea54411 | |||
| ee04ed2f8c | |||
| 8f6e47c1da | |||
| c22fb790b8 | |||
| 1d55c5aa6a | |||
| 59dbfee126 | |||
| 469db981a4 | |||
| d8950b52c2 | |||
| 56e66993af | |||
| ab371cff49 | |||
| 97ffcd4ad3 | |||
| b9b740f3b0 | |||
| 08361c6595 | |||
| 3cf7bf58ab | |||
| 707162611e | |||
| 5c8f22b069 | |||
| 43ec8d1417 | |||
| 2ac60da18d | |||
| 3baf131419 | |||
| 554f3dc205 | |||
| 6a6bd97910 | |||
| 6c052ba241 | |||
| af3032de26 | |||
| 4f2b5e65ee | |||
| 5174b2bba6 | |||
| 708cc1c5ae | |||
| 8a31d1b6a6 | |||
| 3800bf1510 | |||
| 136ff8c8dc | |||
| faec36773b | |||
| 247ef8bf62 | |||
| 8cc2e31299 | |||
| dcba5d8018 | |||
| 7dc8323d24 | |||
| e8cea1af66 | |||
| bfa2f33798 | |||
| 15f3c909c7 | |||
| 1713850c0e | |||
| efb29941f5 | |||
| 14a07dc268 | |||
| e4ca510bc0 | |||
| 7c6e95d9f0 | |||
| 5156ebd179 | |||
| e7cf569a1f | |||
| c07e46b7ba | |||
| 667ffcc01a | |||
| 07e8093ee0 | |||
| ab0bb9aec7 | |||
| 644a2d2081 | |||
| 4e0ab54567 | |||
| 5f00068c26 | |||
| c1601fe16c | |||
| e3f0fe6ef9 | |||
| b3dd3aa5f0 | |||
| 944b175a6e | |||
| 86c12ebd7c | |||
| 2bec291700 | |||
| 9fad87efbe | |||
| dcf40072f4 | |||
| 9a8437f468 | |||
| 97bc669a76 | |||
| 50b4ebfcd5 | |||
| 3f1bff26e3 | |||
| 6ce9d1fdac | |||
| 2c2920572c | |||
| ae715277f3 | |||
| 4048f541a0 | |||
| b9960350c8 | |||
| 985d6e815c | |||
| 54341680ec | |||
| 6234d76e8d | |||
| 7f8a2e467e | |||
| ae1f15a449 | |||
| 5db0da52e2 | |||
| e7e0e3ce8d | |||
| 58fff36b4e | |||
| 211e12b842 | |||
| 13948965fa | |||
| ad432d527d | |||
| 2db6998889 | |||
| eb8dc00fa8 | |||
| 1a56983249 | |||
| 588bd3203b | |||
| c9f9e0be7d | |||
| 20fff1a0ee | |||
| a7c22caba5 | |||
| ace1726e29 | |||
| 9a4638e922 | |||
| 62e59e1006 | |||
| cdccd5d786 | |||
| 06d43016c1 | |||
| 794e732c80 | |||
| 73c3d8bb79 | |||
| 467c9cfe9a | |||
| 8a1aa3663a | |||
| f601b2a8ce | |||
| 63bde95462 | |||
| 82f011363d | |||
| 898f9d8b2e | |||
| 9fc80c8bf6 | |||
| 7de07e802e | |||
| 790ea59269 | |||
| 04b4a2e4ad | |||
| 63468fc414 | |||
| 0ea2fd49dd | |||
| 0e0ae520c5 | |||
| 9215e32254 | |||
| 419bd0f0a6 | |||
| ebe9579d66 | |||
| d83e567e97 | |||
| f9f4f7c5f0 | |||
| 2e46c28097 | |||
| 731b97262b | |||
| c98e63af87 | |||
| cfe5841646 | |||
| 9083dd07c9 | |||
| 25be3a82bd | |||
| 6d9a53c956 | |||
| c6b997f98b | |||
| 5d4ed0831a | |||
| f1b88c38c0 | |||
| 0068a6d498 | |||
| 5ff37f1f77 | |||
| c990fa9a45 | |||
| 80e3a34645 | |||
| 58f2d28ff5 | |||
| a6af6a9289 | |||
| d2256b3eb3 | |||
| 3191d7d182 | |||
| bede93cd85 | |||
| e869b3b158 | |||
| 924adef1dc | |||
| 602a63acf6 | |||
| 43533e8dc7 | |||
| bf4d0161be | |||
| 4578a22297 | |||
| 5e5656f110 | |||
| 86b0d612d8 | |||
| 61c38afb52 | |||
| 8bbdc558bb | |||
| 2ddf1dab3f | |||
| 8e38015b2f | |||
| 3872f74e9a | |||
| 39e8b8fa7f | |||
| b26195f49a | |||
| a70a01bd4a | |||
| a226d19096 | |||
| 7b661e31e3 | |||
| 966105e566 | |||
| 237c156503 | |||
| 946efa2fb5 | |||
| 0754019950 | |||
| 842744c384 | |||
| 2e32481d4c | |||
| ec072bb718 | |||
| 015d352e33 | |||
| 219bf26966 | |||
| ff17fed199 | |||
| cb2bcf9f59 | |||
| a352d7c79c | |||
| 37aab3e566 | |||
| b4df131aff | |||
| b32a421b97 | |||
| 69d6cc05de | |||
| 3ae32d9901 | |||
| d53fc6afff | |||
| 87ecce9e44 | |||
| 3e1fc9dae5 | |||
| f96d330993 | |||
| 9c6e0e6789 | |||
| 79cef7c034 | |||
| 6f5ca24838 | |||
| 6126bfeee3 | |||
| 3ef8557865 | |||
| 3906a90a7b | |||
| f0450a4564 | |||
| 53a3f6d7e9 | |||
| e9a7181e9f | |||
| 8c57fd95e7 | |||
| a1c69a8e82 | |||
| a345132e0f | |||
| 72b0492624 | |||
| 78c5e1c6d2 | |||
| 40e2a3e221 | |||
| 2e6e272c72 | |||
| f206dab653 | |||
| eac37c7dc5 | |||
| 38b24ad321 | |||
| 53a1e6d49f | |||
| 16d6f6b93a | |||
| 2dd4c920c7 | |||
| 1701198b11 | |||
| 47c9acc417 | |||
| 513bfaf805 | |||
| a0ec667025 | |||
| ab690ce519 | |||
| d4ea343a39 | |||
| e74975d2f9 | |||
| db22937361 | |||
| 6d62d22262 | |||
| cbdfba7b94 | |||
| 90dc177d75 | |||
| ade6234f12 | |||
| dc0b0c5c2d | |||
| dfd2dd8ae1 | |||
| 260bc1e1e8 | |||
| 541a58181a | |||
| 070aeb54ef | |||
| b272cc2d19 | |||
| 9c907a22dd | |||
| a9da8e7a57 | |||
| 8f41e5a777 | |||
| 213b4aea5f | |||
| dc5bd70728 | |||
| fc0af50b97 | |||
| f4a3e1dde0 | |||
| f474a9fd77 | |||
| 93f7ec7a55 | |||
| 801cbd9072 | |||
| 4f3b097a4a | |||
| a830383220 | |||
| e270ce117f | |||
| 0996d844c4 | |||
| 1e9cec6092 | |||
| e95be4caf3 | |||
| 3b78720167 | |||
| b04efe081b | |||
| 03f59a419b | |||
| ca1a1d615a | |||
| c6c9399305 | |||
| 862e9b0a37 | |||
| 154b456d99 | |||
| 5a1aaadd05 | |||
| 8737f19459 | |||
| 92d38e32c1 | |||
| e635648ed7 | |||
| f40f6921e8 | |||
| 3342778968 | |||
| ad4826eb70 | |||
| 7fc1f16a67 | |||
| cae1402f5a | |||
| bc2e8c2923 | |||
| 8c03929973 | |||
| 6965d67efd | |||
| 1549d1af8b | |||
| 8d66d9ce54 | |||
| 1f97d8220c | |||
| 20bce53b7a | |||
| 9471a2e410 | |||
| dcce0d9e6e | |||
| 4820210cca | |||
| 0d12413297 | |||
| 2c770ca997 | |||
| 3e0ce05e23 | |||
| 0032159492 | |||
| f582c60f34 | |||
| 97652cc6f8 | |||
| dffa85f874 | |||
| b972f08f50 | |||
| 0d76e1441b | |||
| 7a4a41a34e | |||
| 7470cc7846 | |||
| 86353cdff5 | |||
| 34f9b3f6df | |||
| 7d2663e6a2 | |||
| 1f90f21147 | |||
| 05e3802844 | |||
| 31cfde4eaf | |||
| 138525cf7d | |||
| 6c1d98cea8 | |||
| 6642d36079 | |||
| f0c38fe609 | |||
| f418d30dc9 | |||
| d0dfef2f0b | |||
| 3db0c2a4b1 | |||
| aff13c0d66 | |||
| 5dc6c48623 | |||
| fa6c19f79b | |||
| 665120f063 | |||
| 3f0bda4e14 | |||
| 5ab8c050d8 | |||
| d0c698a031 | |||
| 9c2864e130 | |||
| f1073d0c48 | |||
| b526a29e35 | |||
| 25fe6d3826 | |||
| dcaf5c76be | |||
| 31605167ce | |||
| 8a22d96669 | |||
| 8449c5b6ad | |||
| d058e56d6e | |||
| 81c39551d7 | |||
| fd4d674422 | |||
| a313852428 | |||
| b25b268756 | |||
| a0b381765b | |||
| 3a58139e57 | |||
| 14cbb4f4f8 | |||
| 0353bfc00c | |||
| a5b6dcc0c0 | |||
| 4ecb62da40 | |||
| 5c0fc80db2 | |||
| ec00f58d73 | |||
| 3dcaad55fe | |||
| c06edb232b | |||
| 33f218aadb | |||
| 1488d1ef56 | |||
| 36b3cf04a9 | |||
| 9c16bca2a5 | |||
| 9bd5d69776 | |||
| 11f3485173 | |||
| b7608c1530 | |||
| 35ed814e68 | |||
| fe0a5de2d4 | |||
| ac8cee662e | |||
| dc575b790f | |||
| fb2eb845ba | |||
| e8d92bee0f | |||
| dd4a90119f | |||
| 55e6b13555 | |||
| d9bdd6e3dd | |||
| ac07b358ad | |||
| 896812c68a | |||
| 4316326792 | |||
| b6d49e2fa1 | |||
| 195271779d | |||
| 7e80cba121 | |||
| 32c61e93e0 | |||
| d17b55089a | |||
| cf728be7ae | |||
| 3841521174 | |||
| 0bb53636e4 | |||
| dc5326a13a | |||
| 7d48fcf382 | |||
| fb55c69fe5 | |||
| e97f126f35 | |||
| f509c4b12c | |||
| e355f755ee | |||
| 94c36fa34d | |||
| d13d926d7b | |||
| 8d806568b9 | |||
| bef665af8d | |||
| 9cf1d60253 | |||
| dcc67e4cf4 | |||
| 77848132ea | |||
| 73528fa872 | |||
| a1cae73566 | |||
| b1805779a8 | |||
| ff0106b212 | |||
| 51e037150c | |||
| 856db3e21d | |||
| 1a58014da9 | |||
| 6d5be41c13 | |||
| 2d578215e3 | |||
| 25174cc4ce | |||
| 7412bdd6b7 | |||
| d2a073788a | |||
| 702bef4418 | |||
| 799a847ffa | |||
| 670d874d07 | |||
| 7f8776fafb | |||
| 664801337d | |||
| ecfd43fdbd | |||
| 7a4d78149a | |||
| 1eff7c7e37 | |||
| 39707bcf60 | |||
| 1f08b76531 | |||
| e0fceebffd | |||
| 4a444a3423 | |||
| 6450660c50 | |||
| 556c842ec7 | |||
| d51e891f22 | |||
| 41e716219d | |||
| bae24a54b0 | |||
| 8ff4863bbb | |||
| d116ce57da | |||
| c231ddb219 | |||
| 49925a39d0 | |||
| bc2d90f531 |
@@ -1,7 +0,0 @@
|
|||||||
[**.c]
|
|
||||||
indent_size = 4
|
|
||||||
indent_style = space
|
|
||||||
|
|
||||||
[**.yml]
|
|
||||||
indent_size = 4
|
|
||||||
indent_style = space
|
|
||||||
+4
-11
@@ -1,11 +1,4 @@
|
|||||||
.idea/
|
target
|
||||||
prefix/
|
image.bin
|
||||||
sysroot/
|
image
|
||||||
**/target/
|
image*
|
||||||
.gdb_history
|
|
||||||
*.patch
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
/.vim
|
|
||||||
.vscode/
|
|
||||||
|
|
||||||
|
|||||||
+13
-69
@@ -1,7 +1,4 @@
|
|||||||
image: "redoxos/redoxer:latest"
|
image: "redoxos/redoxer"
|
||||||
|
|
||||||
variables:
|
|
||||||
GIT_SUBMODULE_STRATEGY: recursive
|
|
||||||
|
|
||||||
workflow:
|
workflow:
|
||||||
rules:
|
rules:
|
||||||
@@ -10,78 +7,25 @@ workflow:
|
|||||||
|
|
||||||
stages:
|
stages:
|
||||||
- build
|
- build
|
||||||
- cross-build
|
|
||||||
- test
|
- test
|
||||||
before_script:
|
|
||||||
cargo install cbindgen
|
|
||||||
|
|
||||||
fmt:
|
build:linux:
|
||||||
stage: build
|
stage: build
|
||||||
needs: []
|
script: cargo +nightly build --verbose
|
||||||
script:
|
|
||||||
- rustup component add rustfmt-preview
|
|
||||||
- ./fmt.sh -- --check
|
|
||||||
|
|
||||||
clippy:
|
build:redox:
|
||||||
stage: build
|
stage: build
|
||||||
needs: []
|
script: redoxer build --verbose
|
||||||
# TODO: remove when passed
|
|
||||||
allow_failure: true
|
|
||||||
script:
|
|
||||||
- ./check.sh --clippy
|
|
||||||
|
|
||||||
linux:
|
|
||||||
stage: build
|
|
||||||
script:
|
|
||||||
- rustup component add clippy
|
|
||||||
- ./check.sh --host
|
|
||||||
# - ./check.sh --host --clippy
|
|
||||||
|
|
||||||
x86_64:
|
|
||||||
stage: build
|
|
||||||
script:
|
|
||||||
- ./check.sh --arch=x86_64
|
|
||||||
|
|
||||||
i586:
|
|
||||||
stage: cross-build
|
|
||||||
script:
|
|
||||||
- ./check.sh --arch=i586
|
|
||||||
# - ./check.sh --arch=i586 --clippy
|
|
||||||
|
|
||||||
aarch64:
|
|
||||||
stage: cross-build
|
|
||||||
image: "redoxos/redoxer:aarch64"
|
|
||||||
script:
|
|
||||||
- ./check.sh --arch=aarch64
|
|
||||||
# - ./check.sh --arch=aarch64 --clippy
|
|
||||||
|
|
||||||
riscv64gc:
|
|
||||||
stage: cross-build
|
|
||||||
script:
|
|
||||||
- ./check.sh --arch=riscv64gc
|
|
||||||
# - ./check.sh --arch=riscv64gc --clippy
|
|
||||||
|
|
||||||
test:linux:
|
test:linux:
|
||||||
stage: test
|
stage: test
|
||||||
needs: [linux]
|
dependencies:
|
||||||
script:
|
- build:linux
|
||||||
- ./check.sh --host --test
|
script: cargo +nightly test --verbose
|
||||||
|
|
||||||
test:x86_64:
|
test:redox:
|
||||||
stage: test
|
stage: test
|
||||||
needs: [x86_64]
|
dependencies:
|
||||||
script:
|
- build:redox
|
||||||
# timeout: https://gitlab.redox-os.org/redox-os/relibc/-/issues/238
|
# only run integration test as without KVM unit tests is super slow
|
||||||
- timeout -s KILL 9m ./check.sh --arch=x86_64 --test
|
script: redoxer test --verbose -- --test '*' -- --nocapture
|
||||||
|
|
||||||
test:aarch64:
|
|
||||||
stage: test
|
|
||||||
needs: [aarch64]
|
|
||||||
image: "redoxos/redoxer:aarch64"
|
|
||||||
# many issues that not exist in x86_64, and lack of interest to fix so far
|
|
||||||
allow_failure: true
|
|
||||||
when: manual
|
|
||||||
script:
|
|
||||||
- timeout -s KILL 9m ./check.sh --arch=aarch64 --test
|
|
||||||
|
|
||||||
#TODO: Enable more arch once dynamic linker working
|
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
[submodule "openlibm"]
|
|
||||||
path = openlibm
|
|
||||||
url = https://gitlab.redox-os.org/redox-os/openlibm.git
|
|
||||||
branch = master
|
|
||||||
[submodule "src/dlmalloc-rs"]
|
|
||||||
path = dlmalloc-rs
|
|
||||||
url = https://gitlab.redox-os.org/redox-os/dlmalloc-rs.git
|
|
||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
sudo: required
|
||||||
|
language: rust
|
||||||
|
rust:
|
||||||
|
- nightly
|
||||||
|
os:
|
||||||
|
- linux
|
||||||
|
- osx
|
||||||
|
dist: trusty
|
||||||
|
before_install:
|
||||||
|
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||||
|
sudo apt-get install -qq pkg-config fuse libfuse-dev;
|
||||||
|
sudo modprobe fuse;
|
||||||
|
sudo chmod 666 /dev/fuse;
|
||||||
|
sudo chown root:$USER /etc/fuse.conf;
|
||||||
|
fi
|
||||||
|
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then
|
||||||
|
brew update;
|
||||||
|
brew install Caskroom/cask/osxfuse;
|
||||||
|
fi
|
||||||
|
notifications:
|
||||||
|
email: false
|
||||||
-123
@@ -1,123 +0,0 @@
|
|||||||
# Contributing
|
|
||||||
|
|
||||||
## Table of contents
|
|
||||||
1. [What to do](#what-to-do)
|
|
||||||
2. [Code style](#code-style)
|
|
||||||
3. [Sending merge requests](#sending-merge-requests)
|
|
||||||
4. [Writing tests](#writing-tests)
|
|
||||||
5. [Running tests](#running-tests)
|
|
||||||
|
|
||||||
Maintaining a libc is tough work, and we'd love some help!
|
|
||||||
|
|
||||||
## What to do
|
|
||||||
|
|
||||||
For now, we are still trying to get full libc compatibility before we move on to
|
|
||||||
any optimisation.
|
|
||||||
|
|
||||||
- We currently have a number of unimplemented functions. Search for
|
|
||||||
`unimplemented!()` and hop right in!
|
|
||||||
- If you notice any missing functionality, feel free to add it in
|
|
||||||
|
|
||||||
## Code style
|
|
||||||
|
|
||||||
We have a `rustfmt.toml` in the root directory of relibc. Please run `./fmt.sh`
|
|
||||||
before sending in any merge requests as it will automatically format your code.
|
|
||||||
|
|
||||||
With regards to general style:
|
|
||||||
|
|
||||||
### Where applicable, prefer using references to raw pointers
|
|
||||||
|
|
||||||
This is most obvious when looking at `stdio` functions. If raw pointers were
|
|
||||||
used instead of references, then the resulting code would be significantly
|
|
||||||
uglier. Instead try to check for pointer being valid with `pointer::as_ref()`
|
|
||||||
and `pointer::as_mut()` and then immediately use those references instead.
|
|
||||||
|
|
||||||
Internal functions should always take references.
|
|
||||||
|
|
||||||
### Use the c types exposed in our platform module instead of Rust's inbuilt integer types
|
|
||||||
|
|
||||||
This is so we can guarantee that everything works across platforms. While it is
|
|
||||||
generally accepted these days that an `int` has 32 bits (which matches against
|
|
||||||
an `i32`), some platforms have `int` as having 16 bits, and others have long as
|
|
||||||
being 32 bits instead of 64. If you use the types in platform, then we can
|
|
||||||
guarantee that your code will "just work" should we port relibc to a different
|
|
||||||
architecture.
|
|
||||||
|
|
||||||
### Use our internal functions
|
|
||||||
|
|
||||||
If you need to use a C string, don't reinvent the wheel. We have functions in
|
|
||||||
the platform module that convert C strings to Rust slices.
|
|
||||||
|
|
||||||
We also have structures that wrap files, wrap writable strings, and wrap various
|
|
||||||
other commonly used things that you should use instead of rolling your own.
|
|
||||||
|
|
||||||
## Sending merge requests
|
|
||||||
|
|
||||||
If you have sent us a merge request, first of all, thanks for taking your time
|
|
||||||
to help us!
|
|
||||||
|
|
||||||
The first thing to note is that we do most of our development on our
|
|
||||||
[GitLab server](https://gitlab.redox-os.org/redox-os/relibc), and as such it is
|
|
||||||
possible that none of the maintainers will see your merge request if it is
|
|
||||||
opened on GitHub.
|
|
||||||
|
|
||||||
In your merge request, please put in the description:
|
|
||||||
- What functions (if any) have been implemented or changed
|
|
||||||
- The rationale behind your merge request (e.g. why you thought this change was
|
|
||||||
required. If you are just implementing some functions, you can ignore this)
|
|
||||||
- Any issues that are related to the merge request
|
|
||||||
|
|
||||||
We have CI attached to our GitLab instance, so all merge requests are checked to
|
|
||||||
make sure that they are tested before they are merged. Please write tests for
|
|
||||||
the functions that you add/change and test locally on your own machine
|
|
||||||
***before*** submitting a merge request.
|
|
||||||
|
|
||||||
## Writing tests
|
|
||||||
|
|
||||||
Every function that gets written needs to have a test in C in order to make sure
|
|
||||||
it works as intended. Here are a few guidelines for writing good tests.
|
|
||||||
|
|
||||||
### Ensure that any literals you have are mapped to variables instead of being directly passed to a function.
|
|
||||||
|
|
||||||
Sometimes compilers take literals put into libc functions and run them
|
|
||||||
internally during compilation, which can cause some false positives. All tests
|
|
||||||
are compiled with `-fno-builtin`, which theoretically solves this issue, but
|
|
||||||
just in case, it'd be a good idea to map inputs to variables.
|
|
||||||
|
|
||||||
```c
|
|
||||||
#include "string.h"
|
|
||||||
#include "stdio.h"
|
|
||||||
|
|
||||||
int main(void) {
|
|
||||||
// Don't do this
|
|
||||||
printf("%d\n", strcspn("Hello", "Hi"));
|
|
||||||
|
|
||||||
// Do this
|
|
||||||
char *first = "Hello";
|
|
||||||
char *second = "Hi";
|
|
||||||
printf("%d\n", strcspn(first, second));
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Ensure your tests cover every section of code.
|
|
||||||
|
|
||||||
What happens if a string in `strcmp()` is shorter than the other string? What
|
|
||||||
happens if the first argument to `strcspn()` is longer than the second string?
|
|
||||||
In order to make sure that all functions work as expected, we ask that any tests
|
|
||||||
cover as much of the code that you have written as possible.
|
|
||||||
|
|
||||||
## Running tests
|
|
||||||
|
|
||||||
Running tests is an important part in trying to find bugs. Before opening a
|
|
||||||
merge request, we ask that you test on your own machine to make sure there are
|
|
||||||
no regressions.
|
|
||||||
|
|
||||||
You can run tests with `make test` in the root directory of relibc to compile
|
|
||||||
relibc, compile the tests and run them. This *will* print a lot of output to
|
|
||||||
stdout, so be warned!
|
|
||||||
|
|
||||||
You can test against verified correct output with `make verify` in the tests
|
|
||||||
directory. You will need to manually create the correct output and put it in the
|
|
||||||
tests/expected directory. Running any `make` commands in the tests directory
|
|
||||||
will ***not*** rebuild relibc, so you'll need to go back to the root directory
|
|
||||||
if you need to rebuild relibc.
|
|
||||||
Generated
+474
-397
File diff suppressed because it is too large
Load Diff
+84
-135
@@ -1,154 +1,103 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "relibc"
|
name = "redoxfs"
|
||||||
version = "0.2.5+rb0.3.1"
|
description = "The Redox Filesystem"
|
||||||
|
repository = "https://gitlab.redox-os.org/redox-os/redoxfs"
|
||||||
|
version = "0.9.1+rb0.3.1"
|
||||||
|
license-file = "LICENSE"
|
||||||
|
readme = "README.md"
|
||||||
authors = ["Jeremy Soller <jackpot51@gmail.com>", "vasilito <adminpupkin@gmail.com>"]
|
authors = ["Jeremy Soller <jackpot51@gmail.com>", "vasilito <adminpupkin@gmail.com>"]
|
||||||
edition = "2024"
|
edition = "2021"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "relibc"
|
name = "redoxfs"
|
||||||
crate-type = ["staticlib"]
|
path = "src/lib.rs"
|
||||||
|
|
||||||
[workspace]
|
[[bin]]
|
||||||
members = [
|
name = "redoxfs"
|
||||||
"src/crt0",
|
path = "src/bin/mount.rs"
|
||||||
"src/crti",
|
doc = false
|
||||||
"src/crtn",
|
required-features = ["std"]
|
||||||
"redox-rt",
|
|
||||||
"ld_so",
|
|
||||||
"generic-rt",
|
|
||||||
]
|
|
||||||
exclude = ["tests", "dlmalloc-rs"]
|
|
||||||
|
|
||||||
[workspace.lints.clippy]
|
[[bin]]
|
||||||
borrow_as_ptr = "deny"
|
name = "redoxfs-ar"
|
||||||
cast_lossless = "warn" # TODO review occurrences
|
path = "src/bin/ar.rs"
|
||||||
cast_possible_truncation = "allow" # TODO review occurrences
|
doc = false
|
||||||
cast_possible_wrap = "allow" # TODO review occurrences
|
required-features = ["std"]
|
||||||
cast_precision_loss = "allow" # TODO review occurrences
|
|
||||||
cast_ptr_alignment = "allow" # TODO review occurrences
|
|
||||||
cast_sign_loss = "allow" # TODO review occurrences
|
|
||||||
ignored_unit_patterns = "deny"
|
|
||||||
implicit_clone = "deny"
|
|
||||||
manual_let_else = "deny"
|
|
||||||
missing_errors_doc = "allow" # TODO review occurrences
|
|
||||||
missing_panics_doc = "allow" # TODO review occurrences
|
|
||||||
missing_safety_doc = "allow" # TODO review occurrences
|
|
||||||
mut_from_ref = "deny"
|
|
||||||
precedence = "deny"
|
|
||||||
ptr_as_ptr = "warn" # TODO review occurrences
|
|
||||||
ptr_cast_constness = "warn" # TODO review occurrences
|
|
||||||
ptr_offset_by_literal = "deny"
|
|
||||||
ref_as_ptr = "warn" # TODO review occurrences
|
|
||||||
type_complexity = "allow" # TODO review occurrences
|
|
||||||
upper_case_acronyms = "allow" # TODO review occurrences
|
|
||||||
zero_ptr = "deny" # must allow on public constants due to cbindgen issue
|
|
||||||
|
|
||||||
[workspace.lints.rust]
|
[[bin]]
|
||||||
dangling_pointers_from_temporaries = "deny"
|
name = "redoxfs-clone"
|
||||||
dead_code = "allow" # TODO review occuurences
|
path = "src/bin/clone.rs"
|
||||||
deprecated = "deny"
|
doc = false
|
||||||
improper_ctypes_definitions = "deny"
|
required-features = ["std"]
|
||||||
internal_features = "allow" # core_intrinsics and lang_items
|
|
||||||
irrefutable_let_patterns = "deny"
|
|
||||||
mismatched_lifetime_syntaxes = "deny"
|
|
||||||
non_camel_case_types = "allow" # needed for most POSIX type names
|
|
||||||
non_snake_case = "allow" # TODO review occuurences
|
|
||||||
non_upper_case_globals = "allow" # TODO review occuurences
|
|
||||||
unexpected_cfgs = "deny"
|
|
||||||
unpredictable_function_pointer_comparisons = "deny"
|
|
||||||
unreachable_code = "allow" # TODO review occuurences
|
|
||||||
unsafe_op_in_unsafe_fn = "deny"
|
|
||||||
unused_imports = "deny"
|
|
||||||
unused_must_use = "deny"
|
|
||||||
unused_mut = "deny"
|
|
||||||
unused_unsafe = "deny"
|
|
||||||
unused_variables = "allow" # TODO review occurrences (too many for now)
|
|
||||||
|
|
||||||
[lints]
|
[[bin]]
|
||||||
workspace = true
|
name = "redoxfs-mkfs"
|
||||||
|
path = "src/bin/mkfs.rs"
|
||||||
|
doc = false
|
||||||
|
required-features = ["std"]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[[bin]]
|
||||||
bitflags = "2"
|
name = "redoxfs-resize"
|
||||||
ioslice = { version = "0.6", default-features = false }
|
path = "src/bin/resize.rs"
|
||||||
plain = "0.2"
|
doc = false
|
||||||
redox-path = "0.4.0"
|
required-features = ["std"]
|
||||||
redox_protocols = { package = "libredox", path = "../libredox", default-features = false, features = ["protocol"] }
|
|
||||||
redox_syscall = { path = "../syscall" }
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
cc = "1"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags.workspace = true
|
aes = { version = "0.8", default-features = false }
|
||||||
arrayvec = { version = "0.7.6", default-features = false }
|
argon2 = { version = "0.4", default-features = false, features = ["alloc"] }
|
||||||
cbitset = "0.2"
|
base64ct = { version = "1", default-features = false }
|
||||||
posix-regex = { version = "0.1.4", features = ["no_std"] }
|
bitflags = "2"
|
||||||
|
endian-num = "0.1"
|
||||||
rand = { version = "0.10", default-features = false }
|
env_logger = { version = "0.11", optional = true }
|
||||||
rand_xorshift = "0.5"
|
getrandom = { version = "0.2.5", optional = true }
|
||||||
rand_jitter = "0.6"
|
humansize = { version = "2", optional = true }
|
||||||
|
libc = "0.2"
|
||||||
memchr = { version = "2.2.0", default-features = false }
|
log = { version = "0.4.14", default-features = false, optional = true }
|
||||||
plain.workspace = true
|
lz4_flex = { version = "0.11", default-features = false, features = ["checked-decode"] }
|
||||||
unicode-width = "0.1"
|
parse-size = { version = "1", optional = true }
|
||||||
__libc_only_for_layout_checks = { package = "libc", version = "0.2.149", optional = true }
|
range-tree = { version = "0.1", optional = true }
|
||||||
md5-crypto = { package = "md-5", version = "0.10.6", default-features = false }
|
redox_syscall = { path = "../syscall" }
|
||||||
sha-crypt = { version = "0.5", default-features = false }
|
seahash = { version = "4.1.0", default-features = false }
|
||||||
base64ct = { version = "1.6", default-features = false, features = ["alloc"] }
|
smallvec = { version = "1.15.2", optional = true }
|
||||||
bcrypt-pbkdf = { version = "0.10", default-features = false, features = [
|
termion = { version = "4", optional = true }
|
||||||
"alloc",
|
uuid = { version = "1.4", default-features = false }
|
||||||
] }
|
xts-mode = { version = "0.5", default-features = false }
|
||||||
scrypt = { version = "0.11", default-features = false, features = ["simple"] }
|
|
||||||
pbkdf2 = { version = "0.12", features = ["sha2"] }
|
|
||||||
sha2 = { version = "0.10", default-features = false }
|
|
||||||
generic-rt = { path = "generic-rt" }
|
|
||||||
chrono-tz = { version = "0.10", default-features = false }
|
|
||||||
chrono = { version = "0.4", default-features = false, features = ["alloc"] }
|
|
||||||
libm = "0.2"
|
|
||||||
log = "0.4"
|
|
||||||
spin = "0.9.8"
|
|
||||||
argon2 = "0.5.3"
|
|
||||||
|
|
||||||
[dependencies.dlmalloc]
|
|
||||||
path = "dlmalloc-rs"
|
|
||||||
default-features = false
|
|
||||||
features = ["c_api"]
|
|
||||||
|
|
||||||
[dependencies.object]
|
|
||||||
version = "0.36.7"
|
|
||||||
git = "https://gitlab.redox-os.org/andypython/object"
|
|
||||||
branch = "new"
|
|
||||||
default-features = false
|
|
||||||
features = ["elf", "read_core"]
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
|
||||||
sc = "0.2.7"
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "redox")'.dependencies]
|
|
||||||
redox_syscall.workspace = true
|
|
||||||
redox-rt = { path = "redox-rt" }
|
|
||||||
redox-path.workspace = true
|
|
||||||
redox_event = { version = "0.4.8", default-features = false, features = ["redox_syscall"] }
|
|
||||||
ioslice.workspace = true
|
|
||||||
redox-ioctl = { path = "redox-ioctl" }
|
|
||||||
redox_protocols.workspace = true
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# to enable trace level, take out this `no_trace`
|
default = ["std", "log", "fuse"]
|
||||||
default = ["check_against_libc_crate", "no_trace"]
|
fuse = [
|
||||||
check_against_libc_crate = ["__libc_only_for_layout_checks"]
|
"fuser",
|
||||||
math_libm = []
|
"std",
|
||||||
no_trace = ["log/release_max_level_debug"]
|
]
|
||||||
# for very verbose activity beyond trace level
|
std = [
|
||||||
trace_tls = []
|
"env_logger",
|
||||||
|
"getrandom",
|
||||||
|
"humansize",
|
||||||
|
"libredox",
|
||||||
|
"parse-size",
|
||||||
|
"range-tree",
|
||||||
|
"termion",
|
||||||
|
"uuid/v4",
|
||||||
|
"redox_syscall/std",
|
||||||
|
"redox-scheme",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
[profile.dev]
|
[target.'cfg(not(target_os = "redox"))'.dependencies]
|
||||||
panic = "abort"
|
fuser = { version = "0.16", optional = true }
|
||||||
|
|
||||||
[profile.release]
|
[target.'cfg(target_os = "redox")'.dependencies]
|
||||||
panic = "abort"
|
libredox = { path = "../libredox", optional = true }
|
||||||
|
redox-path = "0.4.0"
|
||||||
|
redox-scheme = { path = "../redox-scheme", optional = true }
|
||||||
|
|
||||||
|
[lints.rust]
|
||||||
|
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
assert_cmd = "2.0.17"
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
cc-11 = { git = "https://github.com/tea/cc-rs", branch = "riscv-abi-arch-fix", package = "cc" }
|
|
||||||
redox_syscall = { path = "../syscall" }
|
redox_syscall = { path = "../syscall" }
|
||||||
libredox = { path = "../libredox" }
|
libredox = { path = "../libredox" }
|
||||||
|
redox-scheme = { path = "../redox-scheme" }
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
The MIT License (MIT)
|
||||||
|
|
||||||
Copyright (c) 2018 Redox OS
|
Copyright (c) 2016 Jeremy Soller
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,230 +1,37 @@
|
|||||||
include config.mk
|
UNAME := $(shell uname)
|
||||||
|
|
||||||
CARGO?=cargo
|
ifeq ($(UNAME),Darwin)
|
||||||
CARGO_TEST?=$(CARGO)
|
FUMOUNT=umount
|
||||||
CARGO_COMMON_FLAGS=-Z build-std=core,alloc,compiler_builtins
|
else ifeq ($(UNAME),FreeBSD)
|
||||||
CARGOFLAGS?=$(CARGO_COMMON_FLAGS)
|
FUMOUNT=sudo umount
|
||||||
CC_WRAPPER?=
|
|
||||||
RUSTCFLAGS?=
|
|
||||||
LINKFLAGS?=-lgcc
|
|
||||||
USE_RUST_LIBM?=
|
|
||||||
TESTBIN?=
|
|
||||||
export OBJCOPY?=objcopy
|
|
||||||
|
|
||||||
export CARGO_TARGET_DIR?=$(shell pwd)/target
|
|
||||||
BUILD?=$(CARGO_TARGET_DIR)/$(TARGET)
|
|
||||||
CARGOFLAGS+=--target=$(TARGET)
|
|
||||||
EXCEPT_MATH=-not -name "math"
|
|
||||||
FEATURE_MATH=
|
|
||||||
ifneq ($(USE_RUST_LIBM),)
|
|
||||||
FEATURE_MATH=--features math_libm
|
|
||||||
EXCEPT_MATH=
|
|
||||||
endif
|
|
||||||
|
|
||||||
TARGET_HEADERS?=$(BUILD)/include
|
|
||||||
export CFLAGS=-I$(TARGET_HEADERS)
|
|
||||||
|
|
||||||
PROFILE?=release
|
|
||||||
|
|
||||||
HEADERS_UNPARSED=$(shell find src/header -mindepth 1 -maxdepth 1 -type d -not -name "_*" $(EXCEPT_MATH) -printf "%f\n")
|
|
||||||
HEADERS_DEPS=$(shell find src/header -type f \( -name "cbindgen.toml" -o -name "*.rs" \))
|
|
||||||
#HEADERS=$(patsubst %,%.h,$(subst _,/,$(HEADERS_UNPARSED)))
|
|
||||||
|
|
||||||
SRC=\
|
|
||||||
Cargo.* \
|
|
||||||
$(shell find src/ redox-rt/src/ ld_so/src/ redox-ioctl/src/ include/ -type f)
|
|
||||||
|
|
||||||
BUILTINS_VERSION=0.1.70
|
|
||||||
|
|
||||||
.PHONY: all clean fmt install install-libs install-headers install-tests libs headers submodules test
|
|
||||||
|
|
||||||
all: | headers libs
|
|
||||||
|
|
||||||
headers: $(BUILD)/$(PROFILE)/librelibc.a $(HEADERS_DEPS)
|
|
||||||
rm -rf $(TARGET_HEADERS)
|
|
||||||
mkdir -p $(TARGET_HEADERS)
|
|
||||||
cp -r include/* $(TARGET_HEADERS)
|
|
||||||
ifeq ($(USE_RUST_LIBM),)
|
|
||||||
cp "openlibm/include"/*.h $(TARGET_HEADERS)
|
|
||||||
cp "openlibm/src"/*.h $(TARGET_HEADERS)
|
|
||||||
endif
|
|
||||||
@set -e ; \
|
|
||||||
for header in $(HEADERS_UNPARSED); do \
|
|
||||||
if test -f "src/header/$$header/cbindgen.toml"; then \
|
|
||||||
printf "\033[0;36;49mWriting Header $$header\033[0m\n"; \
|
|
||||||
out=`echo "$$header" | sed 's/_/\//g'`; \
|
|
||||||
out="$(TARGET_HEADERS)/$$out.h"; \
|
|
||||||
cat "src/header/$$header/cbindgen.toml" cbindgen.globdefs.toml \
|
|
||||||
| cbindgen "src/header/$$header/mod.rs" --config=/dev/stdin --output "$$out"; \
|
|
||||||
fi \
|
|
||||||
done; printf "\033[0;36;49mAll headers written\033[0m\n";
|
|
||||||
|
|
||||||
clean:
|
|
||||||
$(CARGO) clean
|
|
||||||
$(MAKE) -C tests clean
|
|
||||||
rm -rf sysroot
|
|
||||||
|
|
||||||
check:
|
|
||||||
$(CARGO) check
|
|
||||||
|
|
||||||
fmt:
|
|
||||||
./fmt.sh
|
|
||||||
|
|
||||||
install-headers: headers libs
|
|
||||||
mkdir -pv "$(DESTDIR)/include"
|
|
||||||
cp -rv "$(TARGET_HEADERS)"/* "$(DESTDIR)/include"
|
|
||||||
|
|
||||||
libs: \
|
|
||||||
$(BUILD)/$(PROFILE)/libc.a \
|
|
||||||
$(BUILD)/$(PROFILE)/libc.so \
|
|
||||||
$(BUILD)/$(PROFILE)/crt0.o \
|
|
||||||
$(BUILD)/$(PROFILE)/crti.o \
|
|
||||||
$(BUILD)/$(PROFILE)/crtn.o
|
|
||||||
|
|
||||||
install-libs: headers libs
|
|
||||||
mkdir -pv "$(DESTDIR)/lib"
|
|
||||||
cp -v "$(BUILD)/$(PROFILE)/libc.a" "$(DESTDIR)/lib"
|
|
||||||
cp -v "$(BUILD)/$(PROFILE)/libc.so" "$(DESTDIR)/lib"
|
|
||||||
ln -vnfs libc.so "$(DESTDIR)/lib/libc.so.6"
|
|
||||||
ln -vnfs libc.so "$(DESTDIR)/lib/$(LD_SONAME)"
|
|
||||||
cp -v "$(BUILD)/$(PROFILE)/crt0.o" "$(DESTDIR)/lib"
|
|
||||||
ln -vnfs crt0.o "$(DESTDIR)/lib/crt1.o"
|
|
||||||
ln -vnfs crt0.o "$(DESTDIR)/lib/Scrt1.o"
|
|
||||||
cp -v "$(BUILD)/$(PROFILE)/crti.o" "$(DESTDIR)/lib"
|
|
||||||
cp -v "$(BUILD)/$(PROFILE)/crtn.o" "$(DESTDIR)/lib"
|
|
||||||
ifeq ($(USE_RUST_LIBM),)
|
|
||||||
cp -v "$(BUILD)/openlibm/libopenlibm.a" "$(DESTDIR)/lib/libm.a"
|
|
||||||
else
|
else
|
||||||
$(AR) -rcs "$(DESTDIR)/lib/libm.a"
|
# Detect which version of the fusermount binary is available.
|
||||||
|
ifneq (, $(shell which fusermount3))
|
||||||
|
FUMOUNT=fusermount3 -u
|
||||||
|
else
|
||||||
|
FUMOUNT=fusermount -u
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
# Empty libraries for dl, pthread, and rt
|
|
||||||
$(AR) -rcs "$(DESTDIR)/lib/libdl.a"
|
|
||||||
$(AR) -rcs "$(DESTDIR)/lib/libpthread.a"
|
|
||||||
$(AR) -rcs "$(DESTDIR)/lib/librt.a"
|
|
||||||
|
|
||||||
install-tests: tests
|
image.bin:
|
||||||
$(MAKE) -C tests
|
cargo build --release --bin redoxfs-mkfs
|
||||||
mkdir -p "$(DESTDIR)/relibc-tests"
|
dd if=/dev/zero of=image.bin bs=1048576 count=1024
|
||||||
cp -vr tests/build_$(TARGET)/* "$(DESTDIR)/relibc-tests/"
|
target/release/redoxfs-mkfs image.bin
|
||||||
|
|
||||||
install: install-headers install-libs
|
mount: image.bin FORCE
|
||||||
|
mkdir -p image
|
||||||
|
cargo build --release --bin redoxfs
|
||||||
|
target/release/redoxfs image.bin image
|
||||||
|
|
||||||
submodules:
|
unmount: FORCE
|
||||||
git submodule sync
|
sync
|
||||||
git submodule update --init --recursive
|
-${FUMOUNT} image
|
||||||
|
rm -rf image
|
||||||
|
|
||||||
sysroot:
|
clean: FORCE
|
||||||
@mkdir -p $@
|
sync
|
||||||
|
-${FUMOUNT} image
|
||||||
|
rm -rf image image.bin
|
||||||
|
cargo clean
|
||||||
|
|
||||||
.PHONY: sysroot/$(TARGET)
|
FORCE:
|
||||||
sysroot/$(TARGET): | sysroot
|
|
||||||
rm -rf $@
|
|
||||||
rm -rf $@.partial
|
|
||||||
mkdir -p $@.partial
|
|
||||||
$(MAKE) install DESTDIR=$(shell pwd)/$@.partial
|
|
||||||
mv $@.partial $@
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
test: sysroot/$(TARGET)
|
|
||||||
# TODO: Fix SIGILL when running cargo test
|
|
||||||
# $(CARGO_TEST) test
|
|
||||||
$(MAKE) -C tests run
|
|
||||||
|
|
||||||
test-once: sysroot/$(TARGET)
|
|
||||||
$(MAKE) -C tests run-once TESTBIN=$(TESTBIN)
|
|
||||||
|
|
||||||
|
|
||||||
$(BUILD)/$(PROFILE)/libc.so: $(BUILD)/$(PROFILE)/ld_so.o $(BUILD)/$(PROFILE)/libc.a
|
|
||||||
# Specifying a dynamic list makes all non-local `STV_DEFAULT`
|
|
||||||
# definitions non-preemptible, except for the symbols listed in
|
|
||||||
# `dynlst.txt`. So, the symbols defined in `dynlst.txt` may be
|
|
||||||
# interposed by other definitions at runtime (either from the
|
|
||||||
# executable or some shared library).
|
|
||||||
$(CC) -nostdlib \
|
|
||||||
-shared \
|
|
||||||
-Wl,--gc-sections \
|
|
||||||
-Wl,--dynamic-list=dynlst.txt \
|
|
||||||
-Wl,-z,pack-relative-relocs \
|
|
||||||
-Wl,--sort-common \
|
|
||||||
-Wl,--whole-archive $^ -Wl,--no-whole-archive \
|
|
||||||
-Wl,-soname,libc.so.6 \
|
|
||||||
$(LINKFLAGS) \
|
|
||||||
-o $@
|
|
||||||
|
|
||||||
$(BUILD)/$(PROFILE)/libc.a: $(BUILD)/$(PROFILE)/librelibc.a $(BUILD)/openlibm/libopenlibm.a
|
|
||||||
echo "create $@" > "$@.mri"
|
|
||||||
for lib in $^; do\
|
|
||||||
echo "addlib $$lib" >> "$@.mri"; \
|
|
||||||
done
|
|
||||||
echo "save" >> "$@.mri"
|
|
||||||
echo "end" >> "$@.mri"
|
|
||||||
$(AR) -M < "$@.mri"
|
|
||||||
|
|
||||||
# Debug targets
|
|
||||||
|
|
||||||
$(BUILD)/debug/librelibc.a: $(SRC)
|
|
||||||
$(CARGO) rustc $(CARGOFLAGS) $(FEATURE_MATH) -- --emit link=$@ -g -C debug-assertions=no $(RUSTCFLAGS)
|
|
||||||
./renamesyms.sh "$@" "$(BUILD)/debug/deps/"
|
|
||||||
./stripcore.sh "$@"
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
$(BUILD)/debug/crt0.o: $(SRC)
|
|
||||||
$(CARGO) rustc --manifest-path src/crt0/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS)
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
$(BUILD)/debug/crti.o: $(SRC)
|
|
||||||
$(CARGO) rustc --manifest-path src/crti/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS)
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
$(BUILD)/debug/crtn.o: $(SRC)
|
|
||||||
$(CARGO) rustc --manifest-path src/crtn/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS)
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
$(BUILD)/debug/ld_so.o: $(SRC)
|
|
||||||
$(CARGO) rustc --manifest-path ld_so/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort -g -C debug-assertions=no $(RUSTCFLAGS)
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
# Release targets
|
|
||||||
|
|
||||||
$(BUILD)/release/librelibc.a: $(SRC)
|
|
||||||
$(CARGO) rustc --release $(CARGOFLAGS) $(FEATURE_MATH) -- --emit link=$@ $(RUSTCFLAGS)
|
|
||||||
@# TODO: Better to only allow a certain whitelisted set of symbols? Perhaps
|
|
||||||
@# use some cbindgen hook, specify them manually, or grep for #[unsafe(no_mangle)].
|
|
||||||
./renamesyms.sh "$@" "$(BUILD)/release/deps/"
|
|
||||||
./stripcore.sh "$@"
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
$(BUILD)/release/crt0.o: $(SRC)
|
|
||||||
$(CARGO) rustc --release --manifest-path src/crt0/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS)
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
$(BUILD)/release/crti.o: $(SRC)
|
|
||||||
$(CARGO) rustc --release --manifest-path src/crti/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS)
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
$(BUILD)/release/crtn.o: $(SRC)
|
|
||||||
$(CARGO) rustc --release --manifest-path src/crtn/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS)
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
$(BUILD)/release/ld_so.o: $(SRC)
|
|
||||||
$(CARGO) rustc --release --manifest-path ld_so/Cargo.toml $(CARGOFLAGS) -- --emit obj=$@ -C panic=abort $(RUSTCFLAGS)
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
# Other targets
|
|
||||||
|
|
||||||
$(BUILD)/openlibm: openlibm
|
|
||||||
rm -rf $@ $@.partial
|
|
||||||
mkdir -p $(BUILD)
|
|
||||||
cp -r $< $@.partial
|
|
||||||
mv $@.partial $@
|
|
||||||
touch $@
|
|
||||||
|
|
||||||
ifeq ($(USE_RUST_LIBM),)
|
|
||||||
$(BUILD)/openlibm/libopenlibm.a: $(BUILD)/openlibm headers $(BUILD)/$(PROFILE)/librelibc.a
|
|
||||||
$(MAKE) -s AR=$(AR) CC="$(CC_WRAPPER) $(CC)" LD=$(LD) CPPFLAGS="$(CPPFLAGS) -fno-stack-protector -I$(shell pwd)/include -I$(TARGET_HEADERS)" -C $< libopenlibm.a
|
|
||||||
./renamesyms.sh "$@" "$(BUILD)/$(PROFILE)/deps/"
|
|
||||||
else
|
|
||||||
$(BUILD)/openlibm/libopenlibm.a:
|
|
||||||
mkdir -p "$(BUILD)/openlibm"
|
|
||||||
$(AR) -rcs "$(BUILD)/openlibm/libopenlibm.a"
|
|
||||||
endif
|
|
||||||
|
|||||||
@@ -1,173 +1,53 @@
|
|||||||
# Redox C Library (relibc)
|
# RedoxFS
|
||||||
|
|
||||||
relibc is a portable C standard library written in Rust and is under heavy development, this library contains the following items:
|
This is the default filesystem of Redox OS inspired by [ZFS](https://docs.freebsd.org/en/books/handbook/zfs/) and adapted to a microkernel architecture.
|
||||||
|
|
||||||
- C, Linux, BSD functions and extensions
|
(It's a replacement for [TFS](https://gitlab.redox-os.org/redox-os/tfs))
|
||||||
- POSIX compatibility layer
|
|
||||||
- Interfaces for system components
|
|
||||||
|
|
||||||
The motivation for this project is twofold: Reduce issues that the Redox developers were having with [newlib](https://sourceware.org/newlib/), and create a more stable and safe alternative to C standard libraries written in C. It is mainly designed to be used under Redox, as an alternative to newlib, but it also supports Linux via the [sc](https://crates.io/crates/sc) crate.
|
Current features:
|
||||||
|
|
||||||
Currently Redox and Linux are supported.
|
- Compatible with Redox and Linux (FUSE)
|
||||||
|
- Copy-on-write
|
||||||
|
- Data/metadata checksums
|
||||||
|
- Transparent encryption
|
||||||
|
- Standard Unix file attributes
|
||||||
|
- File/directory size limit up to 193TiB (212TB)
|
||||||
|
- File/directory quantity limit up to 4 billion per 193TiB (2^32 - 1 = 4294967295)
|
||||||
|
- MIT licensed
|
||||||
|
- Disk encryption fully supported by the Redox bootloader, letting it load the kernel off an encrypted partition.
|
||||||
|
|
||||||
## `redox-rt`
|
Being MIT licensed, RedoxFS can be bundled on GPL-licensed operating systems (Linux, for example).
|
||||||
|
|
||||||
`redox-rt` is a runtime library that provides much of the code that enables POSIX on Redox, like `fork`, `exec`, signal handling, etc.
|
### Install RedoxFS
|
||||||
Relibc uses it as backend in `src/platform/redox`, and it's intended to eventually be usable independently, without relibc.
|
|
||||||
|
|
||||||
## Repository Layout
|
|
||||||
|
|
||||||
- `include` - Header files (mostly macros and variadic functions `cbindgen` can't generate)
|
|
||||||
- `src` - Source files
|
|
||||||
- `src/c` - C code
|
|
||||||
- `src/crt0` - Runtime code
|
|
||||||
- `src/crti` - Runtime code
|
|
||||||
- `src/crtn` - Runtime code
|
|
||||||
- `src/header` - Header files implementation
|
|
||||||
- `src/header/*` - Each folder has a `cbindgen.toml` file, it generates a C-to-Rust interface and header files
|
|
||||||
- `src/ld_so` - Dynamic loader code
|
|
||||||
- `src/platform` - Platform-specific and common code
|
|
||||||
- `src/platform/redox` - Redox-specific code
|
|
||||||
- `src/platform/linux` - Linux-specific code
|
|
||||||
- `src/pthread` - pthread implementation
|
|
||||||
- `src/sync` - Synchronization primitives
|
|
||||||
- `tests` - C tests (each MR needs to give success in all of them)
|
|
||||||
|
|
||||||
## Download the sources
|
|
||||||
|
|
||||||
To download the relibc sources run the following command:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone --recursive https://gitlab.redox-os.org/redox-os/relibc
|
cargo install redoxfs
|
||||||
```
|
```
|
||||||
|
|
||||||
## Build Instructions
|
You can also build RedoxFS from this repository.
|
||||||
|
|
||||||
To build relibc out of the Redox build system, do the following steps:
|
### Configure your storage device to allow rootless usage
|
||||||
|
|
||||||
### Dependencies
|
If you are on Linux you need root permission to acess block devices (storage), but it's recommended to run RedoxFS as rootless.
|
||||||
|
|
||||||
- Install `cbindgen`
|
To do that you need to configure your storage device permission to your user with the following command:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cargo install cbindgen
|
sudo setfacl -m u:your-username:rw /path/to/disk
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Install the `expect` tool
|
### Create, mount and customize your RedoxFS partition
|
||||||
|
|
||||||
- Debian, Ubuntu and PopOS:
|
See [the instructions in the book](https://doc.redox-os.org/book/redoxfs.html) for RedoxFS tooling usage.
|
||||||
|
|
||||||
```sh
|
Currently RedoxFS tooling are:
|
||||||
sudo apt install expect
|
|
||||||
```
|
|
||||||
|
|
||||||
- Fedora:
|
- `redoxfs` mount a RedoxFS disk
|
||||||
|
- `redoxfs-ar` write files to a RedoxFS disk
|
||||||
|
- `redoxfs-clone` clone a RedoxFS disk
|
||||||
|
- `redoxfs-mkfs` create an empty RedoxFS disk
|
||||||
|
- `redoxfs-resize` resize a RedoxFS disk
|
||||||
|
|
||||||
```sh
|
[](./LICENSE)
|
||||||
sudo dnf install expect
|
[](https://crates.io/crates/redoxfs)
|
||||||
```
|
[](https://docs.rs/redoxfs)
|
||||||
|
|
||||||
- Arch Linux:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
sudo pacman -S expect
|
|
||||||
```
|
|
||||||
|
|
||||||
### Build Relibc
|
|
||||||
|
|
||||||
To build the relibc library objects, run the following command:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
make all
|
|
||||||
```
|
|
||||||
|
|
||||||
- Clean old library objects and tests
|
|
||||||
|
|
||||||
```sh
|
|
||||||
make clean
|
|
||||||
```
|
|
||||||
|
|
||||||
## Build relibc inside the Redox build system
|
|
||||||
|
|
||||||
Inside of your Redox build system, run:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
make prefix
|
|
||||||
```
|
|
||||||
|
|
||||||
If you need to rebuild `relibc` for testing a Cookbook recipe, run:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
touch relibc
|
|
||||||
make prefix r.recipe-name
|
|
||||||
```
|
|
||||||
|
|
||||||
Touching (changing the "last modified time" of) the `relibc` folder is needed to trigger recompilation for `make prefix`. Replace `recipe-name` with your desired recipe name.
|
|
||||||
|
|
||||||
Note: Do not edit `relibc` inside `prefix` folder! Do your work on `relibc` folder directly inside your Redox build system instead.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
Relibc has a test suite that also runs every time a new commit get pushed. You can see `.gitlab-ci.yml` to see how it's being executed. That being said, `./check.sh` is the recommended way to run tests. Here's few examples:
|
|
||||||
|
|
||||||
+ `./check.sh` - Run build, without running the test
|
|
||||||
+ `./check.sh --test` - Run all tests in x86_64 Redox using Redoxer
|
|
||||||
+ `./check.sh --test --host` - Run all tests in host (Linux)
|
|
||||||
+ `./check.sh --test --arch=aarch64` - Run all tests in specified arch
|
|
||||||
- Arch can be `x86_64`, `aarch64`, `i586`, or `riscv64gc`
|
|
||||||
+ `./check.sh --test=stdio/printf` - Run a single test
|
|
||||||
- Can be combined with `--host` or `--arch`
|
|
||||||
- Will run statically linked test in Linux, dynamically linked in Redox
|
|
||||||
|
|
||||||
Couple of notes:
|
|
||||||
|
|
||||||
- Relibc and its tests will rebuild if files are changed, however switching between arch or host requires you to run `make clean`
|
|
||||||
- Redoxer is needed to run tests for Redox without `--host`. You can install it using `cargo install redoxer`
|
|
||||||
- Tests can hang, the test runner can anticipate this, assuming the kernel doesn't hang too.
|
|
||||||
|
|
||||||
## Issues
|
|
||||||
|
|
||||||
#### I'm building for my own platform which I run, and am getting `x86_64-linux-gnu-ar: command not found` (or similar)
|
|
||||||
|
|
||||||
The Makefile expects GNU compiler tools prefixed with the platform specifier, as would be present when you installed a cross compiler. Since you are building for your own platform, some Linux distributions (like Manjaro) don't install/symlink the prefixed executables.
|
|
||||||
|
|
||||||
An easy fix would be to replace the corresponding lines in `config.mk`, e.g.
|
|
||||||
|
|
||||||
```diff
|
|
||||||
ifeq ($(TARGET),x86_64-unknown-linux-gnu)
|
|
||||||
- export CC=x86_64-linux-gnu-gcc
|
|
||||||
- export LD=x86_64-linux-gnu-ld
|
|
||||||
- export AR=x86_64-linux-gnu-ar
|
|
||||||
- export NM=x86_64-linux-gnu-nm
|
|
||||||
+ export CC=gcc
|
|
||||||
+ export LD=ld
|
|
||||||
+ export AR=ar
|
|
||||||
+ export NM=nm
|
|
||||||
export OBJCOPY=objcopy
|
|
||||||
export CPPFLAGS=
|
|
||||||
LD_SO_PATH=lib/ld64.so.1
|
|
||||||
endif
|
|
||||||
```
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
Before starting to contribute, read [this](CONTRIBUTING.md) document.
|
|
||||||
|
|
||||||
## Supported OSes
|
|
||||||
|
|
||||||
- Redox OS
|
|
||||||
- Linux
|
|
||||||
|
|
||||||
## Supported architectures
|
|
||||||
|
|
||||||
- i586 (Intel/AMD)
|
|
||||||
- x86_64 (Intel/AMD)
|
|
||||||
- aarch64 (ARM64)
|
|
||||||
- riscv64gc (RISC-V)
|
|
||||||
|
|
||||||
## Funding - _Unix-style Signals and Process Management_
|
|
||||||
|
|
||||||
This project is funded through [NGI Zero Core](https://nlnet.nl/core), a fund established by [NLnet](https://nlnet.nl) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu) program. Learn more at the [NLnet project page](https://nlnet.nl/project/RedoxOS-Signals).
|
|
||||||
|
|
||||||
[<img src="https://nlnet.nl/logo/banner.png" alt="NLnet foundation logo" width="20%" />](https://nlnet.nl)
|
|
||||||
[<img src="https://nlnet.nl/image/logos/NGI0_tag.svg" alt="NGI Zero Logo" width="20%" />](https://nlnet.nl/core)
|
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
extern crate cc;
|
|
||||||
|
|
||||||
use std::{env, fs};
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let _crate_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set");
|
|
||||||
let target = env::var("TARGET").unwrap();
|
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=src/c");
|
|
||||||
|
|
||||||
let mut cc_builder = &mut cc::Build::new();
|
|
||||||
|
|
||||||
cc_builder = cc_builder.flag("-nostdinc").flag("-nostdlib");
|
|
||||||
|
|
||||||
if target.starts_with("aarch64") {
|
|
||||||
cc_builder = cc_builder.flag("-mno-outline-atomics")
|
|
||||||
}
|
|
||||||
|
|
||||||
cc_builder
|
|
||||||
.flag("-fno-stack-protector")
|
|
||||||
.flag("-Wno-expansion-to-defined")
|
|
||||||
.files(
|
|
||||||
fs::read_dir("src/c")
|
|
||||||
.expect("src/c directory missing")
|
|
||||||
.map(|res| res.expect("read_dir error").path()),
|
|
||||||
)
|
|
||||||
.compile("relibc_c");
|
|
||||||
|
|
||||||
println!("cargo:rustc-link-lib=static=relibc_c");
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
|
|
||||||
# needs a leading newline
|
|
||||||
[defines]
|
|
||||||
"target_os=redox" = "__redox__"
|
|
||||||
"target_os=linux" = "__linux__"
|
|
||||||
"target_pointer_width=64" = "__LP64__"
|
|
||||||
"target_pointer_width=32" = "__ILP32__"
|
|
||||||
"target_arch=x86" = "__i386__"
|
|
||||||
"target_arch=x86_64" = "__x86_64__"
|
|
||||||
"target_arch=aarch64" = "__aarch64__"
|
|
||||||
# This is not exact. It should be `defined(__riscv) && defined(__LP64__)`, or `defined(__riscv) && __riscv_xlen==64`
|
|
||||||
# This will do however, as long as we only support riscv64 and not riscv32
|
|
||||||
"target_arch=riscv64" = "__riscv"
|
|
||||||
|
|
||||||
# XXX: silences a warning
|
|
||||||
"feature = no_std" = "__relibc__"
|
|
||||||
"feature = ip6" = "__ip6__"
|
|
||||||
|
|
||||||
# Ensure attributes are passed down from Rust
|
|
||||||
# <features.h> must be included where attributes are used in relibc
|
|
||||||
[fn]
|
|
||||||
must_use = "__nodiscard"
|
|
||||||
deprecated = "__deprecated"
|
|
||||||
deprecated_with_note = "__deprecatedNote({})"
|
|
||||||
no_return = "__noreturn"
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
RED='\033[1;38;5;196m'
|
|
||||||
GREEN='\033[1;38;5;46m'
|
|
||||||
NC='\033[0m'
|
|
||||||
|
|
||||||
show_help() {
|
|
||||||
echo "Usage: $(basename "$0") [OPTIONS]"
|
|
||||||
echo ""
|
|
||||||
echo "Description:"
|
|
||||||
echo " Wrapper for Makefile / Cargo to run checks or tests on Redox OS targets."
|
|
||||||
echo ""
|
|
||||||
echo "Options:"
|
|
||||||
echo " --test Run 'make test' instead of 'make all'"
|
|
||||||
echo " --test= Run single 'make test'"
|
|
||||||
echo " --cargo Run 'cargo check' / 'cargo test' instead"
|
|
||||||
echo " (note: cargo test is currently not maintained for relibc)"
|
|
||||||
echo " --host Run the command on host (linux) target"
|
|
||||||
echo " --all-target Run the command on all supported Redox architectures"
|
|
||||||
echo " --target=<target> Override the target architecture (e.g., i586-unknown-redox)"
|
|
||||||
echo " --arch=<arch> Override the target architecture using arch (e.g., i586)"
|
|
||||||
echo " --help Show this help message"
|
|
||||||
echo ""
|
|
||||||
echo "Supported Targets:"
|
|
||||||
for t in "${SUPPORTED_TARGETS[@]}"; do
|
|
||||||
echo " - $t"
|
|
||||||
done
|
|
||||||
echo " - $(uname -m)-unknown-linux-gnu"
|
|
||||||
echo ""
|
|
||||||
echo "Environment:"
|
|
||||||
echo " TARGET Sets the default target (overridden by --target)"
|
|
||||||
}
|
|
||||||
|
|
||||||
if ! command -v cbindgen &> /dev/null; then
|
|
||||||
echo "Error: 'cbindgen' CLI not found."
|
|
||||||
echo "Please install it: cargo install cbindgen"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
SUPPORTED_TARGETS=(
|
|
||||||
"x86_64-unknown-redox"
|
|
||||||
"i586-unknown-redox"
|
|
||||||
"aarch64-unknown-redox"
|
|
||||||
"riscv64gc-unknown-redox"
|
|
||||||
)
|
|
||||||
|
|
||||||
CURRENT_TARGET="${TARGET:-x86_64-unknown-redox}"
|
|
||||||
CHECK_ALL=false
|
|
||||||
CMD_ACTION="make"
|
|
||||||
CARGO_ACTION="check"
|
|
||||||
MAKE_ACTION="all"
|
|
||||||
TEST_BIN=""
|
|
||||||
IS_HOST=0
|
|
||||||
|
|
||||||
while [[ $# -gt 0 ]]; do
|
|
||||||
case "$1" in
|
|
||||||
--all-target)
|
|
||||||
CHECK_ALL=true
|
|
||||||
;;
|
|
||||||
--test)
|
|
||||||
MAKE_ACTION="test"
|
|
||||||
CARGO_ACTION="test"
|
|
||||||
;;
|
|
||||||
--test=*)
|
|
||||||
TEST_BIN="${1#*=}"
|
|
||||||
MAKE_ACTION="test-once"
|
|
||||||
;;
|
|
||||||
--cargo)
|
|
||||||
CMD_ACTION="cargo"
|
|
||||||
;;
|
|
||||||
--clippy)
|
|
||||||
CMD_ACTION="cargo"
|
|
||||||
CARGO_ACTION="clippy"
|
|
||||||
;;
|
|
||||||
--host)
|
|
||||||
CURRENT_TARGET="$(uname -m)-unknown-linux-gnu"
|
|
||||||
IS_HOST=1
|
|
||||||
;;
|
|
||||||
--target=*)
|
|
||||||
CURRENT_TARGET="${1#*=}"
|
|
||||||
;;
|
|
||||||
--arch=*)
|
|
||||||
CURRENT_TARGET="${1#*=}-unknown-redox"
|
|
||||||
;;
|
|
||||||
--help)
|
|
||||||
show_help
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo -e "${RED}Error: Unknown option '$1'${NC}"
|
|
||||||
show_help
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ "$IS_HOST" -eq 0 ]; then
|
|
||||||
if ! command -v redoxer &> /dev/null; then
|
|
||||||
echo "Error: 'redoxer' CLI not found."
|
|
||||||
echo "Please install it: cargo install redoxer"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
run_redoxer() {
|
|
||||||
export TARGET=$1
|
|
||||||
REDOXER_ENV="redoxer env"
|
|
||||||
if [ "$IS_HOST" -eq 0 ]; then
|
|
||||||
redoxer toolchain || { echo -e "${RED}Fail: redoxer toolchain for: $target.${NC}" && exit 1; }
|
|
||||||
export CARGO_TEST="redoxer"
|
|
||||||
export TEST_RUNNER="redoxer exec --folder ../../sysroot/$TARGET/:/usr --folder . --"
|
|
||||||
|
|
||||||
MAKE_ACTION="$MAKE_ACTION IS_REDOX=1"
|
|
||||||
else
|
|
||||||
REDOXER_ENV=""
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$TEST_BIN" != "" ]; then
|
|
||||||
if [ "$IS_HOST" -eq 0 ]; then
|
|
||||||
MAKE_ACTION="$MAKE_ACTION TESTBIN=bins_dynamic/$TEST_BIN"
|
|
||||||
else
|
|
||||||
MAKE_ACTION="$MAKE_ACTION TESTBIN=bins_static/$TEST_BIN"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$CMD_ACTION" == "make" ]; then
|
|
||||||
CMD_OPT="-j $(nproc) $MAKE_ACTION"
|
|
||||||
else
|
|
||||||
CMD_OPT="$CARGO_ACTION --target=$TARGET"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "----------------------------------------"
|
|
||||||
echo "Running $REDOXER_ENV $CMD_ACTION $CMD_OPT for: $TARGET"
|
|
||||||
|
|
||||||
if $REDOXER_ENV $CMD_ACTION $CMD_OPT; then
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
echo -e "${RED}Fail: $CMD_ACTION $CMD_OPT for $TARGET failed.${NC}"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
if [ "$CHECK_ALL" = true ]; then
|
|
||||||
echo "Running $CMD_ACTION for all supported Redox targets..."
|
|
||||||
|
|
||||||
has_error=false
|
|
||||||
|
|
||||||
for target in "${SUPPORTED_TARGETS[@]}"; do
|
|
||||||
if ! run_redoxer "$target"; then
|
|
||||||
has_error=true
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "----------------------------------------"
|
|
||||||
if [ "$has_error" = true ]; then
|
|
||||||
echo -e "${RED}Summary: One or more targets failed.${NC}"
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo -e "${GREEN}Summary: All targets passed!${NC}"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
if run_redoxer "$CURRENT_TARGET"; then
|
|
||||||
echo -e "${GREEN}Success: $CARGO_ACTION for $CURRENT_TARGET passed.${NC}"
|
|
||||||
exit 0
|
|
||||||
else
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
ifndef TARGET
|
|
||||||
export TARGET:=$(shell rustc -Z unstable-options --print target-spec-json | grep llvm-target | cut -d '"' -f4)
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(TARGET),aarch64-unknown-linux-gnu)
|
|
||||||
export CC=aarch64-linux-gnu-gcc
|
|
||||||
export LD=aarch64-linux-gnu-ld
|
|
||||||
export AR=aarch64-linux-gnu-ar
|
|
||||||
export NM=aarch64-linux-gnu-nm
|
|
||||||
export OBJCOPY=aarch64-linux-gnu-objcopy
|
|
||||||
export CPPFLAGS=
|
|
||||||
LD_SONAME=ld.so.1
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(TARGET),aarch64-unknown-linux-relibc)
|
|
||||||
export CC=aarch64-linux-relibc-gcc
|
|
||||||
export LD=aarch64-linux-relibc-ld
|
|
||||||
export AR=aarch64-linux-relibc-ar
|
|
||||||
export NM=aarch64-linux-relibc-nm
|
|
||||||
export OBJCOPY=aarch64-linux-relibc-objcopy
|
|
||||||
export CPPFLAGS=
|
|
||||||
LD_SONAME=ld.so.1
|
|
||||||
endif
|
|
||||||
|
|
||||||
|
|
||||||
ifeq ($(TARGET),aarch64-unknown-redox)
|
|
||||||
export CC=aarch64-unknown-redox-gcc
|
|
||||||
export LD=aarch64-unknown-redox-ld
|
|
||||||
export AR=aarch64-unknown-redox-ar
|
|
||||||
export NM=aarch64-unknown-redox-nm
|
|
||||||
export OBJCOPY=aarch64-unknown-redox-objcopy
|
|
||||||
export CPPFLAGS=
|
|
||||||
LD_SONAME=ld.so.1
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(TARGET),i586-unknown-redox)
|
|
||||||
export CC=i586-unknown-redox-gcc
|
|
||||||
export LD=i586-unknown-redox-ld
|
|
||||||
export AR=i586-unknown-redox-ar
|
|
||||||
export NM=i586-unknown-redox-nm
|
|
||||||
export OBJCOPY=i586-unknown-redox-objcopy
|
|
||||||
export CPPFLAGS=
|
|
||||||
LD_SONAME=ld.so.1
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(TARGET),i686-unknown-redox)
|
|
||||||
export CC=i686-unknown-redox-gcc
|
|
||||||
export LD=i686-unknown-redox-ld
|
|
||||||
export AR=i686-unknown-redox-ar
|
|
||||||
export NM=i686-unknown-redox-nm
|
|
||||||
export OBJCOPY=i686-unknown-redox-objcopy
|
|
||||||
export CPPFLAGS=
|
|
||||||
LD_SONAME=ld.so.1
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(TARGET),x86_64-unknown-linux-gnu)
|
|
||||||
export CC=x86_64-linux-gnu-gcc
|
|
||||||
export LD=x86_64-linux-gnu-ld
|
|
||||||
export AR=x86_64-linux-gnu-ar
|
|
||||||
export NM=x86_64-linux-gnu-nm
|
|
||||||
export OBJCOPY=objcopy
|
|
||||||
export CPPFLAGS=
|
|
||||||
LD_SONAME=ld64.so.1
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(TARGET),x86_64-unknown-linux-relibc)
|
|
||||||
export CC=x86_64-linux-relibc-gcc
|
|
||||||
export LD=x86_64-linux-relibc-ld
|
|
||||||
export AR=x86_64-linux-relibc-ar
|
|
||||||
export NM=x86_64-linux-relibc-nm
|
|
||||||
export OBJCOPY=x86_64-linux-relibc-objcopy
|
|
||||||
export CPPFLAGS=
|
|
||||||
LD_SONAME=ld64.so.1
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(TARGET),x86_64-unknown-redox)
|
|
||||||
export CC=x86_64-unknown-redox-gcc
|
|
||||||
export LD=x86_64-unknown-redox-ld
|
|
||||||
export AR=x86_64-unknown-redox-ar
|
|
||||||
export NM=x86_64-unknown-redox-nm
|
|
||||||
export OBJCOPY=x86_64-unknown-redox-objcopy
|
|
||||||
export CPPFLAGS=
|
|
||||||
LD_SONAME=ld64.so.1
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(TARGET),riscv64gc-unknown-redox)
|
|
||||||
export CC=riscv64-unknown-redox-gcc
|
|
||||||
export LD=riscv64-unknown-redox-ld
|
|
||||||
export AR=riscv64-unknown-redox-ar
|
|
||||||
export NM=riscv64-unknown-redox-nm
|
|
||||||
export OBJCOPY=riscv64-unknown-redox-objcopy
|
|
||||||
export CPPFLAGS=-march=rv64gc -mabi=lp64d
|
|
||||||
LD_SONAME=ld.so.1
|
|
||||||
endif
|
|
||||||
-1
Submodule dlmalloc-rs deleted from 3b0ee3b467
-27
@@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
environ;
|
|
||||||
|
|
||||||
stdin;
|
|
||||||
stdout;
|
|
||||||
stderr;
|
|
||||||
|
|
||||||
timezone;
|
|
||||||
daylight;
|
|
||||||
tzname;
|
|
||||||
__timezone;
|
|
||||||
__daylight;
|
|
||||||
__tzname;
|
|
||||||
|
|
||||||
signgam;
|
|
||||||
__signgam;
|
|
||||||
|
|
||||||
optarg;
|
|
||||||
optind;
|
|
||||||
opterr;
|
|
||||||
optreset;
|
|
||||||
__optreset;
|
|
||||||
|
|
||||||
getdate_err;
|
|
||||||
|
|
||||||
__stack_chk_guard;
|
|
||||||
};
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
cargo fmt --package relibc --package crt0 --package redox-rt "$@"
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
target
|
||||||
|
corpus
|
||||||
|
artifacts
|
||||||
|
coverage
|
||||||
Generated
+858
@@ -0,0 +1,858 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aes"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"cipher",
|
||||||
|
"cpufeatures",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstream"
|
||||||
|
version = "0.6.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"anstyle-parse",
|
||||||
|
"anstyle-query",
|
||||||
|
"anstyle-wincon",
|
||||||
|
"colorchoice",
|
||||||
|
"is_terminal_polyfill",
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-parse"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||||
|
dependencies = [
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-query"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-wincon"
|
||||||
|
version = "3.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"once_cell",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.86"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arbitrary"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
|
||||||
|
dependencies = [
|
||||||
|
"derive_arbitrary",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "argon2"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "db4ce4441f99dbd377ca8a8f57b698c44d0d6e712d8329b5040da5a64aa1ce73"
|
||||||
|
dependencies = [
|
||||||
|
"base64ct",
|
||||||
|
"blake2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64ct"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blake2"
|
||||||
|
version = "0.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
||||||
|
dependencies = [
|
||||||
|
"digest",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.10.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.99"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "96c51067fd44124faa7f870b4b1c969379ad32b2ba805aa959430ceaa384f695"
|
||||||
|
dependencies = [
|
||||||
|
"jobserver",
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg_aliases"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cipher"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-common",
|
||||||
|
"inout",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpufeatures"
|
||||||
|
version = "0.2.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-common"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_arbitrary"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.10.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"crypto-common",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "endian-num"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1ad847bb2094f110bbdd6fa564894ca4556fd978958e93985420d680d3cb6d14"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_filter"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "env_logger"
|
||||||
|
version = "0.11.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"env_filter",
|
||||||
|
"jiff",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fastrand"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fuser"
|
||||||
|
version = "0.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0bb29a3ae32279fe3e79a958fe01899f5fb23eadccee919cf88e145b54ed9367"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"memchr",
|
||||||
|
"nix",
|
||||||
|
"page_size",
|
||||||
|
"smallvec",
|
||||||
|
"zerocopy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "humansize"
|
||||||
|
version = "2.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7"
|
||||||
|
dependencies = [
|
||||||
|
"libm",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inout"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is_terminal_polyfill"
|
||||||
|
version = "1.70.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jiff"
|
||||||
|
version = "0.2.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5ad87c89110f55e4cd4dc2893a9790820206729eaf221555f742d540b0724a0"
|
||||||
|
dependencies = [
|
||||||
|
"jiff-static",
|
||||||
|
"log",
|
||||||
|
"portable-atomic",
|
||||||
|
"portable-atomic-util",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jiff-static"
|
||||||
|
version = "0.2.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d076d5b64a7e2fe6f0743f02c43ca4a6725c0f904203bfe276a5b3e793103605"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jobserver"
|
||||||
|
version = "0.1.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.155"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libfuzzer-sys"
|
||||||
|
version = "0.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7"
|
||||||
|
dependencies = [
|
||||||
|
"arbitrary",
|
||||||
|
"cc",
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libm"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libredox"
|
||||||
|
version = "0.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall 0.7.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lz4_flex"
|
||||||
|
version = "0.11.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08ab2867e3eeeca90e844d1940eab391c9dc5228783db2ed999acbc0a9ed375a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.29.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cfg-if",
|
||||||
|
"cfg_aliases",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "numtoa"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6aa2c4e539b869820a2b82e1aef6ff40aa85e65decdd5185e83fb4b1249cd00f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.21.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "page_size"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parse-size"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "487f2ccd1e17ce8c1bfab3a65c89525af41cfad4c8659021a1e9a2aacd73b89b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "portable-atomic"
|
||||||
|
version = "1.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "portable-atomic-util"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
|
||||||
|
dependencies = [
|
||||||
|
"portable-atomic",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.95"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "range-tree"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "384c2842d4e069d5ccacf5fe1dca4ef8d07a5444329715f0fc3c61813502d4d1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox-path"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox-scheme"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2cea2668a5932f878a4298a1d7f8950249bbbb77120fb263da252c589152f5ea"
|
||||||
|
dependencies = [
|
||||||
|
"libredox",
|
||||||
|
"redox_syscall 0.6.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec96166dafa0886eb81fe1c0a388bece180fbef2135f97c1e2cf8302e74b43b5"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_termios"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redoxfs"
|
||||||
|
version = "0.8.4"
|
||||||
|
dependencies = [
|
||||||
|
"aes",
|
||||||
|
"argon2",
|
||||||
|
"base64ct",
|
||||||
|
"bitflags",
|
||||||
|
"endian-num",
|
||||||
|
"env_logger",
|
||||||
|
"fuser",
|
||||||
|
"getrandom",
|
||||||
|
"humansize",
|
||||||
|
"libc",
|
||||||
|
"libredox",
|
||||||
|
"log",
|
||||||
|
"lz4_flex",
|
||||||
|
"parse-size",
|
||||||
|
"range-tree",
|
||||||
|
"redox-path",
|
||||||
|
"redox-scheme",
|
||||||
|
"redox_syscall 0.6.0",
|
||||||
|
"seahash",
|
||||||
|
"termion",
|
||||||
|
"uuid",
|
||||||
|
"xts-mode",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redoxfs-fuzz"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"arbitrary",
|
||||||
|
"fuser",
|
||||||
|
"libfuzzer-sys",
|
||||||
|
"nix",
|
||||||
|
"redoxfs",
|
||||||
|
"tempfile",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.10.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "0.38.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "seahash"
|
||||||
|
version = "4.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.219"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.219"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.100"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempfile"
|
||||||
|
version = "3.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"fastrand",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termion"
|
||||||
|
version = "4.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3669a69de26799d6321a5aa713f55f7e2cd37bd47be044b50f2acafc42c122bb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"libredox",
|
||||||
|
"numtoa",
|
||||||
|
"redox_termios",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "1.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.59.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_gnullvm",
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu",
|
||||||
|
"windows_i686_gnullvm",
|
||||||
|
"windows_i686_msvc",
|
||||||
|
"windows_x86_64_gnu",
|
||||||
|
"windows_x86_64_gnullvm",
|
||||||
|
"windows_x86_64_msvc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_i686_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnu"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_gnullvm"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_x86_64_msvc"
|
||||||
|
version = "0.52.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xts-mode"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09cbddb7545ca0b9ffa7bdc653e8743303e1712687a6918ced25f2cdbed42520"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"cipher",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy"
|
||||||
|
version = "0.8.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3"
|
||||||
|
dependencies = [
|
||||||
|
"zerocopy-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerocopy-derive"
|
||||||
|
version = "0.8.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
[package]
|
||||||
|
name = "redoxfs-fuzz"
|
||||||
|
version = "0.0.0"
|
||||||
|
publish = false
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
log = []
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
cargo-fuzz = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.86"
|
||||||
|
arbitrary = { version = "1.3.2", features = ["derive"] }
|
||||||
|
fuser = { version = "0.16" }
|
||||||
|
libfuzzer-sys = "0.4"
|
||||||
|
nix = { version = "0.29.0", features = ["fs"] }
|
||||||
|
tempfile = "3.10.1"
|
||||||
|
|
||||||
|
[dependencies.redoxfs]
|
||||||
|
path = ".."
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "fuse_fuzz_target"
|
||||||
|
path = "fuzz_targets/fuse_fuzz_target.rs"
|
||||||
|
test = false
|
||||||
|
doc = false
|
||||||
|
bench = false
|
||||||
@@ -0,0 +1,338 @@
|
|||||||
|
//! Fuzzer that exercises random file system operations against a FUSE-mounted redoxfs.
|
||||||
|
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use anyhow::{ensure, Result};
|
||||||
|
use fuser;
|
||||||
|
use libfuzzer_sys::{arbitrary::Arbitrary, fuzz_target, Corpus};
|
||||||
|
use nix::sys::statvfs::statvfs;
|
||||||
|
use std::{
|
||||||
|
fs::{self, File, FileTimes, OpenOptions},
|
||||||
|
io::{Read, Seek, SeekFrom, Write},
|
||||||
|
os::unix::fs::{self as unix_fs, PermissionsExt},
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
thread,
|
||||||
|
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||||
|
};
|
||||||
|
use tempfile;
|
||||||
|
|
||||||
|
use redoxfs::{mount::fuse::Fuse, DiskSparse, FileSystem};
|
||||||
|
|
||||||
|
/// Maximum size for files and buffers. Chosen arbitrarily with fuzzing performance in mind.
|
||||||
|
const MAX_SIZE: u64 = 10_000_000;
|
||||||
|
/// Limit on the number of remounts in a single test case. Chosen arbitrarily with fuzzing
|
||||||
|
/// performance in mind: remounts are costly.
|
||||||
|
const MAX_MOUNT_SEQUENCES: usize = 3;
|
||||||
|
|
||||||
|
/// An operation to be performed by the fuzzer.
|
||||||
|
#[derive(Arbitrary, Clone, Debug)]
|
||||||
|
enum Operation {
|
||||||
|
Chown {
|
||||||
|
path: PathBuf,
|
||||||
|
uid: Option<u32>,
|
||||||
|
gid: Option<u32>,
|
||||||
|
},
|
||||||
|
CreateDir {
|
||||||
|
path: PathBuf,
|
||||||
|
},
|
||||||
|
HardLink {
|
||||||
|
original: PathBuf,
|
||||||
|
link: PathBuf,
|
||||||
|
},
|
||||||
|
Metadata {
|
||||||
|
path: PathBuf,
|
||||||
|
},
|
||||||
|
Read {
|
||||||
|
path: PathBuf,
|
||||||
|
},
|
||||||
|
ReadDir {
|
||||||
|
path: PathBuf,
|
||||||
|
},
|
||||||
|
ReadLink {
|
||||||
|
path: PathBuf,
|
||||||
|
},
|
||||||
|
RemoveDir {
|
||||||
|
path: PathBuf,
|
||||||
|
},
|
||||||
|
RemoveFile {
|
||||||
|
path: PathBuf,
|
||||||
|
},
|
||||||
|
Rename {
|
||||||
|
from: PathBuf,
|
||||||
|
to: PathBuf,
|
||||||
|
},
|
||||||
|
SeekRead {
|
||||||
|
path: PathBuf,
|
||||||
|
seek_pos: u64,
|
||||||
|
buf_size: usize,
|
||||||
|
},
|
||||||
|
SeekWrite {
|
||||||
|
path: PathBuf,
|
||||||
|
seek_pos: u64,
|
||||||
|
buf_size: usize,
|
||||||
|
},
|
||||||
|
SetLen {
|
||||||
|
path: PathBuf,
|
||||||
|
size: u64,
|
||||||
|
},
|
||||||
|
SetPermissions {
|
||||||
|
path: PathBuf,
|
||||||
|
readonly: Option<bool>,
|
||||||
|
mode: Option<u32>,
|
||||||
|
},
|
||||||
|
SetTimes {
|
||||||
|
path: PathBuf,
|
||||||
|
accessed_since_epoch: Option<Duration>,
|
||||||
|
modified_since_epoch: Option<Duration>,
|
||||||
|
},
|
||||||
|
Statvfs {},
|
||||||
|
SymLink {
|
||||||
|
original: PathBuf,
|
||||||
|
link: PathBuf,
|
||||||
|
},
|
||||||
|
Write {
|
||||||
|
path: PathBuf,
|
||||||
|
buf_size: usize,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parameters for mounting the file system and operations to be performed afterwards.
|
||||||
|
#[derive(Arbitrary, Clone, Debug)]
|
||||||
|
struct MountSequence {
|
||||||
|
cleanup: bool,
|
||||||
|
operations: Vec<Operation>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The whole input to a single fuzzer invocation.
|
||||||
|
#[derive(Arbitrary, Clone, Debug)]
|
||||||
|
struct TestCase {
|
||||||
|
disk_size: u64,
|
||||||
|
reserved_size: u64,
|
||||||
|
mount_sequences: Vec<MountSequence>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates the disk for backing the Redoxfs.
|
||||||
|
fn create_disk(temp_path: &Path, disk_size: u64) -> DiskSparse {
|
||||||
|
let disk_path = temp_path.join("disk.img");
|
||||||
|
DiskSparse::create(disk_path, disk_size).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an empty Redoxfs.
|
||||||
|
fn create_redoxfs(disk: DiskSparse, reserved_size: u64) -> bool {
|
||||||
|
let password = None;
|
||||||
|
let reserved = vec![0; reserved_size as usize];
|
||||||
|
let ctime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
|
||||||
|
FileSystem::create_reserved(
|
||||||
|
disk,
|
||||||
|
password,
|
||||||
|
&reserved,
|
||||||
|
ctime.as_secs(),
|
||||||
|
ctime.subsec_nanos(),
|
||||||
|
)
|
||||||
|
.is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mounts an existing Redoxfs, runs the callback and performs the unmount.
|
||||||
|
fn with_redoxfs_mount<F>(temp_path: &Path, disk: DiskSparse, cleanup: bool, callback: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(&Path) + Send + 'static,
|
||||||
|
{
|
||||||
|
let password = None;
|
||||||
|
let block = None;
|
||||||
|
let mut fs = FileSystem::open(disk, password, block, cleanup).unwrap();
|
||||||
|
|
||||||
|
let mount_path = temp_path.join("mount");
|
||||||
|
fs::create_dir_all(&mount_path).unwrap();
|
||||||
|
let mut session = fuser::Session::new(Fuse { fs: &mut fs }, &mount_path, &[]).unwrap();
|
||||||
|
let mut unmounter = session.unmount_callable();
|
||||||
|
|
||||||
|
let join_handle = thread::spawn(move || {
|
||||||
|
callback(&mount_path);
|
||||||
|
unmounter.unmount().unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
session.run().unwrap();
|
||||||
|
join_handle.join().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_path_within_fs(fs_path: &Path, path_to_add: &Path) -> Result<PathBuf> {
|
||||||
|
ensure!(path_to_add.is_relative());
|
||||||
|
ensure!(path_to_add
|
||||||
|
.components()
|
||||||
|
.all(|c| c != std::path::Component::ParentDir));
|
||||||
|
Ok(fs_path.join(path_to_add))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_operation(fs_path: &Path, op: &Operation) -> Result<()> {
|
||||||
|
match op {
|
||||||
|
Operation::Chown { path, uid, gid } => {
|
||||||
|
let path = get_path_within_fs(fs_path, path)?;
|
||||||
|
unix_fs::chown(path, *uid, *gid)?;
|
||||||
|
}
|
||||||
|
Operation::CreateDir { path } => {
|
||||||
|
let path = get_path_within_fs(fs_path, path)?;
|
||||||
|
fs::create_dir(path)?;
|
||||||
|
}
|
||||||
|
Operation::HardLink { original, link } => {
|
||||||
|
let original = get_path_within_fs(fs_path, original)?;
|
||||||
|
let link = get_path_within_fs(fs_path, link)?;
|
||||||
|
fs::hard_link(original, link)?;
|
||||||
|
}
|
||||||
|
Operation::Metadata { path } => {
|
||||||
|
let path = get_path_within_fs(fs_path, path)?;
|
||||||
|
fs::metadata(path)?;
|
||||||
|
}
|
||||||
|
Operation::Read { path } => {
|
||||||
|
let path = get_path_within_fs(fs_path, path)?;
|
||||||
|
fs::read(path)?;
|
||||||
|
}
|
||||||
|
Operation::ReadDir { path } => {
|
||||||
|
let path = get_path_within_fs(fs_path, path)?;
|
||||||
|
let _ = fs::read_dir(path)?.count();
|
||||||
|
}
|
||||||
|
Operation::ReadLink { path } => {
|
||||||
|
let path = get_path_within_fs(fs_path, path)?;
|
||||||
|
fs::read_link(path)?;
|
||||||
|
}
|
||||||
|
Operation::RemoveDir { path } => {
|
||||||
|
let path = get_path_within_fs(fs_path, path)?;
|
||||||
|
fs::remove_dir(path)?;
|
||||||
|
}
|
||||||
|
Operation::RemoveFile { path } => {
|
||||||
|
let path = get_path_within_fs(fs_path, path)?;
|
||||||
|
fs::remove_file(path)?;
|
||||||
|
}
|
||||||
|
Operation::Rename { from, to } => {
|
||||||
|
let from = get_path_within_fs(fs_path, from)?;
|
||||||
|
let to = get_path_within_fs(fs_path, to)?;
|
||||||
|
fs::rename(from, to)?;
|
||||||
|
}
|
||||||
|
Operation::SeekRead {
|
||||||
|
path,
|
||||||
|
seek_pos,
|
||||||
|
buf_size,
|
||||||
|
} => {
|
||||||
|
ensure!(*buf_size as u64 <= MAX_SIZE);
|
||||||
|
let path = get_path_within_fs(fs_path, path)?;
|
||||||
|
let mut file = File::open(path)?;
|
||||||
|
file.seek(SeekFrom::Start(*seek_pos))?;
|
||||||
|
let mut buf = vec![0; *buf_size];
|
||||||
|
file.read(&mut buf)?;
|
||||||
|
}
|
||||||
|
Operation::SeekWrite {
|
||||||
|
path,
|
||||||
|
seek_pos,
|
||||||
|
buf_size,
|
||||||
|
} => {
|
||||||
|
ensure!(*seek_pos <= MAX_SIZE);
|
||||||
|
ensure!(*buf_size as u64 <= MAX_SIZE);
|
||||||
|
let path = get_path_within_fs(fs_path, path)?;
|
||||||
|
let mut file = OpenOptions::new().write(true).open(path)?;
|
||||||
|
file.seek(SeekFrom::Start(*seek_pos))?;
|
||||||
|
let buf = vec![0; *buf_size];
|
||||||
|
file.write(&buf)?;
|
||||||
|
}
|
||||||
|
Operation::SetLen { path, size } => {
|
||||||
|
let path = get_path_within_fs(fs_path, path)?;
|
||||||
|
let file = OpenOptions::new().write(true).open(path)?;
|
||||||
|
file.set_len(*size)?;
|
||||||
|
}
|
||||||
|
Operation::SetPermissions {
|
||||||
|
path,
|
||||||
|
readonly,
|
||||||
|
mode,
|
||||||
|
} => {
|
||||||
|
let path = get_path_within_fs(fs_path, path)?;
|
||||||
|
let metadata = fs::metadata(&path)?;
|
||||||
|
let mut perms = metadata.permissions();
|
||||||
|
if let Some(readonly) = readonly {
|
||||||
|
perms.set_readonly(*readonly);
|
||||||
|
}
|
||||||
|
if let Some(mode) = mode {
|
||||||
|
perms.set_mode(*mode);
|
||||||
|
}
|
||||||
|
fs::set_permissions(path, perms)?;
|
||||||
|
}
|
||||||
|
Operation::SetTimes {
|
||||||
|
path,
|
||||||
|
accessed_since_epoch,
|
||||||
|
modified_since_epoch,
|
||||||
|
} => {
|
||||||
|
let path = get_path_within_fs(fs_path, path)?;
|
||||||
|
let file = File::options().write(true).open(path)?;
|
||||||
|
let mut times = FileTimes::new();
|
||||||
|
if let Some(accessed_since_epoch) = accessed_since_epoch {
|
||||||
|
if let Some(accessed) = UNIX_EPOCH.checked_add(*accessed_since_epoch) {
|
||||||
|
times = times.set_accessed(accessed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(modified_since_epoch) = modified_since_epoch {
|
||||||
|
if let Some(modified) = UNIX_EPOCH.checked_add(*modified_since_epoch) {
|
||||||
|
times = times.set_modified(modified);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
file.set_times(times)?;
|
||||||
|
}
|
||||||
|
Operation::Statvfs {} => {
|
||||||
|
statvfs(fs_path)?;
|
||||||
|
}
|
||||||
|
Operation::SymLink { original, link } => {
|
||||||
|
let original = get_path_within_fs(fs_path, original)?;
|
||||||
|
let link = get_path_within_fs(fs_path, link)?;
|
||||||
|
unix_fs::symlink(original, link)?;
|
||||||
|
}
|
||||||
|
Operation::Write { path, buf_size } => {
|
||||||
|
ensure!(*buf_size as u64 <= MAX_SIZE);
|
||||||
|
let path = get_path_within_fs(fs_path, path)?;
|
||||||
|
let buf = vec![0; *buf_size];
|
||||||
|
fs::write(path, &buf)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fuzz_target!(|test_case: TestCase| -> Corpus {
|
||||||
|
if test_case.disk_size > MAX_SIZE
|
||||||
|
|| test_case.reserved_size > MAX_SIZE
|
||||||
|
|| test_case.mount_sequences.len() > MAX_MOUNT_SEQUENCES
|
||||||
|
{
|
||||||
|
return Corpus::Reject;
|
||||||
|
}
|
||||||
|
|
||||||
|
let temp_dir = tempfile::Builder::new()
|
||||||
|
.prefix("fuse_fuzz_target")
|
||||||
|
.tempdir()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
eprintln!("create fs");
|
||||||
|
let disk = create_disk(temp_dir.path(), test_case.disk_size);
|
||||||
|
if !create_redoxfs(disk, test_case.reserved_size) {
|
||||||
|
// File system creation failed (e.g., due to insufficient space) so we bail out, still
|
||||||
|
// exercising this code path is useful.
|
||||||
|
return Corpus::Keep;
|
||||||
|
}
|
||||||
|
|
||||||
|
for mount_seq in test_case.mount_sequences.iter() {
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
eprintln!("mount fs: path {:?}, size{}", temp_dir.path(), test_case.disk_size);
|
||||||
|
|
||||||
|
let disk = create_disk(temp_dir.path(), test_case.disk_size);
|
||||||
|
let operations = mount_seq.operations.clone();
|
||||||
|
with_redoxfs_mount(temp_dir.path(), disk, mount_seq.cleanup, move |fs_path| {
|
||||||
|
for operation in operations.iter() {
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
eprintln!("do operation {operation:?}");
|
||||||
|
|
||||||
|
let _result = do_operation(fs_path, operation);
|
||||||
|
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
eprintln!("operation result {:?}", _result.err());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
eprintln!("unmounted fs");
|
||||||
|
}
|
||||||
|
Corpus::Keep
|
||||||
|
});
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "generic-rt"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![allow(internal_features)]
|
|
||||||
#![deny(unsafe_op_in_unsafe_fn)]
|
|
||||||
|
|
||||||
use core::{
|
|
||||||
arch::asm,
|
|
||||||
mem::{self, offset_of},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct GenericTcb<Os> {
|
|
||||||
/// Pointer to the end of static TLS. Must be the first member
|
|
||||||
pub tls_end: *mut u8,
|
|
||||||
/// Size of the memory allocated for the static TLS in bytes (multiple of page size)
|
|
||||||
pub tls_len: usize,
|
|
||||||
/// Pointer to this structure
|
|
||||||
pub tcb_ptr: *mut Self,
|
|
||||||
/// Size of the memory allocated for this structure in bytes (should be same as page size)
|
|
||||||
pub tcb_len: usize,
|
|
||||||
pub os_specific: Os,
|
|
||||||
}
|
|
||||||
impl<Os> GenericTcb<Os> {
|
|
||||||
/// Architecture specific code to read a usize from the TCB - aarch64
|
|
||||||
#[allow(unsafe_op_in_unsafe_fn)]
|
|
||||||
#[inline(always)]
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
pub unsafe fn arch_read(offset: usize) -> usize {
|
|
||||||
let abi_ptr: usize;
|
|
||||||
asm!(
|
|
||||||
"mrs {}, tpidr_el0",
|
|
||||||
out(reg) abi_ptr,
|
|
||||||
);
|
|
||||||
|
|
||||||
let tcb_ptr = *(abi_ptr as *const usize);
|
|
||||||
*((tcb_ptr + offset) as *const usize)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Architecture specific code to read a usize from the TCB - x86
|
|
||||||
#[allow(unsafe_op_in_unsafe_fn)]
|
|
||||||
#[inline(always)]
|
|
||||||
#[cfg(target_arch = "x86")]
|
|
||||||
pub unsafe fn arch_read(offset: usize) -> usize {
|
|
||||||
let value;
|
|
||||||
asm!(
|
|
||||||
"
|
|
||||||
mov {}, gs:[{}]
|
|
||||||
",
|
|
||||||
out(reg) value,
|
|
||||||
in(reg) offset,
|
|
||||||
);
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Architecture specific code to read a usize from the TCB - x86_64
|
|
||||||
#[allow(unsafe_op_in_unsafe_fn)]
|
|
||||||
#[inline(always)]
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
pub unsafe fn arch_read(offset: usize) -> usize {
|
|
||||||
let value;
|
|
||||||
asm!(
|
|
||||||
"
|
|
||||||
mov {}, fs:[{}]
|
|
||||||
",
|
|
||||||
out(reg) value,
|
|
||||||
in(reg) offset,
|
|
||||||
);
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Architecture specific code to read a usize from the TCB - riscv64
|
|
||||||
#[allow(unsafe_op_in_unsafe_fn)]
|
|
||||||
#[inline(always)]
|
|
||||||
#[cfg(target_arch = "riscv64")]
|
|
||||||
unsafe fn arch_read(offset: usize) -> usize {
|
|
||||||
let value;
|
|
||||||
asm!(
|
|
||||||
"ld {value}, -8(tp)", // TCB
|
|
||||||
"add {value}, {value}, {offset}",
|
|
||||||
"ld {value}, 0({value})",
|
|
||||||
value = out(reg) value,
|
|
||||||
offset = in(reg) offset,
|
|
||||||
);
|
|
||||||
value
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn current_ptr() -> Option<*mut Self> {
|
|
||||||
let tcb_ptr = unsafe { Self::arch_read(offset_of!(Self, tcb_ptr)) as *mut Self };
|
|
||||||
let tcb_len = unsafe { Self::arch_read(offset_of!(Self, tcb_len)) };
|
|
||||||
if tcb_ptr.is_null() || tcb_len < mem::size_of::<Self>() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(tcb_ptr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub unsafe fn current() -> Option<&'static mut Self> {
|
|
||||||
unsafe { Some(&mut *Self::current_ptr()?) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#ifndef _ALLOCA_H
|
|
||||||
#define _ALLOCA_H
|
|
||||||
|
|
||||||
#define alloca(size) __builtin_alloca (size)
|
|
||||||
|
|
||||||
#endif /* _ALLOCA_H */
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
#include <openlibm_complex.h>
|
|
||||||
@@ -1,112 +0,0 @@
|
|||||||
/*
|
|
||||||
* MIT License
|
|
||||||
* Copyright (c) 2020 Rich Felker musl-libc
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _FEATURES_H__RELIBC
|
|
||||||
#define _FEATURES_H__RELIBC
|
|
||||||
|
|
||||||
// Version metadata for feature gating
|
|
||||||
// This is useful for divergent implementation specific behavior
|
|
||||||
// glibc, ulibc, and likely others define a similar macro
|
|
||||||
// musl does not define an equivalent macro
|
|
||||||
#define __RELIBC__ 1
|
|
||||||
#define __RELIBC__MAJOR 0
|
|
||||||
#define __RELIBC__MINOR 2
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Sources:
|
|
||||||
* https://en.cppreference.com/w/c/language/attributes
|
|
||||||
* https://clang.llvm.org/docs/LanguageExtensions.html
|
|
||||||
* https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005fc_005fattribute.html
|
|
||||||
* https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Clang doesn't define __has_cpp_attribute if compiling C code
|
|
||||||
#if !defined(__has_cpp_attribute)
|
|
||||||
#define __has_cpp_attribute(x) 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Clang doesn't define __has_c_attribute if compiling C++ code
|
|
||||||
#if !defined(__has_c_attribute)
|
|
||||||
#define __has_c_attribute(x) 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Check if C23+ attributes are available
|
|
||||||
#if defined(__cplusplus)
|
|
||||||
// HACK: GCC backports C++ attributes to C++98 but doesn't accept attributes
|
|
||||||
// placed before the function like cbindgen emits.
|
|
||||||
// Let's just disable attributes for C++98 by checking if a random C++11
|
|
||||||
// feature is available.
|
|
||||||
#define __HAS_ATTRIBUTE(x) __cpp_variable_templates &&__has_cpp_attribute(x)
|
|
||||||
#else
|
|
||||||
#define __HAS_ATTRIBUTE(x) \
|
|
||||||
(__has_c_attribute(x) || __STDC_VERSION__ >= 202311L || \
|
|
||||||
__has_cpp_attribute(x))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// From musl, license MIT {
|
|
||||||
#if defined(_ALL_SOURCE) && !defined(_GNU_SOURCE)
|
|
||||||
#define _GNU_SOURCE 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(_DEFAULT_SOURCE) && !defined(_BSD_SOURCE)
|
|
||||||
#define _BSD_SOURCE 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(_POSIX_SOURCE) && !defined(_POSIX_C_SOURCE) \
|
|
||||||
&& !defined(_XOPEN_SOURCE) && !defined(_GNU_SOURCE) \
|
|
||||||
&& !defined(_BSD_SOURCE) && !defined(__STRICT_ANSI__)
|
|
||||||
#define _BSD_SOURCE 1
|
|
||||||
#define _XOPEN_SOURCE 700
|
|
||||||
#endif
|
|
||||||
// } from musl
|
|
||||||
|
|
||||||
// TODO: Not emitted with cbindgen
|
|
||||||
#if __STDC_VERSION__ >= 199901L
|
|
||||||
#define __restrict restrict
|
|
||||||
#elif !defined(__GNUC__)
|
|
||||||
#define __restrict
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// TODO: Not emitted with cbindgen
|
|
||||||
#if __STDC_VERSION__ >= 199901L || defined(__cplusplus)
|
|
||||||
#define __inline inline
|
|
||||||
#elif !defined(__GNUC__)
|
|
||||||
#define __inline
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Analogous to Rust's Never type
|
|
||||||
//TODO: clang fails to compile C with [[noreturn]]
|
|
||||||
#if defined(__cplusplus) && __HAS_ATTRIBUTE(noreturn) && !(__clang__)
|
|
||||||
#define __noreturn [[noreturn]]
|
|
||||||
// #elif __STDC_VERSION__ >= 201112L
|
|
||||||
// FIXME: cbindgen incorrectly places _Noreturn
|
|
||||||
// #define __noreturn _Noreturn
|
|
||||||
#elif defined(__GNUC__)
|
|
||||||
#define __noreturn __attribute__((__noreturn__))
|
|
||||||
#else
|
|
||||||
#define __noreturn
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Analogous to Rust's #[must_use]
|
|
||||||
// C23 only
|
|
||||||
#if __HAS_ATTRIBUTE(nodiscard)
|
|
||||||
#define __nodiscard [[nodiscard]]
|
|
||||||
#define __nodiscardNote(x) [[nodiscard(x)]]
|
|
||||||
#else
|
|
||||||
#define __nodiscard
|
|
||||||
#define __nodiscardNote(x)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Analogous to Rust's #[deprecated]
|
|
||||||
// C23 only
|
|
||||||
#if __HAS_ATTRIBUTE(deprecated)
|
|
||||||
#define __deprecated [[deprecated]]
|
|
||||||
#define __deprecatedNote(x) [[deprecated(x)]]
|
|
||||||
#else
|
|
||||||
#define __deprecated
|
|
||||||
#define __deprecatedNote(x)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#include <openlibm_fenv.h>
|
|
||||||
#undef complex
|
|
||||||
#undef I
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
// Copied from musl
|
|
||||||
|
|
||||||
#ifndef _ISO646_H
|
|
||||||
#define _ISO646_H
|
|
||||||
|
|
||||||
#ifndef __cplusplus
|
|
||||||
|
|
||||||
#define and &&
|
|
||||||
#define and_eq &=
|
|
||||||
#define bitand &
|
|
||||||
#define bitor |
|
|
||||||
#define compl ~
|
|
||||||
#define not !
|
|
||||||
#define not_eq !=
|
|
||||||
#define or ||
|
|
||||||
#define or_eq |=
|
|
||||||
#define xor ^
|
|
||||||
#define xor_eq ^=
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#ifndef __MACHINE_ENDIAN_H__
|
|
||||||
|
|
||||||
/* TODO: Forcing little endian, if you need a big endian system, fix this { */
|
|
||||||
#ifndef BIG_ENDIAN
|
|
||||||
#define BIG_ENDIAN 4321
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef LITTLE_ENDIAN
|
|
||||||
#define LITTLE_ENDIAN 1234
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef BYTE_ORDER
|
|
||||||
#define BYTE_ORDER LITTLE_ENDIAN
|
|
||||||
#endif
|
|
||||||
/* } */
|
|
||||||
|
|
||||||
#endif /* __MACHINE_ENDIAN_H__ */
|
|
||||||
-113
@@ -1,113 +0,0 @@
|
|||||||
#include <openlibm_math.h>
|
|
||||||
|
|
||||||
// Missing typedefs
|
|
||||||
typedef float float_t;
|
|
||||||
typedef double double_t;
|
|
||||||
|
|
||||||
/* double */
|
|
||||||
|
|
||||||
#ifndef M_PI
|
|
||||||
#define M_PI 3.14159265358979323846 /* pi */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_PI_2
|
|
||||||
#define M_PI_2 1.57079632679489661923 /* pi/2 */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_PI_4
|
|
||||||
#define M_PI_4 0.78539816339744830962 /* pi/4 */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_2_PI
|
|
||||||
#define M_2_PI 0.63661977236758134308 /* 2/pi */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_E
|
|
||||||
#define M_E 2.7182818284590452354 /* e */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_LOG2E
|
|
||||||
#define M_LOG2E 1.4426950408889634074 /* log_2 e */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_LOG10E
|
|
||||||
#define M_LOG10E 0.43429448190325182765 /* log_10 e */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_LN2
|
|
||||||
#define M_LN2 0.69314718055994530942 /* log_e 2 */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_LN10
|
|
||||||
#define M_LN10 2.30258509299404568402 /* log_e 10 */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_1_PI
|
|
||||||
#define M_1_PI 0.31830988618379067154 /* 1/pi */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_2_SQRTPI
|
|
||||||
#define M_2_SQRTPI 1.12837916709551257390 /* 2/sqrt(pi) */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_SQRT2
|
|
||||||
#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_SQRT1_2
|
|
||||||
#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* long double */
|
|
||||||
|
|
||||||
#ifndef M_El
|
|
||||||
#define M_El 2.718281828459045235360287471352662498L /* e */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_LOG2El
|
|
||||||
#define M_LOG2El 1.442695040888963407359924681001892137L /* log_2 e */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_LOG10El
|
|
||||||
#define M_LOG10El 0.434294481903251827651128918916605082L /* log_10 e */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_LN2l
|
|
||||||
#define M_LN2l 0.693147180559945309417232121458176568L /* log_e 2 */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_LN10l
|
|
||||||
#define M_LN10l 2.302585092994045684017991454684364208L /* log_e 10 */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_PIl
|
|
||||||
#define M_PIl 3.141592653589793238462643383279502884L /* pi */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_PI_2l
|
|
||||||
#define M_PI_2l 1.570796326794896619231321691639751442L /* pi/2 */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_PI_4l
|
|
||||||
#define M_PI_4l 0.785398163397448309615660845819875721L /* pi/4 */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_1_PIl
|
|
||||||
#define M_1_PIl 0.318309886183790671537767526745028724L /* 1/pi */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_2_PIl
|
|
||||||
#define M_2_PIl 0.636619772367581343075535053490057448L /* 2/pi */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_2_SQRTPIl
|
|
||||||
#define M_2_SQRTPIl 1.128379167095512573896158903121545172L /* 2/sqrt(pi) */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_SQRT2l
|
|
||||||
#define M_SQRT2l 1.414213562373095048801688724209698079L /* sqrt(2) */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_SQRT1_2l
|
|
||||||
#define M_SQRT1_2l 0.707106781186547524400844362104849039L /* 1/sqrt(2) */
|
|
||||||
#endif
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
#include <string.h>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#ifndef _NETINET_IN_SYSTM_H
|
|
||||||
#define _NETINET_IN_SYSTM_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
typedef uint16_t n_short;
|
|
||||||
typedef uint32_t n_long;
|
|
||||||
typedef uint32_t n_time;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
#ifndef _RELIBC_PATHS_H
|
|
||||||
#define _RELIBC_PATHS_H
|
|
||||||
|
|
||||||
#define _PATH_BSHELL "/bin/sh"
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
#ifndef _SETJMP_H
|
|
||||||
#define _SETJMP_H
|
|
||||||
|
|
||||||
#ifdef __aarch64__
|
|
||||||
typedef unsigned long long jmp_buf[22];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __i386__
|
|
||||||
typedef unsigned long long jmp_buf[6];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __x86_64__
|
|
||||||
typedef unsigned long long jmp_buf[16];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __riscv
|
|
||||||
typedef unsigned long long jmp_buf[26];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef jmp_buf sigjmp_buf;
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int setjmp(jmp_buf buf);
|
|
||||||
void longjmp(jmp_buf buf, int value);
|
|
||||||
int sigsetjmp(jmp_buf buf, int savemask);
|
|
||||||
int siglongjmp(jmp_buf buf, int savemask);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} // extern "C"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* _SETJMP_H */
|
|
||||||
@@ -1,243 +0,0 @@
|
|||||||
/* Copyright (C) 2013-2022 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
This file is part of GCC.
|
|
||||||
|
|
||||||
GCC is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 3, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
GCC is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
Under Section 7 of GPL version 3, you are granted additional
|
|
||||||
permissions described in the GCC Runtime Library Exception, version
|
|
||||||
3.1, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License and
|
|
||||||
a copy of the GCC Runtime Library Exception along with this program;
|
|
||||||
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
|
||||||
<http://www.gnu.org/licenses/>. */
|
|
||||||
|
|
||||||
/* ISO C11 Standard: 7.17 Atomics <stdatomic.h>. */
|
|
||||||
|
|
||||||
#ifndef _STDATOMIC_H
|
|
||||||
#define _STDATOMIC_H
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
memory_order_relaxed = __ATOMIC_RELAXED,
|
|
||||||
memory_order_consume = __ATOMIC_CONSUME,
|
|
||||||
memory_order_acquire = __ATOMIC_ACQUIRE,
|
|
||||||
memory_order_release = __ATOMIC_RELEASE,
|
|
||||||
memory_order_acq_rel = __ATOMIC_ACQ_REL,
|
|
||||||
memory_order_seq_cst = __ATOMIC_SEQ_CST
|
|
||||||
} memory_order;
|
|
||||||
|
|
||||||
|
|
||||||
typedef _Atomic _Bool atomic_bool;
|
|
||||||
typedef _Atomic char atomic_char;
|
|
||||||
typedef _Atomic signed char atomic_schar;
|
|
||||||
typedef _Atomic unsigned char atomic_uchar;
|
|
||||||
typedef _Atomic short atomic_short;
|
|
||||||
typedef _Atomic unsigned short atomic_ushort;
|
|
||||||
typedef _Atomic int atomic_int;
|
|
||||||
typedef _Atomic unsigned int atomic_uint;
|
|
||||||
typedef _Atomic long atomic_long;
|
|
||||||
typedef _Atomic unsigned long atomic_ulong;
|
|
||||||
typedef _Atomic long long atomic_llong;
|
|
||||||
typedef _Atomic unsigned long long atomic_ullong;
|
|
||||||
typedef _Atomic __CHAR16_TYPE__ atomic_char16_t;
|
|
||||||
typedef _Atomic __CHAR32_TYPE__ atomic_char32_t;
|
|
||||||
typedef _Atomic __WCHAR_TYPE__ atomic_wchar_t;
|
|
||||||
typedef _Atomic __INT_LEAST8_TYPE__ atomic_int_least8_t;
|
|
||||||
typedef _Atomic __UINT_LEAST8_TYPE__ atomic_uint_least8_t;
|
|
||||||
typedef _Atomic __INT_LEAST16_TYPE__ atomic_int_least16_t;
|
|
||||||
typedef _Atomic __UINT_LEAST16_TYPE__ atomic_uint_least16_t;
|
|
||||||
typedef _Atomic __INT_LEAST32_TYPE__ atomic_int_least32_t;
|
|
||||||
typedef _Atomic __UINT_LEAST32_TYPE__ atomic_uint_least32_t;
|
|
||||||
typedef _Atomic __INT_LEAST64_TYPE__ atomic_int_least64_t;
|
|
||||||
typedef _Atomic __UINT_LEAST64_TYPE__ atomic_uint_least64_t;
|
|
||||||
typedef _Atomic __INT_FAST8_TYPE__ atomic_int_fast8_t;
|
|
||||||
typedef _Atomic __UINT_FAST8_TYPE__ atomic_uint_fast8_t;
|
|
||||||
typedef _Atomic __INT_FAST16_TYPE__ atomic_int_fast16_t;
|
|
||||||
typedef _Atomic __UINT_FAST16_TYPE__ atomic_uint_fast16_t;
|
|
||||||
typedef _Atomic __INT_FAST32_TYPE__ atomic_int_fast32_t;
|
|
||||||
typedef _Atomic __UINT_FAST32_TYPE__ atomic_uint_fast32_t;
|
|
||||||
typedef _Atomic __INT_FAST64_TYPE__ atomic_int_fast64_t;
|
|
||||||
typedef _Atomic __UINT_FAST64_TYPE__ atomic_uint_fast64_t;
|
|
||||||
typedef _Atomic __INTPTR_TYPE__ atomic_intptr_t;
|
|
||||||
typedef _Atomic __UINTPTR_TYPE__ atomic_uintptr_t;
|
|
||||||
typedef _Atomic __SIZE_TYPE__ atomic_size_t;
|
|
||||||
typedef _Atomic __PTRDIFF_TYPE__ atomic_ptrdiff_t;
|
|
||||||
typedef _Atomic __INTMAX_TYPE__ atomic_intmax_t;
|
|
||||||
typedef _Atomic __UINTMAX_TYPE__ atomic_uintmax_t;
|
|
||||||
|
|
||||||
|
|
||||||
#define ATOMIC_VAR_INIT(VALUE) (VALUE)
|
|
||||||
|
|
||||||
/* Initialize an atomic object pointed to by PTR with VAL. */
|
|
||||||
#define atomic_init(PTR, VAL) \
|
|
||||||
atomic_store_explicit (PTR, VAL, __ATOMIC_RELAXED)
|
|
||||||
|
|
||||||
#define kill_dependency(Y) \
|
|
||||||
__extension__ \
|
|
||||||
({ \
|
|
||||||
__auto_type __kill_dependency_tmp = (Y); \
|
|
||||||
__kill_dependency_tmp; \
|
|
||||||
})
|
|
||||||
|
|
||||||
extern void atomic_thread_fence (memory_order);
|
|
||||||
#define atomic_thread_fence(MO) __atomic_thread_fence (MO)
|
|
||||||
extern void atomic_signal_fence (memory_order);
|
|
||||||
#define atomic_signal_fence(MO) __atomic_signal_fence (MO)
|
|
||||||
#define atomic_is_lock_free(OBJ) __atomic_is_lock_free (sizeof (*(OBJ)), (OBJ))
|
|
||||||
|
|
||||||
#define ATOMIC_BOOL_LOCK_FREE __GCC_ATOMIC_BOOL_LOCK_FREE
|
|
||||||
#define ATOMIC_CHAR_LOCK_FREE __GCC_ATOMIC_CHAR_LOCK_FREE
|
|
||||||
#define ATOMIC_CHAR16_T_LOCK_FREE __GCC_ATOMIC_CHAR16_T_LOCK_FREE
|
|
||||||
#define ATOMIC_CHAR32_T_LOCK_FREE __GCC_ATOMIC_CHAR32_T_LOCK_FREE
|
|
||||||
#define ATOMIC_WCHAR_T_LOCK_FREE __GCC_ATOMIC_WCHAR_T_LOCK_FREE
|
|
||||||
#define ATOMIC_SHORT_LOCK_FREE __GCC_ATOMIC_SHORT_LOCK_FREE
|
|
||||||
#define ATOMIC_INT_LOCK_FREE __GCC_ATOMIC_INT_LOCK_FREE
|
|
||||||
#define ATOMIC_LONG_LOCK_FREE __GCC_ATOMIC_LONG_LOCK_FREE
|
|
||||||
#define ATOMIC_LLONG_LOCK_FREE __GCC_ATOMIC_LLONG_LOCK_FREE
|
|
||||||
#define ATOMIC_POINTER_LOCK_FREE __GCC_ATOMIC_POINTER_LOCK_FREE
|
|
||||||
|
|
||||||
|
|
||||||
/* Note that these macros require __auto_type to remove
|
|
||||||
_Atomic qualifiers (and const qualifiers, if those are valid on
|
|
||||||
macro operands).
|
|
||||||
|
|
||||||
Also note that the header file uses the generic form of __atomic
|
|
||||||
builtins, which requires the address to be taken of the value
|
|
||||||
parameter, and then we pass that value on. This allows the macros
|
|
||||||
to work for any type, and the compiler is smart enough to convert
|
|
||||||
these to lock-free _N variants if possible, and throw away the
|
|
||||||
temps. */
|
|
||||||
|
|
||||||
#define atomic_store_explicit(PTR, VAL, MO) \
|
|
||||||
__extension__ \
|
|
||||||
({ \
|
|
||||||
__auto_type __atomic_store_ptr = (PTR); \
|
|
||||||
__typeof__ ((void)0, *__atomic_store_ptr) __atomic_store_tmp = (VAL); \
|
|
||||||
__atomic_store (__atomic_store_ptr, &__atomic_store_tmp, (MO)); \
|
|
||||||
})
|
|
||||||
|
|
||||||
#define atomic_store(PTR, VAL) \
|
|
||||||
atomic_store_explicit (PTR, VAL, __ATOMIC_SEQ_CST)
|
|
||||||
|
|
||||||
|
|
||||||
#define atomic_load_explicit(PTR, MO) \
|
|
||||||
__extension__ \
|
|
||||||
({ \
|
|
||||||
__auto_type __atomic_load_ptr = (PTR); \
|
|
||||||
__typeof__ ((void)0, *__atomic_load_ptr) __atomic_load_tmp; \
|
|
||||||
__atomic_load (__atomic_load_ptr, &__atomic_load_tmp, (MO)); \
|
|
||||||
__atomic_load_tmp; \
|
|
||||||
})
|
|
||||||
|
|
||||||
#define atomic_load(PTR) atomic_load_explicit (PTR, __ATOMIC_SEQ_CST)
|
|
||||||
|
|
||||||
|
|
||||||
#define atomic_exchange_explicit(PTR, VAL, MO) \
|
|
||||||
__extension__ \
|
|
||||||
({ \
|
|
||||||
__auto_type __atomic_exchange_ptr = (PTR); \
|
|
||||||
__typeof__ ((void)0, *__atomic_exchange_ptr) __atomic_exchange_val = (VAL); \
|
|
||||||
__typeof__ ((void)0, *__atomic_exchange_ptr) __atomic_exchange_tmp; \
|
|
||||||
__atomic_exchange (__atomic_exchange_ptr, &__atomic_exchange_val, \
|
|
||||||
&__atomic_exchange_tmp, (MO)); \
|
|
||||||
__atomic_exchange_tmp; \
|
|
||||||
})
|
|
||||||
|
|
||||||
#define atomic_exchange(PTR, VAL) \
|
|
||||||
atomic_exchange_explicit (PTR, VAL, __ATOMIC_SEQ_CST)
|
|
||||||
|
|
||||||
|
|
||||||
#define atomic_compare_exchange_strong_explicit(PTR, VAL, DES, SUC, FAIL) \
|
|
||||||
__extension__ \
|
|
||||||
({ \
|
|
||||||
__auto_type __atomic_compare_exchange_ptr = (PTR); \
|
|
||||||
__typeof__ ((void)0, *__atomic_compare_exchange_ptr) __atomic_compare_exchange_tmp \
|
|
||||||
= (DES); \
|
|
||||||
__atomic_compare_exchange (__atomic_compare_exchange_ptr, (VAL), \
|
|
||||||
&__atomic_compare_exchange_tmp, 0, \
|
|
||||||
(SUC), (FAIL)); \
|
|
||||||
})
|
|
||||||
|
|
||||||
#define atomic_compare_exchange_strong(PTR, VAL, DES) \
|
|
||||||
atomic_compare_exchange_strong_explicit (PTR, VAL, DES, __ATOMIC_SEQ_CST, \
|
|
||||||
__ATOMIC_SEQ_CST)
|
|
||||||
|
|
||||||
#define atomic_compare_exchange_weak_explicit(PTR, VAL, DES, SUC, FAIL) \
|
|
||||||
__extension__ \
|
|
||||||
({ \
|
|
||||||
__auto_type __atomic_compare_exchange_ptr = (PTR); \
|
|
||||||
__typeof__ ((void)0, *__atomic_compare_exchange_ptr) __atomic_compare_exchange_tmp \
|
|
||||||
= (DES); \
|
|
||||||
__atomic_compare_exchange (__atomic_compare_exchange_ptr, (VAL), \
|
|
||||||
&__atomic_compare_exchange_tmp, 1, \
|
|
||||||
(SUC), (FAIL)); \
|
|
||||||
})
|
|
||||||
|
|
||||||
#define atomic_compare_exchange_weak(PTR, VAL, DES) \
|
|
||||||
atomic_compare_exchange_weak_explicit (PTR, VAL, DES, __ATOMIC_SEQ_CST, \
|
|
||||||
__ATOMIC_SEQ_CST)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define atomic_fetch_add(PTR, VAL) __atomic_fetch_add ((PTR), (VAL), \
|
|
||||||
__ATOMIC_SEQ_CST)
|
|
||||||
#define atomic_fetch_add_explicit(PTR, VAL, MO) \
|
|
||||||
__atomic_fetch_add ((PTR), (VAL), (MO))
|
|
||||||
|
|
||||||
#define atomic_fetch_sub(PTR, VAL) __atomic_fetch_sub ((PTR), (VAL), \
|
|
||||||
__ATOMIC_SEQ_CST)
|
|
||||||
#define atomic_fetch_sub_explicit(PTR, VAL, MO) \
|
|
||||||
__atomic_fetch_sub ((PTR), (VAL), (MO))
|
|
||||||
|
|
||||||
#define atomic_fetch_or(PTR, VAL) __atomic_fetch_or ((PTR), (VAL), \
|
|
||||||
__ATOMIC_SEQ_CST)
|
|
||||||
#define atomic_fetch_or_explicit(PTR, VAL, MO) \
|
|
||||||
__atomic_fetch_or ((PTR), (VAL), (MO))
|
|
||||||
|
|
||||||
#define atomic_fetch_xor(PTR, VAL) __atomic_fetch_xor ((PTR), (VAL), \
|
|
||||||
__ATOMIC_SEQ_CST)
|
|
||||||
#define atomic_fetch_xor_explicit(PTR, VAL, MO) \
|
|
||||||
__atomic_fetch_xor ((PTR), (VAL), (MO))
|
|
||||||
|
|
||||||
#define atomic_fetch_and(PTR, VAL) __atomic_fetch_and ((PTR), (VAL), \
|
|
||||||
__ATOMIC_SEQ_CST)
|
|
||||||
#define atomic_fetch_and_explicit(PTR, VAL, MO) \
|
|
||||||
__atomic_fetch_and ((PTR), (VAL), (MO))
|
|
||||||
|
|
||||||
|
|
||||||
typedef _Atomic struct
|
|
||||||
{
|
|
||||||
#if __GCC_ATOMIC_TEST_AND_SET_TRUEVAL == 1
|
|
||||||
_Bool __val;
|
|
||||||
#else
|
|
||||||
unsigned char __val;
|
|
||||||
#endif
|
|
||||||
} atomic_flag;
|
|
||||||
|
|
||||||
#define ATOMIC_FLAG_INIT { 0 }
|
|
||||||
|
|
||||||
|
|
||||||
extern _Bool atomic_flag_test_and_set (volatile atomic_flag *);
|
|
||||||
#define atomic_flag_test_and_set(PTR) \
|
|
||||||
__atomic_test_and_set ((PTR), __ATOMIC_SEQ_CST)
|
|
||||||
extern _Bool atomic_flag_test_and_set_explicit (volatile atomic_flag *,
|
|
||||||
memory_order);
|
|
||||||
#define atomic_flag_test_and_set_explicit(PTR, MO) \
|
|
||||||
__atomic_test_and_set ((PTR), (MO))
|
|
||||||
|
|
||||||
extern void atomic_flag_clear (volatile atomic_flag *);
|
|
||||||
#define atomic_flag_clear(PTR) __atomic_clear ((PTR), __ATOMIC_SEQ_CST)
|
|
||||||
extern void atomic_flag_clear_explicit (volatile atomic_flag *, memory_order);
|
|
||||||
#define atomic_flag_clear_explicit(PTR, MO) __atomic_clear ((PTR), (MO))
|
|
||||||
|
|
||||||
#endif /* _STDATOMIC_H */
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
#ifndef _STDBOOL_H
|
|
||||||
#define _STDBOOL_H
|
|
||||||
|
|
||||||
#ifndef __cplusplus
|
|
||||||
#define bool _Bool
|
|
||||||
#define true 1
|
|
||||||
#define false 0
|
|
||||||
#else /* __cplusplus */
|
|
||||||
#if __cplusplus < 201103L
|
|
||||||
#define bool bool
|
|
||||||
#define false false
|
|
||||||
#define true true
|
|
||||||
#endif /*__cplusplus < 201103L*/
|
|
||||||
#endif /* __cplusplus */
|
|
||||||
|
|
||||||
#define __bool_true_false_are_defined 1
|
|
||||||
|
|
||||||
#endif /* _STDBOOL_H */
|
|
||||||
@@ -1,370 +0,0 @@
|
|||||||
/* Copyright (C) 2008-2018 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
This file is part of GCC.
|
|
||||||
|
|
||||||
GCC is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 3, or (at your option)
|
|
||||||
any later version.
|
|
||||||
|
|
||||||
GCC is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
Under Section 7 of GPL version 3, you are granted additional
|
|
||||||
permissions described in the GCC Runtime Library Exception, version
|
|
||||||
3.1, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License and
|
|
||||||
a copy of the GCC Runtime Library Exception along with this program;
|
|
||||||
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
|
||||||
<http://www.gnu.org/licenses/>. */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ISO C Standard: 7.18 Integer types <stdint.h>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _STDINT_H
|
|
||||||
#define _STDINT_H
|
|
||||||
|
|
||||||
/* 7.8.1.1 Exact-width integer types */
|
|
||||||
|
|
||||||
#ifdef __INT8_TYPE__
|
|
||||||
typedef __INT8_TYPE__ int8_t;
|
|
||||||
#endif
|
|
||||||
#ifdef __INT16_TYPE__
|
|
||||||
typedef __INT16_TYPE__ int16_t;
|
|
||||||
#endif
|
|
||||||
#ifdef __INT32_TYPE__
|
|
||||||
typedef __INT32_TYPE__ int32_t;
|
|
||||||
#endif
|
|
||||||
#ifdef __INT64_TYPE__
|
|
||||||
typedef __INT64_TYPE__ int64_t;
|
|
||||||
#endif
|
|
||||||
#ifdef __UINT8_TYPE__
|
|
||||||
typedef __UINT8_TYPE__ uint8_t;
|
|
||||||
typedef __UINT8_TYPE__ u_int8_t;
|
|
||||||
#endif
|
|
||||||
#ifdef __UINT16_TYPE__
|
|
||||||
typedef __UINT16_TYPE__ uint16_t;
|
|
||||||
typedef __UINT16_TYPE__ u_int16_t;
|
|
||||||
#endif
|
|
||||||
#include <bits/uint32-t.h> // for uint32_t
|
|
||||||
#ifdef __UINT64_TYPE__
|
|
||||||
typedef __UINT64_TYPE__ uint64_t;
|
|
||||||
typedef __UINT64_TYPE__ u_int64_t;
|
|
||||||
// Required by openlibm
|
|
||||||
typedef __UINT64_TYPE__ __uint64_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* 7.8.1.2 Minimum-width integer types */
|
|
||||||
|
|
||||||
typedef __INT_LEAST8_TYPE__ int_least8_t;
|
|
||||||
typedef __INT_LEAST16_TYPE__ int_least16_t;
|
|
||||||
typedef __INT_LEAST32_TYPE__ int_least32_t;
|
|
||||||
typedef __INT_LEAST64_TYPE__ int_least64_t;
|
|
||||||
typedef __UINT_LEAST8_TYPE__ uint_least8_t;
|
|
||||||
typedef __UINT_LEAST16_TYPE__ uint_least16_t;
|
|
||||||
typedef __UINT_LEAST32_TYPE__ uint_least32_t;
|
|
||||||
typedef __UINT_LEAST64_TYPE__ uint_least64_t;
|
|
||||||
|
|
||||||
/* 7.8.1.3 Fastest minimum-width integer types */
|
|
||||||
|
|
||||||
typedef __INT_FAST8_TYPE__ int_fast8_t;
|
|
||||||
typedef __INT_FAST16_TYPE__ int_fast16_t;
|
|
||||||
typedef __INT_FAST32_TYPE__ int_fast32_t;
|
|
||||||
typedef __INT_FAST64_TYPE__ int_fast64_t;
|
|
||||||
typedef __UINT_FAST8_TYPE__ uint_fast8_t;
|
|
||||||
typedef __UINT_FAST16_TYPE__ uint_fast16_t;
|
|
||||||
typedef __UINT_FAST32_TYPE__ uint_fast32_t;
|
|
||||||
typedef __UINT_FAST64_TYPE__ uint_fast64_t;
|
|
||||||
|
|
||||||
/* 7.8.1.4 Integer types capable of holding object pointers */
|
|
||||||
|
|
||||||
#include <bits/intptr-t.h> // for intptr_t
|
|
||||||
#ifdef __UINTPTR_TYPE__
|
|
||||||
typedef __UINTPTR_TYPE__ uintptr_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* 7.8.1.5 Greatest-width integer types */
|
|
||||||
|
|
||||||
typedef __INTMAX_TYPE__ intmax_t;
|
|
||||||
typedef __UINTMAX_TYPE__ uintmax_t;
|
|
||||||
|
|
||||||
#if (!defined __cplusplus || __cplusplus >= 201103L \
|
|
||||||
|| defined __STDC_LIMIT_MACROS)
|
|
||||||
|
|
||||||
/* 7.18.2 Limits of specified-width integer types */
|
|
||||||
|
|
||||||
#ifdef __INT8_MAX__
|
|
||||||
# undef INT8_MAX
|
|
||||||
# define INT8_MAX __INT8_MAX__
|
|
||||||
# undef INT8_MIN
|
|
||||||
# define INT8_MIN (-INT8_MAX - 1)
|
|
||||||
#endif
|
|
||||||
#ifdef __UINT8_MAX__
|
|
||||||
# undef UINT8_MAX
|
|
||||||
# define UINT8_MAX __UINT8_MAX__
|
|
||||||
#endif
|
|
||||||
#ifdef __INT16_MAX__
|
|
||||||
# undef INT16_MAX
|
|
||||||
# define INT16_MAX __INT16_MAX__
|
|
||||||
# undef INT16_MIN
|
|
||||||
# define INT16_MIN (-INT16_MAX - 1)
|
|
||||||
#endif
|
|
||||||
#ifdef __UINT16_MAX__
|
|
||||||
# undef UINT16_MAX
|
|
||||||
# define UINT16_MAX __UINT16_MAX__
|
|
||||||
#endif
|
|
||||||
#ifdef __INT32_MAX__
|
|
||||||
# undef INT32_MAX
|
|
||||||
# define INT32_MAX __INT32_MAX__
|
|
||||||
# undef INT32_MIN
|
|
||||||
# define INT32_MIN (-INT32_MAX - 1)
|
|
||||||
#endif
|
|
||||||
#ifdef __UINT32_MAX__
|
|
||||||
# undef UINT32_MAX
|
|
||||||
# define UINT32_MAX __UINT32_MAX__
|
|
||||||
#endif
|
|
||||||
#ifdef __INT64_MAX__
|
|
||||||
# undef INT64_MAX
|
|
||||||
# define INT64_MAX __INT64_MAX__
|
|
||||||
# undef INT64_MIN
|
|
||||||
# define INT64_MIN (-INT64_MAX - 1)
|
|
||||||
#endif
|
|
||||||
#ifdef __UINT64_MAX__
|
|
||||||
# undef UINT64_MAX
|
|
||||||
# define UINT64_MAX __UINT64_MAX__
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#undef INT_LEAST8_MAX
|
|
||||||
#define INT_LEAST8_MAX __INT_LEAST8_MAX__
|
|
||||||
#undef INT_LEAST8_MIN
|
|
||||||
#define INT_LEAST8_MIN (-INT_LEAST8_MAX - 1)
|
|
||||||
#undef UINT_LEAST8_MAX
|
|
||||||
#define UINT_LEAST8_MAX __UINT_LEAST8_MAX__
|
|
||||||
#undef INT_LEAST16_MAX
|
|
||||||
#define INT_LEAST16_MAX __INT_LEAST16_MAX__
|
|
||||||
#undef INT_LEAST16_MIN
|
|
||||||
#define INT_LEAST16_MIN (-INT_LEAST16_MAX - 1)
|
|
||||||
#undef UINT_LEAST16_MAX
|
|
||||||
#define UINT_LEAST16_MAX __UINT_LEAST16_MAX__
|
|
||||||
#undef INT_LEAST32_MAX
|
|
||||||
#define INT_LEAST32_MAX __INT_LEAST32_MAX__
|
|
||||||
#undef INT_LEAST32_MIN
|
|
||||||
#define INT_LEAST32_MIN (-INT_LEAST32_MAX - 1)
|
|
||||||
#undef UINT_LEAST32_MAX
|
|
||||||
#define UINT_LEAST32_MAX __UINT_LEAST32_MAX__
|
|
||||||
#undef INT_LEAST64_MAX
|
|
||||||
#define INT_LEAST64_MAX __INT_LEAST64_MAX__
|
|
||||||
#undef INT_LEAST64_MIN
|
|
||||||
#define INT_LEAST64_MIN (-INT_LEAST64_MAX - 1)
|
|
||||||
#undef UINT_LEAST64_MAX
|
|
||||||
#define UINT_LEAST64_MAX __UINT_LEAST64_MAX__
|
|
||||||
|
|
||||||
#undef INT_FAST8_MAX
|
|
||||||
#define INT_FAST8_MAX __INT_FAST8_MAX__
|
|
||||||
#undef INT_FAST8_MIN
|
|
||||||
#define INT_FAST8_MIN (-INT_FAST8_MAX - 1)
|
|
||||||
#undef UINT_FAST8_MAX
|
|
||||||
#define UINT_FAST8_MAX __UINT_FAST8_MAX__
|
|
||||||
#undef INT_FAST16_MAX
|
|
||||||
#define INT_FAST16_MAX __INT_FAST16_MAX__
|
|
||||||
#undef INT_FAST16_MIN
|
|
||||||
#define INT_FAST16_MIN (-INT_FAST16_MAX - 1)
|
|
||||||
#undef UINT_FAST16_MAX
|
|
||||||
#define UINT_FAST16_MAX __UINT_FAST16_MAX__
|
|
||||||
#undef INT_FAST32_MAX
|
|
||||||
#define INT_FAST32_MAX __INT_FAST32_MAX__
|
|
||||||
#undef INT_FAST32_MIN
|
|
||||||
#define INT_FAST32_MIN (-INT_FAST32_MAX - 1)
|
|
||||||
#undef UINT_FAST32_MAX
|
|
||||||
#define UINT_FAST32_MAX __UINT_FAST32_MAX__
|
|
||||||
#undef INT_FAST64_MAX
|
|
||||||
#define INT_FAST64_MAX __INT_FAST64_MAX__
|
|
||||||
#undef INT_FAST64_MIN
|
|
||||||
#define INT_FAST64_MIN (-INT_FAST64_MAX - 1)
|
|
||||||
#undef UINT_FAST64_MAX
|
|
||||||
#define UINT_FAST64_MAX __UINT_FAST64_MAX__
|
|
||||||
|
|
||||||
#ifdef __INTPTR_MAX__
|
|
||||||
# undef INTPTR_MAX
|
|
||||||
# define INTPTR_MAX __INTPTR_MAX__
|
|
||||||
# undef INTPTR_MIN
|
|
||||||
# define INTPTR_MIN (-INTPTR_MAX - 1)
|
|
||||||
#endif
|
|
||||||
#ifdef __UINTPTR_MAX__
|
|
||||||
# undef UINTPTR_MAX
|
|
||||||
# define UINTPTR_MAX __UINTPTR_MAX__
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#undef INTMAX_MAX
|
|
||||||
#define INTMAX_MAX __INTMAX_MAX__
|
|
||||||
#undef INTMAX_MIN
|
|
||||||
#define INTMAX_MIN (-INTMAX_MAX - 1)
|
|
||||||
#undef UINTMAX_MAX
|
|
||||||
#define UINTMAX_MAX __UINTMAX_MAX__
|
|
||||||
|
|
||||||
/* 7.18.3 Limits of other integer types */
|
|
||||||
|
|
||||||
#undef PTRDIFF_MAX
|
|
||||||
#define PTRDIFF_MAX __PTRDIFF_MAX__
|
|
||||||
#undef PTRDIFF_MIN
|
|
||||||
#define PTRDIFF_MIN (-PTRDIFF_MAX - 1)
|
|
||||||
|
|
||||||
#undef SIG_ATOMIC_MAX
|
|
||||||
#define SIG_ATOMIC_MAX __SIG_ATOMIC_MAX__
|
|
||||||
#undef SIG_ATOMIC_MIN
|
|
||||||
#define SIG_ATOMIC_MIN __SIG_ATOMIC_MIN__
|
|
||||||
|
|
||||||
#undef SIZE_MAX
|
|
||||||
#define SIZE_MAX __SIZE_MAX__
|
|
||||||
|
|
||||||
#undef WCHAR_MAX
|
|
||||||
#define WCHAR_MAX __WCHAR_MAX__
|
|
||||||
#undef WCHAR_MIN
|
|
||||||
#define WCHAR_MIN __WCHAR_MIN__
|
|
||||||
|
|
||||||
#undef WINT_MAX
|
|
||||||
#define WINT_MAX __WINT_MAX__
|
|
||||||
#undef WINT_MIN
|
|
||||||
#define WINT_MIN __WINT_MIN__
|
|
||||||
|
|
||||||
#endif /* (!defined __cplusplus || __cplusplus >= 201103L
|
|
||||||
|| defined __STDC_LIMIT_MACROS) */
|
|
||||||
|
|
||||||
#if (!defined __cplusplus || __cplusplus >= 201103L \
|
|
||||||
|| defined __STDC_CONSTANT_MACROS)
|
|
||||||
|
|
||||||
#undef INT8_C
|
|
||||||
#define INT8_C(c) __INT8_C(c)
|
|
||||||
#undef INT16_C
|
|
||||||
#define INT16_C(c) __INT16_C(c)
|
|
||||||
#undef INT32_C
|
|
||||||
#define INT32_C(c) __INT32_C(c)
|
|
||||||
#undef INT64_C
|
|
||||||
#define INT64_C(c) __INT64_C(c)
|
|
||||||
#undef UINT8_C
|
|
||||||
#define UINT8_C(c) __UINT8_C(c)
|
|
||||||
#undef UINT16_C
|
|
||||||
#define UINT16_C(c) __UINT16_C(c)
|
|
||||||
#undef UINT32_C
|
|
||||||
#define UINT32_C(c) __UINT32_C(c)
|
|
||||||
#undef UINT64_C
|
|
||||||
#define UINT64_C(c) __UINT64_C(c)
|
|
||||||
#undef INTMAX_C
|
|
||||||
#define INTMAX_C(c) __INTMAX_C(c)
|
|
||||||
#undef UINTMAX_C
|
|
||||||
#define UINTMAX_C(c) __UINTMAX_C(c)
|
|
||||||
|
|
||||||
#endif /* (!defined __cplusplus || __cplusplus >= 201103L
|
|
||||||
|| defined __STDC_CONSTANT_MACROS) */
|
|
||||||
|
|
||||||
#ifdef __STDC_WANT_IEC_60559_BFP_EXT__
|
|
||||||
/* TS 18661-1 widths of integer types. */
|
|
||||||
|
|
||||||
#ifdef __INT8_TYPE__
|
|
||||||
# undef INT8_WIDTH
|
|
||||||
# define INT8_WIDTH 8
|
|
||||||
#endif
|
|
||||||
#ifdef __UINT8_TYPE__
|
|
||||||
# undef UINT8_WIDTH
|
|
||||||
# define UINT8_WIDTH 8
|
|
||||||
#endif
|
|
||||||
#ifdef __INT16_TYPE__
|
|
||||||
# undef INT16_WIDTH
|
|
||||||
# define INT16_WIDTH 16
|
|
||||||
#endif
|
|
||||||
#ifdef __UINT16_TYPE__
|
|
||||||
# undef UINT16_WIDTH
|
|
||||||
# define UINT16_WIDTH 16
|
|
||||||
#endif
|
|
||||||
#ifdef __INT32_TYPE__
|
|
||||||
# undef INT32_WIDTH
|
|
||||||
# define INT32_WIDTH 32
|
|
||||||
#endif
|
|
||||||
#ifdef __UINT32_TYPE__
|
|
||||||
# undef UINT32_WIDTH
|
|
||||||
# define UINT32_WIDTH 32
|
|
||||||
#endif
|
|
||||||
#ifdef __INT64_TYPE__
|
|
||||||
# undef INT64_WIDTH
|
|
||||||
# define INT64_WIDTH 64
|
|
||||||
#endif
|
|
||||||
#ifdef __UINT64_TYPE__
|
|
||||||
# undef UINT64_WIDTH
|
|
||||||
# define UINT64_WIDTH 64
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#undef INT_LEAST8_WIDTH
|
|
||||||
#define INT_LEAST8_WIDTH __INT_LEAST8_WIDTH__
|
|
||||||
#undef UINT_LEAST8_WIDTH
|
|
||||||
#define UINT_LEAST8_WIDTH __INT_LEAST8_WIDTH__
|
|
||||||
#undef INT_LEAST16_WIDTH
|
|
||||||
#define INT_LEAST16_WIDTH __INT_LEAST16_WIDTH__
|
|
||||||
#undef UINT_LEAST16_WIDTH
|
|
||||||
#define UINT_LEAST16_WIDTH __INT_LEAST16_WIDTH__
|
|
||||||
#undef INT_LEAST32_WIDTH
|
|
||||||
#define INT_LEAST32_WIDTH __INT_LEAST32_WIDTH__
|
|
||||||
#undef UINT_LEAST32_WIDTH
|
|
||||||
#define UINT_LEAST32_WIDTH __INT_LEAST32_WIDTH__
|
|
||||||
#undef INT_LEAST64_WIDTH
|
|
||||||
#define INT_LEAST64_WIDTH __INT_LEAST64_WIDTH__
|
|
||||||
#undef UINT_LEAST64_WIDTH
|
|
||||||
#define UINT_LEAST64_WIDTH __INT_LEAST64_WIDTH__
|
|
||||||
|
|
||||||
#undef INT_FAST8_WIDTH
|
|
||||||
#define INT_FAST8_WIDTH __INT_FAST8_WIDTH__
|
|
||||||
#undef UINT_FAST8_WIDTH
|
|
||||||
#define UINT_FAST8_WIDTH __INT_FAST8_WIDTH__
|
|
||||||
#undef INT_FAST16_WIDTH
|
|
||||||
#define INT_FAST16_WIDTH __INT_FAST16_WIDTH__
|
|
||||||
#undef UINT_FAST16_WIDTH
|
|
||||||
#define UINT_FAST16_WIDTH __INT_FAST16_WIDTH__
|
|
||||||
#undef INT_FAST32_WIDTH
|
|
||||||
#define INT_FAST32_WIDTH __INT_FAST32_WIDTH__
|
|
||||||
#undef UINT_FAST32_WIDTH
|
|
||||||
#define UINT_FAST32_WIDTH __INT_FAST32_WIDTH__
|
|
||||||
#undef INT_FAST64_WIDTH
|
|
||||||
#define INT_FAST64_WIDTH __INT_FAST64_WIDTH__
|
|
||||||
#undef UINT_FAST64_WIDTH
|
|
||||||
#define UINT_FAST64_WIDTH __INT_FAST64_WIDTH__
|
|
||||||
|
|
||||||
#ifdef __INTPTR_TYPE__
|
|
||||||
# undef INTPTR_WIDTH
|
|
||||||
# define INTPTR_WIDTH __INTPTR_WIDTH__
|
|
||||||
#endif
|
|
||||||
#ifdef __UINTPTR_TYPE__
|
|
||||||
# undef UINTPTR_WIDTH
|
|
||||||
# define UINTPTR_WIDTH __INTPTR_WIDTH__
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#undef INTMAX_WIDTH
|
|
||||||
#define INTMAX_WIDTH __INTMAX_WIDTH__
|
|
||||||
#undef UINTMAX_WIDTH
|
|
||||||
#define UINTMAX_WIDTH __INTMAX_WIDTH__
|
|
||||||
|
|
||||||
#undef PTRDIFF_WIDTH
|
|
||||||
#define PTRDIFF_WIDTH __PTRDIFF_WIDTH__
|
|
||||||
|
|
||||||
#undef SIG_ATOMIC_WIDTH
|
|
||||||
#define SIG_ATOMIC_WIDTH __SIG_ATOMIC_WIDTH__
|
|
||||||
|
|
||||||
#undef SIZE_WIDTH
|
|
||||||
#define SIZE_WIDTH __SIZE_WIDTH__
|
|
||||||
|
|
||||||
#undef WCHAR_WIDTH
|
|
||||||
#define WCHAR_WIDTH __WCHAR_WIDTH__
|
|
||||||
|
|
||||||
#undef WINT_WIDTH
|
|
||||||
#define WINT_WIDTH __WINT_WIDTH__
|
|
||||||
|
|
||||||
#ifdef __ILP32__
|
|
||||||
#define SIZE_MAX UINT32_MAX
|
|
||||||
#else
|
|
||||||
#define SIZE_MAX UINT64_MAX
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* _GCC_STDINT_H */
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
#ifndef _STDIO_EXT_H
|
|
||||||
#define _STDIO_EXT_H
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
size_t __freadahead(FILE *stream);
|
|
||||||
size_t __fpending(FILE *stream);
|
|
||||||
int __freadable(FILE *stream);
|
|
||||||
int __freading(FILE *stream);
|
|
||||||
void __fseterr(FILE *stream);
|
|
||||||
int __fwritable(FILE *stream);
|
|
||||||
int __fwriting(FILE *stream);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} // extern "C"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* _STDIO_EXT_H */
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
/* Spec:
|
|
||||||
* The <stdnoreturn.h> header shall define the macro noreturn which shall
|
|
||||||
* expand to _Noreturn */
|
|
||||||
|
|
||||||
#ifndef _STDNORETURN_H
|
|
||||||
#define _STDNORETURN_H
|
|
||||||
|
|
||||||
#ifndef __cplusplus
|
|
||||||
/* Borrowed from musl */
|
|
||||||
#if __STDC_VERSION__ >= 201112L
|
|
||||||
#elif defined(__GNUC__)
|
|
||||||
#define _Noreturn __attribute__((__noreturn__))
|
|
||||||
#else
|
|
||||||
#define _Noreturn
|
|
||||||
#endif
|
|
||||||
#define noreturn _Noreturn
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
#ifndef _SYS_PARAM_H
|
|
||||||
#define _SYS_PARAM_H
|
|
||||||
|
|
||||||
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
|
|
||||||
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
|
|
||||||
|
|
||||||
#define __bitop(array, index, op) ((array)[(index) / 8] op (1 << (index) % 8))
|
|
||||||
#define setbit(array, index) __bitop(array, index, |=)
|
|
||||||
#define clrbit(array, index) __bitop(array, index, &= ~)
|
|
||||||
#define isset(array, index) __bitop(array, index, &)
|
|
||||||
#define isclr(array, index) !isset(array, index)
|
|
||||||
|
|
||||||
#define howmany(bits, size) (((bits) + (size) - 1) / (size))
|
|
||||||
#define roundup(bits, size) (howmany(bits, size) * (size))
|
|
||||||
#define powerof2(n) !(((n) - 1) & (n))
|
|
||||||
|
|
||||||
// Shamelessly copied from musl.
|
|
||||||
// Tweak as needed.
|
|
||||||
#define MAXSYMLINKS 20
|
|
||||||
#define MAXHOSTNAMELEN 64
|
|
||||||
#define MAXNAMLEN 255
|
|
||||||
#define MAXPATHLEN 4096
|
|
||||||
#define NBBY 8
|
|
||||||
#define NGROUPS 32
|
|
||||||
#define CANBSIZ 255
|
|
||||||
#define NOFILE 256
|
|
||||||
#define NCARGS 131072
|
|
||||||
#define DEV_BSIZE 512
|
|
||||||
#define NOGROUP (-1)
|
|
||||||
|
|
||||||
#include <sys/resource.h>
|
|
||||||
#include <limits.h>
|
|
||||||
|
|
||||||
#include <machine/endian.h>
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
#include <poll.h>
|
|
||||||
@@ -1,846 +0,0 @@
|
|||||||
/* $NetBSD: queue.h,v 1.70 2015/11/02 15:21:23 christos Exp $ */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) 1991, 1993
|
|
||||||
* The Regents of the University of California. All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the University nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* @(#)queue.h 8.5 (Berkeley) 8/20/94
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _SYS_QUEUE_H_
|
|
||||||
#define _SYS_QUEUE_H_
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This file defines five types of data structures: singly-linked lists,
|
|
||||||
* lists, simple queues, tail queues, and circular queues.
|
|
||||||
*
|
|
||||||
* A singly-linked list is headed by a single forward pointer. The
|
|
||||||
* elements are singly linked for minimum space and pointer manipulation
|
|
||||||
* overhead at the expense of O(n) removal for arbitrary elements. New
|
|
||||||
* elements can be added to the list after an existing element or at the
|
|
||||||
* head of the list. Elements being removed from the head of the list
|
|
||||||
* should use the explicit macro for this purpose for optimum
|
|
||||||
* efficiency. A singly-linked list may only be traversed in the forward
|
|
||||||
* direction. Singly-linked lists are ideal for applications with large
|
|
||||||
* datasets and few or no removals or for implementing a LIFO queue.
|
|
||||||
*
|
|
||||||
* A list is headed by a single forward pointer (or an array of forward
|
|
||||||
* pointers for a hash table header). The elements are doubly linked
|
|
||||||
* so that an arbitrary element can be removed without a need to
|
|
||||||
* traverse the list. New elements can be added to the list before
|
|
||||||
* or after an existing element or at the head of the list. A list
|
|
||||||
* may only be traversed in the forward direction.
|
|
||||||
*
|
|
||||||
* A simple queue is headed by a pair of pointers, one the head of the
|
|
||||||
* list and the other to the tail of the list. The elements are singly
|
|
||||||
* linked to save space, so elements can only be removed from the
|
|
||||||
* head of the list. New elements can be added to the list after
|
|
||||||
* an existing element, at the head of the list, or at the end of the
|
|
||||||
* list. A simple queue may only be traversed in the forward direction.
|
|
||||||
*
|
|
||||||
* A tail queue is headed by a pair of pointers, one to the head of the
|
|
||||||
* list and the other to the tail of the list. The elements are doubly
|
|
||||||
* linked so that an arbitrary element can be removed without a need to
|
|
||||||
* traverse the list. New elements can be added to the list before or
|
|
||||||
* after an existing element, at the head of the list, or at the end of
|
|
||||||
* the list. A tail queue may be traversed in either direction.
|
|
||||||
*
|
|
||||||
* A circle queue is headed by a pair of pointers, one to the head of the
|
|
||||||
* list and the other to the tail of the list. The elements are doubly
|
|
||||||
* linked so that an arbitrary element can be removed without a need to
|
|
||||||
* traverse the list. New elements can be added to the list before or after
|
|
||||||
* an existing element, at the head of the list, or at the end of the list.
|
|
||||||
* A circle queue may be traversed in either direction, but has a more
|
|
||||||
* complex end of list detection.
|
|
||||||
*
|
|
||||||
* For details on the use of these macros, see the queue(3) manual page.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Include the definition of NULL only on NetBSD because sys/null.h
|
|
||||||
* is not available elsewhere. This conditional makes the header
|
|
||||||
* portable and it can simply be dropped verbatim into any system.
|
|
||||||
* The caveat is that on other systems some other header
|
|
||||||
* must provide NULL before the macros can be used.
|
|
||||||
*/
|
|
||||||
#ifdef __NetBSD__
|
|
||||||
#include <sys/null.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(QUEUEDEBUG)
|
|
||||||
# if defined(_KERNEL)
|
|
||||||
# define QUEUEDEBUG_ABORT(...) panic(__VA_ARGS__)
|
|
||||||
# else
|
|
||||||
# include <err.h>
|
|
||||||
# define QUEUEDEBUG_ABORT(...) err(1, __VA_ARGS__)
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Singly-linked List definitions.
|
|
||||||
*/
|
|
||||||
#define SLIST_HEAD(name, type) \
|
|
||||||
struct name { \
|
|
||||||
struct type *slh_first; /* first element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SLIST_HEAD_INITIALIZER(head) \
|
|
||||||
{ NULL }
|
|
||||||
|
|
||||||
#define SLIST_ENTRY(type) \
|
|
||||||
struct { \
|
|
||||||
struct type *sle_next; /* next element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Singly-linked List access methods.
|
|
||||||
*/
|
|
||||||
#define SLIST_FIRST(head) ((head)->slh_first)
|
|
||||||
#define SLIST_END(head) NULL
|
|
||||||
#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
|
|
||||||
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
|
||||||
|
|
||||||
#define SLIST_FOREACH(var, head, field) \
|
|
||||||
for((var) = (head)->slh_first; \
|
|
||||||
(var) != SLIST_END(head); \
|
|
||||||
(var) = (var)->field.sle_next)
|
|
||||||
|
|
||||||
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
|
|
||||||
for ((var) = SLIST_FIRST((head)); \
|
|
||||||
(var) != SLIST_END(head) && \
|
|
||||||
((tvar) = SLIST_NEXT((var), field), 1); \
|
|
||||||
(var) = (tvar))
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Singly-linked List functions.
|
|
||||||
*/
|
|
||||||
#define SLIST_INIT(head) do { \
|
|
||||||
(head)->slh_first = SLIST_END(head); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
|
|
||||||
(elm)->field.sle_next = (slistelm)->field.sle_next; \
|
|
||||||
(slistelm)->field.sle_next = (elm); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SLIST_INSERT_HEAD(head, elm, field) do { \
|
|
||||||
(elm)->field.sle_next = (head)->slh_first; \
|
|
||||||
(head)->slh_first = (elm); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SLIST_REMOVE_AFTER(slistelm, field) do { \
|
|
||||||
(slistelm)->field.sle_next = \
|
|
||||||
SLIST_NEXT(SLIST_NEXT((slistelm), field), field); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SLIST_REMOVE_HEAD(head, field) do { \
|
|
||||||
(head)->slh_first = (head)->slh_first->field.sle_next; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SLIST_REMOVE(head, elm, type, field) do { \
|
|
||||||
if ((head)->slh_first == (elm)) { \
|
|
||||||
SLIST_REMOVE_HEAD((head), field); \
|
|
||||||
} \
|
|
||||||
else { \
|
|
||||||
struct type *curelm = (head)->slh_first; \
|
|
||||||
while(curelm->field.sle_next != (elm)) \
|
|
||||||
curelm = curelm->field.sle_next; \
|
|
||||||
curelm->field.sle_next = \
|
|
||||||
curelm->field.sle_next->field.sle_next; \
|
|
||||||
} \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* List definitions.
|
|
||||||
*/
|
|
||||||
#define LIST_HEAD(name, type) \
|
|
||||||
struct name { \
|
|
||||||
struct type *lh_first; /* first element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define LIST_HEAD_INITIALIZER(head) \
|
|
||||||
{ NULL }
|
|
||||||
|
|
||||||
#define LIST_ENTRY(type) \
|
|
||||||
struct { \
|
|
||||||
struct type *le_next; /* next element */ \
|
|
||||||
struct type **le_prev; /* address of previous next element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* List access methods.
|
|
||||||
*/
|
|
||||||
#define LIST_FIRST(head) ((head)->lh_first)
|
|
||||||
#define LIST_END(head) NULL
|
|
||||||
#define LIST_EMPTY(head) ((head)->lh_first == LIST_END(head))
|
|
||||||
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
|
|
||||||
|
|
||||||
#define LIST_FOREACH(var, head, field) \
|
|
||||||
for ((var) = ((head)->lh_first); \
|
|
||||||
(var) != LIST_END(head); \
|
|
||||||
(var) = ((var)->field.le_next))
|
|
||||||
|
|
||||||
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
|
|
||||||
for ((var) = LIST_FIRST((head)); \
|
|
||||||
(var) != LIST_END(head) && \
|
|
||||||
((tvar) = LIST_NEXT((var), field), 1); \
|
|
||||||
(var) = (tvar))
|
|
||||||
|
|
||||||
#define LIST_MOVE(head1, head2) do { \
|
|
||||||
LIST_INIT((head2)); \
|
|
||||||
if (!LIST_EMPTY((head1))) { \
|
|
||||||
(head2)->lh_first = (head1)->lh_first; \
|
|
||||||
LIST_INIT((head1)); \
|
|
||||||
} \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* List functions.
|
|
||||||
*/
|
|
||||||
#if defined(QUEUEDEBUG)
|
|
||||||
#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field) \
|
|
||||||
if ((head)->lh_first && \
|
|
||||||
(head)->lh_first->field.le_prev != &(head)->lh_first) \
|
|
||||||
QUEUEDEBUG_ABORT("LIST_INSERT_HEAD %p %s:%d", (head), \
|
|
||||||
__FILE__, __LINE__);
|
|
||||||
#define QUEUEDEBUG_LIST_OP(elm, field) \
|
|
||||||
if ((elm)->field.le_next && \
|
|
||||||
(elm)->field.le_next->field.le_prev != \
|
|
||||||
&(elm)->field.le_next) \
|
|
||||||
QUEUEDEBUG_ABORT("LIST_* forw %p %s:%d", (elm), \
|
|
||||||
__FILE__, __LINE__); \
|
|
||||||
if (*(elm)->field.le_prev != (elm)) \
|
|
||||||
QUEUEDEBUG_ABORT("LIST_* back %p %s:%d", (elm), \
|
|
||||||
__FILE__, __LINE__);
|
|
||||||
#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field) \
|
|
||||||
(elm)->field.le_next = (void *)1L; \
|
|
||||||
(elm)->field.le_prev = (void *)1L;
|
|
||||||
#else
|
|
||||||
#define QUEUEDEBUG_LIST_INSERT_HEAD(head, elm, field)
|
|
||||||
#define QUEUEDEBUG_LIST_OP(elm, field)
|
|
||||||
#define QUEUEDEBUG_LIST_POSTREMOVE(elm, field)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define LIST_INIT(head) do { \
|
|
||||||
(head)->lh_first = LIST_END(head); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
|
|
||||||
QUEUEDEBUG_LIST_OP((listelm), field) \
|
|
||||||
if (((elm)->field.le_next = (listelm)->field.le_next) != \
|
|
||||||
LIST_END(head)) \
|
|
||||||
(listelm)->field.le_next->field.le_prev = \
|
|
||||||
&(elm)->field.le_next; \
|
|
||||||
(listelm)->field.le_next = (elm); \
|
|
||||||
(elm)->field.le_prev = &(listelm)->field.le_next; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
|
|
||||||
QUEUEDEBUG_LIST_OP((listelm), field) \
|
|
||||||
(elm)->field.le_prev = (listelm)->field.le_prev; \
|
|
||||||
(elm)->field.le_next = (listelm); \
|
|
||||||
*(listelm)->field.le_prev = (elm); \
|
|
||||||
(listelm)->field.le_prev = &(elm)->field.le_next; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define LIST_INSERT_HEAD(head, elm, field) do { \
|
|
||||||
QUEUEDEBUG_LIST_INSERT_HEAD((head), (elm), field) \
|
|
||||||
if (((elm)->field.le_next = (head)->lh_first) != LIST_END(head))\
|
|
||||||
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
|
|
||||||
(head)->lh_first = (elm); \
|
|
||||||
(elm)->field.le_prev = &(head)->lh_first; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define LIST_REMOVE(elm, field) do { \
|
|
||||||
QUEUEDEBUG_LIST_OP((elm), field) \
|
|
||||||
if ((elm)->field.le_next != NULL) \
|
|
||||||
(elm)->field.le_next->field.le_prev = \
|
|
||||||
(elm)->field.le_prev; \
|
|
||||||
*(elm)->field.le_prev = (elm)->field.le_next; \
|
|
||||||
QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define LIST_REPLACE(elm, elm2, field) do { \
|
|
||||||
if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
|
|
||||||
(elm2)->field.le_next->field.le_prev = \
|
|
||||||
&(elm2)->field.le_next; \
|
|
||||||
(elm2)->field.le_prev = (elm)->field.le_prev; \
|
|
||||||
*(elm2)->field.le_prev = (elm2); \
|
|
||||||
QUEUEDEBUG_LIST_POSTREMOVE((elm), field) \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Simple queue definitions.
|
|
||||||
*/
|
|
||||||
#define SIMPLEQ_HEAD(name, type) \
|
|
||||||
struct name { \
|
|
||||||
struct type *sqh_first; /* first element */ \
|
|
||||||
struct type **sqh_last; /* addr of last next element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SIMPLEQ_HEAD_INITIALIZER(head) \
|
|
||||||
{ NULL, &(head).sqh_first }
|
|
||||||
|
|
||||||
#define SIMPLEQ_ENTRY(type) \
|
|
||||||
struct { \
|
|
||||||
struct type *sqe_next; /* next element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Simple queue access methods.
|
|
||||||
*/
|
|
||||||
#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
|
|
||||||
#define SIMPLEQ_END(head) NULL
|
|
||||||
#define SIMPLEQ_EMPTY(head) ((head)->sqh_first == SIMPLEQ_END(head))
|
|
||||||
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
|
|
||||||
|
|
||||||
#define SIMPLEQ_FOREACH(var, head, field) \
|
|
||||||
for ((var) = ((head)->sqh_first); \
|
|
||||||
(var) != SIMPLEQ_END(head); \
|
|
||||||
(var) = ((var)->field.sqe_next))
|
|
||||||
|
|
||||||
#define SIMPLEQ_FOREACH_SAFE(var, head, field, next) \
|
|
||||||
for ((var) = ((head)->sqh_first); \
|
|
||||||
(var) != SIMPLEQ_END(head) && \
|
|
||||||
((next = ((var)->field.sqe_next)), 1); \
|
|
||||||
(var) = (next))
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Simple queue functions.
|
|
||||||
*/
|
|
||||||
#define SIMPLEQ_INIT(head) do { \
|
|
||||||
(head)->sqh_first = NULL; \
|
|
||||||
(head)->sqh_last = &(head)->sqh_first; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
|
|
||||||
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
|
|
||||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
|
||||||
(head)->sqh_first = (elm); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
|
|
||||||
(elm)->field.sqe_next = NULL; \
|
|
||||||
*(head)->sqh_last = (elm); \
|
|
||||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
|
||||||
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
|
|
||||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
|
||||||
(listelm)->field.sqe_next = (elm); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
|
|
||||||
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
|
|
||||||
(head)->sqh_last = &(head)->sqh_first; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
|
|
||||||
if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
|
|
||||||
== NULL) \
|
|
||||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SIMPLEQ_REMOVE(head, elm, type, field) do { \
|
|
||||||
if ((head)->sqh_first == (elm)) { \
|
|
||||||
SIMPLEQ_REMOVE_HEAD((head), field); \
|
|
||||||
} else { \
|
|
||||||
struct type *curelm = (head)->sqh_first; \
|
|
||||||
while (curelm->field.sqe_next != (elm)) \
|
|
||||||
curelm = curelm->field.sqe_next; \
|
|
||||||
if ((curelm->field.sqe_next = \
|
|
||||||
curelm->field.sqe_next->field.sqe_next) == NULL) \
|
|
||||||
(head)->sqh_last = &(curelm)->field.sqe_next; \
|
|
||||||
} \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SIMPLEQ_CONCAT(head1, head2) do { \
|
|
||||||
if (!SIMPLEQ_EMPTY((head2))) { \
|
|
||||||
*(head1)->sqh_last = (head2)->sqh_first; \
|
|
||||||
(head1)->sqh_last = (head2)->sqh_last; \
|
|
||||||
SIMPLEQ_INIT((head2)); \
|
|
||||||
} \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define SIMPLEQ_LAST(head, type, field) \
|
|
||||||
(SIMPLEQ_EMPTY((head)) ? \
|
|
||||||
NULL : \
|
|
||||||
((struct type *)(void *) \
|
|
||||||
((char *)((head)->sqh_last) - offsetof(struct type, field))))
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Tail queue definitions.
|
|
||||||
*/
|
|
||||||
#define _TAILQ_HEAD(name, type, qual) \
|
|
||||||
struct name { \
|
|
||||||
qual type *tqh_first; /* first element */ \
|
|
||||||
qual type *qual *tqh_last; /* addr of last next element */ \
|
|
||||||
}
|
|
||||||
#define TAILQ_HEAD(name, type) _TAILQ_HEAD(name, struct type,)
|
|
||||||
|
|
||||||
#define TAILQ_HEAD_INITIALIZER(head) \
|
|
||||||
{ TAILQ_END(head), &(head).tqh_first }
|
|
||||||
|
|
||||||
#define _TAILQ_ENTRY(type, qual) \
|
|
||||||
struct { \
|
|
||||||
qual type *tqe_next; /* next element */ \
|
|
||||||
qual type *qual *tqe_prev; /* address of previous next element */\
|
|
||||||
}
|
|
||||||
#define TAILQ_ENTRY(type) _TAILQ_ENTRY(struct type,)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Tail queue access methods.
|
|
||||||
*/
|
|
||||||
#define TAILQ_FIRST(head) ((head)->tqh_first)
|
|
||||||
#define TAILQ_END(head) (NULL)
|
|
||||||
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
|
||||||
#define TAILQ_LAST(head, headname) \
|
|
||||||
(*(((struct headname *)(void *)((head)->tqh_last))->tqh_last))
|
|
||||||
#define TAILQ_PREV(elm, headname, field) \
|
|
||||||
(*(((struct headname *)(void *)((elm)->field.tqe_prev))->tqh_last))
|
|
||||||
#define TAILQ_EMPTY(head) (TAILQ_FIRST(head) == TAILQ_END(head))
|
|
||||||
|
|
||||||
|
|
||||||
#define TAILQ_FOREACH(var, head, field) \
|
|
||||||
for ((var) = ((head)->tqh_first); \
|
|
||||||
(var) != TAILQ_END(head); \
|
|
||||||
(var) = ((var)->field.tqe_next))
|
|
||||||
|
|
||||||
#define TAILQ_FOREACH_SAFE(var, head, field, next) \
|
|
||||||
for ((var) = ((head)->tqh_first); \
|
|
||||||
(var) != TAILQ_END(head) && \
|
|
||||||
((next) = TAILQ_NEXT(var, field), 1); (var) = (next))
|
|
||||||
|
|
||||||
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
|
|
||||||
for ((var) = TAILQ_LAST((head), headname); \
|
|
||||||
(var) != TAILQ_END(head); \
|
|
||||||
(var) = TAILQ_PREV((var), headname, field))
|
|
||||||
|
|
||||||
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, prev) \
|
|
||||||
for ((var) = TAILQ_LAST((head), headname); \
|
|
||||||
(var) != TAILQ_END(head) && \
|
|
||||||
((prev) = TAILQ_PREV((var), headname, field), 1); (var) = (prev))
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Tail queue functions.
|
|
||||||
*/
|
|
||||||
#if defined(QUEUEDEBUG)
|
|
||||||
#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field) \
|
|
||||||
if ((head)->tqh_first && \
|
|
||||||
(head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \
|
|
||||||
QUEUEDEBUG_ABORT("TAILQ_INSERT_HEAD %p %s:%d", (head), \
|
|
||||||
__FILE__, __LINE__);
|
|
||||||
#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field) \
|
|
||||||
if (*(head)->tqh_last != NULL) \
|
|
||||||
QUEUEDEBUG_ABORT("TAILQ_INSERT_TAIL %p %s:%d", (head), \
|
|
||||||
__FILE__, __LINE__);
|
|
||||||
#define QUEUEDEBUG_TAILQ_OP(elm, field) \
|
|
||||||
if ((elm)->field.tqe_next && \
|
|
||||||
(elm)->field.tqe_next->field.tqe_prev != \
|
|
||||||
&(elm)->field.tqe_next) \
|
|
||||||
QUEUEDEBUG_ABORT("TAILQ_* forw %p %s:%d", (elm), \
|
|
||||||
__FILE__, __LINE__); \
|
|
||||||
if (*(elm)->field.tqe_prev != (elm)) \
|
|
||||||
QUEUEDEBUG_ABORT("TAILQ_* back %p %s:%d", (elm), \
|
|
||||||
__FILE__, __LINE__);
|
|
||||||
#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field) \
|
|
||||||
if ((elm)->field.tqe_next == NULL && \
|
|
||||||
(head)->tqh_last != &(elm)->field.tqe_next) \
|
|
||||||
QUEUEDEBUG_ABORT("TAILQ_PREREMOVE head %p elm %p %s:%d",\
|
|
||||||
(head), (elm), __FILE__, __LINE__);
|
|
||||||
#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field) \
|
|
||||||
(elm)->field.tqe_next = (void *)1L; \
|
|
||||||
(elm)->field.tqe_prev = (void *)1L;
|
|
||||||
#else
|
|
||||||
#define QUEUEDEBUG_TAILQ_INSERT_HEAD(head, elm, field)
|
|
||||||
#define QUEUEDEBUG_TAILQ_INSERT_TAIL(head, elm, field)
|
|
||||||
#define QUEUEDEBUG_TAILQ_OP(elm, field)
|
|
||||||
#define QUEUEDEBUG_TAILQ_PREREMOVE(head, elm, field)
|
|
||||||
#define QUEUEDEBUG_TAILQ_POSTREMOVE(elm, field)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define TAILQ_INIT(head) do { \
|
|
||||||
(head)->tqh_first = TAILQ_END(head); \
|
|
||||||
(head)->tqh_last = &(head)->tqh_first; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
|
|
||||||
QUEUEDEBUG_TAILQ_INSERT_HEAD((head), (elm), field) \
|
|
||||||
if (((elm)->field.tqe_next = (head)->tqh_first) != TAILQ_END(head))\
|
|
||||||
(head)->tqh_first->field.tqe_prev = \
|
|
||||||
&(elm)->field.tqe_next; \
|
|
||||||
else \
|
|
||||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
|
||||||
(head)->tqh_first = (elm); \
|
|
||||||
(elm)->field.tqe_prev = &(head)->tqh_first; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
|
|
||||||
QUEUEDEBUG_TAILQ_INSERT_TAIL((head), (elm), field) \
|
|
||||||
(elm)->field.tqe_next = TAILQ_END(head); \
|
|
||||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
|
||||||
*(head)->tqh_last = (elm); \
|
|
||||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
|
||||||
QUEUEDEBUG_TAILQ_OP((listelm), field) \
|
|
||||||
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != \
|
|
||||||
TAILQ_END(head)) \
|
|
||||||
(elm)->field.tqe_next->field.tqe_prev = \
|
|
||||||
&(elm)->field.tqe_next; \
|
|
||||||
else \
|
|
||||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
|
||||||
(listelm)->field.tqe_next = (elm); \
|
|
||||||
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
|
||||||
QUEUEDEBUG_TAILQ_OP((listelm), field) \
|
|
||||||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
|
||||||
(elm)->field.tqe_next = (listelm); \
|
|
||||||
*(listelm)->field.tqe_prev = (elm); \
|
|
||||||
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define TAILQ_REMOVE(head, elm, field) do { \
|
|
||||||
QUEUEDEBUG_TAILQ_PREREMOVE((head), (elm), field) \
|
|
||||||
QUEUEDEBUG_TAILQ_OP((elm), field) \
|
|
||||||
if (((elm)->field.tqe_next) != TAILQ_END(head)) \
|
|
||||||
(elm)->field.tqe_next->field.tqe_prev = \
|
|
||||||
(elm)->field.tqe_prev; \
|
|
||||||
else \
|
|
||||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
|
||||||
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
|
|
||||||
QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define TAILQ_REPLACE(head, elm, elm2, field) do { \
|
|
||||||
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != \
|
|
||||||
TAILQ_END(head)) \
|
|
||||||
(elm2)->field.tqe_next->field.tqe_prev = \
|
|
||||||
&(elm2)->field.tqe_next; \
|
|
||||||
else \
|
|
||||||
(head)->tqh_last = &(elm2)->field.tqe_next; \
|
|
||||||
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
|
|
||||||
*(elm2)->field.tqe_prev = (elm2); \
|
|
||||||
QUEUEDEBUG_TAILQ_POSTREMOVE((elm), field); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define TAILQ_CONCAT(head1, head2, field) do { \
|
|
||||||
if (!TAILQ_EMPTY(head2)) { \
|
|
||||||
*(head1)->tqh_last = (head2)->tqh_first; \
|
|
||||||
(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
|
|
||||||
(head1)->tqh_last = (head2)->tqh_last; \
|
|
||||||
TAILQ_INIT((head2)); \
|
|
||||||
} \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Singly-linked Tail queue declarations.
|
|
||||||
*/
|
|
||||||
#define STAILQ_HEAD(name, type) \
|
|
||||||
struct name { \
|
|
||||||
struct type *stqh_first; /* first element */ \
|
|
||||||
struct type **stqh_last; /* addr of last next element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define STAILQ_HEAD_INITIALIZER(head) \
|
|
||||||
{ NULL, &(head).stqh_first }
|
|
||||||
|
|
||||||
#define STAILQ_ENTRY(type) \
|
|
||||||
struct { \
|
|
||||||
struct type *stqe_next; /* next element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Singly-linked Tail queue access methods.
|
|
||||||
*/
|
|
||||||
#define STAILQ_FIRST(head) ((head)->stqh_first)
|
|
||||||
#define STAILQ_END(head) NULL
|
|
||||||
#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
|
|
||||||
#define STAILQ_EMPTY(head) (STAILQ_FIRST(head) == STAILQ_END(head))
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Singly-linked Tail queue functions.
|
|
||||||
*/
|
|
||||||
#define STAILQ_INIT(head) do { \
|
|
||||||
(head)->stqh_first = NULL; \
|
|
||||||
(head)->stqh_last = &(head)->stqh_first; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define STAILQ_INSERT_HEAD(head, elm, field) do { \
|
|
||||||
if (((elm)->field.stqe_next = (head)->stqh_first) == NULL) \
|
|
||||||
(head)->stqh_last = &(elm)->field.stqe_next; \
|
|
||||||
(head)->stqh_first = (elm); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define STAILQ_INSERT_TAIL(head, elm, field) do { \
|
|
||||||
(elm)->field.stqe_next = NULL; \
|
|
||||||
*(head)->stqh_last = (elm); \
|
|
||||||
(head)->stqh_last = &(elm)->field.stqe_next; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define STAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
|
||||||
if (((elm)->field.stqe_next = (listelm)->field.stqe_next) == NULL)\
|
|
||||||
(head)->stqh_last = &(elm)->field.stqe_next; \
|
|
||||||
(listelm)->field.stqe_next = (elm); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define STAILQ_REMOVE_HEAD(head, field) do { \
|
|
||||||
if (((head)->stqh_first = (head)->stqh_first->field.stqe_next) == NULL) \
|
|
||||||
(head)->stqh_last = &(head)->stqh_first; \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define STAILQ_REMOVE(head, elm, type, field) do { \
|
|
||||||
if ((head)->stqh_first == (elm)) { \
|
|
||||||
STAILQ_REMOVE_HEAD((head), field); \
|
|
||||||
} else { \
|
|
||||||
struct type *curelm = (head)->stqh_first; \
|
|
||||||
while (curelm->field.stqe_next != (elm)) \
|
|
||||||
curelm = curelm->field.stqe_next; \
|
|
||||||
if ((curelm->field.stqe_next = \
|
|
||||||
curelm->field.stqe_next->field.stqe_next) == NULL) \
|
|
||||||
(head)->stqh_last = &(curelm)->field.stqe_next; \
|
|
||||||
} \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define STAILQ_FOREACH(var, head, field) \
|
|
||||||
for ((var) = ((head)->stqh_first); \
|
|
||||||
(var); \
|
|
||||||
(var) = ((var)->field.stqe_next))
|
|
||||||
|
|
||||||
#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
|
||||||
for ((var) = STAILQ_FIRST((head)); \
|
|
||||||
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
|
|
||||||
(var) = (tvar))
|
|
||||||
|
|
||||||
#define STAILQ_CONCAT(head1, head2) do { \
|
|
||||||
if (!STAILQ_EMPTY((head2))) { \
|
|
||||||
*(head1)->stqh_last = (head2)->stqh_first; \
|
|
||||||
(head1)->stqh_last = (head2)->stqh_last; \
|
|
||||||
STAILQ_INIT((head2)); \
|
|
||||||
} \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define STAILQ_LAST(head, type, field) \
|
|
||||||
(STAILQ_EMPTY((head)) ? \
|
|
||||||
NULL : \
|
|
||||||
((struct type *)(void *) \
|
|
||||||
((char *)((head)->stqh_last) - offsetof(struct type, field))))
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef _KERNEL
|
|
||||||
/*
|
|
||||||
* Circular queue definitions. Do not use. We still keep the macros
|
|
||||||
* for compatibility but because of pointer aliasing issues their use
|
|
||||||
* is discouraged!
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* __launder_type(): We use this ugly hack to work around the the compiler
|
|
||||||
* noticing that two types may not alias each other and elide tests in code.
|
|
||||||
* We hit this in the CIRCLEQ macros when comparing 'struct name *' and
|
|
||||||
* 'struct type *' (see CIRCLEQ_HEAD()). Modern compilers (such as GCC
|
|
||||||
* 4.8) declare these comparisons as always false, causing the code to
|
|
||||||
* not run as designed.
|
|
||||||
*
|
|
||||||
* This hack is only to be used for comparisons and thus can be fully const.
|
|
||||||
* Do not use for assignment.
|
|
||||||
*
|
|
||||||
* If we ever choose to change the ABI of the CIRCLEQ macros, we could fix
|
|
||||||
* this by changing the head/tail sentinal values, but see the note above
|
|
||||||
* this one.
|
|
||||||
*/
|
|
||||||
static __inline const void * __launder_type(const void *);
|
|
||||||
static __inline const void *
|
|
||||||
__launder_type(const void *__x)
|
|
||||||
{
|
|
||||||
__asm __volatile("" : "+r" (__x));
|
|
||||||
return __x;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(QUEUEDEBUG)
|
|
||||||
#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field) \
|
|
||||||
if ((head)->cqh_first != CIRCLEQ_ENDC(head) && \
|
|
||||||
(head)->cqh_first->field.cqe_prev != CIRCLEQ_ENDC(head)) \
|
|
||||||
QUEUEDEBUG_ABORT("CIRCLEQ head forw %p %s:%d", (head), \
|
|
||||||
__FILE__, __LINE__); \
|
|
||||||
if ((head)->cqh_last != CIRCLEQ_ENDC(head) && \
|
|
||||||
(head)->cqh_last->field.cqe_next != CIRCLEQ_ENDC(head)) \
|
|
||||||
QUEUEDEBUG_ABORT("CIRCLEQ head back %p %s:%d", (head), \
|
|
||||||
__FILE__, __LINE__);
|
|
||||||
#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field) \
|
|
||||||
if ((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) { \
|
|
||||||
if ((head)->cqh_last != (elm)) \
|
|
||||||
QUEUEDEBUG_ABORT("CIRCLEQ elm last %p %s:%d", \
|
|
||||||
(elm), __FILE__, __LINE__); \
|
|
||||||
} else { \
|
|
||||||
if ((elm)->field.cqe_next->field.cqe_prev != (elm)) \
|
|
||||||
QUEUEDEBUG_ABORT("CIRCLEQ elm forw %p %s:%d", \
|
|
||||||
(elm), __FILE__, __LINE__); \
|
|
||||||
} \
|
|
||||||
if ((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) { \
|
|
||||||
if ((head)->cqh_first != (elm)) \
|
|
||||||
QUEUEDEBUG_ABORT("CIRCLEQ elm first %p %s:%d", \
|
|
||||||
(elm), __FILE__, __LINE__); \
|
|
||||||
} else { \
|
|
||||||
if ((elm)->field.cqe_prev->field.cqe_next != (elm)) \
|
|
||||||
QUEUEDEBUG_ABORT("CIRCLEQ elm prev %p %s:%d", \
|
|
||||||
(elm), __FILE__, __LINE__); \
|
|
||||||
}
|
|
||||||
#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field) \
|
|
||||||
(elm)->field.cqe_next = (void *)1L; \
|
|
||||||
(elm)->field.cqe_prev = (void *)1L;
|
|
||||||
#else
|
|
||||||
#define QUEUEDEBUG_CIRCLEQ_HEAD(head, field)
|
|
||||||
#define QUEUEDEBUG_CIRCLEQ_ELM(head, elm, field)
|
|
||||||
#define QUEUEDEBUG_CIRCLEQ_POSTREMOVE(elm, field)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define CIRCLEQ_HEAD(name, type) \
|
|
||||||
struct name { \
|
|
||||||
struct type *cqh_first; /* first element */ \
|
|
||||||
struct type *cqh_last; /* last element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CIRCLEQ_HEAD_INITIALIZER(head) \
|
|
||||||
{ CIRCLEQ_END(&head), CIRCLEQ_END(&head) }
|
|
||||||
|
|
||||||
#define CIRCLEQ_ENTRY(type) \
|
|
||||||
struct { \
|
|
||||||
struct type *cqe_next; /* next element */ \
|
|
||||||
struct type *cqe_prev; /* previous element */ \
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Circular queue functions.
|
|
||||||
*/
|
|
||||||
#define CIRCLEQ_INIT(head) do { \
|
|
||||||
(head)->cqh_first = CIRCLEQ_END(head); \
|
|
||||||
(head)->cqh_last = CIRCLEQ_END(head); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
|
||||||
QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
|
|
||||||
QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field) \
|
|
||||||
(elm)->field.cqe_next = (listelm)->field.cqe_next; \
|
|
||||||
(elm)->field.cqe_prev = (listelm); \
|
|
||||||
if ((listelm)->field.cqe_next == CIRCLEQ_ENDC(head)) \
|
|
||||||
(head)->cqh_last = (elm); \
|
|
||||||
else \
|
|
||||||
(listelm)->field.cqe_next->field.cqe_prev = (elm); \
|
|
||||||
(listelm)->field.cqe_next = (elm); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \
|
|
||||||
QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
|
|
||||||
QUEUEDEBUG_CIRCLEQ_ELM((head), (listelm), field) \
|
|
||||||
(elm)->field.cqe_next = (listelm); \
|
|
||||||
(elm)->field.cqe_prev = (listelm)->field.cqe_prev; \
|
|
||||||
if ((listelm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \
|
|
||||||
(head)->cqh_first = (elm); \
|
|
||||||
else \
|
|
||||||
(listelm)->field.cqe_prev->field.cqe_next = (elm); \
|
|
||||||
(listelm)->field.cqe_prev = (elm); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \
|
|
||||||
QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
|
|
||||||
(elm)->field.cqe_next = (head)->cqh_first; \
|
|
||||||
(elm)->field.cqe_prev = CIRCLEQ_END(head); \
|
|
||||||
if ((head)->cqh_last == CIRCLEQ_ENDC(head)) \
|
|
||||||
(head)->cqh_last = (elm); \
|
|
||||||
else \
|
|
||||||
(head)->cqh_first->field.cqe_prev = (elm); \
|
|
||||||
(head)->cqh_first = (elm); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \
|
|
||||||
QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
|
|
||||||
(elm)->field.cqe_next = CIRCLEQ_END(head); \
|
|
||||||
(elm)->field.cqe_prev = (head)->cqh_last; \
|
|
||||||
if ((head)->cqh_first == CIRCLEQ_ENDC(head)) \
|
|
||||||
(head)->cqh_first = (elm); \
|
|
||||||
else \
|
|
||||||
(head)->cqh_last->field.cqe_next = (elm); \
|
|
||||||
(head)->cqh_last = (elm); \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define CIRCLEQ_REMOVE(head, elm, field) do { \
|
|
||||||
QUEUEDEBUG_CIRCLEQ_HEAD((head), field) \
|
|
||||||
QUEUEDEBUG_CIRCLEQ_ELM((head), (elm), field) \
|
|
||||||
if ((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) \
|
|
||||||
(head)->cqh_last = (elm)->field.cqe_prev; \
|
|
||||||
else \
|
|
||||||
(elm)->field.cqe_next->field.cqe_prev = \
|
|
||||||
(elm)->field.cqe_prev; \
|
|
||||||
if ((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \
|
|
||||||
(head)->cqh_first = (elm)->field.cqe_next; \
|
|
||||||
else \
|
|
||||||
(elm)->field.cqe_prev->field.cqe_next = \
|
|
||||||
(elm)->field.cqe_next; \
|
|
||||||
QUEUEDEBUG_CIRCLEQ_POSTREMOVE((elm), field) \
|
|
||||||
} while (/*CONSTCOND*/0)
|
|
||||||
|
|
||||||
#define CIRCLEQ_FOREACH(var, head, field) \
|
|
||||||
for ((var) = ((head)->cqh_first); \
|
|
||||||
(var) != CIRCLEQ_ENDC(head); \
|
|
||||||
(var) = ((var)->field.cqe_next))
|
|
||||||
|
|
||||||
#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \
|
|
||||||
for ((var) = ((head)->cqh_last); \
|
|
||||||
(var) != CIRCLEQ_ENDC(head); \
|
|
||||||
(var) = ((var)->field.cqe_prev))
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Circular queue access methods.
|
|
||||||
*/
|
|
||||||
#define CIRCLEQ_FIRST(head) ((head)->cqh_first)
|
|
||||||
#define CIRCLEQ_LAST(head) ((head)->cqh_last)
|
|
||||||
/* For comparisons */
|
|
||||||
#define CIRCLEQ_ENDC(head) (__launder_type(head))
|
|
||||||
/* For assignments */
|
|
||||||
#define CIRCLEQ_END(head) ((void *)(head))
|
|
||||||
#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next)
|
|
||||||
#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev)
|
|
||||||
#define CIRCLEQ_EMPTY(head) \
|
|
||||||
(CIRCLEQ_FIRST(head) == CIRCLEQ_ENDC(head))
|
|
||||||
|
|
||||||
#define CIRCLEQ_LOOP_NEXT(head, elm, field) \
|
|
||||||
(((elm)->field.cqe_next == CIRCLEQ_ENDC(head)) \
|
|
||||||
? ((head)->cqh_first) \
|
|
||||||
: (elm->field.cqe_next))
|
|
||||||
#define CIRCLEQ_LOOP_PREV(head, elm, field) \
|
|
||||||
(((elm)->field.cqe_prev == CIRCLEQ_ENDC(head)) \
|
|
||||||
? ((head)->cqh_last) \
|
|
||||||
: (elm->field.cqe_prev))
|
|
||||||
#endif /* !_KERNEL */
|
|
||||||
|
|
||||||
#endif /* !_SYS_QUEUE_H_ */
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
#ifndef _SYS_REDOX_H
|
|
||||||
#define _SYS_REDOX_H
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __redox__
|
|
||||||
|
|
||||||
ssize_t redox_fpath(int fd, void * buf, size_t count);
|
|
||||||
void * redox_physalloc(size_t size);
|
|
||||||
int redox_physfree(void * physical_address, size_t size);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} // extern "C"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
// From musl, license MIT
|
|
||||||
#ifndef _SYS_SYSMACROS_H
|
|
||||||
#define _SYS_SYSMACROS_H
|
|
||||||
|
|
||||||
#define major(x) \
|
|
||||||
((unsigned)( (((x)>>31>>1) & 0xfffff000) | (((x)>>8) & 0x00000fff) ))
|
|
||||||
#define minor(x) \
|
|
||||||
((unsigned)( (((x)>>12) & 0xffffff00) | ((x) & 0x000000ff) ))
|
|
||||||
|
|
||||||
#define makedev(x,y) ( \
|
|
||||||
(((x)&0xfffff000ULL) << 32) | \
|
|
||||||
(((x)&0x00000fffULL) << 8) | \
|
|
||||||
(((y)&0xffffff00ULL) << 12) | \
|
|
||||||
(((y)&0x000000ffULL)) )
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
#ifndef _SYS_USER_H
|
|
||||||
#define _SYS_USER_H
|
|
||||||
#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64)
|
|
||||||
#include <arch/x64/user.h>
|
|
||||||
#elif defined(__aarch64__)
|
|
||||||
#include <arch/aarch64/user.h>
|
|
||||||
#elif defined(__riscv) && __riscv_xlen==64
|
|
||||||
#include <arch/riscv64/user.h>
|
|
||||||
#else
|
|
||||||
#error "Unknown architecture"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
#include <sys/syslog.h>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "ld_so"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Jeremy Soller <jackpot51@gmail.com>"]
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "ld_so"
|
|
||||||
crate-type = ["staticlib"]
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![feature(linkage)]
|
|
||||||
#![deny(unsafe_op_in_unsafe_fn)]
|
|
||||||
|
|
||||||
use core::arch::global_asm;
|
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
global_asm!(
|
|
||||||
"
|
|
||||||
.weak _DYNAMIC
|
|
||||||
.hidden _DYNAMIC
|
|
||||||
|
|
||||||
.global _start
|
|
||||||
_start:
|
|
||||||
mov x28, sp
|
|
||||||
// align stack to 16 bytes
|
|
||||||
and sp, x28, #0xfffffffffffffff0
|
|
||||||
adr x1, _start
|
|
||||||
mov x0, x28
|
|
||||||
adrp x2, _DYNAMIC
|
|
||||||
add x2, x2, #:lo12:_DYNAMIC
|
|
||||||
// ld_so_start(stack=x0, ld_entry=x1, dynamic=x2)
|
|
||||||
bl relibc_ld_so_start
|
|
||||||
// restore original stack, clear registers, and jump to the new start function
|
|
||||||
mov sp, x28
|
|
||||||
mov x1, xzr
|
|
||||||
mov x2, xzr
|
|
||||||
mov x3, xzr
|
|
||||||
mov x4, xzr
|
|
||||||
mov x5, xzr
|
|
||||||
mov x6, xzr
|
|
||||||
mov x7, xzr
|
|
||||||
mov x8, xzr
|
|
||||||
mov x9, xzr
|
|
||||||
mov x10, xzr
|
|
||||||
mov x11, xzr
|
|
||||||
mov x12, xzr
|
|
||||||
mov x13, xzr
|
|
||||||
mov x14, xzr
|
|
||||||
mov x15, xzr
|
|
||||||
mov x16, xzr
|
|
||||||
mov x17, xzr
|
|
||||||
mov x18, xzr
|
|
||||||
mov x19, xzr
|
|
||||||
mov x20, xzr
|
|
||||||
mov x21, xzr
|
|
||||||
mov x22, xzr
|
|
||||||
mov x23, xzr
|
|
||||||
mov x24, xzr
|
|
||||||
mov x25, xzr
|
|
||||||
mov x26, xzr
|
|
||||||
mov x27, xzr
|
|
||||||
mov x28, xzr
|
|
||||||
mov x29, xzr
|
|
||||||
mov x30, xzr
|
|
||||||
br x0
|
|
||||||
udf #0
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86")]
|
|
||||||
global_asm!(
|
|
||||||
"
|
|
||||||
.globl _start
|
|
||||||
_start:
|
|
||||||
push esp
|
|
||||||
call relibc_ld_so_start
|
|
||||||
pop esp
|
|
||||||
# TODO: x86
|
|
||||||
ud2
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
global_asm!(
|
|
||||||
"
|
|
||||||
.weak _DYNAMIC
|
|
||||||
.hidden _DYNAMIC
|
|
||||||
|
|
||||||
.globl _start
|
|
||||||
_start:
|
|
||||||
lea rsi, [rip + _start]
|
|
||||||
|
|
||||||
# Save original stack and align stack to 16 bytes
|
|
||||||
mov rbp, rsp
|
|
||||||
and rsp, 0xfffffffffffffff0
|
|
||||||
|
|
||||||
# Call ld_so_start(stack=rdi, ld_entry=rsi, dynamic=rdx)
|
|
||||||
mov rdi, rbp
|
|
||||||
lea rdx, [rip + _DYNAMIC]
|
|
||||||
call relibc_ld_so_start
|
|
||||||
|
|
||||||
# Restore original stack, clear registers, and jump to new start function
|
|
||||||
mov rsp, rbp
|
|
||||||
xor rcx, rcx
|
|
||||||
xor rdx, rdx
|
|
||||||
xor rdi, rdi
|
|
||||||
xor rsi, rsi
|
|
||||||
xor r8, r8
|
|
||||||
xor r9, r9
|
|
||||||
xor r10, r10
|
|
||||||
xor r11, r11
|
|
||||||
fninit
|
|
||||||
jmp rax
|
|
||||||
ud2
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(target_arch = "riscv64")]
|
|
||||||
global_asm!(
|
|
||||||
"
|
|
||||||
.globl _start
|
|
||||||
_start:
|
|
||||||
mv a0, sp
|
|
||||||
call relibc_ld_so_start
|
|
||||||
unimp
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub unsafe extern "C" fn main(_argc: isize, _argv: *const *const i8) -> usize {
|
|
||||||
// LD
|
|
||||||
0x1D
|
|
||||||
}
|
|
||||||
|
|
||||||
#[linkage = "weak"]
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
extern "C" fn relibc_panic(_pi: &::core::panic::PanicInfo) -> ! {
|
|
||||||
loop {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[panic_handler]
|
|
||||||
#[linkage = "weak"]
|
|
||||||
pub unsafe fn rust_begin_unwind(pi: &::core::panic::PanicInfo) -> ! {
|
|
||||||
relibc_panic(pi)
|
|
||||||
}
|
|
||||||
-1
Submodule openlibm deleted from f5ed774593
@@ -1,16 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "redox-ioctl"
|
|
||||||
authors = ["bjorn3 <bjorn3_gh@protonmail.com>"]
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
license = "MIT"
|
|
||||||
description = "Ioctl definitions and (de)serialization for Redox"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
drm-sys = "0.8.0"
|
|
||||||
redox_syscall = "0.9.0"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
@@ -1,347 +0,0 @@
|
|||||||
use alloc::vec::Vec;
|
|
||||||
use core::{
|
|
||||||
cmp,
|
|
||||||
ffi::{c_char, c_int, c_uint},
|
|
||||||
iter, mem, slice,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use drm_sys::{
|
|
||||||
__kernel_size_t, DRM_PROP_NAME_LEN, drm_clip_rect, drm_get_cap, drm_mode_card_res,
|
|
||||||
drm_mode_closefb, drm_mode_connector_set_property, drm_mode_create_dumb, drm_mode_crtc,
|
|
||||||
drm_mode_crtc_page_flip_target, drm_mode_cursor, drm_mode_cursor2, drm_mode_destroy_dumb,
|
|
||||||
drm_mode_fb_cmd, drm_mode_fb_cmd2, drm_mode_fb_dirty_cmd, drm_mode_get_blob,
|
|
||||||
drm_mode_get_connector, drm_mode_get_encoder, drm_mode_get_plane, drm_mode_get_plane_res,
|
|
||||||
drm_mode_get_property, drm_mode_map_dumb, drm_mode_modeinfo, drm_mode_obj_get_properties,
|
|
||||||
drm_mode_property_enum, drm_mode_set_plane, drm_set_client_cap, drm_set_version, drm_unique,
|
|
||||||
drm_version,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const VERSION: u64 = 0;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_version, DrmVersion {
|
|
||||||
version_major: c_int,
|
|
||||||
version_minor: c_int,
|
|
||||||
version_patchlevel: c_int,
|
|
||||||
name_len: __kernel_size_t,
|
|
||||||
name: *mut c_char [array<c_char, name_len>],
|
|
||||||
date_len: __kernel_size_t,
|
|
||||||
date: *mut c_char [array<c_char, date_len>],
|
|
||||||
desc_len: __kernel_size_t,
|
|
||||||
desc: *mut c_char [array<c_char, desc_len>],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const GET_UNIQUE: u64 = 0x01;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_unique, DrmUnique {
|
|
||||||
unique_len: __kernel_size_t,
|
|
||||||
unique: *mut c_char [array<c_char, unique_len>],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const SET_VERSION: u64 = 0x07;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_set_version, DrmSetVersion {
|
|
||||||
drm_di_major: c_int,
|
|
||||||
drm_di_minor: c_int,
|
|
||||||
drm_dd_major: c_int,
|
|
||||||
drm_dd_minor: c_int,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const GET_CAP: u64 = 0x0C;
|
|
||||||
pub use drm_sys::DRM_CAP_DUMB_BUFFER;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_get_cap, DrmGetCap {
|
|
||||||
capability: u64,
|
|
||||||
value: u64,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const SET_CLIENT_CAP: u64 = 0x0D;
|
|
||||||
pub use drm_sys::DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_set_client_cap, DrmSetClientCap {
|
|
||||||
capability: u64,
|
|
||||||
value: u64,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_CARD_RES: u64 = 0xA0;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_card_res, DrmModeCardRes {
|
|
||||||
fb_id_ptr: u64 [array<u32, count_fbs>],
|
|
||||||
crtc_id_ptr: u64 [array<u32, count_crtcs>],
|
|
||||||
connector_id_ptr: u64 [array<u32, count_connectors>],
|
|
||||||
encoder_id_ptr: u64 [array<u32, count_encoders>],
|
|
||||||
count_fbs: u32,
|
|
||||||
count_crtcs: u32,
|
|
||||||
count_connectors: u32,
|
|
||||||
count_encoders: u32,
|
|
||||||
min_width: u32,
|
|
||||||
max_width: u32,
|
|
||||||
min_height: u32,
|
|
||||||
max_height: u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_GET_CRTC: u64 = 0xA1;
|
|
||||||
pub const MODE_SET_CRTC: u64 = 0xA2;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_crtc, DrmModeCrtc {
|
|
||||||
set_connectors_ptr: u64 [array<u32, count_connectors>],
|
|
||||||
count_connectors: u32,
|
|
||||||
crtc_id: u32,
|
|
||||||
fb_id: u32,
|
|
||||||
x: u32,
|
|
||||||
y: u32,
|
|
||||||
gamma_size: u32,
|
|
||||||
mode_valid: u32,
|
|
||||||
mode: drm_mode_modeinfo,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_CURSOR: u64 = 0xA3;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_cursor, DrmModeCursor {
|
|
||||||
flags: u32,
|
|
||||||
crtc_id: u32,
|
|
||||||
x: i32,
|
|
||||||
y:i32,
|
|
||||||
width:u32,
|
|
||||||
height:u32,
|
|
||||||
handle:u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_GET_ENCODER: u64 = 0xA6;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_get_encoder, DrmModeGetEncoder {
|
|
||||||
encoder_id: u32,
|
|
||||||
encoder_type: u32,
|
|
||||||
crtc_id: u32,
|
|
||||||
possible_crtcs: u32,
|
|
||||||
possible_clones: u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_GET_CONNECTOR: u64 = 0xA7;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_get_connector, DrmModeGetConnector {
|
|
||||||
encoders_ptr: u64 [array<u32, count_encoders>],
|
|
||||||
modes_ptr: u64 [array<drm_mode_modeinfo, count_modes>],
|
|
||||||
props_ptr: u64 [array<u32, count_props>],
|
|
||||||
prop_values_ptr: u64 [array<u64, count_props>],
|
|
||||||
count_modes: u32,
|
|
||||||
count_props: u32,
|
|
||||||
count_encoders: u32,
|
|
||||||
encoder_id: u32,
|
|
||||||
connector_id: u32,
|
|
||||||
connector_type: u32,
|
|
||||||
connector_type_id: u32,
|
|
||||||
connection: u32,
|
|
||||||
mm_width: u32,
|
|
||||||
mm_height: u32,
|
|
||||||
subpixel: u32,
|
|
||||||
pad: u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_GET_PROPERTY: u64 = 0xAA;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_get_property, DrmModeGetProperty {
|
|
||||||
values_ptr: u64 [array<u64, count_values>],
|
|
||||||
enum_blob_ptr: u64 [array<drm_mode_property_enum, count_enum_blobs>],
|
|
||||||
prop_id: u32,
|
|
||||||
flags: u32,
|
|
||||||
name: [c_char; DRM_PROP_NAME_LEN as usize],
|
|
||||||
count_values: u32,
|
|
||||||
count_enum_blobs: u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_SET_PROPERTY: u64 = 0xAB;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_connector_set_property, DrmModeConnectorSetProperty {
|
|
||||||
value: u64,
|
|
||||||
prop_id: u32,
|
|
||||||
connector_id: u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_GET_PROP_BLOB: u64 = 0xAC;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_get_blob, DrmModeGetBlob {
|
|
||||||
blob_id: u32,
|
|
||||||
length: u32,
|
|
||||||
data: u64 [array<u8, length>],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_GET_FB: u64 = 0xAD;
|
|
||||||
pub const MODE_ADD_FB: u64 = 0xAE;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_fb_cmd, DrmModeFbCmd {
|
|
||||||
fb_id: u32,
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
pitch: u32,
|
|
||||||
bpp: u32,
|
|
||||||
depth: u32,
|
|
||||||
handle: u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_RM_FB: u64 = 0xAF;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct standin_for_uint, StandinForUint {
|
|
||||||
inner: c_uint,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(transparent)]
|
|
||||||
#[allow(non_camel_case_types)]
|
|
||||||
pub struct standin_for_uint {
|
|
||||||
pub inner: c_uint,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_PAGE_FLIP: u64 = 0xB0;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_crtc_page_flip_target, DrmModeCrtcPageFlipTarget {
|
|
||||||
crtc_id: u32,
|
|
||||||
fb_id: u32,
|
|
||||||
flags: u32,
|
|
||||||
sequence: u32,
|
|
||||||
user_data: u64,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_DIRTYFB: u64 = 0xB1;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_fb_dirty_cmd, DrmModeFbDirtyCmd {
|
|
||||||
fb_id: u32,
|
|
||||||
flags: u32,
|
|
||||||
color: u32,
|
|
||||||
num_clips: u32,
|
|
||||||
clips_ptr: u64 [array<drm_clip_rect, num_clips>],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_CREATE_DUMB: u64 = 0xB2;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_create_dumb, DrmModeCreateDumb {
|
|
||||||
height: u32,
|
|
||||||
width: u32,
|
|
||||||
bpp: u32,
|
|
||||||
flags: u32,
|
|
||||||
handle: u32,
|
|
||||||
pitch: u32,
|
|
||||||
size: u64,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_MAP_DUMB: u64 = 0xB3;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_map_dumb, DrmModeMapDumb {
|
|
||||||
handle: u32,
|
|
||||||
pad: u32,
|
|
||||||
offset: u64,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_DESTROY_DUMB: u64 = 0xB4;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_destroy_dumb, DrmModeDestroyDumb {
|
|
||||||
handle: u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_GET_PLANE_RES: u64 = 0xB5;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_get_plane_res, DrmModeGetPlaneRes {
|
|
||||||
plane_id_ptr: u64 [array<u32, count_planes>],
|
|
||||||
count_planes: u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_GET_PLANE: u64 = 0xB6;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_get_plane, DrmModeGetPlane {
|
|
||||||
plane_id: u32,
|
|
||||||
crtc_id: u32,
|
|
||||||
fb_id: u32,
|
|
||||||
possible_crtcs: u32,
|
|
||||||
gamma_size: u32,
|
|
||||||
count_format_types: u32,
|
|
||||||
format_type_ptr: u64 [array<u32, count_format_types>],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_SET_PLANE: u64 = 0xB7;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_set_plane, DrmModeSetPlane {
|
|
||||||
plane_id: u32,
|
|
||||||
crtc_id: u32,
|
|
||||||
fb_id: u32,
|
|
||||||
flags: u32,
|
|
||||||
crtc_x: i32,
|
|
||||||
crtc_y: i32,
|
|
||||||
crtc_w: u32,
|
|
||||||
crtc_h: u32,
|
|
||||||
src_x: u32,
|
|
||||||
src_y: u32,
|
|
||||||
src_h: u32,
|
|
||||||
src_w: u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_ADD_FB2: u64 = 0xB8;
|
|
||||||
|
|
||||||
pub const MODE_OBJ_GET_PROPERTIES: u64 = 0xB9;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_obj_get_properties, DrmModeObjGetProperties {
|
|
||||||
props_ptr: u64 [array<u32, count_props>],
|
|
||||||
prop_values_ptr: u64 [array<u64, count_props>],
|
|
||||||
count_props: u32,
|
|
||||||
obj_id: u32,
|
|
||||||
obj_type: u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_CURSOR2: u64 = 0xBB;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_cursor2, DrmModeCursor2 {
|
|
||||||
flags: u32,
|
|
||||||
crtc_id: u32,
|
|
||||||
x: i32,
|
|
||||||
y: i32,
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
handle: u32,
|
|
||||||
hot_x: i32,
|
|
||||||
hot_y: i32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_GET_FB2: u64 = 0xCE;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_fb_cmd2, DrmModeFbCmd2 {
|
|
||||||
fb_id: u32,
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
pixel_format: u32,
|
|
||||||
flags: u32,
|
|
||||||
handles: [u32; 4],
|
|
||||||
pitches: [u32; 4],
|
|
||||||
offsets: [u32; 4],
|
|
||||||
modifier: [u64; 4],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const MODE_CLOSE_FB: u64 = 0xD0;
|
|
||||||
define_ioctl_data! {
|
|
||||||
struct drm_mode_closefb, DrmModeClosefb {
|
|
||||||
fb_id: u32,
|
|
||||||
pad: u32,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
use alloc::vec::Vec;
|
|
||||||
|
|
||||||
pub trait IoctlData {
|
|
||||||
unsafe fn write(&self) -> Vec<u8>;
|
|
||||||
unsafe fn read_from(&mut self, buf: &[u8]);
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! define_ioctl_data {
|
|
||||||
(struct $ioctl_ty:ident, $mem_ty:ident {
|
|
||||||
$($rest:tt)*
|
|
||||||
}) => {
|
|
||||||
define_ioctl_data!(
|
|
||||||
struct $ioctl_ty, $mem_ty { $($rest)* } => (), (), ()
|
|
||||||
);
|
|
||||||
};
|
|
||||||
(struct $ioctl_ty:ident, $mem_ty:ident {
|
|
||||||
$field:ident: $ty:ty,
|
|
||||||
$($rest:tt)*
|
|
||||||
} =>
|
|
||||||
($($ioctl_fields:tt)*),
|
|
||||||
($($counted_fields:tt)*),
|
|
||||||
($($noncounted_fields:tt)*)
|
|
||||||
) => {
|
|
||||||
define_ioctl_data!(
|
|
||||||
struct $ioctl_ty, $mem_ty { $($rest)* } =>
|
|
||||||
($($ioctl_fields)* $field: $ty,),
|
|
||||||
($($counted_fields)*),
|
|
||||||
($($noncounted_fields)* $field: $ty,)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
(struct $ioctl_ty:ident, $mem_ty:ident {
|
|
||||||
$field:ident: $ty:ty [array<$el:ty, $counted_by:ident>],
|
|
||||||
$($rest:tt)*
|
|
||||||
} =>
|
|
||||||
($($ioctl_fields:tt)*),
|
|
||||||
($($counted_fields:tt)*),
|
|
||||||
($($noncounted_fields:tt)*)
|
|
||||||
) => {
|
|
||||||
define_ioctl_data!(
|
|
||||||
struct $ioctl_ty, $mem_ty { $($rest)* } =>
|
|
||||||
($($ioctl_fields)* $field: $ty,),
|
|
||||||
($($counted_fields)* $field: $ty [array<$el, $counted_by>],),
|
|
||||||
($($noncounted_fields)*)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
(struct $ioctl_ty:ident, $mem_ty:ident {} =>
|
|
||||||
($($ioctl_field:ident: $ioctl_field_ty:ty,)*),
|
|
||||||
($($counted_field:ident: $counted_ty:ty [array<$el:ty, $counted_by:ident>],)*),
|
|
||||||
($($noncounted_field:ident: $noncounted_ty:ty,)*)
|
|
||||||
) => {
|
|
||||||
// FIXME check ioctl_ty doesn't have padding
|
|
||||||
const _: $ioctl_ty = $ioctl_ty {
|
|
||||||
$($ioctl_field: unsafe { mem::zeroed::<$ioctl_field_ty>() },)*
|
|
||||||
};
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct ${concat(__, $mem_ty, Noncounted)} {
|
|
||||||
$($noncounted_field: $noncounted_ty,)*
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct $mem_ty<'a> {
|
|
||||||
noncounted_fields: &'a mut ${concat(__, $mem_ty, Noncounted)},
|
|
||||||
$($counted_field: &'a mut [$el],)*
|
|
||||||
}
|
|
||||||
|
|
||||||
impl $crate::ioctl_data::IoctlData for $ioctl_ty {
|
|
||||||
unsafe fn write(&self) -> Vec<u8> {
|
|
||||||
let noncounted_fields = ${concat(__, $mem_ty, Noncounted)} {
|
|
||||||
$($noncounted_field: self.$noncounted_field,)*
|
|
||||||
};
|
|
||||||
// FIXME use Vec::with_capacity
|
|
||||||
let mut data = Vec::<u8>::new();
|
|
||||||
data.extend_from_slice(&unsafe {
|
|
||||||
mem::transmute::<
|
|
||||||
${concat(__, $mem_ty, Noncounted)},
|
|
||||||
[u8; size_of::<${concat(__, $mem_ty, Noncounted)}>()],
|
|
||||||
>(noncounted_fields)
|
|
||||||
});
|
|
||||||
$(
|
|
||||||
let size = self.$counted_by as usize * size_of::<$el>();
|
|
||||||
if self.$counted_field as usize != 0 {
|
|
||||||
let $counted_field = unsafe {
|
|
||||||
slice::from_raw_parts(self.$counted_field as *const u8, size)
|
|
||||||
};
|
|
||||||
data.extend_from_slice(&$counted_field);
|
|
||||||
} else {
|
|
||||||
data.extend(iter::repeat(0u8).take(size));
|
|
||||||
};
|
|
||||||
|
|
||||||
)*
|
|
||||||
data
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn read_from(&mut self, mut buf: &[u8]) {
|
|
||||||
// FIXME be robust against malicious scheme implementations by returning an error
|
|
||||||
// when the buf is the wrong size
|
|
||||||
let noncounted_fields = buf.split_off(..size_of::<${concat(__, $mem_ty, Noncounted)}>()).unwrap();
|
|
||||||
|
|
||||||
$(
|
|
||||||
let size = self.$counted_by as usize * size_of::<$el>();
|
|
||||||
let $counted_field = buf.split_off(..size).unwrap();
|
|
||||||
if self.$counted_field as usize != 0 {
|
|
||||||
unsafe {
|
|
||||||
slice::from_raw_parts_mut(self.$counted_field as *mut u8, size).copy_from_slice($counted_field);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
|
|
||||||
assert!(buf.is_empty());
|
|
||||||
|
|
||||||
let noncounted_fields = unsafe { &*core::ptr::from_ref(noncounted_fields).cast::<${concat(__, $mem_ty, Noncounted)}>() };
|
|
||||||
$(self.$noncounted_field = noncounted_fields.$noncounted_field;)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> $mem_ty<'a> {
|
|
||||||
pub fn with(
|
|
||||||
mut buf: &'a mut [u8],
|
|
||||||
f: impl FnOnce($mem_ty<'a>) -> syscall::Result<usize>,
|
|
||||||
) -> syscall::Result<usize> {
|
|
||||||
let noncounted_fields = buf.split_off_mut(..size_of::<${concat(__, $mem_ty, Noncounted)}>())
|
|
||||||
.ok_or(syscall::Error::new(syscall::EINVAL))?;
|
|
||||||
let noncounted_fields = unsafe { &mut *core::ptr::from_mut(noncounted_fields).cast::<${concat(__, $mem_ty, Noncounted)}>() };
|
|
||||||
|
|
||||||
$(
|
|
||||||
let $counted_field = buf.split_off_mut(..noncounted_fields.$counted_by as usize * size_of::<$el>())
|
|
||||||
.ok_or(syscall::Error::new(syscall::EINVAL))?;
|
|
||||||
let $counted_field = unsafe {
|
|
||||||
slice::from_raw_parts_mut(core::ptr::from_mut($counted_field).cast::<$el>(), noncounted_fields.$counted_by as usize)
|
|
||||||
};
|
|
||||||
)*
|
|
||||||
|
|
||||||
if !buf.is_empty() {
|
|
||||||
return Err(syscall::Error::new(syscall::EINVAL));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
f($mem_ty {
|
|
||||||
noncounted_fields,
|
|
||||||
$($counted_field,)*
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
$(
|
|
||||||
pub fn $noncounted_field(&self) -> $noncounted_ty {
|
|
||||||
self.noncounted_fields.$noncounted_field
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Should not be called for fields used as array length
|
|
||||||
pub fn ${concat(set_, $noncounted_field)}(&mut self, data: $noncounted_ty) {
|
|
||||||
self.noncounted_fields.$noncounted_field = data;
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
|
|
||||||
$(
|
|
||||||
pub fn $counted_field(&self) -> &[$el] {
|
|
||||||
self.$counted_field
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ${concat(set_, $counted_field)}(&mut self, data: &[$el]) {
|
|
||||||
let copied_count = cmp::min(data.len(), self.$counted_field.len());
|
|
||||||
self.$counted_field[..copied_count].copy_from_slice(&data[..copied_count]);
|
|
||||||
self.noncounted_fields.$counted_by = data.len() as _;
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#![feature(macro_metavar_expr_concat)]
|
|
||||||
#![deny(unsafe_op_in_unsafe_fn)]
|
|
||||||
#![no_std]
|
|
||||||
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
mod ioctl_data;
|
|
||||||
pub use ioctl_data::IoctlData;
|
|
||||||
|
|
||||||
pub mod drm;
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "redox-rt"
|
|
||||||
authors = ["4lDO2 <4lDO2@protonmail.com>"]
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
license = "MIT"
|
|
||||||
description = "Libc-independent runtime for Redox"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bitflags.workspace = true
|
|
||||||
goblin = { version = "0.10", default-features = false, features = ["elf32", "elf64", "endian_fd"] }
|
|
||||||
plain.workspace = true
|
|
||||||
ioslice.workspace = true
|
|
||||||
redox_syscall.workspace = true
|
|
||||||
redox-path.workspace = true
|
|
||||||
redox_protocols.workspace = true
|
|
||||||
|
|
||||||
generic-rt = { path = "../generic-rt" }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
proc = []
|
|
||||||
default = ["proc"]
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
@@ -1,492 +0,0 @@
|
|||||||
use core::{cell::SyncUnsafeCell, mem::offset_of, ptr::NonNull};
|
|
||||||
|
|
||||||
use syscall::{data::*, error::*};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
Tcb,
|
|
||||||
proc::{FdGuard, FdGuardUpper, ForkArgs, fork_inner},
|
|
||||||
signal::{PosixStackt, RtSigarea, SigStack, inner_c},
|
|
||||||
};
|
|
||||||
use redox_protocols::protocol::{ProcCall, RtSigInfo};
|
|
||||||
|
|
||||||
use super::ForkScratchpad;
|
|
||||||
|
|
||||||
// Setup a stack starting from the very end of the address space, and then growing downwards.
|
|
||||||
pub const STACK_TOP: usize = 1 << 47;
|
|
||||||
pub const STACK_SIZE: usize = 1024 * 1024;
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct SigArea {
|
|
||||||
pub altstack_top: usize,
|
|
||||||
pub altstack_bottom: usize,
|
|
||||||
pub tmp_x1_x2: [usize; 2],
|
|
||||||
pub tmp_x3_x4: [usize; 2],
|
|
||||||
pub tmp_x5_x6: [usize; 2],
|
|
||||||
pub tmp_x7_x8: [usize; 2],
|
|
||||||
pub tmp_sp: usize,
|
|
||||||
pub onstack: u64,
|
|
||||||
pub disable_signals_depth: u64,
|
|
||||||
pub pctl: usize, // TODO: remove
|
|
||||||
pub last_sig_was_restart: bool,
|
|
||||||
pub last_sigstack: Option<NonNull<SigStack>>,
|
|
||||||
pub tmp_rt_inf: RtSigInfo,
|
|
||||||
pub tmp_id_inf: u64,
|
|
||||||
}
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct ArchIntRegs {
|
|
||||||
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 sp: usize,
|
|
||||||
pub nzcv: usize, // user-accessible PSTATE bits
|
|
||||||
|
|
||||||
pub pc: usize,
|
|
||||||
pub x0: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deactive TLS, used before exec() on Redox to not trick target executable into thinking TLS
|
|
||||||
/// is already initialized as if it was a thread.
|
|
||||||
pub unsafe fn deactivate_tcb(open_via_dup: &FdGuardUpper) -> Result<()> {
|
|
||||||
let mut env = syscall::EnvRegisters::default();
|
|
||||||
|
|
||||||
let file = open_via_dup.dup_into_upper(b"regs/env")?;
|
|
||||||
|
|
||||||
env.tpidr_el0 = 0;
|
|
||||||
|
|
||||||
file.write(&mut env)?;
|
|
||||||
crate::TLS_ACTIVATED.store(false, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) -> usize {
|
|
||||||
Error::mux(fork_inner(initial_rsp, args))
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C" fn child_hook(scratchpad: &ForkScratchpad) {
|
|
||||||
//let _ = syscall::write(1, alloc::format!("CUR{cur_filetable_fd}PROC{new_proc_fd}THR{new_thr_fd}\n").as_bytes());
|
|
||||||
let _ = crate::sys::close(scratchpad.cur_filetable_fd);
|
|
||||||
unsafe {
|
|
||||||
crate::child_hook_common(crate::ChildHookCommonArgs {
|
|
||||||
new_thr_fd: FdGuard::new(scratchpad.new_thr_fd),
|
|
||||||
new_proc_fd: if scratchpad.new_proc_fd == usize::MAX {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(FdGuard::new(scratchpad.new_proc_fd))
|
|
||||||
},
|
|
||||||
new_filetable_fd: FdGuard::new(scratchpad.new_filetable_fd)
|
|
||||||
.to_upper()
|
|
||||||
.unwrap(),
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
asmfunction!(__relibc_internal_fork_wrapper (usize) -> usize: ["
|
|
||||||
stp x29, x30, [sp, #-16]!
|
|
||||||
stp x27, x28, [sp, #-16]!
|
|
||||||
stp x25, x26, [sp, #-16]!
|
|
||||||
stp x23, x24, [sp, #-16]!
|
|
||||||
stp x21, x22, [sp, #-16]!
|
|
||||||
stp x19, x20, [sp, #-16]!
|
|
||||||
|
|
||||||
//TODO: store floating point regs
|
|
||||||
|
|
||||||
// x0: &ForkArgs
|
|
||||||
mov x1, sp
|
|
||||||
bl {fork_impl}
|
|
||||||
|
|
||||||
ldp x19, x20, [sp], #16
|
|
||||||
ldp x21, x22, [sp], #16
|
|
||||||
ldp x23, x24, [sp], #16
|
|
||||||
ldp x25, x26, [sp], #16
|
|
||||||
ldp x27, x28, [sp], #16
|
|
||||||
ldp x29, x30, [sp], #16
|
|
||||||
ret
|
|
||||||
"] <= [fork_impl = sym fork_impl]);
|
|
||||||
|
|
||||||
asmfunction!(__relibc_internal_fork_ret: ["
|
|
||||||
# scratchpad is in x1, move to x0 for child_hook
|
|
||||||
mov x0, x1
|
|
||||||
|
|
||||||
bl {child_hook}
|
|
||||||
|
|
||||||
//TODO: load floating point regs
|
|
||||||
|
|
||||||
mov x0, xzr
|
|
||||||
|
|
||||||
ldp x19, x20, [sp], #16
|
|
||||||
ldp x21, x22, [sp], #16
|
|
||||||
ldp x23, x24, [sp], #16
|
|
||||||
ldp x25, x26, [sp], #16
|
|
||||||
ldp x27, x28, [sp], #16
|
|
||||||
ldp x29, x30, [sp], #16
|
|
||||||
|
|
||||||
ret
|
|
||||||
"] <= [child_hook = sym child_hook]);
|
|
||||||
|
|
||||||
// https://devblogs.microsoft.com/oldnewthing/20220811-00/?p=106963
|
|
||||||
asmfunction!(__relibc_internal_sigentry: ["
|
|
||||||
// Clear any active reservation.
|
|
||||||
clrex
|
|
||||||
|
|
||||||
// The old pc and x0 are saved in the sigcontrol struct.
|
|
||||||
mrs x0, tpidr_el0 // ABI ptr
|
|
||||||
ldr x0, [x0] // TCB ptr
|
|
||||||
|
|
||||||
// Save x1-x6 and sp
|
|
||||||
stp x1, x2, [x0, #{tcb_sa_off} + {sa_tmp_x1_x2}]
|
|
||||||
stp x3, x4, [x0, #{tcb_sa_off} + {sa_tmp_x3_x4}]
|
|
||||||
stp x5, x6, [x0, #{tcb_sa_off} + {sa_tmp_x5_x6}]
|
|
||||||
stp x7, x8, [x0, #{tcb_sa_off} + {sa_tmp_x7_x8}]
|
|
||||||
mov x1, sp
|
|
||||||
str x1, [x0, #{tcb_sa_off} + {sa_tmp_sp}]
|
|
||||||
|
|
||||||
ldr x6, [x0, #{tcb_sa_off} + {sa_pctl}]
|
|
||||||
1:
|
|
||||||
// Load x1 with the thread's bits
|
|
||||||
add x5, x0, #{tcb_sc_off} + {sc_word}
|
|
||||||
ldaxr x1, [x5]
|
|
||||||
|
|
||||||
// First check if there are standard thread signals,
|
|
||||||
and x4, x1, x1, lsr #32 // x4 := x1 & (x1 >> 32)
|
|
||||||
cbnz x4, 3f // jump if x4 != 0
|
|
||||||
clrex
|
|
||||||
|
|
||||||
// and if not, load process pending bitset.
|
|
||||||
add x5, x6, #{pctl_pending}
|
|
||||||
ldaxr x2, [x5]
|
|
||||||
|
|
||||||
// Check if there are standard proc signals:
|
|
||||||
lsr x3, x1, #32 // mask
|
|
||||||
and w3, w2, w3 // pending unblocked proc
|
|
||||||
cbz w3, 4f // skip 'fetch_andn' step if zero
|
|
||||||
|
|
||||||
// If there was one, find which one, and try clearing the bit (last value in x3, addr in x6)
|
|
||||||
// this picks the MSB rather than the LSB, unlike x86. POSIX does not require any specific
|
|
||||||
// ordering though.
|
|
||||||
clz w3, w3
|
|
||||||
mov w4, #31
|
|
||||||
sub w3, w4, w3
|
|
||||||
// x3 now contains the sig_idx
|
|
||||||
|
|
||||||
mov x4, #1
|
|
||||||
lsl x4, x4, x3 // bit to remove
|
|
||||||
|
|
||||||
sub x4, x2, x4 // bit was certainly set, so sub is allowed
|
|
||||||
// x4 is now the new mask to be set
|
|
||||||
add x5, x6, #{pctl_pending}
|
|
||||||
|
|
||||||
add x2, x5, #{pctl_sender_infos}
|
|
||||||
add x2, x2, w3, uxtb 3
|
|
||||||
ldar x2, [x2]
|
|
||||||
|
|
||||||
// Try clearing the bit, retrying on failure.
|
|
||||||
stxr w1, x4, [x5] // try setting pending set to x4, set w1 := 0 on success
|
|
||||||
cbnz w1, 1b // retry everything if this fails
|
|
||||||
mov x1, x3
|
|
||||||
b 2f
|
|
||||||
4:
|
|
||||||
// Check for realtime signals, thread/proc.
|
|
||||||
clrex
|
|
||||||
|
|
||||||
// Load the pending set again. TODO: optimize this?
|
|
||||||
// Process pending - realtime
|
|
||||||
add x1, x6, #{pctl_pending}
|
|
||||||
ldaxr x2, [x1]
|
|
||||||
lsr x2, x2, #32
|
|
||||||
|
|
||||||
// Thread pending - realtime and allowset
|
|
||||||
add x5, x0, #{tcb_sc_off} + {sc_word} + 8
|
|
||||||
ldar x1, [x5]
|
|
||||||
|
|
||||||
orr x2, x1, x2 // combine proc and thread pending
|
|
||||||
and x2, x2, x2, lsr #32 // AND pending with allowset
|
|
||||||
cbz x2, 7f // spurious signal if realtime is clear
|
|
||||||
|
|
||||||
rbit x2, x2
|
|
||||||
clz x2, x2
|
|
||||||
// x2 now contains sig_idx - 32
|
|
||||||
|
|
||||||
// If realtime signal was directed at thread, handle it as an idempotent signal.
|
|
||||||
lsr x3, x1, x2 // x3 := x1 >> x2; x1 is thread pending
|
|
||||||
tbnz x3, #0, 5f // jump if bit is nonzero
|
|
||||||
|
|
||||||
// SYS_CALL(fd, payload_base, payload_len, metadata_len, metadata_base | (flags << 8))
|
|
||||||
// x8 x0 x1 x2 x3 x4
|
|
||||||
|
|
||||||
mov x5, x0 // save TCB pointer
|
|
||||||
mov x6, x2
|
|
||||||
ldr x8, ={SYS_CALL}
|
|
||||||
adrp x0, {proc_fd}
|
|
||||||
ldr x0, [x0, #:lo12:{proc_fd}]
|
|
||||||
add x1, x5, #{tcb_sa_off} + {sa_tmp_rt_inf}
|
|
||||||
str x6, [x1]
|
|
||||||
mov x2, #{RTINF_SIZE}
|
|
||||||
adrp x4, {proc_call}
|
|
||||||
add x4, x4, :lo12:{proc_call}
|
|
||||||
mov x3, #1
|
|
||||||
svc 0
|
|
||||||
mov x3, x0
|
|
||||||
mov x0, x5 // restore TCB pointer
|
|
||||||
mov x2, x6 // restore signal number - 32
|
|
||||||
add x1, x2, #32 // signal number
|
|
||||||
cbnz x3, 1b
|
|
||||||
b 2f
|
|
||||||
5:
|
|
||||||
// A realtime signal was sent to this thread, try clearing its bit.
|
|
||||||
// x3 contains last rt signal word, x2 contains rt_idx
|
|
||||||
clrex
|
|
||||||
|
|
||||||
// Calculate the absolute sig_idx
|
|
||||||
add x1, x3, 32
|
|
||||||
|
|
||||||
// Load si_pid and si_uid
|
|
||||||
add x2, x0, #{tcb_sc_off} + {sc_sender_infos}
|
|
||||||
add x2, x2, w1, uxtb #3
|
|
||||||
ldar x2, [x2]
|
|
||||||
|
|
||||||
add x3, x0, #{tcb_sc_off} + {sc_word} + 8
|
|
||||||
ldxr x2, [x3]
|
|
||||||
|
|
||||||
// Calculate new mask
|
|
||||||
mov x4, #1
|
|
||||||
lsl x4, x4, x2
|
|
||||||
sub x2, x2, x4 // remove bit
|
|
||||||
|
|
||||||
stxr w5, x2, [x3]
|
|
||||||
cbnz w5, 1b
|
|
||||||
str x2, [x0, #{tcb_sa_off} + {sa_tmp_id_inf}]
|
|
||||||
b 2f
|
|
||||||
3:
|
|
||||||
// A standard signal was sent to this thread, try clearing its bit.
|
|
||||||
clz w1, w1
|
|
||||||
mov x2, #31
|
|
||||||
sub x1, x2, x1
|
|
||||||
|
|
||||||
// Load si_pid and si_uid
|
|
||||||
add x2, x0, #{tcb_sc_off} + {sc_sender_infos}
|
|
||||||
add x2, x2, w1, uxtb #3
|
|
||||||
ldar x2, [x2]
|
|
||||||
|
|
||||||
// Clear bit from mask
|
|
||||||
mov x3, #1
|
|
||||||
lsl x3, x3, x1
|
|
||||||
sub x4, x4, x3
|
|
||||||
|
|
||||||
// Try updating the mask
|
|
||||||
stxr w3, x1, [x5]
|
|
||||||
cbnz w3, 1b
|
|
||||||
|
|
||||||
str x2, [x0, #{tcb_sa_off} + {sa_tmp_id_inf}]
|
|
||||||
2:
|
|
||||||
ldr x3, [x0, #{tcb_sa_off} + {sa_pctl}]
|
|
||||||
add x3, x3, {pctl_actions}
|
|
||||||
add x2, x3, w1, uxtb #4 // actions_base + sig_idx * sizeof Action
|
|
||||||
// TODO: NOT ATOMIC (tearing allowed between regs)!
|
|
||||||
ldxp x2, x3, [x2]
|
|
||||||
clrex
|
|
||||||
|
|
||||||
// Calculate new sp wrt redzone and alignment
|
|
||||||
mov x4, sp
|
|
||||||
sub x4, x4, {REDZONE_SIZE}
|
|
||||||
and x4, x4, -{STACK_ALIGN}
|
|
||||||
mov sp, x4
|
|
||||||
|
|
||||||
// skip sigaltstack step if SA_ONSTACK is clear
|
|
||||||
// tbz x2, #{SA_ONSTACK_BIT}, 2f
|
|
||||||
|
|
||||||
ldr x2, [x0, #{tcb_sc_off} + {sc_saved_pc}]
|
|
||||||
ldr x3, [x0, #{tcb_sc_off} + {sc_saved_x0}]
|
|
||||||
stp x2, x3, [sp, #-16]!
|
|
||||||
|
|
||||||
ldr x2, [x0, #{tcb_sa_off} + {sa_tmp_sp}]
|
|
||||||
mrs x3, nzcv
|
|
||||||
stp x2, x3, [sp, #-16]!
|
|
||||||
|
|
||||||
ldp x2, x3, [x0, #{tcb_sa_off} + {sa_tmp_x1_x2}]
|
|
||||||
stp x2, x3, [sp, #-16]!
|
|
||||||
ldp x3, x4, [x0, #{tcb_sa_off} + {sa_tmp_x3_x4}]
|
|
||||||
stp x4, x3, [sp, #-16]!
|
|
||||||
ldp x5, x6, [x0, #{tcb_sa_off} + {sa_tmp_x5_x6}]
|
|
||||||
stp x6, x5, [sp, #-16]!
|
|
||||||
ldp x7, x8, [x0, #{tcb_sa_off} + {sa_tmp_x7_x8}]
|
|
||||||
stp x8, x7, [sp, #-16]!
|
|
||||||
|
|
||||||
stp x10, x9, [sp, #-16]!
|
|
||||||
stp x12, x11, [sp, #-16]!
|
|
||||||
stp x14, x13, [sp, #-16]!
|
|
||||||
stp x16, x15, [sp, #-16]!
|
|
||||||
stp x18, x17, [sp, #-16]!
|
|
||||||
stp x20, x19, [sp, #-16]!
|
|
||||||
stp x22, x21, [sp, #-16]!
|
|
||||||
stp x24, x23, [sp, #-16]!
|
|
||||||
stp x26, x25, [sp, #-16]!
|
|
||||||
stp x28, x27, [sp, #-16]!
|
|
||||||
stp x30, x29, [sp, #-16]!
|
|
||||||
|
|
||||||
str w1, [sp, #-4]
|
|
||||||
sub sp, sp, #64
|
|
||||||
|
|
||||||
mov x0, sp
|
|
||||||
bl {inner}
|
|
||||||
|
|
||||||
add sp, sp, #64
|
|
||||||
|
|
||||||
ldp x30, x29, [sp], #16
|
|
||||||
ldp x28, x27, [sp], #16
|
|
||||||
ldp x26, x25, [sp], #16
|
|
||||||
ldp x24, x23, [sp], #16
|
|
||||||
ldp x22, x21, [sp], #16
|
|
||||||
ldp x20, x19, [sp], #16
|
|
||||||
ldp x18, x17, [sp], #16
|
|
||||||
ldp x16, x15, [sp], #16
|
|
||||||
ldp x14, x13, [sp], #16
|
|
||||||
ldp x12, x11, [sp], #16
|
|
||||||
ldp x10, x9, [sp], #16
|
|
||||||
ldp x8, x7, [sp], #16
|
|
||||||
ldp x6, x5, [sp], #16
|
|
||||||
ldp x4, x3, [sp], #16
|
|
||||||
ldp x2, x1, [sp], #16
|
|
||||||
|
|
||||||
ldr x0, [sp, #8]
|
|
||||||
msr nzcv, x0
|
|
||||||
|
|
||||||
8:
|
|
||||||
// x18 is reserved by ABI as 'platform register', so clobbering it should be safe.
|
|
||||||
mov x18, sp
|
|
||||||
ldr x0, [x18]
|
|
||||||
mov sp, x0
|
|
||||||
|
|
||||||
ldp x18, x0, [x18, #16]
|
|
||||||
br x18
|
|
||||||
7:
|
|
||||||
// Spurious signal, i.e. all bitsets were 0 at the time they were checked
|
|
||||||
clrex
|
|
||||||
|
|
||||||
ldr x1, [x0, #{tcb_sc_off} + {sc_flags}]
|
|
||||||
and x1, x1, ~1
|
|
||||||
str x1, [x0, #{tcb_sc_off} + {sc_flags}]
|
|
||||||
|
|
||||||
ldp x1, x2, [x0, #{tcb_sa_off} + {sa_tmp_x1_x2}]
|
|
||||||
ldp x3, x4, [x0, #{tcb_sa_off} + {sa_tmp_x3_x4}]
|
|
||||||
ldp x5, x6, [x0, #{tcb_sa_off} + {sa_tmp_x5_x6}]
|
|
||||||
ldr x18, [x0, #{tcb_sc_off} + {sc_saved_pc}]
|
|
||||||
ldr x0, [x0, #{tcb_sc_off} + {sc_saved_x0}]
|
|
||||||
br x18
|
|
||||||
"] <= [
|
|
||||||
pctl_pending = const (offset_of!(SigProcControl, pending)),
|
|
||||||
pctl_actions = const (offset_of!(SigProcControl, actions)),
|
|
||||||
pctl_sender_infos = const (offset_of!(SigProcControl, sender_infos)),
|
|
||||||
tcb_sc_off = const (offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control)),
|
|
||||||
tcb_sa_off = const (offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, arch)),
|
|
||||||
sa_tmp_x1_x2 = const offset_of!(SigArea, tmp_x1_x2),
|
|
||||||
sa_tmp_x3_x4 = const offset_of!(SigArea, tmp_x3_x4),
|
|
||||||
sa_tmp_x5_x6 = const offset_of!(SigArea, tmp_x5_x6),
|
|
||||||
sa_tmp_x7_x8 = const offset_of!(SigArea, tmp_x7_x8),
|
|
||||||
sa_tmp_sp = const offset_of!(SigArea, tmp_sp),
|
|
||||||
sa_tmp_rt_inf = const offset_of!(SigArea, tmp_rt_inf),
|
|
||||||
sa_tmp_id_inf = const offset_of!(SigArea, tmp_id_inf),
|
|
||||||
sa_pctl = const offset_of!(SigArea, pctl),
|
|
||||||
sc_saved_pc = const offset_of!(Sigcontrol, saved_ip),
|
|
||||||
sc_saved_x0 = const offset_of!(Sigcontrol, saved_archdep_reg),
|
|
||||||
sc_sender_infos = const offset_of!(Sigcontrol, sender_infos),
|
|
||||||
sc_word = const offset_of!(Sigcontrol, word),
|
|
||||||
sc_flags = const offset_of!(Sigcontrol, control_flags),
|
|
||||||
proc_fd = sym PROC_FD,
|
|
||||||
inner = sym inner_c,
|
|
||||||
proc_call = sym PROC_CALL,
|
|
||||||
|
|
||||||
SA_ONSTACK_BIT = const 58, // (1 << 58) >> 32 = 0x0400_0000
|
|
||||||
|
|
||||||
SYS_CALL = const syscall::SYS_CALL,
|
|
||||||
|
|
||||||
STACK_ALIGN = const 16,
|
|
||||||
REDZONE_SIZE = const 128,
|
|
||||||
RTINF_SIZE = const size_of::<RtSigInfo>(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
asmfunction!(__relibc_internal_rlct_clone_ret: ["
|
|
||||||
# Load registers
|
|
||||||
ldp x8, x0, [sp], #16
|
|
||||||
ldp x1, x2, [sp], #16
|
|
||||||
ldp x3, x4, [sp], #16
|
|
||||||
|
|
||||||
# Call entry point
|
|
||||||
blr x8
|
|
||||||
|
|
||||||
ret
|
|
||||||
"] <= []);
|
|
||||||
|
|
||||||
pub fn current_sp() -> usize {
|
|
||||||
let sp: usize;
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!("mov {}, sp", out(reg) sp);
|
|
||||||
}
|
|
||||||
sp
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn manually_enter_trampoline() {
|
|
||||||
let ctl = unsafe { &Tcb::current().unwrap().os_specific.control };
|
|
||||||
|
|
||||||
ctl.saved_archdep_reg.set(0);
|
|
||||||
let ip_location = &ctl.saved_ip as *const _ as usize;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!("
|
|
||||||
bl 2f
|
|
||||||
b 3f
|
|
||||||
2:
|
|
||||||
str lr, [x0]
|
|
||||||
b __relibc_internal_sigentry
|
|
||||||
3:
|
|
||||||
",
|
|
||||||
inout("x0") ip_location => _, out("lr") _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn arch_pre(stack: &mut SigStack, os: &mut SigArea) -> PosixStackt {
|
|
||||||
PosixStackt {
|
|
||||||
sp: core::ptr::null_mut(), // TODO
|
|
||||||
size: 0, // TODO
|
|
||||||
flags: 0, // TODO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn arch_ret_to_sig(stack: &mut SigStack, control: &Sigcontrol) {
|
|
||||||
let orig_pc = core::mem::replace(
|
|
||||||
&mut stack.regs.pc,
|
|
||||||
__relibc_internal_sigentry as *const () as usize,
|
|
||||||
);
|
|
||||||
control.saved_ip.set(orig_pc);
|
|
||||||
control.saved_archdep_reg.set(stack.regs.x0);
|
|
||||||
}
|
|
||||||
pub(crate) static PROC_FD: SyncUnsafeCell<usize> = SyncUnsafeCell::new(usize::MAX);
|
|
||||||
static PROC_CALL: [usize; 1] = [ProcCall::Sigdeq as usize];
|
|
||||||
@@ -1,418 +0,0 @@
|
|||||||
use core::{cell::SyncUnsafeCell, mem::offset_of, ptr::NonNull, sync::atomic::Ordering};
|
|
||||||
|
|
||||||
use syscall::*;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
proc::{FdGuard, FdGuardUpper, ForkArgs, fork_inner},
|
|
||||||
signal::{PROC_CONTROL_STRUCT, PosixStackt, RtSigarea, SigStack, inner_fastcall},
|
|
||||||
};
|
|
||||||
use redox_protocols::protocol::{ProcCall, RtSigInfo};
|
|
||||||
|
|
||||||
use super::ForkScratchpad;
|
|
||||||
|
|
||||||
// Setup a stack starting from the very end of the address space, and then growing downwards.
|
|
||||||
pub const STACK_TOP: usize = 1 << 31;
|
|
||||||
pub const STACK_SIZE: usize = 1024 * 1024;
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct SigArea {
|
|
||||||
pub altstack_top: usize,
|
|
||||||
pub altstack_bottom: usize,
|
|
||||||
pub tmp_eip: usize,
|
|
||||||
pub tmp_esp: usize,
|
|
||||||
pub tmp_eax: usize,
|
|
||||||
pub tmp_ebx: usize,
|
|
||||||
pub tmp_ecx: usize,
|
|
||||||
pub tmp_edx: usize,
|
|
||||||
pub tmp_edi: usize,
|
|
||||||
pub tmp_esi: usize,
|
|
||||||
pub tmp_rt_inf: RtSigInfo,
|
|
||||||
pub tmp_signo: usize,
|
|
||||||
pub tmp_id_inf: u64,
|
|
||||||
pub tmp_mm0: u64,
|
|
||||||
pub disable_signals_depth: u64,
|
|
||||||
pub last_sig_was_restart: bool,
|
|
||||||
pub last_sigstack: Option<NonNull<SigStack>>,
|
|
||||||
}
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
#[repr(C, align(16))]
|
|
||||||
pub struct ArchIntRegs {
|
|
||||||
pub fxsave: [u16; 29],
|
|
||||||
|
|
||||||
// ensure fxsave region is 16 byte aligned
|
|
||||||
pub _pad: [usize; 2], // fxsave "available" +0
|
|
||||||
|
|
||||||
pub ebp: usize, // fxsave "available" +8
|
|
||||||
pub esi: usize, // avail +12
|
|
||||||
pub edi: usize, // avail +16
|
|
||||||
pub ebx: usize, // avail +20
|
|
||||||
pub eax: usize, // avail +24
|
|
||||||
pub ecx: usize, // avail +28
|
|
||||||
pub edx: usize, // avail +32
|
|
||||||
|
|
||||||
pub eflags: usize, // avail +36
|
|
||||||
pub eip: usize, // avail +40
|
|
||||||
pub esp: usize, // avail +44
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deactive TLS, used before exec() on Redox to not trick target executable into thinking TLS
|
|
||||||
/// is already initialized as if it was a thread.
|
|
||||||
pub unsafe fn deactivate_tcb(open_via_dup: &FdGuardUpper) -> Result<()> {
|
|
||||||
let mut env = syscall::EnvRegisters::default();
|
|
||||||
|
|
||||||
let file = open_via_dup.dup_into_upper(b"regs/env")?;
|
|
||||||
|
|
||||||
env.fsbase = 0;
|
|
||||||
env.gsbase = 0;
|
|
||||||
|
|
||||||
file.write(&mut env)?;
|
|
||||||
crate::TLS_ACTIVATED.store(false, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "fastcall" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) -> usize {
|
|
||||||
Error::mux(fork_inner(initial_rsp, args))
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: duplicate code with x86_64
|
|
||||||
unsafe extern "cdecl" fn child_hook(scratchpad: ForkScratchpad) {
|
|
||||||
let _ = crate::sys::close(scratchpad.cur_filetable_fd);
|
|
||||||
unsafe {
|
|
||||||
crate::child_hook_common(crate::ChildHookCommonArgs {
|
|
||||||
new_thr_fd: FdGuard::new(scratchpad.new_thr_fd),
|
|
||||||
new_proc_fd: if scratchpad.new_proc_fd == usize::MAX {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(FdGuard::new(scratchpad.new_proc_fd))
|
|
||||||
},
|
|
||||||
new_filetable_fd: FdGuard::new(scratchpad.new_filetable_fd)
|
|
||||||
.to_upper()
|
|
||||||
.unwrap(),
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
asmfunction!(__relibc_internal_fork_wrapper (usize) -> usize: ["
|
|
||||||
mov ecx, [esp+4]
|
|
||||||
|
|
||||||
push ebp
|
|
||||||
mov ebp, esp
|
|
||||||
|
|
||||||
// Push preserved registers
|
|
||||||
push ebx
|
|
||||||
push edi
|
|
||||||
push esi
|
|
||||||
push ebp
|
|
||||||
|
|
||||||
sub esp, 32
|
|
||||||
|
|
||||||
//TODO stmxcsr [esp+16]
|
|
||||||
fnstcw [esp+24]
|
|
||||||
|
|
||||||
mov edx, esp
|
|
||||||
call {fork_impl}
|
|
||||||
|
|
||||||
jmp 2f
|
|
||||||
"] <= [fork_impl = sym fork_impl]);
|
|
||||||
|
|
||||||
asmfunction!(__relibc_internal_fork_ret: ["
|
|
||||||
// Arguments already on the stack
|
|
||||||
call {child_hook}
|
|
||||||
|
|
||||||
//TODO ldmxcsr [esp+16]
|
|
||||||
fldcw [esp+24]
|
|
||||||
|
|
||||||
xor eax, eax
|
|
||||||
|
|
||||||
.p2align 4
|
|
||||||
2:
|
|
||||||
add esp, 32
|
|
||||||
|
|
||||||
// Pop preserved registers
|
|
||||||
pop ebp
|
|
||||||
pop esi
|
|
||||||
pop edi
|
|
||||||
pop ebx
|
|
||||||
|
|
||||||
pop ebp
|
|
||||||
|
|
||||||
ret
|
|
||||||
"] <= [child_hook = sym child_hook]);
|
|
||||||
asmfunction!(__relibc_internal_sigentry: ["
|
|
||||||
// Save some registers
|
|
||||||
mov gs:[{tcb_sa_off} + {sa_tmp_esp}], esp
|
|
||||||
mov gs:[{tcb_sa_off} + {sa_tmp_eax}], eax
|
|
||||||
mov gs:[{tcb_sa_off} + {sa_tmp_edx}], edx
|
|
||||||
mov gs:[{tcb_sa_off} + {sa_tmp_ecx}], ecx
|
|
||||||
mov gs:[{tcb_sa_off} + {sa_tmp_ebx}], ebx
|
|
||||||
mov gs:[{tcb_sa_off} + {sa_tmp_edi}], edi
|
|
||||||
mov gs:[{tcb_sa_off} + {sa_tmp_esi}], esi
|
|
||||||
1:
|
|
||||||
// Read standard signal word - first for this thread
|
|
||||||
mov edx, gs:[{tcb_sc_off} + {sc_word} + 4]
|
|
||||||
mov eax, gs:[{tcb_sc_off} + {sc_word}]
|
|
||||||
and eax, edx
|
|
||||||
bsf eax, eax
|
|
||||||
jnz 9f
|
|
||||||
|
|
||||||
// Read standard signal word - for the process
|
|
||||||
lea ecx, [{pctl}]
|
|
||||||
mov eax, [ecx + {pctl_pending}]
|
|
||||||
and eax, edx
|
|
||||||
bsf eax, eax
|
|
||||||
jz 3f
|
|
||||||
|
|
||||||
// Read si_pid and si_uid, atomically.
|
|
||||||
movq gs:[{tcb_sa_off} + {sa_tmp_mm0}], mm0
|
|
||||||
movq mm0, [ecx + {pctl_sender_infos} + eax * 8]
|
|
||||||
movq gs:[{tcb_sa_off} + {sa_tmp_id_inf}], mm0
|
|
||||||
movq mm0, gs:[{tcb_sa_off} + {sa_tmp_mm0}]
|
|
||||||
|
|
||||||
// Try clearing the pending bit, otherwise retry if another thread did that first
|
|
||||||
lock btr [ecx + {pctl_pending}], eax
|
|
||||||
jnc 1b
|
|
||||||
jmp 2f
|
|
||||||
3:
|
|
||||||
// Read realtime thread and process signal word together
|
|
||||||
mov edx, [ecx + {pctl_pending} + 4]
|
|
||||||
mov eax, gs:[{tcb_sc_off} + {sc_word} + 8]
|
|
||||||
or eax, edx
|
|
||||||
and eax, gs:[{tcb_sc_off} + {sc_word} + 12]
|
|
||||||
bsf eax, eax
|
|
||||||
jz 7f // spurious signal
|
|
||||||
|
|
||||||
// If thread rather than process was specifically targeted, send the signal to it first.
|
|
||||||
bt edx, eax
|
|
||||||
jnc 8f
|
|
||||||
|
|
||||||
// SYS_CALL(fd, payload_base, payload_len, metadata_len, metadata_base)
|
|
||||||
// eax ebx ecx edx esi edi
|
|
||||||
|
|
||||||
mov ebx, [{proc_fd}]
|
|
||||||
mov ecx, gs:[0]
|
|
||||||
add ecx, {tcb_sa_off} + {sa_tmp_rt_inf}
|
|
||||||
mov [ecx], eax
|
|
||||||
mov gs:[{tcb_sa_off} + {sa_tmp_signo}], eax
|
|
||||||
mov edx, {RTINF_SIZE}
|
|
||||||
mov esi, 1
|
|
||||||
lea edi, [{proc_call}]
|
|
||||||
mov eax, {SYS_CALL}
|
|
||||||
int 0x80
|
|
||||||
test eax, eax
|
|
||||||
jnz 1b
|
|
||||||
|
|
||||||
mov eax, gs:[{tcb_sa_off} + {sa_tmp_signo}]
|
|
||||||
add eax, 32
|
|
||||||
jmp 2f
|
|
||||||
8:
|
|
||||||
add eax, 32
|
|
||||||
9:
|
|
||||||
// Read si_pid and si_uid, atomically.
|
|
||||||
movq gs:[{tcb_sa_off} + {sa_tmp_mm0}], mm0
|
|
||||||
movq mm0, gs:[{tcb_sc_off} + {sc_sender_infos} + eax * 8]
|
|
||||||
movq gs:[{tcb_sa_off} + {sa_tmp_id_inf}], mm0
|
|
||||||
movq mm0, gs:[{tcb_sa_off} + {sa_tmp_mm0}]
|
|
||||||
mov edx, eax
|
|
||||||
shr edx, 5
|
|
||||||
mov ecx, eax
|
|
||||||
and ecx, 31
|
|
||||||
lock btr gs:[{tcb_sc_off} + {sc_word} + edx * 8], ecx
|
|
||||||
|
|
||||||
add eax, 64
|
|
||||||
2:
|
|
||||||
and esp, -{STACK_ALIGN}
|
|
||||||
|
|
||||||
mov edx, eax
|
|
||||||
add edx, edx
|
|
||||||
bt dword ptr [{pctl} + {pctl_actions} + edx * 8 + 4], 28
|
|
||||||
jnc 4f
|
|
||||||
|
|
||||||
mov edx, gs:[{tcb_sa_off} + {sa_altstack_top}]
|
|
||||||
cmp esp, edx
|
|
||||||
ja 3f
|
|
||||||
|
|
||||||
cmp esp, gs:[{tcb_sa_off} + {sa_altstack_bottom}]
|
|
||||||
jnbe 4f
|
|
||||||
3:
|
|
||||||
mov esp, edx
|
|
||||||
4:
|
|
||||||
// Now that we have a stack, we can finally start populating the signal stack.
|
|
||||||
push dword ptr gs:[{tcb_sa_off} + {sa_tmp_esp}]
|
|
||||||
push dword ptr gs:[{tcb_sc_off} + {sc_saved_eip}]
|
|
||||||
push dword ptr gs:[{tcb_sc_off} + {sc_saved_eflags}]
|
|
||||||
|
|
||||||
push dword ptr gs:[{tcb_sa_off} + {sa_tmp_edx}]
|
|
||||||
push dword ptr gs:[{tcb_sa_off} + {sa_tmp_ecx}]
|
|
||||||
push dword ptr gs:[{tcb_sa_off} + {sa_tmp_eax}]
|
|
||||||
push dword ptr gs:[{tcb_sa_off} + {sa_tmp_ebx}]
|
|
||||||
push dword ptr gs:[{tcb_sa_off} + {sa_tmp_edi}]
|
|
||||||
push dword ptr gs:[{tcb_sa_off} + {sa_tmp_esi}]
|
|
||||||
push ebp
|
|
||||||
|
|
||||||
sub esp, 2 * 4 + 29 * 16
|
|
||||||
fxsave [esp]
|
|
||||||
|
|
||||||
mov [esp - 4], eax
|
|
||||||
sub esp, 48
|
|
||||||
|
|
||||||
mov ecx, esp
|
|
||||||
call {inner}
|
|
||||||
|
|
||||||
fxrstor [esp + 48]
|
|
||||||
add esp, 48 + 29 * 16 + 2 * 4
|
|
||||||
|
|
||||||
pop ebp
|
|
||||||
pop esi
|
|
||||||
pop edi
|
|
||||||
pop ebx
|
|
||||||
pop eax
|
|
||||||
pop ecx
|
|
||||||
pop edx
|
|
||||||
|
|
||||||
popfd
|
|
||||||
pop dword ptr gs:[{tcb_sa_off} + {sa_tmp_eip}]
|
|
||||||
|
|
||||||
.globl __relibc_internal_sigentry_crit_first
|
|
||||||
__relibc_internal_sigentry_crit_first:
|
|
||||||
pop esp
|
|
||||||
|
|
||||||
.globl __relibc_internal_sigentry_crit_second
|
|
||||||
__relibc_internal_sigentry_crit_second:
|
|
||||||
jmp dword ptr gs:[{tcb_sa_off} + {sa_tmp_eip}]
|
|
||||||
7:
|
|
||||||
mov eax, gs:[0]
|
|
||||||
lea esp, [eax + {tcb_sc_off} + {sc_saved_eflags}]
|
|
||||||
popfd
|
|
||||||
|
|
||||||
mov esp, gs:[{tcb_sa_off} + {sa_tmp_esp}]
|
|
||||||
|
|
||||||
mov eax, gs:[{tcb_sc_off} + {sc_saved_eip}]
|
|
||||||
mov gs:[{tcb_sa_off} + {sa_tmp_eip}], eax
|
|
||||||
|
|
||||||
mov eax, gs:[{tcb_sa_off} + {sa_tmp_eax}]
|
|
||||||
mov ebx, gs:[{tcb_sa_off} + {sa_tmp_ebx}]
|
|
||||||
mov ecx, gs:[{tcb_sa_off} + {sa_tmp_ecx}]
|
|
||||||
mov edx, gs:[{tcb_sa_off} + {sa_tmp_edx}]
|
|
||||||
mov edi, gs:[{tcb_sa_off} + {sa_tmp_edi}]
|
|
||||||
mov esi, gs:[{tcb_sa_off} + {sa_tmp_esi}]
|
|
||||||
|
|
||||||
and dword ptr gs:[{tcb_sc_off} + {sc_control}], ~1
|
|
||||||
.globl __relibc_internal_sigentry_crit_third
|
|
||||||
__relibc_internal_sigentry_crit_third:
|
|
||||||
jmp dword ptr gs:[{tcb_sa_off} + {sa_tmp_eip}]
|
|
||||||
"] <= [
|
|
||||||
inner = sym inner_fastcall,
|
|
||||||
sa_tmp_eip = const offset_of!(SigArea, tmp_eip),
|
|
||||||
sa_tmp_esp = const offset_of!(SigArea, tmp_esp),
|
|
||||||
sa_tmp_eax = const offset_of!(SigArea, tmp_eax),
|
|
||||||
sa_tmp_ebx = const offset_of!(SigArea, tmp_ebx),
|
|
||||||
sa_tmp_ecx = const offset_of!(SigArea, tmp_ecx),
|
|
||||||
sa_tmp_edx = const offset_of!(SigArea, tmp_edx),
|
|
||||||
sa_tmp_edi = const offset_of!(SigArea, tmp_edi),
|
|
||||||
sa_tmp_esi = const offset_of!(SigArea, tmp_esi),
|
|
||||||
sa_tmp_mm0 = const offset_of!(SigArea, tmp_mm0),
|
|
||||||
sa_tmp_rt_inf = const offset_of!(SigArea, tmp_rt_inf),
|
|
||||||
sa_tmp_id_inf = const offset_of!(SigArea, tmp_id_inf),
|
|
||||||
sa_tmp_signo = const offset_of!(SigArea, tmp_signo),
|
|
||||||
sa_altstack_top = const offset_of!(SigArea, altstack_top),
|
|
||||||
sa_altstack_bottom = const offset_of!(SigArea, altstack_bottom),
|
|
||||||
sc_control = const offset_of!(Sigcontrol, control_flags),
|
|
||||||
sc_saved_eflags = const offset_of!(Sigcontrol, saved_archdep_reg),
|
|
||||||
sc_saved_eip = const offset_of!(Sigcontrol, saved_ip),
|
|
||||||
sc_word = const offset_of!(Sigcontrol, word),
|
|
||||||
sc_sender_infos = const offset_of!(Sigcontrol, sender_infos),
|
|
||||||
tcb_sa_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, arch),
|
|
||||||
tcb_sc_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control),
|
|
||||||
pctl_actions = const offset_of!(SigProcControl, actions),
|
|
||||||
pctl_sender_infos = const offset_of!(SigProcControl, sender_infos),
|
|
||||||
pctl_pending = const offset_of!(SigProcControl, pending),
|
|
||||||
pctl = sym PROC_CONTROL_STRUCT,
|
|
||||||
proc_fd = sym PROC_FD,
|
|
||||||
proc_call = sym PROC_CALL,
|
|
||||||
STACK_ALIGN = const 16,
|
|
||||||
SYS_CALL = const syscall::SYS_CALL,
|
|
||||||
RTINF_SIZE = const size_of::<RtSigInfo>(),
|
|
||||||
]);
|
|
||||||
|
|
||||||
asmfunction!(__relibc_internal_rlct_clone_ret -> usize: ["
|
|
||||||
# Load registers
|
|
||||||
pop eax
|
|
||||||
|
|
||||||
sub esp, 8
|
|
||||||
|
|
||||||
mov DWORD PTR [esp], 0x00001F80
|
|
||||||
# TODO: ldmxcsr [esp]
|
|
||||||
mov WORD PTR [esp], 0x037F
|
|
||||||
fldcw [esp]
|
|
||||||
|
|
||||||
add esp, 8
|
|
||||||
|
|
||||||
# Call entry point
|
|
||||||
call eax
|
|
||||||
|
|
||||||
ret
|
|
||||||
"] <= []);
|
|
||||||
unsafe extern "C" {
|
|
||||||
fn __relibc_internal_sigentry_crit_first();
|
|
||||||
fn __relibc_internal_sigentry_crit_second();
|
|
||||||
fn __relibc_internal_sigentry_crit_third();
|
|
||||||
}
|
|
||||||
pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) -> PosixStackt {
|
|
||||||
if stack.regs.eip == __relibc_internal_sigentry_crit_first as *const () as usize {
|
|
||||||
let stack_ptr = stack.regs.esp as *const usize;
|
|
||||||
stack.regs.esp = unsafe { stack_ptr.read() };
|
|
||||||
stack.regs.eip = unsafe { stack_ptr.sub(1).read() };
|
|
||||||
} else if stack.regs.eip == __relibc_internal_sigentry_crit_second as *const () as usize
|
|
||||||
|| stack.regs.eip == __relibc_internal_sigentry_crit_third as *const () as usize
|
|
||||||
{
|
|
||||||
stack.regs.eip = area.tmp_eip;
|
|
||||||
}
|
|
||||||
PosixStackt {
|
|
||||||
sp: stack.regs.esp as *mut (),
|
|
||||||
size: 0, // TODO
|
|
||||||
flags: 0, // TODO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn arch_ret_to_sig(stack: &mut SigStack, control: &Sigcontrol) {
|
|
||||||
let orig_eip = core::mem::replace(
|
|
||||||
&mut stack.regs.eip,
|
|
||||||
__relibc_internal_sigentry as *const () as usize,
|
|
||||||
);
|
|
||||||
control.saved_ip.set(orig_eip);
|
|
||||||
control.saved_archdep_reg.set(stack.regs.eflags);
|
|
||||||
}
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub unsafe fn manually_enter_trampoline() {
|
|
||||||
let c = unsafe { &crate::Tcb::current().unwrap().os_specific.control };
|
|
||||||
c.control_flags.store(
|
|
||||||
c.control_flags.load(Ordering::Relaxed) | syscall::flag::INHIBIT_DELIVERY.bits(),
|
|
||||||
Ordering::Release,
|
|
||||||
);
|
|
||||||
c.saved_archdep_reg.set(0); // TODO: Just reset DF on x86?
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!("
|
|
||||||
call 2f
|
|
||||||
jmp 3f
|
|
||||||
2:
|
|
||||||
pop dword ptr gs:[{tcb_sc_off} + {sc_saved_eip}]
|
|
||||||
jmp __relibc_internal_sigentry
|
|
||||||
3:
|
|
||||||
",
|
|
||||||
tcb_sc_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control),
|
|
||||||
sc_saved_eip = const offset_of!(Sigcontrol, saved_ip),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Get current stack pointer, weak granularity guarantees.
|
|
||||||
pub fn current_sp() -> usize {
|
|
||||||
let sp: usize;
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!("mov {}, esp", out(reg) sp);
|
|
||||||
}
|
|
||||||
sp
|
|
||||||
}
|
|
||||||
|
|
||||||
pub static PROC_FD: SyncUnsafeCell<usize> = SyncUnsafeCell::new(usize::MAX);
|
|
||||||
static PROC_CALL: u64 = ProcCall::Sigdeq as u64;
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
pub use self::aarch64::*;
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
pub mod aarch64;
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86")]
|
|
||||||
pub use self::i686::*;
|
|
||||||
#[cfg(target_arch = "x86")]
|
|
||||||
pub mod i686;
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
pub use self::x86_64::*;
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
pub mod x86_64;
|
|
||||||
|
|
||||||
#[cfg(target_arch = "riscv64")]
|
|
||||||
pub use self::riscv64::*;
|
|
||||||
#[cfg(target_arch = "riscv64")]
|
|
||||||
pub mod riscv64;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct ForkScratchpad {
|
|
||||||
pub cur_filetable_fd: usize,
|
|
||||||
pub new_filetable_fd: usize,
|
|
||||||
pub new_proc_fd: usize,
|
|
||||||
pub new_thr_fd: usize,
|
|
||||||
}
|
|
||||||
@@ -1,661 +0,0 @@
|
|||||||
use core::cell::SyncUnsafeCell;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
Tcb,
|
|
||||||
proc::{FdGuard, FdGuardUpper, ForkArgs, fork_inner},
|
|
||||||
signal::{PosixStackt, RtSigarea, SigStack, get_sigaltstack, inner_c},
|
|
||||||
};
|
|
||||||
use core::{mem::offset_of, ptr::NonNull, sync::atomic::Ordering};
|
|
||||||
use redox_protocols::protocol::{ProcCall, RtSigInfo};
|
|
||||||
use syscall::{data::*, error::*};
|
|
||||||
|
|
||||||
use super::ForkScratchpad;
|
|
||||||
|
|
||||||
// Setup a stack starting from the very end of the address space, and then growing downwards.
|
|
||||||
pub const STACK_TOP: usize = 1 << 38;
|
|
||||||
pub const STACK_SIZE: usize = 1024 * 1024;
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct SigArea {
|
|
||||||
pub tmp_sp: u64,
|
|
||||||
pub tmp_t1: u64,
|
|
||||||
pub tmp_t2: u64,
|
|
||||||
pub tmp_t3: u64,
|
|
||||||
pub tmp_t4: u64,
|
|
||||||
pub tmp_a0: u64,
|
|
||||||
pub tmp_a1: u64,
|
|
||||||
pub tmp_a2: u64,
|
|
||||||
pub tmp_a3: u64,
|
|
||||||
pub tmp_a4: u64,
|
|
||||||
pub tmp_a7: u64,
|
|
||||||
|
|
||||||
pub pctl: usize, // TODO: remove
|
|
||||||
pub tmp_ip: u64,
|
|
||||||
pub tmp_rt_inf: RtSigInfo,
|
|
||||||
pub tmp_id_inf: u64,
|
|
||||||
pub altstack_top: usize,
|
|
||||||
pub altstack_bottom: usize,
|
|
||||||
pub disable_signals_depth: u64,
|
|
||||||
pub last_sig_was_restart: bool,
|
|
||||||
pub last_sigstack: Option<NonNull<SigStack>>,
|
|
||||||
}
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct ArchIntRegs {
|
|
||||||
pub int_regs: [u64; 31],
|
|
||||||
pub pc: u64,
|
|
||||||
pub fp_regs: [u64; 32],
|
|
||||||
pub fcsr: u32,
|
|
||||||
_pad: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deactive TLS, used before exec() on Redox to not trick target executable into thinking TLS
|
|
||||||
/// is already initialized as if it was a thread.
|
|
||||||
pub unsafe fn deactivate_tcb(open_via_dup: &FdGuardUpper) -> Result<()> {
|
|
||||||
let mut env = syscall::EnvRegisters::default();
|
|
||||||
|
|
||||||
let file = open_via_dup.dup_into_upper(b"regs/env")?;
|
|
||||||
|
|
||||||
env.tp = 0;
|
|
||||||
|
|
||||||
file.write(&mut env)?;
|
|
||||||
crate::TLS_ACTIVATED.store(false, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) -> usize {
|
|
||||||
Error::mux(fork_inner(initial_rsp, args))
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C" fn child_hook(scratchpad: &ForkScratchpad) {
|
|
||||||
let _ = crate::sys::close(scratchpad.cur_filetable_fd);
|
|
||||||
unsafe {
|
|
||||||
crate::child_hook_common(crate::ChildHookCommonArgs {
|
|
||||||
new_thr_fd: FdGuard::new(scratchpad.new_thr_fd),
|
|
||||||
new_proc_fd: if scratchpad.new_proc_fd == usize::MAX {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(FdGuard::new(scratchpad.new_proc_fd))
|
|
||||||
},
|
|
||||||
new_filetable_fd: FdGuard::new(scratchpad.new_filetable_fd)
|
|
||||||
.to_upper()
|
|
||||||
.unwrap(),
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
asmfunction!(__relibc_internal_fork_wrapper (usize) -> usize: ["
|
|
||||||
.attribute arch, \"rv64gc\" # rust bug 80608
|
|
||||||
addi sp, sp, -200
|
|
||||||
sd s0, 0(sp)
|
|
||||||
sd s1, 8(sp)
|
|
||||||
sd s2, 16(sp)
|
|
||||||
sd s3, 24(sp)
|
|
||||||
sd s4, 32(sp)
|
|
||||||
sd s5, 40(sp)
|
|
||||||
sd s6, 48(sp)
|
|
||||||
sd s7, 56(sp)
|
|
||||||
sd s8, 64(sp)
|
|
||||||
sd s9, 72(sp)
|
|
||||||
sd s10, 80(sp)
|
|
||||||
sd s11, 88(sp)
|
|
||||||
sd ra, 96(sp)
|
|
||||||
|
|
||||||
fsd fs0, 104(sp)
|
|
||||||
fsd fs1, 112(sp)
|
|
||||||
fsd fs2, 120(sp)
|
|
||||||
fsd fs3, 128(sp)
|
|
||||||
fsd fs4, 136(sp)
|
|
||||||
fsd fs5, 144(sp)
|
|
||||||
fsd fs6, 152(sp)
|
|
||||||
fsd fs7, 160(sp)
|
|
||||||
fsd fs8, 168(sp)
|
|
||||||
fsd fs9, 176(sp)
|
|
||||||
fsd fs10, 184(sp)
|
|
||||||
fsd fs11, 192(sp)
|
|
||||||
|
|
||||||
// a0 is forwarded from this function
|
|
||||||
mv a1, sp
|
|
||||||
jal {fork_impl}
|
|
||||||
|
|
||||||
ld s0, 0(sp)
|
|
||||||
ld s1, 8(sp)
|
|
||||||
ld s2, 16(sp)
|
|
||||||
ld s3, 24(sp)
|
|
||||||
ld s4, 32(sp)
|
|
||||||
ld s5, 40(sp)
|
|
||||||
ld s6, 48(sp)
|
|
||||||
ld s7, 56(sp)
|
|
||||||
ld s8, 64(sp)
|
|
||||||
ld s9, 72(sp)
|
|
||||||
ld s10, 80(sp)
|
|
||||||
ld s11, 88(sp)
|
|
||||||
ld ra, 96(sp)
|
|
||||||
|
|
||||||
fld fs0, 104(sp)
|
|
||||||
fld fs1, 112(sp)
|
|
||||||
fld fs2, 120(sp)
|
|
||||||
fld fs3, 128(sp)
|
|
||||||
fld fs4, 136(sp)
|
|
||||||
fld fs5, 144(sp)
|
|
||||||
fld fs6, 152(sp)
|
|
||||||
fld fs7, 160(sp)
|
|
||||||
fld fs8, 168(sp)
|
|
||||||
fld fs9, 176(sp)
|
|
||||||
fld fs10, 184(sp)
|
|
||||||
fld fs11, 192(sp)
|
|
||||||
|
|
||||||
addi sp, sp, 200
|
|
||||||
ret
|
|
||||||
"] <= [fork_impl = sym fork_impl]);
|
|
||||||
|
|
||||||
asmfunction!(__relibc_internal_fork_ret: ["
|
|
||||||
.attribute arch, \"rv64gc\" # rust bug 80608
|
|
||||||
|
|
||||||
# scratchpad is in a1, move to a0 for child_hook
|
|
||||||
mv a0, a1
|
|
||||||
|
|
||||||
jal {child_hook}
|
|
||||||
|
|
||||||
mv a0, zero
|
|
||||||
|
|
||||||
ld s0, 0(sp)
|
|
||||||
ld s1, 8(sp)
|
|
||||||
ld s2, 16(sp)
|
|
||||||
ld s3, 24(sp)
|
|
||||||
ld s4, 32(sp)
|
|
||||||
ld s5, 40(sp)
|
|
||||||
ld s6, 48(sp)
|
|
||||||
ld s7, 56(sp)
|
|
||||||
ld s8, 64(sp)
|
|
||||||
ld s9, 72(sp)
|
|
||||||
ld s10, 80(sp)
|
|
||||||
ld s11, 88(sp)
|
|
||||||
ld ra, 96(sp)
|
|
||||||
|
|
||||||
fld fs0, 104(sp)
|
|
||||||
fld fs1, 112(sp)
|
|
||||||
fld fs2, 120(sp)
|
|
||||||
fld fs3, 128(sp)
|
|
||||||
fld fs4, 136(sp)
|
|
||||||
fld fs5, 144(sp)
|
|
||||||
fld fs6, 152(sp)
|
|
||||||
fld fs7, 160(sp)
|
|
||||||
fld fs8, 168(sp)
|
|
||||||
fld fs9, 176(sp)
|
|
||||||
fld fs10, 184(sp)
|
|
||||||
fld fs11, 192(sp)
|
|
||||||
|
|
||||||
addi sp, sp, 200
|
|
||||||
ret
|
|
||||||
"] <= [child_hook = sym child_hook]);
|
|
||||||
|
|
||||||
asmfunction!(__relibc_internal_sigentry: ["
|
|
||||||
.attribute arch, \"rv64gc\" # rust bug 80608
|
|
||||||
// Save some registers
|
|
||||||
ld t0, -8(tp) // Tcb
|
|
||||||
sd sp, ({tcb_sa_off} + {sa_tmp_sp})(t0)
|
|
||||||
sd t1, ({tcb_sa_off} + {sa_tmp_t1})(t0)
|
|
||||||
sd t2, ({tcb_sa_off} + {sa_tmp_t2})(t0)
|
|
||||||
sd t3, ({tcb_sa_off} + {sa_tmp_t3})(t0)
|
|
||||||
sd t4, ({tcb_sa_off} + {sa_tmp_t4})(t0)
|
|
||||||
ld t4, ({tcb_sa_off} + {sa_off_pctl})(t0)
|
|
||||||
|
|
||||||
// First, select signal, always pick first available bit
|
|
||||||
99:
|
|
||||||
// Read first signal word
|
|
||||||
ld t1, ({tcb_sc_off} + {sc_word})(t0)
|
|
||||||
srli t2, t1, 32 // bitset to low word
|
|
||||||
and t1, t1, t2 // masked bitset in low word
|
|
||||||
beqz t1, 3f
|
|
||||||
|
|
||||||
// Found in first thread signal word
|
|
||||||
mv t3, x0
|
|
||||||
2: andi t2, t1, 1
|
|
||||||
bnez t2, 10f
|
|
||||||
addi t3, t3, 1
|
|
||||||
srli t1, t1, 1
|
|
||||||
j 2b
|
|
||||||
|
|
||||||
// If no unblocked thread signal was found, check for process.
|
|
||||||
// This is competitive; we need to atomically check if *we* cleared the process-wide pending
|
|
||||||
// bit, otherwise restart.
|
|
||||||
3: lw t1, {pctl_off_pending}(t4)
|
|
||||||
and t1, t1, t2
|
|
||||||
beqz t1, 3f
|
|
||||||
// Found in first process signal word
|
|
||||||
li t3, -1
|
|
||||||
2: andi t2, t1, 1
|
|
||||||
addi t3, t3, 1
|
|
||||||
srli t1, t1, 1
|
|
||||||
beqz t2, 2b
|
|
||||||
slli t1, t3, 3 // * 8 == size_of SenderInfo
|
|
||||||
add t1, t1, t4
|
|
||||||
ld t1, {pctl_off_sender_infos}(t1)
|
|
||||||
sd t1, ({tcb_sa_off} + {sa_tmp_id_inf})(t0)
|
|
||||||
li t1, 1
|
|
||||||
sll t1, t1, t3
|
|
||||||
not t1, t1
|
|
||||||
addi t2, t4, {pctl_off_pending}
|
|
||||||
amoand.w.aq t2, t1, (t2)
|
|
||||||
and t1, t1, t2
|
|
||||||
bne t1, t2, 9f
|
|
||||||
|
|
||||||
3:
|
|
||||||
// Read second signal word - both process and thread simultaneously.
|
|
||||||
// This must be done since POSIX requires low realtime signals to be picked first.
|
|
||||||
ld t1, ({tcb_sc_off} + {sc_word} + 8)(t0)
|
|
||||||
lw t2, ({pctl_off_pending} + 4)(t4)
|
|
||||||
or t4, t1, t2
|
|
||||||
srli t2, t1, 32
|
|
||||||
and t4, t2, t4
|
|
||||||
beqz t4, 7f
|
|
||||||
li t3, -1
|
|
||||||
2: andi t2, t4, 1
|
|
||||||
addi t3, t3, 1
|
|
||||||
srli t4, t4, 1
|
|
||||||
beqz t2, 2b
|
|
||||||
li t2, 1
|
|
||||||
sll t2, t2, t3
|
|
||||||
and t1, t1, t2
|
|
||||||
addi t3, t3, 32
|
|
||||||
bnez t1, 10f // thread signal
|
|
||||||
|
|
||||||
// otherwise, try (competitively) dequeueing realtime signal
|
|
||||||
|
|
||||||
// SYS_CALL(fd, payload_base, payload_len, metadata_len, metadata_base)
|
|
||||||
// a7 a0 a1 a2 a3 a4
|
|
||||||
|
|
||||||
// TODO: This SYS_CALL invocation has not yet been tested due to toolchain issues.
|
|
||||||
|
|
||||||
sd a0, ({tcb_sa_off} + {sa_tmp_a0})(t0)
|
|
||||||
sd a1, ({tcb_sa_off} + {sa_tmp_a1})(t0)
|
|
||||||
sd a2, ({tcb_sa_off} + {sa_tmp_a2})(t0)
|
|
||||||
sd a3, ({tcb_sa_off} + {sa_tmp_a3})(t0)
|
|
||||||
sd a4, ({tcb_sa_off} + {sa_tmp_a4})(t0)
|
|
||||||
sd a7, ({tcb_sa_off} + {sa_tmp_a7})(t0)
|
|
||||||
li a7, {SYS_CALL}
|
|
||||||
addi a2, t3, -32
|
|
||||||
add a1, t0, {tcb_sa_off} + {sa_tmp_rt_inf} // out pointer of dequeued realtime sig
|
|
||||||
sd a2, (a1)
|
|
||||||
li a2, {RTINF_SIZE}
|
|
||||||
li a3, 1
|
|
||||||
1337:
|
|
||||||
auipc a4, %pcrel_hi({proc_fd})
|
|
||||||
addi a4, a4, %pcrel_lo(1337b)
|
|
||||||
ecall
|
|
||||||
ld a3, ({tcb_sa_off} + {sa_tmp_a3})(t0)
|
|
||||||
ld a4, ({tcb_sa_off} + {sa_tmp_a4})(t0)
|
|
||||||
bnez a0, 99b // assumes error can only be EAGAIN
|
|
||||||
j 9f
|
|
||||||
|
|
||||||
10: // thread signal. t3 holds signal number
|
|
||||||
srli t1, t3, 5
|
|
||||||
bnez t1, 2f // FIXME senderinfo?
|
|
||||||
sll t2, t3, 3 // * 8 == size_of SenderInfo
|
|
||||||
add t2, t2, t0
|
|
||||||
ld t2, ({tcb_sc_off} + {sc_sender_infos})(t2)
|
|
||||||
sd t2, ({tcb_sa_off} + {sa_tmp_id_inf})(t0)
|
|
||||||
2: andi t4, t3, 31
|
|
||||||
li t2, 1
|
|
||||||
sll t2, t2, t4
|
|
||||||
not t2, t2
|
|
||||||
sll t1, t1, 3
|
|
||||||
add t1, t1, t0
|
|
||||||
addi t1, t1, {tcb_sc_off} + {sc_word}
|
|
||||||
amoand.w.aq x0, t2, (t1)
|
|
||||||
addi t3, t3, 64 // indicate signal was targeted at thread
|
|
||||||
|
|
||||||
9: // process signal t3 holds signal number
|
|
||||||
|
|
||||||
// By now we have selected a signal, stored in eax (6-bit). We now need to choose whether or
|
|
||||||
// not to switch to the alternate signal stack. If SA_ONSTACK is clear for this signal, then
|
|
||||||
// skip the sigaltstack logic.
|
|
||||||
ld t4, ({tcb_sa_off} + {sa_off_pctl})(t0)
|
|
||||||
andi t1, t3, 63
|
|
||||||
slli t1, t1, 4 // * 16 == size_of RawAction
|
|
||||||
add t1, t1, t4
|
|
||||||
ld t1, {pctl_off_actions}(t1)
|
|
||||||
slli t1, t1, 63-58 // SA_ONSTACK in sign bit
|
|
||||||
bgez t1, 3f
|
|
||||||
|
|
||||||
// If current RSP is above altstack region, switch to altstack
|
|
||||||
ld t1, ({tcb_sa_off} + {sa_altstack_top})(t0)
|
|
||||||
bgtu sp, t1, 2f
|
|
||||||
ld t2, ({tcb_sa_off} + {sa_altstack_bottom})(t0)
|
|
||||||
bgtu sp, t3, 3f
|
|
||||||
2: mv sp, t1
|
|
||||||
3:
|
|
||||||
// form mcontext on stack
|
|
||||||
addi sp, sp, -33 * 8
|
|
||||||
fsd f0, (0 * 8)(sp)
|
|
||||||
fsd f1, (1 * 8)(sp)
|
|
||||||
fsd f2, (2 * 8)(sp)
|
|
||||||
fsd f3, (3 * 8)(sp)
|
|
||||||
fsd f4, (4 * 8)(sp)
|
|
||||||
fsd f5, (5 * 8)(sp)
|
|
||||||
fsd f6, (6 * 8)(sp)
|
|
||||||
fsd f7, (7 * 8)(sp)
|
|
||||||
fsd f8, (8 * 8)(sp)
|
|
||||||
fsd f9, (9 * 8)(sp)
|
|
||||||
fsd f10, (10 * 8)(sp)
|
|
||||||
fsd f11, (11 * 8)(sp)
|
|
||||||
fsd f12, (12 * 8)(sp)
|
|
||||||
fsd f13, (13 * 8)(sp)
|
|
||||||
fsd f14, (14 * 8)(sp)
|
|
||||||
fsd f15, (15 * 8)(sp)
|
|
||||||
fsd f16, (16 * 8)(sp)
|
|
||||||
fsd f17, (17 * 8)(sp)
|
|
||||||
fsd f18, (18 * 8)(sp)
|
|
||||||
fsd f19, (19 * 8)(sp)
|
|
||||||
fsd f20, (20 * 8)(sp)
|
|
||||||
fsd f21, (21 * 8)(sp)
|
|
||||||
fsd f22, (22 * 8)(sp)
|
|
||||||
fsd f23, (23 * 8)(sp)
|
|
||||||
fsd f24, (24 * 8)(sp)
|
|
||||||
fsd f25, (25 * 8)(sp)
|
|
||||||
fsd f26, (26 * 8)(sp)
|
|
||||||
fsd f27, (27 * 8)(sp)
|
|
||||||
fsd f28, (28 * 8)(sp)
|
|
||||||
fsd f29, (29 * 8)(sp)
|
|
||||||
fsd f30, (30 * 8)(sp)
|
|
||||||
fsd f31, (31 * 8)(sp)
|
|
||||||
csrr t1, fcsr
|
|
||||||
sw t1, (32 * 8)(sp)
|
|
||||||
|
|
||||||
addi sp, sp, -32 * 8
|
|
||||||
sd x1, 0(sp)
|
|
||||||
ld t1, ({tcb_sa_off} + {sa_tmp_sp})(t0)
|
|
||||||
sd t1, (1 * 8)(sp) // x2 is sp
|
|
||||||
sd x3, (2 * 8)(sp)
|
|
||||||
sd x4, (3 * 8)(sp)
|
|
||||||
ld t1, ({tcb_sc_off} + {sc_saved_t0})(t0)
|
|
||||||
sd t1, (4 * 8)(sp) // x5 is t0
|
|
||||||
ld t1, ({tcb_sa_off} + {sa_tmp_t1})(t0)
|
|
||||||
sd t1, (5 * 8)(sp) // x6 is t1
|
|
||||||
ld t1, ({tcb_sa_off} + {sa_tmp_t2})(t0)
|
|
||||||
sd t1, (6 * 8)(sp) // x7 is t2
|
|
||||||
sd x8, (7 * 8)(sp)
|
|
||||||
sd x9, (8 * 8)(sp)
|
|
||||||
sd x10, (9 * 8)(sp)
|
|
||||||
sd x11, (10 * 8)(sp)
|
|
||||||
sd x12, (11 * 8)(sp)
|
|
||||||
sd x13, (12 * 8)(sp)
|
|
||||||
sd x14, (13 * 8)(sp)
|
|
||||||
sd x15, (14 * 8)(sp)
|
|
||||||
sd x16, (15 * 8)(sp)
|
|
||||||
sd x17, (16 * 8)(sp)
|
|
||||||
sd x18, (17 * 8)(sp)
|
|
||||||
sd x19, (18 * 8)(sp)
|
|
||||||
sd x20, (19 * 8)(sp)
|
|
||||||
sd x21, (20 * 8)(sp)
|
|
||||||
sd x22, (21 * 8)(sp)
|
|
||||||
sd x23, (22 * 8)(sp)
|
|
||||||
sd x24, (23 * 8)(sp)
|
|
||||||
sd x25, (24 * 8)(sp)
|
|
||||||
sd x26, (25 * 8)(sp)
|
|
||||||
sd x27, (26 * 8)(sp)
|
|
||||||
ld t1, ({tcb_sa_off} + {sa_tmp_t3})(t0)
|
|
||||||
sd t1, (27 * 8)(sp) // t3 is x28
|
|
||||||
ld t1, ({tcb_sa_off} + {sa_tmp_t4})(t0)
|
|
||||||
sd t1, (28 * 8)(sp) // t4 is x29
|
|
||||||
sd x30, (29 * 8)(sp)
|
|
||||||
sd x31, (30 * 8)(sp)
|
|
||||||
ld t1, ({tcb_sc_off} + {sc_saved_ip})(t0)
|
|
||||||
sd t1, (31 * 8)(sp)
|
|
||||||
|
|
||||||
// form ucontext
|
|
||||||
addi sp, sp, -64
|
|
||||||
sw t3, 60(sp)
|
|
||||||
|
|
||||||
mv t0, sp
|
|
||||||
jal {inner}
|
|
||||||
|
|
||||||
addi sp, sp, 64
|
|
||||||
|
|
||||||
addi t0, sp, 32 * 8
|
|
||||||
fld f0, (0 * 8)(t0)
|
|
||||||
fld f1, (1 * 8)(t0)
|
|
||||||
fld f2, (2 * 8)(t0)
|
|
||||||
fld f3, (3 * 8)(t0)
|
|
||||||
fld f4, (4 * 8)(t0)
|
|
||||||
fld f5, (5 * 8)(t0)
|
|
||||||
fld f6, (6 * 8)(t0)
|
|
||||||
fld f7, (7 * 8)(t0)
|
|
||||||
fld f8, (8 * 8)(t0)
|
|
||||||
fld f9, (9 * 8)(t0)
|
|
||||||
fld f10, (10 * 8)(t0)
|
|
||||||
fld f11, (11 * 8)(t0)
|
|
||||||
fld f12, (12 * 8)(t0)
|
|
||||||
fld f13, (13 * 8)(t0)
|
|
||||||
fld f14, (14 * 8)(t0)
|
|
||||||
fld f15, (15 * 8)(t0)
|
|
||||||
fld f16, (16 * 8)(t0)
|
|
||||||
fld f17, (17 * 8)(t0)
|
|
||||||
fld f18, (18 * 8)(t0)
|
|
||||||
fld f19, (19 * 8)(t0)
|
|
||||||
fld f20, (20 * 8)(t0)
|
|
||||||
fld f21, (21 * 8)(t0)
|
|
||||||
fld f22, (22 * 8)(t0)
|
|
||||||
fld f23, (23 * 8)(t0)
|
|
||||||
fld f24, (24 * 8)(t0)
|
|
||||||
fld f25, (25 * 8)(t0)
|
|
||||||
fld f26, (26 * 8)(t0)
|
|
||||||
fld f27, (27 * 8)(t0)
|
|
||||||
fld f28, (28 * 8)(t0)
|
|
||||||
fld f29, (29 * 8)(t0)
|
|
||||||
fld f30, (30 * 8)(t0)
|
|
||||||
fld f31, (31 * 8)(t0)
|
|
||||||
lw t1, (32 * 8)(t0)
|
|
||||||
csrw fcsr, t1
|
|
||||||
|
|
||||||
ld x1, 0(sp)
|
|
||||||
// skip sp
|
|
||||||
// skip gp
|
|
||||||
ld x4, (3 * 8)(sp)
|
|
||||||
ld x5, (4 * 8)(sp)
|
|
||||||
ld x6, (5 * 8)(sp)
|
|
||||||
ld x7, (6 * 8)(sp)
|
|
||||||
ld x8, (7 * 8)(sp)
|
|
||||||
ld x9, (8 * 8)(sp)
|
|
||||||
ld x10, (9 * 8)(sp)
|
|
||||||
ld x11, (10 * 8)(sp)
|
|
||||||
ld x12, (11 * 8)(sp)
|
|
||||||
ld x13, (12 * 8)(sp)
|
|
||||||
ld x14, (13 * 8)(sp)
|
|
||||||
ld x15, (14 * 8)(sp)
|
|
||||||
ld x16, (15 * 8)(sp)
|
|
||||||
ld x17, (16 * 8)(sp)
|
|
||||||
ld x18, (17 * 8)(sp)
|
|
||||||
ld x19, (18 * 8)(sp)
|
|
||||||
ld x20, (19 * 8)(sp)
|
|
||||||
ld x21, (20 * 8)(sp)
|
|
||||||
ld x22, (21 * 8)(sp)
|
|
||||||
ld x23, (22 * 8)(sp)
|
|
||||||
ld x24, (23 * 8)(sp)
|
|
||||||
ld x25, (24 * 8)(sp)
|
|
||||||
ld x26, (25 * 8)(sp)
|
|
||||||
ld x27, (26 * 8)(sp)
|
|
||||||
ld x28, (27 * 8)(sp)
|
|
||||||
ld x29, (28 * 8)(sp)
|
|
||||||
ld x30, (29 * 8)(sp)
|
|
||||||
ld x31, (30 * 8)(sp)
|
|
||||||
ld gp, (31 * 8)(sp) // new IP; this clobbers register x3/gp which is ABI reserved
|
|
||||||
.global __relibc_internal_sigentry_crit_first
|
|
||||||
__relibc_internal_sigentry_crit_first:
|
|
||||||
ld sp, (1 * 8)(sp)
|
|
||||||
.global __relibc_internal_sigentry_crit_second
|
|
||||||
__relibc_internal_sigentry_crit_second:
|
|
||||||
jr gp
|
|
||||||
7:
|
|
||||||
// A spurious signal occurred. Signals are still disabled here, but will need to be re-enabled.
|
|
||||||
|
|
||||||
// restore stack
|
|
||||||
ld sp, ({tcb_sa_off} + {sa_tmp_sp})(t0)
|
|
||||||
|
|
||||||
// move saved IP away from control, allowing arch_pre to save us if interrupted.
|
|
||||||
ld t1, ({tcb_sc_off} + {sc_saved_ip})(t0)
|
|
||||||
sd t1, ({tcb_sa_off} + {sa_tmp_ip})(t0)
|
|
||||||
|
|
||||||
// restore regs
|
|
||||||
ld t2, ({tcb_sa_off} + {sa_tmp_t2})(t0)
|
|
||||||
ld t3, ({tcb_sa_off} + {sa_tmp_t3})(t0)
|
|
||||||
ld t4, ({tcb_sa_off} + {sa_tmp_t4})(t0)
|
|
||||||
|
|
||||||
// move saved t0 away from control as well
|
|
||||||
mv t1, t0
|
|
||||||
ld t0, ({tcb_sc_off} + {sc_saved_t0})(t0)
|
|
||||||
|
|
||||||
// Re-enable signals. This code can be interrupted after this signal, so we need to define
|
|
||||||
// 'crit_third'.
|
|
||||||
ld gp, ({tcb_sc_off} + {sc_control})(t1)
|
|
||||||
andi gp, gp, ~1
|
|
||||||
sd gp, ({tcb_sc_off} + {sc_control})(t1)
|
|
||||||
|
|
||||||
.globl __relibc_internal_sigentry_crit_third
|
|
||||||
__relibc_internal_sigentry_crit_third:
|
|
||||||
ld gp, ({tcb_sa_off} + {sa_tmp_ip})(t1)
|
|
||||||
.globl __relibc_internal_sigentry_crit_fourth
|
|
||||||
__relibc_internal_sigentry_crit_fourth:
|
|
||||||
ld t1, ({tcb_sa_off} + {sa_tmp_t1})(t1)
|
|
||||||
.globl __relibc_internal_sigentry_crit_fifth
|
|
||||||
__relibc_internal_sigentry_crit_fifth:
|
|
||||||
jr gp
|
|
||||||
"] <= [
|
|
||||||
tcb_sc_off = const (offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control)),
|
|
||||||
sc_word = const offset_of!(Sigcontrol, word),
|
|
||||||
sc_saved_t0 = const offset_of!(Sigcontrol, saved_archdep_reg),
|
|
||||||
sc_saved_ip = const offset_of!(Sigcontrol, saved_ip),
|
|
||||||
sc_sender_infos = const offset_of!(Sigcontrol, sender_infos),
|
|
||||||
sc_control = const offset_of!(Sigcontrol, control_flags),
|
|
||||||
|
|
||||||
tcb_sa_off = const (offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, arch)),
|
|
||||||
sa_off_pctl = const offset_of!(SigArea, pctl),
|
|
||||||
sa_tmp_sp = const offset_of!(SigArea, tmp_sp),
|
|
||||||
sa_tmp_t1 = const offset_of!(SigArea, tmp_t1),
|
|
||||||
sa_tmp_t2 = const offset_of!(SigArea, tmp_t2),
|
|
||||||
sa_tmp_t3 = const offset_of!(SigArea, tmp_t3),
|
|
||||||
sa_tmp_t4 = const offset_of!(SigArea, tmp_t4),
|
|
||||||
sa_tmp_a0 = const offset_of!(SigArea, tmp_a0),
|
|
||||||
sa_tmp_a1 = const offset_of!(SigArea, tmp_a1),
|
|
||||||
sa_tmp_a2 = const offset_of!(SigArea, tmp_a2),
|
|
||||||
sa_tmp_a3 = const offset_of!(SigArea, tmp_a3),
|
|
||||||
sa_tmp_a4 = const offset_of!(SigArea, tmp_a4),
|
|
||||||
sa_tmp_a7 = const offset_of!(SigArea, tmp_a7),
|
|
||||||
sa_tmp_ip = const offset_of!(SigArea, tmp_ip),
|
|
||||||
sa_tmp_id_inf = const offset_of!(SigArea, tmp_id_inf),
|
|
||||||
sa_tmp_rt_inf = const offset_of!(SigArea, tmp_rt_inf),
|
|
||||||
sa_altstack_top = const offset_of!(SigArea, altstack_top),
|
|
||||||
sa_altstack_bottom = const offset_of!(SigArea, altstack_bottom),
|
|
||||||
|
|
||||||
pctl_off_actions = const offset_of!(SigProcControl, actions),
|
|
||||||
inner = sym inner_c,
|
|
||||||
pctl_off_pending = const offset_of!(SigProcControl, pending),
|
|
||||||
pctl_off_sender_infos = const offset_of!(SigProcControl, sender_infos),
|
|
||||||
SYS_CALL = const syscall::SYS_CALL,
|
|
||||||
RTINF_SIZE = const size_of::<RtSigInfo>(),
|
|
||||||
proc_fd = sym PROC_FD,
|
|
||||||
]);
|
|
||||||
|
|
||||||
asmfunction!(__relibc_internal_rlct_clone_ret: ["
|
|
||||||
ld t0, 0(sp)
|
|
||||||
ld a0, 8(sp)
|
|
||||||
ld a1, 16(sp)
|
|
||||||
ld a2, 24(sp)
|
|
||||||
ld a3, 32(sp)
|
|
||||||
ld a4, 40(sp)
|
|
||||||
ld a5, 48(sp)
|
|
||||||
addi sp, sp, 56
|
|
||||||
|
|
||||||
jalr t0
|
|
||||||
ret
|
|
||||||
"] <= []);
|
|
||||||
|
|
||||||
pub fn current_sp() -> usize {
|
|
||||||
let sp: usize;
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!(
|
|
||||||
"mv {}, sp",
|
|
||||||
out(reg) sp,
|
|
||||||
options(nomem));
|
|
||||||
}
|
|
||||||
sp
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn manually_enter_trampoline() {
|
|
||||||
let ctl = unsafe { &Tcb::current().unwrap().os_specific.control };
|
|
||||||
|
|
||||||
ctl.control_flags.store(
|
|
||||||
ctl.control_flags.load(Ordering::Relaxed) | syscall::flag::INHIBIT_DELIVERY.bits(),
|
|
||||||
Ordering::Release,
|
|
||||||
);
|
|
||||||
ctl.saved_archdep_reg.set(0);
|
|
||||||
let ip_location = &ctl.saved_ip as *const _ as usize;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!("
|
|
||||||
jal 2f
|
|
||||||
j 3f
|
|
||||||
2:
|
|
||||||
sd ra, 0(t0)
|
|
||||||
la t0, __relibc_internal_sigentry
|
|
||||||
jalr x0, t0
|
|
||||||
3:
|
|
||||||
",
|
|
||||||
inout("t0") ip_location => _, out("ra") _);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "C" {
|
|
||||||
fn __relibc_internal_sigentry_crit_first();
|
|
||||||
fn __relibc_internal_sigentry_crit_second();
|
|
||||||
fn __relibc_internal_sigentry_crit_third();
|
|
||||||
fn __relibc_internal_sigentry_crit_fourth();
|
|
||||||
fn __relibc_internal_sigentry_crit_fifth();
|
|
||||||
}
|
|
||||||
pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) -> PosixStackt {
|
|
||||||
// It is impossible to update SP and PC atomically. Instead, we abuse the fact that
|
|
||||||
// signals are disabled in the prologue of the signal trampoline, which allows us to emulate
|
|
||||||
// atomicity inside the critical section, consisting of one instruction at 'crit_first', and
|
|
||||||
// one at 'crit_second', see asm.
|
|
||||||
|
|
||||||
if stack.regs.pc == __relibc_internal_sigentry_crit_first as *const () as u64 {
|
|
||||||
// Reexecute 'ld sp, (1 * 8)(sp)'
|
|
||||||
let stack_ptr = stack.regs.int_regs[1] as *const u64; // x2
|
|
||||||
stack.regs.int_regs[1] = unsafe { stack_ptr.add(1).read() };
|
|
||||||
// and 'jr gp' steps.
|
|
||||||
stack.regs.pc = stack.regs.int_regs[2];
|
|
||||||
} else if stack.regs.pc == __relibc_internal_sigentry_crit_second as *const () as u64
|
|
||||||
|| stack.regs.pc == __relibc_internal_sigentry_crit_fifth as *const () as u64
|
|
||||||
{
|
|
||||||
// just reexecute the jump
|
|
||||||
stack.regs.pc = stack.regs.int_regs[2];
|
|
||||||
} else if stack.regs.pc == __relibc_internal_sigentry_crit_third as *const () as u64 {
|
|
||||||
// ld gp, ({tcb_sa_off} + {sa_tmp_ip})(t1)
|
|
||||||
stack.regs.int_regs[2] = area.tmp_ip;
|
|
||||||
// ld t1, ({tcb_sa_off} + {sa_tmp_t1})(t1)
|
|
||||||
stack.regs.int_regs[5] = area.tmp_t1;
|
|
||||||
// j gp
|
|
||||||
stack.regs.pc = stack.regs.int_regs[2];
|
|
||||||
} else if stack.regs.pc == __relibc_internal_sigentry_crit_fourth as *const () as u64 {
|
|
||||||
// ld t1, ({tcb_sa_off} + {sa_tmp_t1})(t1)
|
|
||||||
stack.regs.int_regs[5] = area.tmp_t1;
|
|
||||||
// jr gp
|
|
||||||
stack.regs.pc = stack.regs.int_regs[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
get_sigaltstack(area, stack.regs.int_regs[1] as usize).into()
|
|
||||||
}
|
|
||||||
pub fn arch_ret_to_sig(stack: &mut SigStack, control: &Sigcontrol) {
|
|
||||||
let orig_pc = core::mem::replace(
|
|
||||||
&mut stack.regs.pc,
|
|
||||||
__relibc_internal_sigentry as *const () as u64,
|
|
||||||
);
|
|
||||||
control.saved_ip.set(orig_pc as usize);
|
|
||||||
control
|
|
||||||
.saved_archdep_reg
|
|
||||||
.set(stack.regs.int_regs[4] as usize); // t0
|
|
||||||
}
|
|
||||||
pub(crate) static PROC_FD: SyncUnsafeCell<usize> = SyncUnsafeCell::new(usize::MAX);
|
|
||||||
static PROC_CALL: [usize; 1] = [ProcCall::Sigdeq as usize];
|
|
||||||
@@ -1,535 +0,0 @@
|
|||||||
use core::{
|
|
||||||
cell::SyncUnsafeCell,
|
|
||||||
mem::offset_of,
|
|
||||||
ptr::NonNull,
|
|
||||||
sync::atomic::{AtomicU8, Ordering},
|
|
||||||
};
|
|
||||||
|
|
||||||
use syscall::{
|
|
||||||
data::{SigProcControl, Sigcontrol},
|
|
||||||
error::*,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
Tcb,
|
|
||||||
proc::{FdGuard, FdGuardUpper, ForkArgs, fork_inner},
|
|
||||||
signal::{PROC_CONTROL_STRUCT, PosixStackt, RtSigarea, SigStack, get_sigaltstack, inner_c},
|
|
||||||
};
|
|
||||||
use redox_protocols::protocol::{ProcCall, RtSigInfo};
|
|
||||||
|
|
||||||
use super::ForkScratchpad;
|
|
||||||
|
|
||||||
// Setup a stack starting from the very end of the address space, and then growing downwards.
|
|
||||||
pub const STACK_TOP: usize = 1 << 47;
|
|
||||||
pub const STACK_SIZE: usize = 8 * 1024 * 1024;
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct SigArea {
|
|
||||||
pub tmp_rip: usize,
|
|
||||||
pub tmp_rsp: usize,
|
|
||||||
pub tmp_rax: usize,
|
|
||||||
pub tmp_rdx: usize,
|
|
||||||
pub tmp_rdi: usize,
|
|
||||||
pub tmp_rsi: usize,
|
|
||||||
pub tmp_r8: usize,
|
|
||||||
pub tmp_r10: usize,
|
|
||||||
pub tmp_r12: usize,
|
|
||||||
pub tmp_rt_inf: RtSigInfo,
|
|
||||||
pub tmp_id_inf: u64,
|
|
||||||
|
|
||||||
pub altstack_top: usize,
|
|
||||||
pub altstack_bottom: usize,
|
|
||||||
pub disable_signals_depth: u64,
|
|
||||||
pub last_sig_was_restart: bool,
|
|
||||||
pub last_sigstack: Option<NonNull<SigStack>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C, align(16))]
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct ArchIntRegs {
|
|
||||||
pub ymm_upper: [u128; 16],
|
|
||||||
pub fxsave: [u128; 29],
|
|
||||||
pub r15: usize, // fxsave "available" +0
|
|
||||||
pub r14: usize, // available +8
|
|
||||||
pub r13: usize, // available +16
|
|
||||||
pub r12: usize, // available +24
|
|
||||||
pub rbp: usize, // available +32
|
|
||||||
pub rbx: usize, // available +40
|
|
||||||
pub r11: usize, // outside fxsave, and so on
|
|
||||||
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 rflags: usize,
|
|
||||||
pub rip: usize,
|
|
||||||
pub rsp: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Deactive TLS, used before exec() on Redox to not trick target executable into thinking TLS
|
|
||||||
/// is already initialized as if it was a thread.
|
|
||||||
pub unsafe fn deactivate_tcb(open_via_dup: &FdGuardUpper) -> Result<()> {
|
|
||||||
let mut env = syscall::EnvRegisters::default();
|
|
||||||
|
|
||||||
let file = open_via_dup.dup_into_upper(b"regs/env")?;
|
|
||||||
|
|
||||||
env.fsbase = 0;
|
|
||||||
env.gsbase = 0;
|
|
||||||
|
|
||||||
file.write(&env)?;
|
|
||||||
crate::TLS_ACTIVATED.store(false, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe extern "sysv64" fn fork_impl(args: &ForkArgs, initial_rsp: *mut usize) -> usize {
|
|
||||||
Error::mux(fork_inner(initial_rsp, args))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unsafe_op_in_unsafe_fn)]
|
|
||||||
unsafe extern "sysv64" fn child_hook(scratchpad: &ForkScratchpad) {
|
|
||||||
let _ = crate::sys::close(scratchpad.cur_filetable_fd);
|
|
||||||
crate::child_hook_common(crate::ChildHookCommonArgs {
|
|
||||||
new_thr_fd: FdGuard::new(scratchpad.new_thr_fd),
|
|
||||||
new_proc_fd: if scratchpad.new_proc_fd == usize::MAX {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(FdGuard::new(scratchpad.new_proc_fd))
|
|
||||||
},
|
|
||||||
new_filetable_fd: FdGuard::new(scratchpad.new_filetable_fd)
|
|
||||||
.to_upper()
|
|
||||||
.unwrap(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
asmfunction!(__relibc_internal_fork_wrapper (usize) -> usize: ["
|
|
||||||
push rbp
|
|
||||||
mov rbp, rsp
|
|
||||||
|
|
||||||
push rbx
|
|
||||||
push rbp
|
|
||||||
push r12
|
|
||||||
push r13
|
|
||||||
push r14
|
|
||||||
push r15
|
|
||||||
|
|
||||||
sub rsp, 16
|
|
||||||
|
|
||||||
stmxcsr [rsp+16]
|
|
||||||
fnstcw [rsp+8]
|
|
||||||
|
|
||||||
// rdi: &ForkArgs
|
|
||||||
// rsi: initial_rsp
|
|
||||||
mov rsi, rsp
|
|
||||||
call {fork_impl}
|
|
||||||
|
|
||||||
add rsp, 64
|
|
||||||
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
|
|
||||||
"] <= [fork_impl = sym fork_impl]);
|
|
||||||
asmfunction!(__relibc_internal_fork_ret: ["
|
|
||||||
# scratchpad is in rsi, move to rdi for child_hook
|
|
||||||
mov rdi, rsi
|
|
||||||
|
|
||||||
call {child_hook}
|
|
||||||
|
|
||||||
ldmxcsr [rsp + 16]
|
|
||||||
mov rcx, [rsp + 8]
|
|
||||||
|
|
||||||
xor rax, rax
|
|
||||||
|
|
||||||
add rsp, 16
|
|
||||||
pop r15
|
|
||||||
pop r14
|
|
||||||
pop r13
|
|
||||||
pop r12
|
|
||||||
pop rbp
|
|
||||||
pop rbx
|
|
||||||
|
|
||||||
pop rbp
|
|
||||||
ret
|
|
||||||
"] <= [child_hook = sym child_hook]);
|
|
||||||
asmfunction!(__relibc_internal_rlct_clone_ret: ["
|
|
||||||
# Load registers
|
|
||||||
pop rax
|
|
||||||
pop rdi
|
|
||||||
pop rsi
|
|
||||||
pop rdx
|
|
||||||
pop rcx
|
|
||||||
pop r8
|
|
||||||
pop r9
|
|
||||||
|
|
||||||
mov DWORD PTR [rsp - 8], 0x00001F80
|
|
||||||
ldmxcsr [rsp - 8]
|
|
||||||
mov WORD PTR [rsp - 8], 0x037F
|
|
||||||
fldcw [rsp - 8]
|
|
||||||
|
|
||||||
# Call entry point
|
|
||||||
call rax
|
|
||||||
|
|
||||||
ret
|
|
||||||
"] <= []);
|
|
||||||
|
|
||||||
asmfunction!(__relibc_internal_sigentry: ["
|
|
||||||
// Save some registers
|
|
||||||
mov fs:[{tcb_sa_off} + {sa_tmp_rsp}], rsp
|
|
||||||
mov fs:[{tcb_sa_off} + {sa_tmp_rax}], rax
|
|
||||||
mov fs:[{tcb_sa_off} + {sa_tmp_rdx}], rdx
|
|
||||||
mov fs:[{tcb_sa_off} + {sa_tmp_rdi}], rdi
|
|
||||||
mov fs:[{tcb_sa_off} + {sa_tmp_rsi}], rsi
|
|
||||||
mov fs:[{tcb_sa_off} + {sa_tmp_r8}], r8
|
|
||||||
mov fs:[{tcb_sa_off} + {sa_tmp_r10}], r10
|
|
||||||
mov fs:[{tcb_sa_off} + {sa_tmp_r12}], r12
|
|
||||||
|
|
||||||
// First, select signal, always pick first available bit
|
|
||||||
1:
|
|
||||||
// Read standard signal word - first targeting this thread
|
|
||||||
mov rax, fs:[{tcb_sc_off} + {sc_word}]
|
|
||||||
mov rdx, rax
|
|
||||||
shr rdx, 32
|
|
||||||
and eax, edx
|
|
||||||
bsf eax, eax
|
|
||||||
jnz 2f
|
|
||||||
|
|
||||||
// If no unblocked thread signal was found, check for process.
|
|
||||||
// This is competitive; we need to atomically check if *we* cleared the process-wide pending
|
|
||||||
// bit, otherwise restart.
|
|
||||||
mov eax, [rip + {pctl} + {pctl_off_pending}]
|
|
||||||
and eax, edx
|
|
||||||
bsf eax, eax
|
|
||||||
jz 8f
|
|
||||||
lea rdi, [rip + {pctl} + {pctl_off_sender_infos}]
|
|
||||||
mov rdi, [rdi + rax * 8]
|
|
||||||
lock btr [rip + {pctl} + {pctl_off_pending}], eax
|
|
||||||
mov fs:[{tcb_sa_off} + {sa_tmp_id_inf}], rdi
|
|
||||||
jc 9f
|
|
||||||
8:
|
|
||||||
// Read second signal word - both process and thread simultaneously.
|
|
||||||
// This must be done since POSIX requires low realtime signals to be picked first.
|
|
||||||
mov edx, fs:[{tcb_sc_off} + {sc_word} + 8]
|
|
||||||
mov eax, [rip + {pctl} + {pctl_off_pending} + 4]
|
|
||||||
or eax, edx
|
|
||||||
and eax, fs:[{tcb_sc_off} + {sc_word} + 12]
|
|
||||||
bsf eax, eax
|
|
||||||
jz 7f
|
|
||||||
|
|
||||||
bt edx, eax // check if signal was sent to thread specifically
|
|
||||||
jnc 81f
|
|
||||||
// if so, continue as usual
|
|
||||||
add eax, 32
|
|
||||||
jmp 2f
|
|
||||||
81:
|
|
||||||
// otherwise, try (competitively) dequeueing realtime signal
|
|
||||||
|
|
||||||
// SYS_CALL(fd, payload_base, payload_len, metadata_len | (flags << 8), metadata_base)
|
|
||||||
// rax rdi rsi rdx r10 r8
|
|
||||||
|
|
||||||
mov r12d, eax
|
|
||||||
mov rsi, fs:[0]
|
|
||||||
mov rdi, [rip+{proc_fd}]
|
|
||||||
add rsi, {tcb_sa_off} + {sa_tmp_rt_inf} // out pointer of dequeued realtime sig
|
|
||||||
mov rdx, {RTINF_SIZE}
|
|
||||||
mov [rsi], eax
|
|
||||||
lea r8, [rip + {proc_call_sigdeq}]
|
|
||||||
mov r10, 1
|
|
||||||
mov eax, {SYS_CALL}
|
|
||||||
syscall
|
|
||||||
test eax, eax
|
|
||||||
jnz 1b // assumes error can only be EAGAIN
|
|
||||||
lea eax, [r12d + 32]
|
|
||||||
jmp 9f
|
|
||||||
2:
|
|
||||||
mov edx, eax
|
|
||||||
shr edx, 5
|
|
||||||
mov rdi, fs:[{tcb_sc_off} + {sc_sender_infos} + eax * 8]
|
|
||||||
lock btr fs:[{tcb_sc_off} + {sc_word} + edx * 4], eax
|
|
||||||
mov fs:[{tcb_sa_off} + {sa_tmp_id_inf}], rdi
|
|
||||||
add eax, 64 // indicate signal was targeted at thread
|
|
||||||
9:
|
|
||||||
sub rsp, {REDZONE_SIZE}
|
|
||||||
and rsp, -{STACK_ALIGN}
|
|
||||||
|
|
||||||
// By now we have selected a signal, stored in eax (6-bit). We now need to choose whether or
|
|
||||||
// not to switch to the alternate signal stack. If SA_ONSTACK is clear for this signal, then
|
|
||||||
// skip the sigaltstack logic.
|
|
||||||
lea rdx, [rip + {pctl} + {pctl_off_actions}]
|
|
||||||
|
|
||||||
mov ecx, eax
|
|
||||||
and ecx, 63
|
|
||||||
|
|
||||||
// LEA doesn't support 16x, so just do two x8s.
|
|
||||||
lea rdx, [rdx + 8 * rcx]
|
|
||||||
lea rdx, [rdx + 8 * rcx]
|
|
||||||
|
|
||||||
bt qword ptr [rdx], {SA_ONSTACK_BIT}
|
|
||||||
jnc 4f
|
|
||||||
|
|
||||||
// Otherwise, the altstack is already active. The sigaltstack being disabled, is equivalent
|
|
||||||
// to setting 'top' to usize::MAX and 'bottom' to 0.
|
|
||||||
|
|
||||||
// If current RSP is above altstack region, switch to altstack
|
|
||||||
mov rdx, fs:[{tcb_sa_off} + {sa_altstack_top}]
|
|
||||||
cmp rsp, rdx
|
|
||||||
cmova rsp, rdx
|
|
||||||
|
|
||||||
// If current RSP is below altstack region, also switch to altstack
|
|
||||||
cmp rsp, fs:[{tcb_sa_off} + {sa_altstack_bottom}]
|
|
||||||
cmovbe rsp, rdx
|
|
||||||
|
|
||||||
.p2align 4
|
|
||||||
4:
|
|
||||||
// Now that we have a stack, we can finally start initializing the signal stack!
|
|
||||||
|
|
||||||
push fs:[{tcb_sa_off} + {sa_tmp_rsp}]
|
|
||||||
push fs:[{tcb_sc_off} + {sc_saved_rip}]
|
|
||||||
push fs:[{tcb_sc_off} + {sc_saved_rflags}]
|
|
||||||
|
|
||||||
push fs:[{tcb_sa_off} + {sa_tmp_rdi}]
|
|
||||||
push fs:[{tcb_sa_off} + {sa_tmp_rsi}]
|
|
||||||
push fs:[{tcb_sa_off} + {sa_tmp_rdx}]
|
|
||||||
push rcx
|
|
||||||
push fs:[{tcb_sa_off} + {sa_tmp_rax}]
|
|
||||||
push fs:[{tcb_sa_off} + {sa_tmp_r8}]
|
|
||||||
push r9
|
|
||||||
push fs:[{tcb_sa_off} + {sa_tmp_r10}]
|
|
||||||
push r11
|
|
||||||
push rbx
|
|
||||||
push rbp
|
|
||||||
push fs:[{tcb_sa_off} + {sa_tmp_r12}]
|
|
||||||
push r13
|
|
||||||
push r14
|
|
||||||
push r15
|
|
||||||
sub rsp, (29 + 16) * 16 // fxsave region minus available bytes
|
|
||||||
fxsave64 [rsp + 16 * 16]
|
|
||||||
|
|
||||||
// TODO: self-modifying?
|
|
||||||
cmp byte ptr [rip + {supports_avx}], 0
|
|
||||||
je 5f
|
|
||||||
|
|
||||||
// Prefer vextractf128 over vextracti128 since the former only requires AVX version 1.
|
|
||||||
vextractf128 [rsp + 15 * 16], ymm0, 1
|
|
||||||
vextractf128 [rsp + 14 * 16], ymm1, 1
|
|
||||||
vextractf128 [rsp + 13 * 16], ymm2, 1
|
|
||||||
vextractf128 [rsp + 12 * 16], ymm3, 1
|
|
||||||
vextractf128 [rsp + 11 * 16], ymm4, 1
|
|
||||||
vextractf128 [rsp + 10 * 16], ymm5, 1
|
|
||||||
vextractf128 [rsp + 9 * 16], ymm6, 1
|
|
||||||
vextractf128 [rsp + 8 * 16], ymm7, 1
|
|
||||||
vextractf128 [rsp + 7 * 16], ymm8, 1
|
|
||||||
vextractf128 [rsp + 6 * 16], ymm9, 1
|
|
||||||
vextractf128 [rsp + 5 * 16], ymm10, 1
|
|
||||||
vextractf128 [rsp + 4 * 16], ymm11, 1
|
|
||||||
vextractf128 [rsp + 3 * 16], ymm12, 1
|
|
||||||
vextractf128 [rsp + 2 * 16], ymm13, 1
|
|
||||||
vextractf128 [rsp + 16], ymm14, 1
|
|
||||||
vextractf128 [rsp], ymm15, 1
|
|
||||||
5:
|
|
||||||
mov [rsp - 4], eax
|
|
||||||
sub rsp, 64 // alloc space for ucontext fields
|
|
||||||
|
|
||||||
mov rdi, rsp
|
|
||||||
call {inner}
|
|
||||||
|
|
||||||
add rsp, 64
|
|
||||||
|
|
||||||
fxrstor64 [rsp + 16 * 16]
|
|
||||||
|
|
||||||
cmp byte ptr [rip + {supports_avx}], 0
|
|
||||||
je 6f
|
|
||||||
|
|
||||||
vinsertf128 ymm0, ymm0, [rsp + 15 * 16], 1
|
|
||||||
vinsertf128 ymm1, ymm1, [rsp + 14 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 13 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 12 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 11 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 10 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 9 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 8 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 7 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 6 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 5 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 4 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 3 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 2 * 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp + 16], 1
|
|
||||||
vinsertf128 ymm2, ymm2, [rsp], 1
|
|
||||||
6:
|
|
||||||
add rsp, (29 + 16) * 16
|
|
||||||
|
|
||||||
pop r15
|
|
||||||
pop r14
|
|
||||||
pop r13
|
|
||||||
pop r12
|
|
||||||
pop rbp
|
|
||||||
pop rbx
|
|
||||||
pop r11
|
|
||||||
pop r10
|
|
||||||
pop r9
|
|
||||||
pop r8
|
|
||||||
pop rax
|
|
||||||
pop rcx
|
|
||||||
pop rdx
|
|
||||||
pop rsi
|
|
||||||
pop rdi
|
|
||||||
|
|
||||||
popfq
|
|
||||||
pop qword ptr fs:[{tcb_sa_off} + {sa_tmp_rip}]
|
|
||||||
|
|
||||||
// x86 lacks atomic instructions for setting both the stack and instruction pointer
|
|
||||||
// simultaneously, except the slow microcoded IRETQ instruction. Thus, we let the arch_pre
|
|
||||||
// function emulate atomicity between the pop rsp and indirect jump.
|
|
||||||
|
|
||||||
.globl __relibc_internal_sigentry_crit_first
|
|
||||||
__relibc_internal_sigentry_crit_first:
|
|
||||||
|
|
||||||
pop rsp
|
|
||||||
|
|
||||||
.globl __relibc_internal_sigentry_crit_second
|
|
||||||
__relibc_internal_sigentry_crit_second:
|
|
||||||
jmp qword ptr fs:[{tcb_sa_off} + {sa_tmp_rip}]
|
|
||||||
7:
|
|
||||||
// A spurious signal occurred. Signals are still disabled here, but will need to be re-enabled.
|
|
||||||
|
|
||||||
// restore flags
|
|
||||||
mov rax, fs:[0] // load FS base
|
|
||||||
// TODO: Use lahf/sahf rather than pushfq/popfq?
|
|
||||||
lea rsp, [rax + {tcb_sc_off} + {sc_saved_rflags}]
|
|
||||||
popfq
|
|
||||||
|
|
||||||
// restore stack
|
|
||||||
mov rsp, fs:[{tcb_sa_off} + {sa_tmp_rsp}]
|
|
||||||
|
|
||||||
// move saved RIP away from control, allowing arch_pre to save us if interrupted.
|
|
||||||
mov rax, fs:[{tcb_sc_off} + {sc_saved_rip}]
|
|
||||||
mov fs:[{tcb_sa_off} + {sa_tmp_rip}], rax
|
|
||||||
|
|
||||||
// restore regs
|
|
||||||
mov rax, fs:[{tcb_sa_off} + {sa_tmp_rax}]
|
|
||||||
mov rdx, fs:[{tcb_sa_off} + {sa_tmp_rdx}]
|
|
||||||
|
|
||||||
// Re-enable signals. This code can be interrupted after this signal, so we need to define
|
|
||||||
// 'crit_third'.
|
|
||||||
and qword ptr fs:[{tcb_sc_off} + {sc_control}], ~1
|
|
||||||
|
|
||||||
.globl __relibc_internal_sigentry_crit_third
|
|
||||||
__relibc_internal_sigentry_crit_third:
|
|
||||||
jmp qword ptr fs:[{tcb_sa_off} + {sa_tmp_rip}]
|
|
||||||
"] <= [
|
|
||||||
inner = sym inner_c,
|
|
||||||
sa_tmp_rip = const offset_of!(SigArea, tmp_rip),
|
|
||||||
sa_tmp_rsp = const offset_of!(SigArea, tmp_rsp),
|
|
||||||
sa_tmp_rax = const offset_of!(SigArea, tmp_rax),
|
|
||||||
sa_tmp_rdx = const offset_of!(SigArea, tmp_rdx),
|
|
||||||
sa_tmp_rdi = const offset_of!(SigArea, tmp_rdi),
|
|
||||||
sa_tmp_rsi = const offset_of!(SigArea, tmp_rsi),
|
|
||||||
sa_tmp_r8 = const offset_of!(SigArea, tmp_r8),
|
|
||||||
sa_tmp_r10 = const offset_of!(SigArea, tmp_r10),
|
|
||||||
sa_tmp_r12 = const offset_of!(SigArea, tmp_r12),
|
|
||||||
sa_tmp_rt_inf = const offset_of!(SigArea, tmp_rt_inf),
|
|
||||||
sa_tmp_id_inf = const offset_of!(SigArea, tmp_id_inf),
|
|
||||||
sa_altstack_top = const offset_of!(SigArea, altstack_top),
|
|
||||||
sa_altstack_bottom = const offset_of!(SigArea, altstack_bottom),
|
|
||||||
sc_saved_rflags = const offset_of!(Sigcontrol, saved_archdep_reg),
|
|
||||||
sc_saved_rip = const offset_of!(Sigcontrol, saved_ip),
|
|
||||||
sc_word = const offset_of!(Sigcontrol, word),
|
|
||||||
sc_sender_infos = const offset_of!(Sigcontrol, sender_infos),
|
|
||||||
sc_control = const offset_of!(Sigcontrol, control_flags),
|
|
||||||
tcb_sa_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, arch),
|
|
||||||
tcb_sc_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control),
|
|
||||||
pctl_off_actions = const offset_of!(SigProcControl, actions),
|
|
||||||
pctl_off_pending = const offset_of!(SigProcControl, pending),
|
|
||||||
pctl_off_sender_infos = const offset_of!(SigProcControl, sender_infos),
|
|
||||||
pctl = sym PROC_CONTROL_STRUCT,
|
|
||||||
supports_avx = sym SUPPORTS_AVX,
|
|
||||||
REDZONE_SIZE = const 128,
|
|
||||||
STACK_ALIGN = const 16,
|
|
||||||
SA_ONSTACK_BIT = const 58, // (1 << 58) >> 32 = 0x0400_0000
|
|
||||||
SYS_CALL = const syscall::SYS_CALL,
|
|
||||||
proc_call_sigdeq = sym PROC_CALL_SIGDEQ,
|
|
||||||
RTINF_SIZE = const size_of::<RtSigInfo>(),
|
|
||||||
proc_fd = sym PROC_FD,
|
|
||||||
]);
|
|
||||||
|
|
||||||
unsafe extern "C" {
|
|
||||||
fn __relibc_internal_sigentry_crit_first();
|
|
||||||
fn __relibc_internal_sigentry_crit_second();
|
|
||||||
fn __relibc_internal_sigentry_crit_third();
|
|
||||||
}
|
|
||||||
/// Fixes some edge cases, and calculates the value for uc_stack.
|
|
||||||
pub unsafe fn arch_pre(stack: &mut SigStack, area: &mut SigArea) -> PosixStackt {
|
|
||||||
// It is impossible to update RSP and RIP atomically on x86_64, without using IRETQ, which is
|
|
||||||
// almost as slow as calling a SIGRETURN syscall would be. Instead, we abuse the fact that
|
|
||||||
// signals are disabled in the prologue of the signal trampoline, which allows us to emulate
|
|
||||||
// atomicity inside the critical section, consisting of one instruction at 'crit_first', one at
|
|
||||||
// 'crit_second', and one at 'crit_third', see asm.
|
|
||||||
|
|
||||||
if stack.regs.rip == __relibc_internal_sigentry_crit_first as *const () as usize {
|
|
||||||
// Reexecute pop rsp and jump steps. This case needs to be different from the one below,
|
|
||||||
// since rsp has not been overwritten with the previous context's stack, just yet. At this
|
|
||||||
// point, we know [rsp+0] contains the saved RSP, and [rsp-8] contains the saved RIP.
|
|
||||||
let stack_ptr = stack.regs.rsp as *const usize;
|
|
||||||
stack.regs.rsp = unsafe { stack_ptr.read() };
|
|
||||||
stack.regs.rip = unsafe { stack_ptr.sub(1).read() };
|
|
||||||
} else if stack.regs.rip == __relibc_internal_sigentry_crit_second as *const () as usize
|
|
||||||
|| stack.regs.rip == __relibc_internal_sigentry_crit_third as *const () as usize
|
|
||||||
{
|
|
||||||
// Almost finished, just reexecute the jump before tmp_rip is overwritten by this
|
|
||||||
// deeper-level signal.
|
|
||||||
stack.regs.rip = area.tmp_rip;
|
|
||||||
}
|
|
||||||
|
|
||||||
get_sigaltstack(area, stack.regs.rsp).into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Rearrange the restore-stack and sigarea in a way that makes it look like a new signal was
|
|
||||||
/// immediately delivered after restoring the allowset to what it was prior to the original signal delivery.
|
|
||||||
pub fn arch_ret_to_sig(stack: &mut SigStack, control: &Sigcontrol) {
|
|
||||||
let orig_rip = core::mem::replace(
|
|
||||||
&mut stack.regs.rip,
|
|
||||||
__relibc_internal_sigentry as *const () as usize,
|
|
||||||
);
|
|
||||||
control.saved_ip.set(orig_rip);
|
|
||||||
control.saved_archdep_reg.set(stack.regs.rflags);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) static SUPPORTS_AVX: AtomicU8 = AtomicU8::new(0);
|
|
||||||
|
|
||||||
// __relibc will be prepended to the name, so no_mangle is fine
|
|
||||||
#[allow(unsafe_op_in_unsafe_fn)]
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub unsafe fn manually_enter_trampoline() {
|
|
||||||
let c = &Tcb::current().unwrap().os_specific.control;
|
|
||||||
c.control_flags.store(
|
|
||||||
c.control_flags.load(Ordering::Relaxed) | syscall::flag::INHIBIT_DELIVERY.bits(),
|
|
||||||
Ordering::Release,
|
|
||||||
);
|
|
||||||
c.saved_archdep_reg.set(0); // TODO: Just reset DF on x86?
|
|
||||||
|
|
||||||
core::arch::asm!("
|
|
||||||
lea rax, [rip + 2f]
|
|
||||||
mov fs:[{tcb_sc_off} + {sc_saved_rip}], rax
|
|
||||||
jmp __relibc_internal_sigentry
|
|
||||||
2:
|
|
||||||
",
|
|
||||||
out("rax") _,
|
|
||||||
tcb_sc_off = const offset_of!(crate::Tcb, os_specific) + offset_of!(RtSigarea, control),
|
|
||||||
sc_saved_rip = const offset_of!(Sigcontrol, saved_ip),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get current stack pointer, weak granularity guarantees.
|
|
||||||
pub fn current_sp() -> usize {
|
|
||||||
let sp: usize;
|
|
||||||
unsafe {
|
|
||||||
core::arch::asm!("mov {}, rsp", out(reg) sp);
|
|
||||||
}
|
|
||||||
sp
|
|
||||||
}
|
|
||||||
|
|
||||||
static PROC_CALL_SIGDEQ: u64 = ProcCall::Sigdeq as u64;
|
|
||||||
pub(crate) static PROC_FD: SyncUnsafeCell<usize> = SyncUnsafeCell::new(usize::MAX);
|
|
||||||
@@ -1,366 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![allow(internal_features)]
|
|
||||||
#![deny(unsafe_op_in_unsafe_fn)]
|
|
||||||
#![feature(core_intrinsics, int_roundings, slice_ptr_get, sync_unsafe_cell)]
|
|
||||||
#![forbid(unreachable_patterns)]
|
|
||||||
|
|
||||||
use core::cell::UnsafeCell;
|
|
||||||
|
|
||||||
use generic_rt::GenericTcb;
|
|
||||||
use redox_protocols::protocol::ProcMeta;
|
|
||||||
use syscall::Sigcontrol;
|
|
||||||
|
|
||||||
use self::{
|
|
||||||
proc::{FdGuard, FdGuardUpper, STATIC_PROC_INFO},
|
|
||||||
sync::Mutex,
|
|
||||||
sys::FdTbl,
|
|
||||||
};
|
|
||||||
|
|
||||||
extern crate alloc;
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! asmfunction(
|
|
||||||
($name:ident $(($($arg:ty),*))? $(-> $ret:ty)? : [$($asmstmt:expr),*$(,)?] <= [$($decl:ident = $(sym $symname:ident)?$(const $constval:expr)?),*$(,)?]$(,)? ) => {
|
|
||||||
::core::arch::global_asm!(concat!("
|
|
||||||
.p2align 4
|
|
||||||
.section .text.", stringify!($name), ", \"ax\", @progbits
|
|
||||||
.globl ", stringify!($name), "
|
|
||||||
.type ", stringify!($name), ", @function
|
|
||||||
", stringify!($name), ":
|
|
||||||
", $($asmstmt, "\n",)* "
|
|
||||||
.size ", stringify!($name), ", . - ", stringify!($name), "
|
|
||||||
"), $($decl = $(sym $symname)?$(const $constval)?),*);
|
|
||||||
|
|
||||||
unsafe extern "C" {
|
|
||||||
pub fn $name($($(_: $arg),*)?) $(-> $ret)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
pub mod arch;
|
|
||||||
pub mod proc;
|
|
||||||
|
|
||||||
// TODO: Replace auxvs with a non-stack-based interface, but keep getauxval for compatibility
|
|
||||||
#[path = "../../src/platform/auxv_defs.rs"]
|
|
||||||
pub mod auxv_defs;
|
|
||||||
|
|
||||||
pub mod signal;
|
|
||||||
pub mod sync;
|
|
||||||
pub mod sys;
|
|
||||||
pub mod thread;
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct RtTcb {
|
|
||||||
pub control: Sigcontrol,
|
|
||||||
pub arch: UnsafeCell<crate::arch::SigArea>,
|
|
||||||
pub thr_fd: UnsafeCell<Option<FdGuardUpper>>,
|
|
||||||
}
|
|
||||||
impl RtTcb {
|
|
||||||
pub fn current() -> &'static Self {
|
|
||||||
unsafe { &Tcb::current().unwrap().os_specific }
|
|
||||||
}
|
|
||||||
pub fn thread_fd(&self) -> &FdGuardUpper {
|
|
||||||
unsafe { (&*self.thr_fd.get()).as_ref().unwrap() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Tcb = GenericTcb<RtTcb>;
|
|
||||||
|
|
||||||
// TODO: Remove this by keeping the TCB initialized during exec
|
|
||||||
// and informing ld.so through some other way.
|
|
||||||
pub static TLS_ACTIVATED: core::sync::atomic::AtomicBool =
|
|
||||||
core::sync::atomic::AtomicBool::new(false);
|
|
||||||
|
|
||||||
/// OS and architecture specific code to activate TLS - Redox aarch64
|
|
||||||
#[allow(unsafe_op_in_unsafe_fn)]
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
pub unsafe fn tcb_activate(_tcb: &RtTcb, tls_end: usize, tls_len: usize) {
|
|
||||||
// Uses ABI page
|
|
||||||
let abi_ptr = tls_end - tls_len - 16;
|
|
||||||
core::ptr::write(abi_ptr as *mut usize, tls_end);
|
|
||||||
core::arch::asm!(
|
|
||||||
"msr tpidr_el0, {}",
|
|
||||||
in(reg) abi_ptr,
|
|
||||||
);
|
|
||||||
TLS_ACTIVATED.store(true, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// OS and architecture specific code to activate TLS - Redox x86
|
|
||||||
#[allow(unsafe_op_in_unsafe_fn)]
|
|
||||||
#[cfg(target_arch = "x86")]
|
|
||||||
pub unsafe fn tcb_activate(tcb: &RtTcb, tls_end: usize, _tls_len: usize) {
|
|
||||||
let mut env = syscall::EnvRegisters::default();
|
|
||||||
|
|
||||||
let file_fd = crate::sys::dup_into_upper_raw(tcb.thread_fd().as_raw_fd(), b"regs/env")
|
|
||||||
.expect("failed to open handle for process registers");
|
|
||||||
|
|
||||||
syscall::read(file_fd, &mut env).expect("failed to read gsbase");
|
|
||||||
|
|
||||||
env.gsbase = tls_end as u32;
|
|
||||||
|
|
||||||
syscall::write(file_fd, &env).expect("failed to write gsbase");
|
|
||||||
|
|
||||||
let _ = crate::sys::close_raw(file_fd);
|
|
||||||
|
|
||||||
TLS_ACTIVATED.store(true, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// OS and architecture specific code to activate TLS - Redox x86_64
|
|
||||||
#[allow(unsafe_op_in_unsafe_fn)]
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
pub unsafe fn tcb_activate(tcb: &RtTcb, tls_end_and_tcb_start: usize, _tls_len: usize) {
|
|
||||||
let mut env = syscall::EnvRegisters::default();
|
|
||||||
|
|
||||||
let file_fd = crate::sys::dup_into_upper_raw(tcb.thread_fd().as_raw_fd(), b"regs/env")
|
|
||||||
.expect("failed to open handle for process registers");
|
|
||||||
|
|
||||||
syscall::read(file_fd, &mut env).expect("failed to read fsbase");
|
|
||||||
|
|
||||||
env.fsbase = tls_end_and_tcb_start as u64;
|
|
||||||
|
|
||||||
syscall::write(file_fd, &env).expect("failed to write fsbase");
|
|
||||||
|
|
||||||
let _ = crate::sys::close_raw(file_fd);
|
|
||||||
|
|
||||||
TLS_ACTIVATED.store(true, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// OS and architecture specific code to activate TLS - Redox riscv64
|
|
||||||
#[allow(unsafe_op_in_unsafe_fn)]
|
|
||||||
#[cfg(target_arch = "riscv64")]
|
|
||||||
pub unsafe fn tcb_activate(_tcb: &RtTcb, tls_end: usize, tls_len: usize) {
|
|
||||||
// tp points to static tls block
|
|
||||||
// FIXME limited to a single initial master
|
|
||||||
let tls_start = tls_end - tls_len;
|
|
||||||
let abi_ptr = tls_start - 8;
|
|
||||||
core::ptr::write(abi_ptr as *mut usize, tls_end);
|
|
||||||
core::arch::asm!(
|
|
||||||
"mv tp, {}",
|
|
||||||
in(reg) tls_start
|
|
||||||
);
|
|
||||||
TLS_ACTIVATED.store(true, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initialize redox-rt in situations where relibc is not used
|
|
||||||
#[allow(unsafe_op_in_unsafe_fn)]
|
|
||||||
#[cfg(not(feature = "proc"))]
|
|
||||||
pub unsafe fn initialize_freestanding(this_thr_fd: FdGuardUpper) -> &'static FdGuardUpper {
|
|
||||||
// TODO: This code is a hack! Integrate the ld_so TCB code into generic-rt, and then use that
|
|
||||||
// (this function will need pointers to the ELF structs normally passed in auxvs), so the TCB
|
|
||||||
// is initialized properly.
|
|
||||||
|
|
||||||
// TODO: TLS
|
|
||||||
let page = {
|
|
||||||
&mut *(syscall::fmap(
|
|
||||||
!0,
|
|
||||||
&syscall::Map {
|
|
||||||
offset: 0,
|
|
||||||
size: syscall::PAGE_SIZE,
|
|
||||||
flags: syscall::MapFlags::PROT_READ
|
|
||||||
| syscall::MapFlags::PROT_WRITE
|
|
||||||
| syscall::MapFlags::MAP_PRIVATE,
|
|
||||||
address: 0,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap() as *mut Tcb)
|
|
||||||
};
|
|
||||||
page.tcb_ptr = page;
|
|
||||||
page.tcb_len = syscall::PAGE_SIZE;
|
|
||||||
page.tls_end = (page as *mut Tcb).cast();
|
|
||||||
|
|
||||||
let cur_filetable_fd = syscall::dup_into(
|
|
||||||
this_thr_fd.as_raw_fd(),
|
|
||||||
this_thr_fd.as_raw_fd() + 1,
|
|
||||||
b"filetable-binary",
|
|
||||||
)
|
|
||||||
.expect("failed to open filetable-binary");
|
|
||||||
|
|
||||||
*current_filetable() =
|
|
||||||
FdTbl::from_binary_fd(FdGuard::new(cur_filetable_fd).to_upper().unwrap())
|
|
||||||
.expect("failed to populate fds");
|
|
||||||
|
|
||||||
// Make sure to use ptr::write to prevent dropping the existing FdGuard
|
|
||||||
page.os_specific.thr_fd.get().write(Some(this_thr_fd));
|
|
||||||
|
|
||||||
#[cfg(not(any(target_arch = "aarch64", target_arch = "riscv64")))]
|
|
||||||
unsafe {
|
|
||||||
let tcb_addr = page as *mut Tcb as usize;
|
|
||||||
tcb_activate(&page.os_specific, tcb_addr, 0)
|
|
||||||
}
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
unsafe {
|
|
||||||
let abi_ptr = core::ptr::addr_of_mut!(page.tcb_ptr);
|
|
||||||
core::arch::asm!("msr tpidr_el0, {}", in(reg) abi_ptr);
|
|
||||||
}
|
|
||||||
#[cfg(target_arch = "riscv64")]
|
|
||||||
unsafe {
|
|
||||||
let abi_ptr = core::ptr::addr_of_mut!(page.tcb_ptr) as usize;
|
|
||||||
core::arch::asm!("mv tp, {}", in(reg) (abi_ptr + 8));
|
|
||||||
}
|
|
||||||
TLS_ACTIVATED.store(true, core::sync::atomic::Ordering::Relaxed);
|
|
||||||
|
|
||||||
initialize();
|
|
||||||
|
|
||||||
(*page.os_specific.thr_fd.get()).as_ref().unwrap()
|
|
||||||
}
|
|
||||||
pub(crate) fn read_proc_meta(proc: &FdGuardUpper) -> syscall::Result<ProcMeta> {
|
|
||||||
let mut bytes = [0_u8; size_of::<ProcMeta>()];
|
|
||||||
proc.read(&mut bytes)?;
|
|
||||||
Ok(*plain::from_bytes::<ProcMeta>(&bytes).unwrap())
|
|
||||||
}
|
|
||||||
pub unsafe fn initialize(
|
|
||||||
#[cfg(feature = "proc")] proc_fd: FdGuardUpper,
|
|
||||||
#[cfg(feature = "proc")] ns_fd: Option<FdGuardUpper>,
|
|
||||||
) {
|
|
||||||
#[cfg(feature = "proc")]
|
|
||||||
let metadata = read_proc_meta(&proc_fd).unwrap();
|
|
||||||
|
|
||||||
#[cfg(not(feature = "proc"))]
|
|
||||||
// Bootstrap mode, don't associate proc fds with PIDs
|
|
||||||
let metadata = ProcMeta::default();
|
|
||||||
|
|
||||||
#[cfg(feature = "proc")]
|
|
||||||
{
|
|
||||||
unsafe { crate::arch::PROC_FD.get().write(proc_fd.as_raw_fd()) };
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
STATIC_PROC_INFO.get().write(StaticProcInfo {
|
|
||||||
pid: metadata.pid,
|
|
||||||
|
|
||||||
#[cfg(feature = "proc")]
|
|
||||||
proc_fd: Some(proc_fd),
|
|
||||||
|
|
||||||
#[cfg(not(feature = "proc"))]
|
|
||||||
proc_fd: None,
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(feature = "proc")]
|
|
||||||
{
|
|
||||||
*DYNAMIC_PROC_INFO.lock() = DynamicProcInfo {
|
|
||||||
pgid: metadata.pgid,
|
|
||||||
ruid: metadata.ruid,
|
|
||||||
euid: metadata.euid,
|
|
||||||
suid: metadata.suid,
|
|
||||||
egid: metadata.egid,
|
|
||||||
rgid: metadata.rgid,
|
|
||||||
sgid: metadata.sgid,
|
|
||||||
ns_fd,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct StaticProcInfo {
|
|
||||||
pid: u32,
|
|
||||||
proc_fd: Option<FdGuardUpper>,
|
|
||||||
}
|
|
||||||
pub struct DynamicProcInfo {
|
|
||||||
pub pgid: u32,
|
|
||||||
pub euid: u32,
|
|
||||||
pub suid: u32,
|
|
||||||
pub ruid: u32,
|
|
||||||
pub egid: u32,
|
|
||||||
pub rgid: u32,
|
|
||||||
pub sgid: u32,
|
|
||||||
pub ns_fd: Option<FdGuardUpper>,
|
|
||||||
}
|
|
||||||
|
|
||||||
static DYNAMIC_PROC_INFO: Mutex<DynamicProcInfo> = Mutex::new(DynamicProcInfo {
|
|
||||||
pgid: u32::MAX,
|
|
||||||
ruid: u32::MAX,
|
|
||||||
euid: u32::MAX,
|
|
||||||
suid: u32::MAX,
|
|
||||||
rgid: u32::MAX,
|
|
||||||
egid: u32::MAX,
|
|
||||||
sgid: u32::MAX,
|
|
||||||
ns_fd: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub(crate) fn static_proc_info() -> &'static StaticProcInfo {
|
|
||||||
unsafe { &*STATIC_PROC_INFO.get() }
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn current_proc_fd() -> &'static FdGuardUpper {
|
|
||||||
let info = static_proc_info();
|
|
||||||
info.proc_fd.as_ref().unwrap()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn current_namespace_fd() -> syscall::Result<usize> {
|
|
||||||
DYNAMIC_PROC_INFO
|
|
||||||
.lock()
|
|
||||||
.ns_fd
|
|
||||||
.as_ref()
|
|
||||||
.map(|g| g.as_raw_fd())
|
|
||||||
.ok_or(syscall::Error::new(syscall::ENOENT))
|
|
||||||
}
|
|
||||||
struct ChildHookCommonArgs {
|
|
||||||
new_thr_fd: FdGuard,
|
|
||||||
new_proc_fd: Option<FdGuard>,
|
|
||||||
new_filetable_fd: FdGuardUpper,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn child_hook_common(args: ChildHookCommonArgs) {
|
|
||||||
let new_filetable_fd = args.new_filetable_fd;
|
|
||||||
|
|
||||||
let old_filetable_fd;
|
|
||||||
{
|
|
||||||
let mut guard = current_filetable();
|
|
||||||
old_filetable_fd = guard.take();
|
|
||||||
guard.set_fd(new_filetable_fd);
|
|
||||||
guard
|
|
||||||
.override_at(args.new_thr_fd.as_raw_fd(), args.new_thr_fd.as_raw_fd())
|
|
||||||
.expect("failed to add new_thr_fd");
|
|
||||||
if let Some(new_proc_fd) = args.new_proc_fd.as_ref() {
|
|
||||||
guard
|
|
||||||
.override_at(new_proc_fd.as_raw_fd(), new_proc_fd.as_raw_fd())
|
|
||||||
.expect("failed to add new_proc_fd");
|
|
||||||
}
|
|
||||||
if let Some(ref old) = old_filetable_fd {
|
|
||||||
let _ = guard.remove(old.as_raw_fd());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_thr_fd = args.new_thr_fd.to_upper().unwrap();
|
|
||||||
let new_proc_fd = args.new_proc_fd.map(|x| x.to_upper().unwrap());
|
|
||||||
|
|
||||||
// TODO: just pass PID to child rather than obtaining it via IPC?
|
|
||||||
#[cfg(feature = "proc")]
|
|
||||||
let metadata = read_proc_meta(
|
|
||||||
new_proc_fd
|
|
||||||
.as_ref()
|
|
||||||
.expect("must be present with proc feature"),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
#[cfg(not(feature = "proc"))]
|
|
||||||
let metadata = ProcMeta::default();
|
|
||||||
|
|
||||||
if let Some(proc_fd) = &new_proc_fd {
|
|
||||||
unsafe { crate::arch::PROC_FD.get().write(proc_fd.as_raw_fd()) };
|
|
||||||
}
|
|
||||||
|
|
||||||
let old_proc_fd = unsafe {
|
|
||||||
STATIC_PROC_INFO
|
|
||||||
.get()
|
|
||||||
.replace(StaticProcInfo {
|
|
||||||
pid: metadata.pid,
|
|
||||||
proc_fd: new_proc_fd,
|
|
||||||
})
|
|
||||||
.proc_fd
|
|
||||||
};
|
|
||||||
drop(old_proc_fd);
|
|
||||||
|
|
||||||
let old_thr_fd = unsafe { RtTcb::current().thr_fd.get().replace(Some(new_thr_fd)) };
|
|
||||||
drop(old_thr_fd);
|
|
||||||
|
|
||||||
drop(old_filetable_fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
static FILETABLE: Mutex<FdTbl> = Mutex::new(FdTbl::new());
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn current_filetable() -> crate::sync::MutexGuard<'static, FdTbl> {
|
|
||||||
FILETABLE.lock()
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,68 +0,0 @@
|
|||||||
// TODO: Share code for simple futex-based mutex between relibc's Mutex<()> and this.
|
|
||||||
|
|
||||||
use core::{
|
|
||||||
cell::UnsafeCell,
|
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
sync::atomic::{AtomicU32, Ordering},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Mutex<T> {
|
|
||||||
pub lockword: AtomicU32,
|
|
||||||
pub inner: UnsafeCell<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<T: Send> Send for Mutex<T> {}
|
|
||||||
unsafe impl<T: Send> Sync for Mutex<T> {}
|
|
||||||
|
|
||||||
impl<T> Mutex<T> {
|
|
||||||
/// Represents an unlocked [Mutex].
|
|
||||||
pub const UNLOCKED: u32 = 0;
|
|
||||||
/// Represents a locked [Mutex].
|
|
||||||
pub const LOCKED: u32 = 1;
|
|
||||||
/// Represents a waiting [Mutex].
|
|
||||||
pub const WAITING: u32 = 2;
|
|
||||||
|
|
||||||
pub const fn new(t: T) -> Self {
|
|
||||||
Self {
|
|
||||||
lockword: AtomicU32::new(0),
|
|
||||||
inner: UnsafeCell::new(t),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn lock(&self) -> MutexGuard<'_, T> {
|
|
||||||
while self
|
|
||||||
.lockword
|
|
||||||
.compare_exchange(
|
|
||||||
Self::UNLOCKED,
|
|
||||||
Self::LOCKED,
|
|
||||||
Ordering::Acquire,
|
|
||||||
Ordering::Relaxed,
|
|
||||||
)
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
core::hint::spin_loop();
|
|
||||||
}
|
|
||||||
MutexGuard { lock: self }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub struct MutexGuard<'l, T> {
|
|
||||||
lock: &'l Mutex<T>,
|
|
||||||
}
|
|
||||||
impl<T> Deref for MutexGuard<'_, T> {
|
|
||||||
type Target = T;
|
|
||||||
|
|
||||||
fn deref(&self) -> &T {
|
|
||||||
unsafe { &*self.lock.inner.get() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T> DerefMut for MutexGuard<'_, T> {
|
|
||||||
fn deref_mut(&mut self) -> &mut T {
|
|
||||||
unsafe { &mut *self.lock.inner.get() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T> Drop for MutexGuard<'_, T> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.lock
|
|
||||||
.lockword
|
|
||||||
.store(Mutex::<T>::UNLOCKED, Ordering::Release);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-2048
File diff suppressed because it is too large
Load Diff
@@ -1,76 +0,0 @@
|
|||||||
use core::mem::size_of;
|
|
||||||
|
|
||||||
use syscall::Result;
|
|
||||||
|
|
||||||
use crate::{RtTcb, arch::*, proc::*, static_proc_info};
|
|
||||||
|
|
||||||
/// Spawns a new context sharing the same address space as the current one (i.e. a new thread).
|
|
||||||
pub unsafe fn rlct_clone_impl(stack: *mut usize, tcb: &RtTcb) -> Result<usize> {
|
|
||||||
let proc_info = static_proc_info();
|
|
||||||
let cur_proc_fd = proc_info.proc_fd.as_ref().unwrap();
|
|
||||||
|
|
||||||
let cur_thr_fd = RtTcb::current().thread_fd();
|
|
||||||
let new_thr_fd = cur_proc_fd.dup_into_upper(b"new-thread")?;
|
|
||||||
|
|
||||||
// Inherit existing address space
|
|
||||||
{
|
|
||||||
let cur_addr_space_fd = cur_thr_fd.dup_into_upper(b"addrspace")?;
|
|
||||||
let new_addr_space_sel_fd = new_thr_fd.dup_into_upper(b"current-addrspace")?;
|
|
||||||
|
|
||||||
let buf = create_set_addr_space_buf(
|
|
||||||
cur_addr_space_fd.as_raw_fd(),
|
|
||||||
__relibc_internal_rlct_clone_ret as *const () as usize,
|
|
||||||
stack as usize,
|
|
||||||
);
|
|
||||||
new_addr_space_sel_fd.write(&buf)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inherit reference to file table
|
|
||||||
{
|
|
||||||
let cur_filetable_fd = cur_thr_fd.dup_into_upper(b"filetable")?;
|
|
||||||
let new_filetable_sel_fd = new_thr_fd.dup_into_upper(b"current-filetable")?;
|
|
||||||
|
|
||||||
new_filetable_sel_fd.write(&usize::to_ne_bytes(cur_filetable_fd.as_raw_fd()))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since the signal handler is not yet initialized, signals specifically targeting the thread
|
|
||||||
// (relibc is only required to implement thread-specific signals that already originate from
|
|
||||||
// the same process) will be discarded. Process-specific signals will ignore this new thread,
|
|
||||||
// until it has initialized its own signal handler.
|
|
||||||
|
|
||||||
let start_fd = new_thr_fd.dup_into_upper(b"start")?;
|
|
||||||
|
|
||||||
let fd = new_thr_fd.as_raw_fd();
|
|
||||||
unsafe {
|
|
||||||
tcb.thr_fd.get().write(Some(new_thr_fd));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unblock context.
|
|
||||||
start_fd.write(&[0])?;
|
|
||||||
|
|
||||||
Ok(fd)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub unsafe fn exit_this_thread(stack_base: *mut (), stack_size: usize) -> ! {
|
|
||||||
let _guard = crate::signal::tmp_disable_signals();
|
|
||||||
let thread_fd = RtTcb::current().thread_fd();
|
|
||||||
|
|
||||||
// TODO: modify interface so it writes directly to the thread fd?
|
|
||||||
let status_fd = thread_fd.dup_into_upper(b"status").unwrap();
|
|
||||||
unsafe { exit_this_thread_inner(stack_base, stack_size, status_fd) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// exit_this_thread, but current thread_fd might has disappear
|
|
||||||
pub unsafe fn exit_this_thread_inner(
|
|
||||||
stack_base: *mut (),
|
|
||||||
stack_size: usize,
|
|
||||||
status_fd: FdGuard<true>,
|
|
||||||
) -> ! {
|
|
||||||
let mut buf = [0; size_of::<usize>() * 3];
|
|
||||||
plain::slice_from_mut_bytes(&mut buf)
|
|
||||||
.unwrap()
|
|
||||||
.copy_from_slice(&[usize::MAX, stack_base as usize, stack_size]);
|
|
||||||
// TODO: SYS_CALL w/CONSUME
|
|
||||||
status_fd.write(&buf).unwrap();
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
target=$1
|
|
||||||
deps_dir=$2
|
|
||||||
|
|
||||||
if [ -z "$target" ] || [ -z "$deps_dir" ]; then
|
|
||||||
echo "Usage:\n\t./renamesyms.sh TARGET DEPS_DIR"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f "$target" ]; then
|
|
||||||
echo "Target file '$target' does not exist"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ ! -d "$deps_dir" ] ; then
|
|
||||||
echo "Deps dir '$deps_dir' does not exist or not a directory"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
symbols_file=`mktemp`
|
|
||||||
special_syms=(
|
|
||||||
__rdl_oom
|
|
||||||
__rg_alloc
|
|
||||||
__rg_alloc_zeroed
|
|
||||||
__rg_dealloc
|
|
||||||
__rg_oom
|
|
||||||
__rg_realloc
|
|
||||||
__rust_alloc
|
|
||||||
__rust_alloc_error_handler
|
|
||||||
__rust_alloc_error_handler_should_panic
|
|
||||||
__rust_alloc_zeroed
|
|
||||||
__rust_dealloc
|
|
||||||
__rust_no_alloc_shim_is_unstable
|
|
||||||
__rust_realloc
|
|
||||||
)
|
|
||||||
|
|
||||||
for dep in `find $deps_dir -type f -name "*.rlib"`; do
|
|
||||||
"${NM}" --format=posix -g "$dep" 2>/dev/null | sed 's/.*:.*//g' | awk '{if ($2 == "T") print $1}' | sed 's/^\(.*\)$/\1 __relibc_\1/g' >> $symbols_file
|
|
||||||
done
|
|
||||||
|
|
||||||
for special_sym in "${special_syms[@]}"; do
|
|
||||||
echo "$special_sym __relibc_$special_sym" >> $symbols_file
|
|
||||||
done
|
|
||||||
|
|
||||||
mangled_alloc_syms=$("${NM}" --format=posix -g "$target" 2>/dev/null | awk '{print $1}' | grep "7___rustc" || true)
|
|
||||||
for sym in $mangled_alloc_syms; do
|
|
||||||
echo "$sym __relibc_$sym" >> $symbols_file
|
|
||||||
done
|
|
||||||
|
|
||||||
sorted_file=`mktemp`
|
|
||||||
sort -u "$symbols_file" > "$sorted_file"
|
|
||||||
rm -f "$symbols_file"
|
|
||||||
|
|
||||||
"${OBJCOPY}" --redefine-syms="$sorted_file" "$target"
|
|
||||||
|
|
||||||
rm -f "$sorted_file"
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
[toolchain]
|
|
||||||
channel = "nightly-2026-05-24"
|
|
||||||
components = ["rust-src"]
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
blank_lines_lower_bound = 0
|
|
||||||
blank_lines_upper_bound = 1
|
|
||||||
brace_style = "SameLineWhere"
|
|
||||||
disable_all_formatting = false
|
|
||||||
edition = "2024"
|
|
||||||
empty_item_single_line = true
|
|
||||||
fn_single_line = false
|
|
||||||
force_explicit_abi = true
|
|
||||||
format_strings = false
|
|
||||||
hard_tabs = false
|
|
||||||
imports_granularity = "Crate"
|
|
||||||
imports_indent = "Block"
|
|
||||||
imports_layout = "Mixed"
|
|
||||||
indent_style = "Block"
|
|
||||||
max_width = 100
|
|
||||||
newline_style = "Unix"
|
|
||||||
show_parse_errors = true
|
|
||||||
skip_children = false
|
|
||||||
tab_spaces = 4
|
|
||||||
trailing_comma = "Vertical"
|
|
||||||
where_single_line = false
|
|
||||||
@@ -0,0 +1,394 @@
|
|||||||
|
use alloc::{collections::BTreeSet, vec::Vec};
|
||||||
|
use core::{fmt, mem, ops, slice};
|
||||||
|
use endian_num::Le;
|
||||||
|
|
||||||
|
use crate::{BlockAddr, BlockLevel, BlockMeta, BlockPtr, BlockTrait, Node, TreePtr, BLOCK_SIZE};
|
||||||
|
|
||||||
|
pub const ALLOC_LIST_ENTRIES: usize =
|
||||||
|
(BLOCK_SIZE as usize - mem::size_of::<BlockPtr<AllocList>>()) / mem::size_of::<AllocEntry>();
|
||||||
|
pub const RELEASE_LIST_ENTRIES: usize = (BLOCK_SIZE as usize
|
||||||
|
- mem::size_of::<BlockPtr<ReleaseList>>())
|
||||||
|
/ mem::size_of::<TreePtr<Node>>();
|
||||||
|
|
||||||
|
/// The RedoxFS block allocator. This struct manages all "data" blocks in RedoxFS
|
||||||
|
/// (i.e, all blocks that aren't reserved or part of the header chain).
|
||||||
|
///
|
||||||
|
/// [`Allocator`] can allocate blocks of many "levels"---that is, it can
|
||||||
|
/// allocate multiple consecutive [`BLOCK_SIZE`] blocks in one operation.
|
||||||
|
///
|
||||||
|
/// This reduces the amount of memory that the [`Allocator`] uses:
|
||||||
|
/// Instead of storing the index of each free [`BLOCK_SIZE`] block,
|
||||||
|
/// the `levels` array can keep track of higher-level blocks, splitting
|
||||||
|
/// them when a smaller block is requested.
|
||||||
|
///
|
||||||
|
/// Higher-level blocks also allow us to more efficiently allocate memory
|
||||||
|
/// for large files.
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
pub struct Allocator {
|
||||||
|
/// This array keeps track of all free blocks of each level,
|
||||||
|
/// and is initialized using the AllocList chain when we open the filesystem.
|
||||||
|
///
|
||||||
|
/// Every element of the outer array represents a block level:
|
||||||
|
/// - item 0: free level 0 blocks (with size [`BLOCK_SIZE`])
|
||||||
|
/// - item 1: free level 1 blocks (with size 2*[`BLOCK_SIZE`])
|
||||||
|
/// - item 2: free level 2 blocks (with size 4*[`BLOCK_SIZE`])
|
||||||
|
/// ...and so on.
|
||||||
|
///
|
||||||
|
/// Each inner array contains a list of free block indices,
|
||||||
|
levels: Vec<BTreeSet<u64>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Allocator {
|
||||||
|
pub fn levels(&self) -> &Vec<BTreeSet<u64>> {
|
||||||
|
&self.levels
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Count the number of free [`BLOCK_SIZE`] available to this [`Allocator`].
|
||||||
|
pub fn free(&self) -> u64 {
|
||||||
|
let mut free = 0;
|
||||||
|
for level in 0..self.levels.len() {
|
||||||
|
let level_size = 1 << level;
|
||||||
|
free += self.levels[level].len() as u64 * level_size;
|
||||||
|
}
|
||||||
|
free
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find a free block of the given level, mark it as "used", and return its address.
|
||||||
|
/// Returns [`None`] if there are no free blocks with this level.
|
||||||
|
pub fn allocate(&mut self, meta: BlockMeta) -> Option<BlockAddr> {
|
||||||
|
// First, find the lowest level with a free block
|
||||||
|
let mut free_opt = None;
|
||||||
|
{
|
||||||
|
let mut level = meta.level.0;
|
||||||
|
// Start searching at the level we want. Smaller levels are too small!
|
||||||
|
while level < self.levels.len() {
|
||||||
|
if let Some(&index) = self.levels[level].first() {
|
||||||
|
// Find the index closest to the start of the filesystem
|
||||||
|
free_opt = match free_opt {
|
||||||
|
Some((free_level, free_index)) if free_index <= index => {
|
||||||
|
Some((free_level, free_index))
|
||||||
|
}
|
||||||
|
_ => Some((level, index)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
level += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a free block was found, split it until we find a usable block of the right level.
|
||||||
|
// The left side of the split block is kept free, and the right side is allocated.
|
||||||
|
let (mut level, index) = free_opt?;
|
||||||
|
self.levels[level].remove(&index);
|
||||||
|
while level > meta.level.0 {
|
||||||
|
level -= 1;
|
||||||
|
let level_size = 1 << level;
|
||||||
|
self.levels[level].insert(index + level_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(unsafe { BlockAddr::new(index, meta) })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to allocate the exact block specified, making all necessary splits.
|
||||||
|
/// Returns [`None`] if this some (or all) of this block is already allocated.
|
||||||
|
///
|
||||||
|
/// Note that [`BlockAddr`] encodes the blocks location _and_ level.
|
||||||
|
pub fn allocate_exact(&mut self, exact_addr: BlockAddr) -> Option<BlockAddr> {
|
||||||
|
// This function only supports level 0 right now
|
||||||
|
assert_eq!(exact_addr.level().0, 0);
|
||||||
|
let exact_index = exact_addr.index();
|
||||||
|
|
||||||
|
let mut index_opt = None;
|
||||||
|
|
||||||
|
// Go from the highest to the lowest level
|
||||||
|
for level in (0..self.levels.len()).rev() {
|
||||||
|
let level_size = 1 << level;
|
||||||
|
|
||||||
|
// Split higher block if found
|
||||||
|
if let Some(index) = index_opt.take() {
|
||||||
|
self.levels[level].insert(index);
|
||||||
|
self.levels[level].insert(index + level_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for matching block and remove it
|
||||||
|
for &start in self.levels[level].iter() {
|
||||||
|
if start <= exact_index {
|
||||||
|
let end = start + level_size;
|
||||||
|
if end > exact_index {
|
||||||
|
self.levels[level].remove(&start);
|
||||||
|
index_opt = Some(start);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(unsafe { BlockAddr::new(index_opt?, exact_addr.meta()) })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deallocate the given block, marking it "free" so that it can be re-used later.
|
||||||
|
pub fn deallocate(&mut self, addr: BlockAddr) {
|
||||||
|
// When we deallocate, we check if block we're deallocating has a free sibling.
|
||||||
|
// If it does, we join the two to create one free block in the next (higher) level.
|
||||||
|
//
|
||||||
|
// We repeat this until we no longer have a sibling to join.
|
||||||
|
let mut index = addr.index();
|
||||||
|
let mut level = addr.level().0;
|
||||||
|
loop {
|
||||||
|
while level >= self.levels.len() {
|
||||||
|
self.levels.push(BTreeSet::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
let level_size = 1 << level;
|
||||||
|
let next_size = level_size << 1;
|
||||||
|
|
||||||
|
let mut found = false;
|
||||||
|
// look at all free blocks in the current level...
|
||||||
|
for &level_index in self.levels[level].iter() {
|
||||||
|
// - the block we just freed aligns with the next largest block, and
|
||||||
|
// - the second block we're looking at is the right sibling of this block
|
||||||
|
if index % next_size == 0 && index + level_size == level_index {
|
||||||
|
// "alloc" the next highest block, repeat deallocation process.
|
||||||
|
self.levels[level].remove(&level_index);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
// - the index of this block doesn't align with the next largest block, and
|
||||||
|
// - the block we're looking at is the left neighbor of this block
|
||||||
|
} else if level_index % next_size == 0 && level_index + level_size == index {
|
||||||
|
// "alloc" the next highest block, repeat deallocation process.
|
||||||
|
self.levels[level].remove(&level_index);
|
||||||
|
index = level_index; // index moves to left block
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We couldn't find a higher block,
|
||||||
|
// deallocate this one and finish
|
||||||
|
if !found {
|
||||||
|
self.levels[level].insert(index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// repeat deallocation process on the
|
||||||
|
// higher-level block we just created.
|
||||||
|
level += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C, packed)]
|
||||||
|
#[derive(Clone, Copy, Default, Debug)]
|
||||||
|
pub struct AllocEntry {
|
||||||
|
/// The index of the first block this [`AllocEntry`] refers to
|
||||||
|
index: Le<u64>,
|
||||||
|
|
||||||
|
/// The number of blocks after (and including) `index` that are are free or used.
|
||||||
|
/// If negative, they are used; if positive, they are free.
|
||||||
|
count: Le<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AllocEntry {
|
||||||
|
pub fn new(index: u64, count: i64) -> Self {
|
||||||
|
Self {
|
||||||
|
index: index.into(),
|
||||||
|
count: count.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn allocate(addr: BlockAddr) -> Self {
|
||||||
|
Self::new(addr.index(), -addr.level().blocks::<i64>())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deallocate(addr: BlockAddr) -> Self {
|
||||||
|
Self::new(addr.index(), addr.level().blocks::<i64>())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn index(&self) -> u64 {
|
||||||
|
self.index.to_ne()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn count(&self) -> i64 {
|
||||||
|
self.count.to_ne()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_null(&self) -> bool {
|
||||||
|
self.count() == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A node in the allocation chain.
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct AllocList {
|
||||||
|
/// A pointer to the previous AllocList.
|
||||||
|
/// If this is the null pointer, this is the first element of the chain.
|
||||||
|
pub prev: BlockPtr<AllocList>,
|
||||||
|
|
||||||
|
/// Allocation entries.
|
||||||
|
pub entries: [AllocEntry; ALLOC_LIST_ENTRIES],
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl BlockTrait for AllocList {
|
||||||
|
fn empty(level: BlockLevel) -> Option<Self> {
|
||||||
|
if level.0 == 0 {
|
||||||
|
Some(Self {
|
||||||
|
prev: BlockPtr::default(),
|
||||||
|
entries: [AllocEntry::default(); ALLOC_LIST_ENTRIES],
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for AllocList {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let prev = self.prev;
|
||||||
|
let entries: Vec<&AllocEntry> = self
|
||||||
|
.entries
|
||||||
|
.iter()
|
||||||
|
.filter(|entry| entry.count() > 0)
|
||||||
|
.collect();
|
||||||
|
f.debug_struct("AllocList")
|
||||||
|
.field("prev", &prev)
|
||||||
|
.field("entries", &entries)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Deref for AllocList {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const AllocList as *const u8,
|
||||||
|
mem::size_of::<AllocList>(),
|
||||||
|
) as &[u8]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::DerefMut for AllocList {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self as *mut AllocList as *mut u8,
|
||||||
|
mem::size_of::<AllocList>(),
|
||||||
|
) as &mut [u8]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A list of nodes pending release.
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct ReleaseList {
|
||||||
|
/// A pointer to the previous ReleaseList.
|
||||||
|
/// If this is the null pointer, this is the first element of the chain.
|
||||||
|
pub prev: BlockPtr<ReleaseList>,
|
||||||
|
|
||||||
|
/// Allocation entries.
|
||||||
|
pub entries: [TreePtr<Node>; RELEASE_LIST_ENTRIES],
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl BlockTrait for ReleaseList {
|
||||||
|
fn empty(level: BlockLevel) -> Option<Self> {
|
||||||
|
if level.0 == 0 {
|
||||||
|
Some(Self {
|
||||||
|
prev: BlockPtr::default(),
|
||||||
|
entries: [TreePtr::<Node>::default(); RELEASE_LIST_ENTRIES],
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for ReleaseList {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let prev = self.prev;
|
||||||
|
let entries: Vec<_> = self
|
||||||
|
.entries
|
||||||
|
.iter()
|
||||||
|
.filter(|entry| !entry.is_null())
|
||||||
|
.map(|entry| entry.id())
|
||||||
|
.collect();
|
||||||
|
f.debug_struct("ReleaseList")
|
||||||
|
.field("prev", &prev)
|
||||||
|
.field("entries", &entries)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Deref for ReleaseList {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const ReleaseList as *const u8,
|
||||||
|
mem::size_of::<ReleaseList>(),
|
||||||
|
) as &[u8]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::DerefMut for ReleaseList {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self as *mut ReleaseList as *mut u8,
|
||||||
|
mem::size_of::<ReleaseList>(),
|
||||||
|
) as &mut [u8]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn alloc_node_size_test() {
|
||||||
|
assert_eq!(mem::size_of::<AllocList>(), crate::BLOCK_SIZE as usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn release_node_size_test() {
|
||||||
|
assert_eq!(mem::size_of::<ReleaseList>(), crate::BLOCK_SIZE as usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn allocator_test() {
|
||||||
|
let mut alloc = Allocator::default();
|
||||||
|
|
||||||
|
assert_eq!(alloc.allocate(BlockMeta::default()), None);
|
||||||
|
|
||||||
|
alloc.deallocate(unsafe { BlockAddr::new(1, BlockMeta::default()) });
|
||||||
|
assert_eq!(
|
||||||
|
alloc.allocate(BlockMeta::default()),
|
||||||
|
Some(unsafe { BlockAddr::new(1, BlockMeta::default()) })
|
||||||
|
);
|
||||||
|
assert_eq!(alloc.allocate(BlockMeta::default()), None);
|
||||||
|
|
||||||
|
for addr in 1023..2048 {
|
||||||
|
alloc.deallocate(unsafe { BlockAddr::new(addr, BlockMeta::default()) });
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(alloc.levels.len(), 11);
|
||||||
|
for level in 0..alloc.levels.len() {
|
||||||
|
if level == 0 {
|
||||||
|
assert_eq!(alloc.levels[level], [1023].into());
|
||||||
|
} else if level == 10 {
|
||||||
|
assert_eq!(alloc.levels[level], [1024].into());
|
||||||
|
} else {
|
||||||
|
assert_eq!(alloc.levels[level], [0u64; 0].into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for addr in 1023..2048 {
|
||||||
|
assert_eq!(
|
||||||
|
alloc.allocate(BlockMeta::default()),
|
||||||
|
Some(unsafe { BlockAddr::new(addr, BlockMeta::default()) })
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assert_eq!(alloc.allocate(BlockMeta::default()), None);
|
||||||
|
|
||||||
|
assert_eq!(alloc.levels.len(), 11);
|
||||||
|
for level in 0..alloc.levels.len() {
|
||||||
|
assert_eq!(alloc.levels[level], [0u64; 0].into());
|
||||||
|
}
|
||||||
|
}
|
||||||
+146
@@ -0,0 +1,146 @@
|
|||||||
|
use std::fs;
|
||||||
|
use std::io;
|
||||||
|
use std::os::unix::ffi::OsStrExt;
|
||||||
|
use std::os::unix::fs::MetadataExt;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use crate::{Disk, FileSystem, Node, Transaction, TreePtr, BLOCK_SIZE};
|
||||||
|
|
||||||
|
fn syscall_err(err: syscall::Error) -> io::Error {
|
||||||
|
io::Error::from_raw_os_error(err.errno)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn archive_at<D: Disk, P: AsRef<Path>>(
|
||||||
|
tx: &mut Transaction<D>,
|
||||||
|
parent_path: P,
|
||||||
|
parent_ptr: TreePtr<Node>,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
for entry_res in fs::read_dir(parent_path)? {
|
||||||
|
let entry = entry_res?;
|
||||||
|
|
||||||
|
let metadata = entry.metadata()?;
|
||||||
|
let file_type = metadata.file_type();
|
||||||
|
|
||||||
|
let name = entry.file_name().into_string().map_err(|_| {
|
||||||
|
io::Error::new(io::ErrorKind::InvalidData, "filename is not valid UTF-8")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let mode_type = if file_type.is_dir() {
|
||||||
|
Node::MODE_DIR
|
||||||
|
} else if file_type.is_file() {
|
||||||
|
Node::MODE_FILE
|
||||||
|
} else if file_type.is_symlink() {
|
||||||
|
Node::MODE_SYMLINK
|
||||||
|
} else {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!("Does not support parsing {:?}", file_type),
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
|
let node_ptr;
|
||||||
|
{
|
||||||
|
let mode = mode_type | (metadata.mode() as u16 & Node::MODE_PERM);
|
||||||
|
let mut node = tx
|
||||||
|
.create_node(
|
||||||
|
parent_ptr,
|
||||||
|
&name,
|
||||||
|
mode,
|
||||||
|
metadata.ctime() as u64,
|
||||||
|
metadata.ctime_nsec() as u32,
|
||||||
|
)
|
||||||
|
.map_err(syscall_err)?;
|
||||||
|
|
||||||
|
node_ptr = node.ptr();
|
||||||
|
|
||||||
|
if node.data().uid() != metadata.uid() || node.data().gid() != metadata.gid() {
|
||||||
|
node.data_mut().set_uid(metadata.uid());
|
||||||
|
node.data_mut().set_gid(metadata.gid());
|
||||||
|
tx.sync_tree(node).map_err(syscall_err)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = entry.path();
|
||||||
|
if file_type.is_dir() {
|
||||||
|
archive_at(tx, path, node_ptr)?;
|
||||||
|
} else if file_type.is_file() {
|
||||||
|
let data = fs::read(path)?;
|
||||||
|
let count = tx
|
||||||
|
.write_node(
|
||||||
|
node_ptr,
|
||||||
|
0,
|
||||||
|
&data,
|
||||||
|
metadata.mtime() as u64,
|
||||||
|
metadata.mtime_nsec() as u32,
|
||||||
|
)
|
||||||
|
.map_err(syscall_err)?;
|
||||||
|
if count != data.len() {
|
||||||
|
panic!("file write count {} != {}", count, data.len());
|
||||||
|
}
|
||||||
|
} else if file_type.is_symlink() {
|
||||||
|
let destination = fs::read_link(path)?;
|
||||||
|
let data = destination.as_os_str().as_bytes();
|
||||||
|
let count = tx
|
||||||
|
.write_node(
|
||||||
|
node_ptr,
|
||||||
|
0,
|
||||||
|
data,
|
||||||
|
metadata.mtime() as u64,
|
||||||
|
metadata.mtime_nsec() as u32,
|
||||||
|
)
|
||||||
|
.map_err(syscall_err)?;
|
||||||
|
if count != data.len() {
|
||||||
|
panic!("symlink write count {} != {}", count, data.len());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::Other,
|
||||||
|
format!("Does not support creating {:?}", file_type),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn archive<D: Disk, P: AsRef<Path>>(fs: &mut FileSystem<D>, parent_path: P) -> io::Result<u64> {
|
||||||
|
let end_block = fs
|
||||||
|
.tx(|tx| {
|
||||||
|
// Archive_at root node
|
||||||
|
archive_at(tx, parent_path, 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_err)?;
|
||||||
|
|
||||||
|
Ok((fs.block + end_block) * BLOCK_SIZE)
|
||||||
|
}
|
||||||
+108
@@ -0,0 +1,108 @@
|
|||||||
|
extern crate redoxfs;
|
||||||
|
extern crate syscall;
|
||||||
|
extern crate uuid;
|
||||||
|
|
||||||
|
use std::io::Read;
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
use std::{env, fs, process};
|
||||||
|
|
||||||
|
use redoxfs::{archive, DiskFile, FileSystem};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
|
let mut args = env::args().skip(1);
|
||||||
|
|
||||||
|
let disk_path = if let Some(path) = args.next() {
|
||||||
|
path
|
||||||
|
} else {
|
||||||
|
println!("redoxfs-ar: no disk image provided");
|
||||||
|
println!("redoxfs-ar DISK FOLDER [BOOTLOADER]");
|
||||||
|
process::exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
let folder_path = if let Some(path) = args.next() {
|
||||||
|
path
|
||||||
|
} else {
|
||||||
|
println!("redoxfs-ar: no folder provided");
|
||||||
|
println!("redoxfs-ar DISK FOLDER [BOOTLOADER]");
|
||||||
|
process::exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
let bootloader_path_opt = args.next();
|
||||||
|
|
||||||
|
let disk = match DiskFile::open(&disk_path) {
|
||||||
|
Ok(disk) => disk,
|
||||||
|
Err(err) => {
|
||||||
|
println!("redoxfs-ar: failed to open image {}: {}", disk_path, err);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut bootloader = vec![];
|
||||||
|
if let Some(bootloader_path) = bootloader_path_opt {
|
||||||
|
match fs::File::open(&bootloader_path) {
|
||||||
|
Ok(mut file) => match file.read_to_end(&mut bootloader) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(err) => {
|
||||||
|
println!(
|
||||||
|
"redoxfs-ar: failed to read bootloader {}: {}",
|
||||||
|
bootloader_path, err
|
||||||
|
);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
println!(
|
||||||
|
"redoxfs-ar: failed to open bootloader {}: {}",
|
||||||
|
bootloader_path, err
|
||||||
|
);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let ctime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
|
||||||
|
match FileSystem::create_reserved(
|
||||||
|
disk,
|
||||||
|
None,
|
||||||
|
&bootloader,
|
||||||
|
ctime.as_secs(),
|
||||||
|
ctime.subsec_nanos(),
|
||||||
|
) {
|
||||||
|
Ok(mut fs) => {
|
||||||
|
let size = match archive(&mut fs, &folder_path) {
|
||||||
|
Ok(ok) => ok,
|
||||||
|
Err(err) => {
|
||||||
|
println!("redoxfs-ar: failed to archive {}: {}", folder_path, err);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(err) = fs.disk.file.set_len(size) {
|
||||||
|
println!(
|
||||||
|
"redoxfs-ar: failed to truncate {} to {}: {}",
|
||||||
|
disk_path, size, err
|
||||||
|
);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let uuid = Uuid::from_bytes(fs.header.uuid());
|
||||||
|
println!(
|
||||||
|
"redoxfs-ar: created filesystem on {}, reserved {} blocks, size {} MB, uuid {}",
|
||||||
|
disk_path,
|
||||||
|
fs.block,
|
||||||
|
fs.header.size() / 1000 / 1000,
|
||||||
|
uuid.hyphenated()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
println!(
|
||||||
|
"redoxfs-ar: failed to create filesystem on {}: {}",
|
||||||
|
disk_path, err
|
||||||
|
);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
extern crate redoxfs;
|
||||||
|
extern crate syscall;
|
||||||
|
extern crate uuid;
|
||||||
|
|
||||||
|
use std::io::Read;
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
use std::{env, fs, process};
|
||||||
|
|
||||||
|
use redoxfs::{clone, DiskFile, FileSystem};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
|
let mut args = env::args().skip(1);
|
||||||
|
|
||||||
|
let disk_path_old = if let Some(path) = args.next() {
|
||||||
|
path
|
||||||
|
} else {
|
||||||
|
println!("redoxfs-clone: no old disk image provided");
|
||||||
|
println!("redoxfs-clone NEW-DISK OLD-DISK [BOOTLOADER]");
|
||||||
|
process::exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
let disk_path = if let Some(path) = args.next() {
|
||||||
|
path
|
||||||
|
} else {
|
||||||
|
println!("redoxfs-clone: no new disk image provided");
|
||||||
|
println!("redoxfs-clone NEW-DISK OLD-DISK [BOOTLOADER]");
|
||||||
|
process::exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
let bootloader_path_opt = args.next();
|
||||||
|
|
||||||
|
// Open old disk in readonly mode
|
||||||
|
let disk_old = match fs::OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(false)
|
||||||
|
.open(&disk_path_old)
|
||||||
|
.map(DiskFile::from)
|
||||||
|
{
|
||||||
|
Ok(disk) => disk,
|
||||||
|
Err(err) => {
|
||||||
|
println!(
|
||||||
|
"redoxfs-clone: failed to open old disk image {}: {}",
|
||||||
|
disk_path_old, err
|
||||||
|
);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut fs_old = match FileSystem::open(disk_old, None, None, false) {
|
||||||
|
Ok(fs) => fs,
|
||||||
|
Err(err) => {
|
||||||
|
println!(
|
||||||
|
"redoxfs-clone: failed to open filesystem on {}: {}",
|
||||||
|
disk_path_old, err
|
||||||
|
);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let disk = match DiskFile::open(&disk_path) {
|
||||||
|
Ok(disk) => disk,
|
||||||
|
Err(err) => {
|
||||||
|
println!(
|
||||||
|
"redoxfs-clone: failed to open new disk image {}: {}",
|
||||||
|
disk_path, err
|
||||||
|
);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut bootloader = vec![];
|
||||||
|
if let Some(bootloader_path) = bootloader_path_opt {
|
||||||
|
match fs::File::open(&bootloader_path) {
|
||||||
|
Ok(mut file) => match file.read_to_end(&mut bootloader) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(err) => {
|
||||||
|
println!(
|
||||||
|
"redoxfs-clone: failed to read bootloader {}: {}",
|
||||||
|
bootloader_path, err
|
||||||
|
);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
println!(
|
||||||
|
"redoxfs-clone: failed to open bootloader {}: {}",
|
||||||
|
bootloader_path, err
|
||||||
|
);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let ctime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
|
||||||
|
let mut fs = match FileSystem::create_reserved(
|
||||||
|
disk,
|
||||||
|
None,
|
||||||
|
&bootloader,
|
||||||
|
ctime.as_secs(),
|
||||||
|
ctime.subsec_nanos(),
|
||||||
|
) {
|
||||||
|
Ok(fs) => fs,
|
||||||
|
Err(err) => {
|
||||||
|
println!(
|
||||||
|
"redoxfs-clone: failed to create filesystem on {}: {}",
|
||||||
|
disk_path, err
|
||||||
|
);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let size_old = fs_old.header.size();
|
||||||
|
let free_old = fs_old.allocator().free() * redoxfs::BLOCK_SIZE;
|
||||||
|
let used_old = size_old - free_old;
|
||||||
|
let mut last_percent = 0;
|
||||||
|
let clone_res = clone(&mut fs_old, &mut fs, move |used| {
|
||||||
|
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!();
|
||||||
|
match clone_res {
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(err) => {
|
||||||
|
println!(
|
||||||
|
"redoxfs-clone: failed to clone {} to {}: {}",
|
||||||
|
disk_path_old, disk_path, err
|
||||||
|
);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let uuid = Uuid::from_bytes(fs.header.uuid());
|
||||||
|
let size = fs.header.size();
|
||||||
|
let free = fs.allocator().free() * redoxfs::BLOCK_SIZE;
|
||||||
|
let used = size - free;
|
||||||
|
println!("redoxfs-clone: created filesystem on {}", disk_path,);
|
||||||
|
println!("\treserved: {} blocks", fs.block);
|
||||||
|
println!("\tuuid: {}", uuid.hyphenated());
|
||||||
|
println!("\tsize: {} MB", size / 1000 / 1000);
|
||||||
|
println!("\tused: {} MB", used / 1000 / 1000);
|
||||||
|
println!("\tfree: {} MB", free / 1000 / 1000);
|
||||||
|
}
|
||||||
+121
@@ -0,0 +1,121 @@
|
|||||||
|
extern crate redoxfs;
|
||||||
|
extern crate uuid;
|
||||||
|
|
||||||
|
use std::io::Read;
|
||||||
|
use std::{env, fs, io, process, time};
|
||||||
|
|
||||||
|
use redoxfs::{DiskFile, FileSystem};
|
||||||
|
use termion::input::TermRead;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
fn usage() -> ! {
|
||||||
|
eprintln!("redoxfs-mkfs [--encrypt] DISK [BOOTLOADER]");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
|
let mut encrypt = false;
|
||||||
|
let mut disk_path_opt = None;
|
||||||
|
let mut bootloader_path_opt = None;
|
||||||
|
for arg in env::args().skip(1) {
|
||||||
|
if arg == "--encrypt" {
|
||||||
|
encrypt = true;
|
||||||
|
} else if disk_path_opt.is_none() {
|
||||||
|
disk_path_opt = Some(arg);
|
||||||
|
} else if bootloader_path_opt.is_none() {
|
||||||
|
bootloader_path_opt = Some(arg);
|
||||||
|
} else {
|
||||||
|
eprintln!("redoxfs-mkfs: too many arguments provided");
|
||||||
|
usage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let disk_path = if let Some(path) = disk_path_opt {
|
||||||
|
path
|
||||||
|
} else {
|
||||||
|
eprintln!("redoxfs-mkfs: no disk image provided");
|
||||||
|
usage();
|
||||||
|
};
|
||||||
|
|
||||||
|
let disk = match DiskFile::open(&disk_path) {
|
||||||
|
Ok(disk) => disk,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("redoxfs-mkfs: failed to open image {}: {}", disk_path, err);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut bootloader = vec![];
|
||||||
|
if let Some(bootloader_path) = bootloader_path_opt {
|
||||||
|
match fs::File::open(&bootloader_path) {
|
||||||
|
Ok(mut file) => match file.read_to_end(&mut bootloader) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!(
|
||||||
|
"redoxfs-mkfs: failed to read bootloader {}: {}",
|
||||||
|
bootloader_path, err
|
||||||
|
);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!(
|
||||||
|
"redoxfs-mkfs: failed to open bootloader {}: {}",
|
||||||
|
bootloader_path, err
|
||||||
|
);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let password_opt = if encrypt {
|
||||||
|
eprint!("redoxfs-mkfs: password: ");
|
||||||
|
|
||||||
|
let password = io::stdin()
|
||||||
|
.read_passwd(&mut io::stderr())
|
||||||
|
.unwrap()
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
eprintln!();
|
||||||
|
|
||||||
|
if password.is_empty() {
|
||||||
|
eprintln!("redoxfs-mkfs: empty password, giving up");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(password)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let ctime = time::SystemTime::now()
|
||||||
|
.duration_since(time::UNIX_EPOCH)
|
||||||
|
.unwrap();
|
||||||
|
match FileSystem::create_reserved(
|
||||||
|
disk,
|
||||||
|
password_opt.as_ref().map(|x| x.as_bytes()),
|
||||||
|
&bootloader,
|
||||||
|
ctime.as_secs(),
|
||||||
|
ctime.subsec_nanos(),
|
||||||
|
) {
|
||||||
|
Ok(filesystem) => {
|
||||||
|
let uuid = Uuid::from_bytes(filesystem.header.uuid());
|
||||||
|
eprintln!(
|
||||||
|
"redoxfs-mkfs: created filesystem on {}, reserved {} blocks, size {} MB, uuid {}",
|
||||||
|
disk_path,
|
||||||
|
filesystem.block,
|
||||||
|
filesystem.header.size() / 1000 / 1000,
|
||||||
|
uuid.hyphenated()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!(
|
||||||
|
"redoxfs-mkfs: failed to create filesystem on {}: {}",
|
||||||
|
disk_path, err
|
||||||
|
);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,410 @@
|
|||||||
|
extern crate libc;
|
||||||
|
extern crate redoxfs;
|
||||||
|
#[cfg(target_os = "redox")]
|
||||||
|
extern crate syscall;
|
||||||
|
extern crate uuid;
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{self, Read, Write};
|
||||||
|
use std::os::unix::io::{FromRawFd, RawFd};
|
||||||
|
use std::process;
|
||||||
|
|
||||||
|
#[cfg(target_os = "redox")]
|
||||||
|
use std::{mem::MaybeUninit, ptr::addr_of_mut, sync::atomic::Ordering};
|
||||||
|
|
||||||
|
use redoxfs::{mount, DiskCache, DiskFile, FileSystem};
|
||||||
|
use termion::input::TermRead;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[cfg(target_os = "redox")]
|
||||||
|
extern "C" fn unmount_handler(_s: usize) {
|
||||||
|
redoxfs::IS_UMT.store(1, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "redox")]
|
||||||
|
//set up a signal handler on redox, this implements unmounting. I have no idea what sa_flags is
|
||||||
|
//for, so I put 2. I don't think 0,0 is a valid sa_mask. I don't know what i'm doing here. When u
|
||||||
|
//send it a sigkill, it shuts off the filesystem
|
||||||
|
fn setsig() {
|
||||||
|
// TODO: High-level wrapper like the nix crate?
|
||||||
|
unsafe {
|
||||||
|
let mut action = MaybeUninit::<libc::sigaction>::uninit();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
libc::sigemptyset(addr_of_mut!((*action.as_mut_ptr()).sa_mask)),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
addr_of_mut!((*action.as_mut_ptr()).sa_flags).write(0);
|
||||||
|
addr_of_mut!((*action.as_mut_ptr()).sa_sigaction)
|
||||||
|
.write(unmount_handler as *const () as usize);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
libc::sigaction(libc::SIGTERM, action.as_ptr(), core::ptr::null_mut()),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "redox"))]
|
||||||
|
// on linux, this is implemented properly, so no need for this unscrupulous nonsense!
|
||||||
|
fn setsig() {}
|
||||||
|
|
||||||
|
fn fork() -> isize {
|
||||||
|
unsafe { libc::fork() as isize }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pipe(pipes: &mut [i32; 2]) -> isize {
|
||||||
|
unsafe { libc::pipe(pipes.as_mut_ptr()) as isize }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "redox"))]
|
||||||
|
fn capability_mode() {}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "redox"))]
|
||||||
|
fn bootloader_password() -> Option<Vec<u8>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "redox")]
|
||||||
|
fn capability_mode() {
|
||||||
|
libredox::call::setrens(0, 0).expect("redoxfs: failed to enter null namespace");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "redox")]
|
||||||
|
fn bootloader_password() -> Option<Vec<u8>> {
|
||||||
|
use libredox::call::MmapArgs;
|
||||||
|
|
||||||
|
let addr_env = env::var_os("REDOXFS_PASSWORD_ADDR")?;
|
||||||
|
let size_env = env::var_os("REDOXFS_PASSWORD_SIZE")?;
|
||||||
|
|
||||||
|
let addr = usize::from_str_radix(
|
||||||
|
addr_env.to_str().expect("REDOXFS_PASSWORD_ADDR not valid"),
|
||||||
|
16,
|
||||||
|
)
|
||||||
|
.expect("failed to parse REDOXFS_PASSWORD_ADDR");
|
||||||
|
|
||||||
|
let size = usize::from_str_radix(
|
||||||
|
size_env.to_str().expect("REDOXFS_PASSWORD_SIZE not valid"),
|
||||||
|
16,
|
||||||
|
)
|
||||||
|
.expect("failed to parse REDOXFS_PASSWORD_SIZE");
|
||||||
|
|
||||||
|
let mut password = Vec::with_capacity(size);
|
||||||
|
unsafe {
|
||||||
|
let aligned_size = size.next_multiple_of(syscall::PAGE_SIZE);
|
||||||
|
|
||||||
|
let fd = libredox::Fd::open("memory:physical", libredox::flag::O_CLOEXEC, 0)
|
||||||
|
.expect("failed to open physical memory file");
|
||||||
|
|
||||||
|
let password_map = libredox::call::mmap(MmapArgs {
|
||||||
|
addr: core::ptr::null_mut(),
|
||||||
|
length: aligned_size,
|
||||||
|
prot: libredox::flag::PROT_READ,
|
||||||
|
flags: libredox::flag::MAP_SHARED,
|
||||||
|
fd: fd.raw(),
|
||||||
|
offset: addr as u64,
|
||||||
|
})
|
||||||
|
.expect("failed to map REDOXFS_PASSWORD")
|
||||||
|
.cast::<u8>();
|
||||||
|
|
||||||
|
for i in 0..size {
|
||||||
|
password.push(password_map.add(i).read());
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = libredox::call::munmap(password_map.cast(), aligned_size);
|
||||||
|
}
|
||||||
|
Some(password)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_err_exit(err: impl AsRef<str>) -> ! {
|
||||||
|
eprintln!("redoxfs: {}", err.as_ref());
|
||||||
|
usage();
|
||||||
|
process::exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn print_usage_exit() -> ! {
|
||||||
|
usage();
|
||||||
|
process::exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage() {
|
||||||
|
eprintln!("redoxfs [--no-daemon|-d] [--uuid] [disk or uuid] [mountpoint] [block in hex]");
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DiskId {
|
||||||
|
Path(String),
|
||||||
|
Uuid(Uuid),
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filesystem_by_path(
|
||||||
|
path: &str,
|
||||||
|
block_opt: Option<u64>,
|
||||||
|
log_errors: bool,
|
||||||
|
) -> Option<(String, FileSystem<DiskCache<DiskFile>>)> {
|
||||||
|
log::debug!("opening {}", path);
|
||||||
|
let attempts = 10;
|
||||||
|
for attempt in 0..=attempts {
|
||||||
|
let password_opt = if attempt > 0 {
|
||||||
|
eprint!("redoxfs: password: ");
|
||||||
|
|
||||||
|
let password = io::stdin()
|
||||||
|
.read_passwd(&mut io::stderr())
|
||||||
|
.unwrap()
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
eprintln!();
|
||||||
|
|
||||||
|
if password.is_empty() {
|
||||||
|
eprintln!("redoxfs: empty password, giving up");
|
||||||
|
|
||||||
|
// Password is empty, exit loop
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(password.into_bytes())
|
||||||
|
} else {
|
||||||
|
bootloader_password()
|
||||||
|
};
|
||||||
|
|
||||||
|
match DiskFile::open(path).map(DiskCache::new) {
|
||||||
|
Ok(disk) => {
|
||||||
|
match redoxfs::FileSystem::open(disk, password_opt.as_deref(), block_opt, true) {
|
||||||
|
Ok(filesystem) => {
|
||||||
|
log::debug!(
|
||||||
|
"opened filesystem on {} with uuid {}",
|
||||||
|
path,
|
||||||
|
Uuid::from_bytes(filesystem.header.uuid()).hyphenated()
|
||||||
|
);
|
||||||
|
|
||||||
|
return Some((path.to_string(), filesystem));
|
||||||
|
}
|
||||||
|
Err(err) => match err.errno {
|
||||||
|
syscall::ENOKEY => {
|
||||||
|
if password_opt.is_some() {
|
||||||
|
eprintln!("redoxfs: incorrect password ({}/{})", attempt, attempts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if log_errors {
|
||||||
|
log::error!("failed to open filesystem {}: {}", path, err);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
if log_errors {
|
||||||
|
log::error!("failed to open image {}: {}", path, err);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "redox"))]
|
||||||
|
fn filesystem_by_uuid(
|
||||||
|
_uuid: &Uuid,
|
||||||
|
_block_opt: Option<u64>,
|
||||||
|
) -> Option<(String, FileSystem<DiskCache<DiskFile>>)> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "redox")]
|
||||||
|
fn filesystem_by_uuid(
|
||||||
|
uuid: &Uuid,
|
||||||
|
block_opt: Option<u64>,
|
||||||
|
) -> Option<(String, FileSystem<DiskCache<DiskFile>>)> {
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
use redox_path::RedoxPath;
|
||||||
|
|
||||||
|
match fs::read_dir("/scheme") {
|
||||||
|
Ok(entries) => {
|
||||||
|
for entry_res in entries {
|
||||||
|
if let Ok(entry) = entry_res {
|
||||||
|
if let Some(disk) = entry.path().to_str() {
|
||||||
|
if RedoxPath::from_absolute(disk)
|
||||||
|
.unwrap_or(RedoxPath::from_absolute("/")?)
|
||||||
|
.is_scheme_category("disk")
|
||||||
|
{
|
||||||
|
log::debug!("found scheme {}", disk);
|
||||||
|
match fs::read_dir(disk) {
|
||||||
|
Ok(entries) => {
|
||||||
|
for entry_res in entries {
|
||||||
|
if let Ok(entry) = entry_res {
|
||||||
|
if let Ok(path) =
|
||||||
|
entry.path().into_os_string().into_string()
|
||||||
|
{
|
||||||
|
log::debug!("found path {}", path);
|
||||||
|
if let Some((path, filesystem)) =
|
||||||
|
filesystem_by_path(&path, block_opt, false)
|
||||||
|
{
|
||||||
|
if &filesystem.header.uuid() == uuid.as_bytes()
|
||||||
|
{
|
||||||
|
log::debug!(
|
||||||
|
"filesystem on {} matches uuid {}",
|
||||||
|
path,
|
||||||
|
uuid.hyphenated()
|
||||||
|
);
|
||||||
|
return Some((path, filesystem));
|
||||||
|
} else {
|
||||||
|
log::debug!(
|
||||||
|
"filesystem on {} does not match uuid {}",
|
||||||
|
path,
|
||||||
|
uuid.hyphenated()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::debug!("failed to list '{}': {}", disk, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("failed to list schemes: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn daemon(
|
||||||
|
disk_id: &DiskId,
|
||||||
|
mountpoint: &str,
|
||||||
|
block_opt: Option<u64>,
|
||||||
|
mut write: Option<File>,
|
||||||
|
) -> ! {
|
||||||
|
setsig();
|
||||||
|
|
||||||
|
let filesystem_opt = match *disk_id {
|
||||||
|
DiskId::Path(ref path) => filesystem_by_path(path, block_opt, true),
|
||||||
|
DiskId::Uuid(ref uuid) => filesystem_by_uuid(uuid, block_opt),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some((path, filesystem)) = filesystem_opt {
|
||||||
|
match mount(filesystem, mountpoint, |mounted_path| {
|
||||||
|
capability_mode();
|
||||||
|
|
||||||
|
log::info!(
|
||||||
|
"mounted filesystem on {} to {}",
|
||||||
|
path,
|
||||||
|
mounted_path.display()
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(ref mut write) = write {
|
||||||
|
let _ = write.write(&[0]);
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
Ok(()) => {
|
||||||
|
process::exit(0);
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::error!("failed to mount {} to {}: {}", path, mountpoint, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match *disk_id {
|
||||||
|
DiskId::Path(ref path) => {
|
||||||
|
log::error!("not able to mount path {}", path);
|
||||||
|
}
|
||||||
|
DiskId::Uuid(ref uuid) => {
|
||||||
|
log::error!("not able to mount uuid {}", uuid.hyphenated());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref mut write) = write {
|
||||||
|
let _ = write.write(&[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
|
let mut args = env::args().skip(1);
|
||||||
|
|
||||||
|
let mut daemonise = true;
|
||||||
|
let mut disk_id: Option<DiskId> = None;
|
||||||
|
let mut mountpoint: Option<String> = None;
|
||||||
|
let mut block_opt: Option<u64> = None;
|
||||||
|
|
||||||
|
while let Some(arg) = args.next() {
|
||||||
|
match arg.as_str() {
|
||||||
|
"--no-daemon" | "-d" => daemonise = false,
|
||||||
|
|
||||||
|
"--uuid" if disk_id.is_none() => {
|
||||||
|
disk_id = Some(DiskId::Uuid(
|
||||||
|
match args.next().as_deref().map(Uuid::parse_str) {
|
||||||
|
Some(Ok(uuid)) => uuid,
|
||||||
|
Some(Err(err)) => {
|
||||||
|
print_err_exit(format!("invalid uuid '{}': {}", arg, err))
|
||||||
|
}
|
||||||
|
None => print_err_exit("no uuid provided"),
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
disk if disk_id.is_none() => disk_id = Some(DiskId::Path(disk.to_owned())),
|
||||||
|
|
||||||
|
mnt if disk_id.is_some() && mountpoint.is_none() => mountpoint = Some(mnt.to_owned()),
|
||||||
|
|
||||||
|
opts if mountpoint.is_some() => match u64::from_str_radix(opts, 16) {
|
||||||
|
Ok(block) => block_opt = Some(block),
|
||||||
|
Err(err) => print_err_exit(format!("invalid block '{}': {}", opts, err)),
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => print_usage_exit(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(disk_id) = disk_id else {
|
||||||
|
print_err_exit("no disk provided");
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(mountpoint) = mountpoint else {
|
||||||
|
print_err_exit("no mountpoint provided");
|
||||||
|
};
|
||||||
|
|
||||||
|
if daemonise {
|
||||||
|
let mut pipes = [0; 2];
|
||||||
|
if pipe(&mut pipes) == 0 {
|
||||||
|
let mut read = unsafe { File::from_raw_fd(pipes[0] as RawFd) };
|
||||||
|
let write = unsafe { File::from_raw_fd(pipes[1] as RawFd) };
|
||||||
|
|
||||||
|
let pid = fork();
|
||||||
|
if pid == 0 {
|
||||||
|
drop(read);
|
||||||
|
|
||||||
|
daemon(&disk_id, &mountpoint, block_opt, Some(write));
|
||||||
|
} else if pid > 0 {
|
||||||
|
drop(write);
|
||||||
|
|
||||||
|
let mut res = [0];
|
||||||
|
read.read_exact(&mut res).unwrap();
|
||||||
|
|
||||||
|
process::exit(res[0] as i32);
|
||||||
|
} else {
|
||||||
|
panic!("redoxfs: failed to fork");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!("redoxfs: failed to create pipe");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log::info!("running in foreground");
|
||||||
|
daemon(&disk_id, &mountpoint, block_opt, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
use std::{env, process};
|
||||||
|
|
||||||
|
use humansize::{format_size, BINARY, DECIMAL};
|
||||||
|
use redoxfs::{BlockAddr, BlockMeta, Disk, DiskFile, FileSystem};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
fn resize<D: Disk>(fs: &mut FileSystem<D>, size_arg: String) -> Result<(), String> {
|
||||||
|
let disk_size = fs
|
||||||
|
.disk
|
||||||
|
.size()
|
||||||
|
.map_err(|err| format!("failed to read disk size: {}", err))?;
|
||||||
|
|
||||||
|
// Find contiguous free region
|
||||||
|
//TODO: better error management
|
||||||
|
let mut last_free = None;
|
||||||
|
let mut last_end = 0;
|
||||||
|
fs.tx(|tx| {
|
||||||
|
let mut alloc_ptr = tx.header.alloc;
|
||||||
|
while !alloc_ptr.is_null() {
|
||||||
|
let alloc = tx.read_block(alloc_ptr)?;
|
||||||
|
alloc_ptr = alloc.data().prev;
|
||||||
|
for entry in alloc.data().entries.iter() {
|
||||||
|
let count = entry.count();
|
||||||
|
if count <= 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let end = entry.index() + count as u64;
|
||||||
|
if end > last_end {
|
||||||
|
last_free = Some(*entry);
|
||||||
|
last_end = end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.map_err(|err| format!("failed to read alloc log: {}", err))?;
|
||||||
|
|
||||||
|
let old_size = fs.header.size();
|
||||||
|
let min_size = if let Some(entry) = last_free {
|
||||||
|
entry.index() * redoxfs::BLOCK_SIZE
|
||||||
|
} else {
|
||||||
|
old_size
|
||||||
|
};
|
||||||
|
let max_size = disk_size - (fs.block * redoxfs::BLOCK_SIZE);
|
||||||
|
|
||||||
|
let new_size = match size_arg.to_lowercase().as_str() {
|
||||||
|
"min" | "minimum" => min_size,
|
||||||
|
"" | "max" | "maximum" => max_size,
|
||||||
|
_ => match parse_size::parse_size(&size_arg) {
|
||||||
|
Ok(new_size) => {
|
||||||
|
if new_size < min_size {
|
||||||
|
return Err(format!(
|
||||||
|
"requested size {} is smaller than {} by {}",
|
||||||
|
new_size,
|
||||||
|
min_size,
|
||||||
|
min_size - new_size
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if new_size > max_size {
|
||||||
|
return Err(format!(
|
||||||
|
"requested size {} is larger than {} by {}",
|
||||||
|
new_size,
|
||||||
|
max_size,
|
||||||
|
new_size - max_size
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
new_size
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
return Err(format!(
|
||||||
|
"failed to parse size argument {:?}: {}",
|
||||||
|
size_arg, err
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"minimum size: {} ({})",
|
||||||
|
format_size(min_size, DECIMAL),
|
||||||
|
format_size(min_size, BINARY)
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"maximum size: {} ({})",
|
||||||
|
format_size(max_size, DECIMAL),
|
||||||
|
format_size(max_size, BINARY)
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"new size: {} ({})",
|
||||||
|
format_size(new_size, DECIMAL),
|
||||||
|
format_size(new_size, BINARY)
|
||||||
|
);
|
||||||
|
|
||||||
|
let old_blocks = old_size / redoxfs::BLOCK_SIZE;
|
||||||
|
let new_blocks = new_size / redoxfs::BLOCK_SIZE;
|
||||||
|
let (start, end, shrink) = if new_size == old_size {
|
||||||
|
println!("already requested size");
|
||||||
|
return Ok(());
|
||||||
|
} else if new_size < old_size {
|
||||||
|
println!("shrinking by {}", old_size - new_size);
|
||||||
|
(new_blocks, old_blocks, true)
|
||||||
|
} else {
|
||||||
|
println!("growing by {}", new_size - old_size);
|
||||||
|
(old_blocks, new_blocks, false)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Allocate or deallocate blocks as needed
|
||||||
|
unsafe {
|
||||||
|
let allocator = fs.allocator_mut();
|
||||||
|
for index in start..end {
|
||||||
|
if shrink {
|
||||||
|
//TODO: replace assert with error?
|
||||||
|
let addr = BlockAddr::new(index as u64, BlockMeta::default());
|
||||||
|
assert_eq!(allocator.allocate_exact(addr), Some(addr));
|
||||||
|
} else {
|
||||||
|
let addr = BlockAddr::new(index as u64, BlockMeta::default());
|
||||||
|
allocator.deallocate(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.tx(|tx| {
|
||||||
|
// Update header
|
||||||
|
tx.header.size = new_size.into();
|
||||||
|
tx.header_changed = true;
|
||||||
|
|
||||||
|
// Sync with squash
|
||||||
|
tx.sync(true)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
.map_err(|err| format!("transaction failed: {}", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
|
let mut args = env::args().skip(1);
|
||||||
|
|
||||||
|
let disk_path = if let Some(path) = args.next() {
|
||||||
|
path
|
||||||
|
} else {
|
||||||
|
eprintln!("redoxfs-resize: no new disk image provided");
|
||||||
|
eprintln!("redoxfs-resize NEW-DISK [SIZE]");
|
||||||
|
process::exit(1);
|
||||||
|
};
|
||||||
|
|
||||||
|
let size_arg = args.next().unwrap_or_default();
|
||||||
|
|
||||||
|
let disk = match DiskFile::open(&disk_path) {
|
||||||
|
Ok(disk) => disk,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!(
|
||||||
|
"redoxfs-resize: failed to open disk image {}: {}",
|
||||||
|
disk_path, err
|
||||||
|
);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut fs = match FileSystem::open(disk, None, None, true) {
|
||||||
|
Ok(fs) => fs,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!(
|
||||||
|
"redoxfs-resize: failed to open filesystem on {}: {}",
|
||||||
|
disk_path, err
|
||||||
|
);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match resize(&mut fs, size_arg) {
|
||||||
|
Ok(()) => {}
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!(
|
||||||
|
"redoxfs-resize: failed to resize filesystem on {}: {}",
|
||||||
|
disk_path, err
|
||||||
|
);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let uuid = Uuid::from_bytes(fs.header.uuid());
|
||||||
|
let size = fs.header.size();
|
||||||
|
let free = fs.allocator().free() * redoxfs::BLOCK_SIZE;
|
||||||
|
let used = size - free;
|
||||||
|
println!("redoxfs-resize: resized filesystem on {}", disk_path);
|
||||||
|
println!("\tuuid: {}", uuid.hyphenated());
|
||||||
|
println!(
|
||||||
|
"\tsize: {} ({})",
|
||||||
|
format_size(size, DECIMAL),
|
||||||
|
format_size(size, BINARY)
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"\tused: {} ({})",
|
||||||
|
format_size(used, DECIMAL),
|
||||||
|
format_size(used, BINARY)
|
||||||
|
);
|
||||||
|
println!(
|
||||||
|
"\tfree: {} ({})",
|
||||||
|
format_size(free, DECIMAL),
|
||||||
|
format_size(free, BINARY)
|
||||||
|
);
|
||||||
|
}
|
||||||
+393
@@ -0,0 +1,393 @@
|
|||||||
|
use core::{fmt, marker::PhantomData, mem, ops, slice};
|
||||||
|
use endian_num::Le;
|
||||||
|
|
||||||
|
use crate::BLOCK_SIZE;
|
||||||
|
|
||||||
|
const BLOCK_LIST_ENTRIES: usize = BLOCK_SIZE as usize / mem::size_of::<BlockPtr<BlockRaw>>();
|
||||||
|
|
||||||
|
/// An address of a data block.
|
||||||
|
///
|
||||||
|
/// This encodes a block's position _and_ [`BlockLevel`]:
|
||||||
|
/// the first four bits of this `u64` encode the block's level,
|
||||||
|
/// the next four bits indicates decompression level,
|
||||||
|
/// the rest encode its index.
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
|
pub struct BlockAddr(u64);
|
||||||
|
|
||||||
|
impl BlockAddr {
|
||||||
|
const INDEX_SHIFT: u64 = 8;
|
||||||
|
const DECOMP_LEVEL_MASK: u64 = 0xF0;
|
||||||
|
const DECOMP_LEVEL_SHIFT: u64 = 4;
|
||||||
|
const LEVEL_MASK: u64 = 0xF;
|
||||||
|
|
||||||
|
// Unsafe because this can create invalid blocks
|
||||||
|
pub unsafe fn new(index: u64, meta: BlockMeta) -> Self {
|
||||||
|
// Level must fit within LEVEL_MASK
|
||||||
|
if meta.level.0 > Self::LEVEL_MASK as usize {
|
||||||
|
panic!("block level too large");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decomp level must fit within DECOMP_LEVEL_MASK
|
||||||
|
let decomp_level = meta.decomp_level.unwrap_or_default();
|
||||||
|
if (decomp_level.0 << Self::DECOMP_LEVEL_SHIFT) > Self::DECOMP_LEVEL_MASK as usize {
|
||||||
|
panic!("decompressed block level too large");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index must not use the metadata bits
|
||||||
|
let inner = index
|
||||||
|
.checked_shl(Self::INDEX_SHIFT as u32)
|
||||||
|
.expect("block index too large")
|
||||||
|
| ((decomp_level.0 as u64) << Self::DECOMP_LEVEL_SHIFT)
|
||||||
|
| (meta.level.0 as u64);
|
||||||
|
Self(inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn null(meta: BlockMeta) -> Self {
|
||||||
|
unsafe { Self::new(0, meta) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn index(&self) -> u64 {
|
||||||
|
// The first four bits store the level
|
||||||
|
self.0 >> Self::INDEX_SHIFT
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn level(&self) -> BlockLevel {
|
||||||
|
// The first four bits store the level
|
||||||
|
BlockLevel((self.0 & Self::LEVEL_MASK) as usize)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decomp_level(&self) -> Option<BlockLevel> {
|
||||||
|
let value = (self.0 & Self::DECOMP_LEVEL_MASK) >> Self::DECOMP_LEVEL_SHIFT;
|
||||||
|
if value != 0 {
|
||||||
|
Some(BlockLevel(value as usize))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn meta(&self) -> BlockMeta {
|
||||||
|
BlockMeta {
|
||||||
|
level: self.level(),
|
||||||
|
decomp_level: self.decomp_level(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_null(&self) -> bool {
|
||||||
|
self.index() == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
|
||||||
|
pub struct BlockMeta {
|
||||||
|
pub(crate) level: BlockLevel,
|
||||||
|
pub(crate) decomp_level: Option<BlockLevel>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockMeta {
|
||||||
|
pub fn new(level: BlockLevel) -> Self {
|
||||||
|
Self {
|
||||||
|
level,
|
||||||
|
decomp_level: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_compressed(level: BlockLevel, decomp_level: BlockLevel) -> Self {
|
||||||
|
Self {
|
||||||
|
level,
|
||||||
|
decomp_level: Some(decomp_level),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The size of a block.
|
||||||
|
///
|
||||||
|
/// Level 0 blocks are blocks of [`BLOCK_SIZE`] bytes.
|
||||||
|
/// A level 1 block consists of two consecutive level 0 blocks.
|
||||||
|
/// A level n block consists of two consecutive level n-1 blocks.
|
||||||
|
///
|
||||||
|
/// See [`crate::Allocator`] docs for more details.
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||||
|
pub struct BlockLevel(pub(crate) usize);
|
||||||
|
|
||||||
|
impl BlockLevel {
|
||||||
|
/// Returns the smallest block level that can contain
|
||||||
|
/// the given number of bytes.
|
||||||
|
pub(crate) fn for_bytes(bytes: u64) -> Self {
|
||||||
|
if bytes == 0 {
|
||||||
|
return BlockLevel(0);
|
||||||
|
}
|
||||||
|
let level = bytes
|
||||||
|
.div_ceil(BLOCK_SIZE)
|
||||||
|
.next_power_of_two()
|
||||||
|
.trailing_zeros() as usize;
|
||||||
|
BlockLevel(level)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The number of [`BLOCK_SIZE`] blocks (i.e, level 0 blocks)
|
||||||
|
/// in a block of this level
|
||||||
|
pub fn blocks<T: From<u32>>(self) -> T {
|
||||||
|
T::from(1u32 << self.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The number of bytes in a block of this level
|
||||||
|
pub fn bytes(self) -> u64 {
|
||||||
|
BLOCK_SIZE << self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe trait BlockTrait {
|
||||||
|
/// Create an empty block of this type.
|
||||||
|
fn empty(level: BlockLevel) -> Option<Self>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [`BlockAddr`] and the data it points to.
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct BlockData<T> {
|
||||||
|
addr: BlockAddr,
|
||||||
|
data: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> BlockData<T> {
|
||||||
|
pub fn new(addr: BlockAddr, data: T) -> Self {
|
||||||
|
Self { addr, data }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addr(&self) -> BlockAddr {
|
||||||
|
self.addr
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data(&self) -> &T {
|
||||||
|
&self.data
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data_mut(&mut self) -> &mut T {
|
||||||
|
&mut self.data
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn into_parts(self) -> (BlockAddr, T) {
|
||||||
|
(self.addr, self.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the address of this [`BlockData`] to `addr`, returning this
|
||||||
|
/// block's old address. This method does not update block data.
|
||||||
|
///
|
||||||
|
/// `addr` must point to a block with the same level as this block.
|
||||||
|
#[must_use = "don't forget to de-allocate old block address"]
|
||||||
|
pub fn swap_addr(&mut self, addr: BlockAddr) -> BlockAddr {
|
||||||
|
// Address levels must match
|
||||||
|
assert_eq!(self.addr.level(), addr.level());
|
||||||
|
let old = self.addr;
|
||||||
|
self.addr = addr;
|
||||||
|
old
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: BlockTrait> BlockData<T> {
|
||||||
|
pub fn empty(addr: BlockAddr) -> Option<Self> {
|
||||||
|
let empty = T::empty(addr.level())?;
|
||||||
|
Some(Self::new(addr, empty))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: ops::Deref<Target = [u8]>> BlockData<T> {
|
||||||
|
pub fn create_ptr(&self) -> BlockPtr<T> {
|
||||||
|
BlockPtr {
|
||||||
|
addr: self.addr.0.into(),
|
||||||
|
hash: seahash::hash(self.data.deref()).into(),
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct BlockList<T> {
|
||||||
|
pub ptrs: [BlockPtr<T>; BLOCK_LIST_ENTRIES],
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl<T> BlockTrait for BlockList<T> {
|
||||||
|
fn empty(level: BlockLevel) -> Option<Self> {
|
||||||
|
if level.0 == 0 {
|
||||||
|
Some(Self {
|
||||||
|
ptrs: [BlockPtr::default(); BLOCK_LIST_ENTRIES],
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> BlockList<T> {
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.ptrs.iter().all(|ptr| ptr.is_null())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ops::Deref for BlockList<T> {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const BlockList<T> as *const u8,
|
||||||
|
mem::size_of::<BlockList<T>>(),
|
||||||
|
) as &[u8]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> ops::DerefMut for BlockList<T> {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self as *mut BlockList<T> as *mut u8,
|
||||||
|
mem::size_of::<BlockList<T>>(),
|
||||||
|
) as &mut [u8]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An address of a data block, along with a checksum of its data.
|
||||||
|
///
|
||||||
|
/// This encodes a block's position _and_ [`BlockLevel`].
|
||||||
|
/// the first four bits of `addr` encode the block's level,
|
||||||
|
/// the rest encode its index.
|
||||||
|
///
|
||||||
|
/// Also see [`BlockAddr`].
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct BlockPtr<T> {
|
||||||
|
addr: Le<u64>,
|
||||||
|
hash: Le<u64>,
|
||||||
|
phantom: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> BlockPtr<T> {
|
||||||
|
pub fn null(meta: BlockMeta) -> Self {
|
||||||
|
Self {
|
||||||
|
addr: BlockAddr::null(meta).0.into(),
|
||||||
|
hash: 0.into(),
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn addr(&self) -> BlockAddr {
|
||||||
|
BlockAddr(self.addr.to_ne())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hash(&self) -> u64 {
|
||||||
|
self.hash.to_ne()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_null(&self) -> bool {
|
||||||
|
self.addr().is_null()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn marker(level: u8) -> Self {
|
||||||
|
assert!(level <= 0xF);
|
||||||
|
Self {
|
||||||
|
addr: (0xFFFF_FFFF_FFFF_FFF0 | (level as u64)).into(),
|
||||||
|
hash: u64::MAX.into(),
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_marker(&self) -> bool {
|
||||||
|
(self.addr.to_ne() | 0xF) == u64::MAX && self.hash.to_ne() == u64::MAX
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cast BlockPtr to another type
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// Unsafe because it can be used to transmute types
|
||||||
|
pub unsafe fn cast<U>(self) -> BlockPtr<U> {
|
||||||
|
BlockPtr {
|
||||||
|
addr: self.addr,
|
||||||
|
hash: self.hash,
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use = "the returned pointer should usually be deallocated"]
|
||||||
|
pub fn clear(&mut self) -> BlockPtr<T> {
|
||||||
|
let mut ptr = Self::default();
|
||||||
|
mem::swap(self, &mut ptr);
|
||||||
|
ptr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Clone for BlockPtr<T> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Copy for BlockPtr<T> {}
|
||||||
|
|
||||||
|
impl<T> Default for BlockPtr<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
addr: 0.into(),
|
||||||
|
hash: 0.into(),
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> fmt::Debug for BlockPtr<T> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let addr = self.addr();
|
||||||
|
let hash = self.hash();
|
||||||
|
f.debug_struct("BlockPtr")
|
||||||
|
.field("addr", &addr)
|
||||||
|
.field("hash", &hash)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C, packed)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct BlockRaw([u8; BLOCK_SIZE as usize]);
|
||||||
|
|
||||||
|
unsafe impl BlockTrait for BlockRaw {
|
||||||
|
fn empty(level: BlockLevel) -> Option<Self> {
|
||||||
|
if level.0 == 0 {
|
||||||
|
Some(Self([0; BLOCK_SIZE as usize]))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Deref for BlockRaw {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::DerefMut for BlockRaw {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn block_list_size_test() {
|
||||||
|
assert_eq!(mem::size_of::<BlockList<BlockRaw>>(), BLOCK_SIZE as usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn block_raw_size_test() {
|
||||||
|
assert_eq!(mem::size_of::<BlockRaw>(), BLOCK_SIZE as usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn block_ptr_marker_test() {
|
||||||
|
let ptr = BlockPtr::<BlockRaw>::marker(0);
|
||||||
|
assert_eq!(ptr.addr().level().0, 0);
|
||||||
|
assert!(ptr.is_marker());
|
||||||
|
|
||||||
|
let ptr = BlockPtr::<BlockRaw>::marker(2);
|
||||||
|
assert_eq!(ptr.addr().level().0, 2);
|
||||||
|
assert!(ptr.is_marker());
|
||||||
|
}
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
use crate::platform::types::c_char;
|
|
||||||
|
|
||||||
/// An abstraction over a byte literal to provide a method to convert safely
|
|
||||||
/// to a `c_char`.
|
|
||||||
///
|
|
||||||
/// The abstraction is required so we can contain architecture specific code
|
|
||||||
/// in a central location.
|
|
||||||
pub struct ByteLiteral;
|
|
||||||
|
|
||||||
impl ByteLiteral {
|
|
||||||
/// Casts a byte literal (`u8`) to a `c_char` without using `as`.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
/// If `input` is not within the following range of ascii characters:
|
|
||||||
/// - Octal: `040`..=`176`
|
|
||||||
/// - Decimal: `30`..=`126`
|
|
||||||
/// - Hexadecimal: `20`..=`7E`
|
|
||||||
/// - Byte literals: The space character (` `) upto and including tilde (`~`)
|
|
||||||
pub const fn cast_cchar(input: u8) -> c_char {
|
|
||||||
match input {
|
|
||||||
b' '..=b'~' => {
|
|
||||||
// `c_char` is an `i8` on these arches
|
|
||||||
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
|
|
||||||
{
|
|
||||||
input.cast_signed()
|
|
||||||
}
|
|
||||||
// `c_char` is already a `u8` on these arches
|
|
||||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
|
|
||||||
{
|
|
||||||
input
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => panic!("Not a printable ascii character!"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
double strtod(const char *nptr, char **endptr);
|
|
||||||
|
|
||||||
long double strtold(const char *nptr, char **endptr) {
|
|
||||||
return (long double)strtod(nptr, endptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
double relibc_ldtod(const long double* val) {
|
|
||||||
return (double)(*val);
|
|
||||||
}
|
|
||||||
|
|
||||||
void relibc_dtold(double val, long double* out) {
|
|
||||||
*out = (long double)val;
|
|
||||||
}
|
|
||||||
-326
@@ -1,326 +0,0 @@
|
|||||||
//! Nul-terminated byte strings.
|
|
||||||
|
|
||||||
use core::{marker::PhantomData, ptr::NonNull, str::Utf8Error};
|
|
||||||
|
|
||||||
use alloc::{borrow::Cow, string::String};
|
|
||||||
|
|
||||||
use crate::platform::types::{c_char, wchar_t};
|
|
||||||
|
|
||||||
mod private {
|
|
||||||
pub trait Sealed {}
|
|
||||||
}
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub enum Thin {}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
pub enum Wide {}
|
|
||||||
|
|
||||||
impl private::Sealed for Thin {}
|
|
||||||
impl private::Sealed for Wide {}
|
|
||||||
|
|
||||||
pub trait Kind: private::Sealed + Copy + 'static {
|
|
||||||
/// c_char or wchar_t
|
|
||||||
type C: Copy + 'static;
|
|
||||||
// u8 or u32
|
|
||||||
type Char: Copy + From<u8> + Into<u32> + PartialEq + 'static;
|
|
||||||
|
|
||||||
const NUL: Self::Char;
|
|
||||||
|
|
||||||
const IS_THIN_NOT_WIDE: bool;
|
|
||||||
|
|
||||||
fn r2c(c: Self::Char) -> Self::C;
|
|
||||||
fn c2r(c: Self::C) -> Self::Char;
|
|
||||||
|
|
||||||
fn chars_from_bytes(b: &[u8]) -> Option<&[Self::Char]>;
|
|
||||||
fn chars_to_bytes(c: &[Self::Char]) -> Option<&[u8]>;
|
|
||||||
|
|
||||||
unsafe fn strlen(s: *const Self::C) -> usize;
|
|
||||||
unsafe fn strchr(s: *const Self::C, c: Self::C) -> *const Self::C;
|
|
||||||
unsafe fn strchrnul(s: *const Self::C, c: Self::C) -> *const Self::C;
|
|
||||||
}
|
|
||||||
impl Kind for Thin {
|
|
||||||
type C = c_char;
|
|
||||||
type Char = u8;
|
|
||||||
|
|
||||||
const NUL: Self::Char = 0;
|
|
||||||
const IS_THIN_NOT_WIDE: bool = true;
|
|
||||||
|
|
||||||
unsafe fn strlen(s: *const c_char) -> usize {
|
|
||||||
unsafe { crate::header::string::strlen(s) }
|
|
||||||
}
|
|
||||||
unsafe fn strchr(s: *const c_char, c: c_char) -> *const c_char {
|
|
||||||
unsafe { crate::header::string::strchr(s, c.into()) }
|
|
||||||
}
|
|
||||||
unsafe fn strchrnul(s: *const c_char, c: c_char) -> *const c_char {
|
|
||||||
unsafe { crate::header::string::strchrnul(s, c.into()) }
|
|
||||||
}
|
|
||||||
fn r2c(c: u8) -> c_char {
|
|
||||||
c as _
|
|
||||||
}
|
|
||||||
fn c2r(c: c_char) -> u8 {
|
|
||||||
c as _
|
|
||||||
}
|
|
||||||
fn chars_from_bytes(b: &[u8]) -> Option<&[Self::Char]> {
|
|
||||||
Some(b)
|
|
||||||
}
|
|
||||||
fn chars_to_bytes(c: &[Self::Char]) -> Option<&[u8]> {
|
|
||||||
Some(c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Kind for Wide {
|
|
||||||
type C = wchar_t;
|
|
||||||
type Char = u32;
|
|
||||||
|
|
||||||
const NUL: Self::Char = 0;
|
|
||||||
const IS_THIN_NOT_WIDE: bool = false;
|
|
||||||
|
|
||||||
unsafe fn strlen(s: *const Self::C) -> usize {
|
|
||||||
unsafe { crate::header::wchar::wcslen(s) }
|
|
||||||
}
|
|
||||||
unsafe fn strchr(s: *const Self::C, c: Self::C) -> *const Self::C {
|
|
||||||
unsafe { crate::header::wchar::wcschr(s, c) }
|
|
||||||
}
|
|
||||||
unsafe fn strchrnul(mut s: *const Self::C, c: Self::C) -> *const Self::C {
|
|
||||||
// TODO: optimized function
|
|
||||||
while unsafe { s.read() } != c && unsafe { s.read() } != 0 {
|
|
||||||
s = unsafe { s.add(1) };
|
|
||||||
}
|
|
||||||
s
|
|
||||||
}
|
|
||||||
fn r2c(c: Self::Char) -> Self::C {
|
|
||||||
c as _
|
|
||||||
}
|
|
||||||
fn c2r(c: Self::C) -> Self::Char {
|
|
||||||
c as _
|
|
||||||
}
|
|
||||||
fn chars_from_bytes(b: &[u8]) -> Option<&[Self::Char]> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
fn chars_to_bytes(c: &[Self::Char]) -> Option<&[u8]> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Safe wrapper for immutable borrowed C strings, guaranteed to be the same layout as `*const u8`.
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
#[repr(transparent)]
|
|
||||||
pub struct NulStr<'a, T: Kind> {
|
|
||||||
ptr: NonNull<T::C>,
|
|
||||||
_marker: PhantomData<&'a [u8]>,
|
|
||||||
}
|
|
||||||
pub type CStr<'a> = NulStr<'a, Thin>;
|
|
||||||
pub type WStr<'a> = NulStr<'a, Wide>;
|
|
||||||
|
|
||||||
impl<'a, T: Kind> NulStr<'a, T> {
|
|
||||||
/// Safety
|
|
||||||
///
|
|
||||||
/// The ptr must be valid up to and including the first NUL byte from the base ptr.
|
|
||||||
pub const unsafe fn from_ptr(ptr: *const T::C) -> Self {
|
|
||||||
Self {
|
|
||||||
ptr: unsafe { NonNull::new_unchecked(ptr.cast_mut()) },
|
|
||||||
_marker: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub unsafe fn from_nullable_ptr(ptr: *const T::C) -> Option<Self> {
|
|
||||||
if ptr.is_null() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(unsafe { Self::from_ptr(ptr) })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Look for the closest occurence of `c`, and if found, split the string into a slice up to
|
|
||||||
/// that byte and a `CStr` starting at that byte.
|
|
||||||
#[inline]
|
|
||||||
#[doc(alias = "strchrnul")]
|
|
||||||
pub fn find_get_subslice_or_all(
|
|
||||||
self,
|
|
||||||
c: impl Into<T::Char>,
|
|
||||||
) -> Result<(&'a [T::Char], Self), (&'a [T::Char], Self)> {
|
|
||||||
let c = c.into();
|
|
||||||
|
|
||||||
// SAFETY: strchrnul expects self.as_ptr() to be valid up to and including its last NUL
|
|
||||||
// byte
|
|
||||||
let found = unsafe { T::strchrnul(self.as_ptr(), T::r2c(c)) };
|
|
||||||
|
|
||||||
// SAFETY: the pointer returned from strchrnul is always a substring of this string, and
|
|
||||||
// hence always valid as a CStr.
|
|
||||||
let found = unsafe { Self::from_ptr(found) };
|
|
||||||
let until = unsafe { self.slice_until_substr(found) };
|
|
||||||
|
|
||||||
if found.first() == T::NUL {
|
|
||||||
// The character was not found, and we got the end of the string instead.
|
|
||||||
Err((until, found))
|
|
||||||
} else {
|
|
||||||
Ok((until, found))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// # Safety
|
|
||||||
///
|
|
||||||
/// `substr` must be contained within `self`
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn slice_until_substr(self, substr: NulStr<'_, T>) -> &'a [T::Char] {
|
|
||||||
let index = unsafe {
|
|
||||||
// SAFETY: the sub-pointer as returned by strchr must be derived from the same
|
|
||||||
// allocation
|
|
||||||
substr.as_ptr().offset_from(self.as_ptr()) as usize
|
|
||||||
};
|
|
||||||
unsafe { core::slice::from_raw_parts(self.as_ptr().cast::<T::Char>(), index) }
|
|
||||||
}
|
|
||||||
/// Look for the closest occurence of `c`, and if found, split the string into a slice up to
|
|
||||||
/// that byte and a `CStr` starting at that byte.
|
|
||||||
#[inline]
|
|
||||||
pub fn find_get_subslice(self, c: T::Char) -> Option<(&'a [T::Char], Self)> {
|
|
||||||
let rest = self.find(c)?;
|
|
||||||
|
|
||||||
// SAFETY: the output of strchr is obviously a substring if it doesn't return NULL
|
|
||||||
Some((unsafe { self.slice_until_substr(rest) }, rest))
|
|
||||||
}
|
|
||||||
/// Look for the closest occurence of `c`, and return a new string starting at that byte if
|
|
||||||
/// found.
|
|
||||||
#[doc(alias = "strchr")]
|
|
||||||
#[doc(alias = "wcschr")]
|
|
||||||
#[inline]
|
|
||||||
pub fn find(self, c: T::Char) -> Option<Self> {
|
|
||||||
unsafe {
|
|
||||||
// SAFETY: the only requirement is for self.as_ptr() to be valid up to and including
|
|
||||||
// the nearest NUL byte, which this type requires
|
|
||||||
let ret = T::strchr(self.as_ptr(), T::r2c(c));
|
|
||||||
// SAFETY: strchr must either return NULL (not found) or a substring of self, which can
|
|
||||||
// never exceed the nearest NUL byte of self
|
|
||||||
Self::from_nullable_ptr(ret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: strrchr, strchrnul wrappers
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn contains(self, c: T::Char) -> bool {
|
|
||||||
self.find(c).is_some()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn first(self) -> T::Char {
|
|
||||||
unsafe {
|
|
||||||
// SAFETY: Self must be valid up to and including its nearest NUL byte, which certainly
|
|
||||||
// implies its readable length is nonzero (string is empty if this first byte is 0).
|
|
||||||
T::c2r(self.ptr.read())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn first_char(self) -> Option<char> {
|
|
||||||
char::from_u32(self.first().into())
|
|
||||||
}
|
|
||||||
/// Same as `split_first` except also requires that the first char be convertible into `char`
|
|
||||||
#[inline]
|
|
||||||
pub fn split_first_char(self) -> Option<(char, Self)> {
|
|
||||||
self.split_first()
|
|
||||||
.and_then(|(c, r)| Some((char::from_u32(c.into())?, r)))
|
|
||||||
}
|
|
||||||
/// Split this string into `Some((first_byte, string_after_that))` or `None` if empty.
|
|
||||||
#[inline]
|
|
||||||
pub fn split_first(self) -> Option<(T::Char, Self)> {
|
|
||||||
if self.first() == T::NUL {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some((self.first(), unsafe {
|
|
||||||
Self::from_ptr(self.as_ptr().add(1))
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
pub fn to_chars_with_nul(self) -> &'a [T::Char] {
|
|
||||||
unsafe {
|
|
||||||
// SAFETY: The string must be valid at least until (and including) the NUL byte.
|
|
||||||
let len = T::strlen(self.ptr.as_ptr());
|
|
||||||
core::slice::from_raw_parts(self.ptr.as_ptr().cast(), len + 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn to_chars(self) -> &'a [T::Char] {
|
|
||||||
let s = self.to_chars_with_nul();
|
|
||||||
&s[..s.len() - 1]
|
|
||||||
}
|
|
||||||
pub const fn as_ptr(self) -> *const T::C {
|
|
||||||
self.ptr.as_ptr()
|
|
||||||
}
|
|
||||||
pub const unsafe fn from_chars_with_nul_unchecked(chars: &'a [T::Char]) -> Self {
|
|
||||||
unsafe { Self::from_ptr(chars.as_ptr().cast()) }
|
|
||||||
}
|
|
||||||
pub fn from_chars_with_nul(chars: &'a [T::Char]) -> Result<Self, FromCharsWithNulError> {
|
|
||||||
if chars.last() != Some(&T::NUL) || chars[..chars.len() - 1].contains(&T::NUL) {
|
|
||||||
return Err(FromCharsWithNulError);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(unsafe { Self::from_chars_with_nul_unchecked(chars) })
|
|
||||||
}
|
|
||||||
pub fn from_chars_until_nul(chars: &'a [T::Char]) -> Result<Self, FromCharsUntilNulError> {
|
|
||||||
if !chars.contains(&T::NUL) {
|
|
||||||
return Err(FromCharsUntilNulError);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(unsafe { Self::from_chars_with_nul_unchecked(chars) })
|
|
||||||
}
|
|
||||||
/// Scan the string to get its length.
|
|
||||||
#[doc(alias = "strlen")]
|
|
||||||
#[doc(alias = "wcslen")]
|
|
||||||
#[expect(clippy::len_without_is_empty, reason = "len() signature intentional")]
|
|
||||||
pub fn len(self) -> usize {
|
|
||||||
self.to_chars().len()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn is_empty(&self) -> bool {
|
|
||||||
self.first() == T::NUL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'a> CStr<'a> {
|
|
||||||
pub fn to_owned_cstring(self) -> CString {
|
|
||||||
CString::from(self.to_cstr())
|
|
||||||
}
|
|
||||||
pub fn borrow(string: &'a CString) -> Self {
|
|
||||||
unsafe { Self::from_ptr(string.as_ptr()) }
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn to_bytes(self) -> &'a [u8] {
|
|
||||||
self.to_chars()
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn to_bytes_with_nul(self) -> &'a [u8] {
|
|
||||||
self.to_chars_with_nul()
|
|
||||||
}
|
|
||||||
pub fn to_str(self) -> Result<&'a str, Utf8Error> {
|
|
||||||
core::str::from_utf8(self.to_bytes())
|
|
||||||
}
|
|
||||||
pub fn to_cstr(self) -> &'a core::ffi::CStr {
|
|
||||||
unsafe { core::ffi::CStr::from_ptr(self.ptr.as_ptr()) }
|
|
||||||
}
|
|
||||||
pub fn to_string_lossy(self) -> Cow<'a, str> {
|
|
||||||
String::from_utf8_lossy(self.to_bytes())
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &'a [u8]) -> Self {
|
|
||||||
unsafe { Self::from_chars_with_nul_unchecked(bytes) }
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn from_bytes_with_nul(bytes: &'a [u8]) -> Result<Self, FromCharsWithNulError> {
|
|
||||||
Self::from_chars_with_nul(bytes)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn from_bytes_until_nul(bytes: &'a [u8]) -> Result<Self, FromCharsUntilNulError> {
|
|
||||||
Self::from_chars_until_nul(bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl<T: Kind> Send for NulStr<'_, T> {}
|
|
||||||
unsafe impl<T: Kind> Sync for NulStr<'_, T> {}
|
|
||||||
|
|
||||||
impl From<&core::ffi::CStr> for CStr<'_> {
|
|
||||||
fn from(s: &core::ffi::CStr) -> Self {
|
|
||||||
// SAFETY:
|
|
||||||
// * We can assume that `s` is valid because the caller should have upheld its
|
|
||||||
// safety concerns when constructing it.
|
|
||||||
unsafe { Self::from_ptr(s.as_ptr()) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct FromCharsWithNulError;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct FromCharsUntilNulError;
|
|
||||||
|
|
||||||
pub use alloc::ffi::CString;
|
|
||||||
-294
@@ -1,294 +0,0 @@
|
|||||||
//! Equivalent of Rust's `Vec<T>`, but using relibc's own allocator.
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
io::{self, Write},
|
|
||||||
platform::{self, WriteByte, types::*},
|
|
||||||
};
|
|
||||||
use core::{
|
|
||||||
cmp, fmt,
|
|
||||||
iter::IntoIterator,
|
|
||||||
mem,
|
|
||||||
ops::{Deref, DerefMut},
|
|
||||||
ptr::{self, NonNull},
|
|
||||||
slice,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Error that occurs when an allocation fails
|
|
||||||
#[derive(Debug, Default, Hash, PartialEq, Eq, Clone, Copy)]
|
|
||||||
pub struct AllocError;
|
|
||||||
|
|
||||||
/// A normal vector allocated in Rust needs to be dropped from Rust
|
|
||||||
/// too, in order to avoid UB. This CVec is an abstraction that works
|
|
||||||
/// using only C allocations functions and can therefore be dropped
|
|
||||||
/// from C. Just like the Rust Vec, this does bounds checks to assure
|
|
||||||
/// you never reach isize::MAX. Unless you need to drop something from
|
|
||||||
/// C, prefer Rust's builtin Vec.
|
|
||||||
pub struct CVec<T> {
|
|
||||||
ptr: NonNull<T>,
|
|
||||||
len: usize,
|
|
||||||
cap: usize,
|
|
||||||
}
|
|
||||||
impl<T> CVec<T> {
|
|
||||||
#[allow(clippy::new_without_default)]
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
ptr: NonNull::dangling(),
|
|
||||||
len: 0,
|
|
||||||
cap: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn check_bounds(i: usize) -> Result<usize, AllocError> {
|
|
||||||
if i > isize::MAX as usize {
|
|
||||||
Err(AllocError)
|
|
||||||
} else {
|
|
||||||
Ok(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn check_mul(x: usize, y: usize) -> Result<usize, AllocError> {
|
|
||||||
x.checked_mul(y)
|
|
||||||
.ok_or(AllocError)
|
|
||||||
.and_then(Self::check_bounds)
|
|
||||||
}
|
|
||||||
pub fn with_capacity(cap: usize) -> Result<Self, AllocError> {
|
|
||||||
if cap == 0 {
|
|
||||||
return Ok(Self::new());
|
|
||||||
}
|
|
||||||
let size = Self::check_mul(cap, mem::size_of::<T>())?;
|
|
||||||
let ptr = NonNull::new(unsafe { platform::alloc(size).cast::<T>() }).ok_or(AllocError)?;
|
|
||||||
Ok(Self { ptr, len: 0, cap })
|
|
||||||
}
|
|
||||||
unsafe fn resize(&mut self, cap: usize) -> Result<(), AllocError> {
|
|
||||||
let size = Self::check_mul(cap, mem::size_of::<T>())?;
|
|
||||||
let ptr = if cap == 0 {
|
|
||||||
NonNull::dangling()
|
|
||||||
} else if self.cap > 0 {
|
|
||||||
NonNull::new(
|
|
||||||
unsafe { platform::realloc(self.ptr.as_ptr().cast::<c_void>(), size) }.cast::<T>(),
|
|
||||||
)
|
|
||||||
.ok_or(AllocError)?
|
|
||||||
} else {
|
|
||||||
NonNull::new((unsafe { platform::alloc(size) }).cast::<T>()).ok_or(AllocError)?
|
|
||||||
};
|
|
||||||
self.ptr = ptr;
|
|
||||||
self.cap = cap;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
unsafe fn drop_range(&mut self, start: usize, end: usize) {
|
|
||||||
let mut start = unsafe { self.ptr.as_ptr().add(start) };
|
|
||||||
let end = unsafe { self.ptr.as_ptr().add(end) };
|
|
||||||
while start < end {
|
|
||||||
unsafe { ptr::drop_in_place(start) };
|
|
||||||
start = unsafe { start.add(1) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push stuff
|
|
||||||
|
|
||||||
pub fn reserve(&mut self, required: usize) -> Result<(), AllocError> {
|
|
||||||
let required_len = self
|
|
||||||
.len
|
|
||||||
.checked_add(required)
|
|
||||||
.ok_or(AllocError)
|
|
||||||
.and_then(Self::check_bounds)?;
|
|
||||||
if required_len > self.cap {
|
|
||||||
let new_cap = cmp::min(required_len.next_power_of_two(), isize::MAX as usize);
|
|
||||||
unsafe {
|
|
||||||
self.resize(new_cap)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn push(&mut self, elem: T) -> Result<(), AllocError> {
|
|
||||||
self.reserve(1)?;
|
|
||||||
unsafe {
|
|
||||||
ptr::write(self.ptr.as_ptr().add(self.len), elem);
|
|
||||||
}
|
|
||||||
self.len += 1; // no need to bounds check, as new len <= cap
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn extend_from_slice(&mut self, elems: &[T]) -> Result<(), AllocError>
|
|
||||||
where
|
|
||||||
T: Copy,
|
|
||||||
{
|
|
||||||
self.reserve(elems.len())?;
|
|
||||||
unsafe {
|
|
||||||
ptr::copy_nonoverlapping(elems.as_ptr(), self.ptr.as_ptr().add(self.len), elems.len());
|
|
||||||
}
|
|
||||||
self.len += elems.len(); // no need to bounds check, as new len <= cap
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn append(&mut self, other: &mut Self) -> Result<(), AllocError> {
|
|
||||||
let len = other.len;
|
|
||||||
other.len = 0; // move
|
|
||||||
self.reserve(len)?;
|
|
||||||
unsafe {
|
|
||||||
ptr::copy_nonoverlapping(other.as_ptr(), self.ptr.as_ptr().add(self.len), len);
|
|
||||||
}
|
|
||||||
self.len += other.len(); // no need to bounds check, as new len <= cap
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pop stuff
|
|
||||||
|
|
||||||
pub fn truncate(&mut self, len: usize) {
|
|
||||||
if len < self.len {
|
|
||||||
unsafe {
|
|
||||||
let old_len = self.len;
|
|
||||||
self.drop_range(len, old_len);
|
|
||||||
}
|
|
||||||
self.len = len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn shrink_to_fit(&mut self) -> Result<(), AllocError> {
|
|
||||||
if self.len < self.cap {
|
|
||||||
unsafe {
|
|
||||||
let new_cap = self.len;
|
|
||||||
self.resize(new_cap)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub fn pop(&mut self) -> Option<T> {
|
|
||||||
if self.is_empty() {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let elem = unsafe { ptr::read(self.as_ptr().add(self.len - 1)) };
|
|
||||||
self.len -= 1;
|
|
||||||
Some(elem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Misc stuff
|
|
||||||
|
|
||||||
pub fn capacity(&self) -> usize {
|
|
||||||
self.cap
|
|
||||||
}
|
|
||||||
pub fn as_ptr(&self) -> *const T {
|
|
||||||
self.ptr.as_ptr()
|
|
||||||
}
|
|
||||||
pub fn as_mut_ptr(&mut self) -> *mut T {
|
|
||||||
self.ptr.as_ptr()
|
|
||||||
}
|
|
||||||
/// Leaks the inner data. This is safe to drop from C!
|
|
||||||
pub fn leak(mut self) -> *mut T {
|
|
||||||
let ptr = self.as_mut_ptr();
|
|
||||||
mem::forget(self);
|
|
||||||
ptr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T> Deref for CVec<T> {
|
|
||||||
type Target = [T];
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T> DerefMut for CVec<T> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T> Drop for CVec<T> {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
unsafe {
|
|
||||||
let len = self.len;
|
|
||||||
self.drop_range(0, len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'a, T> IntoIterator for &'a CVec<T> {
|
|
||||||
type Item = <&'a [T] as IntoIterator>::Item;
|
|
||||||
type IntoIter = <&'a [T] as IntoIterator>::IntoIter;
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
<&[T]>::into_iter(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'a, T> IntoIterator for &'a mut CVec<T> {
|
|
||||||
type Item = <&'a mut [T] as IntoIterator>::Item;
|
|
||||||
type IntoIter = <&'a mut [T] as IntoIterator>::IntoIter;
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
|
||||||
<&mut [T]>::into_iter(&mut *self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Write for CVec<u8> {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
||||||
self.extend_from_slice(buf).map_err(|err| {
|
|
||||||
io::Error::new(
|
|
||||||
io::ErrorKind::Other,
|
|
||||||
"AllocStringWriter::write failed to allocate",
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
Ok(buf.len())
|
|
||||||
}
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl fmt::Write for CVec<u8> {
|
|
||||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
|
||||||
self.write(s.as_bytes()).map_err(|_| fmt::Error)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl WriteByte for CVec<u8> {
|
|
||||||
fn write_u8(&mut self, byte: u8) -> fmt::Result {
|
|
||||||
self.write(&[byte]).map_err(|_| fmt::Error)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::CVec;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn push_pop() {
|
|
||||||
let mut vec = CVec::new();
|
|
||||||
vec.push(1).unwrap();
|
|
||||||
vec.push(2).unwrap();
|
|
||||||
vec.push(3).unwrap();
|
|
||||||
assert_eq!(&vec[..], &[1, 2, 3]);
|
|
||||||
assert_eq!(vec.pop().unwrap(), 3);
|
|
||||||
assert_eq!(&vec[..], &[1, 2]);
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn extend_from_slice() {
|
|
||||||
use crate::io::Write;
|
|
||||||
|
|
||||||
let mut vec = CVec::new();
|
|
||||||
vec.extend_from_slice(&[1, 2, 3]).unwrap();
|
|
||||||
vec.extend_from_slice(&[4, 5, 6]).unwrap();
|
|
||||||
assert_eq!(&vec[..], &[1, 2, 3, 4, 5, 6]);
|
|
||||||
assert_eq!(vec.write(&[7, 8, 9]).unwrap(), 3);
|
|
||||||
assert_eq!(&vec[..], &[1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
|
||||||
}
|
|
||||||
#[test]
|
|
||||||
fn dropped() {
|
|
||||||
use alloc::rc::Rc;
|
|
||||||
|
|
||||||
let counter = Rc::new(());
|
|
||||||
let mut vec = CVec::with_capacity(3).unwrap();
|
|
||||||
vec.push(Rc::clone(&counter)).unwrap();
|
|
||||||
vec.push(Rc::clone(&counter)).unwrap();
|
|
||||||
vec.push(Rc::clone(&counter)).unwrap();
|
|
||||||
assert_eq!(Rc::strong_count(&counter), 4);
|
|
||||||
|
|
||||||
let popped = vec.pop().unwrap();
|
|
||||||
assert_eq!(Rc::strong_count(&counter), 4);
|
|
||||||
drop(popped);
|
|
||||||
assert_eq!(Rc::strong_count(&counter), 3);
|
|
||||||
|
|
||||||
vec.push(Rc::clone(&counter)).unwrap();
|
|
||||||
vec.push(Rc::clone(&counter)).unwrap();
|
|
||||||
vec.push(Rc::clone(&counter)).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(vec.len(), 5);
|
|
||||||
assert_eq!(Rc::strong_count(&counter), 6);
|
|
||||||
vec.truncate(1);
|
|
||||||
assert_eq!(Rc::strong_count(&counter), 2);
|
|
||||||
|
|
||||||
drop(vec);
|
|
||||||
assert_eq!(Rc::strong_count(&counter), 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
use crate::{Disk, FileSystem, Node, Transaction, TreePtr, BLOCK_SIZE};
|
||||||
|
|
||||||
|
fn tx_progress<D: Disk, F: FnMut(u64)>(tx: &mut Transaction<D>, progress: &mut F) {
|
||||||
|
let size = tx.header.size();
|
||||||
|
let free = tx.allocator.free() * BLOCK_SIZE;
|
||||||
|
progress(size - free);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: handle hard links
|
||||||
|
fn clone_at<D: Disk, E: Disk, F: FnMut(u64)>(
|
||||||
|
tx_old: &mut Transaction<D>,
|
||||||
|
parent_ptr_old: TreePtr<Node>,
|
||||||
|
tx: &mut Transaction<E>,
|
||||||
|
parent_ptr: TreePtr<Node>,
|
||||||
|
buf: &mut [u8],
|
||||||
|
progress: &mut F,
|
||||||
|
) -> syscall::Result<()> {
|
||||||
|
let mut entries = Vec::new();
|
||||||
|
tx_old.child_nodes(parent_ptr_old, &mut entries)?;
|
||||||
|
for entry in entries {
|
||||||
|
//TODO: return error instead?
|
||||||
|
let Some(name) = entry.name() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let node_ptr_old = entry.node_ptr();
|
||||||
|
let node_old = tx_old.read_tree(node_ptr_old)?;
|
||||||
|
|
||||||
|
//TODO: this slows down the clone, but Redox has issues without this (Linux is fine)
|
||||||
|
if tx.write_cache.len() > 64 {
|
||||||
|
tx.sync(false)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let node_ptr = {
|
||||||
|
let mode = node_old.data().mode();
|
||||||
|
let (ctime, ctime_nsec) = node_old.data().ctime();
|
||||||
|
let (mtime, mtime_nsec) = node_old.data().mtime();
|
||||||
|
let mut node = tx.create_node(parent_ptr, &name, mode, ctime, ctime_nsec)?;
|
||||||
|
node.data_mut().set_uid(node_old.data().uid());
|
||||||
|
node.data_mut().set_gid(node_old.data().gid());
|
||||||
|
node.data_mut().set_mtime(mtime, mtime_nsec);
|
||||||
|
|
||||||
|
if !node_old.data().is_dir() {
|
||||||
|
let mut offset = 0;
|
||||||
|
loop {
|
||||||
|
let count = tx_old.read_node_inner(&node_old, offset, buf)?;
|
||||||
|
if count == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
tx.write_node_inner(&mut node, &mut offset, &buf[..count])?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let node_ptr = node.ptr();
|
||||||
|
tx.sync_tree(node)?;
|
||||||
|
node_ptr
|
||||||
|
};
|
||||||
|
|
||||||
|
tx_progress(tx, progress);
|
||||||
|
|
||||||
|
if node_old.data().is_dir() {
|
||||||
|
clone_at(tx_old, node_ptr_old, tx, node_ptr, buf, progress)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clone<D: Disk, E: Disk, F: FnMut(u64)>(
|
||||||
|
fs_old: &mut FileSystem<D>,
|
||||||
|
fs: &mut FileSystem<E>,
|
||||||
|
mut progress: F,
|
||||||
|
) -> syscall::Result<()> {
|
||||||
|
fs_old.tx(|tx_old| {
|
||||||
|
let mut tx = Transaction::new(fs);
|
||||||
|
|
||||||
|
// Clone at root node
|
||||||
|
let mut buf = vec![0; 4 * 1024 * 1024];
|
||||||
|
clone_at(
|
||||||
|
tx_old,
|
||||||
|
TreePtr::root(),
|
||||||
|
&mut tx,
|
||||||
|
TreePtr::root(),
|
||||||
|
&mut buf,
|
||||||
|
&mut progress,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Commit and squash alloc log
|
||||||
|
tx.commit(true)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "crt0"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Jeremy Soller <jackpot51@gmail.com>"]
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "crt0"
|
|
||||||
crate-type = ["staticlib"]
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
//! crt0
|
|
||||||
|
|
||||||
#![no_std]
|
|
||||||
#![feature(linkage)]
|
|
||||||
|
|
||||||
use core::{
|
|
||||||
arch::global_asm,
|
|
||||||
ffi::{c_char, c_int},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
global_asm!(
|
|
||||||
"
|
|
||||||
.globl _start
|
|
||||||
_start:
|
|
||||||
mov x0, sp
|
|
||||||
and sp, x0, #0xfffffffffffffff0 //align sp
|
|
||||||
bl relibc_crt0
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86")]
|
|
||||||
global_asm!(
|
|
||||||
"
|
|
||||||
.globl _start
|
|
||||||
.type _start, @function
|
|
||||||
_start:
|
|
||||||
sub esp, 8
|
|
||||||
|
|
||||||
mov DWORD PTR [esp], 0x00001F80
|
|
||||||
# ldmxcsr [esp]
|
|
||||||
mov WORD PTR [esp], 0x037F
|
|
||||||
fldcw [esp]
|
|
||||||
|
|
||||||
add esp, 8
|
|
||||||
|
|
||||||
push esp
|
|
||||||
call relibc_crt0
|
|
||||||
.size _start, . - _start
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
|
||||||
global_asm!(
|
|
||||||
"
|
|
||||||
.globl _start
|
|
||||||
.type _start, @function
|
|
||||||
_start:
|
|
||||||
mov rdi, rsp
|
|
||||||
and rsp, 0xFFFFFFFFFFFFFFF0
|
|
||||||
|
|
||||||
sub rsp, 8
|
|
||||||
|
|
||||||
mov DWORD PTR [rsp], 0x00001F80
|
|
||||||
ldmxcsr [rsp]
|
|
||||||
mov WORD PTR [rsp], 0x037F
|
|
||||||
fldcw [rsp]
|
|
||||||
|
|
||||||
add rsp, 8
|
|
||||||
|
|
||||||
call relibc_crt0
|
|
||||||
.size _start, . - _start
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(target_arch = "riscv64")]
|
|
||||||
global_asm!(
|
|
||||||
"
|
|
||||||
.globl _start
|
|
||||||
_start:
|
|
||||||
mv a0, sp
|
|
||||||
la t0, relibc_crt0
|
|
||||||
jalr ra, t0
|
|
||||||
"
|
|
||||||
);
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub unsafe extern "C" fn relibc_crt0(sp: usize) -> ! {
|
|
||||||
// This wrapper ensures a dynamic libc.so can access a hidden main function
|
|
||||||
//TODO: common definition of types
|
|
||||||
unsafe extern "C" {
|
|
||||||
fn main(argc: isize, argv: *mut *mut c_char, envp: *mut *mut c_char) -> c_int;
|
|
||||||
fn relibc_start_v1(
|
|
||||||
sp: usize,
|
|
||||||
main: unsafe extern "C" fn(
|
|
||||||
argc: isize,
|
|
||||||
argv: *mut *mut c_char,
|
|
||||||
envp: *mut *mut c_char,
|
|
||||||
) -> c_int,
|
|
||||||
) -> !;
|
|
||||||
}
|
|
||||||
unsafe { relibc_start_v1(sp, main) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[linkage = "weak"]
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub extern "C" fn relibc_panic(_pi: &::core::panic::PanicInfo) -> ! {
|
|
||||||
loop {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[panic_handler]
|
|
||||||
#[linkage = "weak"]
|
|
||||||
pub unsafe fn rust_begin_unwind(pi: &::core::panic::PanicInfo) -> ! {
|
|
||||||
relibc_panic(pi)
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "crti"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["jD91mZM2 <me@krake.one>"]
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
|
|
||||||
// we don't support _init/_fini functions, only init/fini arrays
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "crtn"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["jD91mZM2 <me@krake.one>"]
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[lints]
|
|
||||||
workspace = true
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
|
|
||||||
// we don't support _init/_fini functions, only init/fini arrays
|
|
||||||
-103
@@ -1,103 +0,0 @@
|
|||||||
use crate::platform::types::{c_int, c_void};
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
use core::cell::RefCell;
|
|
||||||
use spin::Mutex;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
struct CxaAtExitFunc {
|
|
||||||
func: extern "C" fn(*mut c_void),
|
|
||||||
arg: usize,
|
|
||||||
dso: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
struct CxaThreadAtExitFunc {
|
|
||||||
func: extern "C" fn(*mut c_void),
|
|
||||||
obj: *mut c_void,
|
|
||||||
dso: *mut c_void,
|
|
||||||
}
|
|
||||||
|
|
||||||
static CXA_ATEXIT_FUNCS: Mutex<Vec<Option<CxaAtExitFunc>>> = Mutex::new(Vec::new());
|
|
||||||
#[thread_local]
|
|
||||||
static DTORS: RefCell<Vec<CxaThreadAtExitFunc>> = RefCell::new(Vec::new());
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub unsafe extern "C" fn __cxa_atexit(
|
|
||||||
func: Option<extern "C" fn(*mut c_void)>,
|
|
||||||
arg: *mut c_void,
|
|
||||||
dso: *mut c_void,
|
|
||||||
) -> c_int {
|
|
||||||
let Some(func) = func else {
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
let entry = CxaAtExitFunc {
|
|
||||||
func,
|
|
||||||
arg: arg as usize,
|
|
||||||
dso: dso as usize,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut funcs = CXA_ATEXIT_FUNCS.lock();
|
|
||||||
|
|
||||||
for slot in funcs.iter_mut() {
|
|
||||||
if slot.is_none() {
|
|
||||||
*slot = Some(entry);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// No empty slots
|
|
||||||
funcs.push(Some(entry));
|
|
||||||
0
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub unsafe extern "C" fn __cxa_finalize(dso: *mut c_void) {
|
|
||||||
let mut funcs = CXA_ATEXIT_FUNCS.lock();
|
|
||||||
|
|
||||||
let dso_usize = dso as usize;
|
|
||||||
|
|
||||||
for slot in funcs.iter_mut().rev() {
|
|
||||||
if let Some(entry) = slot.as_ref()
|
|
||||||
&& (dso.is_null() || entry.dso == dso_usize)
|
|
||||||
&& let Some(entry_to_run) = slot.take()
|
|
||||||
{
|
|
||||||
(entry_to_run.func)(entry_to_run.arg as *mut c_void);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// clean up remaining list
|
|
||||||
if dso.is_null() {
|
|
||||||
funcs.clear();
|
|
||||||
} else {
|
|
||||||
funcs.retain(|opt| opt.is_some());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub unsafe extern "C" fn __cxa_thread_atexit_impl(
|
|
||||||
func: extern "C" fn(*mut c_void),
|
|
||||||
obj: *mut c_void,
|
|
||||||
dso: *mut c_void,
|
|
||||||
) {
|
|
||||||
let entry = CxaThreadAtExitFunc { func, obj, dso };
|
|
||||||
DTORS.borrow_mut().push(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
// called internally
|
|
||||||
pub unsafe fn __cxa_thread_finalize() {
|
|
||||||
let mut dtors = DTORS.borrow_mut();
|
|
||||||
while let Some(entry) = dtors.pop() {
|
|
||||||
(entry.func)(entry.obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub unsafe extern "C" fn _ITM_deregisterTMCloneTable(_ptr: *mut c_void) {
|
|
||||||
// No-op
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub unsafe extern "C" fn _ITM_registerTMCloneTable(_ptr: *mut c_void, _len: usize) {
|
|
||||||
// No-op
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
use alloc::{string::String, vec::Vec};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
c_str::CStr,
|
|
||||||
fs::File,
|
|
||||||
header::fcntl,
|
|
||||||
io::{self, BufRead, BufReader},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub enum Separator {
|
|
||||||
Character(char),
|
|
||||||
Whitespace,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Db<R: BufRead> {
|
|
||||||
reader: R,
|
|
||||||
separator: Separator,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<R: BufRead> Db<R> {
|
|
||||||
pub fn new(reader: R, separator: Separator) -> Self {
|
|
||||||
Db { reader, separator }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read(&mut self) -> io::Result<Option<Vec<String>>> {
|
|
||||||
let mut line = String::new();
|
|
||||||
if self.reader.read_line(&mut line)? == 0 {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
let vec = if let Some(not_comment) = line.trim().split('#').next() {
|
|
||||||
match self.separator {
|
|
||||||
Separator::Character(c) => not_comment.split(c).map(String::from).collect(),
|
|
||||||
Separator::Whitespace => not_comment.split_whitespace().map(String::from).collect(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Vec::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Some(vec))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type FileDb = Db<BufReader<File>>;
|
|
||||||
|
|
||||||
impl FileDb {
|
|
||||||
pub fn open(path: CStr, separator: Separator) -> io::Result<Self> {
|
|
||||||
let file = File::open(path, fcntl::O_RDONLY | fcntl::O_CLOEXEC)?;
|
|
||||||
Ok(Db::new(BufReader::new(file), separator))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+300
@@ -0,0 +1,300 @@
|
|||||||
|
use core::{mem, ops, slice, str};
|
||||||
|
|
||||||
|
use crate::{BlockLevel, BlockTrait, Node, TreePtr, BLOCK_SIZE, DIR_ENTRY_MAX_LENGTH};
|
||||||
|
|
||||||
|
#[repr(C, packed)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct DirEntry {
|
||||||
|
node_ptr: TreePtr<Node>,
|
||||||
|
name: [u8; DIR_ENTRY_MAX_LENGTH],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DirEntry {
|
||||||
|
pub fn new(node_ptr: TreePtr<Node>, name: &str) -> DirEntry {
|
||||||
|
let mut entry = DirEntry {
|
||||||
|
node_ptr,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
entry.name[..name.len()].copy_from_slice(name.as_bytes());
|
||||||
|
|
||||||
|
entry
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn node_ptr(&self) -> TreePtr<Node> {
|
||||||
|
self.node_ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name_len(&self) -> usize {
|
||||||
|
let mut len = 0;
|
||||||
|
while len < self.name.len() {
|
||||||
|
if self.name[len] == 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
len
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> Option<&str> {
|
||||||
|
let len = self.name_len();
|
||||||
|
//TODO: report utf8 error?
|
||||||
|
str::from_utf8(&self.name[..len]).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4 bytes TreePtr
|
||||||
|
// 1 byte name_len
|
||||||
|
const SERIALIZED_PREFIX_SIZE: usize = mem::size_of::<TreePtr<Node>>() + 1;
|
||||||
|
|
||||||
|
pub fn serialized_size(&self) -> usize {
|
||||||
|
DirEntry::SERIALIZED_PREFIX_SIZE + self.name_len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_into(&self, buf: &mut [u8]) -> Option<usize> {
|
||||||
|
let required = self.serialized_size();
|
||||||
|
if buf.len() < required {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[0..4].copy_from_slice(&self.node_ptr().to_bytes());
|
||||||
|
buf[4] = self.name_len() as u8;
|
||||||
|
buf[5..5 + self.name_len()].copy_from_slice(&self.name[..self.name_len()]);
|
||||||
|
|
||||||
|
Some(required)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deserialize_from(buf: &[u8]) -> Result<(Self, usize), &'static str> {
|
||||||
|
if buf.len() <= DirEntry::SERIALIZED_PREFIX_SIZE {
|
||||||
|
return Err("Buffer too small");
|
||||||
|
}
|
||||||
|
|
||||||
|
let node_ptr: TreePtr<Node> =
|
||||||
|
TreePtr::from_bytes(buf[0..4].try_into().expect("Slice must be 4 bytes long"));
|
||||||
|
let name_len = buf[4] as usize;
|
||||||
|
|
||||||
|
if name_len < 1 || name_len > DIR_ENTRY_MAX_LENGTH {
|
||||||
|
return Err("Invalid name length");
|
||||||
|
}
|
||||||
|
|
||||||
|
if buf.len() < DirEntry::SERIALIZED_PREFIX_SIZE + name_len {
|
||||||
|
return Err("Buffer too small");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut name = [0u8; DIR_ENTRY_MAX_LENGTH];
|
||||||
|
name[..name_len].copy_from_slice(
|
||||||
|
&buf[DirEntry::SERIALIZED_PREFIX_SIZE..DirEntry::SERIALIZED_PREFIX_SIZE + name_len],
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
DirEntry { node_ptr, name },
|
||||||
|
DirEntry::SERIALIZED_PREFIX_SIZE + name_len,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DirEntry {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
node_ptr: TreePtr::default(),
|
||||||
|
name: [0; DIR_ENTRY_MAX_LENGTH],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DirList {
|
||||||
|
count: u16,
|
||||||
|
entry_bytes_len: u16,
|
||||||
|
entry_bytes: [u8; BLOCK_SIZE as usize - 4],
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl BlockTrait for DirList {
|
||||||
|
fn empty(level: BlockLevel) -> Option<Self> {
|
||||||
|
if level.0 == 0 {
|
||||||
|
Some(Self {
|
||||||
|
count: 0,
|
||||||
|
entry_bytes_len: 0,
|
||||||
|
entry_bytes: [0; BLOCK_SIZE as usize - 4],
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DirList {
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.count == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn entries(&self) -> DirEntryIterator<'_> {
|
||||||
|
DirEntryIterator {
|
||||||
|
dir_list: self,
|
||||||
|
emit_count: 0,
|
||||||
|
position: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn entry_position_for_name(&self, name: &str) -> Option<usize> {
|
||||||
|
let name_len = name.len();
|
||||||
|
let mut position = 0;
|
||||||
|
let mut entry_id = 0;
|
||||||
|
|
||||||
|
while entry_id < self.count {
|
||||||
|
let entry_name_len = self.entry_bytes[position + 4] as usize;
|
||||||
|
if entry_name_len == name_len {
|
||||||
|
let start = DirEntry::SERIALIZED_PREFIX_SIZE + position;
|
||||||
|
let entry_name = &self.entry_bytes[start..start + entry_name_len];
|
||||||
|
if entry_name == name.as_bytes() {
|
||||||
|
return Some(position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
position += DirEntry::SERIALIZED_PREFIX_SIZE + entry_name_len;
|
||||||
|
entry_id += 1;
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_entry(&self, name: &str) -> Option<DirEntry> {
|
||||||
|
if let Some(position) = self.entry_position_for_name(name) {
|
||||||
|
let (entry, _) = DirEntry::deserialize_from(&self.entry_bytes[position..]).unwrap();
|
||||||
|
return Some(entry);
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_entry(&mut self, name: &str) -> bool {
|
||||||
|
if let Some(position) = self.entry_position_for_name(name) {
|
||||||
|
let entry_size =
|
||||||
|
DirEntry::SERIALIZED_PREFIX_SIZE + self.entry_bytes[position + 4] as usize;
|
||||||
|
let remaining_size = self.entry_bytes_len as usize - position - entry_size;
|
||||||
|
if remaining_size > 0 {
|
||||||
|
self.entry_bytes.copy_within(
|
||||||
|
position + entry_size..self.entry_bytes_len as usize,
|
||||||
|
position,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
self.entry_bytes_len -= entry_size as u16;
|
||||||
|
self.count -= 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn for_each_entry<F>(&self, mut f: F)
|
||||||
|
where
|
||||||
|
F: FnMut(&[u8; 4], &[u8]),
|
||||||
|
{
|
||||||
|
let mut position = 0;
|
||||||
|
let mut entry_id = 0;
|
||||||
|
|
||||||
|
while entry_id < self.count {
|
||||||
|
let node_ptr_bytes = &self.entry_bytes[position..position + 4];
|
||||||
|
//let node_ptr = TreePtr::<Node>::from_bytes(node_ptr_bytes.try_into().unwrap());
|
||||||
|
let entry_name_len = self.entry_bytes[position + 4] as usize;
|
||||||
|
let start = DirEntry::SERIALIZED_PREFIX_SIZE + position;
|
||||||
|
let entry_name = &self.entry_bytes[start..start + entry_name_len];
|
||||||
|
|
||||||
|
f(node_ptr_bytes.try_into().unwrap(), entry_name);
|
||||||
|
|
||||||
|
position += DirEntry::SERIALIZED_PREFIX_SIZE + entry_name_len;
|
||||||
|
entry_id += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append(&mut self, entry: &DirEntry) -> bool {
|
||||||
|
let entry_bytes_len = self.entry_bytes_len as usize;
|
||||||
|
if let Some(size) = entry.serialize_into(&mut self.entry_bytes[entry_bytes_len..]) {
|
||||||
|
self.count += 1;
|
||||||
|
self.entry_bytes_len += size as u16;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn entry_count(&self) -> usize {
|
||||||
|
self.count as usize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::Deref for DirList {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const DirList as *const u8,
|
||||||
|
mem::size_of::<DirList>(),
|
||||||
|
) as &[u8]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ops::DerefMut for DirList {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(self as *mut DirList as *mut u8, mem::size_of::<DirList>())
|
||||||
|
as &mut [u8]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DirEntryIterator<'a> {
|
||||||
|
dir_list: &'a DirList,
|
||||||
|
emit_count: usize,
|
||||||
|
position: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for DirEntryIterator<'_> {
|
||||||
|
type Item = DirEntry;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.emit_count < self.dir_list.entry_count() {
|
||||||
|
let position = self.position;
|
||||||
|
let (entry, bytes_read) =
|
||||||
|
DirEntry::deserialize_from(&self.dir_list.entry_bytes[position..]).unwrap();
|
||||||
|
|
||||||
|
self.emit_count += 1;
|
||||||
|
self.position += bytes_read;
|
||||||
|
|
||||||
|
Some(entry)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use alloc::format;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dir_list_size_test() {
|
||||||
|
use core::ops::Deref;
|
||||||
|
assert_eq!(
|
||||||
|
DirList::empty(BlockLevel(0)).unwrap().deref().len(),
|
||||||
|
BLOCK_SIZE as usize
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_append() {
|
||||||
|
let mut dir_list = DirList::empty(BlockLevel(0)).unwrap();
|
||||||
|
let dirent = DirEntry::new(TreePtr::new(123), "test000");
|
||||||
|
|
||||||
|
assert!(dir_list.append(&dirent));
|
||||||
|
assert_eq!(dir_list.entry_count(), 1);
|
||||||
|
assert_eq!(dir_list.entry_bytes_len as usize, dirent.serialized_size());
|
||||||
|
|
||||||
|
let max_entries = dir_list.entry_bytes.len() / dirent.serialized_size();
|
||||||
|
for i in 1..max_entries {
|
||||||
|
let dirent = DirEntry::new(TreePtr::new(123), format!("test{i:03}").as_str());
|
||||||
|
assert!(dir_list.append(&dirent), "Failed on iteration {i}");
|
||||||
|
}
|
||||||
|
let dirent = DirEntry::new(TreePtr::new(123), format!("test{max_entries}").as_str());
|
||||||
|
assert!(!dir_list.append(&dirent));
|
||||||
|
|
||||||
|
for (i, entry) in dir_list.entries().enumerate() {
|
||||||
|
assert_eq!(entry.name().unwrap(), format!("test{i:03}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
use std::collections::{HashMap, VecDeque};
|
||||||
|
use std::{cmp, ptr};
|
||||||
|
use syscall::error::Result;
|
||||||
|
|
||||||
|
use crate::disk::Disk;
|
||||||
|
use crate::BLOCK_SIZE;
|
||||||
|
|
||||||
|
fn copy_memory(src: &[u8], dest: &mut [u8]) -> usize {
|
||||||
|
let len = cmp::min(src.len(), dest.len());
|
||||||
|
unsafe { ptr::copy(src.as_ptr(), dest.as_mut_ptr(), len) };
|
||||||
|
len
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DiskCache<T> {
|
||||||
|
inner: T,
|
||||||
|
cache: HashMap<u64, [u8; BLOCK_SIZE as usize]>,
|
||||||
|
order: VecDeque<u64>,
|
||||||
|
size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Disk> DiskCache<T> {
|
||||||
|
pub fn new(inner: T) -> Self {
|
||||||
|
// 16 MB cache
|
||||||
|
let size = 16 * 1024 * 1024 / BLOCK_SIZE as usize;
|
||||||
|
DiskCache {
|
||||||
|
inner,
|
||||||
|
cache: HashMap::with_capacity(size),
|
||||||
|
order: VecDeque::with_capacity(size),
|
||||||
|
size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn insert(&mut self, i: u64, data: [u8; BLOCK_SIZE as usize]) {
|
||||||
|
while self.order.len() >= self.size {
|
||||||
|
let removed = self.order.pop_front().unwrap();
|
||||||
|
self.cache.remove(&removed);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cache.insert(i, data);
|
||||||
|
self.order.push_back(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Disk> Disk for DiskCache<T> {
|
||||||
|
unsafe fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
|
||||||
|
// println!("Cache read at {}", block);
|
||||||
|
|
||||||
|
let mut read = 0;
|
||||||
|
let mut failed = false;
|
||||||
|
for i in 0..buffer.len().div_ceil(BLOCK_SIZE as usize) {
|
||||||
|
let block_i = block + i as u64;
|
||||||
|
|
||||||
|
let buffer_i = i * BLOCK_SIZE as usize;
|
||||||
|
let buffer_j = cmp::min(buffer_i + BLOCK_SIZE as usize, buffer.len());
|
||||||
|
let buffer_slice = &mut buffer[buffer_i..buffer_j];
|
||||||
|
|
||||||
|
if let Some(cache_buf) = self.cache.get_mut(&block_i) {
|
||||||
|
read += copy_memory(cache_buf, buffer_slice);
|
||||||
|
} else {
|
||||||
|
failed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if failed {
|
||||||
|
self.inner.read_at(block, buffer)?;
|
||||||
|
|
||||||
|
read = 0;
|
||||||
|
for i in 0..buffer.len().div_ceil(BLOCK_SIZE as usize) {
|
||||||
|
let block_i = block + i as u64;
|
||||||
|
|
||||||
|
let buffer_i = i * BLOCK_SIZE as usize;
|
||||||
|
let buffer_j = cmp::min(buffer_i + BLOCK_SIZE as usize, buffer.len());
|
||||||
|
let buffer_slice = &buffer[buffer_i..buffer_j];
|
||||||
|
|
||||||
|
let mut cache_buf = [0; BLOCK_SIZE as usize];
|
||||||
|
read += copy_memory(buffer_slice, &mut cache_buf);
|
||||||
|
self.insert(block_i, cache_buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(read)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result<usize> {
|
||||||
|
//TODO: Write only blocks that have changed
|
||||||
|
// println!("Cache write at {}", block);
|
||||||
|
|
||||||
|
self.inner.write_at(block, buffer)?;
|
||||||
|
|
||||||
|
let mut written = 0;
|
||||||
|
for i in 0..buffer.len().div_ceil(BLOCK_SIZE as usize) {
|
||||||
|
let block_i = block + i as u64;
|
||||||
|
|
||||||
|
let buffer_i = i * BLOCK_SIZE as usize;
|
||||||
|
let buffer_j = cmp::min(buffer_i + BLOCK_SIZE as usize, buffer.len());
|
||||||
|
let buffer_slice = &buffer[buffer_i..buffer_j];
|
||||||
|
|
||||||
|
let mut cache_buf = [0; BLOCK_SIZE as usize];
|
||||||
|
written += copy_memory(buffer_slice, &mut cache_buf);
|
||||||
|
self.insert(block_i, cache_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(written)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&mut self) -> Result<u64> {
|
||||||
|
self.inner.size()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
use std::fs::{File, OpenOptions};
|
||||||
|
use std::io::{Seek, SeekFrom};
|
||||||
|
use std::os::unix::fs::FileExt;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use syscall::error::{Error, Result, EIO};
|
||||||
|
|
||||||
|
use crate::disk::Disk;
|
||||||
|
use crate::BLOCK_SIZE;
|
||||||
|
|
||||||
|
pub struct DiskFile {
|
||||||
|
pub file: File,
|
||||||
|
}
|
||||||
|
|
||||||
|
trait ResultExt {
|
||||||
|
type T;
|
||||||
|
fn or_eio(self) -> Result<Self::T>;
|
||||||
|
}
|
||||||
|
impl<T> ResultExt for Result<T> {
|
||||||
|
type T = T;
|
||||||
|
fn or_eio(self) -> Result<Self::T> {
|
||||||
|
match self {
|
||||||
|
Ok(t) => Ok(t),
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("RedoxFS: IO ERROR: {err}");
|
||||||
|
Err(Error::new(EIO))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T> ResultExt for std::io::Result<T> {
|
||||||
|
type T = T;
|
||||||
|
fn or_eio(self) -> Result<Self::T> {
|
||||||
|
match self {
|
||||||
|
Ok(t) => Ok(t),
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("RedoxFS: IO ERROR: {err}");
|
||||||
|
Err(Error::new(EIO))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiskFile {
|
||||||
|
pub fn open(path: impl AsRef<Path>) -> Result<DiskFile> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
let file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.open(path)
|
||||||
|
.or_else(|_| OpenOptions::new().read(true).open(path))
|
||||||
|
.or_eio()?;
|
||||||
|
Ok(DiskFile { file })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create(path: impl AsRef<Path>, size: u64) -> Result<DiskFile> {
|
||||||
|
let file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.open(path)
|
||||||
|
.or_eio()?;
|
||||||
|
file.set_len(size).or_eio()?;
|
||||||
|
Ok(DiskFile { file })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Disk for DiskFile {
|
||||||
|
unsafe fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
|
||||||
|
self.file.read_at(buffer, block * BLOCK_SIZE).or_eio()
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result<usize> {
|
||||||
|
self.file.write_at(buffer, block * BLOCK_SIZE).or_eio()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&mut self) -> Result<u64> {
|
||||||
|
self.file.seek(SeekFrom::End(0)).or_eio()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<File> for DiskFile {
|
||||||
|
fn from(file: File) -> Self {
|
||||||
|
Self { file }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
use std::io::{Read, Seek, SeekFrom, Write};
|
||||||
|
use syscall::error::{Error, Result, EIO};
|
||||||
|
|
||||||
|
use crate::disk::Disk;
|
||||||
|
use crate::BLOCK_SIZE;
|
||||||
|
|
||||||
|
macro_rules! try_disk {
|
||||||
|
($expr:expr) => {
|
||||||
|
match $expr {
|
||||||
|
Ok(val) => val,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Disk I/O Error: {}", err);
|
||||||
|
return Err(Error::new(EIO));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DiskIo<T>(pub T);
|
||||||
|
|
||||||
|
impl<T: Read + Write + Seek> Disk for DiskIo<T> {
|
||||||
|
unsafe fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
|
||||||
|
try_disk!(self.0.seek(SeekFrom::Start(block * BLOCK_SIZE)));
|
||||||
|
let count = try_disk!(self.0.read(buffer));
|
||||||
|
Ok(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result<usize> {
|
||||||
|
try_disk!(self.0.seek(SeekFrom::Start(block * BLOCK_SIZE)));
|
||||||
|
let count = try_disk!(self.0.write(buffer));
|
||||||
|
Ok(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&mut self) -> Result<u64> {
|
||||||
|
let size = try_disk!(self.0.seek(SeekFrom::End(0)));
|
||||||
|
Ok(size)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
use syscall::error::{Error, Result, EIO};
|
||||||
|
|
||||||
|
use crate::disk::Disk;
|
||||||
|
use crate::BLOCK_SIZE;
|
||||||
|
|
||||||
|
pub struct DiskMemory {
|
||||||
|
data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiskMemory {
|
||||||
|
pub fn new(size: u64) -> DiskMemory {
|
||||||
|
DiskMemory {
|
||||||
|
data: vec![0; size as usize],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Disk for DiskMemory {
|
||||||
|
unsafe fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
|
||||||
|
let offset = (block * BLOCK_SIZE) as usize;
|
||||||
|
let end = offset + buffer.len();
|
||||||
|
if end > self.data.len() {
|
||||||
|
return Err(Error::new(EIO));
|
||||||
|
}
|
||||||
|
buffer.copy_from_slice(&self.data[offset..end]);
|
||||||
|
Ok(buffer.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result<usize> {
|
||||||
|
let offset = (block * BLOCK_SIZE) as usize;
|
||||||
|
let end = offset + buffer.len();
|
||||||
|
if end > self.data.len() {
|
||||||
|
return Err(Error::new(EIO));
|
||||||
|
}
|
||||||
|
self.data[offset..end].copy_from_slice(buffer);
|
||||||
|
Ok(buffer.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&mut self) -> Result<u64> {
|
||||||
|
Ok(self.data.len() as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
use syscall::error::Result;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub use self::cache::DiskCache;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub use self::file::DiskFile;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub use self::io::DiskIo;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub use self::memory::DiskMemory;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub use self::sparse::DiskSparse;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
mod cache;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
mod file;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
mod io;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
mod memory;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
mod sparse;
|
||||||
|
|
||||||
|
/// A disk
|
||||||
|
pub trait Disk {
|
||||||
|
/// Read blocks from disk
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// Unsafe to discourage use, use filesystem wrappers instead
|
||||||
|
unsafe fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize>;
|
||||||
|
|
||||||
|
/// Write blocks from disk
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// Unsafe to discourage use, use filesystem wrappers instead
|
||||||
|
unsafe fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result<usize>;
|
||||||
|
|
||||||
|
/// Get size of disk in bytes
|
||||||
|
fn size(&mut self) -> Result<u64>;
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
use std::fs::{File, OpenOptions};
|
||||||
|
use std::io::{Read, Seek, SeekFrom, Write};
|
||||||
|
use std::path::Path;
|
||||||
|
use syscall::error::{Error, Result, EIO};
|
||||||
|
|
||||||
|
use crate::disk::Disk;
|
||||||
|
use crate::BLOCK_SIZE;
|
||||||
|
|
||||||
|
macro_rules! try_disk {
|
||||||
|
($expr:expr) => {
|
||||||
|
match $expr {
|
||||||
|
Ok(val) => val,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Disk I/O Error: {}", err);
|
||||||
|
return Err(Error::new(EIO));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DiskSparse {
|
||||||
|
pub file: File,
|
||||||
|
pub max_size: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiskSparse {
|
||||||
|
pub fn create<P: AsRef<Path>>(path: P, max_size: u64) -> Result<DiskSparse> {
|
||||||
|
let file = try_disk!(OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.open(path));
|
||||||
|
Ok(DiskSparse { file, max_size })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Disk for DiskSparse {
|
||||||
|
unsafe fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
|
||||||
|
try_disk!(self.file.seek(SeekFrom::Start(block * BLOCK_SIZE)));
|
||||||
|
let count = try_disk!(self.file.read(buffer));
|
||||||
|
Ok(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result<usize> {
|
||||||
|
try_disk!(self.file.seek(SeekFrom::Start(block * BLOCK_SIZE)));
|
||||||
|
let count = try_disk!(self.file.write(buffer));
|
||||||
|
Ok(count)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&mut self) -> Result<u64> {
|
||||||
|
Ok(self.max_size)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
use alloc::boxed::Box;
|
|
||||||
|
|
||||||
use crate::{header::errno::STR_ERROR, platform::types::c_int};
|
|
||||||
|
|
||||||
/// Positive error codes (EINVAL, not -EINVAL).
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
|
||||||
// TODO: Move to a more generic place.
|
|
||||||
pub struct Errno(pub c_int);
|
|
||||||
|
|
||||||
impl Errno {
|
|
||||||
pub fn sync(self) -> Self {
|
|
||||||
crate::platform::ERRNO.set(self.0);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Result<T, E = Errno> = core::result::Result<T, E>;
|
|
||||||
|
|
||||||
#[cfg(target_os = "redox")]
|
|
||||||
impl From<syscall::Error> for Errno {
|
|
||||||
#[inline]
|
|
||||||
fn from(value: syscall::Error) -> Self {
|
|
||||||
Errno(value.errno)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[cfg(target_os = "redox")]
|
|
||||||
impl From<Errno> for syscall::Error {
|
|
||||||
#[inline]
|
|
||||||
fn from(value: Errno) -> Self {
|
|
||||||
syscall::Error::new(value.0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Errno> for crate::io::Error {
|
|
||||||
#[inline]
|
|
||||||
fn from(Errno(errno): Errno) -> Self {
|
|
||||||
Self::from_raw_os_error(errno)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: core::error::Error
|
|
||||||
|
|
||||||
impl core::fmt::Display for Errno {
|
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
||||||
match usize::try_from(self.0).ok().and_then(|i| STR_ERROR.get(i)) {
|
|
||||||
Some(desc) => write!(f, "{desc}"),
|
|
||||||
None => write!(f, "unknown error ({})", self.0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ResultExt<T> {
|
|
||||||
fn or_minus_one_errno(self) -> T;
|
|
||||||
}
|
|
||||||
impl<T: From<i8>> ResultExt<T> for Result<T, Errno> {
|
|
||||||
fn or_minus_one_errno(self) -> T {
|
|
||||||
match self {
|
|
||||||
Self::Ok(v) => v,
|
|
||||||
Self::Err(Errno(errno)) => {
|
|
||||||
crate::platform::ERRNO.set(errno);
|
|
||||||
T::from(-1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub trait ResultExtPtrMut<T> {
|
|
||||||
fn or_errno_null_mut(self) -> *mut T;
|
|
||||||
}
|
|
||||||
impl<T> ResultExtPtrMut<T> for Result<*mut T, Errno> {
|
|
||||||
fn or_errno_null_mut(self) -> *mut T {
|
|
||||||
match self {
|
|
||||||
Self::Ok(ptr) => ptr,
|
|
||||||
Self::Err(Errno(errno)) => {
|
|
||||||
crate::platform::ERRNO.set(errno);
|
|
||||||
core::ptr::null_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<T> ResultExtPtrMut<T> for Result<Box<T>, Errno> {
|
|
||||||
fn or_errno_null_mut(self) -> *mut T {
|
|
||||||
match self {
|
|
||||||
Self::Ok(ptr) => Box::into_raw(ptr),
|
|
||||||
Self::Err(Errno(errno)) => {
|
|
||||||
crate::platform::ERRNO.set(errno);
|
|
||||||
core::ptr::null_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,331 @@
|
|||||||
|
use aes::Aes128;
|
||||||
|
use alloc::{
|
||||||
|
collections::{BTreeMap, VecDeque},
|
||||||
|
vec,
|
||||||
|
vec::Vec,
|
||||||
|
};
|
||||||
|
use syscall::error::{Error, Result, EKEYREJECTED, ENOENT, ENOKEY};
|
||||||
|
use xts_mode::{get_tweak_default, Xts128};
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use crate::{AllocEntry, AllocList, BlockData, BlockTrait, Key, KeySlot, Node, Salt, TreeList};
|
||||||
|
use crate::{
|
||||||
|
Allocator, BlockAddr, BlockLevel, BlockMeta, Disk, Header, Transaction, BLOCK_SIZE,
|
||||||
|
HEADER_RING, RECORD_SIZE,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn compress_cache() -> Vec<u8> {
|
||||||
|
vec![0; lz4_flex::block::get_maximum_output_size(RECORD_SIZE as usize)]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A file system
|
||||||
|
pub struct FileSystem<D: Disk> {
|
||||||
|
//TODO: make private
|
||||||
|
pub disk: D,
|
||||||
|
//TODO: make private
|
||||||
|
pub block: u64,
|
||||||
|
//TODO: make private
|
||||||
|
pub header: Header,
|
||||||
|
pub(crate) allocator: Allocator,
|
||||||
|
pub(crate) cipher_opt: Option<Xts128<Aes128>>,
|
||||||
|
pub(crate) compress_cache: Vec<u8>,
|
||||||
|
pub node_usages: BTreeMap<u32, u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D: Disk> FileSystem<D> {
|
||||||
|
/// Open a file system on a disk
|
||||||
|
pub fn open(
|
||||||
|
mut disk: D,
|
||||||
|
password_opt: Option<&[u8]>,
|
||||||
|
block_opt: Option<u64>,
|
||||||
|
cleanup: bool,
|
||||||
|
) -> Result<Self> {
|
||||||
|
for ring_block in block_opt.map_or(0..65536, |x| x..x + 1) {
|
||||||
|
let mut header = Header::default();
|
||||||
|
unsafe { disk.read_at(ring_block, &mut header)? };
|
||||||
|
|
||||||
|
// Skip invalid headers
|
||||||
|
if !header.valid() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let block = ring_block - (header.generation() % HEADER_RING);
|
||||||
|
for i in 0..HEADER_RING {
|
||||||
|
let mut other_header = Header::default();
|
||||||
|
unsafe { disk.read_at(block + i, &mut other_header)? };
|
||||||
|
|
||||||
|
// Skip invalid headers
|
||||||
|
if !other_header.valid() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this is a newer header, use it
|
||||||
|
if other_header.generation() > header.generation() {
|
||||||
|
header = other_header;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let cipher_opt = match password_opt {
|
||||||
|
Some(password) => {
|
||||||
|
if !header.encrypted() {
|
||||||
|
// Header not encrypted but password provided
|
||||||
|
return Err(Error::new(EKEYREJECTED));
|
||||||
|
}
|
||||||
|
match header.cipher(password) {
|
||||||
|
Some(cipher) => Some(cipher),
|
||||||
|
None => {
|
||||||
|
// Header encrypted with a different password
|
||||||
|
return Err(Error::new(ENOKEY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
if header.encrypted() {
|
||||||
|
// Header encrypted but no password provided
|
||||||
|
return Err(Error::new(ENOKEY));
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut fs = FileSystem {
|
||||||
|
disk,
|
||||||
|
block,
|
||||||
|
header,
|
||||||
|
allocator: Allocator::default(),
|
||||||
|
cipher_opt,
|
||||||
|
compress_cache: compress_cache(),
|
||||||
|
node_usages: BTreeMap::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
unsafe { fs.reset_allocator()? };
|
||||||
|
|
||||||
|
if cleanup {
|
||||||
|
fs.cleanup()?
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(Error::new(ENOENT))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a file system on a disk
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub fn create(
|
||||||
|
disk: D,
|
||||||
|
password_opt: Option<&[u8]>,
|
||||||
|
ctime: u64,
|
||||||
|
ctime_nsec: u32,
|
||||||
|
) -> Result<Self> {
|
||||||
|
Self::create_reserved(disk, password_opt, &[], ctime, ctime_nsec)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a file system on a disk, with reserved data at the beginning
|
||||||
|
/// Reserved data will be zero padded up to the nearest block
|
||||||
|
/// We need to pass ctime and ctime_nsec in order to initialize the unix timestamps
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub fn create_reserved(
|
||||||
|
mut disk: D,
|
||||||
|
password_opt: Option<&[u8]>,
|
||||||
|
reserved: &[u8],
|
||||||
|
ctime: u64,
|
||||||
|
ctime_nsec: u32,
|
||||||
|
) -> Result<Self> {
|
||||||
|
let disk_size = disk.size()?;
|
||||||
|
let disk_blocks = disk_size / BLOCK_SIZE;
|
||||||
|
let block_offset = (reserved.len() as u64).div_ceil(BLOCK_SIZE);
|
||||||
|
if disk_blocks < (block_offset + HEADER_RING + 4) {
|
||||||
|
return Err(Error::new(syscall::error::ENOSPC));
|
||||||
|
}
|
||||||
|
let fs_blocks = disk_blocks - block_offset;
|
||||||
|
|
||||||
|
// Fill reserved data, pad with zeroes
|
||||||
|
for block in 0..block_offset as usize {
|
||||||
|
let mut data = [0; BLOCK_SIZE as usize];
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
while i < data.len() && block * BLOCK_SIZE as usize + i < reserved.len() {
|
||||||
|
data[i] = reserved[block * BLOCK_SIZE as usize + i];
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
disk.write_at(block as u64, &data)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut header = Header::new(fs_blocks * BLOCK_SIZE);
|
||||||
|
|
||||||
|
let cipher_opt = match password_opt {
|
||||||
|
Some(password) => {
|
||||||
|
//TODO: handle errors
|
||||||
|
header.key_slots[0] = KeySlot::new(
|
||||||
|
password,
|
||||||
|
Salt::new().unwrap(),
|
||||||
|
(Key::new().unwrap(), Key::new().unwrap()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
Some(header.key_slots[0].cipher(password).unwrap())
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut fs = FileSystem {
|
||||||
|
disk,
|
||||||
|
block: block_offset,
|
||||||
|
header,
|
||||||
|
allocator: Allocator::default(),
|
||||||
|
cipher_opt,
|
||||||
|
compress_cache: compress_cache(),
|
||||||
|
node_usages: BTreeMap::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Write header generation zero
|
||||||
|
let count = unsafe { fs.disk.write_at(fs.block, &fs.header)? };
|
||||||
|
if count != core::mem::size_of_val(&fs.header) {
|
||||||
|
// Wrote wrong number of bytes
|
||||||
|
#[cfg(feature = "log")]
|
||||||
|
log::error!("CREATE: WRONG NUMBER OF BYTES");
|
||||||
|
return Err(Error::new(syscall::error::EIO));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set tree and alloc pointers and write header generation one
|
||||||
|
fs.tx(|tx| unsafe {
|
||||||
|
let tree = BlockData::new(
|
||||||
|
BlockAddr::new(HEADER_RING + 1, BlockMeta::default()),
|
||||||
|
TreeList::empty(BlockLevel::default()).unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut alloc = BlockData::new(
|
||||||
|
BlockAddr::new(HEADER_RING + 2, BlockMeta::default()),
|
||||||
|
AllocList::empty(BlockLevel::default()).unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let alloc_free = fs_blocks - (HEADER_RING + 4);
|
||||||
|
alloc.data_mut().entries[0] = AllocEntry::new(HEADER_RING + 4, alloc_free as i64);
|
||||||
|
|
||||||
|
tx.header.tree = tx.write_block(tree)?;
|
||||||
|
tx.header.alloc = tx.write_block(alloc)?;
|
||||||
|
tx.header_changed = true;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
fs.reset_allocator()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.tx(|tx| unsafe {
|
||||||
|
let mut root = BlockData::new(
|
||||||
|
BlockAddr::new(HEADER_RING + 3, BlockMeta::default()),
|
||||||
|
Node::new(Node::MODE_DIR | 0o755, 0, 0, ctime, ctime_nsec),
|
||||||
|
);
|
||||||
|
root.data_mut().set_links(1);
|
||||||
|
let root_ptr = tx.write_block(root)?;
|
||||||
|
assert_eq!(tx.insert_tree(root_ptr)?.id(), 1);
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
fs.cleanup()?;
|
||||||
|
|
||||||
|
Ok(fs)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Release unused nodes and squash allocation log, happens on mount (with cleanup) and unmount
|
||||||
|
pub fn cleanup(&mut self) -> Result<()> {
|
||||||
|
let mut tx = Transaction::new(self);
|
||||||
|
tx.release_unused_nodes()?;
|
||||||
|
tx.commit(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// start a filesystem transaction, required for making any changes
|
||||||
|
pub fn tx<F: FnOnce(&mut Transaction<D>) -> Result<T>, T>(&mut self, f: F) -> Result<T> {
|
||||||
|
let mut tx = Transaction::new(self);
|
||||||
|
let t = f(&mut tx)?;
|
||||||
|
tx.commit(false)?;
|
||||||
|
Ok(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn allocator(&self) -> &Allocator {
|
||||||
|
&self.allocator
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unsafe as it can corrupt the filesystem
|
||||||
|
pub unsafe fn allocator_mut(&mut self) -> &mut Allocator {
|
||||||
|
&mut self.allocator
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reset allocator to state stored on disk
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// Unsafe, it must only be called when opening the filesystem
|
||||||
|
unsafe fn reset_allocator(&mut self) -> Result<()> {
|
||||||
|
self.allocator = Allocator::default();
|
||||||
|
|
||||||
|
// To avoid having to update all prior alloc blocks, there is only a previous pointer
|
||||||
|
// This means we need to roll back all allocations. Currently we do this by reading the
|
||||||
|
// alloc log into a buffer to reverse it.
|
||||||
|
let mut allocs = VecDeque::new();
|
||||||
|
self.tx(|tx| {
|
||||||
|
let mut alloc_ptr = tx.header.alloc;
|
||||||
|
while !alloc_ptr.is_null() {
|
||||||
|
let alloc = tx.read_block(alloc_ptr)?;
|
||||||
|
alloc_ptr = alloc.data().prev;
|
||||||
|
allocs.push_front(alloc);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
|
||||||
|
for alloc in allocs {
|
||||||
|
for entry in alloc.data().entries.iter() {
|
||||||
|
let index = entry.index();
|
||||||
|
let count = entry.count();
|
||||||
|
if count < 0 {
|
||||||
|
for i in 0..-count {
|
||||||
|
//TODO: replace assert with error?
|
||||||
|
let addr = BlockAddr::new(index + i as u64, BlockMeta::default());
|
||||||
|
assert_eq!(self.allocator.allocate_exact(addr), Some(addr));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for i in 0..count {
|
||||||
|
let addr = BlockAddr::new(index + i as u64, BlockMeta::default());
|
||||||
|
self.allocator.deallocate(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn decrypt(&mut self, data: &mut [u8], addr: BlockAddr) -> bool {
|
||||||
|
if let Some(ref cipher) = self.cipher_opt {
|
||||||
|
cipher.decrypt_area(
|
||||||
|
data,
|
||||||
|
BLOCK_SIZE as usize,
|
||||||
|
addr.index().into(),
|
||||||
|
get_tweak_default,
|
||||||
|
);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
// Do nothing if encryption is disabled
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn encrypt(&mut self, data: &mut [u8], addr: BlockAddr) -> bool {
|
||||||
|
if let Some(ref cipher) = self.cipher_opt {
|
||||||
|
cipher.encrypt_area(
|
||||||
|
data,
|
||||||
|
BLOCK_SIZE as usize,
|
||||||
|
addr.index().into(),
|
||||||
|
get_tweak_default,
|
||||||
|
);
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
// Do nothing if encryption is disabled
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
c_str::CStr,
|
|
||||||
error::{Errno, ResultExt},
|
|
||||||
header::{
|
|
||||||
fcntl::O_CREAT,
|
|
||||||
sys_stat::stat,
|
|
||||||
unistd::{SEEK_CUR, SEEK_END, SEEK_SET},
|
|
||||||
},
|
|
||||||
io,
|
|
||||||
out::Out,
|
|
||||||
platform::{Pal, Sys, types::*},
|
|
||||||
};
|
|
||||||
use core::ops::Deref;
|
|
||||||
|
|
||||||
pub struct File {
|
|
||||||
pub fd: c_int,
|
|
||||||
/// To avoid self referential FILE struct that needs both a reader and a writer,
|
|
||||||
/// make "reference" files that share fd but don't close on drop.
|
|
||||||
pub reference: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl File {
|
|
||||||
pub fn new(fd: c_int) -> Self {
|
|
||||||
Self {
|
|
||||||
fd,
|
|
||||||
reference: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn open(path: CStr, oflag: c_int) -> Result<Self, Errno> {
|
|
||||||
Sys::open(path, oflag, 0)
|
|
||||||
.map(Self::new)
|
|
||||||
.map_err(Errno::sync)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn openat(dirfd: c_int, path: CStr, oflag: c_int) -> Result<Self, Errno> {
|
|
||||||
Sys::openat(dirfd, path, oflag, 0)
|
|
||||||
.map(Self::new)
|
|
||||||
.map_err(Errno::sync)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create(path: CStr, oflag: c_int, mode: mode_t) -> Result<Self, Errno> {
|
|
||||||
Sys::open(path, oflag | O_CREAT, mode)
|
|
||||||
.map(Self::new)
|
|
||||||
.map_err(Errno::sync)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn createat(dirfd: c_int, path: CStr, oflag: c_int, mode: mode_t) -> Result<Self, Errno> {
|
|
||||||
Sys::openat(dirfd, path, oflag | O_CREAT, mode)
|
|
||||||
.map(Self::new)
|
|
||||||
.map_err(Errno::sync)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sync_all(&self) -> Result<(), Errno> {
|
|
||||||
Sys::fsync(self.fd).map_err(Errno::sync)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_len(&self, size: u64) -> Result<(), Errno> {
|
|
||||||
Sys::ftruncate(self.fd, size as off_t).map_err(Errno::sync)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fstat(&self) -> Result<stat, Errno> {
|
|
||||||
let mut file_st = stat::default();
|
|
||||||
Sys::fstat(self.fd, Out::from_mut(&mut file_st)).map_err(Errno::sync)?;
|
|
||||||
Ok(file_st)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_clone(&self) -> io::Result<Self> {
|
|
||||||
Ok(Self::new(Sys::dup(self.fd)?))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a new file pointing to the same underlying descriptor. This file
|
|
||||||
/// will know it's a "reference" and won't close the fd. It will, however,
|
|
||||||
/// not prevent the original file from closing the fd.
|
|
||||||
pub unsafe fn get_ref(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
fd: self.fd,
|
|
||||||
reference: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl io::Read for &File {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
match Sys::read(self.fd, buf).map(|read| read as ssize_t).or_minus_one_errno() /* TODO */ {
|
|
||||||
-1 => Err(io::last_os_error()),
|
|
||||||
ok => Ok(ok as usize),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl io::Write for &File {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
||||||
match Sys::write(self.fd, buf)
|
|
||||||
.map(|read| read as ssize_t)
|
|
||||||
.or_minus_one_errno()
|
|
||||||
{
|
|
||||||
-1 => Err(io::last_os_error()),
|
|
||||||
ok => Ok(ok as usize),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl io::Seek for &File {
|
|
||||||
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
|
|
||||||
let (offset, whence) = match pos {
|
|
||||||
io::SeekFrom::Start(start) => (start as off_t, SEEK_SET),
|
|
||||||
io::SeekFrom::Current(current) => (current as off_t, SEEK_CUR),
|
|
||||||
io::SeekFrom::End(end) => (end as off_t, SEEK_END),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Sys::lseek(self.fd, offset, whence)? as u64)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl io::Read for File {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
(&mut &*self).read(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl io::Write for File {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
||||||
(&mut &*self).write(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
|
||||||
(&mut &*self).flush()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl io::Seek for File {
|
|
||||||
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
|
|
||||||
(&mut &*self).seek(pos)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for File {
|
|
||||||
type Target = c_int;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.fd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for File {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if !self.reference {
|
|
||||||
let _ = Sys::close(self.fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user