├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .github ├── pull_request_template.md └── workflows │ ├── lind-selfhost.yml │ └── rust.yml ├── .gitignore ├── .rustfmt.toml ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── benches ├── fs_open_close.rs ├── fs_read_write.rs ├── fs_read_write_seek.rs ├── gen_getid.rs └── global_criterion_settings.rs ├── docs ├── RustPOSIX-README.jpg └── SafePOSIX Rust Diagram.jpg ├── gen_netdevs.c ├── gen_netdevs.sh └── src ├── interface ├── comm.rs ├── errnos.rs ├── file.rs ├── misc.rs ├── mod.rs ├── pipe.rs ├── timer.rs └── types.rs ├── lib.rs ├── lib_fs_utils.rs ├── main.rs ├── safeposix ├── cage.rs ├── dispatcher.rs ├── filesystem.rs ├── mod.rs ├── net.rs ├── shm.rs └── syscalls │ ├── fs_calls.rs │ ├── fs_constants.rs │ ├── mod.rs │ ├── net_calls.rs │ ├── net_constants.rs │ ├── sys_calls.rs │ └── sys_constants.rs ├── tests ├── fs_tests.rs ├── ipc_tests.rs ├── mod.rs ├── networking_tests.rs └── sys_tests.rs └── tools ├── fs_utils.rs ├── interface ├── lib_fs_utils.rs └── safeposix /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # Use an official Rust image as a parent image 2 | FROM ubuntu:latest 3 | ARG DEBIAN_FRONTEND=noninteractive 4 | ARG arch=x86_64 5 | 6 | # Install tools and dependencies for building Rust projects 7 | RUN apt-get update && apt-get install -y \ 8 | build-essential \ 9 | pkg-config \ 10 | curl \ 11 | libssl-dev \ 12 | git 13 | # Install Rust from source 14 | RUN curl https://sh.rustup.rs -sSf | sh -s -- -y 15 | # Add cargo bin to PATH 16 | ENV PATH="/root/.cargo/bin:${PATH}" 17 | 18 | # Install the nightly Rust toolchain 19 | RUN rustup toolchain install nightly 20 | 21 | # Set the default toolchain to nightly 22 | RUN rustup default nightly 23 | 24 | # Verify installation 25 | RUN rustc --version 26 | 27 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Rust (Unstable)", 3 | "build": { 4 | "dockerfile": "Dockerfile" 5 | }, 6 | "customizations": { 7 | "vscode": { 8 | "settings": { 9 | "terminal.integrated.shell.linux": "/bin/bash" 10 | }, 11 | "extensions": [ 12 | "rust-lang.rust" 13 | ] 14 | } 15 | }, 16 | "forwardPorts": [], 17 | "postCreateCommand": "cargo check", 18 | "remoteUser": "root" 19 | } 20 | 21 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Fixes # (issue) 4 | 5 | 6 | 7 | 8 | 9 | ### Type of change 10 | 11 | 12 | 13 | - [ ] Bug fix (non-breaking change which fixes an issue) 14 | - [ ] New feature (non-breaking change which adds functionality) 15 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 16 | - [ ] This change requires a documentation update 17 | 18 | ## How Has This Been Tested? 19 | 20 | 21 | 22 | 23 | 24 | - Test A - `lind_project/tests/test_cases/test_a.c` 25 | - Test B - `lind_project/tests/test_cases/test_b.c` 26 | 27 | ## Checklist: 28 | 29 | 30 | 31 | - [ ] My code follows the style guidelines of this project 32 | - [ ] I have commented my code, particularly in hard-to-understand areas 33 | - [ ] My changes generate no new warnings 34 | - [ ] I have added tests that prove my fix is effective or that my feature works 35 | - [ ] Any dependent changes have been added to a pull request and/or merged in other modules (native-client, lind-glibc, lind-project) 36 | -------------------------------------------------------------------------------- /.github/workflows/lind-selfhost.yml: -------------------------------------------------------------------------------- 1 | name: RustPOSIX Build 2 | 3 | # Controls when the workflow will run 4 | on: 5 | push: 6 | branches: 7 | - develop 8 | - main 9 | pull_request: 10 | branches: 11 | - develop 12 | - main 13 | # Triggers the workflow on push or pull request events but only for the develop branch 14 | # Allows you to run this workflow manually from the Actions tab 15 | workflow_dispatch: 16 | 17 | jobs: 18 | build: 19 | runs-on: self-hosted 20 | if: github.event.pull_request.draft == false 21 | steps: 22 | - name: Echo out branch values 23 | run: | 24 | echo github.base_ref: ${{ github.base_ref }} 25 | echo github.head_ref: ${{ github.head_ref }} 26 | echo github.ref: ${{ github.ref }} 27 | cd /home/lind/lind_project/ 28 | 29 | - name: Checkout lind-project 30 | run: | 31 | git --git-dir /home/lind/lind_project/.git remote update origin --prune; 32 | git --git-dir /home/lind/lind_project/.git checkout --force remotes/origin/develop; 33 | 34 | - name: In the land of RUSTPOSIX where the shadows lie (PR request) 35 | if: github.head_ref != '' 36 | run: | 37 | git --git-dir /home/lind/lind_project/src/safeposix-rust/.git remote update origin --prune; 38 | git --git-dir /home/lind/lind_project/src/safeposix-rust/.git checkout remotes/origin/${{ github.head_ref }}; 39 | make rustposix; 40 | 41 | - name: In the land of RUSTPOSIX where the shadows lie (Develop/Push) 42 | if: github.head_ref == '' 43 | run: | 44 | git --git-dir /home/lind/lind_project/src/safeposix-rust/.git remote update origin --prune; 45 | git --git-dir /home/lind/lind_project/src/safeposix-rust/.git checkout remotes/origin/develop; 46 | make rustposix; 47 | 48 | - name: One NACL to rule them all 49 | run: | 50 | git --git-dir /home/lind/lind_project/src/native_client/.git remote update origin --prune; 51 | git --git-dir /home/lind/lind_project/src/native_client/.git checkout remotes/origin/develop; 52 | make nacl; 53 | 54 | - name: One ring to INSTALL them all 55 | run: | 56 | make install; 57 | 58 | - name: And in darkness TEST them 59 | run: | 60 | make test-verbose; 61 | 62 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "develop" ] 6 | pull_request: 7 | branches: [ "develop" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | 14 | build_and_test: 15 | name: Rust project - latest 16 | runs-on: ubuntu-latest 17 | strategy: 18 | matrix: 19 | toolchain: 20 | - nightly 21 | steps: 22 | - uses: actions/checkout@v4 23 | - run: ./gen_netdevs.sh 24 | - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} 25 | - run: cargo build --verbose 26 | - run: cargo test --lib --verbose 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | 13 | # Added by cargo 14 | 15 | /target 16 | 17 | **/net_devices 18 | 19 | linddata* 20 | gen_netdevs 21 | lind.metadata 22 | lind.md.log 23 | test_file.txt 24 | 25 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | ignore = [ 2 | "src/safeposix/syscalls/net_constants.rs", 3 | "src/safeposix/syscalls/sys_constants.rs", 4 | "src/safeposix/syscalls/fs_constants.rs", 5 | ] 6 | 7 | wrap_comments = true 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustposix" 3 | version = "0.1.0" 4 | authors = ["Nicholas Smith Renner ", "Jonathan Eli Singer ", "Tristan J. Brigham "] 5 | edition = "2018" 6 | 7 | [lib] 8 | path = "src/lib.rs" 9 | # cdylib is a dynamically linkable library, which is great for linking into 10 | # C programs and similar. rlib is needed for the criterion benchmarking libary 11 | # and creates one of Rust's static libraries. We are currently not generating 12 | # dylib files which are Rust's internal (non-stable) ABI. 13 | # Source: https://users.rust-lang.org/t/what-is-the-difference-between-dylib-and-cdylib/28847/3 14 | crate-type = ["cdylib","rlib"] 15 | test = true 16 | 17 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 18 | 19 | [dependencies] 20 | serde = { version = "1.0", features = ["derive", "rc"] } 21 | serde_cbor = "0.10" 22 | libc = "0.2" 23 | ringbuf = "0.2.6" 24 | dashmap = { version = "5.1", features=["serde"] } 25 | parking_lot = "0.12" 26 | rand = "0.8.4" 27 | lazy_static = "1.4.0" 28 | 29 | [dev-dependencies] 30 | criterion = { version = "0.3", features = ["html_reports"]} 31 | tempfile = "3.2.0" 32 | grcov="0.8.19" # code coverage 33 | 34 | [[bin]] 35 | name = "lind_fs_utils" 36 | path = "src/tools/fs_utils.rs" 37 | 38 | # many benchmarks follow. Don't put any non-benchmarks below this... 39 | [[bench]] 40 | name = "gen_getid" 41 | path = "benches/gen_getid.rs" 42 | harness= false 43 | 44 | [[bench]] 45 | name = "fs_open_close" 46 | path = "benches/fs_open_close.rs" 47 | harness= false 48 | 49 | [[bench]] 50 | name = "fs_read_write" 51 | path = "benches/fs_read_write.rs" 52 | harness= false 53 | 54 | [[bench]] 55 | name = "fs_read_write_seek" 56 | path = "benches/fs_read_write_seek.rs" 57 | harness= false 58 | 59 | 60 | # Don't put any thing below this... benchmarks above! 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | 177 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test clean 2 | 3 | test: clean 4 | chmod +x gen_netdevs.sh 5 | ./gen_netdevs.sh 6 | cargo test --lib 7 | 8 | clean: 9 | rm -f gen_netdevs net_devices 10 | rm -f linddata.* 11 | rm -f lind.metadata 12 | 13 | format: 14 | cargo fmt -- --check 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RustPOSIX [![Build Status](https://github.com/Lind-Project/safeposix-rust/actions/workflows/lind-selfhost.yml/badge.svg?branch=develop)](https://github.com/Lind-Project/safeposix-rust/actions/workflows/lind-selfhost.yml) 2 | 3 | More implementation details could be found at [wiki](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Home.md). 4 | 5 | ## Contents 6 | 7 | * [Home](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Home.md) 8 | * [Architecture](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Architecture.md) 9 | * [Interface](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Interface.md) 10 | * [Run Independently](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Run-Independently.md) 11 | * [Security Model](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Security-Model.md) 12 | * [Style Guide](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Style-Guide.md) 13 | * [Testing and Debugging](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Testing-and-Debugging.md) 14 | 15 | ## Run RustPOSIX-Rust 16 | 17 | Quick start 18 | Use Develop branch for the most stable behaviour. 19 | 20 | ```bash 21 | docker build -t --platform .devcontainer 22 | docker run -it 23 | 24 | ``` 25 | 26 | This will create a quick container with rustposix build at your local changes. 27 | helpful for exploration and easy testing. 28 | 29 | See reference at [Run RustPOSIX Independently](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Run-Independently.md) 30 | 31 | ## Test RustPOSIX-Rust 32 | 33 | Use main branch for the most stable behaviour. 34 | 35 | ```bash 36 | cargo build 37 | cargo test --lib 38 | ``` 39 | 40 | See reference at [Testing and Debugging](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Testing-and-Debugging.md) 41 | 42 | ## Development Guideline 43 | 44 | * All PRs should be merged to the Develop branch 45 | 46 | * Any imports from the standard library or any crates should be done in an interface file 47 | 48 | More detailed guideline will be in [RustPOSIX's wiki](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Style-Guide.md) -------------------------------------------------------------------------------- /benches/fs_open_close.rs: -------------------------------------------------------------------------------- 1 | /* Benchmarks for the microvisor implementation. In general, I'm not doing 2 | * results checking / assertations to avoid adding bias to the results. */ 3 | 4 | // I hate allowing this, but this is apparently a known issue for a lot of 5 | // code with CStrings. https://github.com/rust-lang/rust/issues/78691 6 | // I've tried to sanity check where this occurs, but please, please, please 7 | // double check these parts of the code! 8 | #![allow(temporary_cstring_as_ptr)] 9 | 10 | use criterion::{criterion_group, criterion_main, Criterion}; 11 | 12 | use rustposix::interface; 13 | 14 | use std::ffi::*; 15 | 16 | use rustposix::safeposix::cage::*; 17 | 18 | // Using this to include my criterion settings from a single shared file. 19 | // I did not use "use" or "mod" because benches/ isn't in the crate's usual 20 | // namespace and I didn't want to either make a separate crate with a single, 21 | // tiny file or add this file to the rustposix crate. 22 | mod global_criterion_settings; 23 | 24 | pub fn run_benchmark(c: &mut Criterion) { 25 | // I'm following the initialization workflow from the unit tests here. 26 | // 27 | // I'm using the lindrustinit to set up cages and the file system. 28 | rustposix::safeposix::dispatcher::lindrustinit(0); 29 | 30 | // Since all system calls are a method of a cage object, I also need this 31 | // reference. 32 | let cage = interface::cagetable_getref(1); 33 | 34 | // --- COMPARING open / close CALLS ACROSS Lind + Native OS kernel --- 35 | let mut group = c.benchmark_group("Compare fs:open+close"); 36 | 37 | // Should be similar. Use a linear scale... 38 | group.plot_config( 39 | criterion::PlotConfiguration::default().summary_scale(criterion::AxisScale::Linear), 40 | ); 41 | 42 | // Let's see how fast various file system calls are 43 | group.bench_function("TF01: Lind open+close", |b| { 44 | b.iter(|| { 45 | let fd = cage.open_syscall("foo", O_CREAT | O_TRUNC | O_WRONLY, S_IRWXA); 46 | cage.close_syscall(fd); 47 | }) 48 | }); 49 | 50 | // For comparison let's time the native OS... 51 | group.bench_function("TF01: Native OS kernel open+close", |b| { 52 | b.iter(|| unsafe { 53 | let fd = libc::open( 54 | CString::new("/tmp/foo").unwrap().as_ptr(), 55 | O_CREAT | O_TRUNC | O_WRONLY, 56 | S_IRWXA, 57 | ); 58 | libc::close(fd); 59 | }) 60 | }); 61 | group.finish(); 62 | 63 | // This cleans up in ways I do not fully understand. I think it ensures 64 | // the file system is cleaned up 65 | rustposix::safeposix::dispatcher::lindrustfinalize(); 66 | } 67 | 68 | criterion_group!(name=benches; 69 | // Add the global settings here so we don't type it everywhere 70 | config=global_criterion_settings::get_criterion(); 71 | targets=run_benchmark); 72 | criterion_main!(benches); 73 | -------------------------------------------------------------------------------- /benches/fs_read_write.rs: -------------------------------------------------------------------------------- 1 | /* Benchmarks for the microvisor implementation. In general, I'm not doing 2 | * results checking / assertations to avoid adding bias to the results. */ 3 | 4 | use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; 5 | 6 | use rustposix::interface; 7 | 8 | use std::ffi::*; 9 | 10 | use std::time::Duration; 11 | 12 | use rustposix::safeposix::cage::*; 13 | 14 | use rustposix::tests; 15 | 16 | // Using this to include my criterion settings from a single shared file. 17 | // I did not use "use" or "mod" because benches/ isn't in the crate's usual 18 | // namespace and I didn't want to either make a separate crate with a single, 19 | // tiny file or add this file to the rustposix crate. 20 | mod global_criterion_settings; 21 | 22 | pub fn run_benchmark(c: &mut Criterion) { 23 | // I'm following the initialization workflow from the unit tests here. 24 | // 25 | // I'm using the lindrustinit to set up cages and the file system. 26 | rustposix::safeposix::dispatcher::lindrustinit(0); 27 | 28 | // Since all system calls are a method of a cage object, I also need this 29 | // reference. 30 | let cage = interface::cagetable_getref(1); 31 | 32 | // --- COMPARING read + write w/o lseek CALLS ACROSS Lind + Native OS kernel --- 33 | // This is separated because writeat and readat do a lot of seeking. It's 34 | // useful to have a comparison which does not. 35 | let mut group = c.benchmark_group("Compare fs:write+read"); 36 | 37 | // Should be similar. Use a linear scale... 38 | group.plot_config( 39 | criterion::PlotConfiguration::default().summary_scale(criterion::AxisScale::Linear), 40 | ); 41 | 42 | // First do this for Lind 43 | 44 | // Reduce the time to reduce disk space needed and go faster. 45 | // Default is 5s... 46 | group.measurement_time(Duration::from_secs(2)); 47 | 48 | // Shorten the warm up time as well from 3s to this... 49 | group.warm_up_time(Duration::from_secs(1)); 50 | 51 | // Iterate for different buffer sizes... 52 | for buflen in [1, 64, 1024, 65536].iter() { 53 | let deststring = tests::str2cbuf( 54 | &String::from_utf8(vec![b'X'; *buflen]).expect("error building string"), 55 | ); 56 | 57 | let fd = cage.open_syscall("foo", O_CREAT | O_TRUNC | O_WRONLY, S_IRWXA); 58 | // Let's see how fast various file system calls are 59 | group.bench_with_input( 60 | BenchmarkId::new("TF02:Lind write", buflen), 61 | buflen, 62 | |b, buflen| { 63 | b.iter(|| { 64 | let _ = cage.write_syscall(fd, deststring, *buflen); 65 | }) 66 | }, 67 | ); 68 | 69 | cage.lseek_syscall(fd, 0, SEEK_SET); 70 | 71 | let mut read_buffer = tests::sizecbuf(*buflen); 72 | 73 | group.bench_with_input( 74 | BenchmarkId::new("TF02:Lind read", buflen), 75 | buflen, 76 | |b, buflen| { 77 | b.iter(|| { 78 | cage.read_syscall(fd, read_buffer.as_mut_ptr(), *buflen); 79 | }) 80 | }, 81 | ); 82 | 83 | cage.close_syscall(fd); 84 | cage.unlink_syscall("foo"); 85 | } 86 | 87 | // Now do this for Native 88 | 89 | // Iterate for different buffer sizes... 90 | for buflen in [1, 64, 1024, 65536].iter() { 91 | let fd: c_int; 92 | let c_str = CString::new("/tmp/foo").unwrap(); 93 | 94 | let path = c_str.into_raw() as *const u8; 95 | 96 | unsafe { 97 | fd = libc::open(path, O_CREAT | O_TRUNC | O_WRONLY, S_IRWXA); 98 | } 99 | 100 | let deststring = tests::str2cbuf( 101 | &String::from_utf8(vec![b'X'; *buflen]).expect("error building string"), 102 | ); 103 | 104 | // For comparison let's time the native OS... 105 | group.bench_with_input( 106 | BenchmarkId::new("TF02:Native write", buflen), 107 | buflen, 108 | |b, buflen| { 109 | b.iter(|| unsafe { 110 | let _ = libc::write(fd, deststring as *const c_void, *buflen); 111 | }) 112 | }, 113 | ); 114 | 115 | unsafe { 116 | libc::lseek(fd, 0, SEEK_SET); 117 | } 118 | 119 | let mut read_buffer = tests::sizecbuf(*buflen); 120 | 121 | // For comparison let's time the native OS... 122 | group.bench_with_input( 123 | BenchmarkId::new("TF02:Native read", buflen), 124 | buflen, 125 | |b, buflen| { 126 | b.iter(|| unsafe { 127 | libc::read(fd, read_buffer.as_mut_ptr() as *mut c_void, *buflen); 128 | }) 129 | }, 130 | ); 131 | 132 | unsafe { 133 | libc::close(fd); 134 | libc::unlink(path); 135 | } 136 | } 137 | group.finish(); 138 | 139 | // This cleans up in ways I do not fully understand. I think it ensures 140 | // the file system is cleaned up 141 | rustposix::safeposix::dispatcher::lindrustfinalize(); 142 | } 143 | 144 | criterion_group!(name=benches; 145 | // Add the global settings here so we don't type it everywhere 146 | config=global_criterion_settings::get_criterion(); 147 | targets=run_benchmark); 148 | criterion_main!(benches); 149 | -------------------------------------------------------------------------------- /benches/fs_read_write_seek.rs: -------------------------------------------------------------------------------- 1 | /* Benchmarks for the microvisor implementation. In general, I'm not doing 2 | * results checking / assertations to avoid adding bias to the results. */ 3 | 4 | use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; 5 | 6 | use rustposix::interface; 7 | 8 | use std::ffi::*; 9 | 10 | use std::time::Duration; 11 | 12 | use rustposix::safeposix::cage::*; 13 | 14 | use rustposix::tests; 15 | 16 | // Using this to include my criterion settings from a single shared file. 17 | // I did not use "use" or "mod" because benches/ isn't in the crate's usual 18 | // namespace and I didn't want to either make a separate crate with a single, 19 | // tiny file or add this file to the rustposix crate. 20 | mod global_criterion_settings; 21 | 22 | pub fn run_benchmark(c: &mut Criterion) { 23 | // I'm following the initialization workflow from the unit tests here. 24 | // 25 | // I'm using the lindrustinit to set up cages and the file system. 26 | rustposix::safeposix::dispatcher::lindrustinit(0); 27 | 28 | // Since all system calls are a method of a cage object, I also need this 29 | // reference. 30 | let cage = interface::cagetable_getref(1); 31 | 32 | // --- COMPARING read + write + lseek CALLS ACROSS Lind + Native OS kernel --- 33 | let mut group = c.benchmark_group("Compare fs:write+read+lseek"); 34 | 35 | // Should be similar. Use a linear scale... 36 | group.plot_config( 37 | criterion::PlotConfiguration::default().summary_scale(criterion::AxisScale::Linear), 38 | ); 39 | 40 | // Reduce the time to go faster! Default is 5s... 41 | group.measurement_time(Duration::from_secs(2)); 42 | 43 | // Shorten the warm up time as well from 3s to this... 44 | group.warm_up_time(Duration::from_secs(1)); 45 | 46 | // --- Lind --- 47 | 48 | // Iterate for different buffer sizes... 49 | for buflen in [1, 64, 1024, 65536].iter() { 50 | let fd = cage.open_syscall("foo", O_CREAT | O_TRUNC | O_WRONLY, S_IRWXA); 51 | 52 | let deststring = tests::str2cbuf( 53 | &String::from_utf8(vec![b'X'; *buflen]).expect("error building string"), 54 | ); 55 | 56 | let read_buffer = tests::sizecbuf(*buflen).as_mut_ptr(); 57 | // Let's see how fast various file system calls are 58 | group.bench_with_input( 59 | BenchmarkId::new("TF03:Lind write+read+lseek", buflen), 60 | buflen, 61 | |b, buflen| { 62 | b.iter(|| { 63 | let _ = cage.write_syscall(fd, deststring, *buflen); 64 | cage.lseek_syscall(fd, 0, SEEK_SET); 65 | cage.read_syscall(fd, read_buffer, *buflen); 66 | cage.lseek_syscall(fd, 0, SEEK_SET); 67 | }) 68 | }, 69 | ); 70 | 71 | cage.close_syscall(fd); 72 | cage.unlink_syscall("foo"); 73 | } 74 | 75 | // --- Native --- 76 | 77 | // Iterate for different buffer sizes... 78 | for buflen in [1, 64, 1024, 65536].iter() { 79 | let fd: c_int; 80 | 81 | let c_str = CString::new("/tmp/foo").unwrap(); 82 | 83 | let path = c_str.into_raw() as *const u8; 84 | 85 | unsafe { 86 | fd = libc::open(path, O_CREAT | O_TRUNC | O_WRONLY, S_IRWXA); 87 | } 88 | 89 | let deststring = tests::str2cbuf( 90 | &String::from_utf8(vec![b'X'; *buflen]).expect("error building string"), 91 | ); 92 | 93 | let read_buffer = tests::sizecbuf(*buflen).as_mut_ptr(); 94 | 95 | // For comparison let's time the native OS... 96 | group.bench_with_input( 97 | BenchmarkId::new("TF03:Native write+read+lseek", buflen), 98 | buflen, 99 | |b, buflen| { 100 | b.iter(|| unsafe { 101 | let _ = libc::write(fd, deststring as *const c_void, *buflen); 102 | libc::lseek(fd, 0, SEEK_SET); 103 | libc::read(fd, read_buffer as *mut c_void, *buflen); 104 | libc::lseek(fd, 0, SEEK_SET); 105 | }) 106 | }, 107 | ); 108 | unsafe { 109 | libc::close(fd); 110 | libc::unlink(path); 111 | } 112 | } 113 | 114 | group.finish(); 115 | 116 | // This cleans up in ways I do not fully understand. I think it ensures 117 | // the file system is cleaned up 118 | rustposix::safeposix::dispatcher::lindrustfinalize(); 119 | } 120 | 121 | criterion_group!(name=benches; 122 | // Add the global settings here so we don't type it everywhere 123 | config=global_criterion_settings::get_criterion(); 124 | targets=run_benchmark); 125 | criterion_main!(benches); 126 | -------------------------------------------------------------------------------- /benches/gen_getid.rs: -------------------------------------------------------------------------------- 1 | /* Benchmarks for the microvisor implementation. In general, I'm not doing 2 | * results checking / assertations to avoid adding bias to the results. */ 3 | 4 | use criterion::{criterion_group, criterion_main, Criterion}; 5 | 6 | use rustposix::interface; 7 | 8 | // Using this to include my criterion settings from a single shared file. 9 | // I did not use "use" or "mod" because benches/ isn't in the crate's usual 10 | // namespace and I didn't want to either make a separate crate with a single, 11 | // tiny file or add this file to the rustposix crate. 12 | mod global_criterion_settings; 13 | 14 | pub fn run_benchmark(c: &mut Criterion) { 15 | // I'm following the initialization workflow from the unit tests here. 16 | // 17 | // I'm using the lindrustinit to set up cages and the file system. 18 | rustposix::safeposix::dispatcher::lindrustinit(0); 19 | 20 | // Since all system calls are a method of a cage object, I also need this 21 | // reference. 22 | let cage = interface::cagetable_getref(1); 23 | 24 | // --- COMPARING get*id CALLS ACROSS Lind + Native OS kernel --- 25 | let mut group = c.benchmark_group("Compare get*ids"); 26 | 27 | // These should be quite different, so use a log axis.. 28 | group.plot_config( 29 | criterion::PlotConfiguration::default().summary_scale(criterion::AxisScale::Logarithmic), 30 | ); 31 | 32 | // let's have a combined benchmark of all of the get*id* system calls 33 | // in RustPOSIX... I'm not running these separately, because they should 34 | // not vary too much. 35 | group.bench_function("TG01: Lind get*ids", |b| { 36 | b.iter(|| { 37 | cage.getpid_syscall(); 38 | cage.getppid_syscall(); 39 | cage.getgid_syscall(); 40 | cage.getegid_syscall(); 41 | cage.getuid_syscall(); 42 | cage.geteuid_syscall(); 43 | }) 44 | }); 45 | // For comparison let's time the native OS... 46 | group.bench_function("TG01: Native OS kernel get*ids", |b| { 47 | b.iter(|| unsafe { 48 | libc::getpid(); 49 | libc::getppid(); 50 | libc::getgid(); 51 | libc::getegid(); 52 | libc::getuid(); 53 | libc::geteuid(); 54 | }) 55 | }); 56 | group.finish(); 57 | 58 | // This cleans up in ways I do not fully understand. I think it ensures 59 | // the file system is cleaned up 60 | rustposix::safeposix::dispatcher::lindrustfinalize(); 61 | } 62 | 63 | criterion_group!(name=benches; 64 | // Add the global settings here so we don't type it everywhere 65 | config=global_criterion_settings::get_criterion(); 66 | targets=run_benchmark); 67 | criterion_main!(benches); 68 | -------------------------------------------------------------------------------- /benches/global_criterion_settings.rs: -------------------------------------------------------------------------------- 1 | use criterion::Criterion; 2 | 3 | pub fn get_criterion() -> Criterion { 4 | Criterion::default().noise_threshold(0.10) 5 | } 6 | -------------------------------------------------------------------------------- /docs/RustPOSIX-README.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lind-Project/safeposix-rust/cd70d39a364bec626433ccf2f5b99788bb68bbdf/docs/RustPOSIX-README.jpg -------------------------------------------------------------------------------- /docs/SafePOSIX Rust Diagram.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lind-Project/safeposix-rust/cd70d39a364bec626433ccf2f5b99788bb68bbdf/docs/SafePOSIX Rust Diagram.jpg -------------------------------------------------------------------------------- /gen_netdevs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main () 9 | { 10 | struct ifaddrs *ifap, *ifa; 11 | struct sockaddr_in *sa, *ba, *na, *da; 12 | char *addr, *baddr, *naddr, *daddr; 13 | 14 | getifaddrs (&ifap); 15 | for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 16 | if (ifa->ifa_addr && ifa->ifa_addr->sa_family==AF_INET) { 17 | sa = (struct sockaddr_in *) ifa->ifa_addr; 18 | addr = inet_ntoa(sa->sin_addr); 19 | na = (struct sockaddr_in *) ifa->ifa_netmask; 20 | naddr = inet_ntoa(na->sin_addr); 21 | ba = (struct sockaddr_in *) ifa->ifa_broadaddr; 22 | baddr = inet_ntoa(ba->sin_addr); 23 | 24 | printf("%s %d %s %s %s\n", ifa->ifa_name, ifa->ifa_flags, addr, naddr, baddr); 25 | } 26 | } 27 | 28 | freeifaddrs(ifap); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /gen_netdevs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | WORKDIR=$(pwd) # Set working directory to the output of pwd 3 | FILE="$WORKDIR/gen_netdevs" # Use the full path for the file 4 | PARENTPATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) # needs the source location when being used in lind-build. 5 | 6 | if [ -f "$FILE" ]; then 7 | echo "$FILE exists." 8 | else 9 | echo "$FILE does not exist. Compiling." 10 | gcc "$PARENTPATH/gen_netdevs.c" -o "$WORKDIR/gen_netdevs" 11 | if [ $? -ne 0 ]; then # Check if gcc succeeded 12 | echo "Compilation failed." 13 | exit 1 14 | fi 15 | fi 16 | 17 | echo "Generating netdevs" 18 | 19 | "$WORKDIR/gen_netdevs" > "$WORKDIR/net_devices" # Execute and output using full paths 20 | -------------------------------------------------------------------------------- /src/interface/comm.rs: -------------------------------------------------------------------------------- 1 | // // Authors: Nicholas Renner and Jonathan Singer and Tristan Brigham 2 | // // 3 | // // 4 | 5 | use crate::interface; 6 | use std::fs::read_to_string; 7 | use std::mem::size_of; 8 | use std::str::from_utf8; 9 | use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; 10 | 11 | extern crate libc; 12 | 13 | static NET_DEV_FILENAME: &str = "net_devices"; 14 | 15 | static mut UD_ID_COUNTER: AtomicUsize = AtomicUsize::new(0); 16 | 17 | #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] 18 | pub enum GenSockaddr { 19 | Unix(SockaddrUnix), 20 | V4(SockaddrV4), 21 | V6(SockaddrV6), 22 | } 23 | impl GenSockaddr { 24 | pub fn port(&self) -> u16 { 25 | match self { 26 | GenSockaddr::Unix(_) => panic!("Invalid function called for this type of Sockaddr."), 27 | GenSockaddr::V4(v4addr) => v4addr.sin_port, 28 | GenSockaddr::V6(v6addr) => v6addr.sin6_port, 29 | } 30 | } 31 | pub fn set_port(&mut self, port: u16) { 32 | match self { 33 | GenSockaddr::Unix(_) => panic!("Invalid function called for this type of Sockaddr."), 34 | GenSockaddr::V4(v4addr) => v4addr.sin_port = port, 35 | GenSockaddr::V6(v6addr) => v6addr.sin6_port = port, 36 | }; 37 | } 38 | 39 | pub fn addr(&self) -> GenIpaddr { 40 | match self { 41 | GenSockaddr::Unix(_) => panic!("Invalid function called for this type of Sockaddr."), 42 | GenSockaddr::V4(v4addr) => GenIpaddr::V4(v4addr.sin_addr), 43 | GenSockaddr::V6(v6addr) => GenIpaddr::V6(v6addr.sin6_addr), 44 | } 45 | } 46 | 47 | pub fn set_addr(&mut self, ip: GenIpaddr) { 48 | match self { 49 | GenSockaddr::Unix(_unixaddr) => { 50 | panic!("Invalid function called for this type of Sockaddr.") 51 | } 52 | GenSockaddr::V4(v4addr) => { 53 | v4addr.sin_addr = if let GenIpaddr::V4(v4ip) = ip { 54 | v4ip 55 | } else { 56 | unreachable!() 57 | } 58 | } 59 | GenSockaddr::V6(v6addr) => { 60 | v6addr.sin6_addr = if let GenIpaddr::V6(v6ip) = ip { 61 | v6ip 62 | } else { 63 | unreachable!() 64 | } 65 | } 66 | }; 67 | } 68 | 69 | pub fn set_family(&mut self, family: u16) { 70 | match self { 71 | GenSockaddr::Unix(unixaddr) => unixaddr.sun_family = family, 72 | GenSockaddr::V4(v4addr) => v4addr.sin_family = family, 73 | GenSockaddr::V6(v6addr) => v6addr.sin6_family = family, 74 | }; 75 | } 76 | 77 | pub fn get_family(&self) -> u16 { 78 | match self { 79 | GenSockaddr::Unix(unixaddr) => unixaddr.sun_family, 80 | GenSockaddr::V4(v4addr) => v4addr.sin_family, 81 | GenSockaddr::V6(v6addr) => v6addr.sin6_family, 82 | } 83 | } 84 | 85 | pub fn path(&self) -> &str { 86 | match self { 87 | GenSockaddr::Unix(unixaddr) => { 88 | let pathiter = &mut unixaddr.sun_path.split(|idx| *idx == 0); 89 | let pathslice = pathiter.next().unwrap(); 90 | let path = from_utf8(pathslice).unwrap(); 91 | path 92 | } 93 | GenSockaddr::V4(_) => panic!("Invalid function called for this type of Sockaddr."), 94 | GenSockaddr::V6(_) => panic!("Invalid function called for this type of Sockaddr."), 95 | } 96 | } 97 | } 98 | 99 | #[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)] 100 | pub enum GenIpaddr { 101 | V4(V4Addr), 102 | V6(V6Addr), 103 | } 104 | 105 | impl GenIpaddr { 106 | pub fn is_unspecified(&self) -> bool { 107 | match self { 108 | GenIpaddr::V4(v4ip) => v4ip.s_addr == 0, 109 | GenIpaddr::V6(v6ip) => v6ip.s6_addr == [0; 16], 110 | } 111 | } 112 | pub fn from_string(string: &str) -> Option { 113 | let v4candidate: Vec<&str> = string.split('.').collect(); 114 | let v6candidate: Vec<&str> = string.split(':').collect(); 115 | let v4l = v4candidate.len(); 116 | let v6l = v6candidate.len(); 117 | if v4l == 1 && v6l > 1 { 118 | //then we should try parsing it as an ipv6 address 119 | let mut shortarr = [0u8; 16]; 120 | let mut shortindex = 0; 121 | let mut encountered_doublecolon = false; 122 | for short in v6candidate { 123 | if short.is_empty() { 124 | //you can only have a double colon once in an ipv6 address 125 | if encountered_doublecolon { 126 | return None; 127 | } 128 | encountered_doublecolon = true; 129 | 130 | let numzeros = 8 - v6l + 1; //+1 to account for this empty string element 131 | if numzeros == 0 { 132 | return None; 133 | } 134 | shortindex += numzeros; 135 | } else { 136 | //ok we can actually parse the element in this case 137 | if let Ok(b) = short.parse::() { 138 | //manually handle big endianness 139 | shortarr[2 * shortindex] = (b >> 8) as u8; 140 | shortarr[2 * shortindex + 1] = (b & 0xff) as u8; 141 | shortindex += 1; 142 | } else { 143 | return None; 144 | } 145 | } 146 | } 147 | return Some(Self::V6(V6Addr { s6_addr: shortarr })); 148 | } else if v4l == 4 && v6l == 1 { 149 | //then we should try parsing it as an ipv4 address 150 | let mut bytearr = [0u8; 4]; 151 | let mut shortindex = 0; 152 | for byte in v4candidate { 153 | if let Ok(b) = byte.parse::() { 154 | bytearr[shortindex] = b; 155 | shortindex += 1; 156 | } else { 157 | return None; 158 | } 159 | } 160 | return Some(Self::V4(V4Addr { 161 | s_addr: u32::from_ne_bytes(bytearr), 162 | })); 163 | } else { 164 | return None; 165 | } 166 | } 167 | } 168 | 169 | #[repr(C)] 170 | #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] 171 | pub struct SockaddrUnix { 172 | pub sun_family: u16, 173 | pub sun_path: [u8; 108], 174 | } 175 | 176 | pub fn new_sockaddr_unix(family: u16, path: &[u8]) -> SockaddrUnix { 177 | let pathlen = path.len(); 178 | if pathlen > 108 { 179 | panic!("Unix domain paths cannot exceed 108 bytes.") 180 | } 181 | let mut array_path: [u8; 108] = [0; 108]; 182 | array_path[0..pathlen].copy_from_slice(path); 183 | SockaddrUnix { 184 | sun_family: family, 185 | sun_path: array_path, 186 | } 187 | } 188 | 189 | pub fn gen_ud_path() -> String { 190 | let mut owned_path: String = "/sock".to_owned(); 191 | unsafe { 192 | let id = UD_ID_COUNTER.fetch_add(1, Ordering::Relaxed); 193 | owned_path.push_str(&id.to_string()); 194 | } 195 | owned_path.clone() 196 | } 197 | 198 | #[repr(C)] 199 | #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, Default)] 200 | pub struct V4Addr { 201 | pub s_addr: u32, 202 | } 203 | #[repr(C)] 204 | #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, Default)] 205 | pub struct SockaddrV4 { 206 | pub sin_family: u16, 207 | pub sin_port: u16, 208 | pub sin_addr: V4Addr, 209 | pub padding: u64, 210 | } 211 | 212 | #[repr(C)] 213 | #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, Default)] 214 | pub struct V6Addr { 215 | pub s6_addr: [u8; 16], 216 | } 217 | #[repr(C)] 218 | #[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, Default)] 219 | pub struct SockaddrV6 { 220 | pub sin6_family: u16, 221 | pub sin6_port: u16, 222 | pub sin6_flowinfo: u32, 223 | pub sin6_addr: V6Addr, 224 | pub sin6_scope_id: u32, 225 | } 226 | 227 | #[derive(Debug)] 228 | pub struct Socket { 229 | pub raw_sys_fd: i32, 230 | } 231 | 232 | impl Socket { 233 | pub fn new(domain: i32, socktype: i32, protocol: i32) -> Socket { 234 | let fd = unsafe { libc::socket(domain, socktype, protocol) }; 235 | 236 | //we make every socket have a recieve timeout of one second 237 | //This is in order to allow the socket to process and recieve 238 | //shutdowns while blocked on blocking recv syscalls. 239 | let timeoutval = libc::timeval { 240 | tv_sec: 1, 241 | tv_usec: 0, 242 | }; 243 | unsafe { 244 | libc::setsockopt( 245 | fd, 246 | libc::SOL_SOCKET, 247 | libc::SO_RCVTIMEO, 248 | (&timeoutval as *const libc::timeval) as *const libc::c_void, 249 | size_of::() as u32, 250 | ) 251 | }; 252 | if fd < 0 { 253 | panic!("Socket creation failed when it should never fail"); 254 | } 255 | Self { raw_sys_fd: fd } 256 | } 257 | 258 | pub fn bind(&self, addr: &GenSockaddr) -> i32 { 259 | let (finalsockaddr, addrlen) = match addr { 260 | GenSockaddr::V6(addrref6) => ( 261 | (addrref6 as *const SockaddrV6).cast::(), 262 | size_of::(), 263 | ), 264 | GenSockaddr::V4(addrref) => ( 265 | (addrref as *const SockaddrV4).cast::(), 266 | size_of::(), 267 | ), 268 | _ => { 269 | unreachable!() 270 | } 271 | }; 272 | unsafe { libc::bind(self.raw_sys_fd, finalsockaddr, addrlen as u32) } 273 | } 274 | 275 | pub fn connect(&self, addr: &GenSockaddr) -> i32 { 276 | let (finalsockaddr, addrlen) = match addr { 277 | GenSockaddr::V6(addrref6) => ( 278 | (addrref6 as *const SockaddrV6).cast::(), 279 | size_of::(), 280 | ), 281 | GenSockaddr::V4(addrref) => ( 282 | (addrref as *const SockaddrV4).cast::(), 283 | size_of::(), 284 | ), 285 | _ => { 286 | unreachable!() 287 | } 288 | }; 289 | unsafe { libc::connect(self.raw_sys_fd, finalsockaddr, addrlen as u32) } 290 | } 291 | 292 | pub fn sendto(&self, buf: *const u8, len: usize, addr: Option<&GenSockaddr>) -> i32 { 293 | let (finalsockaddr, addrlen) = match addr { 294 | Some(GenSockaddr::V6(addrref6)) => ( 295 | (addrref6 as *const SockaddrV6).cast::(), 296 | size_of::(), 297 | ), 298 | Some(GenSockaddr::V4(addrref)) => ( 299 | (addrref as *const SockaddrV4).cast::(), 300 | size_of::(), 301 | ), 302 | Some(_) => { 303 | unreachable!() 304 | } 305 | None => ( 306 | std::ptr::null::() as *const libc::sockaddr, 307 | 0, 308 | ), 309 | }; 310 | unsafe { 311 | libc::sendto( 312 | self.raw_sys_fd, 313 | buf as *const libc::c_void, 314 | len, 315 | 0, 316 | finalsockaddr, 317 | addrlen as u32, 318 | ) as i32 319 | } 320 | } 321 | 322 | pub fn writev(&self, iov: *const interface::IovecStruct, iovcnt: i32) -> i32 { 323 | unsafe { libc::writev(self.raw_sys_fd, iov as *const libc::iovec, iovcnt) as i32 } 324 | } 325 | 326 | pub fn recvfrom(&self, buf: *mut u8, len: usize, addr: &mut Option<&mut GenSockaddr>) -> i32 { 327 | let (finalsockaddr, mut addrlen) = match addr { 328 | Some(GenSockaddr::V6(ref mut addrref6)) => ( 329 | (addrref6 as *mut SockaddrV6).cast::(), 330 | size_of::() as u32, 331 | ), 332 | Some(GenSockaddr::V4(ref mut addrref)) => ( 333 | (addrref as *mut SockaddrV4).cast::(), 334 | size_of::() as u32, 335 | ), 336 | Some(_) => { 337 | unreachable!() 338 | } 339 | None => (std::ptr::null::() as *mut libc::sockaddr, 0), 340 | }; 341 | unsafe { 342 | libc::recvfrom( 343 | self.raw_sys_fd, 344 | buf as *mut libc::c_void, 345 | len, 346 | 0, 347 | finalsockaddr, 348 | &mut addrlen as *mut u32, 349 | ) as i32 350 | } 351 | } 352 | 353 | pub fn recvfrom_nonblocking( 354 | &self, 355 | buf: *mut u8, 356 | len: usize, 357 | addr: &mut Option<&mut GenSockaddr>, 358 | ) -> i32 { 359 | let (finalsockaddr, mut addrlen) = match addr { 360 | Some(GenSockaddr::V6(ref mut addrref6)) => ( 361 | (addrref6 as *mut SockaddrV6).cast::(), 362 | size_of::() as u32, 363 | ), 364 | Some(GenSockaddr::V4(ref mut addrref)) => ( 365 | (addrref as *mut SockaddrV4).cast::(), 366 | size_of::() as u32, 367 | ), 368 | Some(_) => { 369 | unreachable!() 370 | } 371 | None => (std::ptr::null::() as *mut libc::sockaddr, 0), 372 | }; 373 | self.set_nonblocking(); 374 | let retval = unsafe { 375 | libc::recvfrom( 376 | self.raw_sys_fd, 377 | buf as *mut libc::c_void, 378 | len, 379 | 0, 380 | finalsockaddr, 381 | &mut addrlen as *mut u32, 382 | ) as i32 383 | }; 384 | self.set_blocking(); 385 | retval 386 | } 387 | 388 | pub fn listen(&self, backlog: i32) -> i32 { 389 | unsafe { libc::listen(self.raw_sys_fd, backlog) } 390 | } 391 | 392 | pub fn set_blocking(&self) -> i32 { 393 | unsafe { libc::fcntl(self.raw_sys_fd, libc::F_SETFL, 0) } 394 | } 395 | 396 | pub fn set_nonblocking(&self) -> i32 { 397 | unsafe { libc::fcntl(self.raw_sys_fd, libc::F_SETFL, libc::O_NONBLOCK) } 398 | } 399 | 400 | pub fn accept(&self, isv4: bool) -> (Result, GenSockaddr) { 401 | return if isv4 { 402 | let mut inneraddrbuf = SockaddrV4::default(); 403 | let mut sadlen = size_of::() as u32; 404 | let newfd = unsafe { 405 | libc::accept( 406 | self.raw_sys_fd, 407 | (&mut inneraddrbuf as *mut SockaddrV4).cast::(), 408 | &mut sadlen as *mut u32, 409 | ) 410 | }; 411 | 412 | if newfd < 0 { 413 | (Err(newfd), GenSockaddr::V4(inneraddrbuf)) 414 | } else { 415 | ( 416 | Ok(Self { raw_sys_fd: newfd }), 417 | GenSockaddr::V4(inneraddrbuf), 418 | ) 419 | } 420 | } else { 421 | let mut inneraddrbuf = SockaddrV6::default(); 422 | let mut sadlen = size_of::() as u32; 423 | let newfd = unsafe { 424 | libc::accept( 425 | self.raw_sys_fd, 426 | (&mut inneraddrbuf as *mut SockaddrV6).cast::(), 427 | &mut sadlen as *mut u32, 428 | ) 429 | }; 430 | 431 | if newfd < 0 { 432 | (Err(newfd), GenSockaddr::V6(inneraddrbuf)) 433 | } else { 434 | ( 435 | Ok(Self { raw_sys_fd: newfd }), 436 | GenSockaddr::V6(inneraddrbuf), 437 | ) 438 | } 439 | }; 440 | } 441 | 442 | pub fn nonblock_accept(&self, isv4: bool) -> (Result, GenSockaddr) { 443 | return if isv4 { 444 | let mut inneraddrbuf = SockaddrV4::default(); 445 | let mut sadlen = size_of::() as u32; 446 | self.set_nonblocking(); 447 | let newfd = unsafe { 448 | libc::accept( 449 | self.raw_sys_fd, 450 | (&mut inneraddrbuf as *mut SockaddrV4).cast::(), 451 | &mut sadlen as *mut u32, 452 | ) 453 | }; 454 | self.set_blocking(); 455 | 456 | if newfd < 0 { 457 | (Err(newfd), GenSockaddr::V4(inneraddrbuf)) 458 | } else { 459 | ( 460 | Ok(Self { raw_sys_fd: newfd }), 461 | GenSockaddr::V4(inneraddrbuf), 462 | ) 463 | } 464 | } else { 465 | let mut inneraddrbuf = SockaddrV6::default(); 466 | let mut sadlen = size_of::() as u32; 467 | self.set_nonblocking(); 468 | let newfd = unsafe { 469 | libc::accept( 470 | self.raw_sys_fd, 471 | (&mut inneraddrbuf as *mut SockaddrV6).cast::(), 472 | &mut sadlen as *mut u32, 473 | ) 474 | }; 475 | self.set_blocking(); 476 | 477 | if newfd < 0 { 478 | (Err(newfd), GenSockaddr::V6(inneraddrbuf)) 479 | } else { 480 | ( 481 | Ok(Self { raw_sys_fd: newfd }), 482 | GenSockaddr::V6(inneraddrbuf), 483 | ) 484 | } 485 | }; 486 | } 487 | 488 | pub fn setsockopt(&self, level: i32, optname: i32, optval: i32) -> i32 { 489 | let valbuf = optval; 490 | let ret = unsafe { 491 | libc::setsockopt( 492 | self.raw_sys_fd, 493 | level, 494 | optname, 495 | (&valbuf as *const i32).cast::(), 496 | size_of::() as u32, 497 | ) 498 | }; 499 | ret 500 | } 501 | 502 | pub fn shutdown(&self, how: i32) -> i32 { 503 | let ret = unsafe { libc::shutdown(self.raw_sys_fd, how) }; 504 | ret 505 | } 506 | 507 | pub fn check_rawconnection(&self) -> bool { 508 | let mut valbuf = 0; 509 | let mut len = size_of::() as u32; 510 | let ret = unsafe { 511 | libc::getsockopt( 512 | self.raw_sys_fd, 513 | libc::SOL_SOCKET, 514 | libc::SO_ERROR, 515 | (&mut valbuf as *mut i32).cast::(), 516 | &mut len as *mut u32, 517 | ) 518 | }; 519 | (ret == 0) && (valbuf == 0) // if return val is 0 and error is 0 it's 520 | // connected 521 | } 522 | } 523 | 524 | impl Drop for Socket { 525 | fn drop(&mut self) { 526 | unsafe { 527 | libc::close(self.raw_sys_fd); 528 | } 529 | } 530 | } 531 | 532 | pub fn getifaddrs_from_file() -> String { 533 | read_to_string(NET_DEV_FILENAME) 534 | .expect("No net_devices file present!") 535 | .to_owned() 536 | } 537 | 538 | // Implementations of select related FD_SET structure 539 | pub struct FdSet(libc::fd_set); 540 | 541 | impl FdSet { 542 | pub fn new() -> FdSet { 543 | unsafe { 544 | let mut raw_fd_set = std::mem::MaybeUninit::::uninit(); 545 | libc::FD_ZERO(raw_fd_set.as_mut_ptr()); 546 | FdSet(raw_fd_set.assume_init()) 547 | } 548 | } 549 | 550 | pub fn new_from_ptr(raw_fdset_ptr: *const libc::fd_set) -> &'static mut FdSet { 551 | unsafe { &mut *(raw_fdset_ptr as *mut FdSet) } 552 | } 553 | 554 | // copy the src FdSet into self 555 | pub fn copy_from(&mut self, src_fds: &FdSet) { 556 | unsafe { 557 | std::ptr::copy_nonoverlapping( 558 | &src_fds.0 as *const libc::fd_set, 559 | &mut self.0 as *mut libc::fd_set, 560 | 1, 561 | ); 562 | } 563 | } 564 | 565 | // turn off the fd bit in fd_set (currently only used by the tests) 566 | #[allow(dead_code)] 567 | pub fn clear(&mut self, fd: i32) { 568 | unsafe { libc::FD_CLR(fd, &mut self.0) } 569 | } 570 | 571 | // turn on the fd bit in fd_set 572 | pub fn set(&mut self, fd: i32) { 573 | unsafe { libc::FD_SET(fd, &mut self.0) } 574 | } 575 | 576 | // return true if the bit for fd is set, false otherwise 577 | pub fn is_set(&self, fd: i32) -> bool { 578 | unsafe { libc::FD_ISSET(fd, &self.0) } 579 | } 580 | 581 | pub fn is_empty(&self) -> bool { 582 | let fd_array: &[u8] = unsafe { 583 | std::slice::from_raw_parts(&self.0 as *const _ as *const u8, size_of::()) 584 | }; 585 | fd_array.iter().all(|&byte| byte == 0) 586 | } 587 | 588 | // for each fd, if kernel_fds turned it on, then self will turn the 589 | // corresponding tranlated fd on 590 | pub fn set_from_kernelfds_and_translate( 591 | &mut self, 592 | kernel_fds: &FdSet, 593 | nfds: i32, 594 | rawfd_lindfd_tuples: &Vec<(i32, i32)>, 595 | ) { 596 | for fd in 0..nfds { 597 | if !kernel_fds.is_set(fd) { 598 | continue; 599 | } 600 | // translate and set 601 | if let Some((_, lindfd)) = rawfd_lindfd_tuples.iter().find(|(rawfd, _)| *rawfd == fd) { 602 | self.set(*lindfd); 603 | } 604 | } 605 | } 606 | } 607 | 608 | // for unwrapping in kernel_select 609 | fn to_fdset_ptr(opt: Option<&mut FdSet>) -> *mut libc::fd_set { 610 | match opt { 611 | None => std::ptr::null_mut(), 612 | Some(&mut FdSet(ref mut raw_fd_set)) => raw_fd_set, 613 | } 614 | } 615 | 616 | pub fn kernel_select( 617 | nfds: libc::c_int, 618 | readfds: Option<&mut FdSet>, 619 | writefds: Option<&mut FdSet>, 620 | errorfds: Option<&mut FdSet>, 621 | ) -> i32 { 622 | // Call libc::select and store the result 623 | let result = unsafe { 624 | // Create a timeval struct with zero timeout 625 | 626 | let mut kselect_timeout = libc::timeval { 627 | tv_sec: 0, // 0 seconds 628 | tv_usec: 0, // 0 microseconds 629 | }; 630 | 631 | libc::select( 632 | nfds, 633 | to_fdset_ptr(readfds), 634 | to_fdset_ptr(writefds), 635 | to_fdset_ptr(errorfds), 636 | &mut kselect_timeout as *mut libc::timeval, 637 | ) 638 | }; 639 | 640 | return result; 641 | } 642 | -------------------------------------------------------------------------------- /src/interface/errnos.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | // Error handling for SafePOSIX 3 | use crate::interface; 4 | 5 | use std::sync::OnceLock; 6 | 7 | pub static VERBOSE: OnceLock = OnceLock::new(); 8 | 9 | //A macro which takes the enum and adds to it a try_from trait which can 10 | // convert values back to enum variants 11 | macro_rules! reversible_enum { 12 | ($(#[$settings: meta])* $visibility: vis enum $enumname:ident { 13 | $($valuename: ident = $value: expr,)* 14 | }) => { 15 | $(#[$settings])* 16 | $visibility enum $enumname { 17 | $($valuename = $value,)* 18 | } 19 | 20 | impl $enumname { 21 | $visibility fn from_discriminant(v: i32) -> Result { 22 | match v { 23 | $($value => Ok($enumname::$valuename),)* 24 | _ => Err(()), 25 | } 26 | } 27 | } 28 | } 29 | } 30 | 31 | reversible_enum! { 32 | #[derive(Debug, PartialEq, Eq)] 33 | #[repr(i32)] 34 | pub enum Errno { 35 | EPERM = 1, // Operation not permitted 36 | ENOENT = 2, // No such file or directory 37 | ESRCH = 3, // No such process 38 | EINTR = 4, // Interrupted system call 39 | EIO = 5, // I/O error 40 | ENXIO = 6, // No such device or address 41 | EBIG = 7, // Argument list too long 42 | ENOEXEC = 8, // Exec format error 43 | EBADF = 9, // Bad file number 44 | ECHILD = 10, // No child processes 45 | EAGAIN = 11, // Try again 46 | ENOMEM = 12, // Out of memory 47 | EACCES = 13, // Permission denied 48 | EFAULT = 14, // Bad address 49 | ENOTBLK = 15, // Block device required 50 | EBUSY = 16, // Device or resource busy 51 | EEXIST = 17, // File exists 52 | EXDEV = 18, // Cross-device link 53 | ENODEV = 19, // No such device 54 | ENOTDIR = 20, // Not a directory 55 | EISDIR = 21, // Is a directory 56 | EINVAL = 22, // Invalid argument 57 | ENFILE = 23, // File table overflow 58 | EMFILE = 24, // Too many open files 59 | ENOTTY = 25, // Not a typewriter 60 | ETXTBSY = 26, // Text file busy 61 | EFBIG = 27, // File too large 62 | ENOSPC = 28, // No space left on device 63 | ESPIPE = 29, // Illegal seek 64 | EROFS = 30, // Read-only file system 65 | EMLINK = 31, // Too many links 66 | EPIPE = 32, // Broken pipe 67 | EDOM = 33, // Math argument out of domain of func 68 | ERANGE = 34, // Math result not representable 69 | EDEADLK = 35, // Resource deadlock would occur 70 | ENAMETOOLONG = 36, // File name too long 71 | ENOLCK = 37, // No record locks available 72 | ENOSYS = 38, // Function not implemented 73 | ENOTEMPTY = 39, // Directory not empty 74 | ELOOP = 40, // Too many symbolic links encountered 75 | // EWOULDBLOCK = 11, // Operation would block, returns EAGAIN 76 | ENOMSG = 42, // No message of desired type 77 | EIDRM = 43, // Identifier removed 78 | ECHRNG = 44, // Channel number out of range 79 | EL2NSYNC = 45, // Level not synchronized 80 | EL3HLT = 46, // Level halted 81 | EL3RST = 47, // Level reset 82 | ELNRNG = 48, // Link number out of range 83 | EUNATCH = 49, // Protocol driver not attached 84 | ENOCSI = 50, // No CSI structure available 85 | EL2HLT = 51, // Level halted 86 | EBADE = 52, // Invalid exchange 87 | EBADR = 53, // Invalid request descriptor 88 | EXFULL = 54, // Exchange full 89 | ENOANO = 55, // No anode 90 | EBADRQC = 56, // Invalid request code 91 | EBADSLT = 57, // Invalid slot 92 | EBFONT = 59, // Bad font file format 93 | ENOSTR = 60, // Device not a stream 94 | ENODATA = 61, // No data available 95 | ETIME = 62, // Timer expired 96 | ENOSR = 63, // Out of streams resources 97 | ENONET = 64, // Machine is not on the network 98 | ENOPKG = 65, // Package not installed 99 | EREMOTE = 66, // Object is remote 100 | ENOLINK = 67, // Link has been severed 101 | EADV = 68, // Advertise error 102 | ESRMNT = 69, // Srmount error 103 | ECOMM = 70, // Communication error on send 104 | EPROTO = 71, // Protocol error 105 | EMULTIHOP = 72, // Multihop attempted 106 | EDOTDOT = 73, // RFS specific error 107 | EBADMSG = 74, // Not a data message 108 | EOVERFLOW = 75, // Value too large for defined data type 109 | ENOTUNIQ = 76, // Name not unique on network 110 | EBADFD = 77, // File descriptor in bad state 111 | EREMCHG = 78, // Remote address changed 112 | ELIBACC = 79, // Can not access a needed shared library 113 | ELIBBAD = 80, // Accessing a corrupted shared library 114 | ELIBSCN = 81, // .lib section in a.out corrupted 115 | ELIBMAX = 82, // Attempting to link in too many shared libraries 116 | ELIBEXEC = 83, // Cannot exec a shared library directly 117 | EILSEQ = 84, // Illegal byte sequence 118 | ERESTART = 85, // Interrupted system call should be restarted 119 | ESTRPIPE = 86, // Streams pipe error 120 | EUSERS = 87, // Too many users 121 | ENOTSOCK = 88, // Socket operation on non-socket 122 | EDESTADDRREQ = 89, // Destination address required 123 | EMSGSIZE = 90, // Message too long 124 | EPROTOTYPE = 91, // Protocol wrong type for socket 125 | ENOPROTOOPT = 92, // Protocol not available 126 | EPROTONOSUPPORT = 93, // Protocol not supported 127 | ESOCKTNOSUPPORT = 94, // Socket type not supported 128 | EOPNOTSUPP = 95, // Operation not supported on transport endpoint 129 | EPFNOSUPPORT = 96, // Protocol family not supported 130 | EAFNOSUPPORT = 97, // Address family not supported by protocol 131 | EADDRINUSE = 98, // Address already in use 132 | EADDRNOTAVAIL = 99, // Cannot assign requested address 133 | ENETDOWN = 100, // Network is down 134 | ENETUNREACH = 101, // Network is unreachable 135 | ENETRESET = 102, // Network dropped connection because of reset 136 | ECONNABORTED = 103, // Software caused connection abort 137 | ECONNRESET = 104, // Connection reset by peer 138 | ENOBUFS = 105, // No buffer space available 139 | EISCONN = 106, // Transport endpoint is already connected 140 | ENOTCONN = 107, // Transport endpoint is not connected 141 | ESHUTDOWN = 108, // Cannot send after transport endpoint shutdown 142 | ETOOMANYREFS = 109, // Too many references cannot splice 143 | ETIMEDOUT = 110, // Connection timed out 144 | ECONNREFUSED = 111, // Connection refused 145 | EHOSTDOWN = 112, // Host is down 146 | EHOSTUNREACH = 113, // No route to host 147 | EALREADY = 114, // Operation already in progress 148 | EINPROGRESS = 115, // Operation now in progress 149 | ESTALE = 116, // Stale NFS file handle 150 | EUCLEAN = 117, // Structure needs cleaning 151 | ENOTNAM = 118, // Not a XENIX named type file 152 | ENAVAIL = 119, // No XENIX semaphores available 153 | EISNAM = 120, // Is a named type file 154 | EREMOTEIO = 121, // Remote I/O error 155 | EDQUOT = 122, // Quota exceeded 156 | ENOMEDIUM = 123, // No medium found 157 | EMEDIUMTYPE = 124, // Wrong medium type 158 | ECANCELED = 125, // Operation Canceled 159 | ENOKEY = 126, // Required key not available 160 | EKEYEXPIRED = 127, // Key has expired 161 | EKEYREVOKED = 128, // Key has been revoked 162 | EKEYREJECTED = 129, // Key was rejected by service for robust mutexes 163 | EOWNERDEAD = 130, // Owner died 164 | ENOTRECOVERABLE = 131, // State not recoverable 165 | } 166 | } 167 | 168 | pub fn syscall_error(e: Errno, syscall: &str, message: &str) -> i32 { 169 | if *VERBOSE.get().unwrap() > 0 { 170 | let msg = format!("Error in syscall: {} - {:?}: {}", syscall, e, message); 171 | interface::log_to_stderr(&msg); 172 | } 173 | -(e as i32) 174 | } 175 | -------------------------------------------------------------------------------- /src/interface/file.rs: -------------------------------------------------------------------------------- 1 | // Author: Nicholas Renner 2 | // 3 | // File related interface 4 | #![allow(dead_code)] 5 | 6 | use dashmap::DashSet; 7 | use parking_lot::Mutex; 8 | use std::env; 9 | pub use std::ffi::CStr as RustCStr; 10 | use std::fs::{self, canonicalize, File, OpenOptions}; 11 | use std::io::{IoSlice, Read, Seek, SeekFrom, Write}; 12 | pub use std::path::{Component as RustPathComponent, Path as RustPath, PathBuf as RustPathBuf}; 13 | use std::slice; 14 | use std::sync::Arc; 15 | pub use std::sync::LazyLock as RustLazyGlobal; 16 | 17 | use crate::interface::errnos::{syscall_error, Errno}; 18 | use libc::{mmap, mremap, munmap, off64_t, MAP_SHARED, MREMAP_MAYMOVE, PROT_READ, PROT_WRITE}; 19 | use std::convert::TryInto; 20 | use std::ffi::c_void; 21 | use std::os::unix::fs::FileExt; 22 | use std::os::unix::io::{AsRawFd, RawFd}; 23 | 24 | pub fn removefile(filename: String) -> std::io::Result<()> { 25 | let path: RustPathBuf = [".".to_string(), filename].iter().collect(); 26 | 27 | let absolute_filename = canonicalize(&path)?; //will return an error if the file does not exist 28 | 29 | fs::remove_file(absolute_filename)?; 30 | 31 | Ok(()) 32 | } 33 | 34 | pub fn openfile(filename: String, filesize: usize) -> std::io::Result { 35 | EmulatedFile::new(filename, filesize) 36 | } 37 | 38 | pub fn openmetadata(filename: String) -> std::io::Result { 39 | EmulatedFile::new_metadata(filename) 40 | } 41 | 42 | #[derive(Debug)] 43 | pub struct EmulatedFile { 44 | filename: String, 45 | fobj: Option>>, 46 | filesize: usize, 47 | } 48 | 49 | pub fn pathexists(filename: String) -> bool { 50 | let path: RustPathBuf = [".".to_string(), filename.clone()].iter().collect(); 51 | path.exists() 52 | } 53 | 54 | impl EmulatedFile { 55 | fn new(filename: String, filesize: usize) -> std::io::Result { 56 | let f = OpenOptions::new() 57 | .read(true) 58 | .write(true) 59 | .create(true) 60 | .open(filename.clone()) 61 | .unwrap(); 62 | Ok(EmulatedFile { 63 | filename, 64 | fobj: Some(Arc::new(Mutex::new(f))), 65 | filesize, 66 | }) 67 | } 68 | 69 | fn new_metadata(filename: String) -> std::io::Result { 70 | let f = OpenOptions::new() 71 | .read(true) 72 | .write(true) 73 | .create(true) 74 | .open(filename.clone()) 75 | .unwrap(); 76 | 77 | let filesize = f.metadata()?.len(); 78 | 79 | Ok(EmulatedFile { 80 | filename, 81 | fobj: Some(Arc::new(Mutex::new(f))), 82 | filesize: filesize as usize, 83 | }) 84 | } 85 | 86 | pub fn close(&self) -> std::io::Result<()> { 87 | Ok(()) 88 | } 89 | 90 | pub fn shrink(&mut self, length: usize) -> std::io::Result<()> { 91 | if length > self.filesize { 92 | panic!( 93 | "Something is wrong. {} is already smaller than length.", 94 | self.filename 95 | ); 96 | } 97 | match &self.fobj { 98 | None => panic!("{} is already closed.", self.filename), 99 | Some(f) => { 100 | let fobj = f.lock(); 101 | fobj.set_len(length as u64)?; 102 | self.filesize = length; 103 | Ok(()) 104 | } 105 | } 106 | } 107 | 108 | pub fn fdatasync(&self) -> std::io::Result<()> { 109 | match &self.fobj { 110 | None => panic!("{} is already closed.", self.filename), 111 | Some(f) => { 112 | let fobj = f.lock(); 113 | fobj.sync_data()?; 114 | Ok(()) 115 | } 116 | } 117 | } 118 | 119 | pub fn fsync(&self) -> std::io::Result<()> { 120 | match &self.fobj { 121 | None => panic!("{} is already closed.", self.filename), 122 | Some(f) => { 123 | let fobj = f.lock(); 124 | fobj.sync_all()?; 125 | Ok(()) 126 | } 127 | } 128 | } 129 | 130 | pub fn sync_file_range(&self, offset: isize, nbytes: isize, flags: u32) -> i32 { 131 | let fd = &self.as_fd_handle_raw_int(); 132 | let valid_flags = libc::SYNC_FILE_RANGE_WAIT_BEFORE 133 | | libc::SYNC_FILE_RANGE_WRITE 134 | | libc::SYNC_FILE_RANGE_WAIT_AFTER; 135 | if !(flags & !valid_flags == 0) { 136 | return syscall_error( 137 | Errno::EINVAL, 138 | "sync_file_range", 139 | "flags specifies an invalid bit", 140 | ); 141 | } 142 | unsafe { libc::sync_file_range(*fd, offset as off64_t, nbytes as off64_t, flags) } 143 | } 144 | 145 | // Wrapper around Rust's file object read_at function 146 | // Reads from file at specified offset into provided C-buffer 147 | // We need to specify the offset for read/write operations because multiple 148 | // cages may refer to same system file handle 149 | pub fn readat(&self, ptr: *mut u8, length: usize, offset: usize) -> std::io::Result { 150 | let buf = unsafe { 151 | assert!(!ptr.is_null()); 152 | slice::from_raw_parts_mut(ptr, length) 153 | }; 154 | 155 | match &self.fobj { 156 | None => panic!("{} is already closed.", self.filename), 157 | Some(f) => { 158 | let fobj = f.lock(); 159 | if offset > self.filesize { 160 | panic!("Seek offset extends past the EOF!"); 161 | } 162 | let bytes_read = fobj.read_at(buf, offset as u64)?; 163 | Ok(bytes_read) 164 | } 165 | } 166 | } 167 | 168 | // Wrapper around Rust's file object write_at function 169 | // Writes from provided C-buffer into file at specified offset 170 | // We need to specify the offset for read/write operations because multiple 171 | // cages may refer to same system file handle 172 | pub fn writeat( 173 | &mut self, 174 | ptr: *const u8, 175 | length: usize, 176 | offset: usize, 177 | ) -> std::io::Result { 178 | let bytes_written; 179 | 180 | let buf = unsafe { 181 | assert!(!ptr.is_null()); 182 | slice::from_raw_parts(ptr, length) 183 | }; 184 | 185 | match &self.fobj { 186 | None => panic!("{} is already closed.", self.filename), 187 | Some(f) => { 188 | let fobj = f.lock(); 189 | if offset > self.filesize { 190 | panic!("Seek offset extends past the EOF!"); 191 | } 192 | bytes_written = fobj.write_at(buf, offset as u64)?; 193 | } 194 | } 195 | 196 | // update our recorded filesize if we've written past the old filesize 197 | if offset + length > self.filesize { 198 | self.filesize = offset + length; 199 | } 200 | 201 | Ok(bytes_written) 202 | } 203 | 204 | pub fn write_vectored_at( 205 | &mut self, 206 | bufs: &[IoSlice<'_>], 207 | offset: usize, 208 | ) -> std::io::Result { 209 | let mut total_bytes_written = 0; // To keep track of the total number of bytes written. 210 | 211 | if let Some(f) = &self.fobj { 212 | // Use write_vectored_at directly 213 | total_bytes_written = f.lock().write_vectored_at(bufs, offset as u64)?; 214 | } 215 | 216 | // Update the recorded file size if we've written past the previous file size 217 | if offset + total_bytes_written > self.filesize { 218 | self.filesize = offset + total_bytes_written; 219 | } 220 | 221 | Ok(total_bytes_written) 222 | } 223 | 224 | // Reads entire file into bytes 225 | pub fn readfile_to_new_bytes(&self) -> std::io::Result> { 226 | match &self.fobj { 227 | None => panic!("{} is already closed.", self.filename), 228 | Some(f) => { 229 | let mut stringbuf = Vec::new(); 230 | let mut fobj = f.lock(); 231 | fobj.read_to_end(&mut stringbuf)?; 232 | Ok(stringbuf) // return new buf string 233 | } 234 | } 235 | } 236 | 237 | // Write to entire file from provided bytes 238 | pub fn writefile_from_bytes(&mut self, buf: &[u8]) -> std::io::Result<()> { 239 | let length = buf.len(); 240 | let offset = self.filesize; 241 | 242 | match &self.fobj { 243 | None => panic!("{} is already closed.", self.filename), 244 | Some(f) => { 245 | let mut fobj = f.lock(); 246 | if offset > self.filesize { 247 | panic!("Seek offset extends past the EOF!"); 248 | } 249 | fobj.seek(SeekFrom::Start(offset as u64))?; 250 | fobj.write(buf)?; 251 | } 252 | } 253 | 254 | if offset + length > self.filesize { 255 | self.filesize = offset + length; 256 | } 257 | 258 | Ok(()) 259 | } 260 | 261 | pub fn zerofill_at(&mut self, offset: usize, count: usize) -> std::io::Result { 262 | let bytes_written; 263 | let buf = vec![0; count]; 264 | 265 | match &self.fobj { 266 | None => panic!("{} is already closed.", self.filename), 267 | Some(f) => { 268 | let mut fobj = f.lock(); 269 | if offset > self.filesize { 270 | panic!("Seek offset extends past the EOF!"); 271 | } 272 | fobj.seek(SeekFrom::Start(offset as u64))?; 273 | bytes_written = fobj.write(buf.as_slice())?; 274 | } 275 | } 276 | 277 | if offset + count > self.filesize { 278 | self.filesize = offset + count; 279 | } 280 | 281 | Ok(bytes_written) 282 | } 283 | 284 | //gets the raw fd handle (integer) from a rust fileobject 285 | pub fn as_fd_handle_raw_int(&self) -> i32 { 286 | if let Some(wrapped_barefile) = &self.fobj { 287 | wrapped_barefile.lock().as_raw_fd() as i32 288 | } else { 289 | -1 290 | } 291 | } 292 | } 293 | 294 | pub const COUNTMAPSIZE: usize = 8; 295 | pub const MAP_1MB: usize = usize::pow(2, 20); 296 | 297 | #[derive(Debug)] 298 | pub struct EmulatedFileMap { 299 | filename: String, 300 | fobj: Arc>, 301 | map: Arc>>>, 302 | count: usize, 303 | countmap: Arc>>>, 304 | mapsize: usize, 305 | } 306 | 307 | pub fn mapfilenew(filename: String) -> std::io::Result { 308 | EmulatedFileMap::new(filename) 309 | } 310 | 311 | impl EmulatedFileMap { 312 | fn new(filename: String) -> std::io::Result { 313 | let f = OpenOptions::new() 314 | .read(true) 315 | .write(true) 316 | .create(true) 317 | .open(filename.clone()) 318 | .unwrap(); 319 | 320 | let mapsize = MAP_1MB - COUNTMAPSIZE; 321 | // set the file equal to where were mapping the count and the actual map 322 | let _newsize = f.set_len((COUNTMAPSIZE + mapsize) as u64).unwrap(); 323 | 324 | let map: Vec; 325 | let countmap: Vec; 326 | 327 | // here were going to map the first 8 bytes of the file as the "count" (amount 328 | // of bytes written), and then map another 1MB for logging 329 | unsafe { 330 | let map_addr = mmap( 331 | 0 as *mut c_void, 332 | MAP_1MB, 333 | PROT_READ | PROT_WRITE, 334 | MAP_SHARED, 335 | f.as_raw_fd() as i32, 336 | 0 as i64, 337 | ); 338 | countmap = Vec::::from_raw_parts(map_addr as *mut u8, COUNTMAPSIZE, COUNTMAPSIZE); 339 | let map_ptr = map_addr as *mut u8; 340 | map = 341 | Vec::::from_raw_parts(map_ptr.offset(COUNTMAPSIZE as isize), mapsize, mapsize); 342 | } 343 | 344 | Ok(EmulatedFileMap { 345 | filename, 346 | fobj: Arc::new(Mutex::new(f)), 347 | map: Arc::new(Mutex::new(Some(map))), 348 | count: 0, 349 | countmap: Arc::new(Mutex::new(Some(countmap))), 350 | mapsize, 351 | }) 352 | } 353 | 354 | pub fn write_to_map(&mut self, bytes_to_write: &[u8]) -> std::io::Result<()> { 355 | let writelen = bytes_to_write.len(); 356 | 357 | // if we're writing past the current map, increase the map another 1MB 358 | if writelen + self.count > self.mapsize { 359 | self.extend_map(); 360 | } 361 | 362 | let mut mapopt = self.map.lock(); 363 | let map = mapopt.as_deref_mut().unwrap(); 364 | 365 | let mapslice = &mut map[self.count..(self.count + writelen)]; 366 | mapslice.copy_from_slice(bytes_to_write); 367 | self.count += writelen; 368 | 369 | // update the bytes written in the map portion 370 | let mut countmapopt = self.countmap.lock(); 371 | let countmap = countmapopt.as_deref_mut().unwrap(); 372 | countmap.copy_from_slice(&self.count.to_be_bytes()); 373 | 374 | Ok(()) 375 | } 376 | 377 | fn extend_map(&mut self) { 378 | // open count and map to resize mmap, and file to increase file size 379 | let mut mapopt = self.map.lock(); 380 | let map = mapopt.take().unwrap(); 381 | let mut countmapopt = self.countmap.lock(); 382 | let countmap = countmapopt.take().unwrap(); 383 | let f = self.fobj.lock(); 384 | 385 | // add another 1MB to mapsize 386 | let new_mapsize = self.mapsize + MAP_1MB; 387 | let _newsize = f.set_len((COUNTMAPSIZE + new_mapsize) as u64).unwrap(); 388 | 389 | let newmap: Vec; 390 | let newcountmap: Vec; 391 | 392 | // destruct count and map and re-map 393 | unsafe { 394 | let (old_count_map_addr, countlen, _countcap) = countmap.into_raw_parts(); 395 | assert_eq!(COUNTMAPSIZE, countlen); 396 | let (_old_map_addr, len, _cap) = map.into_raw_parts(); 397 | assert_eq!(self.mapsize, len); 398 | let map_addr = mremap( 399 | old_count_map_addr as *mut c_void, 400 | COUNTMAPSIZE + self.mapsize, 401 | COUNTMAPSIZE + new_mapsize, 402 | MREMAP_MAYMOVE, 403 | ); 404 | 405 | newcountmap = 406 | Vec::::from_raw_parts(map_addr as *mut u8, COUNTMAPSIZE, COUNTMAPSIZE); 407 | let map_ptr = map_addr as *mut u8; 408 | newmap = Vec::::from_raw_parts( 409 | map_ptr.offset(COUNTMAPSIZE as isize), 410 | new_mapsize, 411 | new_mapsize, 412 | ); 413 | } 414 | 415 | // replace maps 416 | mapopt.replace(newmap); 417 | countmapopt.replace(newcountmap); 418 | self.mapsize = new_mapsize; 419 | } 420 | 421 | pub fn close(&self) -> std::io::Result<()> { 422 | let mut mapopt = self.map.lock(); 423 | let map = mapopt.take().unwrap(); 424 | let mut countmapopt = self.countmap.lock(); 425 | let countmap = countmapopt.take().unwrap(); 426 | 427 | unsafe { 428 | let (countmap_addr, countlen, _countcap) = countmap.into_raw_parts(); 429 | assert_eq!(COUNTMAPSIZE, countlen); 430 | munmap(countmap_addr as *mut c_void, COUNTMAPSIZE); 431 | 432 | let (map_addr, len, _cap) = map.into_raw_parts(); 433 | assert_eq!(self.mapsize, len); 434 | munmap(map_addr as *mut c_void, self.mapsize); 435 | } 436 | 437 | Ok(()) 438 | } 439 | } 440 | 441 | #[derive(Debug)] 442 | pub struct ShmFile { 443 | fobj: Arc>, 444 | key: i32, 445 | size: usize, 446 | } 447 | 448 | pub fn new_shm_backing(key: i32, size: usize) -> std::io::Result { 449 | ShmFile::new(key, size) 450 | } 451 | 452 | // Mimic shared memory in Linux by creating a file backing and truncating it to 453 | // the segment size We can then safely unlink the file while still holding a 454 | // descriptor to that segment, which we can use to map shared across cages. 455 | impl ShmFile { 456 | fn new(key: i32, size: usize) -> std::io::Result { 457 | // open file "shm-#id" 458 | let filename = format!("{}{}", "shm-", key); 459 | let f = OpenOptions::new() 460 | .read(true) 461 | .write(true) 462 | .create(true) 463 | .open(filename.clone()) 464 | .unwrap(); 465 | // truncate file to size 466 | f.set_len(size as u64)?; 467 | // unlink file 468 | fs::remove_file(filename)?; 469 | let shmfile = ShmFile { 470 | fobj: Arc::new(Mutex::new(f)), 471 | key, 472 | size, 473 | }; 474 | 475 | Ok(shmfile) 476 | } 477 | 478 | //gets the raw fd handle (integer) from a rust fileobject 479 | pub fn as_fd_handle_raw_int(&self) -> i32 { 480 | self.fobj.lock().as_raw_fd() as i32 481 | } 482 | } 483 | 484 | // convert a series of big endian bytes to a size 485 | pub fn convert_bytes_to_size(bytes_to_write: &[u8]) -> usize { 486 | let sizearray: [u8; 8] = bytes_to_write.try_into().unwrap(); 487 | usize::from_be_bytes(sizearray) 488 | } 489 | 490 | #[cfg(test)] 491 | mod tests { 492 | use super::*; 493 | use tempfile::NamedTempFile; 494 | 495 | #[test] 496 | fn test_path_exists_true() { 497 | let temp_file = NamedTempFile::new().unwrap(); 498 | let file_path = temp_file.path().to_str().unwrap().to_string(); 499 | assert!(pathexists(file_path)); 500 | } 501 | 502 | #[test] 503 | fn test_path_exists_false() { 504 | // Test that pathexists returns false for a non-existent file 505 | let non_existent_file = "/tmp/non_existent_file.txt"; 506 | assert!(!pathexists(non_existent_file.to_string())); 507 | } 508 | #[test] 509 | fn test_new_emulated_file() { 510 | let filename = "test_file.txt"; 511 | let filesize = 1024; 512 | 513 | let emulated_file = EmulatedFile::new(filename.to_string(), filesize).unwrap(); 514 | 515 | assert_eq!(emulated_file.filename, filename); 516 | assert_eq!(emulated_file.filesize, filesize); 517 | assert!(emulated_file.fobj.is_some()); 518 | } 519 | 520 | #[test] 521 | fn test_new_metadata_emulated_file() { 522 | let temp_file = NamedTempFile::new().unwrap(); 523 | let file_path = temp_file.path().to_str().unwrap().to_string(); 524 | 525 | let emulated_file = EmulatedFile::new_metadata(file_path.clone()).unwrap(); 526 | 527 | assert_eq!(emulated_file.filename, file_path); 528 | assert!(emulated_file.fobj.is_some()); 529 | } 530 | #[test] 531 | fn test_readat_emulated_file() { 532 | let temp_file = NamedTempFile::new().unwrap(); 533 | let file_path = temp_file.path().to_str().unwrap().to_string(); 534 | let file_content = b"Hello, world!"; 535 | temp_file.as_file().write_all(file_content).unwrap(); 536 | 537 | let emulated_file = EmulatedFile::new(file_path.clone(), file_content.len()).unwrap(); 538 | 539 | let mut buffer = vec![0; file_content.len()]; 540 | let bytes_read = emulated_file 541 | .readat(buffer.as_mut_ptr(), buffer.len(), 0) 542 | .unwrap(); 543 | 544 | assert_eq!(bytes_read, file_content.len()); 545 | assert_eq!(buffer, file_content); 546 | } 547 | 548 | #[test] 549 | fn test_writeat_emulated_file() { 550 | let temp_file = NamedTempFile::new().unwrap(); 551 | let file_path = temp_file.path().to_str().unwrap().to_string(); 552 | let file_content = b"Hello, world!"; 553 | 554 | let mut emulated_file = EmulatedFile::new(file_path.clone(), file_content.len()).unwrap(); 555 | 556 | let new_content = b"test_writeat_emulated_file, world!"; 557 | let bytes_written = emulated_file 558 | .writeat(new_content.as_ptr(), new_content.len(), 0) 559 | .unwrap(); 560 | 561 | assert_eq!(bytes_written, new_content.len()); 562 | assert_eq!(emulated_file.filesize, new_content.len()); 563 | 564 | let mut buffer = vec![0; new_content.len()]; 565 | emulated_file 566 | .readat(buffer.as_mut_ptr(), buffer.len(), 0) 567 | .unwrap(); 568 | assert_eq!(buffer, new_content); 569 | } 570 | } 571 | -------------------------------------------------------------------------------- /src/interface/mod.rs: -------------------------------------------------------------------------------- 1 | // Author: Nicholas Renner 2 | //! Module definitions for the RustPOSIX interface 3 | //! 4 | //! ## Interface Module 5 | //! 6 | //! Secure interface module that enforces containment of kernel calls to 7 | //! "popular paths" to enhance security. It restricts access to libraries only 8 | //! through specified paths in order to limit kernel calls to these popular 9 | //! paths. 10 | //! 11 | //! This interface limits kernel access from Rust to the popular paths as 12 | //! defined in Lock-in-Pop Libraries are imported only via `use` statements 13 | //! within these files, allowing for focused testing and verification of kernel 14 | //! access via the slimmer interface to ensure restricted access to popular 15 | //! paths. 16 | 17 | mod comm; 18 | pub mod errnos; 19 | mod file; 20 | mod misc; 21 | mod pipe; 22 | mod timer; 23 | pub mod types; 24 | pub use comm::*; 25 | pub use errnos::*; 26 | pub use file::*; 27 | pub use misc::*; 28 | pub use pipe::*; 29 | pub use timer::*; 30 | pub use types::*; 31 | -------------------------------------------------------------------------------- /src/interface/pipe.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | // Author: Nicholas Renner 3 | 4 | //! In-Memory Pipe Imlpementation for the RustPOSIX interface 5 | //! 6 | //! ## Pipe Module 7 | //! 8 | //! This module provides a method for in memory iPC between cages and is able to 9 | //! replicate both pipes and Unix domain sockets. 10 | //! 11 | //! Linux pipes are implemented as a circular buffer of 16 pages (4096 bytes 12 | //! each). Instead for RustPOSIX we implement the buffer as a lock-free circular 13 | //! buffer for the sum of bytes in those pages. 14 | //! 15 | //! This implementation is also used internally by RustPOSIX to approximate Unix 16 | //! sockets by allocating two of these pipes bi-directionally. 17 | //! 18 | //! We expose an API allowing to read and write to the pipe as well as check if 19 | //! pipe descriptors are reading for reading and writing via select/poll 20 | /// 21 | /// To learn more about pipes 22 | /// [pipe(7)](https://man7.org/linux/man-pages/man7/pipe.7.html) 23 | use crate::interface; 24 | use crate::interface::errnos::{syscall_error, Errno}; 25 | 26 | use parking_lot::Mutex; 27 | use ringbuf::{Consumer, Producer, RingBuffer}; 28 | use std::cmp::min; 29 | use std::fmt; 30 | use std::slice; 31 | use std::sync::atomic::{AtomicU32, Ordering}; 32 | use std::sync::Arc; 33 | 34 | // lets define a few constants for permission flags and the standard size of a 35 | // Linux page 36 | const O_RDONLY: i32 = 0o0; 37 | const O_WRONLY: i32 = 0o1; 38 | const O_RDWRFLAGS: i32 = 0o3; 39 | const PAGE_SIZE: usize = 4096; 40 | 41 | // lets also define an interval to check for thread cancellations since we may 42 | // be blocking by busy waiting in trusted space here we're going to define this 43 | // as 2^20 which should be ~1 sec according to the standard CLOCKS_PER_SEC 44 | // definition, though it should be quite shorter on modern CPUs 45 | const CANCEL_CHECK_INTERVAL: usize = 1048576; 46 | 47 | /// # Description 48 | /// In-memory pipe struct of given size which contains references to read and 49 | /// write ends of a lock-free ringbuffer, as well as reference counters to each 50 | /// end 51 | #[derive(Clone)] 52 | pub struct EmulatedPipe { 53 | write_end: Arc>>, 54 | read_end: Arc>>, 55 | refcount_write: Arc, 56 | refcount_read: Arc, 57 | size: usize, 58 | } 59 | 60 | impl EmulatedPipe { 61 | /// # Description 62 | /// Creates an in-memory pipe object of specified size. 63 | /// The size provided is either a constant for a pipe (65,536 bytes) or a 64 | /// domain socket (212,992 bytes) 65 | /// 66 | /// # Arguments 67 | /// 68 | /// * `size` - Size of the iPC construct in bytes (either pipe or Unix 69 | /// socket) 70 | /// 71 | /// # Returns 72 | /// 73 | /// EmulatedPipe object 74 | pub fn new_with_capacity(size: usize) -> EmulatedPipe { 75 | let rb = RingBuffer::::new(size); 76 | let (prod, cons) = rb.split(); 77 | EmulatedPipe { 78 | write_end: Arc::new(Mutex::new(prod)), 79 | read_end: Arc::new(Mutex::new(cons)), 80 | refcount_write: Arc::new(AtomicU32::new(1)), 81 | refcount_read: Arc::new(AtomicU32::new(1)), 82 | size: size, 83 | } 84 | } 85 | 86 | /// # Description 87 | /// Checks the references to each end of the pipe to determine if its closed 88 | /// Necessary for determining if Unix sockets are closed for each direction 89 | /// 90 | /// # Returns 91 | /// 92 | /// True if all references are closed, false if there are open references 93 | pub fn is_pipe_closed(&self) -> bool { 94 | self.get_write_ref() + self.get_read_ref() == 0 95 | } 96 | 97 | /// Internal getter for write references 98 | /// 99 | /// Returns number of references to write end of the pipe 100 | fn get_write_ref(&self) -> u32 { 101 | self.refcount_write.load(Ordering::Relaxed) 102 | } 103 | 104 | /// Internal getter for read references 105 | /// 106 | /// Returns number of references to read end of the pipe 107 | fn get_read_ref(&self) -> u32 { 108 | self.refcount_read.load(Ordering::Relaxed) 109 | } 110 | 111 | /// # Description 112 | /// Increase references to write or read end. 113 | /// This is called when a reference to the pipe end is duplicated in cases 114 | /// such as fork() and dup/dup2(). 115 | /// 116 | /// # Arguments 117 | /// 118 | /// * `flags` - Flags set on pipe descriptor, used to determine if its the 119 | /// read or write end 120 | pub fn incr_ref(&self, flags: i32) { 121 | if (flags & O_RDWRFLAGS) == O_RDONLY { 122 | self.refcount_read.fetch_add(1, Ordering::Relaxed); 123 | } 124 | if (flags & O_RDWRFLAGS) == O_WRONLY { 125 | self.refcount_write.fetch_add(1, Ordering::Relaxed); 126 | } 127 | } 128 | 129 | /// # Description 130 | /// Decrease references to write or read end. 131 | /// This is called when a reference to the pipe end is removed in cases such 132 | /// as close(). 133 | /// 134 | /// # Arguments 135 | /// 136 | /// * `flags` - Flags set on pipe descriptor, used to determine if its the 137 | /// read or write end 138 | pub fn decr_ref(&self, flags: i32) { 139 | if (flags & O_RDWRFLAGS) == O_RDONLY { 140 | self.refcount_read.fetch_sub(1, Ordering::Relaxed); 141 | } 142 | if (flags & O_RDWRFLAGS) == O_WRONLY { 143 | self.refcount_write.fetch_sub(1, Ordering::Relaxed); 144 | } 145 | } 146 | 147 | /// # Description 148 | /// Checks if pipe is currently ready for reading, used by select/poll etc. 149 | /// A pipe descriptor is ready if there is anything in the pipe or there are 150 | /// 0 remaining write references. 151 | /// 152 | /// # Returns 153 | /// 154 | /// True if descriptor is ready for reading, false if it will block 155 | pub fn check_select_read(&self) -> bool { 156 | let read_end = self.read_end.lock(); 157 | let pipe_space = read_end.len(); 158 | 159 | return (pipe_space > 0) || self.get_write_ref() == 0; 160 | } 161 | 162 | /// # Description 163 | /// Checks if pipe is currently ready for writing, used by select/poll etc. 164 | /// A pipe descriptor is ready for writing if there is more than a page 165 | /// (4096 bytes) of room in the pipe or if there are 0 remaining read 166 | /// references. 167 | /// 168 | /// # Returns 169 | /// 170 | /// True if descriptor is ready for writing, false if it will block 171 | pub fn check_select_write(&self) -> bool { 172 | let write_end = self.write_end.lock(); 173 | let pipe_space = write_end.remaining(); 174 | 175 | // Linux considers a pipe writeable if there is at least PAGE_SIZE (PIPE_BUF) 176 | // remaining space (4096 bytes) 177 | return pipe_space > PAGE_SIZE || self.get_read_ref() == 0; 178 | } 179 | 180 | /// ### Description 181 | /// 182 | /// write_to_pipe writes a specified number of bytes starting at the given 183 | /// pointer to a circular buffer. 184 | /// 185 | /// ### Arguments 186 | /// 187 | /// write_to_pipe accepts three arguments: 188 | /// * `ptr` - a pointer to the data being written. 189 | /// * `length` - the amount of bytes to attempt to write 190 | /// * `nonblocking` - if this attempt to write is nonblocking 191 | /// 192 | /// ### Returns 193 | /// 194 | /// Upon successful completion, the amount of bytes written is returned. 195 | /// In case of a failure, an error is returned to the calling syscall. 196 | /// 197 | /// ### Errors 198 | /// 199 | /// * `EAGAIN` - Non-blocking is enabled and the write has failed to fully 200 | /// complete. 201 | /// * `EPIPE` - An attempt to write to a pipe with all read references have 202 | /// been closed. 203 | /// 204 | /// ### Panics 205 | /// 206 | /// A panic occurs if the provided pointer is null 207 | /// 208 | /// To learn more about pipes and the write syscall 209 | /// [pipe(7)](https://man7.org/linux/man-pages/man7/pipe.7.html) 210 | /// [write(2)](https://man7.org/linux/man-pages/man2/write.2.html) 211 | pub fn write_to_pipe(&self, ptr: *const u8, length: usize, nonblocking: bool) -> i32 { 212 | // unlikely but if we attempt to write nothing, return 0 213 | if length == 0 { 214 | return 0; 215 | } 216 | 217 | // convert the raw pointer into a slice to interface with the circular buffer 218 | let buf = unsafe { 219 | assert!(!ptr.is_null()); 220 | slice::from_raw_parts(ptr, length) 221 | }; 222 | 223 | let mut write_end = self.write_end.lock(); 224 | let mut bytes_written = 0; 225 | 226 | // Here we attempt to write the data to the pipe, looping until all bytes are 227 | // written or in non-blocking scenarios error with EAGAIN 228 | // 229 | // Here are the four different scenarios we encounter (via the pipe(7) manpage): 230 | // 231 | // O_NONBLOCK disabled, n <= PAGE_SIZE 232 | // All n bytes are written, write may block if 233 | // there is not room for n bytes to be written immediately 234 | 235 | // O_NONBLOCK enabled, n <= PAGE_SIZE 236 | // If there is room to write n bytes to the pipe, then 237 | // write succeeds immediately, writing all n bytes; 238 | // otherwise write fails, with errno set to EAGAIN. 239 | 240 | // O_NONBLOCK disabled, n > PAGE_SIZE 241 | // The write blocks until n bytes have been written. 242 | // Because Linux implements pipes as a buffer of pages, we need to wait until 243 | // a page worth of bytes is free in our buffer until we can write 244 | 245 | // O_NONBLOCK enabled, n > PAGE_SIZE 246 | // If the pipe is full, then write fails, with errno set to EAGAIN. 247 | // Otherwise, a "partial write" may occur returning the number of bytes written 248 | 249 | while bytes_written < length { 250 | if self.get_read_ref() == 0 { 251 | // we send EPIPE here since all read ends are closed 252 | return syscall_error(Errno::EPIPE, "write", "broken pipe"); 253 | } 254 | 255 | let remaining = write_end.remaining(); 256 | 257 | // we loop until either more than a page of bytes is free in the pipe 258 | // Linux technically writes a page per iteration here but its more efficient and 259 | // should be semantically equivalent to write more for why we wait 260 | if remaining < PAGE_SIZE { 261 | if nonblocking { 262 | // for non-blocking if we have written a bit lets return how much we've written, 263 | // otherwise we return EAGAIN 264 | if bytes_written > 0 { 265 | return bytes_written as i32; 266 | } else { 267 | return syscall_error( 268 | Errno::EAGAIN, 269 | "write", 270 | "there is no space available right now, try again later", 271 | ); 272 | } 273 | } else { 274 | // we yield here on a non-writable pipe to let other threads continue more 275 | // quickly 276 | interface::lind_yield(); 277 | continue; 278 | } 279 | }; 280 | 281 | // lets read the minimum of the specified amount or whatever space we have 282 | let bytes_to_write = min(length, bytes_written as usize + remaining); 283 | write_end.push_slice(&buf[bytes_written..bytes_to_write]); 284 | bytes_written = bytes_to_write; 285 | } 286 | 287 | // lets return the amount we've written to the pipe 288 | bytes_written as i32 289 | } 290 | 291 | /// ### Description 292 | /// 293 | /// `write_vectored_to_pipe` writes data from a set of iovec buffers to a 294 | /// pipe, handling multiple iovec structures in a single operation. This 295 | /// function ensures the complete writing of data from the provided 296 | /// buffers or returns an error code in case of failure. 297 | /// 298 | /// ### Arguments 299 | /// 300 | /// * `ptr` - A pointer to an array of `IovecStruct` which contains the 301 | /// buffers (iovecs) that will be written to the pipe. 302 | /// * `iovcnt` - The number of `IovecStruct` buffers to be written. 303 | /// * `nonblocking` - A boolean flag indicating whether the write operation 304 | /// should be non-blocking. 305 | /// 306 | /// ### Returns 307 | /// 308 | /// Upon success, the function returns the total number of bytes written. If 309 | /// no bytes could be written due to an empty pipe (EAGAIN), it returns 310 | /// an error code. For other error conditions, such as a broken pipe, an 311 | /// appropriate error code is returned. 312 | /// 313 | /// ### Errors 314 | /// 315 | /// * `EPIPE` - Indicates a broken pipe error, where the pipe no longer has 316 | /// an open reading end. 317 | /// * `EAGAIN` - If the pipe is non-blocking and cannot accept data at the 318 | /// moment, this error is returned. If no data was written at all, 319 | /// `EAGAIN` is returned immediately. 320 | /// 321 | /// ### Panics 322 | /// 323 | /// A panic occurs if the provided `ptr` is null when dereferencing the 324 | /// iovecs. 325 | /// 326 | /// To learn more about pipes and the writev syscall 327 | /// [pipe(7)](https://man7.org/linux/man-pages/man7/pipe.7.html) 328 | /// [pipe(7)](https://man7.org/linux/man-pages/man2/writev.2.html) 329 | pub fn write_vectored_to_pipe( 330 | &self, 331 | ptr: *const interface::IovecStruct, 332 | iovcnt: i32, 333 | nonblocking: bool, 334 | ) -> i32 { 335 | // Return 0 if there are no iovecs to write. 336 | if iovcnt == 0 { 337 | return 0; 338 | } 339 | 340 | // Unsafe block to access the iovecs. 341 | let iovecs = unsafe { slice::from_raw_parts(ptr, iovcnt as usize) }; 342 | let mut total_bytes_written = 0; 343 | 344 | // Iterate through each iovec. 345 | for iovec in iovecs { 346 | let buf = unsafe { slice::from_raw_parts(iovec.iov_base as *const u8, iovec.iov_len) }; 347 | 348 | // Write the buffer to the pipe. 349 | let current_write = self.write_to_pipe(buf.as_ptr(), buf.len(), nonblocking); 350 | 351 | // Handle successful write. 352 | if current_write > 0 { 353 | total_bytes_written += current_write as usize; 354 | } else { 355 | // Nested error handling. 356 | if current_write == -(Errno::EPIPE as i32) { 357 | // Pipe is broken, return immediately. 358 | return -(Errno::EPIPE as i32); 359 | } else if current_write == -(Errno::EAGAIN as i32) { 360 | // Handling EAGAIN depending on whether data was previously written. 361 | if total_bytes_written == 0 { 362 | return -(Errno::EAGAIN as i32); 363 | // No data written yet,return EAGAIN. 364 | } else { 365 | return total_bytes_written as i32; 366 | // Return amount of data written before EAGAIN occurred. 367 | } 368 | } 369 | } 370 | } 371 | // Return the total number of bytes written. 372 | total_bytes_written as i32 373 | } 374 | 375 | /// ### Description 376 | /// 377 | /// read_from_pipe reads a specified number of bytes from the circular 378 | /// buffer to a given pointer. 379 | /// 380 | /// ### Arguments 381 | /// 382 | /// write_to_pipe accepts three arguments: 383 | /// * `ptr` - a pointer to the buffer being read to. 384 | /// * `length` - the amount of bytes to attempt to read 385 | /// * `nonblocking` - if this attempt to read is nonblocking 386 | /// 387 | /// ### Returns 388 | /// 389 | /// Upon successful completion, the amount of bytes read is returned. 390 | /// In case of a failure, an error is returned to the calling syscall. 391 | /// 392 | /// ### Errors 393 | /// 394 | /// * `EAGAIN` - A non-blocking read is attempted without EOF being reached 395 | /// but there is no data in the pipe. 396 | /// 397 | /// ### Panics 398 | /// 399 | /// A panic occurs if the provided pointer is null 400 | /// 401 | /// To learn more about pipes and the read syscall 402 | /// [read(2))](https://man7.org/linux/man-pages/man2/read.2.html) 403 | pub fn read_from_pipe(&self, ptr: *mut u8, length: usize, nonblocking: bool) -> i32 { 404 | // unlikely but if we attempt to read nothing, return 0 405 | if length == 0 { 406 | return 0; 407 | } 408 | 409 | // convert the raw pointer into a slice to interface with the circular buffer 410 | let buf = unsafe { 411 | assert!(!ptr.is_null()); 412 | slice::from_raw_parts_mut(ptr, length) 413 | }; 414 | 415 | let mut read_end = self.read_end.lock(); 416 | let mut pipe_space = read_end.len(); 417 | if nonblocking && (pipe_space == 0) { 418 | // if the descriptor is non-blocking and theres nothing in the pipe we either: 419 | // return 0 if the EOF is reached (zero write references) 420 | // or return EAGAIN due to the O_NONBLOCK flag 421 | if self.get_write_ref() == 0 { 422 | return 0; 423 | } 424 | return syscall_error( 425 | Errno::EAGAIN, 426 | "read", 427 | "there is no data available right now, try again later", 428 | ); 429 | } 430 | 431 | // wait for something to be in the pipe, but break on eof 432 | let mut count = 0; 433 | while pipe_space == 0 { 434 | // If write references are 0, we've reached EOF so return 0 435 | if self.get_write_ref() == 0 { 436 | return 0; 437 | } 438 | 439 | // we return EAGAIN here so we can go back to check if this cage has been sent a 440 | // cancel notification in the calling syscall if the calling 441 | // descriptor is blocking we then attempt to read again 442 | if count == CANCEL_CHECK_INTERVAL { 443 | return -(Errno::EAGAIN as i32); 444 | } 445 | 446 | // lets check again if were empty 447 | pipe_space = read_end.len(); 448 | count = count + 1; 449 | 450 | if pipe_space == 0 { 451 | // we yield here on an empty pipe to let other threads continue more quickly 452 | interface::lind_yield(); 453 | } 454 | } 455 | 456 | // we've found something int the pipe 457 | // lets read the minimum of the specified amount or whatever is in the pipe 458 | let bytes_to_read = min(length, pipe_space); 459 | read_end.pop_slice(&mut buf[0..bytes_to_read]); 460 | 461 | // return the amount we read 462 | bytes_to_read as i32 463 | } 464 | } 465 | 466 | impl fmt::Debug for EmulatedPipe { 467 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 468 | f.debug_struct("EmulatedPipe") 469 | .field("refcount read", &self.refcount_read) 470 | .field("refcount write", &self.refcount_write) 471 | .finish() 472 | } 473 | } 474 | -------------------------------------------------------------------------------- /src/interface/timer.rs: -------------------------------------------------------------------------------- 1 | // Author: Nicholas Renner 2 | // 3 | // Timer functions for Rust interface. 4 | #![allow(dead_code)] 5 | 6 | use std::sync::{Arc, Mutex, MutexGuard}; 7 | use std::thread; 8 | pub use std::time::Duration as RustDuration; 9 | pub use std::time::Instant as RustInstant; 10 | use std::time::SystemTime; 11 | 12 | use crate::interface::lind_kill_from_id; 13 | 14 | pub fn timestamp() -> u64 { 15 | SystemTime::now() 16 | .duration_since(SystemTime::UNIX_EPOCH) 17 | .unwrap() 18 | .as_secs() 19 | } 20 | 21 | // Create a new timer 22 | pub fn starttimer() -> RustInstant { 23 | RustInstant::now() 24 | } 25 | 26 | // Return time since timer was started 27 | pub fn readtimer(now: RustInstant) -> RustDuration { 28 | now.elapsed() 29 | } 30 | 31 | // Sleep function to sleep for specified duration 32 | pub fn sleep(dur: RustDuration) { 33 | thread::sleep(dur); 34 | } 35 | 36 | #[derive(Debug)] 37 | struct _IntervalTimer { 38 | pub cageid: u64, 39 | pub init_instant: RustInstant, // The instant this process is created 40 | 41 | pub start_instant: RustInstant, 42 | pub curr_duration: RustDuration, 43 | pub next_duration: RustDuration, 44 | 45 | pub is_ticking: bool, 46 | } 47 | 48 | #[derive(Clone, Debug)] 49 | pub struct IntervalTimer { 50 | _ac: Arc>, 51 | } 52 | 53 | impl IntervalTimer { 54 | pub fn new(cageid: u64) -> Self { 55 | Self { 56 | _ac: Arc::new(Mutex::new(_IntervalTimer { 57 | cageid: cageid, 58 | init_instant: RustInstant::now(), 59 | start_instant: RustInstant::now(), 60 | curr_duration: RustDuration::ZERO, 61 | next_duration: RustDuration::ZERO, 62 | is_ticking: false, 63 | })), 64 | } 65 | } 66 | 67 | // Similar to getitimer. Returns (current value, next value) 68 | pub fn get_itimer(&self) -> (RustDuration, RustDuration) { 69 | let guard = self._ac.lock().unwrap(); 70 | 71 | (guard.curr_duration, guard.next_duration) 72 | } 73 | 74 | fn _set_itimer( 75 | &self, 76 | guard: &mut MutexGuard<_IntervalTimer>, 77 | curr_duration: RustDuration, 78 | next_duration: RustDuration, 79 | ) { 80 | if curr_duration.is_zero() { 81 | guard.is_ticking = false; 82 | } else { 83 | guard.start_instant = RustInstant::now(); 84 | guard.curr_duration = curr_duration; 85 | guard.next_duration = next_duration; 86 | 87 | if !guard.is_ticking { 88 | guard.is_ticking = true; 89 | 90 | let self_dup = self.clone(); 91 | thread::spawn(move || { 92 | // There is a chance that there'll be two ticking threads running 93 | // at the same time 94 | self_dup.tick(); 95 | }); 96 | } 97 | } 98 | } 99 | 100 | pub fn set_itimer(&self, curr_duration: RustDuration, next_duration: RustDuration) { 101 | let mut guard = self._ac.lock().unwrap(); 102 | self._set_itimer(&mut guard, curr_duration, next_duration); 103 | } 104 | 105 | pub fn tick(&self) { 106 | loop { 107 | { 108 | let mut guard = self._ac.lock().unwrap(); 109 | 110 | if guard.is_ticking { 111 | let remaining_seconds = guard 112 | .curr_duration 113 | .saturating_sub(guard.start_instant.elapsed()); 114 | 115 | if remaining_seconds == RustDuration::ZERO { 116 | lind_kill_from_id(guard.cageid, 14); 117 | 118 | let new_curr_duration = guard.next_duration; 119 | // Repeat the intervals until user cancel it 120 | let new_next_duration = guard.next_duration; 121 | 122 | self._set_itimer(&mut guard, new_curr_duration, new_next_duration); 123 | // Calling self.set_itimer will automatically turn of 124 | // the timer if next_duration is 125 | // ZERO 126 | } 127 | } else { 128 | break; 129 | } 130 | } 131 | 132 | thread::sleep(RustDuration::from_millis(20)); // One jiffy 133 | } 134 | } 135 | 136 | pub fn clone_with_new_cageid(&self, cageid: u64) -> Self { 137 | let mut guard = self._ac.lock().unwrap(); 138 | guard.cageid = cageid; 139 | 140 | self.clone() 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(rustc_private)] //for private crate imports for tests 2 | #![feature(vec_into_raw_parts)] 3 | #![feature(thread_local)] 4 | #![feature(unix_file_vectored_at)] 5 | #![allow(unused_imports)] 6 | 7 | //! # RustPOSIX 8 | //! Welcome to the RustPOSIX microvisor. 9 | //! This serves as the OS-in-a-process in the [Lind architecture](https://github.com/Lind-Project/lind-docs/tree/main). 10 | //! ## Overview 11 | //! 12 | //! A microvisor is a piece of software that lives within the same process as 13 | //! the cages. It provides a POSIX interface, allowing cages to believe they 14 | //! have their own operating system. 15 | //! 16 | //! With this crate, you can: 17 | //! - Fork and execute other cages. 18 | //! - Read and write files on disk. 19 | //! - Communicate over the network. 20 | //! - Manage shared memory. 21 | //! 22 | //! Note this is a re-design of the original (now obsolete) [SafePOSIX](https://github.com/Lind-Project/nacl_repy), which was created using the [RepyV2](https://github.com/SeattleTestbed/docs/blob/master/Programming/RepyV2Tutorial.md) sandbox. 23 | 24 | // interface and safeposix are public because otherwise there isn't a great 25 | // way to 'use' them for benchmarking. 26 | pub mod interface; 27 | mod lib_fs_utils; 28 | pub mod safeposix; 29 | pub mod tests; 30 | -------------------------------------------------------------------------------- /src/lib_fs_utils.rs: -------------------------------------------------------------------------------- 1 | tools/lib_fs_utils.rs -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(rustc_private)] //for private crate imports for tests 2 | #![feature(vec_into_raw_parts)] 3 | #![feature(duration_constants)] 4 | #![feature(unix_file_vectored_at)] 5 | #![allow(unused_imports)] 6 | 7 | mod interface; 8 | mod lib_fs_utils; 9 | mod safeposix; 10 | mod tests; 11 | 12 | fn main() { 13 | println!("Hello, world!"); 14 | } 15 | -------------------------------------------------------------------------------- /src/safeposix/cage.rs: -------------------------------------------------------------------------------- 1 | //! This module handles the `Cage` struct which represents an isolated execution 2 | //! context. manages isolated environments with IDs, directories, and file 3 | //! descriptors, handling filesystem, system, and network calls. 4 | //! 5 | //! ## Cage Objects 6 | //! 7 | //! Cage objects represent isolated execution contexts and contain the following 8 | //! components: 9 | //! 10 | //! - Cage ID: An integer uniquely identifying the cage. 11 | //! - Current Working Directory: A string representing the current working 12 | //! directory of the cage. 13 | //! - Parent ID: An integer representing the ID of the parent cage. 14 | //! - File Descriptor Table: A locked hash map mapping integers to descriptor 15 | //! enums. 16 | //! 17 | //! File Descriptor Table: 18 | //! The file descriptor table maps file descriptor integers to their respective 19 | //! representations. These descriptors are implemented as an enum with the 20 | //! following types: 21 | //! - File 22 | //! - Stream 23 | //! - Socket 24 | //! - Pipe 25 | //! - Epoll 26 | //! 27 | //! Each descriptor type is a struct with specific fields, detailed in cage.rs. 28 | //! 29 | //! System Calls: 30 | //! - Cage objects provide public methods for various system calls, categorized 31 | //! into filesystem-related, system-related, or network-related calls. Each 32 | //! system call method returns either a return code or an error code from the 33 | //! errno enum. 34 | 35 | #![allow(dead_code)] 36 | use crate::interface; 37 | //going to get the datatypes and errnos from the cage file from now on 38 | pub use crate::interface::errnos::{syscall_error, Errno}; 39 | pub use crate::interface::types::{ 40 | Arg, EpollEvent, FSData, IoctlPtrUnion, PipeArray, PollStruct, Rlimit, ShmidsStruct, StatData, 41 | }; 42 | 43 | use super::filesystem::normpath; 44 | use super::net::SocketHandle; 45 | pub use super::syscalls::fs_constants::*; 46 | pub use super::syscalls::net_constants::*; 47 | pub use super::syscalls::sys_constants::*; 48 | 49 | pub use crate::interface::CAGE_TABLE; 50 | 51 | #[derive(Debug, Clone)] 52 | pub enum FileDescriptor { 53 | File(FileDesc), 54 | Stream(StreamDesc), 55 | Socket(SocketDesc), 56 | Pipe(PipeDesc), 57 | Epoll(EpollDesc), 58 | } 59 | 60 | #[derive(Debug, Clone)] 61 | pub struct FileDesc { 62 | pub position: usize, 63 | pub inode: usize, 64 | pub flags: i32, 65 | pub advlock: interface::RustRfc, 66 | } 67 | 68 | #[derive(Debug, Clone)] 69 | pub struct StreamDesc { 70 | pub position: usize, 71 | pub stream: i32, //0 for stdin, 1 for stdout, 2 for stderr 72 | pub flags: i32, 73 | pub advlock: interface::RustRfc, 74 | } 75 | 76 | #[derive(Debug, Clone)] 77 | pub struct SocketDesc { 78 | pub flags: i32, 79 | pub domain: i32, 80 | pub rawfd: i32, 81 | pub handle: interface::RustRfc>, 82 | pub advlock: interface::RustRfc, 83 | } 84 | 85 | #[derive(Debug, Clone)] 86 | pub struct PipeDesc { 87 | pub pipe: interface::RustRfc, 88 | pub flags: i32, 89 | pub advlock: interface::RustRfc, 90 | } 91 | 92 | #[derive(Debug, Clone)] 93 | pub struct EpollDesc { 94 | pub mode: i32, 95 | pub registered_fds: interface::RustHashMap, 96 | pub advlock: interface::RustRfc, 97 | pub errno: i32, 98 | pub flags: i32, 99 | } 100 | 101 | pub type FdTable = Vec>>>; 102 | 103 | #[derive(Debug)] 104 | pub struct Cage { 105 | pub cageid: u64, 106 | pub cwd: interface::RustLock>, 107 | pub parent: u64, 108 | pub filedescriptortable: FdTable, 109 | pub cancelstatus: interface::RustAtomicBool, 110 | pub getgid: interface::RustAtomicI32, 111 | pub getuid: interface::RustAtomicI32, 112 | pub getegid: interface::RustAtomicI32, 113 | pub geteuid: interface::RustAtomicI32, 114 | pub rev_shm: interface::Mutex>, //maps addr within cage to shmid 115 | pub mutex_table: interface::RustLock>>>, 116 | pub cv_table: interface::RustLock>>>, 117 | pub sem_table: interface::RustHashMap>, 118 | pub thread_table: interface::RustHashMap, 119 | pub signalhandler: interface::RustHashMap, 120 | pub sigset: interface::RustHashMap, 121 | pub pendingsigset: interface::RustHashMap, 122 | pub main_threadid: interface::RustAtomicU64, 123 | pub interval_timer: interface::IntervalTimer, 124 | } 125 | 126 | impl Cage { 127 | pub fn get_next_fd( 128 | &self, 129 | startfd: Option, 130 | ) -> ( 131 | i32, 132 | Option>>, 133 | ) { 134 | let start = match startfd { 135 | Some(startfd) => startfd, 136 | None => STARTINGFD, 137 | }; 138 | 139 | // let's get the next available fd number. The standard says we need to return 140 | // the lowest open fd number. 141 | for fd in start..MAXFD { 142 | let fdguard = self.filedescriptortable[fd as usize].try_write(); 143 | if let Some(ref fdopt) = fdguard { 144 | // we grab the lock here and if there is no occupied cage, we return the fdno 145 | // and guard while keeping the fd slot locked 146 | if fdopt.is_none() { 147 | return (fd, fdguard); 148 | } 149 | } 150 | } 151 | return ( 152 | syscall_error( 153 | Errno::ENFILE, 154 | "get_next_fd", 155 | "no available file descriptor number could be found", 156 | ), 157 | None, 158 | ); 159 | } 160 | 161 | pub fn changedir(&self, newdir: interface::RustPathBuf) { 162 | let newwd = interface::RustRfc::new(normpath(newdir, self)); 163 | let mut cwdbox = self.cwd.write(); 164 | *cwdbox = newwd; 165 | } 166 | 167 | // function to signal all cvs in a cage when forcing exit 168 | pub fn signalcvs(&self) { 169 | let cvtable = self.cv_table.read(); 170 | 171 | for cv_handle in 0..cvtable.len() { 172 | if cvtable[cv_handle as usize].is_some() { 173 | let clonedcv = cvtable[cv_handle as usize].as_ref().unwrap().clone(); 174 | clonedcv.broadcast(); 175 | } 176 | } 177 | } 178 | 179 | pub fn send_pending_signals(&self, sigset: interface::SigsetType, pthreadid: u64) { 180 | for signo in 1..SIGNAL_MAX { 181 | if interface::lind_sigismember(sigset, signo) { 182 | interface::lind_threadkill(pthreadid, signo); 183 | } 184 | } 185 | } 186 | 187 | pub fn get_filedescriptor( 188 | &self, 189 | fd: i32, 190 | ) -> Result>>, ()> { 191 | if (fd < 0) || (fd >= MAXFD) { 192 | Err(()) 193 | } else { 194 | Ok(self.filedescriptortable[fd as usize].clone()) 195 | } 196 | } 197 | } 198 | 199 | pub fn init_fdtable() -> FdTable { 200 | let mut fdtable = Vec::new(); 201 | // load lower handle stubs 202 | let stdin = interface::RustRfc::new(interface::RustLock::new(Some(FileDescriptor::Stream( 203 | StreamDesc { 204 | position: 0, 205 | stream: 0, 206 | flags: O_RDONLY, 207 | advlock: interface::RustRfc::new(interface::AdvisoryLock::new()), 208 | }, 209 | )))); 210 | let stdout = interface::RustRfc::new(interface::RustLock::new(Some(FileDescriptor::Stream( 211 | StreamDesc { 212 | position: 0, 213 | stream: 1, 214 | flags: O_WRONLY, 215 | advlock: interface::RustRfc::new(interface::AdvisoryLock::new()), 216 | }, 217 | )))); 218 | let stderr = interface::RustRfc::new(interface::RustLock::new(Some(FileDescriptor::Stream( 219 | StreamDesc { 220 | position: 0, 221 | stream: 2, 222 | flags: O_WRONLY, 223 | advlock: interface::RustRfc::new(interface::AdvisoryLock::new()), 224 | }, 225 | )))); 226 | fdtable.push(stdin); 227 | fdtable.push(stdout); 228 | fdtable.push(stderr); 229 | 230 | for _fd in 3..MAXFD as usize { 231 | fdtable.push(interface::RustRfc::new(interface::RustLock::new(None))); 232 | } 233 | fdtable 234 | } 235 | 236 | pub fn create_unix_sockpipes() -> ( 237 | interface::RustRfc, 238 | interface::RustRfc, 239 | ) { 240 | let pipe1 = 241 | interface::RustRfc::new(interface::EmulatedPipe::new_with_capacity(UDSOCK_CAPACITY)); 242 | let pipe2 = 243 | interface::RustRfc::new(interface::EmulatedPipe::new_with_capacity(UDSOCK_CAPACITY)); 244 | 245 | (pipe1, pipe2) 246 | } 247 | -------------------------------------------------------------------------------- /src/safeposix/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module handles system call requests from the Native Client in the 2 | //! RustPOSIX environment. 3 | //! 4 | //! ## top-level features: 5 | //! 6 | //! - ### Dispatcher/RPC: 7 | //! - The dispatcher receives system call requests from Native Client. It 8 | //! checks if the cage exists in the cage table, and if it doesn't, 9 | //! initializes a new cage. It then takes the cage object corresponding to 10 | //! that ID number, and calls the method corresponding to the sent call 11 | //! number. 12 | //! 13 | //! - ### Cage Objects: 14 | //! 15 | //! - Each cage object has a Cage ID, Current Working Directory, Parent ID, 16 | //! and a File Descriptor Table. 17 | //! 18 | //! - ### File Descriptor Table: 19 | //! - The file descriptor table is a hash map of file descriptor integers to 20 | //! our file descriptor representations. File Descriptors are implemented 21 | //! as an Enum that can correspond to five descriptor types (File, Stream, 22 | //! Socket, Pipe, Epoll). 23 | //! 24 | //! - ### System Calls: 25 | //! - Each cage object has public methods corresponding to each system call. 26 | //! These calls are implemented either as filesystem related calls, system 27 | //! related calls, or network related calls in their respective files. 28 | //! 29 | //! - ### FS Metadata: 30 | //! - The table is represented by a struct with fields: nextinode, dev_ud, 31 | //! inodetable. The Inode Enum can describe a variety of Inode structs 32 | //! which include: File(generic), CharDev, Socket, Directory. 33 | //! 34 | //! - ### Public Methods: 35 | //! - The module provides several public methods for interacting with the 36 | //! file descriptor table and the cage objects. Some of them are 37 | //! get_next_fd, load_lower_handle_stubs, insert_next_pipe. There are also 38 | //! some unused methods like add_to_fd_table, rm_from_fd_table, and 39 | //! changedir. 40 | 41 | pub mod cage; 42 | pub mod dispatcher; 43 | pub mod filesystem; 44 | pub mod net; 45 | pub mod shm; 46 | pub mod syscalls; 47 | -------------------------------------------------------------------------------- /src/safeposix/net.rs: -------------------------------------------------------------------------------- 1 | use super::cage::{Cage, FileDescriptor}; 2 | use super::syscalls::net_constants::*; 3 | use crate::interface; 4 | use crate::interface::errnos::{syscall_error, Errno}; 5 | 6 | //Because other processes on the OS may allocate ephemeral ports, we allocate 7 | // them from high to low whereas the OS allocates them from low to high 8 | //Additionally, we can't tell whether a port is truly rebindable, this is 9 | // because even when a port is closed sometimes there still is cleanup that the 10 | // OS needs to do (for ephemeral ports which end up in the TIME_WAIT state). 11 | // Therefore, we will assign ephemeral ports rather than simply from the highest 12 | // available one, in a cyclic fashion skipping over unavailable ports. While 13 | // this still may cause issues if specific port adresses in the ephemeral port 14 | // range are allocated and closed before an ephemeral port would be bound there, 15 | // it is much less likely that this will happen and is easy to avoid and 16 | // nonstandard in user programs. See the code for _get_available_udp_port and 17 | // its tcp counterpart for the implementation details. 18 | const EPHEMERAL_PORT_RANGE_START: u16 = 32768; //sane default on linux 19 | const EPHEMERAL_PORT_RANGE_END: u16 = 60999; 20 | pub const TCPPORT: bool = true; 21 | pub const UDPPORT: bool = false; 22 | 23 | pub static NET_METADATA: interface::RustLazyGlobal> = 24 | interface::RustLazyGlobal::new(|| { 25 | interface::RustRfc::new(NetMetadata { 26 | used_port_set: interface::RustHashMap::new(), 27 | next_ephemeral_port_tcpv4: interface::RustRfc::new(interface::RustLock::new( 28 | EPHEMERAL_PORT_RANGE_END, 29 | )), 30 | next_ephemeral_port_udpv4: interface::RustRfc::new(interface::RustLock::new( 31 | EPHEMERAL_PORT_RANGE_END, 32 | )), 33 | next_ephemeral_port_tcpv6: interface::RustRfc::new(interface::RustLock::new( 34 | EPHEMERAL_PORT_RANGE_END, 35 | )), 36 | next_ephemeral_port_udpv6: interface::RustRfc::new(interface::RustLock::new( 37 | EPHEMERAL_PORT_RANGE_END, 38 | )), 39 | listening_port_set: interface::RustHashSet::new(), 40 | pending_conn_table: interface::RustHashMap::new(), 41 | domsock_accept_table: interface::RustHashMap::new(), /* manages domain socket 42 | * connection process */ 43 | domsock_paths: interface::RustHashSet::new(), /* set of all currently bound domain 44 | * sockets */ 45 | }) 46 | }); //we want to check if fs exists before doing a blank init, but not for now 47 | 48 | //A list of all network devices present on the machine 49 | //It is populated from a file that should be present prior to running 50 | // rustposix, see the implementation of read_netdevs for specifics 51 | pub static NET_IFADDRS_STR: interface::RustLazyGlobal = 52 | interface::RustLazyGlobal::new(|| interface::getifaddrs_from_file()); 53 | 54 | pub static NET_DEVICE_IPLIST: interface::RustLazyGlobal> = 55 | interface::RustLazyGlobal::new(|| ips_from_ifaddrs()); 56 | 57 | fn ips_from_ifaddrs() -> Vec { 58 | let mut ips = vec![]; 59 | for net_device in NET_IFADDRS_STR.as_str().split('\n') { 60 | if net_device == "" { 61 | continue; 62 | } 63 | let ifaddrstr: Vec<&str> = net_device.split(' ').collect(); 64 | let genipopt = interface::GenIpaddr::from_string(ifaddrstr[2]); 65 | ips.push(genipopt.expect("Could not parse device ip address from net_devices file")); 66 | } 67 | 68 | let genipopt0 = interface::GenIpaddr::from_string("0.0.0.0"); 69 | ips.push(genipopt0.expect("Could not parse device ip address from net_devices file")); 70 | return ips; 71 | } 72 | 73 | #[derive(Debug, Hash, Eq, PartialEq, Clone)] 74 | pub enum PortType { 75 | IPv4UDP, 76 | IPv4TCP, 77 | IPv6UDP, 78 | IPv6TCP, 79 | } 80 | 81 | pub fn mux_port( 82 | addr: interface::GenIpaddr, 83 | port: u16, 84 | domain: i32, 85 | istcp: bool, 86 | ) -> (interface::GenIpaddr, u16, PortType) { 87 | match domain { 88 | PF_INET => ( 89 | addr, 90 | port, 91 | if istcp { 92 | PortType::IPv4TCP 93 | } else { 94 | PortType::IPv4UDP 95 | }, 96 | ), 97 | PF_INET6 => ( 98 | addr, 99 | port, 100 | if istcp { 101 | PortType::IPv6TCP 102 | } else { 103 | PortType::IPv6UDP 104 | }, 105 | ), 106 | _ => panic!("How did you manage to set an unsupported domain on the socket?"), 107 | } 108 | } 109 | 110 | //A substructure for information only populated in a unix domain socket 111 | #[derive(Debug)] 112 | pub struct UnixSocketInfo { 113 | pub mode: i32, 114 | pub sendpipe: Option>, 115 | pub receivepipe: Option>, 116 | pub inode: usize, 117 | } 118 | 119 | //This structure contains all socket-associated data that is not held in the fd 120 | #[derive(Debug)] 121 | pub struct SocketHandle { 122 | pub innersocket: Option, 123 | pub socket_options: i32, 124 | pub tcp_options: i32, 125 | pub state: ConnState, 126 | pub protocol: i32, 127 | pub domain: i32, 128 | pub last_peek: interface::RustDeque, 129 | pub localaddr: Option, 130 | pub remoteaddr: Option, 131 | pub unix_info: Option, 132 | pub socktype: i32, 133 | pub sndbuf: i32, 134 | pub rcvbuf: i32, 135 | pub errno: i32, 136 | } 137 | 138 | //This cleanup-on-drop strategy is used in lieu of manual refcounting in order 139 | // to allow the close syscall not to have to wait to increase the refcnt 140 | // manually in case for example it is in a blocking recv. This clean-on-drop 141 | // strategy is made possible by the fact that file descriptors hold reference to 142 | // a SocketHandle via an Arc, so only when the last reference to a SocketHandle 143 | // is gone--that is when the last cage has closed it--do we actually attempt to 144 | // shut down the inner socket, which is what we could have done manually in 145 | // close instead. This should be both cleaner and faster, because we don't have 146 | // to wait for the recv timeout like we do in shutdown 147 | impl Drop for SocketHandle { 148 | fn drop(&mut self) { 149 | Cage::_cleanup_socket_inner_helper(self, -1, false); 150 | } 151 | } 152 | 153 | #[derive(Debug)] 154 | pub struct ConnCondVar { 155 | lock: interface::RustRfc>, 156 | cv: interface::Condvar, 157 | } 158 | 159 | impl ConnCondVar { 160 | pub fn new() -> Self { 161 | Self { 162 | lock: interface::RustRfc::new(interface::Mutex::new(0)), 163 | cv: interface::Condvar::new(), 164 | } 165 | } 166 | 167 | pub fn wait(&self) { 168 | let mut guard = self.lock.lock(); 169 | *guard += 1; 170 | self.cv.wait(&mut guard); 171 | } 172 | 173 | pub fn broadcast(&self) -> bool { 174 | let guard = self.lock.lock(); 175 | if *guard == 1 { 176 | self.cv.notify_all(); 177 | return true; 178 | } else { 179 | return false; 180 | } 181 | } 182 | } 183 | 184 | pub struct DomsockTableEntry { 185 | pub sockaddr: interface::GenSockaddr, 186 | pub receive_pipe: interface::RustRfc, 187 | pub send_pipe: interface::RustRfc, 188 | pub cond_var: Option>, 189 | } 190 | 191 | impl DomsockTableEntry { 192 | pub fn get_cond_var(&self) -> Option<&interface::RustRfc> { 193 | self.cond_var.as_ref() 194 | } 195 | pub fn get_sockaddr(&self) -> &interface::GenSockaddr { 196 | &self.sockaddr 197 | } 198 | pub fn get_send_pipe(&self) -> &interface::RustRfc { 199 | &self.send_pipe 200 | } 201 | pub fn get_receive_pipe(&self) -> &interface::RustRfc { 202 | &self.receive_pipe 203 | } 204 | } 205 | 206 | pub struct NetMetadata { 207 | pub used_port_set: interface::RustHashMap<(u16, PortType), Vec<(interface::GenIpaddr, u32)>>, /* maps port tuple to whether rebinding is allowed: 0 means there's a user but rebinding is not allowed, positive number means that many users, rebinding is allowed */ 208 | next_ephemeral_port_tcpv4: interface::RustRfc>, 209 | next_ephemeral_port_udpv4: interface::RustRfc>, 210 | next_ephemeral_port_tcpv6: interface::RustRfc>, 211 | next_ephemeral_port_udpv6: interface::RustRfc>, 212 | pub listening_port_set: interface::RustHashSet<(interface::GenIpaddr, u16, PortType)>, 213 | pub pending_conn_table: interface::RustHashMap< 214 | (interface::GenIpaddr, u16, PortType), 215 | Vec<(Result, interface::GenSockaddr)>, 216 | >, 217 | pub domsock_accept_table: interface::RustHashMap, 218 | pub domsock_paths: interface::RustHashSet, 219 | } 220 | 221 | impl NetMetadata { 222 | fn initialize_port( 223 | &self, 224 | tup: &(interface::GenIpaddr, u16, PortType), 225 | rebindability: u32, 226 | ) -> bool { 227 | let used_port_tup = (tup.1, tup.2.clone()); 228 | if tup.0.is_unspecified() { 229 | let tupclone = used_port_tup.clone(); 230 | let entry = self.used_port_set.entry(tupclone.clone()); 231 | match entry { 232 | interface::RustHashEntry::Occupied(_) => { 233 | return false; 234 | } 235 | interface::RustHashEntry::Vacant(v) => { 236 | let mut intervec = vec![]; 237 | for interface_addr in &*NET_DEVICE_IPLIST { 238 | intervec.push((interface_addr.clone(), rebindability)); 239 | } 240 | v.insert(intervec); 241 | } 242 | } 243 | true 244 | } else { 245 | match self.used_port_set.entry(used_port_tup) { 246 | interface::RustHashEntry::Occupied(mut o) => { 247 | let addrsused = o.get_mut(); 248 | for addrtup in addrsused.clone() { 249 | if addrtup.0 == tup.0 { 250 | return false; 251 | } 252 | } 253 | addrsused.push((tup.0.clone(), rebindability)); 254 | } 255 | interface::RustHashEntry::Vacant(v) => { 256 | v.insert(vec![(tup.0.clone(), rebindability)]); 257 | } 258 | } 259 | true 260 | } 261 | } 262 | 263 | pub fn _get_available_udp_port( 264 | &self, 265 | addr: interface::GenIpaddr, 266 | domain: i32, 267 | rebindability: bool, 268 | ) -> Result { 269 | if !NET_DEVICE_IPLIST.contains(&addr) { 270 | return Err(syscall_error( 271 | Errno::EADDRNOTAVAIL, 272 | "bind", 273 | "Specified network device is not set up for lind or does not exist!", 274 | )); 275 | } 276 | let mut porttuple = mux_port(addr, 0, domain, UDPPORT); 277 | 278 | //start from the starting location we specified in a previous attempt to get an 279 | // ephemeral port 280 | let mut next_ephemeral = if domain == AF_INET { 281 | self.next_ephemeral_port_udpv4.write() 282 | } else if domain == AF_INET6 { 283 | self.next_ephemeral_port_udpv6.write() 284 | } else { 285 | unreachable!() 286 | }; 287 | for range in [ 288 | (EPHEMERAL_PORT_RANGE_START..=*next_ephemeral), 289 | (*next_ephemeral + 1..=EPHEMERAL_PORT_RANGE_END), 290 | ] { 291 | for ne_port in range.rev() { 292 | let port = ne_port.to_be(); //ports are stored in network endian order 293 | porttuple.1 = port; 294 | 295 | //if we think we can bind to this port 296 | if self.initialize_port(&porttuple, if rebindability { 1 } else { 0 }) { 297 | //rebindability of 0 means not rebindable, 1 means it's rebindable and there's 298 | // 1 bound to it 299 | *next_ephemeral -= 1; 300 | if *next_ephemeral < EPHEMERAL_PORT_RANGE_START { 301 | *next_ephemeral = EPHEMERAL_PORT_RANGE_END; 302 | } 303 | return Ok(port); 304 | } 305 | } 306 | } 307 | return Err(syscall_error( 308 | Errno::EADDRINUSE, 309 | "bind", 310 | "No available ephemeral port could be found", 311 | )); 312 | } 313 | pub fn _get_available_tcp_port( 314 | &self, 315 | addr: interface::GenIpaddr, 316 | domain: i32, 317 | rebindability: bool, 318 | ) -> Result { 319 | if !NET_DEVICE_IPLIST.contains(&addr) { 320 | return Err(syscall_error( 321 | Errno::EADDRNOTAVAIL, 322 | "bind", 323 | "Specified network device is not set up for lind or does not exist!", 324 | )); 325 | } 326 | let mut porttuple = mux_port(addr.clone(), 0, domain, TCPPORT); 327 | 328 | //start from the starting location we specified in a previous attempt to get an 329 | // ephemeral port 330 | let mut next_ephemeral = if domain == AF_INET { 331 | self.next_ephemeral_port_tcpv4.write() 332 | } else if domain == AF_INET6 { 333 | self.next_ephemeral_port_tcpv6.write() 334 | } else { 335 | unreachable!() 336 | }; 337 | for range in [ 338 | (EPHEMERAL_PORT_RANGE_START..=*next_ephemeral), 339 | (*next_ephemeral + 1..=EPHEMERAL_PORT_RANGE_END), 340 | ] { 341 | for ne_port in range.rev() { 342 | let port = ne_port.to_be(); //ports are stored in network endian order 343 | porttuple.1 = port; 344 | 345 | if self.initialize_port(&porttuple, if rebindability { 1 } else { 0 }) { 346 | //rebindability of 0 means not rebindable, 1 means it's rebindable and there's 347 | // 1 bound to it 348 | 349 | *next_ephemeral -= 1; 350 | if *next_ephemeral < EPHEMERAL_PORT_RANGE_START { 351 | *next_ephemeral = EPHEMERAL_PORT_RANGE_END; 352 | } 353 | 354 | return Ok(port); 355 | } 356 | } 357 | } 358 | return Err(syscall_error( 359 | Errno::EADDRINUSE, 360 | "bind", 361 | "No available ephemeral port could be found", 362 | )); 363 | } 364 | 365 | pub fn _reserve_localport( 366 | &self, 367 | addr: interface::GenIpaddr, 368 | port: u16, 369 | protocol: i32, 370 | domain: i32, 371 | rebindability: bool, 372 | ) -> Result { 373 | if !NET_DEVICE_IPLIST.contains(&addr) { 374 | return Err(syscall_error( 375 | Errno::EADDRNOTAVAIL, 376 | "bind", 377 | "Specified network device is not set up for lind or does not exist!", 378 | )); 379 | } 380 | 381 | let muxed; 382 | if protocol == IPPROTO_UDP { 383 | if port == 0 { 384 | return self._get_available_udp_port(addr, domain, rebindability); 385 | //assign ephemeral port 386 | } else { 387 | muxed = mux_port(addr, port, domain, UDPPORT); 388 | } 389 | } else if protocol == IPPROTO_TCP { 390 | if port == 0 { 391 | return self._get_available_tcp_port(addr, domain, rebindability); 392 | //assign ephemeral port 393 | } else { 394 | muxed = mux_port(addr, port, domain, TCPPORT); 395 | } 396 | } else { 397 | panic!("Unknown protocol was set on socket somehow"); 398 | } 399 | 400 | let usedport_muxed = (muxed.1, muxed.2); 401 | let entry = self.used_port_set.entry(usedport_muxed); 402 | if addr.is_unspecified() { 403 | match entry { 404 | interface::RustHashEntry::Occupied(_) => { 405 | return Err(syscall_error( 406 | Errno::EADDRINUSE, 407 | "reserve port", 408 | "port is already in use", 409 | )); 410 | } 411 | interface::RustHashEntry::Vacant(v) => { 412 | v.insert( 413 | NET_DEVICE_IPLIST 414 | .iter() 415 | .map(|x| (x.clone(), if rebindability { 1 } else { 0 })) 416 | .collect(), 417 | ); 418 | } 419 | } 420 | } else { 421 | match entry { 422 | interface::RustHashEntry::Occupied(mut userentry) => { 423 | for portuser in userentry.get_mut() { 424 | if portuser.0 == muxed.0 { 425 | if portuser.1 == 0 { 426 | return Err(syscall_error( 427 | Errno::EADDRINUSE, 428 | "reserve port", 429 | "port is already in use", 430 | )); 431 | } else { 432 | portuser.1 += 1; 433 | } 434 | break; 435 | } 436 | } 437 | } 438 | interface::RustHashEntry::Vacant(v) => { 439 | v.insert(vec![(muxed.0.clone(), if rebindability { 1 } else { 0 })]); 440 | } 441 | } 442 | } 443 | Ok(port) 444 | } 445 | 446 | pub fn _release_localport( 447 | &self, 448 | addr: interface::GenIpaddr, 449 | port: u16, 450 | protocol: i32, 451 | domain: i32, 452 | ) -> Result<(), i32> { 453 | if !NET_DEVICE_IPLIST.contains(&addr) { 454 | return Err(syscall_error( 455 | Errno::EADDRNOTAVAIL, 456 | "bind", 457 | "Specified network device is not set up for lind or does not exist!", 458 | )); 459 | } 460 | 461 | let muxed; 462 | if protocol == IPPROTO_TCP { 463 | muxed = mux_port(addr.clone(), port, domain, TCPPORT); 464 | } else if protocol == IPPROTO_UDP { 465 | muxed = mux_port(addr.clone(), port, domain, UDPPORT); 466 | } else { 467 | return Err(syscall_error( 468 | Errno::EINVAL, 469 | "release", 470 | "provided port has nonsensical protocol", 471 | )); 472 | } 473 | 474 | let usedport_muxed = (muxed.1, muxed.2); 475 | let entry = self.used_port_set.entry(usedport_muxed); 476 | match entry { 477 | interface::RustHashEntry::Occupied(mut userentry) => { 478 | let mut index = 0; 479 | let userarr = userentry.get_mut(); 480 | if addr.is_unspecified() { 481 | for portuser in userarr.clone() { 482 | if portuser.1 <= 1 { 483 | userarr.swap_remove(index); 484 | } else { 485 | //if it's rebindable and there are others bound to it 486 | userarr[index].1 -= 1; 487 | } 488 | } 489 | if userarr.len() == 0 { 490 | userentry.remove(); 491 | } 492 | return Ok(()); 493 | } else { 494 | for portuser in userarr.clone() { 495 | if portuser.0 == muxed.0 { 496 | //if it's rebindable and we're removing the last bound port or it's 497 | // just not rebindable 498 | if portuser.1 <= 1 { 499 | if userarr.len() == 1 { 500 | userentry.remove(); 501 | } else { 502 | userarr.swap_remove(index); 503 | } 504 | } else { 505 | //if it's rebindable and there are others bound to it 506 | userarr[index].1 -= 1; 507 | } 508 | return Ok(()); 509 | } 510 | index += 1; 511 | } 512 | unreachable!(); 513 | } 514 | } 515 | interface::RustHashEntry::Vacant(_) => { 516 | return Err(syscall_error( 517 | Errno::EINVAL, 518 | "release", 519 | "provided port is not being used", 520 | )); 521 | } 522 | } 523 | } 524 | 525 | pub fn get_domainsock_paths(&self) -> Vec { 526 | let mut domainsock_paths: Vec = vec![]; 527 | for ds_path in self.domsock_paths.iter() { 528 | domainsock_paths.push(ds_path.clone()); 529 | } // get vector of domain sock table keys 530 | domainsock_paths 531 | } 532 | } 533 | 534 | pub struct SelectInetInfo { 535 | pub rawfd_lindfd_tuples: Vec<(i32, i32)>, 536 | pub kernel_fds: interface::FdSet, 537 | pub highest_raw_fd: i32, 538 | } 539 | 540 | impl SelectInetInfo { 541 | pub fn new() -> Self { 542 | SelectInetInfo { 543 | rawfd_lindfd_tuples: Vec::new(), 544 | kernel_fds: interface::FdSet::new(), 545 | highest_raw_fd: 0, 546 | } 547 | } 548 | } 549 | 550 | pub fn update_readfds_from_kernel_select( 551 | readfds: &mut interface::FdSet, 552 | inet_info: &mut SelectInetInfo, 553 | retval: &mut i32, 554 | ) -> i32 { 555 | let kernel_ret; 556 | // note that this select call always have timeout = 0, so it doesn't block 557 | 558 | kernel_ret = interface::kernel_select( 559 | inet_info.highest_raw_fd + 1, 560 | Some(&mut inet_info.kernel_fds), 561 | None, 562 | None, 563 | ); 564 | if kernel_ret > 0 { 565 | // increment retval of our select 566 | *retval += kernel_ret; 567 | // translate the kernel checked fds to lindfds, and add to our new_writefds 568 | readfds.set_from_kernelfds_and_translate( 569 | &mut inet_info.kernel_fds, 570 | inet_info.highest_raw_fd + 1, 571 | &inet_info.rawfd_lindfd_tuples, 572 | ); 573 | } 574 | return kernel_ret; 575 | } 576 | -------------------------------------------------------------------------------- /src/safeposix/shm.rs: -------------------------------------------------------------------------------- 1 | // Filesystem metadata struct 2 | #![allow(dead_code)] 3 | 4 | use super::syscalls::fs_constants::*; 5 | use super::syscalls::sys_constants::*; 6 | use crate::interface; 7 | 8 | use super::cage::Cage; 9 | 10 | pub static SHM_METADATA: interface::RustLazyGlobal> = 11 | interface::RustLazyGlobal::new(|| interface::RustRfc::new(ShmMetadata::init_shm_metadata())); 12 | 13 | pub struct ShmSegment { 14 | pub shminfo: interface::ShmidsStruct, 15 | pub key: i32, 16 | pub size: usize, 17 | pub filebacking: interface::ShmFile, 18 | pub rmid: bool, 19 | pub attached_cages: interface::RustHashMap, /* attached cages, number of 20 | * references in cage */ 21 | pub semaphor_offsets: interface::RustHashSet, 22 | } 23 | 24 | pub fn new_shm_segment( 25 | key: i32, 26 | size: usize, 27 | cageid: u32, 28 | uid: u32, 29 | gid: u32, 30 | mode: u16, 31 | ) -> ShmSegment { 32 | ShmSegment::new(key, size, cageid, uid, gid, mode) 33 | } 34 | 35 | impl ShmSegment { 36 | pub fn new(key: i32, size: usize, cageid: u32, uid: u32, gid: u32, mode: u16) -> ShmSegment { 37 | let filebacking = interface::new_shm_backing(key, size).unwrap(); 38 | 39 | let time = interface::timestamp() as isize; //We do a real timestamp now 40 | let permstruct = interface::IpcPermStruct { 41 | __key: key, 42 | uid: uid, 43 | gid: gid, 44 | cuid: uid, 45 | cgid: gid, 46 | mode: mode, 47 | __pad1: 0, 48 | __seq: 0, 49 | __pad2: 0, 50 | __unused1: 0, 51 | __unused2: 0, 52 | }; 53 | let shminfo = interface::ShmidsStruct { 54 | shm_perm: permstruct, 55 | shm_segsz: size as u32, 56 | shm_atime: 0, 57 | shm_dtime: 0, 58 | shm_ctime: time, 59 | shm_cpid: cageid, 60 | shm_lpid: 0, 61 | shm_nattch: 0, 62 | }; 63 | 64 | ShmSegment { 65 | shminfo: shminfo, 66 | key: key, 67 | size: size, 68 | filebacking: filebacking, 69 | rmid: false, 70 | attached_cages: interface::RustHashMap::new(), 71 | semaphor_offsets: interface::RustHashSet::new(), 72 | } 73 | } 74 | // mmap shared segment into cage, and increase attachments 75 | // increase in cage references within attached_cages map 76 | pub fn map_shm(&mut self, shmaddr: *mut u8, prot: i32, cageid: u64) -> i32 { 77 | let fobjfdno = self.filebacking.as_fd_handle_raw_int(); 78 | self.shminfo.shm_nattch += 1; 79 | self.shminfo.shm_atime = interface::timestamp() as isize; 80 | 81 | match self.attached_cages.entry(cageid) { 82 | interface::RustHashEntry::Occupied(mut occupied) => { 83 | *occupied.get_mut() += 1; 84 | } 85 | interface::RustHashEntry::Vacant(vacant) => { 86 | vacant.insert(1); 87 | } 88 | }; 89 | interface::libc_mmap( 90 | shmaddr, 91 | self.size as usize, 92 | prot, 93 | MAP_SHARED | MAP_FIXED, 94 | fobjfdno, 95 | 0, 96 | ) 97 | } 98 | 99 | // unmap shared segment, decrease attachments 100 | // decrease references within attached cages map 101 | pub fn unmap_shm(&mut self, shmaddr: *mut u8, cageid: u64) { 102 | interface::libc_mmap( 103 | shmaddr, 104 | self.size as usize, 105 | PROT_NONE, 106 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 107 | -1, 108 | 0, 109 | ); 110 | self.shminfo.shm_nattch -= 1; 111 | self.shminfo.shm_dtime = interface::timestamp() as isize; 112 | match self.attached_cages.entry(cageid) { 113 | interface::RustHashEntry::Occupied(mut occupied) => { 114 | *occupied.get_mut() -= 1; 115 | if *occupied.get() == 0 { 116 | occupied.remove_entry(); 117 | } 118 | } 119 | interface::RustHashEntry::Vacant(_) => { 120 | panic!("Cage not avilable in segment attached cages"); 121 | } 122 | }; 123 | } 124 | } 125 | 126 | pub struct ShmMetadata { 127 | pub nextid: interface::RustAtomicI32, 128 | pub shmkeyidtable: interface::RustHashMap, 129 | pub shmtable: interface::RustHashMap, 130 | } 131 | 132 | impl ShmMetadata { 133 | pub fn init_shm_metadata() -> ShmMetadata { 134 | ShmMetadata { 135 | nextid: interface::RustAtomicI32::new(1), 136 | shmkeyidtable: interface::RustHashMap::new(), 137 | shmtable: interface::RustHashMap::new(), 138 | } 139 | } 140 | 141 | pub fn new_keyid(&self) -> i32 { 142 | self.nextid 143 | .fetch_add(1, interface::RustAtomicOrdering::Relaxed) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/safeposix/syscalls/fs_constants.rs: -------------------------------------------------------------------------------- 1 | // File system related constants 2 | #![allow(dead_code)] 3 | #![allow(unused_variables)] 4 | 5 | use crate::interface; 6 | 7 | // Define constants using static or const 8 | // Imported into fs_calls file 9 | pub const DT_UNKNOWN: u8 = 0; 10 | 11 | pub const STARTINGFD: i32 = 0; 12 | pub const MAXFD: i32 = 1024; 13 | pub const STARTINGPIPE: i32 = 0; 14 | pub const MAXPIPE: i32 = 1024; 15 | 16 | pub const ROOTDIRECTORYINODE: usize = 1; 17 | pub const STREAMINODE: usize = 2; // Dummy value 18 | pub const PIPEINODE: usize = 0xfeef0000; // Dummy value 19 | pub const EPOLLINODE: usize = 0xfeef0000; // Dummy value 20 | 21 | pub const PIPE_CAPACITY: usize = 65536; 22 | 23 | pub const F_OK: u32 = 0; 24 | pub const X_OK: u32 = 1; 25 | pub const W_OK: u32 = 2; 26 | pub const R_OK: u32 = 4; 27 | 28 | pub const O_RDONLY: i32 = 0o0; 29 | pub const O_WRONLY: i32 = 0o1; 30 | pub const O_RDWR: i32 = 0o2; 31 | pub const O_RDWRFLAGS: i32 = 0o3; 32 | 33 | pub const O_CREAT: i32 = 0o100; 34 | pub const O_EXCL: i32 = 0o200; 35 | pub const O_NOCTTY: i32 = 0o400; 36 | pub const O_TRUNC: i32 = 0o1000; 37 | pub const O_APPEND: i32 = 0o2000; 38 | pub const O_NONBLOCK: i32 = 0o4000; 39 | // O_NDELAY=O_NONBLOCK 40 | pub const O_SYNC: i32 = 0o10000; 41 | // O_FSYNC=O_SYNC 42 | pub const O_ASYNC: i32 = 0o20000; 43 | pub const O_CLOEXEC: i32 = 0o2000000; 44 | 45 | pub const DEFAULTTIME: u64 = 1323630836; 46 | 47 | //Standard flag combinations 48 | pub const S_IRWXA: u32 = 0o777; 49 | pub const S_IRWXU: u32 = 0o700; 50 | pub const S_IRUSR: u32 = 0o400; 51 | pub const S_IWUSR: u32 = 0o200; 52 | pub const S_IXUSR: u32 = 0o100; 53 | pub const S_IRWXG: u32 = 0o070; 54 | pub const S_IRGRP: u32 = 0o040; 55 | pub const S_IWGRP: u32 = 0o020; 56 | pub const S_IXGRP: u32 = 0o010; 57 | pub const S_IRWXO: u32 = 0o007; 58 | pub const S_IROTH: u32 = 0o004; 59 | pub const S_IWOTH: u32 = 0o002; 60 | pub const S_IXOTH: u32 = 0o001; 61 | 62 | //Commands for FCNTL 63 | pub const F_DUPFD: i32 = 0; 64 | pub const F_GETFD: i32 = 1; 65 | pub const F_SETFD: i32 = 2; 66 | pub const F_GETFL: i32 = 3; 67 | pub const F_SETFL: i32 = 4; 68 | pub const F_GETLK: i32 = 5; 69 | pub const F_GETLK64: i32 = 5; 70 | pub const F_SETLK: i32 = 6; 71 | pub const F_SETLK64: i32 = 6; 72 | pub const F_SETLKW: i32 = 7; 73 | pub const F_SETLKW64: i32 = 7; 74 | pub const F_SETOWN: i32 = 8; 75 | pub const F_GETOWN: i32 = 9; 76 | pub const F_SETSIG: i32 = 10; 77 | pub const F_GETSIG: i32 = 11; 78 | pub const F_SETLEASE: i32 = 1024; 79 | pub const F_GETLEASE: i32 = 1025; 80 | pub const F_NOTIFY: i32 = 1026; 81 | 82 | //Commands for IOCTL 83 | pub const FIONBIO: u32 = 21537; 84 | pub const FIOASYNC: u32 = 21586; 85 | 86 | //File types for open/stat etc. 87 | pub const S_IFBLK: i32 = 0o60000; 88 | pub const S_IFCHR: i32 = 0o20000; 89 | pub const S_IFDIR: i32 = 0o40000; 90 | pub const S_IFIFO: i32 = 0o10000; 91 | pub const S_IFLNK: i32 = 0o120000; 92 | pub const S_IFREG: i32 = 0o100000; 93 | pub const S_IFSOCK: i32 = 0o140000; 94 | pub const S_FILETYPEFLAGS: i32 = 0o170000; 95 | 96 | //for flock syscall 97 | pub const LOCK_SH: i32 = 1; 98 | pub const LOCK_EX: i32 = 2; 99 | pub const LOCK_UN: i32 = 8; 100 | pub const LOCK_NB: i32 = 4; 101 | //for mmap/munmap syscall 102 | pub const MAP_SHARED: i32 = 1; 103 | pub const MAP_PRIVATE: i32 = 2; 104 | pub const MAP_FIXED: i32 = 16; 105 | pub const MAP_ANONYMOUS: i32 = 32; 106 | pub const MAP_HUGE_SHIFT: i32 = 26; 107 | pub const MAP_HUGETLB: i32 = 262144; 108 | 109 | pub const PROT_NONE: i32 = 0; 110 | pub const PROT_READ: i32 = 1; 111 | pub const PROT_WRITE: i32 = 2; 112 | pub const PROT_EXEC: i32 = 4; 113 | 114 | pub const SEEK_SET: i32 = 0; 115 | pub const SEEK_CUR: i32 = 1; 116 | pub const SEEK_END: i32 = 2; 117 | 118 | pub const IPC_PRIVATE: i32 = 0o0; 119 | pub const IPC_CREAT: i32 = 0o1000; 120 | pub const IPC_EXCL: i32 = 0o2000; 121 | 122 | pub const IPC_RMID: i32 = 0; 123 | pub const IPC_SET: i32 = 1; 124 | pub const IPC_STAT: i32 = 2; 125 | 126 | pub const SHM_DEST: i32 = 0o1000; 127 | pub const SHM_LOCKED: i32 = 0o2000; 128 | pub const SHM_HUGETLB: i32 = 0o4000; 129 | 130 | pub const SHM_R: i32 = 0o400; 131 | pub const SHM_W: i32 = 0o200; 132 | pub const SHM_RDONLY: i32 = 0o10000; 133 | pub const SHM_RND: i32 = 0o20000; 134 | pub const SHM_REMAP: i32 = 0o40000; 135 | pub const SHM_EXEC: i32 = 0o100000; 136 | 137 | pub const SHMMIN: u32 = 1; 138 | pub const SHMMNI: u32 = 4096; 139 | pub const SHMMAX: u32 = 4278190079; // (ULONG_MAX - (1UL << 24)) 140 | pub const SHMALL: u32 = 4278190079; // (ULONG_MAX - (1UL << 24)); 141 | pub const SHMSEG: u32 = SHMMNI; 142 | 143 | pub const SEM_VALUE_MAX: u32 = 2147483647; 144 | 145 | //device info for char files 146 | #[derive(interface::SerdeSerialize, interface::SerdeDeserialize, PartialEq, Eq, Debug)] 147 | pub struct DevNo { 148 | pub major: u32, 149 | pub minor: u32, 150 | } 151 | pub const NULLDEVNO: DevNo = DevNo { major: 1, minor: 3 }; 152 | pub const ZERODEVNO: DevNo = DevNo { major: 1, minor: 5 }; 153 | pub const RANDOMDEVNO: DevNo = DevNo { major: 1, minor: 8 }; 154 | pub const URANDOMDEVNO: DevNo = DevNo { major: 1, minor: 9 }; 155 | 156 | pub const FILEDATAPREFIX: &str = "linddata."; 157 | 158 | pub fn is_reg(mode: u32) -> bool { 159 | (mode as i32 & S_FILETYPEFLAGS) == S_IFREG 160 | } 161 | 162 | pub fn is_chr(mode: u32) -> bool { 163 | (mode as i32 & S_FILETYPEFLAGS) == S_IFCHR 164 | } 165 | 166 | pub fn is_dir(mode: u32) -> bool { 167 | (mode as i32 & S_FILETYPEFLAGS) == S_IFDIR 168 | } 169 | 170 | pub fn is_wronly(flags: i32) -> bool { 171 | (flags & O_RDWRFLAGS) == O_WRONLY 172 | } 173 | pub fn is_rdonly(flags: i32) -> bool { 174 | (flags & O_RDWRFLAGS) == O_RDONLY 175 | } 176 | 177 | //the same as the glibc makedev 178 | pub fn makedev(dev: &DevNo) -> u64 { 179 | ((dev.major as u64 & 0x00000fff) << 8) 180 | | ((dev.major as u64 & 0xfffff000) << 32) 181 | | ((dev.minor as u64 & 0x000000ff) << 0) 182 | | ((dev.minor as u64 & 0xffffff00) << 12) 183 | } 184 | 185 | //the same as the glibc major and minor functions 186 | pub fn major(devnum: u64) -> u32 { 187 | (((devnum & 0x00000000000fff00) >> 8) | ((devnum & 0xfffff00000000000) >> 32)) as u32 188 | } 189 | pub fn minor(devnum: u64) -> u32 { 190 | (((devnum & 0x00000000000000ff) >> 0) | ((devnum & 0x00000ffffff00000) >> 12)) as u32 191 | } 192 | 193 | pub fn devtuple(devnum: u64) -> DevNo { 194 | DevNo { 195 | major: major(devnum), 196 | minor: minor(devnum), 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /src/safeposix/syscalls/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module acts a wrapper for all system calls in the RustPOSIX 2 | //! environment. and has methods for each system call. divided into three 3 | //! categories: filesystem, system, and network 4 | //! 5 | //! ## System Calls 6 | //! 7 | //! Cage objects have methods for system calls, categorized as filesystem, 8 | //! system, or network-related. They return a code or an error from the `errno` 9 | //! enum. 10 | 11 | pub mod fs_calls; 12 | pub mod fs_constants; 13 | pub mod net_calls; 14 | pub mod net_constants; 15 | pub mod sys_calls; 16 | pub mod sys_constants; 17 | pub use fs_calls::*; 18 | pub use fs_constants::*; 19 | pub use net_calls::*; 20 | pub use net_constants::*; 21 | pub use sys_calls::*; 22 | pub use sys_constants::*; 23 | -------------------------------------------------------------------------------- /src/safeposix/syscalls/net_constants.rs: -------------------------------------------------------------------------------- 1 | // Network related constants 2 | #![allow(dead_code)] 3 | #![allow(non_upper_case_globals)] 4 | 5 | use crate::interface; 6 | 7 | //used for gethostname syscall 8 | pub const DEFAULT_HOSTNAME: &str = "Lind"; 9 | pub const BLOCK_TIME: interface::RustDuration = interface::RustDuration::from_micros(100); 10 | 11 | pub const UDSOCK_CAPACITY: usize = 212992; 12 | 13 | // Define constants using static or const 14 | // Imported into net_calls file 15 | 16 | pub const SOCK_STREAM: i32 = 1; //stream socket 17 | pub const SOCK_DGRAM: i32 = 2; //datagram socket 18 | pub const SOCK_RAW: i32 = 3; //raw protocol interface 19 | pub const SOCK_RDM: i32 = 4; //reliably delivered message 20 | pub const SOCK_SEQPACKET: i32 = 5; //sequenced packet stream 21 | pub const SOCK_CLOEXEC: i32 = 0o02000000; // Atomically set close-on-exec 22 | pub const SOCK_NONBLOCK: i32 = 0o00004000; // Mark as non-blocking 23 | 24 | /* Supported address families. */ 25 | pub const AF_UNSPEC: i32 = 0; 26 | pub const AF_UNIX: i32 = 1; /* Unix domain sockets */ 27 | pub const AF_LOCAL: i32 = 1; /* POSIX name for AF_UNIX */ 28 | pub const AF_INET: i32 = 2; /* Internet IP Protocol */ 29 | pub const AF_AX25: i32 = 3; /* Amateur Radio AX.25 */ 30 | pub const AF_IPX: i32 = 4; /* Novell IPX */ 31 | pub const AF_APPLETALK: i32 = 5; /* AppleTalk DDP */ 32 | pub const AF_NETROM: i32 = 6; /* Amateur Radio NET/ROM */ 33 | pub const AF_BRIDGE: i32 = 7; /* Multiprotocol bridge */ 34 | pub const AF_ATMPVC: i32 = 8; /* ATM PVCs */ 35 | pub const AF_X25: i32 = 9; /* Reserved for X.25 project */ 36 | pub const AF_INET6: i32 = 10; /* IP version 6 */ 37 | pub const AF_ROSE: i32 = 11; /* Amateur Radio X.25 PLP */ 38 | pub const AF_DECnet: i32 = 12; /* Reserved for DECnet project */ 39 | pub const AF_NETBEUI: i32 = 13; /* Reserved for 802.2LLC project */ 40 | pub const AF_SECURITY: i32 = 14; /* Security callback pseudo AF */ 41 | pub const AF_KEY: i32 = 15; /* PF_KEY key management API */ 42 | pub const AF_NETLINK: i32 = 16; 43 | pub const AF_ROUTE: i32 = AF_NETLINK; /* Alias to emulate 4.4BSD */ 44 | pub const AF_PACKET: i32 = 17; /* Packet family */ 45 | pub const AF_ASH: i32 = 18; /* Ash */ 46 | pub const AF_ECONET: i32 = 19; /* Acorn Econet */ 47 | pub const AF_ATMSVC: i32 = 20; /* ATM SVCs */ 48 | pub const AF_RDS: i32 = 21; /* RDS sockets */ 49 | pub const AF_SNA: i32 = 22; /* Linux SNA Project (nutters!) */ 50 | pub const AF_IRDA: i32 = 23; /* IRDA sockets */ 51 | pub const AF_PPPOX: i32 = 24; /* PPPoX sockets */ 52 | pub const AF_WANPIPE: i32 = 25; /* Wanpipe API Sockets */ 53 | pub const AF_LLC: i32 = 26; /* Linux LLC */ 54 | pub const AF_IB: i32 = 27; /* Native InfiniBand address */ 55 | pub const AF_MPLS: i32 = 28; /* MPLS */ 56 | pub const AF_CAN: i32 = 29; /* Controller Area Network */ 57 | pub const AF_TIPC: i32 = 30; /* TIPC sockets */ 58 | pub const AF_BLUETOOTH: i32 = 31; /* Bluetooth sockets */ 59 | pub const AF_IUCV: i32 = 32; /* IUCV sockets */ 60 | pub const AF_RXRPC: i32 = 33; /* RxRPC sockets */ 61 | pub const AF_ISDN: i32 = 34; /* mISDN sockets */ 62 | pub const AF_PHONET: i32 = 35; /* Phonet sockets */ 63 | pub const AF_IEEE802154: i32 = 36; /* IEEE802154 sockets */ 64 | pub const AF_CAIF: i32 = 37; /* CAIF sockets */ 65 | pub const AF_ALG: i32 = 38; /* Algorithm sockets */ 66 | pub const AF_NFC: i32 = 39; /* NFC sockets */ 67 | pub const AF_VSOCK: i32 = 40; /* vSockets */ 68 | pub const AF_KCM: i32 = 41; /* Kernel Connection Multiplexor */ 69 | pub const AF_QIPCRTR: i32 = 42; /* Qualcomm IPC Router */ 70 | pub const AF_SMC: i32 = 43; /* smc sockets: reserve number for 71 | * PF_SMC protocol family that 72 | * reuses AF_INET address family 73 | */ 74 | pub const AF_XDP: i32 = 44; /* XDP sockets */ 75 | pub const AF_MCTP: i32 = 45; /* Management component 76 | * transport protocol 77 | */ 78 | 79 | pub const AF_MAX: i32 = 46; /* For now.. */ 80 | 81 | /* Protocol families, same as address families. */ 82 | pub const PF_UNSPEC: i32 = AF_UNSPEC; 83 | pub const PF_UNIX: i32 = AF_UNIX; 84 | pub const PF_LOCAL: i32 = AF_LOCAL; 85 | pub const PF_INET: i32 = AF_INET; 86 | pub const PF_AX25: i32 = AF_AX25; 87 | pub const PF_IPX: i32 = AF_IPX; 88 | pub const PF_APPLETALK: i32 = AF_APPLETALK; 89 | pub const PF_NETROM: i32 = AF_NETROM; 90 | pub const PF_BRIDGE: i32 = AF_BRIDGE; 91 | pub const PF_ATMPVC: i32 = AF_ATMPVC; 92 | pub const PF_X25: i32 = AF_X25; 93 | pub const PF_INET6: i32 = AF_INET6; 94 | pub const PF_ROSE: i32 = AF_ROSE; 95 | pub const PF_DECnet: i32 = AF_DECnet; 96 | pub const PF_NETBEUI: i32 = AF_NETBEUI; 97 | pub const PF_SECURITY: i32 = AF_SECURITY; 98 | pub const PF_KEY: i32 = AF_KEY; 99 | pub const PF_NETLINK: i32 = AF_NETLINK; 100 | pub const PF_ROUTE: i32 = AF_ROUTE; 101 | pub const PF_PACKET: i32 = AF_PACKET; 102 | pub const PF_ASH: i32 = AF_ASH; 103 | pub const PF_ECONET: i32 = AF_ECONET; 104 | pub const PF_ATMSVC: i32 = AF_ATMSVC; 105 | pub const PF_RDS: i32 = AF_RDS; 106 | pub const PF_SNA: i32 = AF_SNA; 107 | pub const PF_IRDA: i32 = AF_IRDA; 108 | pub const PF_PPPOX: i32 = AF_PPPOX; 109 | pub const PF_WANPIPE: i32 = AF_WANPIPE; 110 | pub const PF_LLC: i32 = AF_LLC; 111 | pub const PF_IB: i32 = AF_IB; 112 | pub const PF_MPLS: i32 = AF_MPLS; 113 | pub const PF_CAN: i32 = AF_CAN; 114 | pub const PF_TIPC: i32 = AF_TIPC; 115 | pub const PF_BLUETOOTH: i32 = AF_BLUETOOTH; 116 | pub const PF_IUCV: i32 = AF_IUCV; 117 | pub const PF_RXRPC: i32 = AF_RXRPC; 118 | pub const PF_ISDN: i32 = AF_ISDN; 119 | pub const PF_PHONET: i32 = AF_PHONET; 120 | pub const PF_IEEE802154: i32 = AF_IEEE802154; 121 | pub const PF_CAIF: i32 = AF_CAIF; 122 | pub const PF_ALG: i32 = AF_ALG; 123 | pub const PF_NFC: i32 = AF_NFC; 124 | pub const PF_VSOCK: i32 = AF_VSOCK; 125 | pub const PF_KCM: i32 = AF_KCM; 126 | pub const PF_QIPCRTR: i32 = AF_QIPCRTR; 127 | pub const PF_SMC: i32 = AF_SMC; 128 | pub const PF_XDP: i32 = AF_XDP; 129 | pub const PF_MCTP: i32 = AF_MCTP; 130 | pub const PF_MAX: i32 = AF_MAX; 131 | 132 | // protocols... 133 | 134 | pub const IPPROTO_IP: i32 = 0; // dummy for IP 135 | pub const IPPROTO_ICMP: i32 = 1; // control message protocol 136 | pub const IPPROTO_IGMP: i32 = 2; // group mgmt protocol 137 | pub const IPPROTO_GGP: i32 = 3; // gateway^2 (deprecated) 138 | pub const IPPROTO_IPV4: i32 = 4; // IPv4 encapsulation 139 | pub const IPPROTO_IPIP: i32 = IPPROTO_IPV4; // for compatibility 140 | pub const IPPROTO_TCP: i32 = 6; // tcp 141 | pub const IPPROTO_ST: i32 = 7; // Stream protocol II 142 | pub const IPPROTO_EGP: i32 = 8; // exterior gateway protocol 143 | pub const IPPROTO_PIGP: i32 = 9; // private interior gateway 144 | pub const IPPROTO_RCCMON: i32 = 10; // BBN RCC Monitoring 145 | pub const IPPROTO_NVPII: i32 = 11; // network voice protocol 146 | pub const IPPROTO_PUP: i32 = 12; // pup 147 | pub const IPPROTO_ARGUS: i32 = 13; // Argus 148 | pub const IPPROTO_EMCON: i32 = 14; // EMCON 149 | pub const IPPROTO_XNET: i32 = 15; // Cross Net Debugger 150 | pub const IPPROTO_CHAOS: i32 = 16; // Chaos 151 | pub const IPPROTO_UDP: i32 = 17; // user datagram protocol 152 | pub const IPPROTO_MUX: i32 = 18; // Multiplexing 153 | pub const IPPROTO_MEAS: i32 = 19; // DCN Measurement Subsystems 154 | pub const IPPROTO_HMP: i32 = 20; // Host Monitoring 155 | pub const IPPROTO_PRM: i32 = 21; // Packet Radio Measurement 156 | pub const IPPROTO_IDP: i32 = 22; // xns idp 157 | pub const IPPROTO_TRUNK1: i32 = 23; // Trunk-1 158 | pub const IPPROTO_TRUNK2: i32 = 24; // Trunk-2 159 | pub const IPPROTO_LEAF1: i32 = 25; // Leaf-1 160 | pub const IPPROTO_LEAF2: i32 = 26; // Leaf-2 161 | pub const IPPROTO_RDP: i32 = 27; // Reliable Data 162 | pub const IPPROTO_IRTP: i32 = 28; // Reliable Transaction 163 | pub const IPPROTO_TP: i32 = 29; // tp-4 w/ class negotiation 164 | pub const IPPROTO_BLT: i32 = 30; // Bulk Data Transfer 165 | pub const IPPROTO_NSP: i32 = 31; // Network Services 166 | pub const IPPROTO_INP: i32 = 32; // Merit Internodal 167 | pub const IPPROTO_SEP: i32 = 33; // Sequential Exchange 168 | pub const IPPROTO_3PC: i32 = 34; // Third Party Connect 169 | pub const IPPROTO_IDPR: i32 = 35; // InterDomain Policy Routing 170 | pub const IPPROTO_XTP: i32 = 36; // XTP 171 | pub const IPPROTO_DDP: i32 = 37; // Datagram Delivery 172 | pub const IPPROTO_CMTP: i32 = 38; // Control Message Transport 173 | pub const IPPROTO_TPXX: i32 = 39; // TP++ Transport 174 | pub const IPPROTO_IL: i32 = 40; // IL transport protocol 175 | pub const IPPROTO_IPV6: i32 = 41; // IP6 header 176 | pub const IPPROTO_SDRP: i32 = 42; // Source Demand Routing 177 | pub const IPPROTO_ROUTING: i32 = 43; // IP6 routing header 178 | pub const IPPROTO_FRAGMENT: i32 = 44; // IP6 fragmentation header 179 | pub const IPPROTO_IDRP: i32 = 45; // InterDomain Routing 180 | pub const IPPROTO_RSVP: i32 = 46; // resource reservation 181 | pub const IPPROTO_GRE: i32 = 47; // General Routing Encap. 182 | pub const IPPROTO_MHRP: i32 = 48; // Mobile Host Routing 183 | pub const IPPROTO_BHA: i32 = 49; // BHA 184 | pub const IPPROTO_ESP: i32 = 50; // IP6 Encap Sec. Payload 185 | pub const IPPROTO_AH: i32 = 51; // IP6 Auth Header 186 | pub const IPPROTO_INLSP: i32 = 52; // Integ. Net Layer Security 187 | pub const IPPROTO_SWIPE: i32 = 53; // IP with encryption 188 | pub const IPPROTO_NHRP: i32 = 54; // Next Hop Resolution 189 | // 55-57: Unassigned 190 | pub const IPPROTO_ICMPV6: i32 = 58; // ICMP6 191 | pub const IPPROTO_NONE: i32 = 59; // IP6 no next header 192 | pub const IPPROTO_DSTOPTS: i32 = 60; // IP6 destination option 193 | pub const IPPROTO_AHIP: i32 = 61; // any host internal protocol 194 | pub const IPPROTO_CFTP: i32 = 62; // CFTP 195 | pub const IPPROTO_HELLO: i32 = 63; // "hello" routing protocol 196 | pub const IPPROTO_SATEXPAK: i32 = 64; // SATNET/Backroom EXPAK 197 | pub const IPPROTO_KRYPTOLAN: i32 = 65; // Kryptolan 198 | pub const IPPROTO_RVD: i32 = 66; // Remote Virtual Disk 199 | pub const IPPROTO_IPPC: i32 = 67; // Pluribus Packet Core 200 | pub const IPPROTO_ADFS: i32 = 68; // Any distributed FS 201 | pub const IPPROTO_SATMON: i32 = 69; // Satnet Monitoring 202 | pub const IPPROTO_VISA: i32 = 70; // VISA Protocol 203 | pub const IPPROTO_IPCV: i32 = 71; // Packet Core Utility 204 | pub const IPPROTO_CPNX: i32 = 72; // Comp. Prot. Net. Executive 205 | pub const IPPROTO_CPHB: i32 = 73; // Comp. Prot. HeartBeat 206 | pub const IPPROTO_WSN: i32 = 74; // Wang Span Network 207 | pub const IPPROTO_PVP: i32 = 75; // Packet Video Protocol 208 | pub const IPPROTO_BRSATMON: i32 = 76; // BackRoom SATNET Monitoring 209 | pub const IPPROTO_ND: i32 = 77; // Sun net disk proto (temp.) 210 | pub const IPPROTO_WBMON: i32 = 78; // WIDEBAND Monitoring 211 | pub const IPPROTO_WBEXPAK: i32 = 79; // WIDEBAND EXPAK 212 | pub const IPPROTO_EON: i32 = 80; // ISO cnlp 213 | pub const IPPROTO_VMTP: i32 = 81; // VMTP 214 | pub const IPPROTO_SVMTP: i32 = 82; // Secure VMTP 215 | pub const IPPROTO_VINES: i32 = 83; // Banyon VINES 216 | pub const IPPROTO_TTP: i32 = 84; // TTP 217 | pub const IPPROTO_IGP: i32 = 85; // NSFNET-IGP 218 | pub const IPPROTO_DGP: i32 = 86; // dissimilar gateway prot. 219 | pub const IPPROTO_TCF: i32 = 87; // TCF 220 | pub const IPPROTO_IGRP: i32 = 88; // Cisco/GXS IGRP 221 | pub const IPPROTO_OSPFIGP: i32 = 89; // OSPFIGP 222 | pub const IPPROTO_SRPC: i32 = 90; // Strite RPC protocol 223 | pub const IPPROTO_LARP: i32 = 91; // Locus Address Resoloution 224 | pub const IPPROTO_MTP: i32 = 92; // Multicast Transport 225 | pub const IPPROTO_AX25: i32 = 93; // AX.25 Frames 226 | pub const IPPROTO_IPEIP: i32 = 94; // IP encapsulated in IP 227 | pub const IPPROTO_MICP: i32 = 95; // Mobile Int.ing control 228 | pub const IPPROTO_SCCSP: i32 = 96; // Semaphore Comm. security 229 | pub const IPPROTO_ETHERIP: i32 = 97; // Ethernet IP encapsulation 230 | pub const IPPROTO_ENCAP: i32 = 98; // encapsulation header 231 | pub const IPPROTO_APES: i32 = 99; // any private encr. scheme 232 | pub const IPPROTO_GMTP: i32 = 100; // GMTP 233 | pub const IPPROTO_PIM: i32 = 103; // Protocol Independent Mcast 234 | pub const IPPROTO_IPCOMP: i32 = 108; // payload compression (IPComp) 235 | pub const IPPROTO_PGM: i32 = 113; // PGM 236 | pub const IPPROTO_SCTP: i32 = 132; // SCTP 237 | pub const IPPROTO_DIVERT: i32 = 254; // divert pseudo-protocol 238 | pub const IPPROTO_RAW: i32 = 255; // raw IP packet 239 | pub const IPPROTO_MAX: i32 = 256; 240 | // last return value of *_input(), meaning "all job for this pkt is done". 241 | pub const IPPROTO_DONE: i32 = 257; 242 | 243 | pub const MSG_OOB: i32 = 1; 244 | pub const MSG_PEEK: i32 = 2; 245 | pub const MSG_DONTROUTE: i32 = 4; 246 | pub const MSG_TRYHARD: i32 = 4; /* Synonym for MSG_DONTROUTE for DECnet */ 247 | pub const MSG_CTRUNC: i32 = 8; 248 | pub const MSG_PROBE: i32 = 0x10; /* Do not send. Only probe path f.e. for MTU */ 249 | pub const MSG_TRUNC: i32 = 0x20; 250 | pub const MSG_DONTWAIT: i32 = 0x40; /* Nonblocking io */ 251 | pub const MSG_EOR: i32 = 0x80; /* End of record */ 252 | pub const MSG_WAITALL: i32 = 0x100; /* Wait for a full request */ 253 | pub const MSG_FIN: i32 = 0x200; 254 | pub const MSG_SYN: i32 = 0x400; 255 | pub const MSG_CONFIRM: i32 = 0x800; /* Confirm path validity */ 256 | pub const MSG_RST: i32 = 0x1000; 257 | pub const MSG_ERRQUEUE: i32 = 0x2000; /* Fetch message from error queue */ 258 | pub const MSG_NOSIGNAL: i32 = 0x4000; /* Do not generate SIGPIPE */ 259 | pub const MSG_MORE: i32 = 0x8000; /* Sender will send more */ 260 | pub const MSG_WAITFORONE: i32 = 0x10000; /* recvmmsg(): block until 1+ packets avail */ 261 | pub const MSG_SENDPAGE_NOPOLICY: i32 = 0x10000; /* sendpage() internal : do no apply policy */ 262 | pub const MSG_SENDPAGE_NOTLAST: i32 = 0x20000; /* sendpage() internal : not the last page */ 263 | pub const MSG_BATCH: i32 = 0x40000; /* sendmmsg(): more messages coming */ 264 | pub const MSG_EOF: i32 = MSG_FIN; 265 | pub const MSG_NO_SHARED_FRAGS: i32 = 0x80000; /* sendpage() internal : page frags are not shared */ 266 | pub const MSG_SENDPAGE_DECRYPTED: i32 = 0x100000; /* sendpage() internal : page may carry 267 | * plain text and require encryption 268 | */ 269 | 270 | //shutdown 271 | pub const SHUT_RD: i32 = 0; 272 | pub const SHUT_WR: i32 = 1; 273 | pub const SHUT_RDWR: i32 = 2; 274 | 275 | ////////////////////// setsockopt / getsockopt... 276 | pub const SOL_SOCKET: i32 = 1; 277 | 278 | pub const SO_DEBUG: i32 = 1; 279 | pub const SO_REUSEADDR: i32 = 2; 280 | pub const SO_TYPE: i32 = 3; 281 | pub const SO_ERROR: i32 = 4; 282 | pub const SO_DONTROUTE: i32 = 5; 283 | pub const SO_BROADCAST: i32 = 6; 284 | pub const SO_SNDBUF: i32 = 7; 285 | pub const SO_RCVBUF: i32 = 8; 286 | pub const SO_SNDBUFFORCE: i32 = 32; 287 | pub const SO_RCVBUFFORCE: i32 = 33; 288 | pub const SO_KEEPALIVE: i32 = 9; 289 | pub const SO_OOBINLINE: i32 = 10; 290 | pub const SO_NO_CHECK: i32 = 11; 291 | pub const SO_PRIORITY: i32 = 12; 292 | pub const SO_LINGER: i32 = 13; 293 | pub const SO_BSDCOMPAT: i32 = 14; 294 | pub const SO_REUSEPORT: i32 = 15; 295 | pub const SO_PASSCRED: i32 = 16; 296 | pub const SO_PEERCRED: i32 = 17; 297 | pub const SO_RCVLOWAT: i32 = 18; 298 | pub const SO_SNDLOWAT: i32 = 19; 299 | pub const SO_RCVTIMEO_OLD: i32 = 20; 300 | pub const SO_SNDTIMEO_OLD: i32 = 21; 301 | pub const SO_PEERNAME: i32 = 28; 302 | pub const SO_ACCEPTCONN: i32 = 30; 303 | 304 | // pub const SO_SECURITY_AUTHENTICATION: i32 = 22; 305 | // pub const SO_SECURITY_ENCRYPTION_TRANSPORT: i32 = 23; 306 | // pub const SO_SECURITY_ENCRYPTION_NETWORK: i32 = 24; 307 | 308 | // pub const SO_BINDTODEVICE: i32 = 25; 309 | 310 | // /* Socket filtering */ 311 | // pub const SO_ATTACH_FILTER: i32 = 26; 312 | // pub const SO_DETACH_FILTER: i32 = 27; 313 | 314 | // pub const SO_TIMESTAMP: i32 = 29; 315 | // pub const SCM_TIMESTAMP: i32 = SO_TIMESTAMP; 316 | 317 | // pub const SO_PEERSEC: i32 = 31; 318 | // pub const SO_PASSSEC: i32 = 34; 319 | // pub const SO_TIMESTAMPNS: i32 = 35; 320 | // pub const SCM_TIMESTAMPNS: i32 = SO_TIMESTAMPNS; 321 | 322 | // pub const SO_MARK: i32 = 36; 323 | 324 | // pub const SO_TIMESTAMPING: i32 = 37; 325 | // pub const SCM_TIMESTAMPING: i32 = SO_TIMESTAMPING; 326 | 327 | // pub const SO_PROTOCOL: i32 = 38; 328 | // pub const SO_DOMAIN: i32 = 39; 329 | 330 | // pub const SO_RXQ_OVFL: i32 = 40; 331 | 332 | // Use this to specify options on a socket. Use the protocol with setsockopt 333 | // to specify something for all sockets with a protocol 334 | pub const SOL_TCP: i32 = IPPROTO_TCP; 335 | pub const SOL_UDP: i32 = IPPROTO_UDP; 336 | 337 | // some TCP flags below are not part of Linux standards 338 | // for example, TCP_NOPUSH is the flag used in FreeBSD/MacOS 339 | // while the actual flag in Linux that serves the similar purpose 340 | // is TCP_CORK 341 | // Besides, some other flags are also not found in Linux man page: 342 | // TCP_KEEPALIVE, TCP_CONNECTIONTIMEOUT, PERSIST_TIMEOUT, TCP_RXT_CONNDROPTIME 343 | // and TCP_RXT_FINDROP 344 | pub const TCP_NODELAY: i32 = 0x01; // don't delay send to coalesce packets 345 | pub const TCP_MAXSEG: i32 = 0x02; // set maximum segment size 346 | pub const TCP_NOPUSH: i32 = 0x04; // don't push last block of write 347 | pub const TCP_NOOPT: i32 = 0x08; // don't use TCP options 348 | pub const TCP_KEEPALIVE: i32 = 0x10; // idle time used when SO_KEEPALIVE is enabled 349 | pub const TCP_CONNECTIONTIMEOUT: i32 = 0x20; // connection timeout 350 | pub const PERSIST_TIMEOUT: i32 = 0x40; // time after which a connection in persist timeout 351 | // will terminate. 352 | // see draft-ananth-tcpm-persist-02.txt 353 | pub const TCP_RXT_CONNDROPTIME: i32 = 0x80; // time after which tcp retransmissions will be 354 | // stopped and the connection will be dropped 355 | pub const TCP_RXT_FINDROP: i32 = 0x100; // When set, a connection is dropped after 3 FINs 356 | 357 | pub const MINSOCKOBJID: i32 = 0; 358 | pub const MAXSOCKOBJID: i32 = 1024; 359 | 360 | //POLL CONSTANTS 361 | pub const POLLIN: i16 = 0o1; // There is data to read. 362 | pub const POLLPRI: i16 = 0o2; //There is urgent data to read. 363 | pub const POLLOUT: i16 = 0o4; // Writing now will not block. 364 | pub const POLLERR: i16 = 0o10; // Error condition. 365 | pub const POLLHUP: i16 = 0o20; // Hung up. 366 | pub const POLLNVAL: i16 = 0o40; // Invalid polling request. 367 | 368 | //EPOLL CONSTANTS 369 | pub const EPOLLIN: i32 = 0x001; 370 | pub const EPOLLPRI: i32 = 0x002; 371 | pub const EPOLLOUT: i32 = 0x004; 372 | pub const EPOLLRDNORM: i32 = 0x040; 373 | pub const EPOLLRDBAND: i32 = 0x080; 374 | pub const EPOLLWRNORM: i32 = 0x100; 375 | pub const EPOLLWRBAND: i32 = 0x200; 376 | pub const EPOLLMSG: i32 = 0x400; 377 | pub const EPOLLERR: i32 = 0x008; 378 | pub const EPOLLHUP: i32 = 0x010; 379 | pub const EPOLLRDHUP: i32 = 0x2000; 380 | pub const EPOLLWAKEUP: i32 = 1 << 29; 381 | pub const EPOLLONESHOT: i32 = 1 << 30; 382 | pub const EPOLLET: i32 = 1 << 31; 383 | 384 | pub const EPOLL_CTL_ADD: i32 = 1; 385 | pub const EPOLL_CTL_DEL: i32 = 2; 386 | pub const EPOLL_CTL_MOD: i32 = 3; 387 | 388 | pub const FD_SET_MAX_FD: i32 = 1024; 389 | 390 | //for internal use 391 | #[derive(Debug, PartialEq, Eq, Clone)] 392 | pub enum ConnState { 393 | NOTCONNECTED, 394 | CONNECTED, 395 | CONNRDONLY, 396 | CONNWRONLY, 397 | LISTEN, 398 | INPROGRESS, 399 | } 400 | -------------------------------------------------------------------------------- /src/safeposix/syscalls/sys_constants.rs: -------------------------------------------------------------------------------- 1 | // System related constants 2 | #![allow(dead_code)] 3 | #![allow(unused_variables)] 4 | 5 | use crate::interface; 6 | 7 | // Define constants using static or const 8 | // Imported into fs_calls file 9 | 10 | //GID AND UID DEFAULT VALUES 11 | 12 | pub const DEFAULT_UID: u32 = 1000; 13 | pub const DEFAULT_GID: u32 = 1000; 14 | 15 | // RESOURCE LIMITS 16 | 17 | pub const SIGNAL_MAX: i32 = 64; 18 | 19 | pub const NOFILE_CUR: u64 = 1024; 20 | pub const NOFILE_MAX: u64 = 4 * 1024; 21 | 22 | pub const STACK_CUR: u64 = 8192 * 1024; 23 | pub const STACK_MAX: u64 = 1 << 32; 24 | 25 | pub const RLIMIT_STACK: u64 = 0; 26 | pub const RLIMIT_NOFILE: u64 = 1; 27 | 28 | // Constants for exit_syscall status 29 | 30 | pub const EXIT_SUCCESS: i32 = 0; 31 | pub const EXIT_FAILURE: i32 = 1; 32 | 33 | // Signal Table (x86/ARM) 34 | // Based on https://man7.org/linux/man-pages/man7/signal.7.html 35 | pub const SIGHUP: i32 = 1; 36 | pub const SIGINT: i32 = 2; 37 | pub const SIGQUIT: i32 = 3; 38 | pub const SIGILL: i32 = 4; 39 | pub const SIGTRAP: i32 = 5; 40 | pub const SIGABRT: i32 = 6; 41 | pub const SIGIOT: i32 = 6; 42 | pub const SIGBUS: i32 = 7; 43 | // pub const SIGEMT: i32 44 | pub const SIGFPE: i32 = 8; 45 | pub const SIGKILL: i32 = 9; 46 | pub const SIGUSR1: i32 = 10; 47 | pub const SIGSEGV: i32 = 11; 48 | pub const SIGUSR2: i32 = 12; 49 | pub const SIGPIPE: i32 = 13; 50 | pub const SIGALRM: i32 = 14; 51 | pub const SIGTERM: i32 = 15; 52 | pub const SIGSTKFLT: i32 = 16; 53 | pub const SIGCHLD: i32 = 17; 54 | // pub const SIGCLD: i32 55 | pub const SIGCONT: i32 = 18; 56 | pub const SIGSTOP: i32 = 19; 57 | pub const SIGTSTP: i32 = 20; 58 | pub const SIGTTIN: i32 = 21; 59 | pub const SIGTTOU: i32 = 22; 60 | pub const SIGURG: i32 = 23; 61 | pub const SIGXCPU: i32 = 24; 62 | pub const SIGXFSZ: i32 = 25; 63 | pub const SIGVTALRM: i32 = 26; 64 | pub const SIGPROF: i32 = 27; 65 | pub const SIGWINCH: i32 = 28; 66 | pub const SIGIO: i32 = 29; 67 | pub const SIGPOLL: i32 = 29; 68 | pub const SIGPWR: i32 = 30; 69 | // pub const SIGINFO: i32 70 | // pub const SIGLOST: i32 71 | pub const SIGSYS: i32 = 31; 72 | pub const SIGUNUSED: i32 = 31; 73 | 74 | pub const SIG_BLOCK: i32 = 0; 75 | pub const SIG_UNBLOCK: i32 = 1; 76 | pub const SIG_SETMASK: i32 = 2; 77 | pub const ITIMER_REAL: i32 = 0; 78 | -------------------------------------------------------------------------------- /src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] //suppress warning for these functions not being used in targets other than the 2 | // tests 3 | 4 | mod fs_tests; 5 | mod ipc_tests; 6 | mod networking_tests; 7 | mod sys_tests; 8 | use rand::Rng; 9 | use std::net::{TcpListener, UdpSocket}; 10 | 11 | use crate::interface; 12 | use crate::safeposix::{cage::*, filesystem::*}; 13 | 14 | #[allow(unused_parens)] 15 | #[cfg(test)] 16 | mod setup { 17 | 18 | use crate::interface; 19 | use crate::safeposix::{cage::*, dispatcher::*, filesystem::*}; 20 | 21 | use lazy_static::lazy_static; 22 | use std::process::Command; 23 | use std::sync::Mutex; 24 | 25 | // Tests in rust as parallel by default and to make them share resources we are 26 | // using a global static lock. 27 | lazy_static! { 28 | // This has a junk value (a bool). Could be anything... 29 | #[derive(Debug)] 30 | pub static ref TESTMUTEX: Mutex = { 31 | Mutex::new(true) 32 | }; 33 | } 34 | 35 | // Using explicit lifetime to have a safe reference to the lock in the tests. 36 | pub fn lock_and_init<'a>() -> std::sync::MutexGuard<'a, bool> { 37 | set_panic_hook(); 38 | 39 | //acquiring a lock on TESTMUTEX prevents other tests from running concurrently 40 | let thelock = TESTMUTEX.lock().unwrap_or_else(|e| { 41 | //if the lock is poisoned, we need to clear the poison and clean up references 42 | // to the cage. 43 | lindrustfinalize(); 44 | //clear the mutex poisoning. 45 | TESTMUTEX.clear_poison(); 46 | //return the underlying guard. 47 | e.into_inner() 48 | }); 49 | 50 | interface::RUSTPOSIX_TESTSUITE.store(true, interface::RustAtomicOrdering::Relaxed); 51 | 52 | //setup the lind filesystem, creates a clean filesystem for each test 53 | lindrustinit(0); 54 | { 55 | println!("test_setup()"); 56 | let cage = interface::cagetable_getref(1); 57 | crate::lib_fs_utils::lind_deltree(&cage, "/"); 58 | assert_eq!(cage.mkdir_syscall("/dev", S_IRWXA), 0); 59 | assert_eq!( 60 | cage.mknod_syscall( 61 | "/dev/null", 62 | S_IFCHR as u32 | 0o777, 63 | makedev(&DevNo { major: 1, minor: 3 }) 64 | ), 65 | 0 66 | ); 67 | assert_eq!( 68 | cage.mknod_syscall( 69 | "/dev/zero", 70 | S_IFCHR as u32 | 0o777, 71 | makedev(&DevNo { major: 1, minor: 5 }) 72 | ), 73 | 0 74 | ); 75 | assert_eq!( 76 | cage.mknod_syscall( 77 | "/dev/urandom", 78 | S_IFCHR as u32 | 0o777, 79 | makedev(&DevNo { major: 1, minor: 9 }) 80 | ), 81 | 0 82 | ); 83 | assert_eq!( 84 | cage.mknod_syscall( 85 | "/dev/random", 86 | S_IFCHR as u32 | 0o777, 87 | makedev(&DevNo { major: 1, minor: 8 }) 88 | ), 89 | 0 90 | ); 91 | assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); 92 | } 93 | lindrustfinalize(); 94 | 95 | //initialize the cage for the test. 96 | lindrustinit(0); 97 | 98 | //return the lock to the caller which holds it till the end of the test. 99 | thelock 100 | } 101 | 102 | fn set_panic_hook() { 103 | let orig_hook = std::panic::take_hook(); 104 | std::panic::set_hook(Box::new(move |panic_info| { 105 | // this hook would be triggered whenever a panic occurs 106 | // good for test cases that panicked inside the non-main thread 107 | // so the trace information could be printed immediately 108 | // instead of raising the error when the thread is joined, which might 109 | // never happen and left the test blocking forever in some test cases 110 | orig_hook(panic_info); 111 | })); 112 | } 113 | } 114 | 115 | pub fn str2cbuf(ruststr: &str) -> *mut u8 { 116 | let cbuflenexpected = ruststr.len(); 117 | let (ptr, len, _) = ruststr.to_string().into_raw_parts(); 118 | assert_eq!(len, cbuflenexpected); 119 | return ptr; 120 | } 121 | 122 | pub fn sizecbuf<'a>(size: usize) -> Box<[u8]> { 123 | let v = vec![0u8; size]; 124 | v.into_boxed_slice() 125 | //buf.as_mut_ptr() as *mut u8 126 | } 127 | 128 | pub fn cbuf2str(buf: &[u8]) -> &str { 129 | std::str::from_utf8(buf).unwrap() 130 | } 131 | 132 | // The RustPOSIX test suite avoids conflicts caused by repeatedly binding to the 133 | // same ports by generating a random port number within the valid range 134 | // (49152-65535) for each test run. This eliminates the need for waiting between 135 | // tests. 136 | 137 | fn is_port_available(port: u16) -> bool { 138 | TcpListener::bind(("127.0.0.1", port)).is_ok() && UdpSocket::bind(("127.0.0.1", port)).is_ok() 139 | } 140 | 141 | pub fn generate_random_port() -> u16 { 142 | for port in 49152..65535 { 143 | if is_port_available(port) { 144 | return port; 145 | } 146 | } 147 | panic!("No available ports found"); 148 | } 149 | -------------------------------------------------------------------------------- /src/tests/sys_tests.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] //suppress warning for these functions not being used in targets other than the 2 | // tests 3 | 4 | #[allow(unused_parens)] 5 | #[cfg(test)] 6 | pub mod sys_tests { 7 | use super::super::*; 8 | use crate::interface; 9 | use crate::safeposix::cage::{FileDescriptor::*, *}; 10 | use crate::safeposix::{cage::*, dispatcher::*, filesystem}; 11 | 12 | #[test] 13 | pub fn ut_lind_getpid() { 14 | //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, 15 | // and also performs clean env setup 16 | let _thelock = setup::lock_and_init(); 17 | let cage = interface::cagetable_getref(1); 18 | assert_eq!(cage.getpid_syscall(), 1); 19 | lindrustfinalize(); 20 | } 21 | 22 | #[test] 23 | pub fn ut_lind_getppid() { 24 | //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, 25 | // and also performs clean env setup 26 | let _thelock = setup::lock_and_init(); 27 | let cage = interface::cagetable_getref(1); 28 | cage.fork_syscall(2); 29 | let cage2 = interface::cagetable_getref(2); 30 | assert_eq!(cage2.getppid_syscall(), 1); 31 | lindrustfinalize(); 32 | } 33 | 34 | #[test] 35 | pub fn ut_lind_getuid() { 36 | //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, 37 | // and also performs clean env setup 38 | let _thelock = setup::lock_and_init(); 39 | let cage = interface::cagetable_getref(1); 40 | // The first call to geteuid always returns -1 41 | assert_eq!(cage.getuid_syscall(), -1); 42 | // Subsequent calls return the default value 43 | assert_eq!(cage.getuid_syscall(), DEFAULT_UID as i32); 44 | lindrustfinalize() 45 | } 46 | 47 | #[test] 48 | pub fn ut_lind_geteuid() { 49 | //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, 50 | // and also performs clean env setup 51 | let _thelock = setup::lock_and_init(); 52 | let cage = interface::cagetable_getref(1); 53 | // The first call to geteuid always returns -1 54 | assert_eq!(cage.geteuid_syscall(), -1); 55 | // Subsequent calls return the default value 56 | assert_eq!(cage.geteuid_syscall(), DEFAULT_UID as i32); 57 | lindrustfinalize() 58 | } 59 | 60 | #[test] 61 | pub fn ut_lind_getgid() { 62 | //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, 63 | // and also performs clean env setup 64 | let _thelock = setup::lock_and_init(); 65 | let cage = interface::cagetable_getref(1); 66 | // The first call to geteuid always returns -1 67 | assert_eq!(cage.getgid_syscall(), -1); 68 | // Subsequent calls return the default value 69 | assert_eq!(cage.getgid_syscall(), DEFAULT_GID as i32); 70 | lindrustfinalize() 71 | } 72 | 73 | #[test] 74 | pub fn ut_lind_getegid() { 75 | //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, 76 | // and also performs clean env setup 77 | let _thelock = setup::lock_and_init(); 78 | let cage = interface::cagetable_getref(1); 79 | // The first call to geteuid always returns -1 80 | assert_eq!(cage.getegid_syscall(), -1); 81 | // Subsequent calls return the default value 82 | assert_eq!(cage.getegid_syscall(), DEFAULT_GID as i32); 83 | lindrustfinalize() 84 | } 85 | 86 | #[test] 87 | pub fn ut_lind_fork() { 88 | // Since the fork syscall is heavily tested in relation to other syscalls 89 | // we only perform simple checks for testing the sanity of the fork syscall 90 | //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, 91 | // and also performs clean env setup 92 | let _thelock = setup::lock_and_init(); 93 | let cage = interface::cagetable_getref(1); 94 | // Spawn a new child object using the fork syscall 95 | cage.fork_syscall(2); 96 | // Search for the new cage object with cage_id = 2 97 | let child_cage = interface::cagetable_getref(2); 98 | // Assert the parent value is the the id of the first cage object 99 | assert_eq!(child_cage.getppid_syscall(), 1); 100 | // Assert that the cage id of the child is the value passed in the original fork 101 | // syscall 102 | assert_eq!(child_cage.getuid_syscall(), -1); 103 | assert_eq!(child_cage.getuid_syscall(), DEFAULT_UID as i32); 104 | lindrustfinalize(); 105 | } 106 | 107 | #[test] 108 | pub fn ut_lind_exit() { 109 | // Since exit function is heavily used and tested in other syscalls and their 110 | // tests We only perform preliminary checks for checking the sanity of 111 | // this syscall We don't check for cases such as exiting a cage twice - 112 | // since the exiting process is handled by the NaCl runtime - and it 113 | // ensures that a cage does not exit twice acquiring a lock on TESTMUTEX 114 | // prevents other tests from running concurrently, and also performs 115 | // clean env setup 116 | let _thelock = setup::lock_and_init(); 117 | let cage = interface::cagetable_getref(1); 118 | // Call the exit call 119 | assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); 120 | lindrustfinalize(); 121 | } 122 | 123 | #[test] 124 | pub fn ut_lind_exec() { 125 | //acquiring a lock on TESTMUTEX prevents other tests from running concurrently, 126 | // and also performs clean env setup 127 | let _thelock = setup::lock_and_init(); 128 | let cage1 = interface::cagetable_getref(1); 129 | // Spawn a new child 130 | cage1.fork_syscall(2); 131 | // Assert that the fork was correct 132 | let child_cage = interface::cagetable_getref(2); 133 | assert_eq!(child_cage.getuid_syscall(), -1); 134 | assert_eq!(child_cage.getuid_syscall(), DEFAULT_UID as i32); 135 | // Spawn exec and check if it returns 0 136 | assert_eq!(cage1.exec_syscall(2), 0); 137 | lindrustfinalize(); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/tools/fs_utils.rs: -------------------------------------------------------------------------------- 1 | #![feature(rustc_private)] //for private crate imports for tests 2 | #![feature(vec_into_raw_parts)] 3 | #![feature(duration_constants)] 4 | #![feature(unix_file_vectored_at)] 5 | #![allow(unused)] 6 | 7 | /// Author: Jonathan Singer 8 | /// 9 | /// This file provides a command line interface for interacting in certain ways 10 | /// with the lind file system from the host, such as copying files from the host 11 | /// into lind, removing files and directories, and listing files in the lind fs, 12 | /// and more 13 | /// 14 | /// This interface should be sufficient for anything we'd need to do between 15 | /// lind and the host 16 | use std::env; 17 | use std::iter::repeat; 18 | 19 | mod interface; 20 | mod lib_fs_utils; 21 | mod safeposix; 22 | use lib_fs_utils::*; 23 | use safeposix::{ 24 | cage::*, 25 | dispatcher::{lindrustfinalize, lindrustinit}, 26 | filesystem::*, 27 | }; 28 | 29 | fn lind_tree(cage: &Cage, path: &str, indentlevel: usize) { 30 | let mut lindstat_res: StatData = StatData::default(); 31 | let stat_us = cage.stat_syscall(path, &mut lindstat_res); 32 | if stat_us == 0 { 33 | if !is_dir(lindstat_res.st_mode) { 34 | eprintln!("Tree must be run on a directory!"); 35 | return; 36 | } 37 | 38 | //visit the children of this directory, and show them all as being children of 39 | // this directory in the tree 40 | visit_children( 41 | cage, 42 | path, 43 | Some(indentlevel), 44 | |childcage, childpath, isdir, childindentlevelopt| { 45 | let childindentlevel = childindentlevelopt.unwrap(); 46 | //lines to connect non-parent ancestors to their remaining children(if any) 47 | print!("{}", "| ".repeat(childindentlevel)); 48 | //line to connect parent to its child 49 | print!("{}", "|---"); 50 | //actually print out file name 51 | println!("{}", childpath); 52 | 53 | //recursive call for child 54 | if isdir { 55 | lind_tree(childcage, childpath, childindentlevel + 1); 56 | } 57 | }, 58 | ); 59 | } else { 60 | eprintln!("No such directory exists!"); 61 | } 62 | } 63 | 64 | fn lind_ls(cage: &Cage, path: &str) { 65 | let mut lindstat_res: StatData = StatData::default(); 66 | let stat_us = cage.stat_syscall(path, &mut lindstat_res); 67 | 68 | if stat_us == 0 { 69 | if is_dir(lindstat_res.st_mode) { 70 | //for each child, if it's a directory, print its name with a slash, otherwise 71 | // omit the slash 72 | visit_children(cage, path, None, |_childcage, childpath, isdir, _| { 73 | if isdir { 74 | print!("{}/ ", childpath); 75 | } else { 76 | print!("{} ", childpath); 77 | } 78 | }); 79 | } else { 80 | print!("{} ", path); 81 | } 82 | println!(); 83 | } else { 84 | eprintln!("No such file exists!"); 85 | } 86 | } 87 | 88 | fn print_usage() { 89 | println!( 90 | " 91 | Usage: lind_fs_utils [commandname] [arguments...] 92 | 93 | Where commandname is one of the following: 94 | 95 | cp [hostsource] [linddest] : Copies files from the host file system into the lind filesystem. 96 | For example, cp bar/etc/passwd /etc/passwd will copy the 97 | former file in the host file system to the latter in lind's fs. 98 | Directories are handled recursively, cp bar/etc /etc/ will make a 99 | directory at /etc in the lind fs, and then populate it with all 100 | of the files in the root fs. 101 | deltree [linddir] : Delete a directory on the lind file system and all it contains 102 | format : Make a new blank fs, removing the current one 103 | help : Print this message 104 | ls [lindpath] : List the contents of a lind file system directory 105 | mkdir [linddir1...] : Create a lind file system directory (for each arg) 106 | rm [lindfile1...] : Delete a file on the lind file system 107 | rmdir [linddir1...] : Delete a directory on the lind file system 108 | tree [startlindpath] : Print the lindfs file tree starting at the specified directory 109 | Assumes root directory if no starting path is specified. 110 | update [hostsource] [linddest] : Copies files from the host file system into the lind filesystem. 111 | Will not copy files if the host and lind files are identical. 112 | For example, update bar/etc/passwd /etc/passwd will copy the 113 | former file in the host file system to the latter in lind's fs if 114 | the latter does not exist or is not identical to the former. 115 | Directories are handled recursively, cp bar/etc /etc/ will make a 116 | directory at /etc in the lind fs, and then populate it with all 117 | of the files in the root fs, with identical files being skipped. 118 | " 119 | ); 120 | } 121 | 122 | fn main() { 123 | lindrustinit(0); // no verbosity 124 | let mut args = env::args(); 125 | let utilcage = Cage { 126 | cageid: 0, 127 | cwd: interface::RustLock::new(interface::RustRfc::new(interface::RustPathBuf::from("/"))), 128 | parent: 0, 129 | filedescriptortable: init_fdtable(), 130 | cancelstatus: interface::RustAtomicBool::new(false), 131 | getgid: interface::RustAtomicI32::new(-1), 132 | getuid: interface::RustAtomicI32::new(-1), 133 | getegid: interface::RustAtomicI32::new(-1), 134 | geteuid: interface::RustAtomicI32::new(-1), 135 | rev_shm: interface::Mutex::new(vec![]), 136 | mutex_table: interface::RustLock::new(vec![]), 137 | cv_table: interface::RustLock::new(vec![]), 138 | sem_table: interface::RustHashMap::new(), 139 | thread_table: interface::RustHashMap::new(), 140 | signalhandler: interface::RustHashMap::new(), 141 | sigset: interface::RustHashMap::new(), 142 | pendingsigset: interface::RustHashMap::new(), 143 | main_threadid: interface::RustAtomicU64::new(0), 144 | interval_timer: interface::IntervalTimer::new(0), 145 | }; 146 | 147 | args.next(); //first arg is executable, we don't care 148 | let command = if let Some(cmd) = args.next() { 149 | cmd 150 | } else { 151 | print_usage(); 152 | return; //print usage 153 | }; 154 | 155 | match command.as_str() { 156 | "help" | "usage" => { 157 | print_usage(); 158 | } 159 | 160 | "cp" => { 161 | let source = args.next().expect("cp needs 2 arguments"); 162 | let dest = args.next().expect("cp needs 2 arguments"); 163 | args.next() 164 | .and_then:: Option>(|_| { 165 | panic!("cp cannot take more than 2 arguments") 166 | }); 167 | cp_dir_into_lind( 168 | &utilcage, 169 | interface::RustPath::new(&source), 170 | dest.as_str(), 171 | true, 172 | ); 173 | } 174 | 175 | "update" => { 176 | let source = args.next().expect("update needs 2 arguments"); 177 | let dest = args.next().expect("update needs 2 arguments"); 178 | args.next() 179 | .and_then:: Option>(|_| { 180 | panic!("update cannot take more than 2 arguments") 181 | }); 182 | update_dir_into_lind(&utilcage, interface::RustPath::new(&source), dest.as_str()); 183 | } 184 | 185 | "ls" => { 186 | let file = args.next().expect("ls needs 1 argument"); 187 | args.next() 188 | .and_then:: Option>(|_| { 189 | panic!("ls cannot take more than 1 argument") 190 | }); 191 | lind_ls(&utilcage, file.as_str()); 192 | } 193 | 194 | "tree" => { 195 | let rootdir = if let Some(dirstr) = args.next() { 196 | dirstr 197 | } else { 198 | "/".to_owned() 199 | }; 200 | println!("{}", rootdir); 201 | lind_tree(&utilcage, rootdir.as_str(), 0); 202 | } 203 | 204 | "format" => { 205 | lind_deltree(&utilcage, "/"); //This doesn't actually fully remove all of the linddata files... TODO: debug 206 | 207 | let mut logobj = LOGMAP.write(); 208 | let log = logobj.take().unwrap(); 209 | let _close = log.close().unwrap(); 210 | drop(logobj); 211 | let _logremove = interface::removefile(LOGFILENAME.to_string()); 212 | 213 | format_fs(); 214 | return; 215 | } 216 | 217 | "deltree" => { 218 | let rootdir = args.next().expect("deltree needs 1 argument"); 219 | args.next() 220 | .and_then:: Option>(|_| { 221 | panic!("deltree cannot take more than 1 argument") 222 | }); 223 | lind_deltree(&utilcage, rootdir.as_str()); 224 | } 225 | 226 | "rm" => { 227 | for file in args { 228 | utilcage.unlink_syscall(file.as_str()); 229 | } 230 | } 231 | 232 | "mkdir" => { 233 | for dir in args { 234 | utilcage.mkdir_syscall(dir.as_str(), S_IRWXA); 235 | } 236 | } 237 | 238 | "rmdir" => { 239 | for dir in args { 240 | utilcage.chmod_syscall(dir.as_str(), S_IRWXA); 241 | utilcage.rmdir_syscall(dir.as_str()); 242 | } 243 | } 244 | 245 | _ => { 246 | eprintln!("Error, command unknown"); 247 | return; 248 | } 249 | } 250 | lindrustfinalize(); 251 | } 252 | -------------------------------------------------------------------------------- /src/tools/interface: -------------------------------------------------------------------------------- 1 | ../interface/ -------------------------------------------------------------------------------- /src/tools/lib_fs_utils.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] //suppress warning for these functions not being used in main library target 2 | 3 | use std::ffi::CStr; 4 | use std::fs::File; 5 | use std::io::{prelude, Read}; 6 | use std::os::raw::c_char; 7 | 8 | use crate::interface; 9 | use crate::interface::errnos::{syscall_error, Errno}; 10 | use crate::interface::types::{ClippedDirent, CLIPPED_DIRENT_SIZE}; 11 | use crate::safeposix::{cage::*, filesystem::*}; 12 | 13 | const LINUX_MAX_RW_COUNT: usize = 0x7FFFF000; 14 | 15 | //we currently handle symlinks as normal files 16 | 17 | pub fn update_dir_into_lind(cage: &Cage, hostfilepath: &interface::RustPath, lindfilepath: &str) { 18 | if hostfilepath.exists() { 19 | if let Ok(_) = hostfilepath.read_link() { 20 | println!("following symlink at {:?} on host fs", hostfilepath); 21 | } //if read_link succeeds it's a symlink, whose destination must exist 22 | // because of the nature of the .exists function 23 | } else { 24 | eprintln!("Cannot locate file on host fs: {:?}", hostfilepath); 25 | return; 26 | } 27 | 28 | //update directly if not a directory on the host, otherwise recursively handle 29 | // children 30 | if hostfilepath.is_file() { 31 | update_into_lind(cage, hostfilepath, lindfilepath); 32 | } else { 33 | let children = hostfilepath.read_dir().unwrap(); 34 | for wrappedchild in children { 35 | let child = wrappedchild.unwrap(); 36 | let newlindpath = if lindfilepath.ends_with("/") { 37 | format!("{}{}", lindfilepath, child.file_name().to_str().unwrap()) 38 | } else { 39 | format!("{}/{}", lindfilepath, child.file_name().to_str().unwrap()) 40 | }; 41 | update_dir_into_lind(cage, child.path().as_path(), newlindpath.as_str()); 42 | } 43 | } 44 | } 45 | 46 | fn update_into_lind(cage: &Cage, hostfilepath: &interface::RustPath, lindfilepath: &str) { 47 | if !hostfilepath.exists() || !hostfilepath.is_file() { 48 | println!( 49 | "{:?} does not exist or is not a regular file, skipping", 50 | hostfilepath 51 | ); 52 | return; 53 | } 54 | let fmetadata = hostfilepath.metadata().unwrap(); 55 | 56 | let host_size = fmetadata.len(); 57 | let mut lindstat_res: StatData = StatData::default(); 58 | let stat_us = cage.stat_syscall(lindfilepath, &mut lindstat_res); 59 | 60 | let lind_exists; 61 | let lind_isfile; 62 | let lind_size; 63 | if stat_us < 0 { 64 | lind_exists = false; 65 | lind_isfile = false; 66 | lind_size = 0; 67 | } else { 68 | lind_exists = true; 69 | lind_isfile = is_reg(lindstat_res.st_mode); 70 | lind_size = lindstat_res.st_size; 71 | } 72 | 73 | if lind_exists && !lind_isfile { 74 | println!( 75 | "{:?} on lind file system is not a regular file, skipping", 76 | hostfilepath 77 | ); 78 | return; 79 | } 80 | 81 | //compare files to tell whether they are identical 82 | let samefile = if host_size as usize == lind_size { 83 | let mut hostslice = vec![0u8; lind_size]; 84 | let mut lindslice = vec![0u8; lind_size]; 85 | let mut hostfile = File::open(hostfilepath).unwrap(); 86 | hostfile.read(hostslice.as_mut_slice()).unwrap(); 87 | let lindfd = cage.open_syscall(lindfilepath, O_RDONLY | O_CREAT, S_IRWXA); 88 | cage.read_syscall(lindfd, lindslice.as_mut_ptr(), lind_size); 89 | cage.close_syscall(lindfd); 90 | hostslice == lindslice 91 | } else { 92 | false 93 | }; 94 | 95 | //if they are not the same file, remove the lind file and replace it with the 96 | // host file 97 | if !samefile { 98 | if lind_exists { 99 | cage.unlink_syscall(lindfilepath); 100 | println!("removing {} on lind file system", lindfilepath); 101 | } 102 | cp_into_lind(cage, hostfilepath, lindfilepath, true); 103 | } else { 104 | println!( 105 | "Same files on host and lind--{:?} and {}, skipping", 106 | hostfilepath, lindfilepath 107 | ); 108 | } 109 | } 110 | 111 | pub fn cp_dir_into_lind( 112 | cage: &Cage, 113 | hostfilepath: &interface::RustPath, 114 | lindfilepath: &str, 115 | create_missing_dirs: bool, 116 | ) { 117 | if hostfilepath.exists() { 118 | if let Ok(_) = hostfilepath.read_link() { 119 | println!("following symlink at {:?} on host fs", hostfilepath); 120 | } //if read_link succeeds it's a symlink, whose destination must exist 121 | // because of the nature of the .exists function 122 | } else { 123 | eprintln!("Cannot locate file on host fs: {:?}", hostfilepath); 124 | return; 125 | } 126 | 127 | //update directly if not a directory on the host, otherwise recursively handle 128 | // children 129 | if hostfilepath.is_file() { 130 | cp_into_lind(cage, hostfilepath, lindfilepath, create_missing_dirs); 131 | } else if hostfilepath.is_dir() { 132 | let children = hostfilepath.read_dir().unwrap(); 133 | for wrappedchild in children { 134 | let child = wrappedchild.unwrap(); 135 | let newlindpath = if lindfilepath.ends_with("/") { 136 | format!("{}{}", lindfilepath, child.file_name().to_str().unwrap()) 137 | } else { 138 | format!("{}/{}", lindfilepath, child.file_name().to_str().unwrap()) 139 | }; 140 | cp_dir_into_lind( 141 | cage, 142 | child.path().as_path(), 143 | newlindpath.as_str(), 144 | create_missing_dirs, 145 | ); 146 | } 147 | } 148 | } 149 | 150 | fn cp_into_lind( 151 | cage: &Cage, 152 | hostfilepath: &interface::RustPath, 153 | lindfilepath: &str, 154 | create_missing_dirs: bool, 155 | ) { 156 | if !hostfilepath.exists() { 157 | eprintln!("Cannot locate file on host fs: {:?}", hostfilepath); 158 | return; 159 | } 160 | if !hostfilepath.is_file() { 161 | eprintln!("File is not a regular file on host fs: {:?}", hostfilepath); 162 | return; 163 | } 164 | 165 | let lindtruepath = normpath(convpath(lindfilepath), cage); 166 | 167 | //if a directory in the lindfilepath does not exist in the lind file system, 168 | // create it! 169 | let mut ancestor = interface::RustPathBuf::from("/"); 170 | for component in lindtruepath.parent().unwrap().components() { 171 | ancestor.push(component); 172 | let mut lindstat_res: StatData = StatData::default(); 173 | 174 | //check whether file exists 175 | let stat_us = cage.stat_syscall(ancestor.to_str().unwrap(), &mut lindstat_res); 176 | if stat_us == 0 { 177 | if !is_dir(lindstat_res.st_mode) { 178 | eprintln!("Fatal error in trying to create child of non-directory file"); 179 | return; 180 | } 181 | continue; 182 | } 183 | if stat_us != -(Errno::ENOENT as i32) { 184 | eprintln!("Fatal error in trying to get lind file path"); 185 | return; 186 | } 187 | 188 | //check whether we are supposed to create missing directories, and whether we'd 189 | // be clobbering anything to do so (if so error out) 190 | if create_missing_dirs { 191 | if cage.mkdir_syscall(ancestor.to_str().unwrap(), S_IRWXA) != 0 { 192 | //let's not mirror stat data 193 | eprintln!("Lind fs path does not exist but should not be created (is rooted at non-directory) {:?}", ancestor); 194 | return; 195 | } 196 | } else { 197 | eprintln!( 198 | "Lind fs path does not exist but should not be created {:?}", 199 | ancestor 200 | ); 201 | return; 202 | } 203 | } 204 | 205 | //copy file contents into lind file system 206 | let mut host_fileobj = File::open(hostfilepath).unwrap(); 207 | let mut filecontents: Vec = Vec::new(); 208 | host_fileobj.read_to_end(&mut filecontents).unwrap(); 209 | 210 | let lindfd = cage.open_syscall( 211 | lindtruepath.to_str().unwrap(), 212 | O_CREAT | O_TRUNC | O_WRONLY, 213 | S_IRWXA, 214 | ); 215 | assert!(lindfd >= 0); 216 | 217 | let veclen = filecontents.len(); 218 | let mut writtenlen: usize = 0; 219 | 220 | //on Linux, write() (and similar system calls) will transfer at most 0x7ffff000 221 | // (2,147,479,552) bytes dividing filecontents into chunks of 0x7ffff000 222 | // (2,147,479,552) bytes and writing each chunk 223 | for chunk in filecontents.chunks(LINUX_MAX_RW_COUNT) { 224 | writtenlen += cage.write_syscall(lindfd, chunk.as_ptr(), chunk.len()) as usize; 225 | } 226 | //confirm that write succeeded 227 | assert_eq!(veclen, writtenlen); 228 | 229 | //get diagnostic data to print 230 | let mut lindstat_res: StatData = StatData::default(); 231 | let _stat_us = cage.fstat_syscall(lindfd, &mut lindstat_res); 232 | let inode = lindstat_res.st_ino; 233 | 234 | assert_eq!(cage.close_syscall(lindfd), 0); 235 | 236 | println!("Copied {:?} as {} ({})", hostfilepath, lindfilepath, inode); 237 | } 238 | 239 | pub fn visit_children( 240 | cage: &Cage, 241 | path: &str, 242 | arg: Option, 243 | visitor: fn(&Cage, &str, bool, Option), 244 | ) { 245 | //get buffer in which getdents will write its stuff 246 | let mut bigbuffer = [0u8; 65536]; 247 | let dentptr = bigbuffer.as_mut_ptr(); 248 | 249 | let dirfd = cage.open_syscall(path, O_RDONLY, 0); 250 | assert!(dirfd >= 0); 251 | 252 | loop { 253 | let direntres = cage.getdents_syscall(dirfd, dentptr, 65536); 254 | 255 | //if we've read every entry in this directory, we're done 256 | if direntres == 0 { 257 | break; 258 | } 259 | 260 | let mut dentptrindex = 0isize; 261 | 262 | //while there are still more entries to read 263 | while dentptrindex < direntres as isize { 264 | //get information for where the next entry is (if relevant) 265 | let clipped_dirent_ptr = dentptr.wrapping_offset(dentptrindex) as *mut ClippedDirent; 266 | let clipped_dirent = unsafe { &*clipped_dirent_ptr }; 267 | 268 | //get the file name for the child 269 | let cstrptr = dentptr.wrapping_offset(dentptrindex + CLIPPED_DIRENT_SIZE as isize); 270 | let filenamecstr = unsafe { CStr::from_ptr(cstrptr as *const c_char) }; 271 | let filenamestr = filenamecstr.to_str().unwrap(); 272 | 273 | dentptrindex += clipped_dirent.d_reclen as isize; 274 | 275 | //ignore these entries 276 | if filenamestr == "." || filenamestr == ".." { 277 | continue; 278 | } 279 | 280 | let fullstatpath = if path.ends_with("/") { 281 | [path, filenamestr].join("") 282 | } else { 283 | [path, "/", filenamestr].join("") 284 | }; 285 | 286 | //stat to tell whether it's a directory 287 | let mut lindstat_res: StatData = StatData::default(); 288 | let _stat_us = cage.stat_syscall(fullstatpath.as_str(), &mut lindstat_res); 289 | 290 | //call the visitor function on the child path 291 | visitor( 292 | cage, 293 | fullstatpath.as_str(), 294 | is_dir(lindstat_res.st_mode), 295 | arg, 296 | ); 297 | } 298 | } 299 | cage.close_syscall(dirfd); 300 | } 301 | 302 | pub fn lind_deltree(cage: &Cage, path: &str) { 303 | let mut lindstat_res: StatData = StatData::default(); 304 | let stat_us = cage.stat_syscall(path, &mut lindstat_res); 305 | 306 | if stat_us == 0 { 307 | if !is_dir(lindstat_res.st_mode) { 308 | cage.unlink_syscall(path); 309 | return; 310 | } else { 311 | //Parent directory's write flag should be set before 312 | //iterating through the child directories, so that they 313 | //could be removed. 314 | //This is important for unit tests where a non empty 315 | //parent directory ends up having write flags off at 316 | //the end of the test. The next unit test's call to 317 | //`lind_deltree()` will not be able to remove the child 318 | //directories if the parent directory's write flags 319 | //are not set before iterating through the child 320 | //directories. 321 | cage.chmod_syscall(path, S_IRWXA); 322 | //remove all children recursively 323 | visit_children(cage, path, None, |childcage, childpath, isdir, _| { 324 | if isdir { 325 | lind_deltree(childcage, childpath); 326 | } else { 327 | childcage.unlink_syscall(childpath); 328 | } 329 | }); 330 | //remove specified directory now that it is empty 331 | cage.rmdir_syscall(path); 332 | } 333 | } else { 334 | eprintln!("No such directory exists!"); 335 | } 336 | } 337 | -------------------------------------------------------------------------------- /src/tools/safeposix: -------------------------------------------------------------------------------- 1 | ../safeposix/ --------------------------------------------------------------------------------