520 Commits

Author SHA1 Message Date
Red Bear OS b78a791e27 redoxfs: sync deps with upstream (redox-path 0.4, smallvec) 2026-07-11 12:44:23 +03:00
Red Bear OS 90e0fe210a 0.3.1: bump version suffix to +rb0.3.1 2026-07-11 11:25:56 +03:00
Red Bear OS c74e2864b0 0.3.0: refresh Cargo.lock for +rb0.3.0 local forks 2026-07-11 11:25:56 +03:00
vasilito 96d435936d 0.3.0: bump version suffix to +rb0.3.0 2026-07-11 11:25:56 +03:00
Red Bear OS a936d00e6c redoxfs: import Vec from alloc for no_std builds (bootloader dependency) 2026-07-11 11:25:56 +03:00
vasilito 439c1f2905 redbear: apply readonly-live-media patch, add author attribution, use local redox-scheme path 2026-07-11 11:25:56 +03:00
Red Bear OS b199929813 fix: use local path deps for redox_syscall and libredox 2026-07-11 11:25:38 +03:00
Red Bear OS dc8413f4cb version: use +rb0.2.5 build metadata instead of -rb0.2.5 pre-release
Semver pre-release suffix (-rb0.2.5) breaks Cargo's [patch.crates-io]
matching for transitive deps. Build metadata (+rb0.2.5) is semver-
compatible: ^0.9.0 matches 0.9.0+rb0.2.5, patch redirection works,
and the Red Bear suffix is still visible in the version string.
2026-07-11 11:23:58 +03:00
Jeremy Soller 6ade863ea5 Merge branch 'symlink-fix' into 'master'
Fix following symlink

See merge request redox-os/redoxfs!123
2026-07-07 10:23:59 -06:00
Wildan M d807dd3e4b Fix following symlink 2026-07-07 05:43:38 +07:00
Jeremy Soller 065e22b7c4 Merge branch 'path-update' into 'master'
Update redox-path

See merge request redox-os/redoxfs!122
2026-07-05 10:54:08 -06:00
Wildan M 57ad275d8f Update redox-path 2026-07-05 18:33:55 +07:00
Jeremy Soller 473b4baeb0 0.9.1 2026-07-01 08:32:44 -06:00
Jeremy Soller 00ba438c95 Merge branch 'update-redox-crates' into 'master'
chore: Update redox crates versions

See merge request redox-os/redoxfs!121
2026-07-01 08:31:44 -06:00
Ibuki Omatsu 6c1e2aa446 chore: Update redox crates versions 2026-07-01 08:31:44 -06:00
Jeremy Soller 3acc7a240f Merge branch 'no-box' into 'master'
Remove Box from implementation

See merge request redox-os/redoxfs!120
2026-06-24 13:48:49 -06:00
Wildan M 47c9d242f5 Remove Box from Vec conversion 2026-06-23 14:09:19 +07:00
Wildan M 6767faba13 Remove Box in scheme handle 2026-06-23 14:01:31 +07:00
Wildan M 44bbd27e1b Fix warning 2026-06-23 13:58:50 +07:00
Jeremy Soller af493b9f4e Merge branch 'inode-cache' into 'master'
Implement Inode cache from fmap

Closes #56

See merge request redox-os/redoxfs!118
2026-05-13 07:44:12 -06:00
Wildan M d03893a253 Add munmap to solve leak 2026-05-13 11:23:45 +07:00
Wildan M 3ed2bee9bb Fix CI run 2026-05-13 07:08:07 +07:00
Wildan M 1d1176de40 Add fmap version for invalidation 2026-05-13 06:48:39 +07:00
Wildan M 5b515c0c9b Implement fmap for read cache 2026-05-13 06:34:39 +07:00
Jeremy Soller f26c86b176 Update dependencies 2026-05-04 12:31:39 -06:00
Jeremy Soller b59677627c Merge branch 'retry-readdir' into 'master'
Skip missing getdents item

See merge request redox-os/redoxfs!117
2026-03-30 14:31:28 -06:00
Wildan M 3d6f9cec6e Skip missing getdents item 2026-03-30 21:04:03 +07:00
Jeremy Soller 0955762196 Merge branch 'fix_unmount' into 'master'
Partially fix unmount on redox

See merge request redox-os/redoxfs!116
2026-03-11 13:40:09 -06:00
bjorn3 7f469db981 Partially fix unmount on redox
This is not enough to cause redoxfs to quit, but does remove it from
the namespace.
2026-03-11 20:06:07 +01:00
Jeremy Soller 7dad335ba7 Merge branch 'fix-fchdir' into 'master'
fix: Fix fpath implementation for root directory

See merge request redox-os/redoxfs!115
2026-03-06 18:01:31 -07:00
Ibuki.O eb50a95c9b fix: Fix fpath implementation for root directory 2026-03-07 09:55:31 +09:00
Jeremy Soller c4b51b9ca8 Merge branch 'stdfscall' into 'master'
feat: Add std_fs_call support

See merge request redox-os/redoxfs!113
2026-02-28 08:05:09 -07:00
Ibuki Omatsu 04dbcbcd91 feat: Add std_fs_call support 2026-02-28 08:05:08 -07:00
Jeremy Soller e75d311776 Merge branch 'touch-dir' into 'master'
Implement utimens for directory

See merge request redox-os/redoxfs!114
2026-02-27 06:18:55 -07:00
Wildan M 64a8ff46e1 Implements utimens for directory 2026-02-27 14:27:34 +07:00
Jeremy Soller d00745b402 Merge branch 'master' into 'master'
feat(redox): flocking

See merge request redox-os/redoxfs!112
2026-02-25 08:24:56 -07:00
Anhad Singh c51642c8b6 chore: bump redox_syscall and redox_scheme
Signed-off-by: Anhad Singh <andypython@protonmail.com>
2026-02-26 02:14:10 +11:00
Anhad Singh 61d52cf6a7 misc(mount/redox): update to new on_detach API
Signed-off-by: Anhad Singh <andypython@protonmail.com>
2026-02-25 01:49:23 +11:00
Anhad Singh 2360a346ac feat(redox): flocking
Signed-off-by: Anhad Singh <andypython@protonmail.com>
2026-02-24 01:02:26 +11:00
Jeremy Soller 1e0119b3cb Merge branch 'canonical-relative-path' into 'master'
feat: Resolve parent paths

See merge request redox-os/redoxfs!111
2026-02-21 07:47:59 -07:00
Ibuki Omatsu ceeaf513b6 feat: Resolve parent paths 2026-02-21 07:47:59 -07:00
Anhad Singh f4a256ebcd misc: cargo fmt
Signed-off-by: Anhad Singh <andypython@protonmail.com>
2026-02-11 10:40:15 +11:00
Jeremy Soller d47cd7d3f6 Merge branch 'unlinkat' into 'master'
Introduce SchemeRoot and integrate unlink and rmdir into unlinkat.

See merge request redox-os/redoxfs!104
2026-01-20 20:55:47 -07:00
Ibuki Omatsu 32d067d293 Introduce SchemeRoot and integrate unlink and rmdir into unlinkat. 2026-01-20 20:55:47 -07:00
Jeremy Soller 4726ebdd9d Update Cargo.lock 2026-01-03 09:45:02 -07:00
Jeremy Soller 42db72daf2 0.8.6: workaround for memory leaks 2026-01-03 09:44:23 -07:00
Jeremy Soller 57277f2041 0.8.5 2026-01-02 19:31:28 -07:00
Jeremy Soller 9d4ae07987 Merge branch 'delete-after-close' into 'master'
delete after close instead of unlink

See merge request redox-os/redoxfs!102
2026-01-02 19:30:11 -07:00
Jeremy Soller 7bd6189ca9 Fix no_std 2026-01-02 19:25:26 -07:00
Jeremy Soller 5778818bf6 Fix typo in redox mount code 2026-01-02 13:26:09 -07:00
Jeremy Soller 0e01427cd8 Implement release list and add open node tracking to filesystem 2026-01-02 13:23:29 -07:00
Jeremy Soller 57e05acef2 Leak nodes still in use, preparation for release list 2025-12-25 15:52:27 -07:00
Jeremy Soller a44303b4f6 WIP: delete after close instead of unlink 2025-12-25 15:51:53 -07:00
Jeremy Soller 2e1bca19ac Merge branch 'test-lints' into 'master'
Fix test lints and add rename test

See merge request redox-os/redoxfs!109
2025-12-20 21:33:15 -07:00
Josh Megnauth 2b8ea938ea Fix test lints and add rename test
This fixes lints for tests/tests.rs and adds a test for rename. The new
rename test is based on the rename_no_replace tests. There are two
discrepancies between Linux and Redox but I'm not sure if they matter. I
added a note to those tests.

There are two fixed lints of note.
* assert_cmd deprecated Command::cargo_bin in favor of a compile time
  checked macro.
* The mode literals were wrong: 0666 instead of 0o666. 0o is the prefix
  for octal literals.
2025-12-20 19:17:36 -05:00
Jeremy Soller a2f3b55b68 Merge branch 'replace-unlink-rmdir-with-unlinkat' into 'master'
refactor: Replace unlink and rmdir with unlinkat.

See merge request redox-os/redoxfs!107
2025-12-17 18:45:39 -07:00
Ibuki Omatsu a59e7fca9b refactor: Replace unlink and rmdir with unlinkat. 2025-12-17 18:45:39 -07:00
Jeremy Soller c0777c08ac Merge branch 'bump-ver' into 'master'
Bump version to fix installation

See merge request redox-os/redoxfs!108
2025-12-16 09:48:06 -07:00
Wildan M 52b230fc67 Bump version to fix installation 2025-12-16 20:53:14 +07:00
Jeremy Soller b8f2c63bdd Merge branch 'book-on-readme' into 'master'
Mention book for usage on README

See merge request redox-os/redoxfs!106
2025-12-14 21:02:33 -07:00
Jeremy Soller dc77e817e2 Merge branch 'impl-rename-noreplace' into 'master'
Implement RENAME_NO_REPLACE

See merge request redox-os/redoxfs!105
2025-12-14 19:12:36 -07:00
Josh Megnauth 66f4766c6b Implement RENAME_NO_REPLACE
RENAME_NO_REPLACE is a neat flag for renameat2 which disallows renaming
a source onto an existing target.

I originally implemented this flag in relibc, but that code is still
subject to TOCTOU because the sequence of checking if the target exists
and the actual rename isn't atomic.

The code for the flag isn't used anywhere yet, like frename, but it's
unit tested and works for now.
2025-12-14 21:03:56 -05:00
Wildan M 644f747648 Mention book for usage on README 2025-12-15 05:21:05 +07:00
Jeremy Soller 5c94db6f1a Merge branch 'decouple-fuse' into 'master'
Split FUSE feature and move redox dependencies

See merge request redox-os/redoxfs!103
2025-11-17 06:53:54 -07:00
Wildan M 50683d5a7a Split fuse feature 2025-11-17 17:21:59 +07:00
Jeremy Soller 54df280b02 Use new scheme format in fpath 2025-11-14 14:29:19 -07:00
Jeremy Soller a7f61b12ad 0.8.2 2025-11-10 21:01:48 -07:00
Jeremy Soller 91c5ffb986 When upgrading from inline data, only take bytes that fit 2025-11-10 20:59:08 -07:00
Jeremy Soller 116d482d2b Reduce logging 2025-11-01 20:25:43 -06:00
Jeremy Soller b6700b94dc 0.8.1 2025-10-31 11:21:04 -06:00
Jeremy Soller fefd943c3f Add resizing support 2025-10-31 11:16:13 -06:00
Jeremy Soller ab92a083e8 Ensure header size matches allocated blocks 2025-10-31 07:41:29 -06:00
Jeremy Soller 5abce443a5 Ensure allocation is done with the lowest block indexes first 2025-10-30 21:25:37 -06:00
Jeremy Soller 84288a2f5d 0.8.0 2025-09-26 15:45:33 -06:00
Jeremy Soller 2dee3bba4c Merge branch 'v8' into 'master'
v8

See merge request redox-os/redoxfs!95
2025-09-26 15:44:29 -06:00
Jeremy Soller 5af2abde0a Fix reading sparse data when using inline data 2025-09-26 14:36:22 -06:00
Jeremy Soller 437d06ab8c Support inline data in files 2025-09-26 14:17:03 -06:00
Jeremy Soller 5c54b189f9 Prepare for inline node data support 2025-09-26 13:03:07 -06:00
Jeremy Soller 9484534c76 Merge remote-tracking branch 'origin/master' into v8 2025-09-26 12:03:22 -06:00
Jeremy Soller eea5c96137 Implement clone threshold to work around Redox problems 2025-09-26 11:25:22 -06:00
Jeremy Soller 5e044e88c0 Fix cursor when printing clone progress 2025-09-26 10:43:34 -06:00
Jeremy Soller d7391bc6a8 Throttle clone progress messages 2025-09-26 10:18:54 -06:00
Jeremy Soller c796cc7413 Add units for clone progress 2025-09-26 10:10:22 -06:00
Jeremy Soller f37afb04bb Add progress callback to clone function 2025-09-26 10:08:54 -06:00
Jeremy Soller 74582a0917 Squash allocation log in clone 2025-09-26 09:29:02 -06:00
Jeremy Soller 5622165c92 Support for fast cloning of RedoxFS partition 2025-09-26 09:24:21 -06:00
Jeremy Soller 8a46a6df39 Implement From<File> for DiskFile 2025-09-26 09:23:41 -06:00
Jeremy Soller ae034662e6 Fix documentation 2025-09-26 09:23:29 -06:00
Jeremy Soller 0a1fd60d76 Merge branch 'fix-ci' into 'master'
Move tests with mount as integration tests

See merge request redox-os/redoxfs!99
2025-09-25 12:15:47 -06:00
Wildan M fc4c2470a1 Fix test 2025-09-26 01:02:07 +07:00
Wildan Mubarok 508feda5eb Only run integration tests 2025-09-25 14:52:19 +00:00
Wildan M 8591d96f67 Detect redoxer hangs 2025-09-25 21:30:43 +07:00
Wildan M 2d61aceff9 Move tests with mount as integration tests 2025-09-25 20:58:49 +07:00
Jeremy Soller 25cc366a63 Merge branch 'partial_getdents' into 'master'
Allow partial result of getdents

See merge request redox-os/redoxfs!98
2025-09-23 10:57:23 -06:00
Wildan M 4af64e157a Allow partial result of getdents 2025-09-23 23:34:26 +07:00
Jeremy Soller 98286ce752 Fix compilation with no_std 2025-08-03 20:02:09 -06:00
Jeremy Soller 5828e12e85 Provide context for all allocations and deallocations 2025-08-01 21:32:16 -06:00
Jeremy Soller 0a14166c2f Fix issue where compressed data could be returned 2025-08-01 14:07:26 -06:00
Jeremy Soller d03aa518fe Fix compression with 128 KiB records, use compress cache 2025-08-01 13:23:56 -06:00
Jeremy Soller b40fecf185 Fix comment in node.rs 2025-08-01 12:55:49 -06:00
Jeremy Soller ca7ea3c0c8 node: debug blocks 2025-08-01 12:07:51 -06:00
Jeremy Soller a1c94311db Store number of blocks actually used 2025-08-01 11:51:37 -06:00
Jeremy Soller f30b363ce1 Fix build with no_std 2025-08-01 09:55:21 -06:00
Jeremy Soller 26dc32d710 v8: transparent lz4 compression 2025-07-31 18:48:17 -06:00
Jeremy Soller 0cb6f3cad5 Fix compilation on Linux 2025-07-24 13:45:57 -06:00
Jeremy Soller 1506ed50fc Merge branch 'bind-connect-redoxfs' into 'master'
feat: Add UDS bind and connect integration

See merge request redox-os/redoxfs!94
2025-07-21 18:32:18 -06:00
Ibuki Omatsu 63217aa69a feat: Add UDS bind and connect integration 2025-07-21 18:32:18 -06:00
Jeremy Soller dc25344b68 Update aes and xts-mode 2025-07-02 12:48:39 -06:00
Jeremy Soller bd6efe0c83 Update crate version to 0.7 2025-07-02 12:27:14 -06:00
Jeremy Soller aecd042afc Use AES-XTS for encryption 2025-07-02 10:20:17 -06:00
Jeremy Soller 291ce5edc0 Merge branch 'Improve-file-creation-performance' into 'v7'
Improve performance

See merge request redox-os/redoxfs!87
2025-07-01 19:11:00 -06:00
James Matlik b959c34287 Improve performance 2025-07-01 19:11:00 -06:00
Jeremy Soller 02685f5c70 Merge branch 'openat' into 'master'
Implement openat syscall

See merge request redox-os/redoxfs!92
2025-07-01 09:42:16 -06:00
Darley Barreto d0d5761b2d Implement openat syscall 2025-07-01 09:42:16 -06:00
Jeremy Soller 066e5f6e8a Merge branch 'update-syscall' into 'master'
Update redox_syscall

See merge request redox-os/redoxfs!91
2025-06-12 12:23:48 -06:00
Jeremy Soller 9e2e370c0b Merge branch 'improve-node-mode-docstrings' into 'master'
Improve node mode docstrings

See merge request redox-os/redoxfs!90
2025-06-12 10:46:40 -06:00
Ron Williams 165d674d98 Update redox_syscall 2025-06-12 09:46:19 -07:00
Yousef Moazzam 3d06dd39ba Refer to "node" rather than "file" in node mode docstrings 2025-05-24 21:56:30 +01:00
Yousef Moazzam 13642ab159 Clarify bit position using most/least significant bit terminology 2025-05-24 21:55:39 +01:00
Yousef Moazzam 204abb9c5d Add node type bits info to node mode docstrings 2025-05-24 21:22:54 +01:00
Jeremy Soller c403c1eb54 Update redox-scheme, implement flink and getdents 2025-05-03 13:27:33 -06:00
Jeremy Soller 940b027514 0.6.11 2025-04-20 18:52:21 -06:00
Jeremy Soller d17b938693 Merge branch 'Fix-reset_allocator-regression-incorrectly-returning-ENOSPC' into 'master'
Fix reset_allocator regression that incorrectly returns ENOSPC

See merge request redox-os/redoxfs!89
2025-04-20 21:54:10 +00:00
James Matlik fd0bb82b50 Fix reset_allocator regression that incorrectly returns ENOSPC 2025-04-20 17:43:08 -04:00
Jeremy Soller 3170e8cd9c Merge branch 'misc_improvements' into 'master'
Couple of misc improvements

See merge request redox-os/redoxfs!88
2025-04-01 20:01:31 +00:00
bjorn3 7e3915deb6 Use Iterator::all in two places 2025-04-01 21:53:28 +02:00
bjorn3 22f759aa4f Rustc now allows many derives for packed structs 2025-04-01 21:51:10 +02:00
bjorn3 dc507f7e2a Remove unreachable dup impl 2025-04-01 21:43:51 +02:00
bjorn3 5ee3138d3d Fix cargo test
Rustc complained about being unable to infer the type for []
2025-04-01 21:42:54 +02:00
Jeremy Soller 6bccac38c3 0.6.10: fix mount path on Redox 2025-03-25 15:45:15 -06:00
Jeremy Soller 32aeff0cae Merge branch 'Add-alloc-log-gc' into 'master'
Add alloc log GC

See merge request redox-os/redoxfs!86
2025-03-01 17:17:46 +00:00
James Matlik 1aa3142f21 Add alloc log GC 2025-03-01 17:17:46 +00:00
Jeremy Soller ec5f32ec38 0.6.9 2025-02-28 18:48:18 -07:00
Jeremy Soller 84cf28b5d0 Merge branch 'Fix-leaking-storage-on-file-delete' into 'master'
Fix leaking tree node on file delete

See merge request redox-os/redoxfs!85
2025-03-01 01:25:18 +00:00
James Matlik 9531b20bf8 Fix leaking tree node on file delete 2025-03-01 01:25:18 +00:00
Jeremy Soller 6abf533fdf Merge branch 'mark/fs-cleanup' into 'master'
Documentation & minor cleanup

See merge request redox-os/redoxfs!84
2024-12-24 14:26:19 +00:00
rm-dr a0c734da01 Documentation & minor cleanup 2024-12-24 14:26:19 +00:00
Jeremy Soller 90fbcab52e Merge branch 'document-mount' into 'master'
Document how to mount a RedoxFS partition

See merge request redox-os/redoxfs!83
2024-12-15 14:29:10 +00:00
Ribbon 356ec3ffb1 Document how to mount a RedoxFS partition 2024-12-15 14:29:10 +00:00
Jeremy Soller 9a0da927f9 Fix argument parsing 2024-11-04 21:24:41 -07:00
Jeremy Soller c2790a7168 Merge branch 'nodaemon' into 'master'
Added a no daemon option.

See merge request redox-os/redoxfs!82
2024-11-04 21:38:17 +00:00
JCake 947ea54411 No Daemon option 2024-11-02 21:30:58 +01:00
Jeremy Soller ee04ed2f8c 0.6.8 2024-10-29 07:52:42 -06:00
Jeremy Soller 8f6e47c1da Merge branch 'node_leak' into 'master'
Don't leak node block on node remove.

See merge request redox-os/redoxfs!81
2024-10-29 12:30:31 +00:00
Andrey Turkin c22fb790b8 Fix parallel test runs 2024-10-29 12:22:15 +03:00
Andrey Turkin 1d55c5aa6a Don't leak node block on node remove.
Refs #50
2024-10-29 10:17:47 +03:00
Jeremy Soller 59dbfee126 Merge branch 'fuse_fix' into 'master'
Fix a fuse bug

See merge request redox-os/redoxfs!80
2024-10-27 18:09:10 +00:00
Andrey Turkin 469db981a4 Fix a fuse bug
Create reply contains flags but those are not O_* passed in by request.
Those are FOPEN_* fuse-specific flags describing new file's mode (caching,
passthrough etc).
2024-10-27 20:45:25 +03:00
Jeremy Soller d8950b52c2 Merge branch 'change_blocklevel_calc' into 'master'
Changed for_bytes method for BlockLevel.

See merge request redox-os/redoxfs!79
2024-10-26 01:25:11 +00:00
James Francis 56e66993af Changed for_bytes method for BlockLevel. 2024-10-26 01:25:11 +00:00
Jeremy Soller ab371cff49 Update redox-scheme 2024-10-09 10:12:48 -06:00
Jeremy Soller 97ffcd4ad3 0.6.7 2024-10-06 11:07:32 -06:00
Jeremy Soller b9b740f3b0 Merge branch 'riscv' into 'master'
Bump dependencies

See merge request redox-os/redoxfs!78
2024-10-06 16:12:25 +00:00
Andrey Turkin 08361c6595 Bump dependencies 2024-10-06 18:58:27 +03:00
Jeremy Soller 3cf7bf58ab 0.6.6 2024-09-19 20:47:29 -06:00
4lDO2 707162611e Future-proof repr(packed) => repr(C, packed). 2024-09-08 21:06:03 +02:00
Jeremy Soller 5c8f22b069 Merge branch 'master' into 'master'
Clean up indentation using let-else and format.

See merge request redox-os/redoxfs!76
2024-09-05 13:53:15 +00:00
syn 43ec8d1417 Clean up indentation using let-else and format. 2024-09-02 00:46:48 +03:00
Jeremy Soller 2ac60da18d Merge branch 'fix' into 'master'
Fixed no_std regression in 0.6.5

See merge request redox-os/redoxfs!75
2024-07-15 14:49:34 +00:00
Andrey Turkin 3baf131419 Fixed no_std regression in 0.6.5
And bumped some dependencies and fixed some warnings
2024-06-29 20:52:24 +03:00
Jeremy Soller 554f3dc205 Merge branch 'fuzzer--fuse' into 'master'
Add redoxfs fuzzer for FUSE interface

See merge request redox-os/redoxfs!74
2024-06-27 16:05:42 +00:00
emaxx-g 6a6bd97910 Add redoxfs fuzzer for FUSE interface 2024-06-27 16:05:42 +00:00
Jeremy Soller 6c052ba241 0.6.5 2024-06-24 11:16:25 -06:00
Jeremy Soller af3032de26 Merge branch 'schemev2plus' into 'master'
Use positioned IO interfaces both for the scheme itself and disk IO

See merge request redox-os/redoxfs!73
2024-06-14 12:46:51 +00:00
4lDO2 4f2b5e65ee Update dependencies. 2024-06-14 14:32:47 +02:00
4lDO2 5174b2bba6 Use FileExt::{read,write}_at for disk IO. 2024-06-13 17:20:34 +02:00
4lDO2 708cc1c5ae Use SYS_READ2 and SYS_WRITE2 for disk IO. 2024-06-13 16:30:13 +02:00
4lDO2 8a31d1b6a6 Use positioned IO scheme interface. 2024-06-12 17:37:06 +02:00
4lDO2 3800bf1510 Switch to v2 packet interface. 2024-06-11 16:10:34 +02:00
Jeremy Soller 136ff8c8dc Update dependencies 2024-05-27 09:40:10 -06:00
Jeremy Soller faec36773b 0.6.4 2024-05-27 09:35:10 -06:00
Jeremy Soller 247ef8bf62 Merge branch 'endian-num' into 'master'
Migrate from `redox_simple_endian` to `endian-num`

See merge request redox-os/redoxfs!72
2024-05-27 15:34:24 +00:00
Martin Kröning 8cc2e31299 Migrate from redox_simple_endian to endian-num
Signed-off-by: Martin Kröning <martin.kroening@eonerc.rwth-aachen.de>
2024-05-27 14:13:51 +02:00
Jeremy Soller dcba5d8018 0.6.3: Fix CoW of records dropping data 2024-04-01 13:30:31 -06:00
Jeremy Soller 7dc8323d24 0.6.2: Make libredox only required for std feature 2024-03-23 09:15:06 -06:00
Jeremy Soller e8cea1af66 0.6.1: fix read_records block use after free 2024-03-23 08:52:54 -06:00
Jeremy Soller bfa2f33798 Merge branch 'libredox' into 'master'
Switch almost fully to libredox.

See merge request redox-os/redoxfs!71
2024-03-18 20:37:13 +00:00
4lDO2 15f3c909c7 Switch almost fully to libredox. 2024-03-18 17:28:43 +01:00
Jeremy Soller 1713850c0e Merge branch 'cancellation' into 'master'
Handle but don't impl scheme cancellation.

See merge request redox-os/redoxfs!70
2024-03-16 13:06:10 +00:00
4lDO2 efb29941f5 Handle but don't impl scheme cancellation. 2024-03-16 09:30:14 +01:00
Jeremy Soller 14a07dc268 Merge branch 'master' into 'master'
Master - Name check & Duplicate code removed & Makefile modification

See merge request redox-os/redoxfs!68
2024-03-07 14:40:28 +00:00
George Malandrakis e4ca510bc0 Master - Name check & Duplicate code removed & Makefile modification 2024-03-07 14:40:27 +00:00
Jeremy Soller 7c6e95d9f0 Merge branch 'path-improvements' into 'master'
use new path formatting and conversion code

See merge request redox-os/redoxfs!69
2024-03-01 14:59:34 +00:00
Ron Williams 5156ebd179 use new path formatting and conversion code 2024-03-01 06:24:13 -08:00
Jeremy Soller e7cf569a1f Merge branch 'v6' into 'master'
V6

See merge request redox-os/redoxfs!67
2024-02-15 23:03:38 +00:00
Jeremy Soller c07e46b7ba V6 2024-02-15 23:03:38 +00:00
Jeremy Soller 667ffcc01a Fix test compilation 2024-02-14 12:59:16 -07:00
Jeremy Soller 07e8093ee0 Allow allocating in different sizes 2024-02-14 12:53:06 -07:00
Jeremy Soller ab0bb9aec7 Fix more clippy lints 2024-02-14 12:45:19 -07:00
Jeremy Soller 644a2d2081 Fix clippy lints 2024-02-14 12:38:02 -07:00
Jeremy Soller 4e0ab54567 Make everything scale with block size and format 2024-02-14 10:15:26 -07:00
Jeremy Soller 5f00068c26 Merge branch 'redox-path' into 'master'
Use new path format

See merge request redox-os/redoxfs!66
2024-02-09 15:06:51 +00:00
Ron Williams c1601fe16c Use new path format 2024-02-09 15:06:51 +00:00
Jeremy Soller e3f0fe6ef9 Remove mmap prints 2024-02-08 17:34:20 -07:00
Jeremy Soller b3dd3aa5f0 Merge branch 'symlink-fix' into 'master'
fix symbolic links plus general cleanup

See merge request redox-os/redoxfs!65
2024-01-03 16:39:58 +00:00
Ron Williams 944b175a6e fix symbolic links plus general cleanup 2024-01-03 16:39:58 +00:00
Jeremy Soller 86c12ebd7c 0.5.13: Remove failure dependency and fix no_std 2023-12-12 13:24:59 -07:00
Jeremy Soller 2bec291700 0.5.12: Make range-tree optional 2023-12-12 13:16:31 -07:00
Jeremy Soller 9fad87efbe 0.5.11 2023-12-12 13:07:57 -07:00
Jeremy Soller dcf40072f4 Ensure that dealloc of allocator log blocks are queued, not immediate 2023-12-12 13:07:10 -07:00
Jeremy Soller 9a8437f468 0.5.10: Squash allocations and sync on unmount 2023-12-12 11:10:04 -07:00
Jeremy Soller 97bc669a76 0.5.9: Remove use of stable feature int_roundings 2023-12-11 10:26:27 -07:00
Jeremy Soller 50b4ebfcd5 Merge branch 'fix-statement' into 'master'
Fix GPL statement and quote FUSE for Linux

See merge request redox-os/redoxfs!64
2023-12-07 15:40:25 +00:00
Ribbon 3f1bff26e3 Fix GPL statement and quote FUSE for Linux 2023-12-07 15:40:25 +00:00
Jeremy Soller 6ce9d1fdac Merge branch 'remove_physmap' into 'master'
Replace physmap with fmap.

See merge request redox-os/redoxfs!63
2023-12-07 15:37:06 +00:00
4lDO2 2c2920572c Replace physmap with fmap. 2023-11-18 13:24:27 +01:00
Jeremy Soller ae715277f3 Cache current fmap size 2023-11-15 12:20:15 -07:00
Jeremy Soller 4048f541a0 Merge branch 'improve-readme' into 'master'
Improve/cleanup the README

See merge request redox-os/redoxfs!62
2023-09-30 17:38:07 +00:00
Ribbon b9960350c8 Improve/cleanup the README 2023-09-30 15:44:24 +00:00
Jeremy Soller 985d6e815c 0.5.8: update uuid to 1.4 2023-09-07 16:19:42 -06:00
Jeremy Soller 54341680ec Merge branch 'better_fmap' into 'master'
Offset-based mmap

See merge request redox-os/redoxfs!61
2023-08-07 15:37:15 +00:00
4lDO2 6234d76e8d Fix mremap usage. 2023-08-03 12:38:19 +02:00
4lDO2 7f8a2e467e Update dependencies. 2023-08-02 15:28:35 +02:00
4lDO2 ae1f15a449 Offset-based file mmap. 2023-07-26 16:06:53 +02:00
4lDO2 5db0da52e2 WIP: Use improved mmap interface. 2023-07-20 16:58:58 +02:00
Jeremy Soller e7e0e3ce8d Merge branch 'sighandling' into 'master'
Prefer libc over redox_syscall in mount.

See merge request redox-os/redoxfs!60
2023-06-21 19:27:43 +00:00
4lDO2 58fff36b4e Prefer libc over redox_syscall in mount. 2023-06-18 19:41:08 +02:00
Jeremy Soller 211e12b842 Merge branch 'master' into 'master'
Fix unnecessary mutable variable warning

See merge request redox-os/redoxfs!59
2023-05-08 12:37:33 +00:00
Will Angenent 13948965fa Fix unnecessary mutable variable warning 2023-05-08 13:11:16 +01:00
Jeremy Soller ad432d527d 0.5.7 - enable compilation with stable 2023-04-15 07:34:34 -06:00
Jeremy Soller 2db6998889 Merge commit '588bd3203b70219bf27bedf0703199ec2ea5f505' 2023-04-15 07:32:11 -06:00
Jeremy Soller eb8dc00fa8 Add cfg(kani) to proofs 2023-04-06 14:48:54 -06:00
Jeremy Soller 1a56983249 Formally verify node owner and permission functions 2023-04-05 20:58:51 -06:00
Jeremy Soller 588bd3203b 0.5.6 2023-03-27 08:45:41 -06:00
Jeremy Soller c9f9e0be7d Merge branch 'use-fusermount3-where-possible' into 'master'
Fix image unmounting & tooling to work under gentoo and MacOS

See merge request redox-os/redoxfs!58
2023-03-25 13:55:49 +00:00
Will Angenent 20fff1a0ee Adapted Makefile to work with gentoo and MacOS 2023-03-24 19:19:33 +00:00
Will Angenent a7c22caba5 Added unmount_path method that tries several fusermount binaries
This fixes unmounting with gentoo. This function will be use by the installer to
unmount the image after the build.
2023-03-24 18:54:08 +00:00
Jeremy Soller ace1726e29 0.5.5 2023-03-20 10:23:14 -06:00
Jeremy Soller 9a4638e922 Merge branch 'migrate-from-fuse-to-fuser' into 'master'
Migrate from fuse to fuser

See merge request redox-os/redoxfs!57
2023-03-20 01:15:13 +00:00
Will Angenent 62e59e1006 Migrate from fuse to fuser 2023-03-18 08:53:54 +00:00
Jeremy Soller cdccd5d786 0.5.4 2023-03-08 12:48:42 -07:00
Jeremy Soller 06d43016c1 Ensure header generation zero is written on filesystem create 2023-03-08 12:48:32 -07:00
Jeremy Soller 794e732c80 Merge branch 'fix_root_unlink' into 'master'
Fix bug where root could not remove other users' files

Closes redox#1366

See merge request redox-os/redoxfs!56
2023-03-01 16:28:58 +00:00
4lDO2 73c3d8bb79 Allow root to remove other users' files. 2023-03-01 17:24:37 +01:00
Jeremy Soller 467c9cfe9a Update libc crate 2023-02-11 14:44:07 -07:00
Jeremy Soller 8a1aa3663a Allow removal of temporary directory hardlinks
These will only exist in the middle of rename
2022-12-01 07:32:19 -07:00
Jeremy Soller f601b2a8ce 0.5.3 - make log optional 2022-11-07 18:12:03 -07:00
Jeremy Soller 63bde95462 Update lock file and fix import 2022-11-04 14:05:32 -06:00
Jeremy Soller 82f011363d 0.5.2 2022-11-04 14:04:16 -06:00
Jeremy Soller 898f9d8b2e redoxfs-ar: require disk to exist 2022-11-04 14:03:58 -06:00
Jeremy Soller 9fc80c8bf6 Update cargo.lock 2022-09-05 08:58:06 -06:00
Jeremy Soller 7de07e802e 0.5.1 - add DiskIo type 2022-09-04 20:27:16 -06:00
Jeremy Soller 790ea59269 Increase disk cache to 16 MiB again, it is pre-allocated now 2022-08-22 09:22:09 -06:00
Jeremy Soller 04b4a2e4ad Reduce disk cache size to 8MiB 2022-08-22 09:18:23 -06:00
4lDO2 63468fc414 Replace syscall::clone() with libc::fork() 2022-07-27 16:34:43 +02:00
Jeremy Soller 0ea2fd49dd Update Cargo.lock 2022-07-26 16:14:53 -06:00
Jeremy Soller 0e0ae520c5 Reduce disk cache size to 16 MiB 2022-04-13 09:03:03 -06:00
Jeremy Soller 9215e32254 Implement frename 2022-04-07 19:57:09 -06:00
Jeremy Soller 419bd0f0a6 Fix for some kinds of symbolic links 2022-04-06 18:17:23 -06:00
4lDO2 ebe9579d66 Use crates.io version of syscall, again. 2022-03-27 12:09:50 +02:00
4lDO2 d83e567e97 Update syscall 2022-03-25 15:29:16 +01:00
Jeremy Soller f9f4f7c5f0 Use redox_simple_endian fork 2022-03-11 09:09:11 -07:00
Jeremy Soller 2e46c28097 Merge branch '0.5.0' into 'master'
RedoxFS 0.5.0

See merge request redox-os/redoxfs!54
2022-03-11 16:02:22 +00:00
Jeremy Soller 731b97262b RedoxFS 0.5.0
Significant re-design to use copy-on-write and hashes for all data
2022-03-11 09:01:47 -07:00
Jeremy Soller c98e63af87 Improve speed by not enumerating all disks when opening by UUID 2022-02-11 10:07:40 -07:00
Jeremy Soller cfe5841646 0.4.4 - no_std support 2022-02-05 20:28:11 -07:00
Jeremy Soller 9083dd07c9 Fix warnings and remove dead code 2021-12-26 19:47:44 -07:00
4lDO2 25be3a82bd Update syscall. 2021-06-17 14:38:08 +02:00
Jeremy Soller 6d9a53c956 Update dependencies 2021-04-28 20:54:39 -06:00
Jeremy Soller c6b997f98b Update dependencies 2021-01-19 11:12:59 -07:00
Jeremy Soller 5d4ed0831a 0.4.3 - update redox_syscall 2020-12-16 10:43:30 -07:00
Jeremy Soller f1b88c38c0 Update syscall 2020-08-02 15:34:01 -06:00
Jeremy Soller 0068a6d498 Update dependencies 2020-05-25 07:42:45 -06:00
Jeremy Soller 5ff37f1f77 Update lock file 2020-03-13 20:06:34 -06:00
Jeremy Soller c990fa9a45 0.4.1 - Support rename in fuse 2020-03-13 20:06:01 -06:00
Jeremy Soller 80e3a34645 Merge branch 'format' into 'master'
Format the codebase

See merge request redox-os/redoxfs!49
2020-01-20 22:06:34 +00:00
Xavier L'Heureux 58f2d28ff5 Format the codebase 2020-01-20 15:00:26 -05:00
Jeremy Soller a6af6a9289 Merge branch 'add-atime' into 'master'
Add tracking of access times

See merge request redox-os/redoxfs!48
2020-01-20 16:56:51 +00:00
AdminXVII d2256b3eb3 Add tracking of access times
Add the atime field, which tracks the last access time. Only reads
update the atime.

**BREAKING CHANGE**: One less extent available per block, 4 more bytes
for node names.
2020-01-20 16:56:51 +00:00
Jeremy Soller 3191d7d182 Add argument to locate redoxfs by block 2019-10-28 20:27:53 -06:00
Jeremy Soller bede93cd85 0.3.7 2019-09-08 20:29:55 -06:00
Jeremy Soller e869b3b158 Merge branch 'update_path' into 'master'
Update path after frename

See merge request redox-os/redoxfs!46
2019-08-11 16:06:44 +00:00
Deepak Sirone 924adef1dc Update path after frename 2019-08-11 15:30:09 +05:30
Jeremy Soller 602a63acf6 Return EXDEV to tell kernel to resolve cross-scheme links 2019-07-23 21:05:54 -06:00
Jeremy Soller 43533e8dc7 Merge branch 'jD91mZM2/redoxfs-master' into HEAD 2019-07-20 19:41:48 -06:00
Jeremy Soller bf4d0161be Merge branch 'tests' into 'master'
Tests

See merge request redox-os/redoxfs!45
2019-07-20 17:08:33 +00:00
Jeremy Soller 4578a22297 Tests 2019-07-20 17:08:33 +00:00
Jeremy Soller 5e5656f110 Make mount points not require an extra reference 2019-07-18 21:36:16 -06:00
Jeremy Soller 86b0d612d8 Fix yaml again 2019-07-06 11:39:29 -06:00
Jeremy Soller 61c38afb52 Fix test:redox dependencies 2019-07-06 11:39:08 -06:00
Jeremy Soller 8bbdc558bb Fix yaml 2019-07-06 11:38:35 -06:00
Jeremy Soller 2ddf1dab3f Update CI configuration to use redoxer 2019-07-06 11:37:19 -06:00
Jeremy Soller 8e38015b2f Set libc to 0.2.54 2019-07-06 11:27:48 -06:00
Jeremy Soller 3872f74e9a 0.3.6 - Accept AsRef<Path> for disk path arguments 2019-07-06 08:21:23 -06:00
Jeremy Soller 39e8b8fa7f 0.3.5 - make archive_at public 2019-07-06 08:07:54 -06:00
Jeremy Soller b26195f49a 0.3.4 - expose archiving functionality 2019-07-04 15:46:12 -06:00
Jeremy Soller a70a01bd4a 0.3.3 2019-05-11 12:27:52 -06:00
Jeremy Soller a226d19096 Implement symlinks and fix truncation 2019-05-11 12:26:01 -06:00
Jeremy Soller 7b661e31e3 Remove unused ctime parameter 2019-05-11 11:51:46 -06:00
Jeremy Soller 966105e566 Copy uid, gid, ctime, and mtime 2019-05-11 11:50:19 -06:00
Jeremy Soller 237c156503 Read permissions from original file 2019-05-11 11:46:49 -06:00
Jeremy Soller 946efa2fb5 Add sparse disk file to not require image to be sized initially. 2019-05-08 21:07:21 -06:00
Jeremy Soller 0754019950 Truncate disk image 2019-05-08 20:55:37 -06:00
Jeremy Soller 842744c384 Merge branch 'master' of https://gitlab.redox-os.org/redox-os/redoxfs 2019-05-08 20:35:15 -06:00
Jeremy Soller 2e32481d4c Add redoxfs-ar program to create a redoxfs image from a folder 2019-05-08 20:34:20 -06:00
Jeremy Soller ec072bb718 Return correct error for fevent on files 2019-04-28 13:32:43 -06:00
Jeremy Soller 015d352e33 Fixes for Redox when part of unix target family 2019-04-23 13:55:46 -06:00
Jeremy Soller 219bf26966 Update for new rust 2019-04-07 10:49:56 -06:00
Jeremy Soller ff17fed199 Reimplement fmap/funmap using improvements to kernel 2019-04-06 20:11:51 -06:00
Jeremy Soller cb2bcf9f59 Fix redoxfs fmap using incorrect offset 2019-04-02 20:18:40 -06:00
Jeremy Soller a352d7c79c Merge branch 'add-getuuid' into 'master'
redoxfs: Add '--get-uuid' support

See merge request redox-os/redoxfs!44
2019-03-15 19:21:32 +00:00
Robin Randhawa 37aab3e566 redoxfs: Add '--get-uuid' support
With this, `redoxfs --get-uuid <disk>` prints out the UUID of the
provided redoxfs disk image.

This is needed especially for aarch64 kernels where the UUID of the boot
disk needs to be passed to the kernel by the bootloader. With this
extension to the mount command, the kernel build glue can query the
prepared redoxfs image for it's UUID.
2019-03-15 15:48:25 +00:00
Jeremy Soller b4df131aff Fix disk usage calculation 2019-02-24 20:46:21 -07:00
Jeremy Soller b32a421b97 Correct access control 2018-12-30 09:58:58 -07:00
Jeremy Soller 69d6cc05de Update fmap support 2018-12-30 09:47:33 -07:00
Jeremy Soller 3ae32d9901 Reduce diff - remove dirty flag as it could be added in a separate change 2018-12-08 08:47:28 -07:00
Jeremy Soller d53fc6afff Handle sigterm instead of sigkill 2018-12-08 08:18:21 -07:00
Jeremy Soller 87ecce9e44 Cleanup and use unmount flag inside of Redox scheme processing function 2018-12-08 08:10:23 -07:00
Jeremy Soller 3e1fc9dae5 Merge branch 'master' into liamnprg/redoxfs-master 2018-12-08 07:40:28 -07:00
Liam Naddell f96d330993 wip implementation of unmounting and dirty bits for redoxfs 2018-12-06 11:09:41 -05:00
Jeremy Soller 9c6e0e6789 Update Cargo.lock 2018-10-14 19:56:46 -06:00
jD91mZM2 79cef7c034 Adopt linux' O_APPEND behavior
According to `man open`:

> The file is opened in append mode. Before each write(2), the file offset
is positioned at the end of the file, as if with lseek(2). The modi‐
fication of the file offset and the write operation are performed as a
single atomic step.
2018-10-07 15:22:13 +02:00
Jeremy Soller 6f5ca24838 Merge branch 'master' into 'master'
A little bit more documentation

See merge request redox-os/redoxfs!38
2018-08-01 23:06:00 +00:00
Liam Naddell 6126bfeee3 docs 2018-08-01 18:47:18 -04:00
Jeremy Soller 3ef8557865 Merge branch 'master' into 'master'
Fmaps: Fix bug with syncing

See merge request redox-os/redoxfs!36
2018-07-03 13:23:34 +00:00
Jeremy Soller 3906a90a7b Merge branch 'mggmuggins/ci' into 'master'
Gitlab CI

See merge request redox-os/redoxfs!37
2018-07-02 18:32:30 +00:00
SamwiseFilmore f0450a4564 Gitlab CI 2018-07-02 18:32:30 +00:00
jD91mZM2 53a3f6d7e9 Fmaps: Fix bug with syncing 2018-06-18 20:21:44 +02:00
Jeremy Soller e9a7181e9f Merge branch 'master' into 'master'
fmap support

See merge request redox-os/redoxfs!35
2018-06-18 10:00:21 +00:00
jD91mZM2 8c57fd95e7 Fix bugs & increase page size 2018-06-18 10:33:08 +02:00
jD91mZM2 a1c69a8e82 WIP: fmap support 2018-06-17 18:40:09 +02:00
Jeremy Soller a345132e0f Update links to gitlab 2018-06-12 12:30:45 -06:00
Jeremy Soller 72b0492624 Prevent colons from being used in filenames 2018-05-24 08:43:05 -06:00
Jeremy Soller 78c5e1c6d2 Enter null namespace for redoxfs 2018-01-20 19:37:06 -07:00
Jeremy Soller 40e2a3e221 Implement frename fixing #37 and fix #11 by checking names 2017-12-29 14:08:36 -07:00
Jeremy Soller 2e6e272c72 Add stub for frename 2017-12-27 21:09:05 -07:00
Jeremy Soller f206dab653 Fix error with borrowing packed fields 2017-12-06 20:12:02 -07:00
Jeremy Soller eac37c7dc5 Add fchmod, fchown 2017-11-29 21:05:17 -07:00
Jeremy Soller 38b24ad321 0.3.2 2017-11-19 21:22:44 -07:00
Jeremy Soller 53a1e6d49f Move cache to HashMap 2017-11-19 21:00:07 -07:00
Jeremy Soller 16d6f6b93a Simplify disk cache 2017-11-19 20:51:12 -07:00
Jeremy Soller 2dd4c920c7 0.3.1 - fix issue with new FUSE 2017-11-19 16:22:35 -07:00
Jeremy Soller 1701198b11 0.3.0 2017-11-19 16:18:27 -07:00
Jeremy Soller 47c9acc417 Version 3 - bump block size to 4096 2017-11-19 16:18:06 -07:00
Jeremy Soller 513bfaf805 Set block size programatically 2017-11-19 16:15:01 -07:00
Jeremy Soller a0ec667025 Update lock format 2017-11-19 16:04:16 -07:00
Jeremy Soller ab690ce519 Update Cargo.lock 2017-10-11 20:54:45 -06:00
Jeremy Soller d4ea343a39 Fix disk scheme path 2017-09-30 20:34:34 -06:00
Jeremy Soller e74975d2f9 Add UUID matching 2017-09-30 20:24:59 -06:00
Jeremy Soller db22937361 Implement UUID 2017-09-30 15:07:46 -06:00
Jeremy Soller 6d62d22262 Fix offset calculations 2017-09-27 21:15:27 -06:00
Jeremy Soller cbdfba7b94 Allow a bootloader to be written during redoxfs-mkfs 2017-09-27 20:27:38 -06:00
Jeremy Soller 90dc177d75 Add docs.rs badge 2017-09-26 19:57:06 -06:00
Jeremy Soller ade6234f12 Update spin 2017-09-26 19:56:14 -06:00
Jeremy Soller dc0b0c5c2d Fix compilation on Redox 2017-09-26 19:55:10 -06:00
Jeremy Soller dfd2dd8ae1 Cleanup fuse backend, send signal at correct time 2017-09-26 19:36:27 -06:00
Jeremy Soller 260bc1e1e8 Fix cargo doc 2017-09-26 19:17:49 -06:00
Jeremy Soller 541a58181a Update FUSE 2017-09-25 21:50:07 -06:00
Jeremy Soller 070aeb54ef Cleanup to allow use of most functions when linking the library, including mounting and creating filesystems 2017-09-25 19:11:02 -06:00
Jeremy Soller b272cc2d19 Use nightly friendly spin 2017-09-18 22:01:58 -06:00
Jeremy Soller 9c907a22dd Merge pull request #34 from ids1024/import
Remove unused import causing lint
2017-09-11 12:50:22 -06:00
Ian Douglas Scott a9da8e7a57 Remove unused import causing lint 2017-09-11 10:22:27 -07:00
Jeremy Soller 8f41e5a777 Update Cargo.lock 2017-08-19 14:46:42 -06:00
Jeremy Soller 213b4aea5f Merge pull request #33 from ids1024/utimens
futimens: do not require O_RWONLY/O_RDWR
2017-08-14 17:59:00 -06:00
Ian Douglas Scott dc5bd70728 futimens: do not require O_RWONLY/O_RDWR 2017-08-14 16:35:03 -07:00
Jeremy Soller fc0af50b97 Merge pull request #32 from ids1024/directoryperm
Make it not an error to open a directory without O_DIRECTORY or O_STAT
2017-08-12 13:29:09 -06:00
Ian Douglas Scott f4a3e1dde0 Make read() on directory not opened as O_DIRECTORY return EISDIR 2017-08-12 12:25:36 -07:00
Ian Douglas Scott f474a9fd77 Actually, O_RDONLY should work with a directory 2017-08-12 11:32:39 -07:00
Ian Douglas Scott 93f7ec7a55 Make it not an error to open a directory without O_DIRECTORY or O_STAT
POSIX says this should work, and some software seems to rely on that.
2017-08-11 18:32:49 -07:00
Jeremy Soller 801cbd9072 Merge pull request #31 from ids1024/mut
Remove unneeded uses of 'mut'
2017-08-11 16:21:43 -06:00
Ian Douglas Scott 4f3b097a4a Remove unneeded uses of 'mut'
This seems to be a warning with the latest Rust, and an error due to #![deny(warnings)]
2017-08-11 14:55:19 -07:00
Jeremy Soller a830383220 Update Cargo.lock 2017-08-02 19:52:30 -06:00
Jeremy Soller e270ce117f Merge pull request #30 from ids1024/free
Avoid corrupting free node
2017-08-02 07:27:10 -06:00
Ian Douglas Scott 0996d844c4 Avoid corrupting free node 2017-08-01 20:27:43 -07:00
Jeremy Soller 1e9cec6092 Typo 2017-08-01 18:26:58 -06:00
Jeremy Soller e95be4caf3 Dealocate when node size changes 2017-08-01 18:23:45 -06:00
Jeremy Soller 3b78720167 Update Cargo.lock 2017-07-29 08:25:06 -06:00
Jeremy Soller b04efe081b Merge pull request #29 from ids1024/dirlink
Directory symlinks
2017-07-26 19:05:59 -06:00
Ian Douglas Scott 03f59a419b Make directory symlinks work correctly 2017-07-26 16:17:49 -07:00
Ian Douglas Scott ca1a1d615a Move symlink resolving to another function 2017-07-26 16:17:48 -07:00
Jeremy Soller c6c9399305 Merge branch 'master' of https://github.com/redox-os/redoxfs 2017-07-26 07:46:49 -06:00
Jeremy Soller 862e9b0a37 Add Cargo.lock 2017-07-26 07:46:45 -06:00
Jeremy Soller 154b456d99 Merge pull request #28 from ids1024/mtimefuse
fuse: allow setting mtime to earlier time
2017-07-25 17:32:05 -06:00
Ian Douglas Scott 5a1aaadd05 fuse: allow setting mtime to earlier time 2017-07-25 15:17:15 -07:00
Jeremy Soller 8737f19459 Merge pull request #27 from ids1024/readdir
fuse: Fix readdir when directory does not fit in buffer
2017-07-25 12:17:46 -06:00
Ian Douglas Scott 92d38e32c1 fuse: Fix readdir when directory does not fit in buffer 2017-07-25 08:59:35 -07:00
Jeremy Soller e635648ed7 Use EINVAL instead of ENOENT 2017-07-22 13:14:31 -06:00
Jeremy Soller f40f6921e8 Return error if dup buf is not empty 2017-07-22 13:08:08 -06:00
Jeremy Soller 3342778968 Merge pull request #26 from ids1024/unlinkuid
Require same uid as owner to unlink, not write permission
2017-07-16 18:52:21 -06:00
Ian Douglas Scott ad4826eb70 Require same uid as owner to unlink, not write permission 2017-07-16 15:43:59 -07:00
Jeremy Soller 7fc1f16a67 Merge pull request #25 from ids1024/o_creat
Do not fail due to file permissions on creation
2017-07-15 08:08:43 -06:00
Ian Douglas Scott cae1402f5a Do not fail due to file permissions on creation
Matches Posix.
2017-07-15 07:01:21 -07:00
Jeremy Soller bc2e8c2923 Merge pull request #24 from ids1024/fcntl
Make fcntl not clobber access mode
2017-07-12 14:41:33 -06:00
Ian Douglas Scott 8c03929973 Make fcntl not clobber access mode 2017-07-12 13:39:26 -07:00
Jeremy Soller 6965d67efd Remove unused import 2017-07-09 18:51:11 -06:00
Jeremy Soller 1549d1af8b Use second argument as mtime 2017-07-09 18:07:00 -06:00
Jeremy Soller 8d66d9ce54 Add futimens 2017-07-09 15:54:03 -06:00
Jeremy Soller 1f97d8220c Add times to stat 2017-07-09 10:25:26 -06:00
Jeremy Soller 20bce53b7a Fix build on Redox 2017-07-09 10:21:20 -06:00
Jeremy Soller 9471a2e410 WIP: Add mtime/ctime to Redox 2017-07-09 10:15:41 -06:00
Jeremy Soller dcce0d9e6e Add associated consts 2017-07-08 19:24:34 -06:00
Jeremy Soller 4820210cca Merge pull request #22 from ids1024/O_NOFOLLOW
Follow symlinks on O_STAT, unless O_NOFOLLOW is passed
2017-07-04 08:17:11 -06:00
Ian Douglas Scott 0d12413297 Follow symlinks on O_STAT, unless O_NOFOLLOW is passed 2017-07-03 19:23:23 -07:00
Jeremy Soller 2c770ca997 Merge pull request #21 from ids1024/unlink
Support unlink() on symlink
2017-07-03 05:25:47 -06:00
Ian Douglas Scott 3e0ce05e23 Support unlink() on symlink 2017-07-02 19:16:14 -07:00
Jeremy Soller 0032159492 Merge pull request #20 from ids1024/fuse
Symlinks in fuse
2017-06-30 12:36:19 -06:00
Ian Douglas Scott f582c60f34 Symlinks in fuse 2017-06-30 11:24:53 -07:00
Jeremy Soller 97652cc6f8 Merge pull request #19 from ids1024/symlink
Return EINVAL for O_SYMLINK on non-symlink
2017-06-29 16:52:12 -06:00
Ian Douglas Scott dffa85f874 Return EINVAL for O_SYMLINK on non-symlink 2017-06-29 14:55:49 -07:00
Jeremy Soller b972f08f50 Merge pull request #18 from ids1024/symlink
[WIP] Symlinks
2017-06-29 15:46:52 -06:00
Ian Douglas Scott 0d76e1441b Relative symlink 2017-06-29 14:08:53 -07:00
Ian Douglas Scott 7a4a41a34e Work in progress symlink implementation 2017-06-26 09:14:12 -07:00
Jeremy Soller 7470cc7846 Merge pull request #17 from ids1024/seek
Allow seek beyond end of file.
2017-06-02 20:23:04 -06:00
Ian Douglas Scott 86353cdff5 Allow seek beyond end of file.
This is enough to fix as. I don't know if it correctly fills the space
with zeros when writing beyond the current EOF.
2017-06-02 19:00:54 -07:00
Jeremy Soller 34f9b3f6df Merge pull request #16 from ids1024/try
Use ? instead of try!
2017-05-29 22:07:21 +02:00
Ian Douglas Scott 7d2663e6a2 Also initialize Stat to zero in implementation of Resource
I didn't notice there were two copies of this code
2017-05-28 23:26:57 -07:00
Ian Douglas Scott 1f90f21147 Use ? instead of try! 2017-05-28 23:06:45 -07:00
Jeremy Soller 05e3802844 Merge pull request #15 from ids1024/stat
Initialize fields of Stat struct to 0
2017-05-28 18:50:11 +02:00
Ian Douglas Scott 31cfde4eaf Use Default::default() 2017-05-28 09:17:36 -07:00
Ian Douglas Scott 138525cf7d Initialize fields of Stat struct to 0
Prevents potential undefined behavior based on how the call is made. Of
course, eventually these fields should actually be set.
2017-05-28 09:10:52 -07:00
Jeremy Soller 6c1d98cea8 Merge pull request #14 from bungcip/travis_macos_error
fix travis macos build
2017-04-26 17:15:15 -06:00
Gigih Aji Ibrahim 6642d36079 trying to fix travis macos build fail. don't know if this work or not. I don't have mac to test it :) 2017-04-27 05:32:20 +07:00
Jeremy Soller f0c38fe609 Perform cloexec in kernel 2017-04-16 12:52:07 -06:00
Jeremy Soller f418d30dc9 Add O_APPEND functionality 2017-04-09 22:09:29 -06:00
Jeremy Soller d0dfef2f0b Fix bug in seek 2017-04-09 22:00:26 -06:00
Jeremy Soller 3db0c2a4b1 Fix handling of missing files 2017-03-15 19:42:11 -06:00
Jeremy Soller aff13c0d66 Merge pull request #8 from xTibor/formatting_and_docs_fixes
Small formatting and documentation fixes
2017-02-13 19:47:33 -07:00
xTibor 5dc6c48623 Small formatting and documentation fixes 2017-02-03 00:04:32 +01:00
Jeremy Soller fa6c19f79b Add inode number to redoxfs 2017-01-30 20:44:09 -07:00
Jeremy Soller 665120f063 Better error messages 2017-01-13 10:44:11 -07:00
Jeremy Soller 3f0bda4e14 Use crates.io for dependencies 2017-01-13 10:28:44 -07:00
Jeremy Soller 5ab8c050d8 Fix for build on OS X 2016-12-13 18:58:59 -07:00
Jeremy Soller d0c698a031 Merge pull request #7 from redox-os/merge_fuse_scheme
Merge fuse and scheme, add mkfs utility, remove old utility
2016-12-13 18:05:24 -07:00
Jeremy Soller 9c2864e130 Merge fuse and scheme, add mkfs utility, remove old utility 2016-12-13 18:05:03 -07:00
Jeremy Soller f1073d0c48 Add leading / to path 2016-11-29 18:13:56 -07:00
Jeremy Soller b526a29e35 Enable caching again 2016-11-28 14:31:00 -07:00
Jeremy Soller 25fe6d3826 Implement O_DIRECTORY 2016-11-25 18:24:15 -07:00
Jeremy Soller dcaf5c76be Add statvfs 2016-11-18 08:42:48 -07:00
Jeremy Soller 31605167ce Add chmod 2016-11-15 17:07:37 -07:00
Jeremy Soller 8a22d96669 Minimal O_CLOEXEC implementation 2016-11-15 16:11:43 -07:00
Jeremy Soller 8449c5b6ad Switch to using fork 2016-11-10 20:01:30 -07:00
Jeremy Soller d058e56d6e Fix access modes 2016-11-08 10:52:27 -07:00
Jeremy Soller 81c39551d7 Update .travis.yml 2016-11-06 18:37:52 -07:00
Jeremy Soller fd4d674422 Merge pull request #4 from genodeftest/patch-3
Travis: fix config (on Linux)
2016-11-05 09:55:17 -06:00
Christian Stadelmann a313852428 Travis: fix config (on Linux)
With this change, travis will work on Linux at least.
2016-11-05 16:46:34 +01:00
Jeremy Soller b25b268756 Fix syscall crate name 2016-11-02 19:46:34 -06:00
Jeremy Soller a0b381765b Use defer permissions instead 2016-11-01 15:33:49 -06:00
Jeremy Soller 3a58139e57 Only add allow_root on OS X 2016-11-01 15:08:08 -06:00
Jeremy Soller 14cbb4f4f8 Merge pull request #3 from Yoric/master
Let root manipulate a fuse-mounted redoxfs vfs.
2016-11-01 14:29:30 -06:00
David Teller 0353bfc00c Let root manipulate a fuse-mounted redoxfs vfs.
This (along with a few Makefile changes) solves my issues building on macOS.
2016-11-01 21:21:20 +01:00
Jeremy Soller a5b6dcc0c0 Disable cache to improve performance 2016-10-31 21:56:35 -06:00
Jeremy Soller 4ecb62da40 Build redoxfs using syscall git 2016-10-31 13:27:35 -06:00
Jeremy Soller 5c0fc80db2 Merge pull request #2 from redox-os/new_kernel
New kernel
2016-10-26 13:21:48 -06:00
Jeremy Soller ec00f58d73 Add buf to dup 2016-10-25 12:16:28 -06:00
Jeremy Soller 3dcaad55fe Implement unix permissions 2016-10-05 18:00:46 -06:00
Jeremy Soller c06edb232b Lookup uid, gid, and mode from block for stat 2016-10-05 14:07:31 -06:00
Jeremy Soller 33f218aadb WIP: Permissions support 2016-10-05 12:13:45 -06:00
Jeremy Soller 1488d1ef56 64-bit stat size 2016-09-28 20:41:45 -06:00
Jeremy Soller 36b3cf04a9 Add name to fpath 2016-09-28 10:27:32 -06:00
Jeremy Soller 9c16bca2a5 Fix warning 2016-09-28 08:58:51 -06:00
Jeremy Soller 9bd5d69776 Compatibility with new kernel 2016-09-27 21:55:57 -06:00
Jeremy Soller 11f3485173 Port to new kernel 2016-09-27 21:19:28 -06:00
Jeremy Soller b7608c1530 Merge pull request #1 from redox-os/new_abi
Switch to new ABI
2016-08-10 19:18:32 -06:00
Jeremy Soller 35ed814e68 WIP: Switch to new ABI 2016-08-08 21:31:12 -06:00
Jeremy Soller fe0a5de2d4 Decrease size to increase install speed 2016-07-31 12:56:56 -06:00
Jeremy Soller ac8cee662e Increase header search range to 4 MB 2016-07-31 06:53:44 -06:00
Jeremy Soller dc575b790f Use Duration sleep 2016-07-30 18:01:16 -06:00
Jeremy Soller fb2eb845ba Increase size of image 2016-07-28 16:55:36 -06:00
Jeremy Soller e8d92bee0f Remove print 2016-06-21 11:14:42 -06:00
Jeremy Soller dd4a90119f Fixes for node_ensure_len 2016-06-21 11:13:59 -06:00
Jeremy Soller 55e6b13555 Alignment of write 2016-06-21 11:01:45 -06:00
Jeremy Soller d9bdd6e3dd Comment out fstat 2016-06-10 20:20:47 -06:00
Jeremy Soller ac07b358ad Support O_TRUNC in open 2016-06-09 20:24:10 -06:00
Jeremy Soller 896812c68a Sync size when truncate occurs 2016-06-09 20:23:30 -06:00
Jeremy Soller 4316326792 Remove debug for 'Creating cache' 2016-06-08 21:08:21 -06:00
Jeremy Soller b6d49e2fa1 Reenable dup, disable debugging 2016-06-07 17:30:33 -06:00
Jeremy Soller 195271779d old_id vs id 2016-06-06 18:24:29 -06:00
Jeremy Soller 7e80cba121 Add dup 2016-06-06 17:29:57 -06:00
Jeremy Soller 32c61e93e0 C stat support 2016-06-03 23:23:40 -06:00
Jeremy Soller d17b55089a More efficient reads 2016-05-16 09:49:05 -06:00
Jeremy Soller cf728be7ae Remove debugging on filesystem open 2016-05-09 20:15:01 -06:00
Jeremy Soller 3841521174 Deny warnings 2016-04-29 17:38:56 -06:00
Jeremy Soller 0bb53636e4 Faster read from disk if cache miss 2016-04-19 20:14:31 -06:00
Jeremy Soller dc5326a13a FUSE truncation 2016-04-16 15:57:11 -06:00
Jeremy Soller 7d48fcf382 WIP: Add truncate 2016-04-16 13:33:21 -06:00
Jeremy Soller fb55c69fe5 Add stat 2016-04-15 08:35:33 -06:00
Jeremy Soller e97f126f35 Fixes for dirs and files so they do not have to read all contents at once 2016-04-14 21:06:12 -06:00
Jeremy Soller f509c4b12c Caching! 2016-04-14 17:29:08 -06:00
Jeremy Soller e355f755ee Comment out prints 2016-04-13 15:34:06 -06:00
Jeremy Soller 94c36fa34d Fixes to fuse, allow filesystem offset 2016-04-13 15:23:52 -06:00
Jeremy Soller d13d926d7b Fixes for fuse 2016-04-12 20:56:38 -06:00
Jeremy Soller 8d806568b9 Turn off per-packet messages 2016-04-12 20:51:04 -06:00
Jeremy Soller bef665af8d Add debugging to scheme, add padding to header 2016-04-12 14:07:49 -06:00
Jeremy Soller 9cf1d60253 Add df support 2016-03-09 14:43:44 -07:00
Jeremy Soller dcc67e4cf4 Fixes for redox scheme 2016-03-09 14:12:17 -07:00
Jeremy Soller 77848132ea Fuse fixes, file read/write implemented 2016-03-09 14:10:56 -07:00
Jeremy Soller 73528fa872 Add read file support 2016-03-09 12:48:16 -07:00
Jeremy Soller a1cae73566 FUSE support for RedoxFS (WIP) 2016-03-09 12:12:46 -07:00
Jeremy Soller b1805779a8 Write can realloc 2016-03-03 17:18:41 -07:00
Jeremy Soller ff0106b212 Support for less-than-sector block allocations 2016-03-03 15:01:36 -07:00
Jeremy Soller 51e037150c Merge branch 'master' of https://github.com/redox-os/redoxfs 2016-03-02 11:42:12 -07:00
Jeremy Soller 856db3e21d WIP: Collection 2016-03-02 11:41:59 -07:00
Jeremy Soller 1a58014da9 Add more attributes 2016-03-01 21:08:21 -07:00
Jeremy Soller 6d5be41c13 Add truncate 2016-03-01 20:09:47 -07:00
Jeremy Soller 2d578215e3 rmdir, mkdir 2016-03-01 19:26:40 -07:00
Jeremy Soller 25174cc4ce WIP: Functions for accessing file data 2016-02-29 21:32:37 -07:00
Jeremy Soller 7412bdd6b7 WIP: Read/write 2016-02-29 20:02:54 -07:00
Jeremy Soller d2a073788a Add unlink and creat 2016-02-27 10:26:53 -07:00
Jeremy Soller 702bef4418 Path handling 2016-02-26 20:22:41 -07:00
Jeremy Soller 799a847ffa WIP: Scheme 2016-02-26 19:30:49 -07:00
Jeremy Soller 670d874d07 Use unix errors 2016-02-26 18:51:29 -07:00
Jeremy Soller 7f8776fafb Do not allow rmdir when node has children 2016-02-26 15:14:16 -07:00
Jeremy Soller 664801337d Add to free node list, fix insertion bug 2016-02-26 14:59:00 -07:00
Jeremy Soller ecfd43fdbd Fix remove truncation 2016-02-26 14:24:03 -07:00
Jeremy Soller 7a4d78149a WIP: Remove 2016-02-26 14:18:29 -07:00
Jeremy Soller 1eff7c7e37 WIP: Simple way to fill file with data 2016-02-26 10:53:05 -07:00
Jeremy Soller 39707bcf60 Add signature as constant 2016-02-25 16:33:12 -07:00
Jeremy Soller 1f08b76531 Add cd, Use invalid version until stable 2016-02-25 16:31:28 -07:00
Jeremy Soller e0fceebffd Allow a parent node to be given to the primitive functions 2016-02-25 14:25:14 -07:00
Jeremy Soller 4a444a3423 Add find function 2016-02-25 12:29:55 -07:00
Jackpot51 6450660c50 Create DESIGN.md 2016-02-25 12:29:08 -07:00
Jeremy Soller 556c842ec7 Add exnode 2016-02-24 21:59:57 -07:00
Jeremy Soller d51e891f22 Add to root node on mkdir, touch 2016-02-24 21:23:46 -07:00
Jeremy Soller 41e716219d Very basic allocation 2016-02-24 18:47:47 -07:00
Jeremy Soller bae24a54b0 Improve debugging and image creation 2016-02-24 18:23:25 -07:00
Jeremy Soller 8ff4863bbb Cleanup, add node and header commands 2016-02-24 17:30:37 -07:00
Jeremy Soller d116ce57da Add simple shell 2016-02-24 16:04:30 -07:00
Jeremy Soller c231ddb219 Remove cat todo 2016-02-24 15:06:35 -07:00
Jeremy Soller 49925a39d0 Error type, subdir argument 2016-02-24 14:59:14 -07:00
Jeremy Soller bc2d90f531 Allow read of filesystem names 2016-02-24 14:02:37 -07:00
1165 changed files with 13602 additions and 99174 deletions
-7
View File
@@ -1,7 +0,0 @@
[**.c]
indent_size = 4
indent_style = space
[**.yml]
indent_size = 4
indent_style = space
+4 -11
View File
@@ -1,11 +1,4 @@
.idea/ target
prefix/ image.bin
sysroot/ image
**/target/ image*
.gdb_history
*.patch
*.swp
*.swo
/.vim
.vscode/
+13 -69
View File
@@ -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
-7
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+84 -135
View File
@@ -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" }
+2 -2
View File
@@ -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
+29 -222
View File
@@ -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
+32 -152
View File
@@ -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 [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
sudo dnf install expect [![crates.io](http://meritbadge.herokuapp.com/redoxfs)](https://crates.io/crates/redoxfs)
``` [![docs.rs](https://docs.rs/redoxfs/badge.svg)](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)
-30
View File
@@ -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");
}
-25
View File
@@ -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"
-170
View File
@@ -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
-94
View File
@@ -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
Submodule dlmalloc-rs deleted from 3b0ee3b467
-27
View File
@@ -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;
};
-3
View File
@@ -1,3 +0,0 @@
#!/usr/bin/env bash
cargo fmt --package relibc --package crt0 --package redox-rt "$@"
+4
View File
@@ -0,0 +1,4 @@
target
corpus
artifacts
coverage
+858
View File
@@ -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",
]
+30
View File
@@ -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
+338
View File
@@ -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
});
-7
View File
@@ -1,7 +0,0 @@
[package]
name = "generic-rt"
version = "0.1.0"
edition = "2024"
[lints]
workspace = true
-99
View File
@@ -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()?) }
}
}
-6
View File
@@ -1,6 +0,0 @@
#ifndef _ALLOCA_H
#define _ALLOCA_H
#define alloca(size) __builtin_alloca (size)
#endif /* _ALLOCA_H */
-1
View File
@@ -1 +0,0 @@
#include <openlibm_complex.h>
-112
View File
@@ -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
-3
View File
@@ -1,3 +0,0 @@
#include <openlibm_fenv.h>
#undef complex
#undef I
-22
View File
@@ -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
-17
View File
@@ -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
View File
@@ -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
View File
@@ -1 +0,0 @@
#include <string.h>
-10
View File
@@ -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
-6
View File
@@ -1,6 +0,0 @@
#ifndef _RELIBC_PATHS_H
#define _RELIBC_PATHS_H
#define _PATH_BSHELL "/bin/sh"
#endif
-35
View File
@@ -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 */
-243
View File
@@ -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 */
-18
View File
@@ -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 */
-370
View File
@@ -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 */
-22
View File
@@ -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 */
-19
View File
@@ -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
-36
View File
@@ -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
View File
@@ -1 +0,0 @@
#include <poll.h>
-846
View File
@@ -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_ */
-22
View File
@@ -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
-16
View File
@@ -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
-13
View File
@@ -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
View File
@@ -1 +0,0 @@
#include <sys/syslog.h>
-12
View File
@@ -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
-136
View File
@@ -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)
}
Submodule openlibm deleted from f5ed774593
-16
View File
@@ -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
-347
View File
@@ -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,
}
}
-168
View File
@@ -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 _;
}
)*
}
};
}
-11
View File
@@ -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;
-27
View File
@@ -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
-492
View File
@@ -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];
-418
View File
@@ -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;
-28
View File
@@ -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,
}
-661
View File
@@ -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];
-535
View File
@@ -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);
-366
View File
@@ -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()
}
-1285
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-68
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
-76
View File
@@ -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!()
}
-58
View File
@@ -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"
-3
View File
@@ -1,3 +0,0 @@
[toolchain]
channel = "nightly-2026-05-24"
components = ["rust-src"]
-21
View File
@@ -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
+394
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
};
}
+153
View File
@@ -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
View File
@@ -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);
}
}
}
+410
View File
@@ -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);
}
}
+206
View File
@@ -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
View File
@@ -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());
}
-36
View File
@@ -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!"),
}
}
}
-13
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
}
+90
View File
@@ -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)
})
}
-12
View File
@@ -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
-105
View File
@@ -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)
}
-8
View File
@@ -1,8 +0,0 @@
[package]
name = "crti"
version = "0.1.0"
authors = ["jD91mZM2 <me@krake.one>"]
edition = "2024"
[lints]
workspace = true
-3
View File
@@ -1,3 +0,0 @@
#![no_std]
// we don't support _init/_fini functions, only init/fini arrays
-8
View File
@@ -1,8 +0,0 @@
[package]
name = "crtn"
version = "0.1.0"
authors = ["jD91mZM2 <me@krake.one>"]
edition = "2024"
[lints]
workspace = true
-3
View File
@@ -1,3 +0,0 @@
#![no_std]
// we don't support _init/_fini functions, only init/fini arrays
-103
View File
@@ -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
}
-51
View File
@@ -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
View File
@@ -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}"));
}
}
}
+110
View File
@@ -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()
}
}
+86
View File
@@ -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 }
}
}
+38
View 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)
}
}
+42
View File
@@ -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)
}
}
+41
View File
@@ -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>;
}
+53
View File
@@ -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)
}
}
-90
View File
@@ -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()
}
}
}
}
+331
View File
@@ -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
}
}
}
-156
View File
@@ -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