From 409aa5845e97f66cbc8098c16395292c7b755d90 Mon Sep 17 00:00:00 2001 From: Yashaswi Makula Date: Thu, 12 Sep 2024 15:19:58 -0400 Subject: [PATCH] Porting RawPosix to Lind_Project (#6) * change to use V6 * accept debug * debug accept * debug accept * debug accept * debug accept * debug accept * debug accept * debug accept * debug accept * debug accept * debug accept * debug accept * debug accept * debug accept * debug accept * debug accept * debug accept * rm debug info * debug bind * debug connect * recvfrom * recvfrom * recvfrom * recvfrom * sem * sem * sem * sem * sem * poll * poll * poll * poll * poll * poll * poll * poll * poll * poll * poll * poll * poll * select * select * select * select * select * select * select * select * select * select * select * select * select * select * select * select * select * select * select * select * select * select * select * select * select * select * select * select * accept * accept * accept * accept * accept * accept * accept * accept * accept * accept * accept * setitimer/getrlimit/setrlimit * epoll * epoll * epoll_create * epoll_ctl * epoll_wait * epoll_wait * epoll_wait * epoll_wait * epoll_wait * epoll_wait * epoll_wait * epoll_wait * epoll_wait * epoll - add mapping table * epoll * epoll * epoll * epoll * epoll * epoll cleanup * add writev * cloexec * cloexec * fix warnings * removing panic in netcalls * add err handling for fscalls * err handling for get_fd * getdents * open * unlink/link * fix return value * fix return value * fix return value * fix return value * callback refine * add bind path conversion * accept * accept * accept * accept * accept * accept * accept * bind * bind * bind * bind * bind * bind * connect/sendto * uds * debug * debug * debug * debug * debug postgres * [feature] add err handling for all libc:: calls * fix * mmap * getsockname * getsockname * getsockname * getsockname * getsockname * getsockname * getsockname * getsockname * getsockopt * getsockopt * getsockopt * getsockopt * getsockopt * getsockopt * getsockopt * getsockopt * getsockopt * getsockopt * comment out err print * comment out err print * select * bind * bind * bind * bind * fcntl * rm select print * accept * refine bind/connect * debug dashmap arr' * debug dashmap arr * debug dashmap arr * debug dashmap arr * debug dashmap arr * debug dashmap arr * debug dashmap arr * vec * arr * arr * arr * arr * arr * change to vec * change to vec * bind * bind * connect * rm bind/connect pt * bind * bind * bind * bind * bind * check fd_set * check fd_set * close * update fd interface and select syscall * select * debug select * debug select * debug select * debug select * debug select * debug select * printing fdtable details * printing fdtable details * printing fdtable details * printing fdtable details * printing fdtable details * printing fdtable details * made new container work * debug select * debug select * debug select * debug select * debug select * debug select * debug select * try fix by adding rm ref count in close * try to see why kernel fd start from 8 instead of 3 * try run lamp * socket * getpeername * getpeername * getpeername * getpeername * getpeername * getpeername * getpeername * getpeername * getpeername * getpeername * getpeername * getpeername * cleanup print * update the fdtable to newer version * check the error cause * debug * debug * debug * debug * debug * debug * debug * debug * debug * debug * comment out print --------- Co-authored-by: Alice W. --- .devcontainer/Dockerfile | 26 + .devcontainer/devcontainer.json | 20 + .github/pull_request_template.md | 35 + .github/workflows/lind-selfhost.yml | 63 + .github/workflows/rust.yml | 26 + .gitignore | 21 +- .rustfmt.toml | 5 + Cargo.toml | 63 + LICENSE | 377 ++-- Makefile | 14 + README.md | 49 +- benches/fs_open_close.rs | 72 + benches/fs_read_write.rs | 148 ++ benches/fs_read_write_seek.rs | 125 ++ benches/gen_getid.rs | 67 + benches/global_criterion_settings.rs | 5 + docs/RustPOSIX-README.jpg | Bin 0 -> 374822 bytes docs/SafePOSIX Rust Diagram.jpg | Bin 0 -> 56951 bytes gen_netdevs.c | 30 + gen_netdevs.sh | 19 + src/example_grates/commonconstants.rs | 104 + src/example_grates/dashmaparrayglobal.rs | 728 +++++++ src/example_grates/dashmapvecglobal.rs | 969 +++++++++ src/example_grates/imfs_grate.rs | 218 +++ src/example_grates/imp_grate.rs | 242 +++ src/example_grates/lindfs_grate.rs | 215 ++ src/example_grates/log_grate.rs | 40 + src/example_grates/mod.rs | 15 + src/example_grates/muthashmaxglobal.rs | 800 ++++++++ src/example_grates/route_grate.rs | 147 ++ src/example_grates/threei.rs | 196 ++ src/example_grates/vanillaglobal.rs | 754 +++++++ src/interface/comm.rs | 644 ++++++ src/interface/errnos.rs | 439 +++++ src/interface/file.rs | 481 +++++ src/interface/misc.rs | 633 ++++++ src/interface/mod.rs | 19 + src/interface/pipe.rs | 200 ++ src/interface/timer.rs | 141 ++ src/interface/types.rs | 1079 ++++++++++ src/lib.rs | 14 + src/safeposix/cage.rs | 212 ++ src/safeposix/dispatcher.rs | 1262 ++++++++++++ src/safeposix/filesystem.rs | 619 ++++++ src/safeposix/mod.rs | 6 + src/safeposix/net.rs | 564 ++++++ src/safeposix/shm.rs | 146 ++ src/safeposix/syscalls/fs_calls.rs | 2273 ++++++++++++++++++++++ src/safeposix/syscalls/fs_constants.rs | 196 ++ src/safeposix/syscalls/mod.rs | 12 + src/safeposix/syscalls/net_calls.rs | 1450 ++++++++++++++ src/safeposix/syscalls/net_constants.rs | 392 ++++ src/safeposix/syscalls/sys_calls.rs | 517 +++++ src/safeposix/syscalls/sys_constants.rs | 77 + src/tests/fs_tests.rs | 1404 +++++++++++++ src/tests/ipc_tests.rs | 342 ++++ src/tests/mod.rs | 98 + src/tests/networking_tests.rs | 355 ++++ src/tools/fs_utils.rs | 250 +++ src/tools/interface | 1 + src/tools/lib_fs_utils.rs | 324 +++ src/tools/safeposix | 1 + 62 files changed, 19532 insertions(+), 212 deletions(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/lind-selfhost.yml create mode 100644 .github/workflows/rust.yml create mode 100644 .rustfmt.toml create mode 100644 Cargo.toml create mode 100644 Makefile create mode 100644 benches/fs_open_close.rs create mode 100644 benches/fs_read_write.rs create mode 100644 benches/fs_read_write_seek.rs create mode 100644 benches/gen_getid.rs create mode 100644 benches/global_criterion_settings.rs create mode 100644 docs/RustPOSIX-README.jpg create mode 100644 docs/SafePOSIX Rust Diagram.jpg create mode 100644 gen_netdevs.c create mode 100755 gen_netdevs.sh create mode 100644 src/example_grates/commonconstants.rs create mode 100644 src/example_grates/dashmaparrayglobal.rs create mode 100644 src/example_grates/dashmapvecglobal.rs create mode 100644 src/example_grates/imfs_grate.rs create mode 100644 src/example_grates/imp_grate.rs create mode 100644 src/example_grates/lindfs_grate.rs create mode 100644 src/example_grates/log_grate.rs create mode 100644 src/example_grates/mod.rs create mode 100644 src/example_grates/muthashmaxglobal.rs create mode 100644 src/example_grates/route_grate.rs create mode 100644 src/example_grates/threei.rs create mode 100644 src/example_grates/vanillaglobal.rs create mode 100644 src/interface/comm.rs create mode 100644 src/interface/errnos.rs create mode 100644 src/interface/file.rs create mode 100644 src/interface/misc.rs create mode 100644 src/interface/mod.rs create mode 100644 src/interface/pipe.rs create mode 100644 src/interface/timer.rs create mode 100644 src/interface/types.rs create mode 100644 src/lib.rs create mode 100644 src/safeposix/cage.rs create mode 100644 src/safeposix/dispatcher.rs create mode 100644 src/safeposix/filesystem.rs create mode 100644 src/safeposix/mod.rs create mode 100644 src/safeposix/net.rs create mode 100644 src/safeposix/shm.rs create mode 100644 src/safeposix/syscalls/fs_calls.rs create mode 100644 src/safeposix/syscalls/fs_constants.rs create mode 100644 src/safeposix/syscalls/mod.rs create mode 100644 src/safeposix/syscalls/net_calls.rs create mode 100644 src/safeposix/syscalls/net_constants.rs create mode 100644 src/safeposix/syscalls/sys_calls.rs create mode 100644 src/safeposix/syscalls/sys_constants.rs create mode 100644 src/tests/fs_tests.rs create mode 100644 src/tests/ipc_tests.rs create mode 100644 src/tests/mod.rs create mode 100644 src/tests/networking_tests.rs create mode 100644 src/tools/fs_utils.rs create mode 120000 src/tools/interface create mode 100644 src/tools/lib_fs_utils.rs create mode 120000 src/tools/safeposix diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..f1194d55 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,26 @@ +# Use an official Rust image as a parent image +FROM ubuntu:latest +ARG DEBIAN_FRONTEND=noninteractive +ARG arch=x86_64 + +# Install tools and dependencies for building Rust projects +RUN apt-get update && apt-get install -y \ + build-essential \ + pkg-config \ + curl \ + libssl-dev \ + git +# Install Rust from source +RUN curl https://sh.rustup.rs -sSf | sh -s -- -y +# Add cargo bin to PATH +ENV PATH="/root/.cargo/bin:${PATH}" + +# Install the nightly Rust toolchain +RUN rustup toolchain install nightly + +# Set the default toolchain to nightly +RUN rustup default nightly + +# Verify installation +RUN rustc --version + diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..7ecbda16 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,20 @@ +{ + "name": "Rust (Unstable)", + "build": { + "dockerfile": "Dockerfile" + }, + "customizations": { + "vscode": { + "settings": { + "terminal.integrated.shell.linux": "/bin/bash" + }, + "extensions": [ + "rust-lang.rust" + ] + } + }, + "forwardPorts": [], + "postCreateCommand": "cargo check", + "remoteUser": "root" +} + diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..36be3b9a --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,35 @@ +## Description + +Fixes # (issue) + + + + + +### Type of change + + + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] This change requires a documentation update + +## How Has This Been Tested? + + + + + +- Test A - `lind_project/tests/test_cases/test_a.c` +- Test B - `lind_project/tests/test_cases/test_b.c` + +## Checklist: + + + +- [ ] My code follows the style guidelines of this project +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] My changes generate no new warnings +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] Any dependent changes have been added to a pull request and/or merged in other modules (native-client, lind-glibc, lind-project) diff --git a/.github/workflows/lind-selfhost.yml b/.github/workflows/lind-selfhost.yml new file mode 100644 index 00000000..7ff4a0f0 --- /dev/null +++ b/.github/workflows/lind-selfhost.yml @@ -0,0 +1,63 @@ +name: RustPOSIX Build + +# Controls when the workflow will run +on: + push: + branches: + - develop + - main + pull_request: + branches: + - develop + - main + # Triggers the workflow on push or pull request events but only for the develop branch + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +jobs: + build: + runs-on: self-hosted + if: github.event.pull_request.draft == false + steps: + - name: Echo out branch values + run: | + echo github.base_ref: ${{ github.base_ref }} + echo github.head_ref: ${{ github.head_ref }} + echo github.ref: ${{ github.ref }} + cd /home/lind/lind_project/ + + - name: Checkout lind-project + run: | + git --git-dir /home/lind/lind_project/.git remote update origin --prune; + git --git-dir /home/lind/lind_project/.git checkout --force remotes/origin/develop; + + - name: In the land of RUSTPOSIX where the shadows lie (PR request) + if: github.head_ref != '' + run: | + git --git-dir /home/lind/lind_project/src/safeposix-rust/.git remote update origin --prune; + git --git-dir /home/lind/lind_project/src/safeposix-rust/.git checkout remotes/origin/${{ github.head_ref }}; + make rustposix; + + - name: In the land of RUSTPOSIX where the shadows lie (Develop/Push) + if: github.head_ref == '' + run: | + git --git-dir /home/lind/lind_project/src/safeposix-rust/.git remote update origin --prune; + git --git-dir /home/lind/lind_project/src/safeposix-rust/.git checkout remotes/origin/develop; + make rustposix; + + - name: One NACL to rule them all + run: | + git --git-dir /home/lind/lind_project/src/native_client/.git remote update origin --prune; + git --git-dir /home/lind/lind_project/src/native_client/.git checkout remotes/origin/develop; + make nacl; + - name: One GLIBC to find them + run: | + git --git-dir /home/lind/lind_project/src/lind_glibc/.git remote update origin --prune; + git --git-dir /home/lind/lind_project/src/lind_glibc/.git checkout remotes/origin/develop; + make glibc; + - name: One ring to INSTALL them all + run: | + make install; + - name: And in darkness TEST them + run: | + make test-verbose; diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 00000000..6f0a29b0 --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,26 @@ +name: Rust + +on: + push: + branches: [ "develop" ] + pull_request: + branches: [ "develop" ] + +env: + CARGO_TERM_COLOR: always + +jobs: + + build_and_test: + name: Rust project - latest + runs-on: ubuntu-latest + strategy: + matrix: + toolchain: + - nightly + steps: + - uses: actions/checkout@v4 + - run: ./gen_netdevs.sh + - run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }} + - run: cargo build --verbose + - run: cargo test --lib --verbose diff --git a/.gitignore b/.gitignore index d01bd1a9..ef637ae2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ # Generated by Cargo # will have compiled files and executables -debug/ -target/ +/target/ # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html @@ -10,12 +9,14 @@ Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk -# MSVC Windows builds of rustc generate these, which store debugging information -*.pdb -# RustRover -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ \ No newline at end of file +# Added by cargo + +/target + +**/net_devices + +linddata* +gen_netdevs +lind.metadata +lind.md.log diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 00000000..8a961074 --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,5 @@ +ignore = [ + "src/safeposix/syscalls/net_constants.rs", + "src/safeposix/syscalls/sys_constants.rs", + "src/safeposix/syscalls/fs_constants.rs", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..b6ce22a6 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,63 @@ +[package] +name = "rustposix" +version = "0.1.0" +authors = ["Nicholas Smith Renner ", "Jonathan Eli Singer ", "Tristan J. Brigham "] +edition = "2018" + +[lib] +path = "src/lib.rs" +# cdylib is a dynamically linkable library, which is great for linking into +# C programs and similar. rlib is needed for the criterion benchmarking libary +# and creates one of Rust's static libraries. We are currently not generating +# dylib files which are Rust's internal (non-stable) ABI. +# Source: https://users.rust-lang.org/t/what-is-the-difference-between-dylib-and-cdylib/28847/3 +crate-type = ["cdylib","rlib"] +test = true + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = { version = "1.0", features = ["derive", "rc"] } +serde_cbor = "0.10" +libc = "0.2" +ringbuf = "0.2.6" +dashmap = { version = "5.1", features=["serde"] } +parking_lot = "0.12" +bit-set = "0.5" + +[dependencies.lazy_static] +version = "1.0" +features = ["spin_no_std"] + +[dev-dependencies] +criterion = { version = "0.3", features = ["html_reports"]} +tempfile = "3.2.0" +grcov="0.8.19" # code coverage + +[[bin]] +name = "lind_fs_utils" +path = "src/tools/fs_utils.rs" + +# many benchmarks follow. Don't put any non-benchmarks below this... +[[bench]] +name = "gen_getid" +path = "benches/gen_getid.rs" +harness= false + +[[bench]] +name = "fs_open_close" +path = "benches/fs_open_close.rs" +harness= false + +[[bench]] +name = "fs_read_write" +path = "benches/fs_read_write.rs" +harness= false + +[[bench]] +name = "fs_read_write_seek" +path = "benches/fs_read_write_seek.rs" +harness= false + + +# Don't put any thing below this... benchmarks above! diff --git a/LICENSE b/LICENSE index 261eeb9e..ed9327c0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,176 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..0537cfc8 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +.PHONY: test clean + +test: clean + chmod +x gen_netdevs.sh + ./gen_netdevs.sh + cargo test --lib + +clean: + rm -f gen_netdevs net_devices + rm -f linddata.* + rm -f lind.metadata + +format: + cargo fmt -- --check diff --git a/README.md b/README.md index 2aaba7c0..8855c085 100644 --- a/README.md +++ b/README.md @@ -1 +1,48 @@ -# RawPOSIX \ No newline at end of file +# 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) + +More implementation details could be found at [wiki](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Home.md). + +## Contents + +* [Home](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Home.md) +* [Architecture](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Architecture.md) +* [Interface](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Interface.md) +* [Run Independently](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Run-Independently.md) +* [Security Model](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Security-Model.md) +* [Style Guide](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Style-Guide.md) +* [Testing and Debugging](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Testing-and-Debugging.md) + +## Run RustPOSIX-Rust + +Quick start +Use Develop branch for the most stable behaviour. + +```bash +docker build -t --platform .devcontainer +docker run -it + +``` + +This will create a quick container with rustposix build at your local changes. +helpful for exploration and easy testing. + +See reference at [Run RustPOSIX Independently](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Run-Independently.md) + +## Test RustPOSIX-Rust + +Use main branch for the most stable behaviour. + +```bash +cargo build +cargo test --lib +``` + +See reference at [Testing and Debugging](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Testing-and-Debugging.md) + +## Development Guideline + +* All PRs should be merged to the Develop branch + +* Any imports from the standard library or any crates should be done in an interface file + +More detailed guideline will be in [RustPOSIX's wiki](https://github.com/Lind-Project/lind-docs/blob/main/docs/RustPOSIX/Style-Guide.md) \ No newline at end of file diff --git a/benches/fs_open_close.rs b/benches/fs_open_close.rs new file mode 100644 index 00000000..b0d2ec73 --- /dev/null +++ b/benches/fs_open_close.rs @@ -0,0 +1,72 @@ +/* Benchmarks for the microvisor implementation. In general, I'm not doing + * results checking / assertations to avoid adding bias to the results. */ + +// I hate allowing this, but this is apparently a known issue for a lot of +// code with CStrings. https://github.com/rust-lang/rust/issues/78691 +// I've tried to sanity check where this occurs, but please, please, please +// double check these parts of the code! +#![allow(temporary_cstring_as_ptr)] + +use criterion::{criterion_group, criterion_main, Criterion}; + +use rustposix::interface; + +use std::ffi::*; + +use rustposix::safeposix::cage::*; + +// Using this to include my criterion settings from a single shared file. +// I did not use "use" or "mod" because benches/ isn't in the crate's usual +// namespace and I didn't want to either make a separate crate with a single, +// tiny file or add this file to the rustposix crate. +mod global_criterion_settings; + +pub fn run_benchmark(c: &mut Criterion) { + // I'm following the initialization workflow from the unit tests here. + // + // I'm using the lindrustinit to set up cages and the file system. + rustposix::safeposix::dispatcher::lindrustinit(0); + + // Since all system calls are a method of a cage object, I also need this + // reference. + let cage = interface::cagetable_getref(1); + + // --- COMPARING open / close CALLS ACROSS Lind + Native OS kernel --- + let mut group = c.benchmark_group("Compare fs:open+close"); + + // Should be similar. Use a linear scale... + group.plot_config( + criterion::PlotConfiguration::default().summary_scale(criterion::AxisScale::Linear), + ); + + // Let's see how fast various file system calls are + group.bench_function("TF01: Lind open+close", |b| { + b.iter(|| { + let fd = cage.open_syscall("foo", O_CREAT | O_TRUNC | O_WRONLY, S_IRWXA); + cage.close_syscall(fd); + }) + }); + + // For comparison let's time the native OS... + group.bench_function("TF01: Native OS kernel open+close", |b| { + b.iter(|| unsafe { + let fd = libc::open( + CString::new("/tmp/foo").unwrap().as_ptr(), + O_CREAT | O_TRUNC | O_WRONLY, + S_IRWXA, + ); + libc::close(fd); + }) + }); + group.finish(); + + // This cleans up in ways I do not fully understand. I think it ensures + // the file system is cleaned up + rustposix::safeposix::dispatcher::lindrustfinalize(); +} + +criterion_group!(name=benches; + // Add the global settings here so we don't type it everywhere + config=global_criterion_settings::get_criterion(); + targets=run_benchmark); +criterion_main!(benches); diff --git a/benches/fs_read_write.rs b/benches/fs_read_write.rs new file mode 100644 index 00000000..dcf73b91 --- /dev/null +++ b/benches/fs_read_write.rs @@ -0,0 +1,148 @@ +/* Benchmarks for the microvisor implementation. In general, I'm not doing + * results checking / assertations to avoid adding bias to the results. */ + +use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; + +use rustposix::interface; + +use std::ffi::*; + +use std::time::Duration; + +use rustposix::safeposix::cage::*; + +use rustposix::tests; + +// Using this to include my criterion settings from a single shared file. +// I did not use "use" or "mod" because benches/ isn't in the crate's usual +// namespace and I didn't want to either make a separate crate with a single, +// tiny file or add this file to the rustposix crate. +mod global_criterion_settings; + +pub fn run_benchmark(c: &mut Criterion) { + // I'm following the initialization workflow from the unit tests here. + // + // I'm using the lindrustinit to set up cages and the file system. + rustposix::safeposix::dispatcher::lindrustinit(0); + + // Since all system calls are a method of a cage object, I also need this + // reference. + let cage = interface::cagetable_getref(1); + + // --- COMPARING read + write w/o lseek CALLS ACROSS Lind + Native OS kernel --- + // This is separated because writeat and readat do a lot of seeking. It's + // useful to have a comparison which does not. + let mut group = c.benchmark_group("Compare fs:write+read"); + + // Should be similar. Use a linear scale... + group.plot_config( + criterion::PlotConfiguration::default().summary_scale(criterion::AxisScale::Linear), + ); + + // First do this for Lind + + // Reduce the time to reduce disk space needed and go faster. + // Default is 5s... + group.measurement_time(Duration::from_secs(2)); + + // Shorten the warm up time as well from 3s to this... + group.warm_up_time(Duration::from_secs(1)); + + // Iterate for different buffer sizes... + for buflen in [1, 64, 1024, 65536].iter() { + let deststring = tests::str2cbuf( + &String::from_utf8(vec![b'X'; *buflen]).expect("error building string"), + ); + + let fd = cage.open_syscall("foo", O_CREAT | O_TRUNC | O_WRONLY, S_IRWXA); + // Let's see how fast various file system calls are + group.bench_with_input( + BenchmarkId::new("TF02:Lind write", buflen), + buflen, + |b, buflen| { + b.iter(|| { + let _ = cage.write_syscall(fd, deststring, *buflen); + }) + }, + ); + + cage.lseek_syscall(fd, 0, SEEK_SET); + + let mut read_buffer = tests::sizecbuf(*buflen); + + group.bench_with_input( + BenchmarkId::new("TF02:Lind read", buflen), + buflen, + |b, buflen| { + b.iter(|| { + cage.read_syscall(fd, read_buffer.as_mut_ptr(), *buflen); + }) + }, + ); + + cage.close_syscall(fd); + cage.unlink_syscall("foo"); + } + + // Now do this for Native + + // Iterate for different buffer sizes... + for buflen in [1, 64, 1024, 65536].iter() { + let fd: c_int; + let c_str = CString::new("/tmp/foo").unwrap(); + + let path = c_str.into_raw() as *const u8; + + unsafe { + fd = libc::open(path, O_CREAT | O_TRUNC | O_WRONLY, S_IRWXA); + } + + let deststring = tests::str2cbuf( + &String::from_utf8(vec![b'X'; *buflen]).expect("error building string"), + ); + + // For comparison let's time the native OS... + group.bench_with_input( + BenchmarkId::new("TF02:Native write", buflen), + buflen, + |b, buflen| { + b.iter(|| unsafe { + let _ = libc::write(fd, deststring as *const c_void, *buflen); + }) + }, + ); + + unsafe { + libc::lseek(fd, 0, SEEK_SET); + } + + let mut read_buffer = tests::sizecbuf(*buflen); + + // For comparison let's time the native OS... + group.bench_with_input( + BenchmarkId::new("TF02:Native read", buflen), + buflen, + |b, buflen| { + b.iter(|| unsafe { + libc::read(fd, read_buffer.as_mut_ptr() as *mut c_void, *buflen); + }) + }, + ); + + unsafe { + libc::close(fd); + libc::unlink(path); + } + } + group.finish(); + + // This cleans up in ways I do not fully understand. I think it ensures + // the file system is cleaned up + rustposix::safeposix::dispatcher::lindrustfinalize(); +} + +criterion_group!(name=benches; + // Add the global settings here so we don't type it everywhere + config=global_criterion_settings::get_criterion(); + targets=run_benchmark); +criterion_main!(benches); diff --git a/benches/fs_read_write_seek.rs b/benches/fs_read_write_seek.rs new file mode 100644 index 00000000..bc7eec58 --- /dev/null +++ b/benches/fs_read_write_seek.rs @@ -0,0 +1,125 @@ +/* Benchmarks for the microvisor implementation. In general, I'm not doing + * results checking / assertations to avoid adding bias to the results. */ + +use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; + +use rustposix::interface; + +use std::ffi::*; + +use std::time::Duration; + +use rustposix::safeposix::cage::*; + +use rustposix::tests; + +// Using this to include my criterion settings from a single shared file. +// I did not use "use" or "mod" because benches/ isn't in the crate's usual +// namespace and I didn't want to either make a separate crate with a single, +// tiny file or add this file to the rustposix crate. +mod global_criterion_settings; + +pub fn run_benchmark(c: &mut Criterion) { + // I'm following the initialization workflow from the unit tests here. + // + // I'm using the lindrustinit to set up cages and the file system. + rustposix::safeposix::dispatcher::lindrustinit(0); + + // Since all system calls are a method of a cage object, I also need this + // reference. + let cage = interface::cagetable_getref(1); + + // --- COMPARING read + write + lseek CALLS ACROSS Lind + Native OS kernel --- + let mut group = c.benchmark_group("Compare fs:write+read+lseek"); + + // Should be similar. Use a linear scale... + group.plot_config( + criterion::PlotConfiguration::default().summary_scale(criterion::AxisScale::Linear), + ); + + // Reduce the time to go faster! Default is 5s... + group.measurement_time(Duration::from_secs(2)); + + // Shorten the warm up time as well from 3s to this... + group.warm_up_time(Duration::from_secs(1)); + + // --- Lind --- + + // Iterate for different buffer sizes... + for buflen in [1, 64, 1024, 65536].iter() { + let fd = cage.open_syscall("foo", O_CREAT | O_TRUNC | O_WRONLY, S_IRWXA); + + let deststring = tests::str2cbuf( + &String::from_utf8(vec![b'X'; *buflen]).expect("error building string"), + ); + + let read_buffer = tests::sizecbuf(*buflen).as_mut_ptr(); + // Let's see how fast various file system calls are + group.bench_with_input( + BenchmarkId::new("TF03:Lind write+read+lseek", buflen), + buflen, + |b, buflen| { + b.iter(|| { + let _ = cage.write_syscall(fd, deststring, *buflen); + cage.lseek_syscall(fd, 0, SEEK_SET); + cage.read_syscall(fd, read_buffer, *buflen); + cage.lseek_syscall(fd, 0, SEEK_SET); + }) + }, + ); + + cage.close_syscall(fd); + cage.unlink_syscall("foo"); + } + + // --- Native --- + + // Iterate for different buffer sizes... + for buflen in [1, 64, 1024, 65536].iter() { + let fd: c_int; + + let c_str = CString::new("/tmp/foo").unwrap(); + + let path = c_str.into_raw() as *const u8; + + unsafe { + fd = libc::open(path, O_CREAT | O_TRUNC | O_WRONLY, S_IRWXA); + } + + let deststring = tests::str2cbuf( + &String::from_utf8(vec![b'X'; *buflen]).expect("error building string"), + ); + + let read_buffer = tests::sizecbuf(*buflen).as_mut_ptr(); + + // For comparison let's time the native OS... + group.bench_with_input( + BenchmarkId::new("TF03:Native write+read+lseek", buflen), + buflen, + |b, buflen| { + b.iter(|| unsafe { + let _ = libc::write(fd, deststring as *const c_void, *buflen); + libc::lseek(fd, 0, SEEK_SET); + libc::read(fd, read_buffer as *mut c_void, *buflen); + libc::lseek(fd, 0, SEEK_SET); + }) + }, + ); + unsafe { + libc::close(fd); + libc::unlink(path); + } + } + + group.finish(); + + // This cleans up in ways I do not fully understand. I think it ensures + // the file system is cleaned up + rustposix::safeposix::dispatcher::lindrustfinalize(); +} + +criterion_group!(name=benches; + // Add the global settings here so we don't type it everywhere + config=global_criterion_settings::get_criterion(); + targets=run_benchmark); +criterion_main!(benches); diff --git a/benches/gen_getid.rs b/benches/gen_getid.rs new file mode 100644 index 00000000..688757a2 --- /dev/null +++ b/benches/gen_getid.rs @@ -0,0 +1,67 @@ +/* Benchmarks for the microvisor implementation. In general, I'm not doing + * results checking / assertations to avoid adding bias to the results. */ + +use criterion::{criterion_group, criterion_main, Criterion}; + +use rustposix::interface; + +// Using this to include my criterion settings from a single shared file. +// I did not use "use" or "mod" because benches/ isn't in the crate's usual +// namespace and I didn't want to either make a separate crate with a single, +// tiny file or add this file to the rustposix crate. +mod global_criterion_settings; + +pub fn run_benchmark(c: &mut Criterion) { + // I'm following the initialization workflow from the unit tests here. + // + // I'm using the lindrustinit to set up cages and the file system. + rustposix::safeposix::dispatcher::lindrustinit(0); + + // Since all system calls are a method of a cage object, I also need this + // reference. + let cage = interface::cagetable_getref(1); + + // --- COMPARING get*id CALLS ACROSS Lind + Native OS kernel --- + let mut group = c.benchmark_group("Compare get*ids"); + + // These should be quite different, so use a log axis.. + group.plot_config( + criterion::PlotConfiguration::default().summary_scale(criterion::AxisScale::Logarithmic), + ); + + // let's have a combined benchmark of all of the get*id* system calls + // in RustPOSIX... I'm not running these separately, because they should + // not vary too much. + group.bench_function("TG01: Lind get*ids", |b| { + b.iter(|| { + cage.getpid_syscall(); + cage.getppid_syscall(); + cage.getgid_syscall(); + cage.getegid_syscall(); + cage.getuid_syscall(); + cage.geteuid_syscall(); + }) + }); + // For comparison let's time the native OS... + group.bench_function("TG01: Native OS kernel get*ids", |b| { + b.iter(|| unsafe { + libc::getpid(); + libc::getppid(); + libc::getgid(); + libc::getegid(); + libc::getuid(); + libc::geteuid(); + }) + }); + group.finish(); + + // This cleans up in ways I do not fully understand. I think it ensures + // the file system is cleaned up + rustposix::safeposix::dispatcher::lindrustfinalize(); +} + +criterion_group!(name=benches; + // Add the global settings here so we don't type it everywhere + config=global_criterion_settings::get_criterion(); + targets=run_benchmark); +criterion_main!(benches); diff --git a/benches/global_criterion_settings.rs b/benches/global_criterion_settings.rs new file mode 100644 index 00000000..b294fd58 --- /dev/null +++ b/benches/global_criterion_settings.rs @@ -0,0 +1,5 @@ +use criterion::Criterion; + +pub fn get_criterion() -> Criterion { + Criterion::default().noise_threshold(0.10) +} diff --git a/docs/RustPOSIX-README.jpg b/docs/RustPOSIX-README.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0f5f01680a0bac03887b6b61c9fc4bbd71baf7db GIT binary patch literal 374822 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
g_o$VEAz}vlfm~5gHit)sOG|}v*u9O=uJy}&= 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
S< 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 literal 0 HcmV?d00001 diff --git a/docs/SafePOSIX Rust Diagram.jpg b/docs/SafePOSIX Rust Diagram.jpg new file mode 100644 index 0000000000000000000000000000000000000000..53c9a67f5db1e1f5719e6f6a8e90f8734cf64045 GIT binary patch literal 56951 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 literal 0 HcmV?d00001 diff --git a/gen_netdevs.c b/gen_netdevs.c new file mode 100644 index 00000000..7f8eb02e --- /dev/null +++ b/gen_netdevs.c @@ -0,0 +1,30 @@ +#include +#include +#include +#include +#include +#include + +int main () +{ + struct ifaddrs *ifap, *ifa; + struct sockaddr_in *sa, *ba, *na, *da; + char *addr, *baddr, *naddr, *daddr; + + getifaddrs (&ifap); + for (ifa = ifap; ifa; ifa = ifa->ifa_next) { + if (ifa->ifa_addr && ifa->ifa_addr->sa_family==AF_INET) { + sa = (struct sockaddr_in *) ifa->ifa_addr; + addr = inet_ntoa(sa->sin_addr); + na = (struct sockaddr_in *) ifa->ifa_netmask; + naddr = inet_ntoa(na->sin_addr); + ba = (struct sockaddr_in *) ifa->ifa_broadaddr; + baddr = inet_ntoa(ba->sin_addr); + + printf("%s %d %s %s %s\n", ifa->ifa_name, ifa->ifa_flags, addr, naddr, baddr); + } + } + + freeifaddrs(ifap); + return 0; +} diff --git a/gen_netdevs.sh b/gen_netdevs.sh new file mode 100755 index 00000000..78bbd7db --- /dev/null +++ b/gen_netdevs.sh @@ -0,0 +1,19 @@ +#!/bin/bash +WORKDIR=$(pwd) # Set working directory to the output of pwd +FILE="$WORKDIR/gen_netdevs" # Use the full path for the file +PARENTPATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) # needs the source location when being used in lind-build. + +if [ -f "$FILE" ]; then + echo "$FILE exists." +else + echo "$FILE does not exist. Compiling." + gcc "$PARENTPATH/gen_netdevs.c" -o "$WORKDIR/gen_netdevs" + if [ $? -ne 0 ]; then # Check if gcc succeeded + echo "Compilation failed." + exit 1 + fi +fi + +echo "Generating netdevs" + +"$WORKDIR/gen_netdevs" > "$WORKDIR/net_devices" # Execute and output using full paths diff --git a/src/example_grates/commonconstants.rs b/src/example_grates/commonconstants.rs new file mode 100644 index 00000000..f6022646 --- /dev/null +++ b/src/example_grates/commonconstants.rs @@ -0,0 +1,104 @@ +// This file exists to make it easier to vary a single file of constants +// instead of editing each implementation... + +/// Per-process maximum number of fds... +pub const FD_PER_PROCESS_MAX: u64 = 1024; +// pub const FD_PER_PROCESS_MAX: u64 = 16; + +/// Use this to indicate there isn't a real fd backing an item +pub const NO_REAL_FD: u64 = 0xff_abcd_ef01; + +/// Use to indicate this is an EPOLLFD +pub const EPOLLFD: u64 = 0xff_abcd_ef02; + +/// Use this to indicate that a FD is invalid... Usually an error will be +/// returned instead, but this is needed for rare cases like poll. +pub const INVALID_FD: u64 = 0xff_abcd_ef00; + +// These are the values we look up with at the end... +// #[doc = include_str!("../docs/fdtableentry.md")] +#[derive(Clone, Copy, Debug, PartialEq)] +/// This is a table entry, looked up by virtual fd. +pub struct FDTableEntry { + /// underlying fd (may be a virtual fd below us or a kernel fd). In + /// some cases is also `NO_REAL_FD` or EPOLLFD to indicate it isn't backed + /// by an underlying fd. + pub realfd: u64, + /// Should I close this on exec? [/`empty_fds_for_exec`] + pub should_cloexec: bool, + /// Used for `NO_REAL_FD` and EPOLLFD types to store extra info. User + /// defined data can be added here. + pub optionalinfo: u64, +} + +#[allow(non_snake_case)] +/// A function used when registering close handlers which does nothing... +pub const fn NULL_FUNC(_: u64) {} + +// BUG / TODO: Use this in some sane way... +#[allow(dead_code)] +/// Global maximum number of fds... (checks may not be implemented) +pub const TOTAL_FD_MAX: u64 = 4096; + +// replicating these constants here so this can compile on systems other than +// Linux... Copied from Rust's libc. +// copied from libc +// pub const EPOLL_CTL_ADD: i32 = 1; +// /// copied from libc +// pub const EPOLL_CTL_MOD: i32 = 2; +// /// copied from libc +// pub const EPOLL_CTL_DEL: i32 = 3; + +// #[allow(non_camel_case_types)] +// /// i32 copied from libc. used in EPOLL event flags even though events are u32 +// pub type c_int = i32; + +// /// copied from libc +// pub const EPOLLIN: c_int = 0x1; +// /// copied from libc +// pub const EPOLLPRI: c_int = 0x2; +// /// copied from libc +// pub const EPOLLOUT: c_int = 0x4; +// /// copied from libc +// pub const EPOLLERR: c_int = 0x8; +// /// copied from libc +// pub const EPOLLHUP: c_int = 0x10; +// /// copied from libc +// pub const EPOLLRDNORM: c_int = 0x40; +// /// copied from libc +// pub const EPOLLRDBAND: c_int = 0x80; +// /// copied from libc +// pub const EPOLLWRNORM: c_int = 0x100; +// /// copied from libc +// pub const EPOLLWRBAND: c_int = 0x200; +// /// copied from libc +// pub const EPOLLMSG: c_int = 0x400; +// /// copied from libc +// pub const EPOLLRDHUP: c_int = 0x2000; +// /// copied from libc +// pub const EPOLLEXCLUSIVE: c_int = 0x1000_0000; +// /// copied from libc +// pub const EPOLLWAKEUP: c_int = 0x2000_0000; +// /// copied from libc +// pub const EPOLLONESHOT: c_int = 0x4000_0000; +// // Turning this on here because we copied from Rust's libc and I assume they +// // intended this... +// #[allow(overflowing_literals)] +// /// copied from libc +// pub const EPOLLET: c_int = 0x8000_0000; + +// use libc::epoll_event; +// // Note, I'm not using libc's version because this isn't defined on Windows +// // or Mac. Hence, I can't compile, etc. on those systems. Of course any +// // system actually running epoll, will need to be on Mac, but that doesn't mean +// // we can't parse those calls. +// #[allow(non_camel_case_types)] +// #[derive(Clone, Debug)] +// /// matches libc in Rust. Copied exactly. +// pub struct epoll_event { +// /// copied from libc. Event types to look at. +// pub events: u32, // So weird that this is a u32, while the constants +// // defined to work with it are i32s... +// /// copied from libc. Not used. +// pub u64: u64, +// } \ No newline at end of file diff --git a/src/example_grates/dashmaparrayglobal.rs b/src/example_grates/dashmaparrayglobal.rs new file mode 100644 index 00000000..63281dc8 --- /dev/null +++ b/src/example_grates/dashmaparrayglobal.rs @@ -0,0 +1,728 @@ +// DashMap;FD_PER_PROCESSS_MAX]> Space is ~24KB +// per cage w/ 1024 fds?!? +// Static DashMap. Let's see if having the FDTableEntries be a static +// array is any faster... + + +use crate::safeposix::cage; +use crate::safeposix::syscalls::fs_calls::*; + +use super::threei; + +use dashmap::DashMap; + +use lazy_static::lazy_static; + +use std::collections::HashMap; + +use std::sync::Mutex; + +// This uses a Dashmap (for cages) with an array of FDTableEntry items. + +// Get constants about the fd table sizes, etc. +pub use super::commonconstants::*; + +// algorithm name. Need not be listed. Used in benchmarking output +#[doc(hidden)] +pub const ALGONAME: &str = "DashMapArrayGlobal"; + +// These are the values we look up with at the end... +// #[doc = include_str!("../docs/fdtableentry.md")] +#[derive(Clone, Copy, Debug, PartialEq,Eq, Hash)] +pub struct FDTableEntry { + pub realfd: u64, // underlying fd (may be a virtual fd below us or + // a kernel fd) + pub should_cloexec: bool, // should I close this when exec is called? + pub optionalinfo: u64, // user specified / controlled data +} + +// It's fairly easy to check the fd count on a per-process basis (I just check +// when I would add a new fd). +// +// BUG: I will ignore the total limit for now. I would ideally do this on +// every creation, close, fork, etc. but it's a PITA to track this. + +// We will raise a panic anywhere we receive an unknown cageid. This frankly +// should not be possible and indicates some sort of internal error in our +// code. However, it is expected we could receive an invalid file descriptor +// when a cage makes a call. + +// In order to store this information, I'm going to use a DashMap which +// has keys of (cageid:u64) and values that are an array of FD_PER_PROCESS_MAX +// Option items. +// +// + +// This lets me initialize the code as a global. +lazy_static! { + + #[derive(Debug)] + static ref FDTABLE: DashMap;FD_PER_PROCESS_MAX as usize]> = { + let m = DashMap::new(); + // Insert a cage so that I have something to fork / test later, if need + // be. Otherwise, I'm not sure how I get this started. I think this + // should be invalid from a 3i standpoint, etc. Could this mask an + // error in the future? + // m.insert(threei::TESTING_CAGEID,[Option::None;FD_PER_PROCESS_MAX as usize]); + m + }; +} + +lazy_static! { + // This is needed for close and similar functionality. I need track the + // number of times a realfd is open + #[derive(Debug)] + static ref REALFDCOUNT: DashMap = { + DashMap::new() + }; + +} + +// Internal helper to hold the close handlers... +struct CloseHandlers { + intermediate_handler: fn(u64), + final_handler: fn(u64), + unreal_handler: fn(u64), +} + +// Seems sort of like a constant... I'm not sure if this is bad form or not... +// #[allow(non_snake_case)] +// pub fn NULL_FUNC(_:u64) { } + +lazy_static! { + // This holds the user registered handlers they want to have called when + // a close occurs. I did this rather than return messy data structures + // from the close, exec, and exit handlers because it seemed cleaner... + #[derive(Debug)] + static ref CLOSEHANDLERTABLE: Mutex = { + let c = CloseHandlers { + intermediate_handler:NULL_FUNC, + final_handler:kernel_close, + unreal_handler:NULL_FUNC, + }; + Mutex::new(c) + }; +} + +// #[doc = include_str!("../docs/init_empty_cage.md")] +pub fn init_empty_cage(cageid: u64) { + + if FDTABLE.contains_key(&cageid) { + panic!("Known cageid in fdtable access"); + } + + FDTABLE.insert(cageid,[Option::None;FD_PER_PROCESS_MAX as usize]); +} + +// #[doc = include_str!("../docs/translate_virtual_fd.md")] +pub fn translate_virtual_fd(cageid: u64, virtualfd: u64) -> Result { + + // They should not be able to pass a new cage I don't know. I should + // always have a table for each cage because each new cage is added at fork + // time + if !FDTABLE.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + return match FDTABLE.get(&cageid).unwrap()[virtualfd as usize] { + Some(tableentry) => Ok(tableentry.realfd), + None => Err(threei::Errno::EBADFD as u64), + }; +} + + +// This is fairly slow if I just iterate sequentially through numbers. +// However there are not that many to choose from. I could pop from a list +// or a set as well... Likely the best solution is to keep a count of the +// largest fd handed out and to just use this until you wrap. This will be +// super fast for a normal cage and will be correct in the weird case. +// Right now, I'll just implement the slow path and will speed this up +// later, if needed. +// #[doc = include_str!("../docs/get_unused_virtual_fd.md")] +pub fn get_unused_virtual_fd( + cageid: u64, + realfd: u64, + should_cloexec: bool, + optionalinfo: u64, +) -> Result { + + if !FDTABLE.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + // Set up the entry so it has the right info... + // Note, a HashMap stores its data on the heap! No need to box it... + // https://doc.rust-lang.org/book/ch08-03-hash-maps.html#creating-a-new-hash-map + let myentry = FDTableEntry { + realfd, + should_cloexec, + optionalinfo, + }; + + let mut myfdarray = FDTABLE.get_mut(&cageid).unwrap(); + + // Check the fds in order. + for fdcandidate in 0..FD_PER_PROCESS_MAX { + // FIXME: This is likely very slow. Should do something smarter... + if myfdarray[fdcandidate as usize].is_none() { + // I just checked. Should not be there... + myfdarray[fdcandidate as usize] = Some(myentry); + _increment_realfd(realfd); + return Ok(fdcandidate); + } + } + + // I must have checked all fds and failed to find one open. Fail! + Err(threei::Errno::EMFILE as u64) +} + +// This is used for things like dup2, which need a specific fd... +// If the requested_virtualfd is used, I close it... +// #[doc = include_str!("../docs/get_specific_virtual_fd.md")] +pub fn get_specific_virtual_fd( + cageid: u64, + requested_virtualfd: u64, + realfd: u64, + should_cloexec: bool, + optionalinfo: u64, +) -> Result<(), threei::RetVal> { + + if !FDTABLE.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + // If you ask for a FD number that is too large, I'm going to reject it. + // Note that, I need to use the FD_PER_PROCESS_MAX setting because this + // is also how I'm tracking how many values you have open. If this + // changed, then these constants could be decoupled... + if requested_virtualfd > FD_PER_PROCESS_MAX { + return Err(threei::Errno::EBADF as u64); + } + + // Set up the entry so it has the right info... + // Note, a HashMap stores its data on the heap! No need to box it... + // https://doc.rust-lang.org/book/ch08-03-hash-maps.html#creating-a-new-hash-map + let myentry = FDTableEntry { + realfd, + should_cloexec, + optionalinfo, + }; + + // I moved this up so that if I decrement the same realfd, it calls + // the intermediate handler instead of the final one. + _increment_realfd(realfd); + if let Some(entry) = FDTABLE.get(&cageid).unwrap()[requested_virtualfd as usize] { + if entry.realfd != NO_REAL_FD { + _decrement_realfd(entry.realfd); + } + else { + // Let their code know this has been closed... + let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); + (closehandlers.unreal_handler)(entry.optionalinfo); + } + } + + // always add the new entry + FDTABLE.get_mut(&cageid).unwrap()[requested_virtualfd as usize] = Some(myentry); + Ok(()) +} + +// We're just setting a flag here, so this should be pretty straightforward. +// #[doc = include_str!("../docs/set_cloexec.md")] +pub fn set_cloexec(cageid: u64, virtualfd: u64, is_cloexec: bool) -> Result<(), threei::RetVal> { + + if !FDTABLE.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + // return EBADFD, if the fd is missing... + if FDTABLE.get(&cageid).unwrap()[virtualfd as usize].is_none() { + return Err(threei::Errno::EBADFD as u64); + } + // Set the is_cloexec flag + FDTABLE.get_mut(&cageid).unwrap()[virtualfd as usize].as_mut().unwrap().should_cloexec = is_cloexec; + Ok(()) +} + +// Super easy, just return the optionalinfo field... +// #[doc = include_str!("../docs/get_optionalinfo.md")] +pub fn get_optionalinfo(cageid: u64, virtualfd: u64) -> Result { + if !FDTABLE.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + return match FDTABLE.get(&cageid).unwrap()[virtualfd as usize] { + Some(tableentry) => Ok(tableentry.optionalinfo), + None => Err(threei::Errno::EBADFD as u64), + }; +} + +// We're setting an opaque value here. This should be pretty straightforward. +// #[doc = include_str!("../docs/set_optionalinfo.md")] +pub fn set_optionalinfo( + cageid: u64, + virtualfd: u64, + optionalinfo: u64, +) -> Result<(), threei::RetVal> { + + if !FDTABLE.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + // return EBADFD, if the fd is missing... + if FDTABLE.get(&cageid).unwrap()[virtualfd as usize].is_none() { + return Err(threei::Errno::EBADFD as u64); + } + + // Set optionalinfo or return EBADFD, if that's missing... + FDTABLE.get_mut(&cageid).unwrap()[virtualfd as usize].as_mut().unwrap().optionalinfo = optionalinfo; + Ok(()) +} + +// Helper function used for fork... Copies an fdtable for another process +// #[doc = include_str!("../docs/copy_fdtable_for_cage.md")] +pub fn copy_fdtable_for_cage(srccageid: u64, newcageid: u64) -> Result<(), threei::Errno> { + + if !FDTABLE.contains_key(&srccageid) { + panic!("Unknown srccageid in fdtable access"); + } + if FDTABLE.contains_key(&newcageid) { + panic!("Known newcageid in fdtable access"); + } + + // Insert a copy and ensure it didn't exist... + // BUG: Is this a copy!?! Am I passing a ref to the same thing!?!?!? + let hmcopy = *FDTABLE.get(&srccageid).unwrap(); + + // Increment copied items + for entry in FDTABLE.get(&srccageid).unwrap().iter() { + if entry.is_some() { + let thisrealfd = entry.unwrap().realfd; + if thisrealfd != NO_REAL_FD { + _increment_realfd(thisrealfd); + } + } + } + + assert!(FDTABLE.insert(newcageid, hmcopy).is_none()); + Ok(()) + // I'm not going to bother to check the number of fds used overall yet... + // Err(threei::Errno::EMFILE as u64), +} + +// This is mostly used in handling exit, etc. Returns the HashMap +// for the cage. +// #[doc = include_str!("../docs/remove_cage_from_fdtable.md")] +pub fn remove_cage_from_fdtable(cageid: u64) { + + if !FDTABLE.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + + let myfdarray = FDTABLE.get(&cageid).unwrap(); + for item in 0..FD_PER_PROCESS_MAX as usize { + if myfdarray[item].is_some() { + let therealfd = myfdarray[item].unwrap().realfd; + if therealfd != NO_REAL_FD { + _decrement_realfd(therealfd); + } + else{ + // Let their code know this has been closed... + let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); + (closehandlers.unreal_handler)(myfdarray[item].unwrap().optionalinfo); + } + } + } + // I need to do this or else I'll try to double claim the lock and + // deadlock... + drop(myfdarray); + + FDTABLE.remove(&cageid); + +} + +// This removes all fds with the should_cloexec flag set. They are returned +// in a new hashmap... +// #[doc = include_str!("../docs/empty_fds_for_exec.md")] +pub fn empty_fds_for_exec(cageid: u64) { + + if !FDTABLE.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + let mut myfdarray = FDTABLE.get_mut(&cageid).unwrap(); + for item in 0..FD_PER_PROCESS_MAX as usize { + if myfdarray[item].is_some() && myfdarray[item].unwrap().should_cloexec { + let therealfd = myfdarray[item].unwrap().realfd; + if therealfd != NO_REAL_FD { + _decrement_realfd(therealfd); + } + else{ + // Let their code know this has been closed... + let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); + (closehandlers.unreal_handler)(myfdarray[item].unwrap().optionalinfo); + } + myfdarray[item] = None; + } + } + +} + +// Returns the HashMap returns a copy of the fdtable for a cage. Useful +// helper function for a caller that needs to examine the table. Likely could +// be more efficient by letting the caller borrow this... +// #[doc = include_str!("../docs/return_fdtable_copy.md")] +pub fn return_fdtable_copy(cageid: u64) -> HashMap { + + if !FDTABLE.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + let mut myhashmap = HashMap::new(); + + let myfdarray = FDTABLE.get(&cageid).unwrap(); + for item in 0..FD_PER_PROCESS_MAX as usize { + if myfdarray[item].is_some() { + myhashmap.insert(item as u64,myfdarray[item].unwrap()); + } + } + myhashmap +} + + + +/******************* CLOSE SPECIFIC FUNCTIONALITY *******************/ + +// Helper for close. Returns a tuple of realfd, number of references +// remaining. +// #[doc = include_str!("../docs/close_virtualfd.md")] +pub fn close_virtualfd(cageid:u64, virtfd:u64) -> Result<(),threei::RetVal> { + if !FDTABLE.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + let mut myfdarray = FDTABLE.get_mut(&cageid).unwrap(); + + + if myfdarray[virtfd as usize].is_some() { + let therealfd = myfdarray[virtfd as usize].unwrap().realfd; + + if therealfd == NO_REAL_FD { + // Let their code know this has been closed... + let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); + (closehandlers.unreal_handler)(myfdarray[virtfd as usize].unwrap().optionalinfo); + // Zero out this entry... + myfdarray[virtfd as usize] = None; + return Ok(()); + } + _decrement_realfd(therealfd); + // Zero out this entry... + myfdarray[virtfd as usize] = None; + return Ok(()); + } + Err(threei::Errno::EBADFD as u64) +} + + +// Register a series of helpers to be called for close. Can be called +// multiple times to override the older helpers. +// #[doc = include_str!("../docs/register_close_handlers.md")] +pub fn register_close_handlers(intermediate_handler: fn(u64), final_handler: fn(u64), unreal_handler: fn(u64)) { + // Unlock the table and set the handlers... + let mut closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); + closehandlers.intermediate_handler = intermediate_handler; + closehandlers.final_handler = final_handler; + closehandlers.unreal_handler = unreal_handler; +} + + +// Helpers to track the count of times each realfd is used +#[doc(hidden)] +fn _decrement_realfd(realfd:u64) -> u64 { + if realfd == NO_REAL_FD { + panic!("Called _decrement_realfd with NO_REAL_FD"); + } + + let newcount:u64 = REALFDCOUNT.get(&realfd).unwrap().value() - 1; + let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); + if newcount > 0 { + (closehandlers.intermediate_handler)(realfd); + REALFDCOUNT.insert(realfd,newcount); + } + else{ + (closehandlers.final_handler)(realfd); + } + newcount +} + +// Helpers to track the count of times each realfd is used +#[doc(hidden)] +fn _increment_realfd(realfd:u64) -> u64 { + if realfd == NO_REAL_FD { + return 0 + } + + // Get a mutable reference to the entry so we can update it. + return match REALFDCOUNT.get_mut(&realfd) { + Some(mut count) => { + *count += 1; + *count + } + None => { + REALFDCOUNT.insert(realfd, 1); + 1 + } + } +} + + + +/*************** Code for handling select() ****************/ + +use libc::fd_set; +use std::collections::HashSet; +use std::cmp; +use std::mem; + +// Helper to get an empty fd_set. Helper function to isolate unsafe code, +// etc. +pub fn _init_fd_set() -> fd_set { + let raw_fd_set:fd_set; + unsafe { + let mut this_fd_set = mem::MaybeUninit::::uninit(); + libc::FD_ZERO(this_fd_set.as_mut_ptr()); + raw_fd_set = this_fd_set.assume_init() + } + raw_fd_set +} + +// Helper to get a null pointer. +pub fn _get_null_fd_set() -> fd_set { + //unsafe{ptr::null_mut()} + // BUG!!! Need to fix this later. + _init_fd_set() +} + +pub fn _fd_set(fd:u64, thisfdset:&mut fd_set) { + unsafe{libc::FD_SET(fd as i32,thisfdset)} +} + +pub fn _fd_isset(fd:u64, thisfdset:&fd_set) -> bool { + unsafe{libc::FD_ISSET(fd as i32,thisfdset)} +} + +// Computes the bitmodifications and returns a (maxnfds, unrealset) tuple... +fn _do_bitmods(myfdarray:[Option;FD_PER_PROCESS_MAX as usize], nfds:u64, infdset: fd_set, thisfdset: &mut fd_set, mappingtable: &mut HashMap) -> Result<(u64,HashSet<(u64,u64)>),threei::RetVal> { + let mut unrealhashset:HashSet<(u64,u64)> = HashSet::new(); + // Iterate through the infdset and set those values as is appropriate + let mut highestpos = 0; + + // Clippy is somehow missing how pos is using bit. + #[allow(clippy::needless_range_loop)] + for bit in 0..nfds as usize { + let pos = bit as u64; + if _fd_isset(pos,&infdset) { + if let Some(entry) = myfdarray[bit] { + if entry.realfd == NO_REAL_FD { + unrealhashset.insert((pos,entry.optionalinfo)); + } + else { + mappingtable.insert(entry.realfd, pos); + _fd_set(entry.realfd,thisfdset); + // I add one because select expects nfds to be the max+1 + highestpos = cmp::max(highestpos, entry.realfd+1); + } + } + else { + return Err(threei::Errno::EINVAL as u64); + } + } + } + Ok((highestpos,unrealhashset)) +} + +// helper to call before calling select beneath you. Translates your virtfds +// to realfds. +// See: https://man7.org/linux/man-pages/man2/select.2.html for details / +// corner cases about the arguments. + +// I hate doing these, but don't know how to make this interface better... +#[allow(clippy::type_complexity)] +#[allow(clippy::too_many_arguments)] +// #[doc = include_str!("../docs/get_real_bitmasks_for_select.md")] +pub fn get_real_bitmasks_for_select(cageid:u64, nfds:u64, readbits:Option, writebits:Option, exceptbits:Option) -> Result<(u64, fd_set, fd_set, fd_set, [HashSet<(u64,u64)>;3], HashMap),threei::RetVal> { + + if nfds >= FD_PER_PROCESS_MAX { + return Err(threei::Errno::EINVAL as u64); + } + + if !FDTABLE.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + let mut unrealarray:[HashSet<(u64,u64)>;3] = [HashSet::new(),HashSet::new(),HashSet::new()]; + let mut mappingtable:HashMap = HashMap::new(); + let mut newnfds = 0; + + // dashmaps are lockless, but usually I would grab a lock on the fdtable + // here... + let binding = FDTABLE.get(&cageid).unwrap(); + let thefdvec = *binding.value(); + + // putting results in a vec was the cleanest way I found to do this.. + let mut resultvec = Vec::new(); + + for (unrealoffset, inset) in [readbits,writebits, exceptbits].into_iter().enumerate() { + match inset { + Some(virtualbits) => { + let mut retset = _init_fd_set(); + let (thisnfds,myunrealhashset) = _do_bitmods(thefdvec,nfds,*virtualbits, &mut retset,&mut mappingtable)?; + resultvec.push(retset); + newnfds = cmp::max(thisnfds, newnfds); + unrealarray[unrealoffset] = myunrealhashset; + } + None => { + // This item is null. No unreal items + // BUG: Need to actually return null! + resultvec.push(_get_null_fd_set()); + unrealarray[unrealoffset] = HashSet::new(); + } + } + } + + Ok((newnfds, resultvec[0], resultvec[1], resultvec[2], unrealarray, mappingtable)) + +} + + +// helper to call after calling select beneath you. returns the fd_sets you +// need for your return from a select call and the number of unique flags +// set... + +// I hate doing these, but don't know how to make this interface better... +#[allow(clippy::type_complexity)] +#[allow(clippy::too_many_arguments)] +// #[doc = include_str!("../docs/get_virtual_bitmasks_from_select_result.md")] +pub fn get_virtual_bitmasks_from_select_result(nfds:u64, readbits:fd_set, writebits:fd_set, exceptbits:fd_set,unrealreadset:HashSet, unrealwriteset:HashSet, unrealexceptset:HashSet, mappingtable:HashMap) -> Result<(u64, fd_set, fd_set, fd_set),threei::RetVal> { + + // Note, I don't need the cage_id here because I have the mappingtable... + + if nfds >= FD_PER_PROCESS_MAX { + panic!("This shouldn't be possible because we shouldn't have returned this previously") + } + + let mut flagsset = 0; + let mut retvec = Vec::new(); + + for (inset,unrealset) in [(readbits,unrealreadset), (writebits,unrealwriteset), (exceptbits,unrealexceptset)] { + let mut retbits = _init_fd_set(); + for bit in 0..nfds as usize { + let pos = bit as u64; + if _fd_isset(pos,&inset)&& !_fd_isset(*mappingtable.get(&pos).unwrap(),&retbits) { + flagsset+=1; + _fd_set(*mappingtable.get(&pos).unwrap(),&mut retbits); + } + } + for virtfd in unrealset { + if !_fd_isset(virtfd,&retbits) { + flagsset+=1; + _fd_set(virtfd,&mut retbits); + } + } + retvec.push(retbits); + } + + Ok((flagsset,retvec[0],retvec[1],retvec[2])) + +} + + + +/********************** POLL SPECIFIC FUNCTIONS **********************/ + +// helper to call before calling poll beneath you. replaces the fds in +// the poll struct with virtual versions and returns the items you need +// to check yourself... +#[allow(clippy::type_complexity)] +// #[doc = include_str!("../docs/convert_virtualfds_to_real.md")] +pub fn convert_virtualfds_to_real(cageid:u64, virtualfds:Vec) -> (Vec, Vec<(u64,u64)>, Vec, HashMap) { + + if !FDTABLE.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + let mut unrealvec = Vec::new(); + let mut realvec = Vec::new(); + let mut invalidvec = Vec::new(); + let thefdarray = *FDTABLE.get(&cageid).unwrap(); + let mut mappingtable:HashMap = HashMap::new(); + + // BUG?: I'm ignoring the fact that virtualfds can show up multiple times. + // I'm not sure this actually matters, but I didn't think hard about it. + for virtfd in virtualfds { + match thefdarray[virtfd as usize] { + Some(entry) => { + // always append the value here. NO_REAL_FD will be added + // in the appropriate places to tell them to handle those calls + // themself. + realvec.push(entry.realfd); + if entry.realfd == NO_REAL_FD { + unrealvec.push((virtfd,entry.optionalinfo)); + } + else{ + mappingtable.insert(entry.realfd, virtfd); + } + } + None => { + // Add this because they need to handle it if POLLNVAL is set. + // An exception should not be raised!!! + realvec.push(INVALID_FD); + invalidvec.push(virtfd); + } + } + } + + (realvec, unrealvec, invalidvec, mappingtable) +} + + + +// helper to call after calling poll. replaces the realfds the vector +// with virtual ones... +// #[doc = include_str!("../docs/convert_realfds_back_to_virtual.md")] +pub fn convert_realfds_back_to_virtual(realfds:Vec, mappingtable:HashMap) -> Vec { + + // I don't care what cage was used, and don't need to lock anything... + // I have the mappingtable! + + let mut virtvec = Vec::new(); + + for realfd in realfds { + virtvec.push(*mappingtable.get(&realfd).unwrap()); + } + + virtvec +} + + + + + + +/********************** TESTING HELPER FUNCTION **********************/ + +#[doc(hidden)] +// Helper to initialize / empty out state so we can test with a clean system... +// This is only used in tests, thus is hidden... +pub fn refresh() { + FDTABLE.clear(); + FDTABLE.insert(threei::TESTING_CAGEID,[Option::None;FD_PER_PROCESS_MAX as usize]); + let mut closehandlers = CLOSEHANDLERTABLE.lock().unwrap_or_else(|e| { + CLOSEHANDLERTABLE.clear_poison(); + e.into_inner() + }); + closehandlers.intermediate_handler = NULL_FUNC; + closehandlers.final_handler = NULL_FUNC; + closehandlers.unreal_handler = NULL_FUNC; + // Note, it doesn't seem that Dashmaps can be poisoned... +} \ No newline at end of file diff --git a/src/example_grates/dashmapvecglobal.rs b/src/example_grates/dashmapvecglobal.rs new file mode 100644 index 00000000..1db56c54 --- /dev/null +++ b/src/example_grates/dashmapvecglobal.rs @@ -0,0 +1,969 @@ +// DashMap;FD_PER_PROCESSS_MAX])> Space is +// ~30KB per cage w/ 1024 fds?!? +// Static DashMap. Let's see if having the FDTableEntries be in a Vector +// matters... + +use crate::safeposix::cage; +use crate::safeposix::syscalls::fs_calls::*; + +use std::io::Write; +use std::io; + +use super::threei; + + +use dashmap::DashMap; + +use lazy_static::lazy_static; +use serde::de::value; + +use std::collections::HashMap; + +use std::sync::Mutex; + +// This uses a Dashmap (for cages) with an array of FDTableEntry items. + +// Get constants about the fd table sizes, etc. +pub use super::commonconstants::*; + +// algorithm name. Need not be listed. Used in benchmarking output +#[doc(hidden)] +pub const ALGONAME: &str = "DashMapVecGlobal"; + +// It's fairly easy to check the fd count on a per-process basis (I just check +// when I would add a new fd). +// +// BUG: I will ignore the total limit for now. I would ideally do this on +// every creation, close, fork, etc. but it's a PITA to track this. + +// We will raise a panic anywhere we receive an unknown cageid. This frankly +// should not be possible and indicates some sort of internal error in our +// code. However, it is expected we could receive an invalid file descriptor +// when a cage makes a call. + +// In order to store this information, I'm going to use a DashMap which +// has keys of (cageid:u64) and values that are an array of FD_PER_PROCESS_MAX +// Option items. +// +// + +// This lets me initialize the code as a global. +lazy_static! { + + #[derive(Debug)] + pub static ref FDTABLE: DashMap>> = { + let m = DashMap::new(); + // Insert a cage so that I have something to fork / test later, if need + // be. Otherwise, I'm not sure how I get this started. I think this + // should be invalid from a 3i standpoint, etc. Could this mask an + // error in the future? + // m.insert(threei::TESTING_CAGEID,vec!(Option::None;FD_PER_PROCESS_MAX as usize)); + m + }; +} + +lazy_static! { + // This is needed for close and similar functionality. I need track the + // number of times a realfd is open + #[derive(Debug)] + pub static ref REALFDCOUNT: DashMap = { + DashMap::new() + }; + +} + +// Internal helper to hold the close handlers... +struct CloseHandlers { + intermediate: fn(u64), + last: fn(u64), + unreal: fn(u64), +} + +// #[doc = include_str!("../docs/init_empty_cage.md")] +pub fn init_empty_cage(cageid: u64) { + + assert!(!FDTABLE.contains_key(&cageid),"Known cageid in fdtable access"); + + FDTABLE.insert(cageid,vec!(Option::None;FD_PER_PROCESS_MAX as usize)); +} + +// #[doc = include_str!("../docs/translate_virtual_fd.md")] +pub fn translate_virtual_fd(cageid: u64, virtualfd: u64) -> Result { + + // They should not be able to pass a new cage I don't know. I should + // always have a table for each cage because each new cage is added at fork + // time + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + return match FDTABLE.get(&cageid).unwrap()[virtualfd as usize] { + Some(tableentry) => Ok(tableentry.realfd), + None => Err(threei::Errno::EBADFD as u64), + }; +} + + +// This is fairly slow if I just iterate sequentially through numbers. +// However there are not that many to choose from. I could pop from a list +// or a set as well... Likely the best solution is to keep a count of the +// largest fd handed out and to just use this until you wrap. This will be +// super fast for a normal cage and will be correct in the weird case. +// Right now, I'll just implement the slow path and will speed this up +// later, if needed. +// #[doc = include_str!("../docs/get_unused_virtual_fd.md")] +pub fn get_unused_virtual_fd( + cageid: u64, + realfd: u64, + should_cloexec: bool, + optionalinfo: u64, +) -> Result { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + // Set up the entry so it has the right info... + // Note, a HashMap stores its data on the heap! No need to box it... + // https://doc.rust-lang.org/book/ch08-03-hash-maps.html#creating-a-new-hash-map + let myentry = FDTableEntry { + realfd, + should_cloexec, + optionalinfo, + }; + + let mut myfdrow = FDTABLE.get_mut(&cageid).unwrap(); + + // Check the fds in order. + for fdcandidate in 0..FD_PER_PROCESS_MAX { + // FIXME: This is likely very slow. Should do something smarter... + if myfdrow[fdcandidate as usize].is_none() { + // I just checked. Should not be there... + myfdrow[fdcandidate as usize] = Some(myentry); + _increment_realfd(realfd); + return Ok(fdcandidate); + } + } + + // I must have checked all fds and failed to find one open. Fail! + Err(threei::Errno::EMFILE as u64) +} + +// This is used for things like dup2, which need a specific fd... +// If the requested_virtualfd is used, I close it... +// #[doc = include_str!("../docs/get_specific_virtual_fd.md")] +pub fn get_specific_virtual_fd( + cageid: u64, + requested_virtualfd: u64, + realfd: u64, + should_cloexec: bool, + optionalinfo: u64, +) -> Result<(), threei::RetVal> { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + // If you ask for a FD number that is too large, I'm going to reject it. + // Note that, I need to use the FD_PER_PROCESS_MAX setting because this + // is also how I'm tracking how many values you have open. If this + // changed, then these constants could be decoupled... + if requested_virtualfd > FD_PER_PROCESS_MAX { + return Err(threei::Errno::EBADF as u64); + } + + // Set up the entry so it has the right info... + // Note, a HashMap stores its data on the heap! No need to box it... + // https://doc.rust-lang.org/book/ch08-03-hash-maps.html#creating-a-new-hash-map + let myentry = FDTableEntry { + realfd, + should_cloexec, + optionalinfo, + }; + + // I moved this up so that if I decrement the same realfd, it calls + // the intermediate handler instead of the last one. + _increment_realfd(realfd); + let myoptionentry = FDTABLE.get(&cageid).unwrap()[requested_virtualfd as usize]; + // always add the new entry. I'm duping this first, before I close + // the old one because I need to ensure I've cleaned up state correctly + // before calling the close handlers... + FDTABLE.get_mut(&cageid).unwrap()[requested_virtualfd as usize] = Some(myentry); + + // Close the old entry, if I need to... + if let Some(entry) = myoptionentry { + + if entry.realfd == NO_REAL_FD { + // Let their code know this has been closed... + let unrealclosehandler = CLOSEHANDLERTABLE.lock().unwrap().unreal; + (unrealclosehandler)(entry.optionalinfo); + } + else { + _decrement_realfd(entry.realfd); + } + } + + Ok(()) +} + +// We're just setting a flag here, so this should be pretty straightforward. +// #[doc = include_str!("../docs/set_cloexec.md")] +pub fn set_cloexec(cageid: u64, virtualfd: u64, is_cloexec: bool) -> Result<(), threei::RetVal> { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + // return EBADFD, if the fd is missing... + if FDTABLE.get(&cageid).unwrap()[virtualfd as usize].is_none() { + return Err(threei::Errno::EBADFD as u64); + } + // Set the is_cloexec flag + FDTABLE.get_mut(&cageid).unwrap()[virtualfd as usize].as_mut().unwrap().should_cloexec = is_cloexec; + Ok(()) +} + +// Super easy, just return the optionalinfo field... +// #[doc = include_str!("../docs/get_optionalinfo.md")] +pub fn get_optionalinfo(cageid: u64, virtualfd: u64) -> Result { + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + return match FDTABLE.get(&cageid).unwrap()[virtualfd as usize] { + Some(tableentry) => Ok(tableentry.optionalinfo), + None => Err(threei::Errno::EBADFD as u64), + }; +} + +// We're setting an opaque value here. This should be pretty straightforward. +// #[doc = include_str!("../docs/set_optionalinfo.md")] +pub fn set_optionalinfo( + cageid: u64, + virtualfd: u64, + optionalinfo: u64, +) -> Result<(), threei::RetVal> { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + // return EBADFD, if the fd is missing... + if FDTABLE.get(&cageid).unwrap()[virtualfd as usize].is_none() { + return Err(threei::Errno::EBADFD as u64); + } + + // Set optionalinfo or return EBADFD, if that's missing... + FDTABLE.get_mut(&cageid).unwrap()[virtualfd as usize].as_mut().unwrap().optionalinfo = optionalinfo; + Ok(()) +} + +// Helper function used for fork... Copies an fdtable for another process +// #[doc = include_str!("../docs/copy_fdtable_for_cage.md")] +pub fn copy_fdtable_for_cage(srccageid: u64, newcageid: u64) -> Result<(), threei::Errno> { + + assert!(FDTABLE.contains_key(&srccageid),"Unknown cageid in fdtable access"); + assert!(!FDTABLE.contains_key(&newcageid),"Known cageid in fdtable access"); + + // Insert a copy and ensure it didn't exist... + let hmcopy = FDTABLE.get(&srccageid).unwrap().clone(); + + // Increment copied items + for entry in FDTABLE.get(&srccageid).unwrap().iter() { + if entry.is_some() { + let thisrealfd = entry.unwrap().realfd; + if thisrealfd != NO_REAL_FD { + _increment_realfd(thisrealfd); + } + } + } + + assert!(FDTABLE.insert(newcageid, hmcopy).is_none()); + Ok(()) + // I'm not going to bother to check the number of fds used overall yet... + // Err(threei::Errno::EMFILE as u64), +} + +// This is mostly used in handling exit, etc. Returns the HashMap +// for the cage. +// #[doc = include_str!("../docs/remove_cage_from_fdtable.md")] +pub fn remove_cage_from_fdtable(cageid: u64) { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + + // remove the item first and then we clean up and call their close + // handlers. + let myfdrow = FDTABLE.remove(&cageid).unwrap().1; + + for entry in myfdrow { + if entry.is_some() { + let therealfd = entry.unwrap().realfd; + if therealfd == NO_REAL_FD { + // Let their code know this has been closed... + let unrealclosehandler = CLOSEHANDLERTABLE.lock().unwrap().unreal; + (unrealclosehandler)(entry.unwrap().optionalinfo); + } + else{ + _decrement_realfd(therealfd); + } + } + } + +} + +// This removes all fds with the should_cloexec flag set. They are returned +// in a new hashmap... +// #[doc = include_str!("../docs/empty_fds_for_exec.md")] +pub fn empty_fds_for_exec(cageid: u64) { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + let mut myfdrow = FDTABLE.get_mut(&cageid).unwrap(); + // I need to call all the close handlers at the end. So I need to + // get vector of them to do the operation on... + let mut closevec = Vec::new(); + + for item in 0..FD_PER_PROCESS_MAX as usize { + if myfdrow[item].is_some() && myfdrow[item].unwrap().should_cloexec { + let therealfd = myfdrow[item].unwrap().realfd; + let optionalinfo = myfdrow[item].unwrap().optionalinfo; + // handle this in a moment... + closevec.push((therealfd,optionalinfo)); + + // Always zero out the row before calling their handler + myfdrow[item] = None; + } + } + + // Need to drop the lock, before calling the handlers. + drop(myfdrow); + + // Now, we can call the close handlers! + for (therealfd,optionalinfo) in closevec { + if therealfd == NO_REAL_FD { + // Let their code know this has been closed... + let unrealclosehandler = CLOSEHANDLERTABLE.lock().unwrap().unreal; + (unrealclosehandler)(optionalinfo); + } + else{ + _decrement_realfd(therealfd); + } + } + +} + +// Returns the HashMap returns a copy of the fdtable for a cage. Useful +// helper function for a caller that needs to examine the table. Likely could +// be more efficient by letting the caller borrow this... +// #[doc = include_str!("../docs/return_fdtable_copy.md")] +#[must_use] // must use the return value if you call it. +pub fn return_fdtable_copy(cageid: u64) -> HashMap { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + let mut myhashmap = HashMap::new(); + + let myfdrow = FDTABLE.get(&cageid).unwrap(); + for item in 0..FD_PER_PROCESS_MAX as usize { + if myfdrow[item].is_some() { + myhashmap.insert(item as u64,myfdrow[item].unwrap()); + } + } + myhashmap +} + + + +/******************* CLOSE SPECIFIC FUNCTIONALITY *******************/ + +lazy_static! { + // This holds the user registered handlers they want to have called when + // a close occurs. I did this rather than return messy data structures + // from the close, exec, and exit handlers because it seemed cleaner... + #[derive(Debug)] + static ref CLOSEHANDLERTABLE: Mutex = { + let c = CloseHandlers { + intermediate:NULL_FUNC, + last:NULL_FUNC, + unreal:NULL_FUNC, + }; + Mutex::new(c) + }; +} + + +// Helper for close. Returns a tuple of realfd, number of references +// remaining. +// #[doc = include_str!("../docs/close_virtualfd.md")] +pub fn close_virtualfd(cageid:u64, virtfd:u64) -> Result<(),threei::RetVal> { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + // cloning this so I don't hold a lock and deadlock close handlers + let mut myfdrow = FDTABLE.get_mut(&cageid).unwrap().clone(); + + + if myfdrow[virtfd as usize].is_some() { + let therealfd = myfdrow[virtfd as usize].unwrap().realfd; + + if therealfd == NO_REAL_FD { + // Let their code know this has been closed... + let unrealclosehandler = CLOSEHANDLERTABLE.lock().unwrap().unreal; + let optionalinfo = myfdrow[virtfd as usize].unwrap().optionalinfo; + + // Zero out this entry... + myfdrow[virtfd as usize] = None; + + // call the handler last... + (unrealclosehandler)(optionalinfo); + return Ok(()); + } + // Zero out this entry... + myfdrow[virtfd as usize] = None; + + FDTABLE.insert(cageid, myfdrow.clone()); + + // always _decrement last as it may call the user handler... + _decrement_realfd(therealfd); + return Ok(()); + } + Err(threei::Errno::EBADFD as u64) +} + + +// Register a series of helpers to be called for close. Can be called +// multiple times to override the older helpers. +// #[doc = include_str!("../docs/register_close_handlers.md")] +pub fn register_close_handlers(intermediate: fn(u64), last: fn(u64), unreal: fn(u64)) { + // Unlock the table and set the handlers... + // TODO: I made a serious attempt to try to keep closehandlers that call + // close, etc. from deadlocking the system. More testing, etc. is needed. + let mut closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); + closehandlers.intermediate = intermediate; + closehandlers.last = last; + closehandlers.unreal = unreal; +} + + +// Helpers to track the count of times each realfd is used +#[doc(hidden)] +fn _decrement_realfd(realfd:u64) -> u64 { + + assert!(realfd != NO_REAL_FD, "Called _decrement_realfd with NO_REAL_FD"); + + // let newcount:u64 = REALFDCOUNT.get(&realfd).unwrap().value() - 1; + let newcount = match REALFDCOUNT.get(&realfd) { + Some(refvalue) => refvalue.value() - 1, + None => { + // println!("[FDTABLE] realfd: {:?}", realfd); + // println!("[FDTABLE] REALFDCOUNT:"); + // io::stdout().flush().unwrap(); + // for entry in REALFDCOUNT.iter() { + // let key = entry.key(); + // let value = entry.value(); + // println!("realfd: {:?}, Value: {:?}", key, value); + // io::stdout().flush().unwrap(); + // } + // println!("[FDTABLE] FDTABLE: "); + // io::stdout().flush().unwrap(); + // for entry in FDTABLE.iter() { + // let key = entry.key(); + // let value = entry.value(); + // let non_none_values: Vec<&FDTableEntry> = value.iter().filter_map(|v| v.as_ref()).collect(); + // println!("cageid: {:?}, Value: {:?}", key, non_none_values); + // io::stdout().flush().unwrap(); + // } + // io::stdout().flush().unwrap(); + panic!(); + } + }; + + // Doing this to release the lock so I can call it recursively... + let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); + let intermediateclosehandler = closehandlers.intermediate; + let lastclosehandler = closehandlers.last; + // release the lock... + drop(closehandlers); + if newcount > 0 { + // Update before calling their close handler in case they do operations + // inside the close handler which create / close fds... + REALFDCOUNT.insert(realfd,newcount); + (intermediateclosehandler)(realfd); + } + else{ + // Remove before calling their close handler in case they do operations + // inside the close handler which create / close fds... + REALFDCOUNT.remove(&realfd); + (lastclosehandler)(realfd); + } + newcount +} + +// Helpers to track the count of times each realfd is used +#[doc(hidden)] +fn _increment_realfd(realfd:u64) -> u64 { + if realfd == NO_REAL_FD { + return 0 + } + + // Get a mutable reference to the entry so we can update it. + return if let Some(mut count) = REALFDCOUNT.get_mut(&realfd) { + *count += 1; + *count + } else { + REALFDCOUNT.insert(realfd, 1); + 1 + } +} + + + +/*************** Code for handling select() ****************/ + +use libc::fd_set; +use std::collections::HashSet; +use std::cmp; +use std::mem; + +// Helper to get an empty fd_set. Helper function to isolate unsafe code, +// etc. +#[doc(hidden)] +#[must_use] // must use the return value if you call it. +pub fn _init_fd_set() -> fd_set { + let raw_fd_set:fd_set; + unsafe { + let mut this_fd_set = mem::MaybeUninit::::uninit(); + libc::FD_ZERO(this_fd_set.as_mut_ptr()); + raw_fd_set = this_fd_set.assume_init(); + } + raw_fd_set +} + +#[doc(hidden)] +pub fn _fd_set(fd:u64, thisfdset:&mut fd_set) { + unsafe{libc::FD_SET(fd as i32,thisfdset)} +} + +#[doc(hidden)] +#[must_use] // must use the return value if you call it. +pub fn _fd_isset(fd:u64, thisfdset:&fd_set) -> bool { + unsafe{libc::FD_ISSET(fd as i32,thisfdset)} +} + +// Computes the bitmodifications and returns a (maxnfds, unrealset) tuple... +#[doc(hidden)] +fn _do_bitmods(myfdrow:&[Option], nfds:u64, infdset: fd_set, thisfdset: &mut fd_set, mappingtable: &mut HashMap) -> Result<(u64,HashSet<(u64,u64)>),threei::RetVal> { + let mut unrealhashset:HashSet<(u64,u64)> = HashSet::new(); + // Iterate through the infdset and set those values as is appropriate + let mut highestpos = 0; + + // Clippy is somehow missing how pos is using bit. + #[allow(clippy::needless_range_loop)] + for bit in 0..nfds as usize { + let pos = bit as u64; + if _fd_isset(pos,&infdset) { + if let Some(entry) = myfdrow[bit] { + if entry.realfd == NO_REAL_FD { + unrealhashset.insert((pos,entry.optionalinfo)); + } + else { + mappingtable.insert(entry.realfd, pos); + _fd_set(entry.realfd,thisfdset); + // I add one because select expects nfds to be the max+1 + highestpos = cmp::max(highestpos, entry.realfd+1); + } + } + else { + return Err(threei::Errno::EINVAL as u64); + } + } + } + Ok((highestpos,unrealhashset)) +} + +// helper to call before calling select beneath you. Translates your virtfds +// to realfds. +// See: https://man7.org/linux/man-pages/man2/select.2.html for details / +// corner cases about the arguments. + +// I hate doing these, but don't know how to make this interface better... +#[allow(clippy::type_complexity)] +#[allow(clippy::too_many_arguments)] +// #[doc = include_str!("../docs/get_real_bitmasks_for_select.md")] +pub fn get_real_bitmasks_for_select(cageid:u64, nfds:u64, readbits:Option, writebits:Option, exceptbits:Option) -> Result<(u64, Option, Option, Option, [HashSet<(u64,u64)>;3], HashMap),threei::RetVal> { + + if nfds >= FD_PER_PROCESS_MAX { + return Err(threei::Errno::EINVAL as u64); + } + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + let mut unrealarray:[HashSet<(u64,u64)>;3] = [HashSet::new(),HashSet::new(),HashSet::new()]; + let mut mappingtable:HashMap = HashMap::new(); + let mut newnfds = 0; + + // dashmaps are lockless, but usually I would grab a lock on the fdtable + // here... + let binding = FDTABLE.get(&cageid).unwrap(); + let thefdrow = binding.value().clone(); + + // putting results in a vec was the cleanest way I found to do this.. + let mut resultvec = Vec::new(); + + for (unrealoffset, inset) in [readbits,writebits, exceptbits].into_iter().enumerate() { + #[allow(clippy::single_match_else)] // I find this clearer + match inset { + Some(virtualbits) => { + let mut retset = _init_fd_set(); + let (thisnfds,myunrealhashset) = _do_bitmods(&thefdrow,nfds,*virtualbits, &mut retset,&mut mappingtable)?; + resultvec.push(Some(retset)); + newnfds = cmp::max(thisnfds, newnfds); + unrealarray[unrealoffset] = myunrealhashset; + } + None => { + // This item is null. No unreal items + resultvec.push(None); + unrealarray[unrealoffset] = HashSet::new(); + } + } + } + + Ok((newnfds, resultvec[0], resultvec[1], resultvec[2], unrealarray, mappingtable)) + +} + + +// helper to call after calling select beneath you. returns the fd_sets you +// need for your return from a select call and the number of unique flags +// set... + +// I hate doing these, but don't know how to make this interface better... +#[allow(clippy::type_complexity)] +#[allow(clippy::too_many_arguments)] +// I given them the hashmap, so don't need flexibility in what they return... +#[allow(clippy::implicit_hasher)] +// #[doc = include_str!("../docs/get_virtual_bitmasks_from_select_result.md")] +pub fn get_virtual_bitmasks_from_select_result(nfds:u64, readbits:Option, writebits:Option, exceptbits:Option,unrealreadset:HashSet, unrealwriteset:HashSet, unrealexceptset:HashSet, mappingtable:&HashMap) -> Result<(u64, Option, Option, Option),threei::RetVal> { + + // Note, I don't need the cage_id here because I have the mappingtable... + + assert!(nfds < FD_PER_PROCESS_MAX,"This shouldn't be possible because we shouldn't have returned this previously"); + + let mut flagsset = 0; + let mut retvec = Vec::new(); + + for (insetoption,unrealset) in [(readbits,unrealreadset), (writebits,unrealwriteset), (exceptbits,unrealexceptset)] { + // If I don't have any data, just return None (NULL) and skip... + if insetoption.is_none()&&unrealset.is_empty() { + retvec.push(None); + continue; + } + + let mut retbits = _init_fd_set(); + if let Some(inset) = insetoption { + for bit in 0..nfds as usize { + let pos = bit as u64; + if _fd_isset(pos,&inset)&& !_fd_isset(*mappingtable.get(&pos).unwrap(),&retbits) { + flagsset+=1; + _fd_set(*mappingtable.get(&pos).unwrap(),&mut retbits); + } + } + } + for virtfd in unrealset { + if !_fd_isset(virtfd,&retbits) { + flagsset+=1; + _fd_set(virtfd,&mut retbits); + } + } + retvec.push(Some(retbits)); + } + + Ok((flagsset,retvec[0],retvec[1],retvec[2])) + +} + + + +/********************** POLL SPECIFIC FUNCTIONS **********************/ + +// helper to call before calling poll beneath you. replaces the fds in +// the poll struct with virtual versions and returns the items you need +// to check yourself... +#[allow(clippy::type_complexity)] +// #[doc = include_str!("../docs/convert_virtualfds_to_real.md")] +#[must_use] // must use the return value if you call it. +pub fn convert_virtualfds_to_real(cageid:u64, virtualfds:Vec) -> (Vec, Vec<(u64,u64)>, Vec, HashMap) { + + assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + + let mut unrealvec = Vec::new(); + let mut realvec = Vec::new(); + let mut invalidvec = Vec::new(); +// I can't do this because it doesn't implement the copy trait... +// let thefdrow = *FDTABLE.get(&cageid).unwrap(); + let binding = FDTABLE.get(&cageid).unwrap(); + let thefdrow = binding.value().clone(); + let mut mappingtable:HashMap = HashMap::new(); + + // BUG?: I'm ignoring the fact that virtualfds can show up multiple times. + // I'm not sure this actually matters, but I didn't think hard about it. + for virtfd in virtualfds { + if let Some(entry) = thefdrow[virtfd as usize] { + // always append the value here. NO_REAL_FD will be added + // in the appropriate places to tell them to handle those calls + // themself. + realvec.push(entry.realfd); + if entry.realfd == NO_REAL_FD { + unrealvec.push((virtfd,entry.optionalinfo)); + } + else{ + mappingtable.insert(entry.realfd, virtfd); + } + } + else { + // Add this because they need to handle it if POLLNVAL is set. + // An exception should not be raised!!! + realvec.push(INVALID_FD); + invalidvec.push(virtfd); + } + } + + (realvec, unrealvec, invalidvec, mappingtable) +} + + + +// helper to call after calling poll. replaces the realfds the vector +// with virtual ones... +// #[doc = include_str!("../docs/convert_realfds_back_to_virtual.md")] +// I given them the hashmap, so don't need flexibility in what they return... +#[allow(clippy::implicit_hasher)] +#[must_use] // must use the return value if you call it. +pub fn convert_realfds_back_to_virtual(realfds:Vec, mappingtable:&HashMap) -> Vec { + + // I don't care what cage was used, and don't need to lock anything... + // I have the mappingtable! + + let mut virtvec = Vec::new(); + + for realfd in realfds { + virtvec.push(*mappingtable.get(&realfd).unwrap()); + } + + virtvec +} + + + +/********************** EPOLL SPECIFIC FUNCTIONS **********************/ + + +// Okay this adds a big wrinkle, epollfds. The reason these are complex is +// multi-fold: +// 1) they themselves are file descriptors and take up a slot. +// 2) a epollfd can point to any number of other fds +// 3) an epollfd can point to epollfds, which can point to other epollfds, etc. +// and possibly cause a loop to occur (which is an error) +// 4) an epollfd can point to a mix of virtual and realfds. +// +// My thinking is this is handled as similarly to poll as possible. We push +// off the problem of understanding what the event types are to the implementer +// of the library. +// +// In my view, epoll_wait() is quite simple to support. One basically just +// keeps a list of virtual fds for this epollfd and their corresponding event +// types, which they may need to poll themselves. After this, they handle the +// call. +// +// epoll_create makes a new fd type, which really is unfortunate. To this +// point, I haven't had to care about anything except in-memory fds (unreal), +// and doing the virtual <-> real mappings. The caller can decide whether to +// create an underlying epollfd when this is called, when epoll_ctl is called +// to add a realfd, etc. +// +// epoll_ctl is complex, but really has the same fundamental problem as +// epoll_create: the epollfd. +// +// What if I just ignore the epollfd problem by just making another table +// for epoll information? Then what I do is set the realfd to EPOLLFD and +// have optionalinfo point into the epollfd table. If I do this, then when +// epoll_create is called, if it contains realfds, those need to be passed +// down to the underlying epoll_create. Similarly, when epoll_ctl is called, +// we either modify our data or return the realfd... +// +// Interestingly, this actually would be just as easy to build on top of the +// fdtables library as into it. +// +// Each epollfd will have some virtual fds associated with it. Each of those +// will have an event mask. So I'll have a mutex around an EPollTable struct. +// This contains the next available entry and an epollhashmap. +// I use a hashmap here to better support removing and modifying items. + +// Note, I'm defining a bunch of symbols myself because libc doesn't import +// them on systems that don't support epoll and I want to be able to build +// the code anywhere. See commonconstants.rs for more info. + +// Design notes: I'm not adding realfds. I return them when you do a epoll_ctl +// operation that tries to add them. So, I only have unrealfds in my epoll +// structures. + +// TODO: I don't clean up this table yet. I probably should when the last +// reference to a fd is closed, but this bookkeeping seems excessive at this +// time... +// #[derive(Clone, Debug)] +// struct EPollTable { +// highestneverusedentry: u64, // Never resets (even after close). Used to +// // let us quickly get an unused entry +// thisepolltable: HashMap>, // the epollentry -> +// // virtfd -> +// // event map +// realfdtable: HashMap, // the epollentry -> realfd map. I need +// // this because the realfd field in the +// // main data structure is EPOLLFD +// } + +// lazy_static! { + +// #[derive(Debug)] +// static ref EPOLLTABLE: Mutex = { +// let newetable = HashMap::new(); +// let newrealfdtable = HashMap::new(); +// let m = EPollTable { +// highestneverusedentry:0, +// thisepolltable:newetable, +// realfdtable:newrealfdtable, +// }; +// Mutex::new(m) +// }; +// } + + +// // #[doc = include_str!("../docs/epoll_create_helper.md")] +// pub fn epoll_create_helper(cageid:u64, realfd:u64, should_cloexec:bool) -> Result { + +// let mut ept = EPOLLTABLE.lock().unwrap(); + +// // I'll use my other functions to make this easier. +// // return the same errno (EMFile), if we get one +// let newepollfd = get_unused_virtual_fd(cageid, EPOLLFD, should_cloexec, ept.highestneverusedentry)?; + +// let newentry = ept.highestneverusedentry; +// ept.highestneverusedentry+=1; +// // add in my realfd. +// ept.realfdtable.insert(newentry,realfd); +// // if it errored out above that is okay. I haven't changed any state yet. +// ept.thisepolltable.insert(newentry, HashMap::new()); +// Ok(newepollfd) + +// } + + +// // #[doc = include_str!("../docs/try_epoll_ctl.md")] +// pub fn try_epoll_ctl(cageid:u64, epfd:u64, op:i32, virtfd:u64, event:epoll_event) -> Result<(u64,u64),threei::RetVal> { + +// assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + +// if epfd == virtfd { +// return Err(threei::Errno::EINVAL as u64); +// } + +// // Is the epfd ok? +// let epollentrynum:u64 = match FDTABLE.get(&cageid).unwrap()[epfd as usize] { +// None => { +// return Err(threei::Errno::EBADF as u64); +// }, +// // Do I need to have EPOLLFDs here too? +// Some(tableentry) => { +// if tableentry.realfd != EPOLLFD { +// return Err(threei::Errno::EINVAL as u64); +// } +// tableentry.optionalinfo +// }, +// }; + +// // Okay, I know which table entry and verified the virtfd... + +// let mut epttable = EPOLLTABLE.lock().unwrap(); +// let realepollfd = *epttable.realfdtable.get(&epollentrynum).unwrap(); +// let eptentry = epttable.thisepolltable.get_mut(&epollentrynum).unwrap(); + +// // check if the virtfd is real and error... +// // I don't care about its contents except to ensure it isn't real... +// if let Some(tableentry) = FDTABLE.get(&cageid).unwrap()[virtfd as usize] { +// // Do I need to have EPOLLFDs here too? +// if tableentry.realfd != NO_REAL_FD { +// // Return realfds because the caller should handle them instead +// // I only track unrealfds. +// if tableentry.realfd == EPOLLFD { +// // BUG: How should I be doing this, really!?! +// println!("epollfds acting on epollfds is not supported!"); +// } +// return Ok((realepollfd,tableentry.realfd)); +// } +// } +// else { +// return Err(threei::Errno::EBADF as u64); +// } + +// // okay, virtfd is real... + +// match op { +// EPOLL_CTL_ADD => { +// if eptentry.contains_key(&virtfd) { +// return Err(threei::Errno::EEXIST as u64); +// } +// // BUG: Need to check for ELOOP here... + +// eptentry.insert(virtfd, event); +// }, +// EPOLL_CTL_MOD => { +// if !eptentry.contains_key(&virtfd) { +// return Err(threei::Errno::ENOENT as u64); +// } +// eptentry.insert(virtfd, event); +// }, +// EPOLL_CTL_DEL => { +// if !eptentry.contains_key(&virtfd) { +// return Err(threei::Errno::ENOENT as u64); +// } +// eptentry.remove(&virtfd); +// }, +// _ => { +// return Err(threei::Errno::EINVAL as u64); +// }, +// }; +// Ok((realepollfd,NO_REAL_FD)) +// } + + +// // #[doc = include_str!("../docs/get_epoll_wait_data.md")] +// pub fn get_epoll_wait_data(cageid:u64, epfd:u64) -> Result<(u64,HashMap),threei::RetVal> { + +// assert!(FDTABLE.contains_key(&cageid),"Unknown cageid in fdtable access"); + +// // Note that because I don't track realfds or deal with epollfds, I just +// // return the epolltable... +// let epollentrynum:u64 = match FDTABLE.get(&cageid).unwrap()[epfd as usize] { +// None => { +// return Err(threei::Errno::EBADF as u64); +// }, +// // Do I need to have EPOLLFDs here too? +// Some(tableentry) => { +// if tableentry.realfd != EPOLLFD { +// return Err(threei::Errno::EINVAL as u64); +// } +// tableentry.optionalinfo +// }, +// }; + +// let epttable = EPOLLTABLE.lock().unwrap(); +// Ok((*epttable.realfdtable.get(&epollentrynum).unwrap(),epttable.thisepolltable[&epollentrynum].clone())) +// } + + + +// /********************** TESTING HELPER FUNCTION **********************/ + +// #[doc(hidden)] +// // Helper to initialize / empty out state so we can test with a clean system... +// // This is only used in tests, thus is hidden... +// pub fn refresh() { +// FDTABLE.clear(); +// FDTABLE.insert(threei::TESTING_CAGEID,vec![Option::None;FD_PER_PROCESS_MAX as usize]); +// let mut closehandlers = CLOSEHANDLERTABLE.lock().unwrap_or_else(|e| { +// CLOSEHANDLERTABLE.clear_poison(); +// e.into_inner() +// }); +// closehandlers.intermediate = NULL_FUNC; +// closehandlers.last = NULL_FUNC; +// closehandlers.unreal = NULL_FUNC; +// // Note, it doesn't seem that Dashmaps can be poisoned... +// } \ No newline at end of file diff --git a/src/example_grates/imfs_grate.rs b/src/example_grates/imfs_grate.rs new file mode 100644 index 00000000..9864a432 --- /dev/null +++ b/src/example_grates/imfs_grate.rs @@ -0,0 +1,218 @@ +// Example grates +// +// handle file requests internally (need inode / dir, fd table) +// ex: in memory file system, network file system, separate /tmp per-process, +// lindfs +// +// note: +// it's slightly odd I need a fd table here. I guess it is custom to the +// grate though. +// + +// ******************************************************************* +// *** This file contains the sketch of Lind's in memory fs *** +// *** THIS CODE WILL NOT RUN! IT IS MEANT TO ILLUSTRATE *** +// *** MY THINKING ABOUT HOW SOMETHING LIKE THIS WOULD *** +// *** WORK. WE WILL MAKE A WORKING GRATE ONCE WE *** +// *** UNDERSTAND THE INTERFACES AND CHALLENGES BETTER. *** +// ******************************************************************* + + + + +use lind_encasement; +use lind_3i; +use lind_3i::constants::*; + +// I'm going to include this, which will use fdtable extensively... +use virt_fsmetadata; + +/**************** virt_fsmetadata selected exerpts *****************/ + +pub struct data_storage_interface { + pub fn create_item(item_id:u64); + pub fn open_item(item_id:u64); + pub fn close_item(item_id:u64); + pub fn store_data(targetcage: u64, item_id:u64, position:u64; data: *[u8], length:u64) -> Result<...>; + pub fn read_data(targetcage: u64, item_id:u64, position:u64; data: *[u8], amount:u64) -> Result<...>; + // could implement or add others... + pub fn log_action(...); + + +}; + +// I'm assuming that we always know all of the metadata because we virtualize +// it. This means we know the size of every file, etc. + +/*** All the inode, data structure code goes here... ***/ + + + +// We hook in the handlers for read, write, etc. +pub fn setup_interposition(calls_to_integrate:data_storage_interface) { + // The encasement library will help w/ syscall replacements for my children + let mut syscall_replacements: Vec = Vec::new(); + + // I handle all open calls, etc... + syscall_replacements.push(repltable{syscallid:PIPE_SYSID, func:open_syscall}); + + // Let's handle read and write... + syscall_replacements.push(repltable{syscallid:READ_SYSID, func:read_syscall}); + syscall_replacements.push(repltable{syscallid:WRITE_SYSID, func:write_syscall}); + + syscall_replacements.push(repltable{syscallid:MMAP_SYSID, func:mmap_syscall}); + // And so on until we have all the file system calls... + + //... until finally + lind_encasement.replace_syscalls(syscall_replacements); +} + +// I'm assuming that how the above calls are used is quite self-evident. +// In essence, we just have the existing interface for most things, but the +// above extracts out the file storage. + +// This gets overridden... As would related calls like munmap() +pub fn mmap_syscall() { + ... +} + + +pub fn initialize_fs_metadata() { + // I'll either make this fresh or read it in here... + ... +} + + +// The code here keeps a fdtable, inode information, directory information, +// etc, much the same way as the existing code. When we open something (which +// is not open), we call open_item + + + + +/**************** Unique code for this implementation ***************/ + +pub fn init_imfs() { + // Create a global data structure which allocates in memory blocks to store + // the things it needs to persist. +} + + +pub fn my_create_item(item_id:u64) { + // create an entry in the item table. The value stored is the fd. + + if item_id in itemtable { + panic!("Item already open"); + } + // Ensure we are creating this item or panic! + itemtable[item_id] = _make_block_list(); +} + + +pub fn my_store_data(targetcageid: u64, item_id:u64, position:u64; data: *[u8], length:u64) -> Result<...> { + + // This is my custom way to handle storing metadata... + let mut source_data_pos = data; + + let currentlength = length; + // loops through while getting and/or locating blocks + while currentlength > 0 { + let blockptr,allowedamount = _alloc_or_get_block_address(itemtable[item_id],position,currentlength); + lind_3i.read_from_cage(targetcageid, source_data_pos, blockptr, allowedamount); + currentlength -= allowedamount; + source_data_pos += allowedamount; + + } + return length; +} + + +pub fn my_read_data(targetcageid: u64, item_id:u64, position:u64; data: *[u8], amount:u64) -> Result<...> { + // This is my custom way to handle reading in data... + + let mut writedatapos = data; + // Walk through the blocks and use + for blockpos,length in _get_block_location_iter(item_id,position,amount) { + lind_3i.write_to_cage(targetcageid,blockpos, writedatapos,length); + writedatapos +=length; + } + + // I always return it all (?virt_metadata tracks the length?) + return amount; +} + + +// I'm going to interpose here, so I need to have the full interface... +pub fn my_mmap_syscall(cageid: u64, targetcageid: u64, callid: u64, addr: u64, length: u64, prot: u64, flags: u64, fd: u64, offset: u64) -> u64 { + // *mmap(void addr[.length], size_t length, int prot, int flags, int fd, off_t offset); + + // do I need to call their code to set anything up, like file descriptors? + + + if ! MAP_ANONYMOUS & flags { + // If this is file-backed, I would loop through and allocate the + // blocks, much like with my_store_data(...). + + } + else { // MAP_ANONYMOUS + // I probably don't understand all the nuances of ANONYMOUS mapping, + // but I'm assuming I can just zero out the blocks of memory... + if flags & MAP_PRIVATE { + // if not shared, likely okay to just do nothing but zero the + // memory... + return...; + } + // I'm not sure how to handle MAP_SHARED. Likely need to set up the + // shared memory mapping on fork... + // However, we don't currently handle this in lindfs... + } + + + // Now I loop through and allocated shared memory for each block so that + // it is mapped from my cache into the cage's memory... + +} + +// munmap is similar, but easier. Just removes memory mappings... + + +// We probably need to do something on fork so that we can handle mmap +// correctly... +pub fn my_fork_syscall(cageid: u64, targetcageid: u64, callid: u64, addr: u64, length: u64, prot: u64, flags: u64, fd: u64, offset: u64) -> u64 { + + // Need to handle MAP_SHARED mappings here, so they are mapped shared + // into the child +} + + + + +fn main() { + // Setup my item table... + init_item_table(); + + // Have the virtual_fs initialize itself... + virt_metadata.initialize_fs_metadata(); + + // Register my calls + let mycalls = virt_metadata::data_storage_interface { + create_item: my_create_item, + open_item: virt_metadata::NOOP, + close_item: virt_metadata::NOOP, + store_data: my_store_data, + read_data: my_read_data, + // could implement or add others... + log_action:virt_metadata::DEFAULT, + // Don't persist metadata, since it's an imfs... + store_directories:false, + store_inodes:false, + }; + + + // need to replace calls with the right ones for our children + virt_metadata.setup_interposition(mycalls); + + // Need to instantiate child cage(s) here... + lind_encasement.initialize_children_and_consume_thread(); + // Never reaches here! +} diff --git a/src/example_grates/imp_grate.rs b/src/example_grates/imp_grate.rs new file mode 100644 index 00000000..77fd1e57 --- /dev/null +++ b/src/example_grates/imp_grate.rs @@ -0,0 +1,242 @@ + +// +// custom handle non-file requests internally (fd table) +// ex: in memory pipes, loopback sockets, unix domain sockets, etc. +// +// note: +// How do I reserve the fd I need here? Do I have a call to reserve a fd? +// If so, I likely need a way to close or release it as well... +// I also should keep a fd table to see how to handle this... +// + +// ******************************************************************* +// *** This file contains the sketch of an in-memory pipe handler *** +// *** THIS CODE WILL NOT RUN! IT IS MEANT TO ILLUSTRATE *** +// *** MY THINKING ABOUT HOW SOMETHING LIKE THIS WOULD *** +// *** WORK. WE WILL MAKE A WORKING GRATE ONCE WE *** +// *** UNDERSTAND THE INTERFACES AND CHALLENGES BETTER. *** +// ******************************************************************* + +use fdtable; +use lind_3i; +use lind_3i::constants::*; +use lind_encasement; + + +// Initialize the circular buffers for the in memory pipes +def init_circular_buffer_table() { + // do whatever initialization I need to for the circular buffers... +} + +// Get a new, unused pipe number... +fn _get_pipe_num() { +} + +// handle pipe creation +pub fn my_pipe_syscall(cageid: u64, targetcageid: u64, callid: u64, pipefds: u64, _: u64, _: u64, _: u64, _: u64, _: u64) -> u64 { + // int pipe(int pipefd[2]); + + let real_read_fd = lind_3i::reserve_fd(targetcageid,None); + let real_write_fd = lind_3i::reserve_fd(targetcageid,None); + // size of a file descriptor in bytes... + const FD_SIZE = 4; + + // Adds an entry to the table. The CLOEXEC flag is needed so we know + // if we need to clobber this on exec or not. + let mypipenum = _get_pipe_num(); + let virt_read_pipe_fd =fdtable::get_unused_virtual_fd(targetcageid,real_read_fd,false,mypipenum); + let virt_write_pipe_fd = fdtable::get_unused_virtual_fd(targetcageid,real_write_fd,false,mypipenum); + + // Writing the fds back to the child... + lind_3i::write_to_cage(targetcageid,pipefds,virt_read_pipe_fd,FD_SIZE); + pipefds += FD_SIZE; + lind_3i::write_to_cage(targetcageid,pipefds,virt_write_pipe_fd,FD_SIZE); + + // No need to call below me... + + // Return success! + return 0; + +} + +pub fn my_read_syscall(cageid: u64, targetcageid: u64, callid: u64, fd: u64, buf: u64, count: u64, _: u64, _: u64, _: u64) -> u64 { + // ssize_t read(int fd, void buf[.count], size_t count); + + + /***** Read from the circular buffer, block, etc. here... *****/ + lind_3i::write_to_cage(targetcageid,buf,local_buffer,count); + + + return count; + + /**** End specific pipe logic */ + +} + +pub fn my_write_syscall(cageid: u64, targetcageid: u64, callid: u64, fd: u64, buf: u64, count: u64, _: u64, _: u64, _: u64) -> u64 { + // ssize_t write(int fd, const void buf[.count], size_t count); + + + /***** Write to the circular buffer, block, etc. here... *****/ + lind_3i::read_from_cage(targetcageid,local_buffer,buf,count); + + + return count; + + /**** End specific pipe logic */ + +} + + +pub fn my_close_syscall(cageid: u64, targetcageid: u64, callid: u64, fd: u64, buf: u64, count: u64, _: u64, _: u64, _: u64) -> u64 { + // int close(int fd); + + /***** clean up the buffer, if needed ****/ + + return 0; + + /**** End specific pipe logic */ + +} + +pub fn my_select_helper(targetcageid: u64, callid: u64, fdvec: Vec) -> Vec { + // Select, poll, etc. are a bit of a mess. Maybe it makes sense to have + // a helper instead? + + /* call with a vector of file descriptors and returns an vector of fds + * with an enum with a statue for each POLLERR, POLLHUP, POLLIN, POLLOUT + * etc. + */ + + + return ...; + + /**** End specific pipe logic */ + +} + + + + + + + + + + + + +/************** Example pseudocode from the fdtable code *****************/ + +pub fn fork_syscall(cageid: u64, targetcageid: u64, callid: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64, arg6: u64) -> u64 { + // int fork(); + let newcageid= lind_encasement::handle_fork(targetcageid); + + // Make a copy of the parent's FD table... + _dup_row(targetcageid,newcageid); + return newcageid; + +} + +pub fn exec_syscall(cageid: u64, targetcageid: u64, callid: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64, arg6: u64) -> u64 { + + // close fds that were close on exec... + _handle_cloexec(targetcageid); + + return MAKE_SYSCALL(targetcageid, callid, arg1, arg2, arg3, arg4, arg5, arg6); + +} + +// dup, etc. are handlede here as well... + + +// This is the basic logic for all of the system calls that fdtable handles. +// It decides where to route things... +fn _route_syscall_or_calldown(cageid: u64, targetcageid: u64, callid: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64, arg6: u64) -> u64 { + // close(fd); + + if _fd_table_in(targetcageid,arg1) { + // Call handler just calls the appropriate system call function from + // the table... + return call_handler(cageid,targetcageid, callid, arg1,arg2,arg3,arg4,arg5,arg6) + else { + // Call beneath us... + return MAKE_SYSCALL(targetcageid, callid, arg1, arg2, arg3, arg4, arg5, arg6); + } + +} + +pub fn boilerplate_init_for_fdtables(Vec ,enum handler) { + + // Let fdtable handle these (and likely I need to add others... This + // most likely would be handled for me via a function call to initialize + // these at the same time instead of me doing it individually... + syscall_replacements::push(repltable{syscallid:FORK_SYSID, func:fdtable::fork_syscall}); + syscall_replacements::push(repltable{syscallid:EXEC_SYSID, func:fdtable::exec_syscall}); + syscall_replacements::push(repltable{syscallid:DUP_SYSID, func:fdtable::dup_syscall}); + syscall_replacements::push(repltable{syscallid:DUP2_SYSID, func:fdtable::dup2_syscall}); + + // Ignore open because we don't care about files... If we handled named + // pipes, we would add open... +// syscall_replacements::push(repltable{syscallid:OPEN_SYSID, func:lind_encasement::DEFAULT}); + if handler == CALLDOWN { + add _route_syscall_or_calldown to essentially all calls... + + } + +} + +/*************************** MAIN *******************************/ + + + +// This sets up the calls so that the right things are interposed... +fn setup_interposition() { + // The encasement library will help w/ syscall replacements for my children + let mut syscall_replacements: Vec = Vec::new(); + + // This sets up basic handlers, and says unknown fds should call to the + // grate below... This is useful when we only want to intercept some calls + // but do want to pass others through. + boilerplate_init_for_fdtables(syscall_replacements,CALLDOWN); + + // I handle all pipe calls in my own code... + syscall_replacements::push(repltable{syscallid:PIPE_SYSID, func:my_pipe_syscall}); + + // Let's handle read and write... + syscall_replacements::push(repltable{syscallid:READ_SYSID, func:fdtable::read_syscall}); + // add my handler for read. Will only be called on fds I added to the + // table. Other FDs will call below... + fdtable::add_handler(syscallid::READ_SYSID, my_read_syscall); + syscall_replacements::push(repltable{syscallid:WRITE_SYSID, func:fdtable::write_syscall}); + fdtable::add_handler(syscallid::WRITE_SYSID, my_write_syscall); + + // add my handler for close. Only called on my fds + syscall_replacements::push(repltable{syscallid:CLOSE_SYSID, func:fdtable::close_syscall}); + fdtable::add_handler(syscallid::CLOSE_SYSID, my_close_syscall); + + + // This handles select, poll, etc. in a clean way. Only called for my fds + fdtable::add_select_helper(my_select_helper); + + + lind_encasement::replace_syscalls(syscall_replacements); + +} + + +fn main() { + // do my setup... Where does this live? Rust doesn't like globals, + // but I need to share this... + fdtable::get_new_table(); + + // If I'm setting up circular buffers, do it here... + init_circular_buffer_table(); + + // need to replace calls with the right ones for our children + setup_interposition(); + + // Need to instantiate child cage(s) here... + lind_encasement::initialize_children_and_consume_thread(); + // Never reaches here! +} diff --git a/src/example_grates/lindfs_grate.rs b/src/example_grates/lindfs_grate.rs new file mode 100644 index 00000000..f00acfe4 --- /dev/null +++ b/src/example_grates/lindfs_grate.rs @@ -0,0 +1,215 @@ +// Example grates +// +// handle file requests internally (need inode / dir, fd table) +// ex: in memory file system, network file system, separate /tmp per-process, +// lindfs +// +// note: +// it's slightly odd I need a fd table here. I guess it is custom to the +// grate though. +// + +// ******************************************************************* +// *** This file contains the sketch of Lind's safeposix_rust fs *** +// *** THIS CODE WILL NOT RUN! IT IS MEANT TO ILLUSTRATE *** +// *** MY THINKING ABOUT HOW SOMETHING LIKE THIS WOULD *** +// *** WORK. WE WILL MAKE A WORKING GRATE ONCE WE *** +// *** UNDERSTAND THE INTERFACES AND CHALLENGES BETTER. *** +// ******************************************************************* + + +use lind_encasement; +use lind_3i; +use lind_3i::constants::*; + +// I'm going to include this, which will use fdtable extensively... +use virt_fsmetadata; + +/**************** virt_fsmetadata selected exerpts *****************/ + +pub struct data_storage_interface { + pub fn create_item(item_id:u64); + pub fn open_item(item_id:u64); + pub fn close_item(item_id:u64); + pub fn store_data(targetcage: u64, item_id:u64, position:u64; data: *[u8], length:u64) -> Result<...>; + pub fn read_data(targetcage: u64, item_id:u64, position:u64; data: *[u8], amount:u64) -> Result<...>; + // could implement or add others... + pub fn log_action(...); + + +}; + +// I'm assuming that we always know all of the metadata because we virtualize +// it. This means we know the size of every file, etc. + +/*** All the inode, data structure code goes here... ***/ + + + +// We hook in the handlers for read, write, etc. +pub fn setup_interposition(calls_to_integrate:data_storage_interface) { + // The encasement library will help w/ syscall replacements for my children + let mut syscall_replacements: Vec = Vec::new(); + + // I handle all open calls, etc... + syscall_replacements.push(repltable{syscallid:PIPE_SYSID, func:open_syscall}); + + // Let's handle read and write... + syscall_replacements.push(repltable{syscallid:READ_SYSID, func:read_syscall}); + syscall_replacements.push(repltable{syscallid:WRITE_SYSID, func:write_syscall}); + + syscall_replacements.push(repltable{syscallid:MMAP_SYSID, func:mmap_syscall}); + // And so on until we have all the file system calls... + + //... until finally + lind_encasement.replace_syscalls(syscall_replacements); +} + +// I'm assuming that how the above calls are used is quite self-evident. +// In essence, we just have the existing interface for most things, but the +// above extracts out the file storage. + +// NOTE: mmap in safeposix_rust just does some error checking and then calls +// down. We can do the same here! +pub fn mmap_syscall() { + ... +} + + +pub fn initialize_fs_metadata() { + // I'll either make this fresh or read it in here... + ... +} + + +// The code here keeps a fdtable, inode information, directory information, +// etc, much the same way as the existing code. When we open something (which +// is not open), we call open_item + + + + +/**************** Unique code for this implementation ***************/ + +pub fn init_item_table() { + // Create a global table of items here. In this implementation, this + // will map item_ids to fds of mine. +} + + +pub fn my_create_item(item_id:u64) { + // create an entry in the item table. The value stored is the fd. + + if item_id in itemtable { + panic!("Item already open"); + } + // Ensure we are creating this item or panic! + itemtable[item_id] = open("linddata.{item_id}",O_CREAT|O_EXCL|O_RDWR,0o600).unwrap(); +} + + +pub fn my_open_item(item_id:u64) { + // create an entry in the item table. The value stored is the fd. + + if item_id in itemtable { + panic!("Item already open"); + } + // Ensure we're opening an existing item or panic! + itemtable[item_id] = open("linddata.{item_id}",O_RDWR,0o600).unwrap(); + +} + +pub fn my_close_item(item_id:u64) { + // create an entry in the item table. The value stored is the fd. + + if !item_id in itemtable { + panic!("Item is not open"); + } + close(itemtable[item_id]) + +} + +pub fn my_store_data(targetcage: u64, item_id:u64, position:u64; data: *[u8], length:u64) -> Result<...> { + // This is my custom way to handle storing data... + + // I should never be past the end of the file since the virt_fs knows + // the position and has checked it... + + let fd = itemtable[item_id]; + + // I need to lock this... + { + + let last_pos = virt_fsmetadata.get_item_id_entry(item_id).position; + + if position != last_pos { + lseek(fd, position); + } + // BUG: This fd is this cage's fd, while the data pointer is from the + // target cage. This means that 3i needs to be able to support a mix + // of cage associations for different arguments... BUG BUG BUG + let amt_written = MAKE_SYSCALL(targetcage, WRITE_SYSID, fd, data, length).unwrap(); + // do error handling, likely panic, since no error should occur... + } + return amt_written; // To be used to update the position... + +} + + +pub fn my_read_data(targetcage: u64, item_id:u64, position:u64; data: *[u8], amount:u64) -> Result<...> { + // This is my custom way to handle reading in data... + + // Once again, I should never be past the end of the file, etc. since the + // virt_fs knows the position and has checked it... + + let fd = itemtable[item_id]; + + // I need to lock this... + { + + let last_pos = virt_fsmetadata.get_item_id_entry(item_id).position; + + if position != last_pos { + lseek(fd, position); + } + // BUG: This fd is the grate's fd, while the data pointer is from the + // target cage. This means that 3i needs to be able to support a mix + // of cage associations for different arguments... BUG BUG BUG + let amt_read = MAKE_SYSCALL(targetcage, READ_SYSID, fd, data, amount).unwrap(); + // do error handling, likely panic, since no error should occur... + } + return amt_read; // Update virt_metadata position... + +} + + + +fn main() { + // Setup my item table... + init_item_table(); + + // Have the virtual_fs initialize itself... + virt_metadata.initialize_fs_metadata(); + + // Register my calls + let mycalls = virt_metadata::data_storage_interface { + create_item: my_create_item, + open_item: my_open_item, + close_item: my_close_item, + store_data: my_store_data, + read_data: my_read_data, + // could implement or add others... + log_action:virt_metadata::DEFAULT, + // persist metadata out into items. + store_directories:true, + store_inodes:true, + }; + + + // need to replace calls with the right ones for our children + virt_metadata.setup_interposition(mycalls); + + // Need to instantiate child cage(s) here... + lind_encasement.initialize_children_and_consume_thread(); + // Never reaches here! +} diff --git a/src/example_grates/log_grate.rs b/src/example_grates/log_grate.rs new file mode 100644 index 00000000..d504f035 --- /dev/null +++ b/src/example_grates/log_grate.rs @@ -0,0 +1,40 @@ +// Example grates +// +// pass through +// ex: filter network calls by dest or file accesses by path, measure API +// usage / do strace equivalent. +// +// note: +// With the ability to set the target cage ID, I don't need to do much to +// support multi-tenancy. I can just use the targetcageid to do this. +// +// + + +// This does the heavy lifting (such as there is any...) +use lind_encasement; + +// This file contains a basic, example system call handler that just prints +// out the arguments + +pub fn log_syscall(cageid: u64, targetcageid: u64, callid: u64, arg1: u64, arg2: u64, arg3: u64, arg4: u64, arg5: u64, arg6: u64) -> u64 { + // do whatever we want to log here... + println!("{cageid}, {targetcageid}, {callid}, {arg1}, {arg2}, {arg3}, {arg4}, {arg5}, {arg6}"); + + // Note, we don’t need to ensure the cageid can do an op + // on the targetcage because 3i checks that for us. Nice! + + // ...and make the original system call! + return MAKE_SYSCALL(targetcageid, callid, arg1, cleaned_up_ptr_arg, count_as_u64, arg4, arg5, arg6); +} + + +fn main() { + + // Replace all calls with the one given here... + lind_encasement.replace_all_syscalls(log_syscall); + + // instantiate child cage(s) as is needed here... + lind_encasement.initialize_children_and_consume_thread(); + // Never reaches here! +} diff --git a/src/example_grates/mod.rs b/src/example_grates/mod.rs new file mode 100644 index 00000000..0f6f3747 --- /dev/null +++ b/src/example_grates/mod.rs @@ -0,0 +1,15 @@ +// pub mod fdtable; +pub mod threei; +pub mod commonconstants; +pub mod dashmapvecglobal; +// pub mod vanillaglobal; +// pub mod muthashmaxglobal; +// pub mod dashmaparrayglobal; + +// pub use fdtable::*; +pub use threei::*; +pub use commonconstants::*; +pub use dashmapvecglobal::*; +// pub use vanillaglobal::*; +// pub use muthashmaxglobal::*; +// pub use dashmaparrayglobal::*; \ No newline at end of file diff --git a/src/example_grates/muthashmaxglobal.rs b/src/example_grates/muthashmaxglobal.rs new file mode 100644 index 00000000..266916cf --- /dev/null +++ b/src/example_grates/muthashmaxglobal.rs @@ -0,0 +1,800 @@ + +use crate::safeposix::cage; +use crate::safeposix::syscalls::fs_calls::*; + +use super::threei; + +use lazy_static::lazy_static; + +use std::sync::Mutex; + +use std::collections::HashMap; + +// This fdtables library tracks the maxfd so it can more quickly get an unused +// file descriptor. + + +// Get constants about the fd table sizes, etc. +pub use super::commonconstants::*; + +// algorithm name. Need not be listed in the docs. +#[doc(hidden)] +pub const ALGONAME: &str = "MutHashMaxGlobal"; + +// These are the values we look up with at the end... +// #[doc = include_str!("../docs/fdtableentry.md")] +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct FDTableEntry { + pub realfd: u64, // underlying fd (may be a virtual fd below us or + // a kernel fd) + pub should_cloexec: bool, // should I close this when exec is called? + pub optionalinfo: u64, // user specified / controlled data +} + +#[derive(Clone, Debug)] +struct FDTable { + highestneverusedfd: u64, // Never resets (even after close). Used to + // let us quickly get an unused fd + thisfdtable: HashMap, // the virtfd -> entry map +} + +// It's fairly easy to check the fd count on a per-process basis (I just check +// when I would +// add a new fd). +// +// BUG: I will ignore the total limit for now. I would ideally do this on +// every creation, close, fork, etc. but it's a PITA to track this. + +// We will raise a panic anywhere we receive an unknown cageid. This frankly +// should not be possible and indicates some sort of internal error in our +// code. However, it is expected we could receive an invalid file descriptor +// when a cage makes a call. + +// In order to store this information, I'm going to use a HashMap which +// has keys of (cageid:u64) and values that are a table with a HashMap and +// a counter of the highestneverusedfd. +// HashMap has keys of (virtualfd:64) and values of (realfd:u64, +// should_cloexec:bool, optionalinfo:u64). +// +// I thought also about having different tables for the entries +// since they aren't always used together, but this seemed needlessly complex +// (at least at first). +// + +// This lets me initialize the code as a global. +lazy_static! { + + #[derive(Debug)] + static ref GLOBALFDTABLE: Mutex> = { + let mut m = HashMap::new(); + // Insert a cage so that I have something to fork / test later, if need + // be. Otherwise, I'm not sure how I get this started. I think this + // should be invalid from a 3i standpoint, etc. Could this mask an + // error in the future? + // + // + let newmap = HashMap::new(); + let emptytab = FDTable{ + highestneverusedfd:0, + thisfdtable:newmap, + }; + + m.insert(threei::TESTING_CAGEID,emptytab); + Mutex::new(m) + }; +} + +lazy_static! { + // This is needed for close and similar functionality. I need track the + // number of times a realfd is open + #[derive(Debug)] + static ref GLOBALREALFDCOUNT: Mutex> = { + Mutex::new(HashMap::new()) + }; + +} + +// Internal helper to hold the close handlers... +struct CloseHandlers { + intermediate_handler: fn(u64), + final_handler: fn(u64), + unreal_handler: fn(u64), +} + +// Seems sort of like a constant... I'm not sure if this is bad form or not... +#[allow(non_snake_case)] +pub fn NULL_FUNC(_:u64) { } + +lazy_static! { + // This holds the user registered handlers they want to have called when + // a close occurs. I did this rather than return messy data structures + // from the close, exec, and exit handlers because it seemed cleaner... + #[derive(Debug)] + static ref CLOSEHANDLERTABLE: Mutex = { + let c = CloseHandlers { + intermediate_handler:NULL_FUNC, + final_handler:NULL_FUNC, + unreal_handler:NULL_FUNC, + }; + Mutex::new(c) + }; +} + +// #[doc = include_str!("../docs/init_empty_cage.md")] +pub fn init_empty_cage(cageid: u64) { + let mut fdtable = GLOBALFDTABLE.lock().unwrap(); + + if fdtable.contains_key(&cageid) { + panic!("Known cageid in fdtable access"); + } + + let newmap = HashMap::new(); + let emptytab = FDTable{ + highestneverusedfd:0, + thisfdtable:newmap, + }; + + fdtable.insert(cageid,emptytab); + +} + +// #[doc = include_str!("../docs/translate_virtual_fd.md")] +pub fn translate_virtual_fd(cageid: u64, virtualfd: u64) -> Result { + // Get the lock on the fdtable... I'm not handling "poisoned locks" now + // where a thread holding the lock died... + let fdtable = GLOBALFDTABLE.lock().unwrap(); + + // They should not be able to pass a new cage I don't know. I should + // always have a table for each cage because each new cage is added at fork + // time + if !fdtable.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + return match fdtable.get(&cageid).unwrap().thisfdtable.get(&virtualfd) { + Some(tableentry) => Ok(tableentry.realfd), + None => Err(threei::Errno::EBADFD as u64), + }; +} + +// This is fairly slow if I just iterate sequentially through numbers. +// However there are not that many to choose from. I could pop from a list +// or a set as well... Likely the best solution is to keep a count of the +// largest fd handed out and to just use this until you wrap. This will be +// super fast for a normal cage and will be correct in the weird case. +// Right now, I'll just implement the slow path and will speed this up +// later, if needed. +// #[doc = include_str!("../docs/get_unused_virtual_fd.md")] +pub fn get_unused_virtual_fd( + cageid: u64, + realfd: u64, + should_cloexec: bool, + optionalinfo: u64, +) -> Result { + let mut fdtable = GLOBALFDTABLE.lock().unwrap(); + + if !fdtable.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + // Set up the entry so it has the right info... + // Note, a HashMap stores its data on the heap! No need to box it... + // https://doc.rust-lang.org/book/ch08-03-hash-maps.html#creating-a-new-hash-map + let myentry = FDTableEntry { + realfd, + should_cloexec, + optionalinfo, + }; + + let myfdentry = fdtable.get_mut(&cageid).unwrap(); + + if myfdentry.highestneverusedfd < FD_PER_PROCESS_MAX { + _increment_realfd(realfd); + // We have an entry we've never touched! + myfdentry.thisfdtable.insert(myfdentry.highestneverusedfd, myentry); + myfdentry.highestneverusedfd += 1; + return Ok(myfdentry.highestneverusedfd-1); + } + + let myfdentry = fdtable.get_mut(&cageid).unwrap(); + let myfdmap = &mut myfdentry.thisfdtable; + + // Check the fds in order. + for fdcandidate in 0..FD_PER_PROCESS_MAX { + // Get the entry if it's Vacant and assign it to e (so I can fill + // it in). + if let std::collections::hash_map::Entry::Vacant(e) = myfdmap.entry(fdcandidate) { + e.insert(myentry); + _increment_realfd(realfd); + return Ok(fdcandidate); + } + } + + // I must have checked all fds and failed to find one open. Fail! + Err(threei::Errno::EMFILE as u64) +} + +// This is used for things like dup2, which need a specific fd... +// If the requested_virtualfd is used, I close it... +// #[doc = include_str!("../docs/get_specific_virtual_fd.md")] +pub fn get_specific_virtual_fd( + cageid: u64, + requested_virtualfd: u64, + realfd: u64, + should_cloexec: bool, + optionalinfo: u64, +) -> Result<(), threei::RetVal> { + let mut fdtable = GLOBALFDTABLE.lock().unwrap(); + + if !fdtable.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + // If you ask for a FD number that is too large, I'm going to reject it. + // Note that, I need to use the FD_PER_PROCESS_MAX setting because this + // is also how I'm tracking how many values you have open. If this + // changed, then these constants could be decoupled... + if requested_virtualfd > FD_PER_PROCESS_MAX { + return Err(threei::Errno::EBADF as u64); + } + + // Set up the entry so it has the right info... + // Note, a HashMap stores its data on the heap! No need to box it... + // https://doc.rust-lang.org/book/ch08-03-hash-maps.html#creating-a-new-hash-map + let myentry = FDTableEntry { + realfd, + should_cloexec, + optionalinfo, + }; + + // I moved this up so that if I decrement the same realfd, it calls + // the intermediate handler instead of the final one. + _increment_realfd(realfd); + if let Some(entry) = fdtable.get(&cageid).unwrap().thisfdtable.get(&requested_virtualfd) { + if entry.realfd != NO_REAL_FD { + _decrement_realfd(entry.realfd); + } + else { + // Let their code know this has been closed... + let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); + (closehandlers.unreal_handler)(entry.optionalinfo); + } + } + + // always add the new entry + fdtable.get_mut(&cageid).unwrap().thisfdtable.insert(requested_virtualfd,myentry); + Ok(()) +} + +// We're just setting a flag here, so this should be pretty straightforward. +// #[doc = include_str!("../docs/set_cloexec.md")] +pub fn set_cloexec(cageid: u64, virtualfd: u64, is_cloexec: bool) -> Result<(), threei::RetVal> { + let mut fdtable = GLOBALFDTABLE.lock().unwrap(); + + if !fdtable.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + // Set the is_cloexec flag or return EBADFD, if that's missing... + return match fdtable.get_mut(&cageid).unwrap().thisfdtable.get_mut(&virtualfd) { + Some(tableentry) => { + tableentry.should_cloexec = is_cloexec; + Ok(()) + } + None => Err(threei::Errno::EBADFD as u64), + }; +} + +// Super easy, just return the optionalinfo field... +// #[doc = include_str!("../docs/get_optionalinfo.md")] +pub fn get_optionalinfo(cageid: u64, virtualfd: u64) -> Result { + let fdtable = GLOBALFDTABLE.lock().unwrap(); + if !fdtable.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + return match fdtable.get(&cageid).unwrap().thisfdtable.get(&virtualfd) { + Some(tableentry) => Ok(tableentry.optionalinfo), + None => Err(threei::Errno::EBADFD as u64), + }; +} + +// We're setting an opaque value here. This should be pretty straightforward. +// #[doc = include_str!("../docs/set_optionalinfo.md")] +pub fn set_optionalinfo( + cageid: u64, + virtualfd: u64, + optionalinfo: u64, +) -> Result<(), threei::RetVal> { + let mut fdtable = GLOBALFDTABLE.lock().unwrap(); + + if !fdtable.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + // Set optionalinfo or return EBADFD, if that's missing... + return match fdtable.get_mut(&cageid).unwrap().thisfdtable.get_mut(&virtualfd) { + Some(tableentry) => { + tableentry.optionalinfo = optionalinfo; + Ok(()) + } + None => Err(threei::Errno::EBADFD as u64), + }; +} + +// Helper function used for fork... Copies an fdtable for another process +// #[doc = include_str!("../docs/copy_fdtable_for_cage.md")] +pub fn copy_fdtable_for_cage(srccageid: u64, newcageid: u64) -> Result<(), threei::Errno> { + let mut fdtable = GLOBALFDTABLE.lock().unwrap(); + + if !fdtable.contains_key(&srccageid) { + panic!("Unknown srccageid in fdtable access"); + } + if fdtable.contains_key(&newcageid) { + panic!("Known newcageid in fdtable access"); + } + + // Insert a copy and ensure it didn't exist... + let hmcopy = fdtable.get(&srccageid).unwrap().clone(); + + // increment the reference to items in the fdtable appropriately... + for v in fdtable.get(&srccageid).unwrap().thisfdtable.values() { + if v.realfd != NO_REAL_FD { + _increment_realfd(v.realfd); + } + } + + // insert the new table... + assert!(fdtable.insert(newcageid, hmcopy).is_none()); + Ok(()) + // I'm not going to bother to check the number of fds used overall yet... + // Err(threei::Errno::EMFILE as u64), +} + +// This is mostly used in handling exit, etc. Returns the HashMap +// for the cage. +// #[doc = include_str!("../docs/remove_cage_from_fdtable.md")] +pub fn remove_cage_from_fdtable(cageid: u64) { + let mut fdtable = GLOBALFDTABLE.lock().unwrap(); + + if !fdtable.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + // decrement the reference to items in the fdtable appropriately... + for v in fdtable.get(&cageid).unwrap().thisfdtable.values() { + if v.realfd != NO_REAL_FD { + _decrement_realfd(v.realfd); + } + else { + // Let their code know this has been closed... + let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); + (closehandlers.unreal_handler)(v.optionalinfo); + } + } + + + fdtable.remove(&cageid).unwrap(); +} + +// This removes all fds with the should_cloexec flag set. They are returned +// in a new hashmap... +// #[doc = include_str!("../docs/empty_fds_for_exec.md")] +pub fn empty_fds_for_exec(cageid: u64) { + let mut fdtable = GLOBALFDTABLE.lock().unwrap(); + + if !fdtable.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + // Create this hashmap through an lambda that checks should_cloexec... + // See: https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.extract_if + +/* fdtable + .get_mut(&cageid) + .unwrap() + .extract_if(|_k, v| v.should_cloexec) + .collect()*/ + + // I'm writing the below code to avoid using the extract_if experimental + // nightly function... + let thiscagefdtable = &mut fdtable.get_mut(&cageid).unwrap().thisfdtable; + + let mut without_cloexec_hm:HashMap = HashMap::new(); + for (k,v) in thiscagefdtable.drain() { + if v.should_cloexec { + if v.realfd == NO_REAL_FD { + // Let their code know this has been closed... + let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); + (closehandlers.unreal_handler)(v.optionalinfo); + } + else { + // Let the helper tell the user and decrement the count + _decrement_realfd(v.realfd); + } + } + else{ + without_cloexec_hm.insert(k,v); + } + + } + + let newhighest = fdtable.get(&cageid).unwrap().highestneverusedfd; + let newfdtable = FDTable { + highestneverusedfd:newhighest, + thisfdtable:without_cloexec_hm, + }; + + // Put the ones without_cloexec back in the hashmap... + fdtable.insert(cageid,newfdtable); + +} + +// returns a copy of the fdtable for a cage. Useful helper function for a +// caller that needs to examine the table. Likely could be more efficient by +// letting the caller borrow this... +// #[doc = include_str!("../docs/return_fdtable_copy.md")] +pub fn return_fdtable_copy(cageid: u64) -> HashMap { + let fdtable = GLOBALFDTABLE.lock().unwrap(); + + if !fdtable.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + fdtable.get(&cageid).unwrap().thisfdtable.clone() +} + + +/******************* CLOSE SPECIFIC FUNCTIONALITY *******************/ + +// Helper for close. Returns a tuple of realfd, number of references +// remaining. +// #[doc = include_str!("../docs/close_virtualfd.md")] +pub fn close_virtualfd(cageid:u64, virtfd:u64) -> Result<(),threei::RetVal> { + let mut fdtable = GLOBALFDTABLE.lock().unwrap(); + + if !fdtable.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + let thiscagesfdtable = &mut fdtable.get_mut(&cageid).unwrap().thisfdtable; + + match thiscagesfdtable.remove(&virtfd) { + Some(entry) => + if entry.realfd == NO_REAL_FD { + // Let their code know this has been closed... + let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); + (closehandlers.unreal_handler)(entry.optionalinfo); + Ok(()) + } + else { + _decrement_realfd(entry.realfd); + Ok(()) + } + None => Err(threei::Errno::EBADFD as u64), + } +} + + +// Register a series of helpers to be called for close. Can be called +// multiple times to override the older helpers. +// #[doc = include_str!("../docs/register_close_handlers.md")] +pub fn register_close_handlers(intermediate_handler: fn(u64), final_handler: fn(u64), unreal_handler: fn(u64)) { + // Unlock the table and set the handlers... + let mut closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); + closehandlers.intermediate_handler = intermediate_handler; + closehandlers.final_handler = final_handler; + closehandlers.unreal_handler = unreal_handler; +} + +// Helpers to track the count of times each realfd is used +#[doc(hidden)] +fn _decrement_realfd(realfd:u64) -> u64 { + // Do nothing if it's not a realfd... + if realfd == NO_REAL_FD { + panic!("Called _decrement_realfd with NO_REAL_FD"); + } + + // Get this table's lock... + let mut realfdcount = GLOBALREALFDCOUNT.lock().unwrap(); + + let newcount:u64 = realfdcount.get(&realfd).unwrap() - 1; + let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); + if newcount > 0 { + (closehandlers.intermediate_handler)(realfd); + realfdcount.insert(realfd,newcount); + } + else { + (closehandlers.final_handler)(realfd); + } + newcount +} + +// Helpers to track the count of times each realfd is used +#[doc(hidden)] +fn _increment_realfd(realfd:u64) -> u64 { + if realfd == NO_REAL_FD { + return 0 + } + + // Get this table's lock... + let mut realfdcount = GLOBALREALFDCOUNT.lock().unwrap(); + + // Get a mutable reference to the entry so we can update it. + return match realfdcount.get_mut(&realfd) { + Some(count) => { + *count += 1; + *count + } + None => { + realfdcount.insert(realfd, 1); + 1 + } + } +} + +/*************** Code for handling select() ****************/ + +use libc::fd_set; +use std::collections::HashSet; +use std::cmp; +use std::mem; + +// Helper to get an empty fd_set. Helper function to isolate unsafe code, +// etc. +pub fn _init_fd_set() -> fd_set { + let raw_fd_set:fd_set; + unsafe { + let mut this_fd_set = mem::MaybeUninit::::uninit(); + libc::FD_ZERO(this_fd_set.as_mut_ptr()); + raw_fd_set = this_fd_set.assume_init() + } + raw_fd_set +} + +// Helper to get a null pointer. +pub fn _get_null_fd_set() -> fd_set { + //unsafe{ptr::null_mut()} + // BUG!!! Need to fix this later. + _init_fd_set() +} + +pub fn _fd_set(fd:u64, thisfdset:&mut fd_set) { + unsafe{libc::FD_SET(fd as i32,thisfdset)} +} + +pub fn _fd_isset(fd:u64, thisfdset:&fd_set) -> bool { + unsafe{libc::FD_ISSET(fd as i32,thisfdset)} +} + +// Computes the bitmodifications and returns a (maxnfds, unrealset) tuple... +fn _do_bitmods(myfdmap:HashMap, nfds:u64, infdset: fd_set, thisfdset: &mut fd_set, mappingtable: &mut HashMap) -> Result<(u64,HashSet<(u64,u64)>),threei::RetVal> { + let mut unrealhashset:HashSet<(u64,u64)> = HashSet::new(); + // Iterate through the infdset and set those values as is appropriate + let mut highestpos = 0; + + // Clippy is somehow missing how pos is using bit. + #[allow(clippy::needless_range_loop)] + for bit in 0..nfds as usize { + let pos = bit as u64; + if _fd_isset(pos,&infdset) { + if let Some(entry) = myfdmap.get(&pos) { + if entry.realfd == NO_REAL_FD { + unrealhashset.insert((pos,entry.optionalinfo)); + } + else { + mappingtable.insert(entry.realfd, pos); + _fd_set(entry.realfd,thisfdset); + // I add one because select expects nfds to be the max+1 + highestpos = cmp::max(highestpos, entry.realfd+1); + } + } + else { + return Err(threei::Errno::EINVAL as u64); + } + } + } + Ok((highestpos,unrealhashset)) +} + +// helper to call before calling select beneath you. Translates your virtfds +// to realfds. +// See: https://man7.org/linux/man-pages/man2/select.2.html for details / +// corner cases about the arguments. + +// I hate doing these, but don't know how to make this interface better... +#[allow(clippy::type_complexity)] +#[allow(clippy::too_many_arguments)] +// #[doc = include_str!("../docs/get_real_bitmasks_for_select.md")] +// pub fn get_real_bitmasks_for_select(cageid:u64, nfds:u64, readbits:Option, writebits:Option, exceptbits:Option) -> Result<(u64, fd_set, fd_set, fd_set, [HashSet<(u64,u64)>;3], HashMap),threei::RetVal> { + +// if nfds >= FD_PER_PROCESS_MAX { +// return Err(threei::Errno::EINVAL as u64); +// } + +// let globfdtable = GLOBALFDTABLE.lock().unwrap(); + +// if !globfdtable.contains_key(&cageid) { +// panic!("Unknown cageid in fdtable access"); +// } + +// let mut unrealarray:[HashSet<(u64,u64)>;3] = [HashSet::new(),HashSet::new(),HashSet::new()]; +// let mut mappingtable:HashMap = HashMap::new(); +// let mut newnfds = 0; + +// // putting results in a vec was the cleanest way I found to do this.. +// let mut resultvec = Vec::new(); + +// for (unrealoffset, inset) in [readbits,writebits, exceptbits].into_iter().enumerate() { +// match inset { +// Some(virtualbits) => { +// let mut retset = _init_fd_set(); +// let (thisnfds,myunrealhashset) = _do_bitmods(globfdtable.get(&cageid).unwrap().clone().thisfdtable,nfds,virtualbits, &mut retset,&mut mappingtable)?; +// resultvec.push(retset); +// newnfds = cmp::max(thisnfds, newnfds); +// unrealarray[unrealoffset] = myunrealhashset; +// } +// None => { +// // This item is null. No unreal items +// // BUG: Need to actually return null! +// resultvec.push(_get_null_fd_set()); +// unrealarray[unrealoffset] = HashSet::new(); +// } +// } +// } + +// Ok((newnfds, resultvec[0], resultvec[1], resultvec[2], unrealarray, mappingtable)) + +// } + + +// helper to call after calling select beneath you. returns the fd_sets you +// need for your return from a select call and the number of unique flags +// set... + +// I hate doing these, but don't know how to make this interface better... +#[allow(clippy::type_complexity)] +#[allow(clippy::too_many_arguments)] +// #[doc = include_str!("../docs/get_virtual_bitmasks_from_select_result.md")] +pub fn get_virtual_bitmasks_from_select_result(nfds:u64, readbits:fd_set, writebits:fd_set, exceptbits:fd_set,unrealreadset:HashSet, unrealwriteset:HashSet, unrealexceptset:HashSet, mappingtable:HashMap) -> Result<(u64, fd_set, fd_set, fd_set),threei::RetVal> { + + // Note, I don't need the cage_id here because I have the mappingtable... + + if nfds >= FD_PER_PROCESS_MAX { + panic!("This shouldn't be possible because we shouldn't have returned this previously") + } + + let mut flagsset = 0; + let mut retvec = Vec::new(); + + for (inset,unrealset) in [(readbits,unrealreadset), (writebits,unrealwriteset), (exceptbits,unrealexceptset)] { + let mut retbits = _init_fd_set(); + for bit in 0..nfds as usize { + let pos = bit as u64; + if _fd_isset(pos,&inset)&& !_fd_isset(*mappingtable.get(&pos).unwrap(),&retbits) { + flagsset+=1; + _fd_set(*mappingtable.get(&pos).unwrap(),&mut retbits); + } + } + for virtfd in unrealset { + if !_fd_isset(virtfd,&retbits) { + flagsset+=1; + _fd_set(virtfd,&mut retbits); + } + } + retvec.push(retbits); + } + + Ok((flagsset,retvec[0],retvec[1],retvec[2])) + +} + + + +/********************** POLL SPECIFIC FUNCTIONS **********************/ + +// helper to call before calling poll beneath you. replaces the fds in +// the poll struct with virtual versions and returns the items you need +// to check yourself... +#[allow(clippy::type_complexity)] +// #[doc = include_str!("../docs/convert_virtualfds_to_real.md")] +pub fn convert_virtualfds_to_real(cageid:u64, virtualfds:Vec) -> (Vec, Vec<(u64,u64)>, Vec, HashMap) { + + let globfdtable = GLOBALFDTABLE.lock().unwrap(); + + if !globfdtable.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + let mut unrealvec = Vec::new(); + let mut realvec = Vec::new(); + let mut invalidvec = Vec::new(); + let thefdhm = globfdtable.get(&cageid).unwrap(); + let mut mappingtable:HashMap = HashMap::new(); + + // BUG?: I'm ignoring the fact that virtualfds can show up multiple times. + // I'm not sure this actually matters, but I didn't think hard about it. + for virtfd in virtualfds { + match thefdhm.thisfdtable.get(&virtfd) { + Some(entry) => { + // always append the value here. NO_REAL_FD will be added + // in the appropriate places to tell them to handle those calls + // themself. + realvec.push(entry.realfd); + if entry.realfd == NO_REAL_FD { + unrealvec.push((virtfd,entry.optionalinfo)); + } + else{ + mappingtable.insert(entry.realfd, virtfd); + } + } + None => { + // Add this because they need to handle it if POLLNVAL is set. + // An exception should not be raised!!! + realvec.push(INVALID_FD); + invalidvec.push(virtfd); + } + } + } + + (realvec, unrealvec, invalidvec, mappingtable) +} + + + +// helper to call after calling poll. replaces the realfds the vector +// with virtual ones... +// #[doc = include_str!("../docs/convert_realfds_back_to_virtual.md")] +pub fn convert_realfds_back_to_virtual(realfds:Vec, mappingtable:HashMap) -> Vec { + + // I don't care what cage was used, and don't need to lock anything... + // I have the mappingtable! + + let mut virtvec = Vec::new(); + + for realfd in realfds { + virtvec.push(*mappingtable.get(&realfd).unwrap()); + } + + virtvec +} + + + + +/********************** TESTING HELPER FUNCTION **********************/ + +// Helper to initialize / empty out state so we can test with a clean system... +// only used when testing... +// +// I'm cleaning up "poisoned" mutexes here so that I can handle tests that +// panic +#[doc(hidden)] +pub fn refresh() { + let mut fdtable = GLOBALFDTABLE.lock().unwrap_or_else(|e| { + GLOBALFDTABLE.clear_poison(); + e.into_inner() + }); + + fdtable.clear(); + + let newmap = HashMap::new(); + let emptytab = FDTable{ + highestneverusedfd:0, + thisfdtable:newmap, + }; + + fdtable.insert(threei::TESTING_CAGEID, emptytab); + let mut closehandlers = CLOSEHANDLERTABLE.lock().unwrap_or_else(|e| { + CLOSEHANDLERTABLE.clear_poison(); + e.into_inner() + }); + + closehandlers.intermediate_handler = NULL_FUNC; + closehandlers.final_handler = NULL_FUNC; + closehandlers.unreal_handler = NULL_FUNC; + + let mut _realfdcount = GLOBALREALFDCOUNT.lock().unwrap_or_else(|e| { + GLOBALREALFDCOUNT.clear_poison(); + e.into_inner() + }); +} diff --git a/src/example_grates/route_grate.rs b/src/example_grates/route_grate.rs new file mode 100644 index 00000000..150ddfac --- /dev/null +++ b/src/example_grates/route_grate.rs @@ -0,0 +1,147 @@ +// Example grates +// +// +// route requests to different grates +// ex: /tmp to imfs + others to lower level fs, etc. +// +// We need a better API to support this example. +// +// - Encasement libary supports a way to save and restore sets of system +// calls. Grate A is instantiated with the pure API. A dummy child +// is forked from it and that API is saved. Grate B is instantiated +// with the pure API. A dummy child if forked from grate B and its +// API is also saved. The filter grate is forked with the pure API +// and the APIs from the two dummy children are provided. Note that +// the filter grate must return its cageID / PID from fork for each +// grate if it wants to make calls of its own to those grates. +// +// + + +// ******************************************************************* +// *** This file contains the sketch of a filtering handler which *** +// *** divides up the calls that are made between different grates *** +// *** THIS CODE WILL NOT RUN! IT IS MEANT TO ILLUSTRATE *** +// *** MY THINKING ABOUT HOW SOMETHING LIKE THIS WOULD *** +// *** WORK. WE WILL MAKE A WORKING GRATE ONCE WE *** +// *** UNDERSTAND THE INTERFACES AND CHALLENGES BETTER. *** +// ******************************************************************* + + +// I'm realizing this likely needs its own library. The reason is that +// there is one tedious part: separating out the grate to route a request +// to by system call. So, really, you just want to decide once for every +// creation of a fd, where to assign it, and thereafter all the calls for +// it should end up in the right place... Manually annotating write, send, +// recv, fcntl, etc. is a pain in the butt. Especially calls like mmap, +// select, etc. which put the fd in a weird spot or do other weird things... +use multigratelib; +use lind_3i; +use lind_3i::constants::*; +use lind_encasement; + + +/*********************** some multigratelib code *************************/ + + +pub gratemap: HashMap; + + +// This adds entries to the gratemap so that they can be called later +// by the other system calls. +pub fn initialize_grates(grates: Vec,interface:...) { + + + for gratename in grates { + + // create the dummy child, snatch its API, and stick it in the + // gratemap. This lets us use it later. + gratemap[gratename] = lind_encasement.dummy_initialize_grate_and_return_syscalls(gratename,interface); + } + +} + +// For each individual call, I check to see where the fd is from and then I +// route to this grate. These can be private functions... +fn fcntl_handler(cageid: u64, targetcageid: u64, callid: u64, fd: u64, cmd: u64, arg3: u64, arg4: u64, arg5: u64, arg6: u64) -> u64 { + //int fcntl(int fd, int cmd, ...); + + // panic if doesn't exist... + let gratename = fdtable.get_entry(targetcageid,fd).extradata; + + return makesyscall(gratename,targetcageid,callid,fd,cmd,arg3,arg4,arg5,arg6); +} + +// Above is just an example. There would be one for each call with a fd... +// Or, at least there is one for every call with a fd in a specific position. +// Some calls like dup, etc. also need special handling. + + +//***************** Code the grate author needs to write ******************* + + +pub fn my_open_syscall(cageid: u64, targetcageid: u64, callid: u64, pathname: u64, flags: u64, mode: u64, _: u64, _: u64, _: u64) -> u64 { + // int open(const char *pathname, int flags, mode_t mode */ + // + + // the max filename length is supposed to be 4096 + let mut localfn_buffer = vec!([0u8,4096]); + + lind_3i.icstrncpy(MY_CAGE_ID, localfn_buffer, targetcageid, pathname,4096); + + // I do my filtering here... + if abs_path_santize(pathname).startswith("/tmp/") { + let myfd = multigratelib::makesyscall("grate_a.rs",targetcageid,OPEN_SYSIDpathname,flags,mode); + // All future calls with this fd, go to this grate... + multigratelib::filterfd("grate_a.rs",targetcageid,myfd); + return myfd; + } + else { + let myfd = multigratelib::makesyscall("grate_b.rs",targetcageid,OPEN_SYSID,pathname,flags,mode); + // All future calls with this fd, go to this grate... + multigratelib::filterfd("grate_b.rs",targetcageid,myfd); + return myfd; + } + +} + + + + +// This sets up interposition so that calls go the right place... +fn setup_interposition() { + // This sets up the basic handlers and says unknown fds should return + // EBADFD. This is useful when all fds should be known to us. + multigratelib::add_handler(syscallid::OPEN_SYSID, my_open_syscall); + + // You can also always route a call to a specific grate without needing + // to write a function + multigratelib::always_handle_with_grate(syscallid::PIPE_SYSID, "grate_a.rs"); + + // You can also just block a certain way to crate FDs... + multigratelib::block_call(syscallid::SOCKET_SYSID); + + lind_encasement.replace_syscalls(syscall_replacements); + +} + + +fn main() { + + // Initialize these two grates and don't change the interface for them + // by setting None (just use my interface) + multigratelib::initialize_grates(["grate_A.rs","grate_B.rs",None); + // The above is equivalent to calling the function two times (each with + // one of the grates). If we want to have different interfaces for them, + // we can do it that way. + + // If I want to have things handled with my API, I could also pass + // the empty string "" as an option. + // For example: multigratelib::initialize_grates(["",None); + + + + // Now, onto the real children! + lind_encasement::initialize_children_and_consume_thread(); + // Never reaches here! +} diff --git a/src/example_grates/threei.rs b/src/example_grates/threei.rs new file mode 100644 index 00000000..021c0635 --- /dev/null +++ b/src/example_grates/threei.rs @@ -0,0 +1,196 @@ +// Defining a few basic things about the 3i interface here... +// This needs enough information that we can make calls effectively and +// so needs errno, etc. + +// Let's not have clippy warn for EAGAIN, etc. +#![allow(clippy::upper_case_acronyms)] +// Let's not have clippy warn for EAGAIN, etc. +#![allow(clippy::upper_case_acronyms)] +// Don't warn if all listed things (like errnos) are not used in code... +#![allow(dead_code)] + +// Define some cageid constants that may be useful. These are not valid for +// normal use as cageids + +pub const INVALID_CAGEID: u64 = 0xfffffffffffffffe; + +// Used for internal testing. Not valid for a normal cageid... +pub const TESTING_CAGEID: u64 = 0xffffffffffffffe0; +pub const TESTING_CAGEID0: u64 = 0xffffffffffffffe0; +pub const TESTING_CAGEID1: u64 = 0xffffffffffffffe1; +pub const TESTING_CAGEID2: u64 = 0xffffffffffffffe2; +pub const TESTING_CAGEID3: u64 = 0xffffffffffffffe3; +pub const TESTING_CAGEID4: u64 = 0xffffffffffffffe4; +pub const TESTING_CAGEID5: u64 = 0xffffffffffffffe5; +pub const TESTING_CAGEID6: u64 = 0xffffffffffffffe6; +pub const TESTING_CAGEID7: u64 = 0xffffffffffffffe7; +pub const TESTING_CAGEID8: u64 = 0xffffffffffffffe8; +pub const TESTING_CAGEID9: u64 = 0xffffffffffffffe9; +pub const TESTING_CAGEID10: u64 = 0xffffffffffffffea; +pub const TESTING_CAGEID11: u64 = 0xffffffffffffffeb; +pub const TESTING_CAGEID12: u64 = 0xffffffffffffffec; +pub const TESTING_CAGEID13: u64 = 0xffffffffffffffed; +pub const TESTING_CAGEID14: u64 = 0xffffffffffffffee; +pub const TESTING_CAGEID15: u64 = 0xffffffffffffffef; + +// Return value for system calls... +pub type RetVal = u64; + +macro_rules! reversible_enum { + ($(#[$settings: meta])* $visibility: vis enum $enumname:ident { + $($valuename: ident = $value: expr,)* + }) => { + $(#[$settings])* + $visibility enum $enumname { + $($valuename = $value,)* + } + + impl $enumname { + $visibility fn from_discriminant(v: u64) -> Result { + match v { + $($value => Ok($enumname::$valuename),)* + _ => Err(()), + } + } + } + } +} + +// BUG: ? I don't understand this setup... +reversible_enum! { + #[derive(Debug, PartialEq, Eq)] + #[repr(u64)] + pub enum Errno { + EPERM = 1, // Operation not permitted + ENOENT = 2, // No such file or directory + ESRCH = 3, // No such process + EINTR = 4, // Interrupted system call + EIO = 5, // I/O error + ENXIO = 6, // No such device or address + EBIG = 7, // Argument list too long + ENOEXEC = 8, // Exec format error + EBADF = 9, // Bad file number + ECHILD = 10, // No child processes + EAGAIN = 11, // Try again + ENOMEM = 12, // Out of memory + EACCES = 13, // Permission denied + EFAULT = 14, // Bad address + ENOTBLK = 15, // Block device required + EBUSY = 16, // Device or resource busy + EEXIST = 17, // File exists + EXDEV = 18, // Cross-device link + ENODEV = 19, // No such device + ENOTDIR = 20, // Not a directory + EISDIR = 21, // Is a directory + EINVAL = 22, // Invalid argument + ENFILE = 23, // File table overflow + EMFILE = 24, // Too many open files + ENOTTY = 25, // Not a typewriter + ETXTBSY = 26, // Text file busy + EFBIG = 27, // File too large + ENOSPC = 28, // No space left on device + ESPIPE = 29, // Illegal seek + EROFS = 30, // Read-only file system + EMLINK = 31, // Too many links + EPIPE = 32, // Broken pipe + EDOM = 33, // Math argument out of domain of func + ERANGE = 34, // Math result not representable + EDEADLK = 35, // Resource deadlock would occur + ENAMETOOLONG = 36, // File name too long + ENOLCK = 37, // No record locks available + ENOSYS = 38, // Function not implemented + ENOTEMPTY = 39, // Directory not empty + ELOOP = 40, // Too many symbolic links encountered + // EWOULDBLOCK = 11, // Operation would block, returns EAGAIN + ENOMSG = 42, // No message of desired type + EIDRM = 43, // Identifier removed + ECHRNG = 44, // Channel number out of range + EL2NSYNC = 45, // Level not synchronized + EL3HLT = 46, // Level halted + EL3RST = 47, // Level reset + ELNRNG = 48, // Link number out of range + EUNATCH = 49, // Protocol driver not attached + ENOCSI = 50, // No CSI structure available + EL2HLT = 51, // Level halted + EBADE = 52, // Invalid exchange + EBADR = 53, // Invalid request descriptor + EXFULL = 54, // Exchange full + ENOANO = 55, // No anode + EBADRQC = 56, // Invalid request code + EBADSLT = 57, // Invalid slot + EBFONT = 59, // Bad font file format + ENOSTR = 60, // Device not a stream + ENODATA = 61, // No data available + ETIME = 62, // Timer expired + ENOSR = 63, // Out of streams resources + ENONET = 64, // Machine is not on the network + ENOPKG = 65, // Package not installed + EREMOTE = 66, // Object is remote + ENOLINK = 67, // Link has been severed + EADV = 68, // Advertise error + ESRMNT = 69, // Srmount error + ECOMM = 70, // Communication error on send + EPROTO = 71, // Protocol error + EMULTIHOP = 72, // Multihop attempted + EDOTDOT = 73, // RFS specific error + EBADMSG = 74, // Not a data message + EOVERFLOW = 75, // Value too large for defined data type + ENOTUNIQ = 76, // Name not unique on network + EBADFD = 77, // File descriptor in bad state + EREMCHG = 78, // Remote address changed + ELIBACC = 79, // Can not access a needed shared library + ELIBBAD = 80, // Accessing a corrupted shared library + ELIBSCN = 81, // .lib section in a.out corrupted + ELIBMAX = 82, // Attempting to link in too many shared libraries + ELIBEXEC = 83, // Cannot exec a shared library directly + EILSEQ = 84, // Illegal byte sequence + ERESTART = 85, // Interrupted system call should be restarted + ESTRPIPE = 86, // Streams pipe error + EUSERS = 87, // Too many users + ENOTSOCK = 88, // Socket operation on non-socket + EDESTADDRREQ = 89, // Destination address required + EMSGSIZE = 90, // Message too long + EPROTOTYPE = 91, // Protocol wrong type for socket + ENOPROTOOPT = 92, // Protocol not available + EPROTONOSUPPORT = 93, // Protocol not supported + ESOCKTNOSUPPORT = 94, // Socket type not supported + EOPNOTSUPP = 95, // Operation not supported on transport endpoint + EPFNOSUPPORT = 96, // Protocol family not supported + EAFNOSUPPORT = 97, // Address family not supported by protocol + EADDRINUSE = 98, // Address already in use + EADDRNOTAVAIL = 99, // Cannot assign requested address + ENETDOWN = 100, // Network is down + ENETUNREACH = 101, // Network is unreachable + ENETRESET = 102, // Network dropped connection because of reset + ECONNABORTED = 103, // Software caused connection abort + ECONNRESET = 104, // Connection reset by peer + ENOBUFS = 105, // No buffer space available + EISCONN = 106, // Transport endpoint is already connected + ENOTCONN = 107, // Transport endpoint is not connected + ESHUTDOWN = 108, // Cannot send after transport endpoint shutdown + ETOOMANYREFS = 109, // Too many references cannot splice + ETIMEDOUT = 110, // Connection timed out + ECONNREFUSED = 111, // Connection refused + EHOSTDOWN = 112, // Host is down + EHOSTUNREACH = 113, // No route to host + EALREADY = 114, // Operation already in progress + EINPROGRESS = 115, // Operation now in progress + ESTALE = 116, // Stale NFS file handle + EUCLEAN = 117, // Structure needs cleaning + ENOTNAM = 118, // Not a XENIX named type file + ENAVAIL = 119, // No XENIX semaphores available + EISNAM = 120, // Is a named type file + EREMOTEIO = 121, // Remote I/O error + EDQUOT = 122, // Quota exceeded + ENOMEDIUM = 123, // No medium found + EMEDIUMTYPE = 124, // Wrong medium type + ECANCELED = 125, // Operation Canceled + ENOKEY = 126, // Required key not available + EKEYEXPIRED = 127, // Key has expired + EKEYREVOKED = 128, // Key has been revoked + EKEYREJECTED = 129, // Key was rejected by service for robust mutexes + EOWNERDEAD = 130, // Owner died + ENOTRECOVERABLE = 131, // State not recoverable + ELIND = 224, // Lind specific error + } +} diff --git a/src/example_grates/vanillaglobal.rs b/src/example_grates/vanillaglobal.rs new file mode 100644 index 00000000..baac065e --- /dev/null +++ b/src/example_grates/vanillaglobal.rs @@ -0,0 +1,754 @@ + +use crate::safeposix::cage; +use crate::safeposix::syscalls::fs_calls::*; + +use super::threei; + +use lazy_static::lazy_static; + +use std::sync::Mutex; + +use std::collections::HashMap; + +// This is a basic fdtables library. The purpose is to allow a cage to have +// a set of virtual fds which is translated into real fds. + + +// Get constants about the fd table sizes, etc. +pub use super::commonconstants::*; + +// algorithm name. Need not be listed in the docs. +#[doc(hidden)] +pub const ALGONAME: &str = "VanillaGlobal"; + +// These are the values we look up with at the end... +// #[doc = include_str!("../docs/fdtableentry.md")] +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct FDTableEntry { + pub realfd: u64, // underlying fd (may be a virtual fd below us or + // a kernel fd) + pub should_cloexec: bool, // should I close this when exec is called? + pub optionalinfo: u64, // user specified / controlled data +} + +// It's fairly easy to check the fd count on a per-process basis (I just check +// when I would +// add a new fd). +// +// BUG: I will ignore the total limit for now. I would ideally do this on +// every creation, close, fork, etc. but it's a PITA to track this. + +// We will raise a panic anywhere we receive an unknown cageid. This frankly +// should not be possible and indicates some sort of internal error in our +// code. However, it is expected we could receive an invalid file descriptor +// when a cage makes a call. + +// In order to store this information, I'm going to use a HashMap which +// has keys of (cageid:u64) and values that are another HashMap. The second +// HashMap has keys of (virtualfd:64) and values of (realfd:u64, +// should_cloexec:bool, optionalinfo:u64). +// +// To speed up lookups, I could have used arrays instead of HashMaps. In +// theory, that space is far too large, but likely each could be bounded to +// smaller values like 1024. For simplicity I avoided this for now. +// +// I thought also about having different tables for the tuple of values +// since they aren't always used together, but this seemed needlessly complex +// (at least at first). +// + +// This lets me initialize the code as a global. +// BUG / TODO: Use a DashMap instead of a Mutex for this? +lazy_static! { + + #[derive(Debug)] + static ref GLOBALFDTABLE: Mutex>> = { + let mut m = HashMap::new(); + // Insert a cage so that I have something to fork / test later, if need + // be. Otherwise, I'm not sure how I get this started. I think this + // should be invalid from a 3i standpoint, etc. Could this mask an + // error in the future? + m.insert(threei::TESTING_CAGEID,HashMap::new()); + Mutex::new(m) + }; +} + +lazy_static! { + // This is needed for close and similar functionality. I need track the + // number of times a realfd is open + #[derive(Debug)] + static ref GLOBALREALFDCOUNT: Mutex> = { + Mutex::new(HashMap::new()) + }; + +} + +// Internal helper to hold the close handlers... +struct CloseHandlers { + intermediate_handler: fn(u64), + final_handler: fn(u64), + unreal_handler: fn(u64), +} + +// Seems sort of like a constant... I'm not sure if this is bad form or not... +#[allow(non_snake_case)] +pub fn NULL_FUNC(_:u64) { } + +lazy_static! { + // This holds the user registered handlers they want to have called when + // a close occurs. I did this rather than return messy data structures + // from the close, exec, and exit handlers because it seemed cleaner... + #[derive(Debug)] + static ref CLOSEHANDLERTABLE: Mutex = { + let c = CloseHandlers { + intermediate_handler:NULL_FUNC, + final_handler:NULL_FUNC, + unreal_handler:NULL_FUNC, + }; + Mutex::new(c) + }; +} + +// #[doc = include_str!("../docs/init_empty_cage.md")] +pub fn init_empty_cage(cageid: u64) { + + let mut fdtable = GLOBALFDTABLE.lock().unwrap(); + + if fdtable.contains_key(&cageid) { + panic!("Known cageid in fdtable access"); + } + + fdtable.insert(cageid,HashMap::new()); +} + +// #[doc = include_str!("../docs/translate_virtual_fd.md")] +pub fn translate_virtual_fd(cageid: u64, virtualfd: u64) -> Result { + // Get the lock on the fdtable... I'm not handling "poisoned locks" now + // where a thread holding the lock died... + let fdtable = GLOBALFDTABLE.lock().unwrap(); + + // They should not be able to pass a new cage I don't know. I should + // always have a table for each cage because each new cage is added at fork + // time + if !fdtable.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + return match fdtable.get(&cageid).unwrap().get(&virtualfd) { + Some(tableentry) => Ok(tableentry.realfd), + None => Err(threei::Errno::EBADFD as u64), + }; +} + +// This is fairly slow if I just iterate sequentially through numbers. +// However there are not that many to choose from. I could pop from a list +// or a set as well... Likely the best solution is to keep a count of the +// largest fd handed out and to just use this until you wrap. This will be +// super fast for a normal cage and will be correct in the weird case. +// Right now, I'll just implement the slow path and will speed this up +// later, if needed. +// #[doc = include_str!("../docs/get_unused_virtual_fd.md")] +pub fn get_unused_virtual_fd( + cageid: u64, + realfd: u64, + should_cloexec: bool, + optionalinfo: u64, +) -> Result { + let mut fdtable = GLOBALFDTABLE.lock().unwrap(); + + if !fdtable.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + // Set up the entry so it has the right info... + // Note, a HashMap stores its data on the heap! No need to box it... + // https://doc.rust-lang.org/book/ch08-03-hash-maps.html#creating-a-new-hash-map + let myentry = FDTableEntry { + realfd, + should_cloexec, + optionalinfo, + }; + + let myfdmap = fdtable.get_mut(&cageid).unwrap(); + + // Check the fds in order. + for fdcandidate in 0..FD_PER_PROCESS_MAX { + // Get the entry if it's Vacant and assign it to e (so I can fill + // it in). + if let std::collections::hash_map::Entry::Vacant(e) = myfdmap.entry(fdcandidate) { + e.insert(myentry); + _increment_realfd(realfd); + return Ok(fdcandidate); + } + } + + // I must have checked all fds and failed to find one open. Fail! + Err(threei::Errno::EMFILE as u64) +} + +// This is used for things like dup2, which need a specific fd... +// If the requested_virtualfd is used, I close it... +// #[doc = include_str!("../docs/get_specific_virtual_fd.md")] +pub fn get_specific_virtual_fd( + cageid: u64, + requested_virtualfd: u64, + realfd: u64, + should_cloexec: bool, + optionalinfo: u64, +) -> Result<(), threei::RetVal> { + let mut fdtable = GLOBALFDTABLE.lock().unwrap(); + + if !fdtable.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + // If you ask for a FD number that is too large, I'm going to reject it. + // Note that, I need to use the FD_PER_PROCESS_MAX setting because this + // is also how I'm tracking how many values you have open. If this + // changed, then these constants could be decoupled... + if requested_virtualfd > FD_PER_PROCESS_MAX { + return Err(threei::Errno::EBADF as u64); + } + + // Set up the entry so it has the right info... + // Note, a HashMap stores its data on the heap! No need to box it... + // https://doc.rust-lang.org/book/ch08-03-hash-maps.html#creating-a-new-hash-map + let myentry = FDTableEntry { + realfd, + should_cloexec, + optionalinfo, + }; + + // I moved this up so that if I decrement the same realfd, it calls + // the intermediate handler instead of the final one. + _increment_realfd(realfd); + if let Some(entry) = fdtable.get(&cageid).unwrap().get(&requested_virtualfd) { + if entry.realfd != NO_REAL_FD { + _decrement_realfd(entry.realfd); + } + else { + // Let their code know this has been closed... + let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); + (closehandlers.unreal_handler)(entry.optionalinfo); + } + } + + // always add the new entry + fdtable.get_mut(&cageid).unwrap().insert(requested_virtualfd,myentry); + Ok(()) +} + +// We're just setting a flag here, so this should be pretty straightforward. +// #[doc = include_str!("../docs/set_cloexec.md")] +pub fn set_cloexec(cageid: u64, virtualfd: u64, is_cloexec: bool) -> Result<(), threei::RetVal> { + let mut fdtable = GLOBALFDTABLE.lock().unwrap(); + + if !fdtable.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + // Set the is_cloexec flag or return EBADFD, if that's missing... + return match fdtable.get_mut(&cageid).unwrap().get_mut(&virtualfd) { + Some(tableentry) => { + tableentry.should_cloexec = is_cloexec; + Ok(()) + } + None => Err(threei::Errno::EBADFD as u64), + }; +} + +// Super easy, just return the optionalinfo field... +// #[doc = include_str!("../docs/get_optionalinfo.md")] +pub fn get_optionalinfo(cageid: u64, virtualfd: u64) -> Result { + let fdtable = GLOBALFDTABLE.lock().unwrap(); + if !fdtable.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + return match fdtable.get(&cageid).unwrap().get(&virtualfd) { + Some(tableentry) => Ok(tableentry.optionalinfo), + None => Err(threei::Errno::EBADFD as u64), + }; +} + +// We're setting an opaque value here. This should be pretty straightforward. +// #[doc = include_str!("../docs/set_optionalinfo.md")] +pub fn set_optionalinfo( + cageid: u64, + virtualfd: u64, + optionalinfo: u64, +) -> Result<(), threei::RetVal> { + let mut fdtable = GLOBALFDTABLE.lock().unwrap(); + + if !fdtable.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + // Set optionalinfo or return EBADFD, if that's missing... + return match fdtable.get_mut(&cageid).unwrap().get_mut(&virtualfd) { + Some(tableentry) => { + tableentry.optionalinfo = optionalinfo; + Ok(()) + } + None => Err(threei::Errno::EBADFD as u64), + }; +} + +// Helper function used for fork... Copies an fdtable for another process +// #[doc = include_str!("../docs/copy_fdtable_for_cage.md")] +pub fn copy_fdtable_for_cage(srccageid: u64, newcageid: u64) -> Result<(), threei::Errno> { + let mut fdtable = GLOBALFDTABLE.lock().unwrap(); + + if !fdtable.contains_key(&srccageid) { + panic!("Unknown srccageid in fdtable access"); + } + if fdtable.contains_key(&newcageid) { + panic!("Known newcageid in fdtable access"); + } + + // Insert a copy and ensure it didn't exist... + let hmcopy = fdtable.get(&srccageid).unwrap().clone(); + + // increment the reference to items in the fdtable appropriately... + for v in fdtable.get(&srccageid).unwrap().values() { + if v.realfd != NO_REAL_FD { + _increment_realfd(v.realfd); + } + } + + // insert the new table... + assert!(fdtable.insert(newcageid, hmcopy).is_none()); + Ok(()) + // I'm not going to bother to check the number of fds used overall yet... + // Err(threei::Errno::EMFILE as u64), +} + +// This is mostly used in handling exit, etc. Returns the HashMap +// for the cage. +// #[doc = include_str!("../docs/remove_cage_from_fdtable.md")] +pub fn remove_cage_from_fdtable(cageid: u64) { + let mut fdtable = GLOBALFDTABLE.lock().unwrap(); + + if !fdtable.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + // decrement the reference to items in the fdtable appropriately... + for v in fdtable.get(&cageid).unwrap().values() { + if v.realfd != NO_REAL_FD { + _decrement_realfd(v.realfd); + } + else { + // Let their code know this has been closed... + let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); + (closehandlers.unreal_handler)(v.optionalinfo); + } + } + + + fdtable.remove(&cageid).unwrap(); +} + +// This removes all fds with the should_cloexec flag set. They are returned +// in a new hashmap... +// #[doc = include_str!("../docs/empty_fds_for_exec.md")] +pub fn empty_fds_for_exec(cageid: u64) { + let mut fdtable = GLOBALFDTABLE.lock().unwrap(); + + if !fdtable.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + // Create this hashmap through an lambda that checks should_cloexec... + // See: https://doc.rust-lang.org/std/collections/struct.HashMap.html#method.extract_if + +/* fdtable + .get_mut(&cageid) + .unwrap() + .extract_if(|_k, v| v.should_cloexec) + .collect()*/ + + // I'm writing the below code to avoid using the extract_if experimental + // nightly function... + let thiscagefdtable = fdtable.get_mut(&cageid).unwrap(); + + let mut without_cloexec_hm:HashMap = HashMap::new(); + for (k,v) in thiscagefdtable.drain() { + if v.should_cloexec { + if v.realfd == NO_REAL_FD { + // Let their code know this has been closed... + let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); + (closehandlers.unreal_handler)(v.optionalinfo); + } + else { + // Let the helper tell the user and decrement the count + _decrement_realfd(v.realfd); + } + } + else{ + without_cloexec_hm.insert(k,v); + } + + } + // Put the ones without_cloexec back in the hashmap... + fdtable.insert(cageid,without_cloexec_hm); + +} + +// returns a copy of the fdtable for a cage. Useful helper function for a +// caller that needs to examine the table. Likely could be more efficient by +// letting the caller borrow this... +// #[doc = include_str!("../docs/return_fdtable_copy.md")] +pub fn return_fdtable_copy(cageid: u64) -> HashMap { + let fdtable = GLOBALFDTABLE.lock().unwrap(); + + if !fdtable.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + fdtable.get(&cageid).unwrap().clone() +} + +/******************* CLOSE SPECIFIC FUNCTIONALITY *******************/ + +// Helper for close. Returns a tuple of realfd, number of references +// remaining. +// #[doc = include_str!("../docs/close_virtualfd.md")] +pub fn close_virtualfd(cageid:u64, virtfd:u64) -> Result<(),threei::RetVal> { + let mut fdtable = GLOBALFDTABLE.lock().unwrap(); + + if !fdtable.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + let thiscagesfdtable = fdtable.get_mut(&cageid).unwrap(); + + match thiscagesfdtable.remove(&virtfd) { + Some(entry) => + if entry.realfd == NO_REAL_FD { + // Let their code know this has been closed... + let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); + (closehandlers.unreal_handler)(entry.optionalinfo); + Ok(()) + } + else { + _decrement_realfd(entry.realfd); + Ok(()) + } + None => Err(threei::Errno::EBADFD as u64), + } +} + + +// Register a series of helpers to be called for close. Can be called +// multiple times to override the older helpers. +// #[doc = include_str!("../docs/register_close_handlers.md")] +pub fn register_close_handlers(intermediate_handler: fn(u64), final_handler: fn(u64), unreal_handler: fn(u64)) { + // Unlock the table and set the handlers... + let mut closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); + closehandlers.intermediate_handler = intermediate_handler; + closehandlers.final_handler = final_handler; + closehandlers.unreal_handler = unreal_handler; +} + +// Helpers to track the count of times each realfd is used +#[doc(hidden)] +fn _decrement_realfd(realfd:u64) -> u64 { + // Do nothing if it's not a realfd... + if realfd == NO_REAL_FD { + panic!("Called _decrement_realfd with NO_REAL_FD"); + } + + // Get this table's lock... + let mut realfdcount = GLOBALREALFDCOUNT.lock().unwrap(); + + let newcount:u64 = realfdcount.get(&realfd).unwrap() - 1; + let closehandlers = CLOSEHANDLERTABLE.lock().unwrap(); + if newcount > 0 { + (closehandlers.intermediate_handler)(realfd); + realfdcount.insert(realfd,newcount); + } + else { + (closehandlers.final_handler)(realfd); + } + newcount +} + +// Helpers to track the count of times each realfd is used +#[doc(hidden)] +fn _increment_realfd(realfd:u64) -> u64 { + if realfd == NO_REAL_FD { + return 0 + } + + // Get this table's lock... + let mut realfdcount = GLOBALREALFDCOUNT.lock().unwrap(); + + // Get a mutable reference to the entry so we can update it. + return match realfdcount.get_mut(&realfd) { + Some(count) => { + *count += 1; + *count + } + None => { + realfdcount.insert(realfd, 1); + 1 + } + } +} + +/*************** Code for handling select() ****************/ + +use libc::fd_set; +use std::collections::HashSet; +use std::cmp; +use std::mem; + +// Helper to get an empty fd_set. Helper function to isolate unsafe code, +// etc. +pub fn _init_fd_set() -> fd_set { + let raw_fd_set:fd_set; + unsafe { + let mut this_fd_set = mem::MaybeUninit::::uninit(); + libc::FD_ZERO(this_fd_set.as_mut_ptr()); + raw_fd_set = this_fd_set.assume_init() + } + raw_fd_set +} + +// Helper to get a null pointer. +pub fn _get_null_fd_set() -> fd_set { + //unsafe{ptr::null_mut()} + // BUG!!! Need to fix this later. + _init_fd_set() +} + +pub fn _fd_set(fd:u64, thisfdset:&mut fd_set) { + unsafe{libc::FD_SET(fd as i32,thisfdset)} +} + +pub fn _fd_isset(fd:u64, thisfdset:&fd_set) -> bool { + unsafe{libc::FD_ISSET(fd as i32,thisfdset)} +} + +// Computes the bitmodifications and returns a (maxnfds, unrealset) tuple... +fn _do_bitmods(myfdmap:HashMap, nfds:u64, infdset: fd_set, thisfdset: &mut fd_set, mappingtable: &mut HashMap) -> Result<(u64,HashSet<(u64,u64)>),threei::RetVal> { + let mut unrealhashset:HashSet<(u64,u64)> = HashSet::new(); + // Iterate through the infdset and set those values as is appropriate + let mut highestpos = 0; + + // Clippy is somehow missing how pos is using bit. + #[allow(clippy::needless_range_loop)] + for bit in 0..nfds as usize { + let pos = bit as u64; + if _fd_isset(pos,&infdset) { + if let Some(entry) = myfdmap.get(&pos) { + if entry.realfd == NO_REAL_FD { + unrealhashset.insert((pos,entry.optionalinfo)); + } + else { + mappingtable.insert(entry.realfd, pos); + _fd_set(entry.realfd,thisfdset); + // I add one because select expects nfds to be the max+1 + highestpos = cmp::max(highestpos, entry.realfd+1); + } + } + else { + return Err(threei::Errno::EINVAL as u64); + } + } + } + Ok((highestpos,unrealhashset)) +} + +// helper to call before calling select beneath you. Translates your virtfds +// to realfds. +// See: https://man7.org/linux/man-pages/man2/select.2.html for details / +// corner cases about the arguments. + +// I hate doing these, but don't know how to make this interface better... +#[allow(clippy::type_complexity)] +#[allow(clippy::too_many_arguments)] +// #[doc = include_str!("../docs/get_real_bitmasks_for_select.md")] +// pub fn get_real_bitmasks_for_select(cageid:u64, nfds:u64, readbits:Option, writebits:Option, exceptbits:Option) -> Result<(u64, fd_set, fd_set, fd_set, [HashSet<(u64,u64)>;3], HashMap),threei::RetVal> { + +// if nfds >= FD_PER_PROCESS_MAX { +// return Err(threei::Errno::EINVAL as u64); +// } + +// let globfdtable = GLOBALFDTABLE.lock().unwrap(); + +// if !globfdtable.contains_key(&cageid) { +// panic!("Unknown cageid in fdtable access"); +// } + +// let mut unrealarray:[HashSet<(u64,u64)>;3] = [HashSet::new(),HashSet::new(),HashSet::new()]; +// let mut mappingtable:HashMap = HashMap::new(); +// let mut newnfds = 0; + +// // putting results in a vec was the cleanest way I found to do this.. +// let mut resultvec = Vec::new(); + +// for (unrealoffset, inset) in [readbits,writebits, exceptbits].into_iter().enumerate() { +// match inset { +// Some(virtualbits) => { +// let mut retset = _init_fd_set(); +// let (thisnfds,myunrealhashset) = _do_bitmods(globfdtable.get(&cageid).unwrap().clone(),nfds,virtualbits, &mut retset,&mut mappingtable)?; +// resultvec.push(retset); +// newnfds = cmp::max(thisnfds, newnfds); +// unrealarray[unrealoffset] = myunrealhashset; +// } +// None => { +// // This item is null. No unreal items +// // BUG: Need to actually return null! +// resultvec.push(_get_null_fd_set()); +// unrealarray[unrealoffset] = HashSet::new(); +// } +// } +// } + +// Ok((newnfds, resultvec[0], resultvec[1], resultvec[2], unrealarray, mappingtable)) + +// } + + +// helper to call after calling select beneath you. returns the fd_sets you +// need for your return from a select call and the number of unique flags +// set... + +// I hate doing these, but don't know how to make this interface better... +#[allow(clippy::type_complexity)] +#[allow(clippy::too_many_arguments)] +// #[doc = include_str!("../docs/get_virtual_bitmasks_from_select_result.md")] +pub fn get_virtual_bitmasks_from_select_result(nfds:u64, readbits:fd_set, writebits:fd_set, exceptbits:fd_set,unrealreadset:HashSet, unrealwriteset:HashSet, unrealexceptset:HashSet, mappingtable:HashMap) -> Result<(u64, fd_set, fd_set, fd_set),threei::RetVal> { + + // Note, I don't need the cage_id here because I have the mappingtable... + + if nfds >= FD_PER_PROCESS_MAX { + panic!("This shouldn't be possible because we shouldn't have returned this previously") + } + + let mut flagsset = 0; + let mut retvec = Vec::new(); + + for (inset,unrealset) in [(readbits,unrealreadset), (writebits,unrealwriteset), (exceptbits,unrealexceptset)] { + let mut retbits = _init_fd_set(); + for bit in 0..nfds as usize { + let pos = bit as u64; + if _fd_isset(pos,&inset)&& !_fd_isset(*mappingtable.get(&pos).unwrap(),&retbits) { + flagsset+=1; + _fd_set(*mappingtable.get(&pos).unwrap(),&mut retbits); + } + } + for virtfd in unrealset { + if !_fd_isset(virtfd,&retbits) { + flagsset+=1; + _fd_set(virtfd,&mut retbits); + } + } + retvec.push(retbits); + } + + Ok((flagsset,retvec[0],retvec[1],retvec[2])) + +} + + + +/********************** POLL SPECIFIC FUNCTIONS **********************/ + +// helper to call before calling poll beneath you. replaces the fds in +// the poll struct with virtual versions and returns the items you need +// to check yourself... +#[allow(clippy::type_complexity)] +// #[doc = include_str!("../docs/convert_virtualfds_to_real.md")] +pub fn convert_virtualfds_to_real(cageid:u64, virtualfds:Vec) -> (Vec, Vec<(u64,u64)>, Vec, HashMap) { + + let globfdtable = GLOBALFDTABLE.lock().unwrap(); + + if !globfdtable.contains_key(&cageid) { + panic!("Unknown cageid in fdtable access"); + } + + let mut unrealvec = Vec::new(); + let mut realvec = Vec::new(); + let mut invalidvec = Vec::new(); + let thefdhm = globfdtable.get(&cageid).unwrap(); + let mut mappingtable:HashMap = HashMap::new(); + + // BUG?: I'm ignoring the fact that virtualfds can show up multiple times. + // I'm not sure this actually matters, but I didn't think hard about it. + for virtfd in virtualfds { + match thefdhm.get(&virtfd) { + Some(entry) => { + // always append the value here. NO_REAL_FD will be added + // in the appropriate places to tell them to handle those calls + // themself. + realvec.push(entry.realfd); + if entry.realfd == NO_REAL_FD { + unrealvec.push((virtfd,entry.optionalinfo)); + } + else{ + mappingtable.insert(entry.realfd, virtfd); + } + } + None => { + // Add this because they need to handle it if POLLNVAL is set. + // An exception should not be raised!!! + realvec.push(INVALID_FD); + invalidvec.push(virtfd); + } + } + } + + (realvec, unrealvec, invalidvec, mappingtable) +} + + + +// helper to call after calling poll. replaces the realfds the vector +// with virtual ones... +// #[doc = include_str!("../docs/convert_realfds_back_to_virtual.md")] +pub fn convert_realfds_back_to_virtual(realfds:Vec, mappingtable:HashMap) -> Vec { + + // I don't care what cage was used, and don't need to lock anything... + // I have the mappingtable! + + let mut virtvec = Vec::new(); + + for realfd in realfds { + virtvec.push(*mappingtable.get(&realfd).unwrap()); + } + + virtvec +} + + + +/********************** TESTING HELPER FUNCTION **********************/ + +// Helper to initialize / empty out state so we can test with a clean system... +// only used when testing... +// +// I'm cleaning up "poisoned" mutexes here so that I can handle tests that +// panic +// #[doc(hidden)] +// pub fn refresh() { +// let mut fdtable = GLOBALFDTABLE.lock().unwrap_or_else(|e| { +// GLOBALFDTABLE.clear_poison(); +// e.into_inner() +// }); +// fdtable.clear(); +// fdtable.insert(threei::TESTING_CAGEID, HashMap::new()); +// let mut closehandlers = CLOSEHANDLERTABLE.lock().unwrap_or_else(|e| { +// CLOSEHANDLERTABLE.clear_poison(); +// e.into_inner() +// }); +// closehandlers.intermediate_handler = NULL_FUNC; +// closehandlers.final_handler = NULL_FUNC; +// closehandlers.unreal_handler = NULL_FUNC; +// let mut _realfdcount = GLOBALREALFDCOUNT.lock().unwrap_or_else(|e| { +// GLOBALREALFDCOUNT.clear_poison(); +// e.into_inner() +// }); +// } + diff --git a/src/interface/comm.rs b/src/interface/comm.rs new file mode 100644 index 00000000..75395936 --- /dev/null +++ b/src/interface/comm.rs @@ -0,0 +1,644 @@ +// // Authors: Nicholas Renner and Jonathan Singer and Tristan Brigham +// // +// // + +use crate::interface; +use std::fs::read_to_string; +use std::mem::size_of; +use std::str::from_utf8; +use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; + +extern crate libc; + +// static NET_DEV_FILENAME: &str = "net_devices"; + +static mut UD_ID_COUNTER: AtomicUsize = AtomicUsize::new(0); + +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +pub enum GenSockaddr { + Unix(SockaddrUnix), + V4(SockaddrV4), + V6(SockaddrV6), +} +impl GenSockaddr { + pub fn port(&self) -> u16 { + match self { + GenSockaddr::Unix(_) => panic!("Invalid function called for this type of Sockaddr."), + GenSockaddr::V4(v4addr) => v4addr.sin_port, + GenSockaddr::V6(v6addr) => v6addr.sin6_port, + } + } + pub fn set_port(&mut self, port: u16) { + match self { + GenSockaddr::Unix(_) => panic!("Invalid function called for this type of Sockaddr."), + GenSockaddr::V4(v4addr) => v4addr.sin_port = port, + GenSockaddr::V6(v6addr) => v6addr.sin6_port = port, + }; + } + + pub fn addr(&self) -> GenIpaddr { + match self { + GenSockaddr::Unix(_) => panic!("Invalid function called for this type of Sockaddr."), + GenSockaddr::V4(v4addr) => GenIpaddr::V4(v4addr.sin_addr), + GenSockaddr::V6(v6addr) => GenIpaddr::V6(v6addr.sin6_addr), + } + } + + pub fn set_addr(&mut self, ip: GenIpaddr) { + match self { + GenSockaddr::Unix(_unixaddr) => { + panic!("Invalid function called for this type of Sockaddr.") + } + GenSockaddr::V4(v4addr) => { + v4addr.sin_addr = if let GenIpaddr::V4(v4ip) = ip { + v4ip + } else { + unreachable!() + } + } + GenSockaddr::V6(v6addr) => { + v6addr.sin6_addr = if let GenIpaddr::V6(v6ip) = ip { + v6ip + } else { + unreachable!() + } + } + }; + } + + pub fn set_family(&mut self, family: u16) { + match self { + GenSockaddr::Unix(unixaddr) => unixaddr.sun_family = family, + GenSockaddr::V4(v4addr) => v4addr.sin_family = family, + GenSockaddr::V6(v6addr) => v6addr.sin6_family = family, + }; + } + + pub fn get_family(&self) -> u16 { + match self { + GenSockaddr::Unix(unixaddr) => unixaddr.sun_family, + GenSockaddr::V4(v4addr) => v4addr.sin_family, + GenSockaddr::V6(v6addr) => v6addr.sin6_family, + } + } + + pub fn path(&self) -> &str { + match self { + GenSockaddr::Unix(unixaddr) => { + let pathiter = &mut unixaddr.sun_path.split(|idx| *idx == 0); + let pathslice = pathiter.next().unwrap(); + let path = from_utf8(pathslice).unwrap(); + path + } + GenSockaddr::V4(_) => panic!("Invalid function called for this type of Sockaddr."), + GenSockaddr::V6(_) => panic!("Invalid function called for this type of Sockaddr."), + } + } +} + +#[derive(Debug, Hash, Eq, PartialEq, Clone, Copy)] +pub enum GenIpaddr { + V4(V4Addr), + V6(V6Addr), +} + +impl GenIpaddr { + pub fn is_unspecified(&self) -> bool { + match self { + GenIpaddr::V4(v4ip) => v4ip.s_addr == 0, + GenIpaddr::V6(v6ip) => v6ip.s6_addr == [0; 16], + } + } + pub fn from_string(string: &str) -> Option { + let v4candidate: Vec<&str> = string.split('.').collect(); + let v6candidate: Vec<&str> = string.split(':').collect(); + let v4l = v4candidate.len(); + let v6l = v6candidate.len(); + if v4l == 1 && v6l > 1 { + //then we should try parsing it as an ipv6 address + let mut shortarr = [0u8; 16]; + let mut shortindex = 0; + let mut encountered_doublecolon = false; + for short in v6candidate { + if short.is_empty() { + //you can only have a double colon once in an ipv6 address + if encountered_doublecolon { + return None; + } + encountered_doublecolon = true; + + let numzeros = 8 - v6l + 1; //+1 to account for this empty string element + if numzeros == 0 { + return None; + } + shortindex += numzeros; + } else { + //ok we can actually parse the element in this case + if let Ok(b) = short.parse::() { + //manually handle big endianness + shortarr[2 * shortindex] = (b >> 8) as u8; + shortarr[2 * shortindex + 1] = (b & 0xff) as u8; + shortindex += 1; + } else { + return None; + } + } + } + return Some(Self::V6(V6Addr { s6_addr: shortarr })); + } else if v4l == 4 && v6l == 1 { + //then we should try parsing it as an ipv4 address + let mut bytearr = [0u8; 4]; + let mut shortindex = 0; + for byte in v4candidate { + if let Ok(b) = byte.parse::() { + bytearr[shortindex] = b; + shortindex += 1; + } else { + return None; + } + } + return Some(Self::V4(V4Addr { + s_addr: u32::from_ne_bytes(bytearr), + })); + } else { + return None; + } + } +} + +#[repr(C)] +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +pub struct SockaddrUnix { + pub sun_family: u16, + pub sun_path: [u8; 108], +} + +impl Default for SockaddrUnix { + fn default() -> Self { + SockaddrUnix { + sun_family: 0, + sun_path: [0; 108], + } + } +} + +pub fn new_sockaddr_unix(family: u16, path: &[u8]) -> SockaddrUnix { + let pathlen = path.len(); + if pathlen > 108 { + panic!("Unix domain paths cannot exceed 108 bytes.") + } + let mut array_path: [u8; 108] = [0; 108]; + array_path[0..pathlen].copy_from_slice(path); + SockaddrUnix { + sun_family: family, + sun_path: array_path, + } +} + +pub fn gen_ud_path() -> String { + let mut owned_path: String = "/sock".to_owned(); + unsafe { + let id = UD_ID_COUNTER.fetch_add(1, Ordering::Relaxed); + owned_path.push_str(&id.to_string()); + } + owned_path.clone() +} + +#[repr(C)] +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, Default)] +pub struct V4Addr { + pub s_addr: u32, +} +#[repr(C)] +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, Default)] +pub struct SockaddrV4 { + pub sin_family: u16, + pub sin_port: u16, + pub sin_addr: V4Addr, + pub padding: u64, +} + +#[repr(C)] +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, Default)] +pub struct V6Addr { + pub s6_addr: [u8; 16], +} +#[repr(C)] +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, Default)] +pub struct SockaddrV6 { + pub sin6_family: u16, + pub sin6_port: u16, + pub sin6_flowinfo: u32, + pub sin6_addr: V6Addr, + pub sin6_scope_id: u32, +} + +// #[derive(Debug)] +// pub struct Socket { +// pub raw_sys_fd: i32, +// } + +// impl Socket { +// pub fn new(domain: i32, socktype: i32, protocol: i32) -> Socket { +// let fd = unsafe { libc::socket(domain, socktype, protocol) }; + +// //we make every socket have a recieve timeout of one second +// //This is in order to allow the socket to process and recieve +// //shutdowns while blocked on blocking recv syscalls. +// let timeoutval = libc::timeval { +// tv_sec: 1, +// tv_usec: 0, +// }; +// unsafe { +// libc::setsockopt( +// fd, +// libc::SOL_SOCKET, +// libc::SO_RCVTIMEO, +// (&timeoutval as *const libc::timeval) as *const libc::c_void, +// size_of::() as u32, +// ) +// }; +// if fd < 0 { +// panic!("Socket creation failed when it should never fail"); +// } +// Self { raw_sys_fd: fd } +// } + +// pub fn bind(&self, addr: &GenSockaddr) -> i32 { +// let (finalsockaddr, addrlen) = match addr { +// GenSockaddr::V6(addrref6) => ( +// (addrref6 as *const SockaddrV6).cast::(), +// size_of::(), +// ), +// GenSockaddr::V4(addrref) => ( +// (addrref as *const SockaddrV4).cast::(), +// size_of::(), +// ), +// _ => { +// unreachable!() +// } +// }; +// unsafe { libc::bind(self.raw_sys_fd, finalsockaddr, addrlen as u32) } +// } + +// pub fn connect(&self, addr: &GenSockaddr) -> i32 { +// let (finalsockaddr, addrlen) = match addr { +// GenSockaddr::V6(addrref6) => ( +// (addrref6 as *const SockaddrV6).cast::(), +// size_of::(), +// ), +// GenSockaddr::V4(addrref) => ( +// (addrref as *const SockaddrV4).cast::(), +// size_of::(), +// ), +// _ => { +// unreachable!() +// } +// }; +// unsafe { libc::connect(self.raw_sys_fd, finalsockaddr, addrlen as u32) } +// } + +// pub fn sendto(&self, buf: *const u8, len: usize, addr: Option<&GenSockaddr>) -> i32 { +// let (finalsockaddr, addrlen) = match addr { +// Some(GenSockaddr::V6(addrref6)) => ( +// (addrref6 as *const SockaddrV6).cast::(), +// size_of::(), +// ), +// Some(GenSockaddr::V4(addrref)) => ( +// (addrref as *const SockaddrV4).cast::(), +// size_of::(), +// ), +// Some(_) => { +// unreachable!() +// } +// None => ( +// std::ptr::null::() as *const libc::sockaddr, +// 0, +// ), +// }; +// unsafe { +// libc::sendto( +// self.raw_sys_fd, +// buf as *const libc::c_void, +// len, +// 0, +// finalsockaddr, +// addrlen as u32, +// ) as i32 +// } +// } + +// pub fn recvfrom(&self, buf: *mut u8, len: usize, addr: &mut Option<&mut GenSockaddr>) -> i32 { +// let (finalsockaddr, mut addrlen) = match addr { +// Some(GenSockaddr::V6(ref mut addrref6)) => ( +// (addrref6 as *mut SockaddrV6).cast::(), +// size_of::() as u32, +// ), +// Some(GenSockaddr::V4(ref mut addrref)) => ( +// (addrref as *mut SockaddrV4).cast::(), +// size_of::() as u32, +// ), +// Some(_) => { +// unreachable!() +// } +// None => (std::ptr::null::() as *mut libc::sockaddr, 0), +// }; +// unsafe { +// libc::recvfrom( +// self.raw_sys_fd, +// buf as *mut libc::c_void, +// len, +// 0, +// finalsockaddr, +// &mut addrlen as *mut u32, +// ) as i32 +// } +// } + +// pub fn recvfrom_nonblocking( +// &self, +// buf: *mut u8, +// len: usize, +// addr: &mut Option<&mut GenSockaddr>, +// ) -> i32 { +// let (finalsockaddr, mut addrlen) = match addr { +// Some(GenSockaddr::V6(ref mut addrref6)) => ( +// (addrref6 as *mut SockaddrV6).cast::(), +// size_of::() as u32, +// ), +// Some(GenSockaddr::V4(ref mut addrref)) => ( +// (addrref as *mut SockaddrV4).cast::(), +// size_of::() as u32, +// ), +// Some(_) => { +// unreachable!() +// } +// None => (std::ptr::null::() as *mut libc::sockaddr, 0), +// }; +// self.set_nonblocking(); +// let retval = unsafe { +// libc::recvfrom( +// self.raw_sys_fd, +// buf as *mut libc::c_void, +// len, +// 0, +// finalsockaddr, +// &mut addrlen as *mut u32, +// ) as i32 +// }; +// self.set_blocking(); +// retval +// } + +// pub fn listen(&self, backlog: i32) -> i32 { +// unsafe { libc::listen(self.raw_sys_fd, backlog) } +// } + +// pub fn set_blocking(&self) -> i32 { +// unsafe { libc::fcntl(self.raw_sys_fd, libc::F_SETFL, 0) } +// } + +// pub fn set_nonblocking(&self) -> i32 { +// unsafe { libc::fcntl(self.raw_sys_fd, libc::F_SETFL, libc::O_NONBLOCK) } +// } + +// pub fn accept(&self, isv4: bool) -> (Result, GenSockaddr) { +// return if isv4 { +// let mut inneraddrbuf = SockaddrV4::default(); +// let mut sadlen = size_of::() as u32; +// let newfd = unsafe { +// libc::accept( +// self.raw_sys_fd, +// (&mut inneraddrbuf as *mut SockaddrV4).cast::(), +// &mut sadlen as *mut u32, +// ) +// }; + +// if newfd < 0 { +// (Err(newfd), GenSockaddr::V4(inneraddrbuf)) +// } else { +// ( +// Ok(Self { raw_sys_fd: newfd }), +// GenSockaddr::V4(inneraddrbuf), +// ) +// } +// } else { +// let mut inneraddrbuf = SockaddrV6::default(); +// let mut sadlen = size_of::() as u32; +// let newfd = unsafe { +// libc::accept( +// self.raw_sys_fd, +// (&mut inneraddrbuf as *mut SockaddrV6).cast::(), +// &mut sadlen as *mut u32, +// ) +// }; + +// if newfd < 0 { +// (Err(newfd), GenSockaddr::V6(inneraddrbuf)) +// } else { +// ( +// Ok(Self { raw_sys_fd: newfd }), +// GenSockaddr::V6(inneraddrbuf), +// ) +// } +// }; +// } + +// pub fn nonblock_accept(&self, isv4: bool) -> (Result, GenSockaddr) { +// return if isv4 { +// let mut inneraddrbuf = SockaddrV4::default(); +// let mut sadlen = size_of::() as u32; +// self.set_nonblocking(); +// let newfd = unsafe { +// libc::accept( +// self.raw_sys_fd, +// (&mut inneraddrbuf as *mut SockaddrV4).cast::(), +// &mut sadlen as *mut u32, +// ) +// }; +// self.set_blocking(); + +// if newfd < 0 { +// (Err(newfd), GenSockaddr::V4(inneraddrbuf)) +// } else { +// ( +// Ok(Self { raw_sys_fd: newfd }), +// GenSockaddr::V4(inneraddrbuf), +// ) +// } +// } else { +// let mut inneraddrbuf = SockaddrV6::default(); +// let mut sadlen = size_of::() as u32; +// self.set_nonblocking(); +// let newfd = unsafe { +// libc::accept( +// self.raw_sys_fd, +// (&mut inneraddrbuf as *mut SockaddrV6).cast::(), +// &mut sadlen as *mut u32, +// ) +// }; +// self.set_blocking(); + +// if newfd < 0 { +// (Err(newfd), GenSockaddr::V6(inneraddrbuf)) +// } else { +// ( +// Ok(Self { raw_sys_fd: newfd }), +// GenSockaddr::V6(inneraddrbuf), +// ) +// } +// }; +// } + +// pub fn setsockopt(&self, level: i32, optname: i32, optval: i32) -> i32 { +// let valbuf = optval; +// let ret = unsafe { +// libc::setsockopt( +// self.raw_sys_fd, +// level, +// optname, +// (&valbuf as *const i32).cast::(), +// size_of::() as u32, +// ) +// }; +// ret +// } + +// pub fn shutdown(&self, how: i32) -> i32 { +// let ret = unsafe { libc::shutdown(self.raw_sys_fd, how) }; +// ret +// } + +// pub fn check_rawconnection(&self) -> bool { +// let mut valbuf = 0; +// let mut len = size_of::() as u32; +// let ret = unsafe { +// libc::getsockopt( +// self.raw_sys_fd, +// libc::SOL_SOCKET, +// libc::SO_ERROR, +// (&mut valbuf as *mut i32).cast::(), +// &mut len as *mut u32, +// ) +// }; +// (ret == 0) && (valbuf == 0) // if return val is 0 and error is 0 it's connected +// } +// } + +// impl Drop for Socket { +// fn drop(&mut self) { +// unsafe { +// libc::close(self.raw_sys_fd); +// } +// } +// } + +// pub fn getifaddrs_from_file() -> String { +// read_to_string(NET_DEV_FILENAME) +// .expect("No net_devices file present!") +// .to_owned() +// } + +// Implementations of select related FD_SET structure +pub struct FdSet(libc::fd_set); + +impl FdSet { + pub fn new() -> FdSet { + unsafe { + let mut raw_fd_set = std::mem::MaybeUninit::::uninit(); + libc::FD_ZERO(raw_fd_set.as_mut_ptr()); + FdSet(raw_fd_set.assume_init()) + } + } + + pub fn new_from_ptr(raw_fdset_ptr: *const libc::fd_set) -> &'static mut FdSet { + unsafe { &mut *(raw_fdset_ptr as *mut FdSet) } + } + + // copy the src FdSet into self + pub fn copy_from(&mut self, src_fds: &FdSet) { + unsafe { + std::ptr::copy_nonoverlapping( + &src_fds.0 as *const libc::fd_set, + &mut self.0 as *mut libc::fd_set, + 1, + ); + } + } + + // turn off the fd bit in fd_set (currently only used by the tests) + #[allow(dead_code)] + pub fn clear(&mut self, fd: i32) { + unsafe { libc::FD_CLR(fd, &mut self.0) } + } + + // turn on the fd bit in fd_set + pub fn set(&mut self, fd: i32) { + unsafe { libc::FD_SET(fd, &mut self.0) } + } + + // return true if the bit for fd is set, false otherwise + pub fn is_set(&self, fd: i32) -> bool { + unsafe { libc::FD_ISSET(fd, &self.0) } + } + + pub fn is_empty(&self) -> bool { + let fd_array: &[u8] = unsafe { + std::slice::from_raw_parts(&self.0 as *const _ as *const u8, size_of::()) + }; + fd_array.iter().all(|&byte| byte == 0) + } + + // for each fd, if kernel_fds turned it on, then self will turn the corresponding tranlated fd on + pub fn set_from_kernelfds_and_translate( + &mut self, + kernel_fds: &FdSet, + nfds: i32, + rawfd_lindfd_tuples: &Vec<(i32, i32)>, + ) { + for fd in 0..nfds { + if !kernel_fds.is_set(fd) { + continue; + } + // translate and set + if let Some((_, lindfd)) = rawfd_lindfd_tuples.iter().find(|(rawfd, _)| *rawfd == fd) { + self.set(*lindfd); + } + } + } +} + +// for unwrapping in kernel_select +fn to_fdset_ptr(opt: Option<&mut FdSet>) -> *mut libc::fd_set { + match opt { + None => std::ptr::null_mut(), + Some(&mut FdSet(ref mut raw_fd_set)) => raw_fd_set, + } +} + +pub fn kernel_select( + nfds: libc::c_int, + readfds: Option<&mut FdSet>, + writefds: Option<&mut FdSet>, + errorfds: Option<&mut FdSet>, +) -> i32 { + // Call libc::select and store the result + let result = unsafe { + // Create a timeval struct with zero timeout + + let mut kselect_timeout = libc::timeval { + tv_sec: 0, // 0 seconds + tv_usec: 0, // 0 microseconds + }; + + libc::select( + nfds, + to_fdset_ptr(readfds), + to_fdset_ptr(writefds), + to_fdset_ptr(errorfds), + &mut kselect_timeout as *mut libc::timeval, + ) + }; + + return result; +} diff --git a/src/interface/errnos.rs b/src/interface/errnos.rs new file mode 100644 index 00000000..067f5f01 --- /dev/null +++ b/src/interface/errnos.rs @@ -0,0 +1,439 @@ +#![allow(dead_code)] +// Error handling for SafePOSIX +use crate::interface; + +use std::sync::OnceLock; + +pub static VERBOSE: OnceLock = OnceLock::new(); + +//A macro which takes the enum and adds to it a try_from trait which can convert values back to +//enum variants +macro_rules! reversible_enum { + ($(#[$settings: meta])* $visibility: vis enum $enumname:ident { + $($valuename: ident = $value: expr,)* + }) => { + $(#[$settings])* + $visibility enum $enumname { + $($valuename = $value,)* + } + + impl $enumname { + $visibility fn from_discriminant(v: i32) -> Result { + match v { + $($value => Ok($enumname::$valuename),)* + _ => Err(()), + } + } + } + } +} + +reversible_enum! { + #[derive(Debug, PartialEq, Eq)] + #[repr(i32)] + pub enum Errno { + EPERM = 1, // Operation not permitted + ENOENT = 2, // No such file or directory + ESRCH = 3, // No such process + EINTR = 4, // Interrupted system call + EIO = 5, // I/O error + ENXIO = 6, // No such device or address + EBIG = 7, // Argument list too long + ENOEXEC = 8, // Exec format error + EBADF = 9, // Bad file number + ECHILD = 10, // No child processes + EAGAIN = 11, // Try again + ENOMEM = 12, // Out of memory + EACCES = 13, // Permission denied + EFAULT = 14, // Bad address + ENOTBLK = 15, // Block device required + EBUSY = 16, // Device or resource busy + EEXIST = 17, // File exists + EXDEV = 18, // Cross-device link + ENODEV = 19, // No such device + ENOTDIR = 20, // Not a directory + EISDIR = 21, // Is a directory + EINVAL = 22, // Invalid argument + ENFILE = 23, // File table overflow + EMFILE = 24, // Too many open files + ENOTTY = 25, // Not a typewriter + ETXTBSY = 26, // Text file busy + EFBIG = 27, // File too large + ENOSPC = 28, // No space left on device + ESPIPE = 29, // Illegal seek + EROFS = 30, // Read-only file system + EMLINK = 31, // Too many links + EPIPE = 32, // Broken pipe + EDOM = 33, // Math argument out of domain of func + ERANGE = 34, // Math result not representable + EDEADLK = 35, // Resource deadlock would occur + ENAMETOOLONG = 36, // File name too long + ENOLCK = 37, // No record locks available + ENOSYS = 38, // Function not implemented + ENOTEMPTY = 39, // Directory not empty + ELOOP = 40, // Too many symbolic links encountered + // EWOULDBLOCK = 11, // Operation would block, returns EAGAIN + ENOMSG = 42, // No message of desired type + EIDRM = 43, // Identifier removed + ECHRNG = 44, // Channel number out of range + EL2NSYNC = 45, // Level not synchronized + EL3HLT = 46, // Level halted + EL3RST = 47, // Level reset + ELNRNG = 48, // Link number out of range + EUNATCH = 49, // Protocol driver not attached + ENOCSI = 50, // No CSI structure available + EL2HLT = 51, // Level halted + EBADE = 52, // Invalid exchange + EBADR = 53, // Invalid request descriptor + EXFULL = 54, // Exchange full + ENOANO = 55, // No anode + EBADRQC = 56, // Invalid request code + EBADSLT = 57, // Invalid slot + EBFONT = 59, // Bad font file format + ENOSTR = 60, // Device not a stream + ENODATA = 61, // No data available + ETIME = 62, // Timer expired + ENOSR = 63, // Out of streams resources + ENONET = 64, // Machine is not on the network + ENOPKG = 65, // Package not installed + EREMOTE = 66, // Object is remote + ENOLINK = 67, // Link has been severed + EADV = 68, // Advertise error + ESRMNT = 69, // Srmount error + ECOMM = 70, // Communication error on send + EPROTO = 71, // Protocol error + EMULTIHOP = 72, // Multihop attempted + EDOTDOT = 73, // RFS specific error + EBADMSG = 74, // Not a data message + EOVERFLOW = 75, // Value too large for defined data type + ENOTUNIQ = 76, // Name not unique on network + EBADFD = 77, // File descriptor in bad state + EREMCHG = 78, // Remote address changed + ELIBACC = 79, // Can not access a needed shared library + ELIBBAD = 80, // Accessing a corrupted shared library + ELIBSCN = 81, // .lib section in a.out corrupted + ELIBMAX = 82, // Attempting to link in too many shared libraries + ELIBEXEC = 83, // Cannot exec a shared library directly + EILSEQ = 84, // Illegal byte sequence + ERESTART = 85, // Interrupted system call should be restarted + ESTRPIPE = 86, // Streams pipe error + EUSERS = 87, // Too many users + ENOTSOCK = 88, // Socket operation on non-socket + EDESTADDRREQ = 89, // Destination address required + EMSGSIZE = 90, // Message too long + EPROTOTYPE = 91, // Protocol wrong type for socket + ENOPROTOOPT = 92, // Protocol not available + EPROTONOSUPPORT = 93, // Protocol not supported + ESOCKTNOSUPPORT = 94, // Socket type not supported + EOPNOTSUPP = 95, // Operation not supported on transport endpoint + EPFNOSUPPORT = 96, // Protocol family not supported + EAFNOSUPPORT = 97, // Address family not supported by protocol + EADDRINUSE = 98, // Address already in use + EADDRNOTAVAIL = 99, // Cannot assign requested address + ENETDOWN = 100, // Network is down + ENETUNREACH = 101, // Network is unreachable + ENETRESET = 102, // Network dropped connection because of reset + ECONNABORTED = 103, // Software caused connection abort + ECONNRESET = 104, // Connection reset by peer + ENOBUFS = 105, // No buffer space available + EISCONN = 106, // Transport endpoint is already connected + ENOTCONN = 107, // Transport endpoint is not connected + ESHUTDOWN = 108, // Cannot send after transport endpoint shutdown + ETOOMANYREFS = 109, // Too many references cannot splice + ETIMEDOUT = 110, // Connection timed out + ECONNREFUSED = 111, // Connection refused + EHOSTDOWN = 112, // Host is down + EHOSTUNREACH = 113, // No route to host + EALREADY = 114, // Operation already in progress + EINPROGRESS = 115, // Operation now in progress + ESTALE = 116, // Stale NFS file handle + EUCLEAN = 117, // Structure needs cleaning + ENOTNAM = 118, // Not a XENIX named type file + ENAVAIL = 119, // No XENIX semaphores available + EISNAM = 120, // Is a named type file + EREMOTEIO = 121, // Remote I/O error + EDQUOT = 122, // Quota exceeded + ENOMEDIUM = 123, // No medium found + EMEDIUMTYPE = 124, // Wrong medium type + ECANCELED = 125, // Operation Canceled + ENOKEY = 126, // Required key not available + EKEYEXPIRED = 127, // Key has expired + EKEYREVOKED = 128, // Key has been revoked + EKEYREJECTED = 129, // Key was rejected by service for robust mutexes + EOWNERDEAD = 130, // Owner died + ENOTRECOVERABLE = 131, // State not recoverable + } +} + +pub fn handle_errno(e: i32, syscall: &str) -> i32 { + match e { + // EPERM = 1, // Operation not permitted + 1 => syscall_error(Errno::EPERM, syscall, "Operation not permitted"), + // ENOENT = 2, // No such file or directory + 2 => syscall_error(Errno::ENOENT, syscall, "No such file or directory"), + // ESRCH = 3, // No such process + 3 => syscall_error(Errno::ESRCH, syscall, "No such process"), + // EINTR = 4, // Interrupted system call + 4=> syscall_error(Errno::EINTR, syscall, "Interrupted system call"), + // EIO = 5, // I/O error + 5=> syscall_error(Errno::EIO, syscall, "I/O error"), + // ENXIO = 6, // No such device or address + 6 => syscall_error(Errno::ENXIO, syscall, "No such device or address"), + // EBIG = 7, // Argument list too long + 7 => syscall_error(Errno::EBIG, syscall, "Argument list too long"), + // ENOEXEC = 8, // Exec format error + 8 => syscall_error(Errno::ENOEXEC, syscall, "Exec format error"), + // EBADF = 9, // Bad file number + 9=> syscall_error(Errno::EBADF, syscall, "Bad file number"), + // ECHILD = 10, // No child processes + 10 => syscall_error(Errno::ECHILD, syscall, "No child processes"), + // EAGAIN = 11, // Try again + 11 => syscall_error(Errno::EAGAIN, syscall, "Try again"), + // ENOMEM = 12, // c + 12 => syscall_error(Errno::ENOMEM, syscall, "Try again"), + // EACCES = 13, // Permission denied + 13 => syscall_error(Errno::EACCES, syscall, "Permission denied"), + // EFAULT = 14, // Bad address + 14 => syscall_error(Errno::EFAULT, syscall, "Bad address"), + // ENOTBLK = 15, // Block device required + 15 => syscall_error(Errno::ENOTBLK, syscall, "Block device required"), + // EBUSY = 16, // Device or resource busy + 16 => syscall_error(Errno::EBUSY, syscall, "Device or resource busy"), + // EEXIST = 17, // File exists + 17 => syscall_error(Errno::EEXIST, syscall, "File exists"), + // EXDEV = 18, // Cross-device link + 18 => syscall_error(Errno::EXDEV, syscall, "Cross-device link"), + // ENODEV = 19, // No such device + 19 => syscall_error(Errno::ENODEV, syscall, "No such device"), + // ENOTDIR = 20, // Not a directory + 20 => syscall_error(Errno::ENOTDIR, syscall, "Not a directory"), + // EISDIR = 21, // Is a directory + 21 => syscall_error(Errno::EISDIR, syscall, "Is a directory"), + // EINVAL = 22, // Invalid argument + 22 => syscall_error(Errno::EINVAL, syscall, "Invalid argument"), + // ENFILE = 23, // File table overflow + 23 => syscall_error(Errno::ENFILE, syscall, "File table overflow"), + // EMFILE = 24, // Too many open files + 24 => syscall_error(Errno::EMFILE, syscall, "Too many open files"), + // ENOTTY = 25, // Not a typewriter + 25 => syscall_error(Errno::ENOTTY, syscall, "Not a typewriter"), + // ETXTBSY = 26, // Text file busy + 26 => syscall_error(Errno::ETXTBSY, syscall, "Text file busy"), + // EFBIG = 27, // File too large + 27 => syscall_error(Errno::EFBIG, syscall, "File too large"), + // ENOSPC = 28, // No space left on device + 28 => syscall_error(Errno::ENOSPC, syscall, "No space left on device"), + // ESPIPE = 29, // Illegal seek + 29 => syscall_error(Errno::ESPIPE, syscall, "Illegal seek"), + // EROFS = 30, // Read-only file system + 30 => syscall_error(Errno::EROFS, syscall, "Read-only file system"), + // EMLINK = 31, // Too many links + 31 => syscall_error(Errno::EMLINK, syscall, "Too many links"), + // EPIPE = 32, // Broken pipe + 32 => syscall_error(Errno::EPIPE, syscall, "Broken pipe"), + // EDOM = 33, // Math argument out of domain of func + 33 => syscall_error(Errno::EDOM, syscall, "Math argument out of domain of func"), + // ERANGE = 34, // Math result not representable + 34 => syscall_error(Errno::ERANGE, syscall, "Math result not representable"), + // EDEADLK = 35, // Resource deadlock would occur + 35 => syscall_error(Errno::EDEADLK, syscall, "Resource deadlock would occur"), + // ENAMETOOLONG = 36, // File name too long + 36 => syscall_error(Errno::ENAMETOOLONG, syscall, "File name too long"), + // ENOLCK = 37, // No record locks available + 37 => syscall_error(Errno::ENOLCK, syscall, "No record locks available"), + // ENOSYS = 38, // Function not implemented + 38 => syscall_error(Errno::ENOSYS, syscall, "Function not implemented"), + // ENOTEMPTY = 39, // Directory not empty + 39 => syscall_error(Errno::ENOTEMPTY, syscall, "Directory not empty"), + // ELOOP = 40, // Too many symbolic links encountered + 40 => syscall_error(Errno::ELOOP, syscall, "Too many symbolic links encountered"), + // // EWOULDBLOCK = 11, // Operation would block, returns EAGAIN + // ENOMSG = 42, // No message of desired type + 42 => syscall_error(Errno::ENOMSG, syscall, "No message of desired type"), + // EIDRM = 43, // Identifier removed + 43 => syscall_error(Errno::EIDRM, syscall, "Identifier removed"), + // ECHRNG = 44, // Channel number out of range + 44 => syscall_error(Errno::ECHRNG, syscall, "Channel number out of range"), + // EL2NSYNC = 45, // Level not synchronized + 45 => syscall_error(Errno::EL2NSYNC, syscall, "Level not synchronized"), + // EL3HLT = 46, // Level halted + 46 => syscall_error(Errno::EL3HLT, syscall, "Level halted"), + // EL3RST = 47, // Level reset + 47 => syscall_error(Errno::EL3RST, syscall, "Level reset"), + // ELNRNG = 48, // Link number out of range + 48 => syscall_error(Errno::ELNRNG, syscall, "Link number out of range"), + // EUNATCH = 49, // Protocol driver not attached + 49 => syscall_error(Errno::EUNATCH, syscall, "Protocol driver not attached"), + // ENOCSI = 50, // No CSI structure available + 50 => syscall_error(Errno::ENOCSI, syscall, "No CSI structure available"), + // EL2HLT = 51, // Level halted + 51 => syscall_error(Errno::EL2HLT, syscall, "Level halted"), + // EBADE = 52, // Invalid exchange + 52 => syscall_error(Errno::EBADE, syscall, "Invalid exchange"), + // EBADR = 53, // Invalid request descriptor + 53 => syscall_error(Errno::EBADR, syscall, "Invalid request descriptor"), + // EXFULL = 54, // Exchange full + 54 => syscall_error(Errno::EXFULL, syscall, "Exchange full"), + // ENOANO = 55, // No anode + 55 => syscall_error(Errno::ENOANO, syscall, "No anode"), + // EBADRQC = 56, // Invalid request code + 56 => syscall_error(Errno::EBADRQC, syscall, "Invalid request code"), + // EBADSLT = 57, // Invalid slot + 57 => syscall_error(Errno::EBADSLT, syscall, "Invalid slot"), + // EBFONT = 59, // Bad font file format + 59 => syscall_error(Errno::EBFONT, syscall, "Bad font file format"), + // ENOSTR = 60, // Device not a stream + 60 => syscall_error(Errno::ENOSTR, syscall, "Device not a stream"), + // ENODATA = 61, // No data available + 61 => syscall_error(Errno::ENODATA, syscall, "No data available"), + // ETIME = 62, // Timer expired + 62 => syscall_error(Errno::ETIME, syscall, "Timer expired"), + // ENOSR = 63, // Out of streams resources + 63 => syscall_error(Errno::ENOSR, syscall, "Out of streams resources"), + // ENONET = 64, // Machine is not on the network + 64 => syscall_error(Errno::ENONET, syscall, "Machine is not on the network"), + // ENOPKG = 65, // Package not installed + 65 => syscall_error(Errno::ENOPKG, syscall, "Package not installed"), + // EREMOTE = 66, // Object is remote + 66 => syscall_error(Errno::EREMOTE, syscall, "Object is remote"), + // ENOLINK = 67, // Link has been severed + 67 => syscall_error(Errno::ENOLINK, syscall, "Link has been severed"), + // EADV = 68, // Advertise error + 68 => syscall_error(Errno::EADV, syscall, "Advertise error"), + // ESRMNT = 69, // Srmount error + 69 => syscall_error(Errno::ESRMNT, syscall, "Srmount error"), + // ECOMM = 70, // Communication error on send + 70 => syscall_error(Errno::ECOMM, syscall, "Communication error on send"), + // EPROTO = 71, // Protocol error + 71 => syscall_error(Errno::EPROTO, syscall, "Protocol error"), + // EMULTIHOP = 72, // Multihop attempted + 72 => syscall_error(Errno::EMULTIHOP, syscall, "Multihop attempted"), + // EDOTDOT = 73, // RFS specific error + 73 => syscall_error(Errno::EDOTDOT, syscall, "RFS specific error"), + // EBADMSG = 74, // Not a data message + 74 => syscall_error(Errno::EBADMSG, syscall, "Not a data message"), + // EOVERFLOW = 75, // Value too large for defined data type + 75 => syscall_error(Errno::EOVERFLOW, syscall, "Value too large for defined data type"), + // ENOTUNIQ = 76, // Name not unique on network + 76 => syscall_error(Errno::ENOTUNIQ, syscall, "Name not unique on network"), + // EBADFD = 77, // File descriptor in bad state + 77 => syscall_error(Errno::EBADFD, syscall, "File descriptor in bad state"), + // EREMCHG = 78, // Remote address changed + 78 => syscall_error(Errno::EREMCHG, syscall, "Remote address changed"), + // ELIBACC = 79, // Can not access a needed shared library + 79 => syscall_error(Errno::ELIBACC, syscall, "Can not access a needed shared library"), + // ELIBBAD = 80, // Accessing a corrupted shared library + 80 => syscall_error(Errno::ELIBBAD, syscall, "Accessing a corrupted shared library"), + // ELIBSCN = 81, // .lib section in a.out corrupted + 81 => syscall_error(Errno::ELIBSCN, syscall, ".lib section in a.out corrupted"), + // ELIBMAX = 82, // Attempting to link in too many shared libraries + 82 => syscall_error(Errno::ELIBMAX, syscall, "Attempting to link in too many shared libraries"), + // ELIBEXEC = 83, // Cannot exec a shared library directly + 83 => syscall_error(Errno::ELIBEXEC, syscall, "Cannot exec a shared library directly"), + // EILSEQ = 84, // Illegal byte sequence + 84 => syscall_error(Errno::EILSEQ, syscall, "Illegal byte sequence"), + // ERESTART = 85, // Interrupted system call should be restarted + 85 => syscall_error(Errno::ERESTART, syscall, "Interrupted system call should be restarted"), + // ESTRPIPE = 86, // Streams pipe error + 86 => syscall_error(Errno::ESTRPIPE, syscall, "Streams pipe error"), + // EUSERS = 87, // Too many users + 87 => syscall_error(Errno::EUSERS, syscall, "Too many users"), + // ENOTSOCK = 88, // Socket operation on non-socket + 88 => syscall_error(Errno::ENOTSOCK, syscall, "Socket operation on non-socket"), + // EDESTADDRREQ = 89, // Destination address required + 89 => syscall_error(Errno::EDESTADDRREQ, syscall, "Destination address required"), + // EMSGSIZE = 90, // Message too long + 90 => syscall_error(Errno::EMSGSIZE, syscall, "Message too long"), + // EPROTOTYPE = 91, // Protocol wrong type for socket + 91 => syscall_error(Errno::EPROTOTYPE, syscall, "Protocol wrong type for socket"), + // ENOPROTOOPT = 92, // Protocol not available + 92 => syscall_error(Errno::ENOPROTOOPT, syscall, "Protocol not available"), + // EPROTONOSUPPORT = 93, // Protocol not supported + 93 => syscall_error(Errno::EPROTONOSUPPORT, syscall, "Protocol not supported"), + // ESOCKTNOSUPPORT = 94, // Socket type not supported + 94 => syscall_error(Errno::ESOCKTNOSUPPORT, syscall, "Socket type not supported"), + // EOPNOTSUPP = 95, // Operation not supported on transport endpoint + 95 => syscall_error(Errno::EOPNOTSUPP, syscall, "Operation not supported on transport endpoint"), + // EPFNOSUPPORT = 96, // Protocol family not supported + 96 => syscall_error(Errno::EPFNOSUPPORT, syscall, "Protocol family not supported"), + // EAFNOSUPPORT = 97, // Address family not supported by protocol + 97 => syscall_error(Errno::EAFNOSUPPORT, syscall, "Address family not supported by protocol"), + // EADDRINUSE = 98, // Address already in use + 98 => syscall_error(Errno::EADDRINUSE, syscall, "Address already in use"), + // EADDRNOTAVAIL = 99, // Cannot assign requested address + 99 => syscall_error(Errno::EADDRNOTAVAIL, syscall, "Cannot assign requested address"), + // ENETDOWN = 100, // Network is down + 100 => syscall_error(Errno::ENETDOWN, syscall, "Network is down"), + // ENETUNREACH = 101, // Network is unreachable + 101 => syscall_error(Errno::ENETUNREACH, syscall, "Network is unreachable"), + // ENETRESET = 102, // Network dropped connection because of reset + 102 => syscall_error(Errno::ENETRESET, syscall, "Network dropped connection because of reset"), + // ECONNABORTED = 103, // Software caused connection abort + 103 => syscall_error(Errno::ECONNABORTED, syscall, "Software caused connection abort"), + // ECONNRESET = 104, // Connection reset by peer + 104 => syscall_error(Errno::ECONNRESET, syscall, "Connection reset by peer"), + // ENOBUFS = 105, // No buffer space available + 105 => syscall_error(Errno::ENOBUFS, syscall, "No buffer space available"), + // EISCONN = 106, // Transport endpoint is already connected + 106 => syscall_error(Errno::EISCONN, syscall, "Transport endpoint is already connected"), + // ENOTCONN = 107, // Transport endpoint is not connected + 107 => syscall_error(Errno::ENOTCONN, syscall, "Transport endpoint is not connected"), + // ESHUTDOWN = 108, // Cannot send after transport endpoint shutdown + 108 => syscall_error(Errno::ESHUTDOWN, syscall, "Cannot send after transport endpoint shutdown"), + // ETOOMANYREFS = 109, // Too many references cannot splice + 109 => syscall_error(Errno::ETOOMANYREFS, syscall, "Too many references cannot splice"), + // ETIMEDOUT = 110, // Connection timed out + 110=> syscall_error(Errno::ETIMEDOUT, syscall, "Connection timed out"), + // ECONNREFUSED = 111, // Connection refused + 111 => syscall_error(Errno::ECONNREFUSED, syscall, "Connection refused"), + // EHOSTDOWN = 112, // Host is down + 112 => syscall_error(Errno::EHOSTDOWN, syscall, "Host is down"), + // EHOSTUNREACH = 113, // No route to host + 113 => syscall_error(Errno::EHOSTUNREACH, syscall, "No route to host"), + // EALREADY = 114, // Operation already in progress + 114 => syscall_error(Errno::EALREADY, syscall, "Operation already in progress"), + // EINPROGRESS = 115, // Operation now in progress + 115 => syscall_error(Errno::EINPROGRESS, syscall, "Operation now in progress"), + // ESTALE = 116, // Stale NFS file handle + 116 => syscall_error(Errno::ESTALE, syscall, "Stale NFS file handle"), + // EUCLEAN = 117, // Structure needs cleaning + 117 => syscall_error(Errno::EUCLEAN, syscall, "Structure needs cleaning"), + // ENOTNAM = 118, // Not a XENIX named type file + 118 => syscall_error(Errno::ENOTNAM, syscall, "Not a XENIX named type file"), + // ENAVAIL = 119, // No XENIX semaphores available + 119 => syscall_error(Errno::ENAVAIL, syscall, "No XENIX semaphores available"), + // EISNAM = 120, // Is a named type file + 120 => syscall_error(Errno::EISNAM, syscall, "Is a named type file"), + // EREMOTEIO = 121, // Remote I/O error + 121 => syscall_error(Errno::EREMOTEIO, syscall, "Remote I/O error"), + // EDQUOT = 122, // Quota exceeded + 122 => syscall_error(Errno::EDQUOT, syscall, "Quota exceeded"), + // ENOMEDIUM = 123, // No medium found + 123 => syscall_error(Errno::ENOMEDIUM, syscall, "No medium found"), + // EMEDIUMTYPE = 124, // Wrong medium type + 124 => syscall_error(Errno::EMEDIUMTYPE, syscall, "Wrong medium type"), + // ECANCELED = 125, // Operation Canceled + 125 => syscall_error(Errno::ECANCELED, syscall, "Operation Canceled"), + // ENOKEY = 126, // Required key not available + 126 => syscall_error(Errno::ENOKEY, syscall, "Required key not available"), + // EKEYEXPIRED = 127, // Key has expired + 127 => syscall_error(Errno::EKEYEXPIRED, syscall, "Key has expired"), + // EKEYREVOKED = 128, // Key has been revoked + 128 => syscall_error(Errno::EKEYREVOKED, syscall, "Key has been revoked"), + // EKEYREJECTED = 129, // Key was rejected by service for robust mutexes + 129 => syscall_error(Errno::EKEYREJECTED, syscall, "Key was rejected by service for robust mutexes"), + // EOWNERDEAD = 130, // Owner died + 130 => syscall_error(Errno::EOWNERDEAD, syscall, "Owner died"), + // ENOTRECOVERABLE = 131, // State not recoverable + 131 => syscall_error(Errno::ENOTRECOVERABLE, syscall, "State not recoverable"), + _ => syscall_error(Errno::EINVAL, syscall, "Invalid error code"), + } +} + +pub fn syscall_error(e: Errno, syscall: &str, message: &str) -> i32 { + if *VERBOSE.get().unwrap() > 0 { + let msg = format!("Error in syscall: {} - {:?}: {}", syscall, e, message); + interface::log_to_stderr(&msg); + } + -(e as i32) +} diff --git a/src/interface/file.rs b/src/interface/file.rs new file mode 100644 index 00000000..7edabeef --- /dev/null +++ b/src/interface/file.rs @@ -0,0 +1,481 @@ +// // Author: Nicholas Renner +// // +// // File related interface +// #![allow(dead_code)] + +// use dashmap::DashSet; +use parking_lot::Mutex; +// use std::env; +// pub use std::ffi::CStr as RustCStr; +use std::fs::{self, canonicalize, File, OpenOptions}; +// use std::io::{Read, Seek, SeekFrom, Write}; +pub use std::path::{Component as RustPathComponent, Path as RustPath, PathBuf as RustPathBuf}; +// use std::slice; +use std::sync::Arc; +pub use std::sync::LazyLock as RustLazyGlobal; + +// use crate::interface::errnos::{syscall_error, Errno}; +// use libc::{mmap, mremap, munmap, off64_t, MAP_SHARED, MREMAP_MAYMOVE, PROT_READ, PROT_WRITE}; +use std::convert::TryInto; +// use std::ffi::c_void; +use std::os::unix::io::{AsRawFd, RawFd}; + +// pub fn removefile(filename: String) -> std::io::Result<()> { +// let path: RustPathBuf = [".".to_string(), filename].iter().collect(); + +// let absolute_filename = canonicalize(&path)?; //will return an error if the file does not exist + +// fs::remove_file(absolute_filename)?; + +// Ok(()) +// } + +// pub fn openfile(filename: String, filesize: usize) -> std::io::Result { +// EmulatedFile::new(filename, filesize) +// } + +// pub fn openmetadata(filename: String) -> std::io::Result { +// EmulatedFile::new_metadata(filename) +// } + +// #[derive(Debug)] +// pub struct EmulatedFile { +// filename: String, +// fobj: Option>>, +// filesize: usize, +// } + +// pub fn pathexists(filename: String) -> bool { +// let path: RustPathBuf = [".".to_string(), filename.clone()].iter().collect(); +// path.exists() +// } + +// impl EmulatedFile { +// fn new(filename: String, filesize: usize) -> std::io::Result { +// let f = OpenOptions::new() +// .read(true) +// .write(true) +// .create(true) +// .open(filename.clone()) +// .unwrap(); +// Ok(EmulatedFile { +// filename, +// fobj: Some(Arc::new(Mutex::new(f))), +// filesize, +// }) +// } + +// fn new_metadata(filename: String) -> std::io::Result { +// let f = OpenOptions::new() +// .read(true) +// .write(true) +// .create(true) +// .open(filename.clone()) +// .unwrap(); + +// let filesize = f.metadata()?.len(); + +// Ok(EmulatedFile { +// filename, +// fobj: Some(Arc::new(Mutex::new(f))), +// filesize: filesize as usize, +// }) +// } + +// pub fn close(&self) -> std::io::Result<()> { +// Ok(()) +// } + +// pub fn shrink(&mut self, length: usize) -> std::io::Result<()> { +// if length > self.filesize { +// panic!( +// "Something is wrong. {} is already smaller than length.", +// self.filename +// ); +// } +// match &self.fobj { +// None => panic!("{} is already closed.", self.filename), +// Some(f) => { +// let fobj = f.lock(); +// fobj.set_len(length as u64)?; +// self.filesize = length; +// Ok(()) +// } +// } +// } + +// pub fn fdatasync(&self) -> std::io::Result<()> { +// match &self.fobj { +// None => panic!("{} is already closed.", self.filename), +// Some(f) => { +// let fobj = f.lock(); +// fobj.sync_data()?; +// Ok(()) +// } +// } +// } + +// pub fn fsync(&self) -> std::io::Result<()> { +// match &self.fobj { +// None => panic!("{} is already closed.", self.filename), +// Some(f) => { +// let fobj = f.lock(); +// fobj.sync_all()?; +// Ok(()) +// } +// } +// } + +// pub fn sync_file_range(&self, offset: isize, nbytes: isize, flags: u32) -> i32 { +// let fd = &self.as_fd_handle_raw_int(); +// let valid_flags = libc::SYNC_FILE_RANGE_WAIT_BEFORE +// | libc::SYNC_FILE_RANGE_WRITE +// | libc::SYNC_FILE_RANGE_WAIT_AFTER; +// if !(flags & !valid_flags == 0) { +// return syscall_error( +// Errno::EINVAL, +// "sync_file_range", +// "flags specifies an invalid bit", +// ); +// } +// unsafe { libc::sync_file_range(*fd, offset as off64_t, nbytes as off64_t, flags) } +// } + +// // Read from file into provided C-buffer +// pub fn readat(&self, ptr: *mut u8, length: usize, offset: usize) -> std::io::Result { +// let buf = unsafe { +// assert!(!ptr.is_null()); +// slice::from_raw_parts_mut(ptr, length) +// }; + +// match &self.fobj { +// None => panic!("{} is already closed.", self.filename), +// Some(f) => { +// let mut fobj = f.lock(); +// if offset > self.filesize { +// panic!("Seek offset extends past the EOF!"); +// } +// fobj.seek(SeekFrom::Start(offset as u64))?; +// let bytes_read = fobj.read(buf)?; +// Ok(bytes_read) +// } +// } +// } + +// // Write to file from provided C-buffer +// pub fn writeat( +// &mut self, +// ptr: *const u8, +// length: usize, +// offset: usize, +// ) -> std::io::Result { +// let bytes_written; + +// let buf = unsafe { +// assert!(!ptr.is_null()); +// slice::from_raw_parts(ptr, length) +// }; + +// match &self.fobj { +// None => panic!("{} is already closed.", self.filename), +// Some(f) => { +// let mut fobj = f.lock(); +// if offset > self.filesize { +// panic!("Seek offset extends past the EOF!"); +// } +// fobj.seek(SeekFrom::Start(offset as u64))?; +// bytes_written = fobj.write(buf)?; +// } +// } + +// if offset + length > self.filesize { +// self.filesize = offset + length; +// } + +// Ok(bytes_written) +// } + +// // Reads entire file into bytes +// pub fn readfile_to_new_bytes(&self) -> std::io::Result> { +// match &self.fobj { +// None => panic!("{} is already closed.", self.filename), +// Some(f) => { +// let mut stringbuf = Vec::new(); +// let mut fobj = f.lock(); +// fobj.read_to_end(&mut stringbuf)?; +// Ok(stringbuf) // return new buf string +// } +// } +// } + +// // Write to entire file from provided bytes +// pub fn writefile_from_bytes(&mut self, buf: &[u8]) -> std::io::Result<()> { +// let length = buf.len(); +// let offset = self.filesize; + +// match &self.fobj { +// None => panic!("{} is already closed.", self.filename), +// Some(f) => { +// let mut fobj = f.lock(); +// if offset > self.filesize { +// panic!("Seek offset extends past the EOF!"); +// } +// fobj.seek(SeekFrom::Start(offset as u64))?; +// fobj.write(buf)?; +// } +// } + +// if offset + length > self.filesize { +// self.filesize = offset + length; +// } + +// Ok(()) +// } + +// pub fn zerofill_at(&mut self, offset: usize, count: usize) -> std::io::Result { +// let bytes_written; +// let buf = vec![0; count]; + +// match &self.fobj { +// None => panic!("{} is already closed.", self.filename), +// Some(f) => { +// let mut fobj = f.lock(); +// if offset > self.filesize { +// panic!("Seek offset extends past the EOF!"); +// } +// fobj.seek(SeekFrom::Start(offset as u64))?; +// bytes_written = fobj.write(buf.as_slice())?; +// } +// } + +// if offset + count > self.filesize { +// self.filesize = offset + count; +// } + +// Ok(bytes_written) +// } + +// //gets the raw fd handle (integer) from a rust fileobject +// pub fn as_fd_handle_raw_int(&self) -> i32 { +// if let Some(wrapped_barefile) = &self.fobj { +// wrapped_barefile.lock().as_raw_fd() as i32 +// } else { +// -1 +// } +// } +// } + +// pub const COUNTMAPSIZE: usize = 8; +// pub const MAP_1MB: usize = usize::pow(2, 20); + +// #[derive(Debug)] +// pub struct EmulatedFileMap { +// filename: String, +// fobj: Arc>, +// map: Arc>>>, +// count: usize, +// countmap: Arc>>>, +// mapsize: usize, +// } + +// pub fn mapfilenew(filename: String) -> std::io::Result { +// EmulatedFileMap::new(filename) +// } + +// impl EmulatedFileMap { +// fn new(filename: String) -> std::io::Result { +// let f = OpenOptions::new() +// .read(true) +// .write(true) +// .create(true) +// .open(filename.clone()) +// .unwrap(); + +// let mapsize = MAP_1MB - COUNTMAPSIZE; +// // set the file equal to where were mapping the count and the actual map +// let _newsize = f.set_len((COUNTMAPSIZE + mapsize) as u64).unwrap(); + +// let map: Vec; +// let countmap: Vec; + +// // here were going to map the first 8 bytes of the file as the "count" (amount of bytes written), and then map another 1MB for logging +// unsafe { +// let map_addr = mmap( +// 0 as *mut c_void, +// MAP_1MB, +// PROT_READ | PROT_WRITE, +// MAP_SHARED, +// f.as_raw_fd() as i32, +// 0 as i64, +// ); +// countmap = Vec::::from_raw_parts(map_addr as *mut u8, COUNTMAPSIZE, COUNTMAPSIZE); +// let map_ptr = map_addr as *mut u8; +// map = +// Vec::::from_raw_parts(map_ptr.offset(COUNTMAPSIZE as isize), mapsize, mapsize); +// } + +// Ok(EmulatedFileMap { +// filename, +// fobj: Arc::new(Mutex::new(f)), +// map: Arc::new(Mutex::new(Some(map))), +// count: 0, +// countmap: Arc::new(Mutex::new(Some(countmap))), +// mapsize, +// }) +// } + +// pub fn write_to_map(&mut self, bytes_to_write: &[u8]) -> std::io::Result<()> { +// let writelen = bytes_to_write.len(); + +// // if we're writing past the current map, increase the map another 1MB +// if writelen + self.count > self.mapsize { +// self.extend_map(); +// } + +// let mut mapopt = self.map.lock(); +// let map = mapopt.as_deref_mut().unwrap(); + +// let mapslice = &mut map[self.count..(self.count + writelen)]; +// mapslice.copy_from_slice(bytes_to_write); +// self.count += writelen; + +// // update the bytes written in the map portion +// let mut countmapopt = self.countmap.lock(); +// let countmap = countmapopt.as_deref_mut().unwrap(); +// countmap.copy_from_slice(&self.count.to_be_bytes()); + +// Ok(()) +// } + +// fn extend_map(&mut self) { +// // open count and map to resize mmap, and file to increase file size +// let mut mapopt = self.map.lock(); +// let map = mapopt.take().unwrap(); +// let mut countmapopt = self.countmap.lock(); +// let countmap = countmapopt.take().unwrap(); +// let f = self.fobj.lock(); + +// // add another 1MB to mapsize +// let new_mapsize = self.mapsize + MAP_1MB; +// let _newsize = f.set_len((COUNTMAPSIZE + new_mapsize) as u64).unwrap(); + +// let newmap: Vec; +// let newcountmap: Vec; + +// // destruct count and map and re-map +// unsafe { +// let (old_count_map_addr, countlen, _countcap) = countmap.into_raw_parts(); +// assert_eq!(COUNTMAPSIZE, countlen); +// let (_old_map_addr, len, _cap) = map.into_raw_parts(); +// assert_eq!(self.mapsize, len); +// let map_addr = mremap( +// old_count_map_addr as *mut c_void, +// COUNTMAPSIZE + self.mapsize, +// COUNTMAPSIZE + new_mapsize, +// MREMAP_MAYMOVE, +// ); + +// newcountmap = +// Vec::::from_raw_parts(map_addr as *mut u8, COUNTMAPSIZE, COUNTMAPSIZE); +// let map_ptr = map_addr as *mut u8; +// newmap = Vec::::from_raw_parts( +// map_ptr.offset(COUNTMAPSIZE as isize), +// new_mapsize, +// new_mapsize, +// ); +// } + +// // replace maps +// mapopt.replace(newmap); +// countmapopt.replace(newcountmap); +// self.mapsize = new_mapsize; +// } + +// pub fn close(&self) -> std::io::Result<()> { +// let mut mapopt = self.map.lock(); +// let map = mapopt.take().unwrap(); +// let mut countmapopt = self.countmap.lock(); +// let countmap = countmapopt.take().unwrap(); + +// unsafe { +// let (countmap_addr, countlen, _countcap) = countmap.into_raw_parts(); +// assert_eq!(COUNTMAPSIZE, countlen); +// munmap(countmap_addr as *mut c_void, COUNTMAPSIZE); + +// let (map_addr, len, _cap) = map.into_raw_parts(); +// assert_eq!(self.mapsize, len); +// munmap(map_addr as *mut c_void, self.mapsize); +// } + +// Ok(()) +// } +// } + +#[derive(Debug)] +pub struct ShmFile { + fobj: Arc>, + key: i32, + size: usize, +} + +pub fn new_shm_backing(key: i32, size: usize) -> std::io::Result { + ShmFile::new(key, size) +} + +// Mimic shared memory in Linux by creating a file backing and truncating it to the segment size +// We can then safely unlink the file while still holding a descriptor to that segment, +// which we can use to map shared across cages. +impl ShmFile { + fn new(key: i32, size: usize) -> std::io::Result { + // open file "shm-#id" + let filename = format!("{}{}", "shm-", key); + let f = OpenOptions::new() + .read(true) + .write(true) + .create(true) + .open(filename.clone()) + .unwrap(); + // truncate file to size + f.set_len(size as u64)?; + // unlink file + fs::remove_file(filename)?; + let shmfile = ShmFile { + fobj: Arc::new(Mutex::new(f)), + key, + size, + }; + + Ok(shmfile) + } + + //gets the raw fd handle (integer) from a rust fileobject + pub fn as_fd_handle_raw_int(&self) -> i32 { + self.fobj.lock().as_raw_fd() as i32 + } +} + +// convert a series of big endian bytes to a size +pub fn convert_bytes_to_size(bytes_to_write: &[u8]) -> usize { + let sizearray: [u8; 8] = bytes_to_write.try_into().unwrap(); + usize::from_be_bytes(sizearray) +} + +// #[cfg(test)] +// mod tests { +// use super::*; +// use tempfile::NamedTempFile; + +// #[test] +// fn test_path_exists_true() { +// let temp_file = NamedTempFile::new().unwrap(); +// let file_path = temp_file.path().to_str().unwrap().to_string(); +// assert!(pathexists(file_path)); +// } + +// #[test] +// fn test_path_exists_false() { +// // Test that pathexists returns false for a non-existent file +// let non_existent_file = "/tmp/non_existent_file.txt"; +// assert!(!pathexists(non_existent_file.to_string())); +// } +// } diff --git a/src/interface/misc.rs b/src/interface/misc.rs new file mode 100644 index 00000000..5da54e0c --- /dev/null +++ b/src/interface/misc.rs @@ -0,0 +1,633 @@ +// Author: Nicholas Renner +// +// Misc functions for interface +// Random, locks, etc. +#![allow(dead_code)] + +pub use dashmap::{ + mapref::entry::Entry as RustHashEntry, DashMap as RustHashMap, DashSet as RustHashSet, +}; +pub use parking_lot::{ + Condvar, Mutex, RwLock as RustLock, RwLockReadGuard as RustLockReadGuard, + RwLockWriteGuard as RustLockWriteGuard, +}; +use std::cell::RefCell; +pub use std::cmp::{max as rust_max, min as rust_min}; +pub use std::collections::VecDeque as RustDeque; +use std::fs::File; +use std::io::{self, Read, Write}; +use std::str::{from_utf8, Utf8Error}; +pub use std::sync::atomic::{ + AtomicBool as RustAtomicBool, AtomicI32 as RustAtomicI32, AtomicU16 as RustAtomicU16, + AtomicU32 as RustAtomicU32, AtomicU64 as RustAtomicU64, AtomicUsize as RustAtomicUsize, + Ordering as RustAtomicOrdering, +}; +pub use std::sync::Arc as RustRfc; +pub use std::thread::spawn as helper_thread; + +use libc::{mmap, pthread_exit, pthread_kill, pthread_self, sched_yield}; +use std::ffi::c_void; + +pub use serde::{Deserialize as SerdeDeserialize, Serialize as SerdeSerialize}; +pub use serde_cbor::{ + from_slice as serde_deserialize_from_bytes, ser::to_vec_packed as serde_serialize_to_bytes, +}; + +use crate::interface; +use crate::interface::errnos::VERBOSE; +use crate::interface::types::SigsetType; +use crate::safeposix::syscalls::fs_constants::SEM_VALUE_MAX; +use std::sync::LazyLock; +use std::time::Duration; + +pub const MAXCAGEID: i32 = 1024; +const EXIT_SUCCESS: i32 = 0; + +pub static RUSTPOSIX_TESTSUITE: LazyLock = + LazyLock::new(|| RustAtomicBool::new(false)); + +thread_local! { + static TRUSTED_SIGNAL_FLAG: RefCell = RefCell::new(0); +} + +use crate::safeposix::cage::Cage; + +pub static mut CAGE_TABLE: Vec>> = Vec::new(); + +pub fn check_cageid(cageid: u64) { + if cageid >= MAXCAGEID as u64 { + panic!("Cage ID is outside of valid range"); + } +} + +pub fn cagetable_init() { + unsafe { + for _cage in 0..MAXCAGEID { + CAGE_TABLE.push(None); + } + } +} + +pub fn cagetable_insert(cageid: u64, cageobj: Cage) { + check_cageid(cageid); + let _insertret = unsafe { CAGE_TABLE[cageid as usize].insert(RustRfc::new(cageobj)) }; +} + +pub fn cagetable_remove(cageid: u64) { + check_cageid(cageid); + unsafe { CAGE_TABLE[cageid as usize].take() }; +} + +pub fn cagetable_getref(cageid: u64) -> RustRfc { + check_cageid(cageid); + unsafe { CAGE_TABLE[cageid as usize].as_ref().unwrap().clone() } +} + +pub fn cagetable_getref_opt(cageid: u64) -> Option> { + check_cageid(cageid); + unsafe { + match CAGE_TABLE[cageid as usize].as_ref() { + Some(cage) => Some(cage.clone()), + None => None, + } + } +} + +pub fn cagetable_clear() { + let mut exitvec = Vec::new(); + unsafe { + for cage in CAGE_TABLE.iter_mut() { + let cageopt = cage.take(); + if cageopt.is_some() { + exitvec.push(cageopt.unwrap()); + } + } + } + + for cage in exitvec { + cage.exit_syscall(EXIT_SUCCESS); + } +} + +pub fn log_from_ptr(buf: *const u8, length: usize) { + if let Ok(s) = from_utf8(unsafe { std::slice::from_raw_parts(buf, length) }) { + log_to_stdout(s); + } +} + +// Print text to stdout +pub fn log_to_stdout(s: &str) { + print!("{}", s); +} + +pub fn log_verbose(s: &str) { + if *VERBOSE.get().unwrap() > 0 { + log_to_stdout(s); + } +} + +// Print text to stderr +pub fn log_to_stderr(s: &str) { + eprintln!("{}", s); +} + +// Flush contents of stdout +pub fn flush_stdout() { + io::stdout().flush().unwrap(); +} + +pub fn get_errno() -> i32 { + (unsafe { *libc::__errno_location() }) as i32 +} + +// Cancellation functions + +pub fn lind_threadexit() { + unsafe { + pthread_exit(0 as *mut c_void); + } +} + +pub fn lind_threadkill(thread_id: u64, sig: i32) -> i32 { + unsafe { pthread_kill(thread_id, sig) as i32 } +} + +pub fn get_pthreadid() -> u64 { + unsafe { pthread_self() as u64 } +} + +pub fn lind_yield() { + unsafe { + sched_yield(); + } +} + +// this function checks if a thread is killable and returns that state +pub fn check_thread(cageid: u64, tid: u64) -> bool { + let cage = cagetable_getref(cageid); + let killable = *cage.thread_table.get(&tid).unwrap(); + killable +} + +// in-rustposix cancelpoints checks if the thread is killable, +// and if sets killable back to false and kills the thread +pub fn cancelpoint(cageid: u64) { + if RUSTPOSIX_TESTSUITE.load(RustAtomicOrdering::Relaxed) { + return; + } // we don't use this when testing rustposix standalone + + let pthread_id = get_pthreadid(); + if check_thread(cageid, pthread_id) { + let cage = cagetable_getref(cageid); + cage.thread_table.insert(pthread_id, false); + lind_threadexit(); + } +} + +pub fn signalflag_set(value: u64) { + TRUSTED_SIGNAL_FLAG.with(|v| *v.borrow_mut() = value); +} + +pub fn signalflag_get() -> u64 { + TRUSTED_SIGNAL_FLAG.with(|v| *v.borrow()) +} + +pub fn sigcheck() -> bool { + if RUSTPOSIX_TESTSUITE.load(RustAtomicOrdering::Relaxed) { + return false; + } + + let boolptr = signalflag_get() as *const bool; + let sigbool = unsafe { *boolptr }; + + sigbool +} + +pub fn fillrandom(bufptr: *mut u8, count: usize) -> i32 { + let slice = unsafe { std::slice::from_raw_parts_mut(bufptr, count) }; + let mut f = std::fs::OpenOptions::new() + .read(true) + .write(false) + .open("/dev/urandom") + .unwrap(); + f.read(slice).unwrap() as i32 +} +pub fn fillzero(bufptr: *mut u8, count: usize) -> i32 { + let slice = unsafe { std::slice::from_raw_parts_mut(bufptr, count) }; + for i in 0..count { + slice[i] = 0u8; + } + count as i32 +} + +pub fn fill(bufptr: *mut u8, count: usize, values: &Vec) -> i32 { + let slice = unsafe { std::slice::from_raw_parts_mut(bufptr, count) }; + slice.copy_from_slice(&values[..count]); + count as i32 +} + +pub fn copy_fromrustdeque_sized(bufptr: *mut u8, count: usize, vecdeq: &RustDeque) { + let (slice1, slice2) = vecdeq.as_slices(); + if slice1.len() >= count { + unsafe { + std::ptr::copy(slice1.as_ptr(), bufptr, count); + } + } else { + unsafe { + std::ptr::copy(slice1.as_ptr(), bufptr, slice1.len()); + } + unsafe { + std::ptr::copy( + slice2.as_ptr(), + bufptr.wrapping_offset(slice1.len() as isize), + count - slice1.len(), + ); + } + } +} + +pub fn extend_fromptr_sized(bufptr: *const u8, count: usize, vecdeq: &mut RustDeque) { + let byteslice = unsafe { std::slice::from_raw_parts(bufptr, count) }; + vecdeq.extend(byteslice.iter()); +} + +// Wrapper to return a dictionary (hashmap) +pub fn new_hashmap() -> RustHashMap { + RustHashMap::new() +} + +#[cfg(target_os = "macos")] +type CharPtr = *const u8; + +#[cfg(not(target_os = "macos"))] +type CharPtr = *const i8; + +pub unsafe fn charstar_to_ruststr<'a>(cstr: CharPtr) -> Result<&'a str, Utf8Error> { + std::ffi::CStr::from_ptr(cstr as *const _).to_str() //returns a result to be unwrapped later +} + +pub fn libc_mmap(addr: *mut u8, len: usize, prot: i32, flags: i32, fildes: i32, off: i64) -> i32 { + return ((unsafe { mmap(addr as *mut c_void, len, prot, flags, fildes, off) } as i64) + & 0xffffffff) as i32; +} + +// Sigset Operations +// +// sigsetops defined here are different from the ones in glibc. Since the sigset is just a u64 +// bitmask, we can just return the modified version of the sigset instead of changing it in-place. +// It would also avoid any ownership issue and make the code cleaner. + +pub fn lind_sigemptyset() -> SigsetType { + 0 +} + +pub fn lind_sigfillset() -> SigsetType { + u64::MAX +} + +pub fn lind_sigaddset(set: SigsetType, signum: i32) -> SigsetType { + set | (1 << (signum - 1)) +} + +pub fn lind_sigdelset(set: SigsetType, signum: i32) -> SigsetType { + set & !(1 << (signum - 1)) +} + +pub fn lind_sigismember(set: SigsetType, signum: i32) -> bool { + set & (1 << (signum - 1)) != 0 +} + +// Signals +pub fn lind_kill_from_id(cage_id: u64, sig: i32) { + if let Some(cage) = cagetable_getref_opt(cage_id as u64) { + let cage_main_thread_id = cage.main_threadid.load(RustAtomicOrdering::Relaxed); + assert!(cage_main_thread_id != 0); + lind_threadkill(cage_main_thread_id, sig); + } +} + +#[derive(Debug)] +pub struct AdvisoryLock { + //0 signifies unlocked, -1 signifies locked exclusively, positive number signifies that many shared lock holders + advisory_lock: RustRfc>, + advisory_condvar: Condvar, +} + +/* +* AdvisoryLock is used to implement advisory locking for files. +* Specifically, it is used by the flock syscall. +* If works as follows: The underying mutex has a guard value associated with it. +* A guard value of zero indicates that it is unlocked. +* In case an exclusive lock is held, the guard value is set to -1. +* In case a shared lock is held, the guard value is incremented by 1. +*/ +// impl AdvisoryLock { +// pub fn new() -> Self { +// Self { +// advisory_lock: RustRfc::new(Mutex::new(0)), +// advisory_condvar: Condvar::new(), +// } +// } + +// // lock_ex is used to acquire an exclusive lock +// // if the lock cannot be obtained, it waits +// pub fn lock_ex(&self) { +// let mut waitedguard = self.advisory_lock.lock(); +// while *waitedguard != 0 { +// self.advisory_condvar.wait(&mut waitedguard); +// } +// *waitedguard = -1; +// } + +// // lock_sh is used to acquire a shared lock +// // if the lock cannot be obtained, it waits +// pub fn lock_sh(&self) { +// let mut waitedguard = self.advisory_lock.lock(); +// while *waitedguard < 0 { +// self.advisory_condvar.wait(&mut waitedguard); +// } +// *waitedguard += 1; +// } +// // try_lock_ex is used to try to acquire an exclusive lock +// // if the lock cannot be obtained, it returns false +// pub fn try_lock_ex(&self) -> bool { +// if let Some(mut guard) = self.advisory_lock.try_lock() { +// if *guard == 0 { +// *guard = -1; +// return true; +// } +// } +// false +// } +// // try_lock_sh is used to try to acquire a shared lock +// // if the lock cannot be obtained, it returns false +// pub fn try_lock_sh(&self) -> bool { +// if let Some(mut guard) = self.advisory_lock.try_lock() { +// if *guard >= 0 { +// *guard += 1; +// return true; +// } +// } +// false +// } + +// /* +// * unlock is used to release a lock +// * If a shared lock was held(guard value > 0), it decrements the guard value by one +// * if no more shared locks are held (i.e. the guard value is now zero), then it notifies a waiting writer +// * If an exclusive lock was held, it sets the guard value to zero and notifies all waiting readers and writers +// */ +// pub fn unlock(&self) -> bool { +// let mut guard = self.advisory_lock.lock(); + +// // check if a shared lock is held +// if *guard > 0 { +// // release one shared lock by decrementing the guard value +// *guard -= 1; + +// // if no more shared locks are held, notify a waiting writer and return +// // only a writer could be waiting at this point +// if *guard == 0 { +// self.advisory_condvar.notify_one(); +// } +// true +// } else if *guard == -1 { +// // check if an exclusive lock is held +// // release the exclusive lock by setting guard to 0 +// *guard = 0; + +// // notify any waiting reads or writers and return +// self.advisory_condvar.notify_all(); +// true +// } else { +// false +// } +// } +// } + +pub struct RawMutex { + inner: libc::pthread_mutex_t, +} + +impl RawMutex { + pub fn create() -> Result { + let libcret; + let mut retval = Self { + inner: unsafe { std::mem::zeroed() }, + }; + unsafe { + libcret = libc::pthread_mutex_init( + (&mut retval.inner) as *mut libc::pthread_mutex_t, + std::ptr::null(), + ); + } + if libcret < 0 { + Err(libcret) + } else { + Ok(retval) + } + } + + pub fn lock(&self) -> i32 { + unsafe { + libc::pthread_mutex_lock( + (&self.inner) as *const libc::pthread_mutex_t as *mut libc::pthread_mutex_t, + ) + } + } + + pub fn trylock(&self) -> i32 { + unsafe { + libc::pthread_mutex_trylock( + (&self.inner) as *const libc::pthread_mutex_t as *mut libc::pthread_mutex_t, + ) + } + } + + pub fn unlock(&self) -> i32 { + unsafe { + libc::pthread_mutex_unlock( + (&self.inner) as *const libc::pthread_mutex_t as *mut libc::pthread_mutex_t, + ) + } + } +} + +impl std::fmt::Debug for RawMutex { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("") + } +} + +impl Drop for RawMutex { + fn drop(&mut self) { + unsafe { + libc::pthread_mutex_destroy((&mut self.inner) as *mut libc::pthread_mutex_t); + } + } +} + +pub struct RawCondvar { + inner: libc::pthread_cond_t, +} + +impl RawCondvar { + pub fn create() -> Result { + let libcret; + let mut retval = Self { + inner: unsafe { std::mem::zeroed() }, + }; + unsafe { + libcret = libc::pthread_cond_init( + (&mut retval.inner) as *mut libc::pthread_cond_t, + std::ptr::null(), + ); + } + if libcret < 0 { + Err(libcret) + } else { + Ok(retval) + } + } + + pub fn signal(&self) -> i32 { + unsafe { + libc::pthread_cond_signal( + (&self.inner) as *const libc::pthread_cond_t as *mut libc::pthread_cond_t, + ) + } + } + + pub fn broadcast(&self) -> i32 { + unsafe { + libc::pthread_cond_broadcast( + (&self.inner) as *const libc::pthread_cond_t as *mut libc::pthread_cond_t, + ) + } + } + + pub fn wait(&self, mutex: &RawMutex) -> i32 { + unsafe { + libc::pthread_cond_wait( + (&self.inner) as *const libc::pthread_cond_t as *mut libc::pthread_cond_t, + (&mutex.inner) as *const libc::pthread_mutex_t as *mut libc::pthread_mutex_t, + ) + } + } + + pub fn timedwait(&self, mutex: &RawMutex, abs_duration: Duration) -> i32 { + let abstime = libc::timespec { + tv_sec: abs_duration.as_secs() as i64, + tv_nsec: (abs_duration.as_nanos() % 1000000000) as i64, + }; + unsafe { + libc::pthread_cond_timedwait( + (&self.inner) as *const libc::pthread_cond_t as *mut libc::pthread_cond_t, + (&mutex.inner) as *const libc::pthread_mutex_t as *mut libc::pthread_mutex_t, + (&abstime) as *const libc::timespec, + ) + } + } +} + +impl Drop for RawCondvar { + fn drop(&mut self) { + unsafe { + libc::pthread_cond_destroy((&mut self.inner) as *mut libc::pthread_cond_t); + } + } +} + +impl std::fmt::Debug for RawCondvar { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("") + } +} + +/* +* RustSemaphore is the rust version of sem_t +*/ +#[derive(Debug)] +pub struct RustSemaphore { + pub value: Mutex, + pub is_shared: RustAtomicBool, +} + +// Semaphore implementation +// we busy wait on lock if value is 0, otherwise we decrease the value +// unlock will increase value up to SEM_VALUE_MAX +impl RustSemaphore { + pub fn new(value_handle: u32, is_shared: bool) -> Self { + Self { + value: Mutex::new(value_handle), + is_shared: RustAtomicBool::new(is_shared), + } + } + + pub fn lock(&self) { + loop { + // acquire the mutex lock + let mut value = self.value.lock(); + if *value == 0 { + // wait for semaphore to be unlocked by another process/thread + interface::lind_yield(); + } else { + // decrement the value + *value = if *value > 0 { *value - 1 } else { 0 }; + break; + } + } + } + + pub fn unlock(&self) -> bool { + // acquire the mutex lock + let mut value = self.value.lock(); + // check if the maximum allowable value for a semaphore has been reached + if *value < SEM_VALUE_MAX { + // increment the value + *value = *value + 1; + return true; + } else { + return false; + } + } + + pub fn get_value(&self) -> i32 { + // returns the value of the semaphore + *self.value.lock() as i32 + } + + pub fn trylock(&self) -> bool { + // acquire the mutex lock + let mut value = self.value.lock(); + if *value == 0 { + // semaphore is locked by another process/thread + return false; + } else { + // decrement the value + *value = if *value > 0 { *value - 1 } else { 0 }; + return true; + } + } + + pub fn timedlock(&self, timeout: Duration) -> bool { + // start the timer to check for timeout + let start_time = interface::starttimer(); + loop { + // acquire the mutex lock + let mut value = self.value.lock(); + if *value == 0 { + // check if we have timed out + let elapsed_time = interface::readtimer(start_time); + if elapsed_time > timeout { + return false; + } + // if not timed out wait for semaphore to be unlocked by another process/thread + interface::lind_yield(); + } else { + *value = if *value > 0 { *value - 1 } else { 0 }; + return true; + } + } + } +} diff --git a/src/interface/mod.rs b/src/interface/mod.rs new file mode 100644 index 00000000..82171c29 --- /dev/null +++ b/src/interface/mod.rs @@ -0,0 +1,19 @@ +// Author: Nicholas Renner +// +// Module definitions for the SafePOSIX Rust interface +// this interface limits kernel access from Rust to the popular paths as defined in Lock-in-Pop + +mod comm; +pub mod errnos; +mod file; +mod misc; +mod pipe; +mod timer; +pub mod types; +pub use comm::*; +pub use errnos::*; +pub use file::*; +pub use misc::*; +pub use pipe::*; +pub use timer::*; +pub use types::*; diff --git a/src/interface/pipe.rs b/src/interface/pipe.rs new file mode 100644 index 00000000..ae76c4d5 --- /dev/null +++ b/src/interface/pipe.rs @@ -0,0 +1,200 @@ +// // Author: Nicholas Renner +// // +// // Pipes for SafePOSIX based on Lock-Free Circular Buffer + +// #![allow(dead_code)] +// use crate::interface; +// use crate::interface::errnos::{syscall_error, Errno}; + +// use parking_lot::Mutex; +// use ringbuf::{Consumer, Producer, RingBuffer}; +// use std::cmp::min; +// use std::fmt; +// use std::slice; +// use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; +// use std::sync::Arc; + +// const O_RDONLY: i32 = 0o0; +// const O_WRONLY: i32 = 0o1; +// const O_RDWRFLAGS: i32 = 0o3; +// const PAGE_SIZE: usize = 4096; + +// const CANCEL_CHECK_INTERVAL: usize = 1048576; // check to cancel pipe reads every 2^20 iterations + +// pub fn new_pipe(size: usize) -> EmulatedPipe { +// EmulatedPipe::new_with_capacity(size) +// } + +// #[derive(Clone)] +// pub struct EmulatedPipe { +// write_end: Arc>>, +// read_end: Arc>>, +// pub refcount_write: Arc, +// pub refcount_read: Arc, +// eof: Arc, +// size: usize, +// } + +// impl EmulatedPipe { +// pub fn new_with_capacity(size: usize) -> EmulatedPipe { +// let rb = RingBuffer::::new(size); +// let (prod, cons) = rb.split(); +// EmulatedPipe { +// write_end: Arc::new(Mutex::new(prod)), +// read_end: Arc::new(Mutex::new(cons)), +// refcount_write: Arc::new(AtomicU32::new(1)), +// refcount_read: Arc::new(AtomicU32::new(1)), +// eof: Arc::new(AtomicBool::new(false)), +// size: size, +// } +// } + +// pub fn set_eof(&self) { +// self.eof.store(true, Ordering::Relaxed); +// } + +// pub fn get_write_ref(&self) -> u32 { +// self.refcount_write.load(Ordering::Relaxed) +// } + +// pub fn get_read_ref(&self) -> u32 { +// self.refcount_read.load(Ordering::Relaxed) +// } + +// pub fn incr_ref(&self, flags: i32) { +// if (flags & O_RDWRFLAGS) == O_RDONLY { +// self.refcount_read.fetch_add(1, Ordering::Relaxed); +// } +// if (flags & O_RDWRFLAGS) == O_WRONLY { +// self.refcount_write.fetch_add(1, Ordering::Relaxed); +// } +// } + +// pub fn decr_ref(&self, flags: i32) { +// if (flags & O_RDWRFLAGS) == O_RDONLY { +// self.refcount_read.fetch_sub(1, Ordering::Relaxed); +// } +// if (flags & O_RDWRFLAGS) == O_WRONLY { +// self.refcount_write.fetch_sub(1, Ordering::Relaxed); +// } +// } + +// pub fn check_select_read(&self) -> bool { +// let read_end = self.read_end.lock(); +// let pipe_space = read_end.len(); + +// if (pipe_space > 0) || self.eof.load(Ordering::SeqCst) { +// return true; +// } else { +// return false; +// } +// } +// pub fn check_select_write(&self) -> bool { +// let write_end = self.write_end.lock(); +// let pipe_space = write_end.remaining(); + +// return pipe_space != 0; +// } + +// // Write length bytes from pointer into pipe +// pub fn write_to_pipe(&self, ptr: *const u8, length: usize, nonblocking: bool) -> i32 { +// let mut bytes_written = 0; + +// let buf = unsafe { +// assert!(!ptr.is_null()); +// slice::from_raw_parts(ptr, length) +// }; + +// let mut write_end = self.write_end.lock(); + +// let pipe_space = write_end.remaining(); +// if nonblocking && (pipe_space == 0) { +// return syscall_error( +// Errno::EAGAIN, +// "write", +// "there is no data available right now, try again later", +// ); +// } + +// while bytes_written < length { +// if self.get_read_ref() == 0 { +// return syscall_error(Errno::EPIPE, "write", "broken pipe"); +// } // EPIPE, all read ends are closed + +// let remaining = write_end.remaining(); + +// if remaining == 0 { +// interface::lind_yield(); //yield on a full pipe +// continue; +// } +// // we write if the pipe is empty, otherwise we try to limit writes to 4096 bytes (unless whats leftover of this write is < 4096) +// if remaining != self.size +// && (length - bytes_written) > PAGE_SIZE +// && remaining < PAGE_SIZE +// { +// continue; +// }; +// let bytes_to_write = min(length, bytes_written as usize + remaining); +// write_end.push_slice(&buf[bytes_written..bytes_to_write]); +// bytes_written = bytes_to_write; +// } + +// bytes_written as i32 +// } + +// // Read length bytes from the pipe into pointer +// // Will wait for bytes unless pipe is empty and eof is set. +// pub fn read_from_pipe(&self, ptr: *mut u8, length: usize, nonblocking: bool) -> i32 { +// let buf = unsafe { +// assert!(!ptr.is_null()); +// slice::from_raw_parts_mut(ptr, length) +// }; + +// let mut read_end = self.read_end.lock(); +// let mut pipe_space = read_end.len(); +// if nonblocking && (pipe_space == 0) { +// if self.eof.load(Ordering::SeqCst) { +// return 0; +// } +// return syscall_error( +// Errno::EAGAIN, +// "read", +// "there is no data available right now, try again later", +// ); +// } + +// // wait for something to be in the pipe, but break on eof +// // check cancel point after 2^20 cycles just in case +// let mut count = 0; +// while pipe_space == 0 { +// if self.eof.load(Ordering::SeqCst) { +// return 0; +// } + +// if count == CANCEL_CHECK_INTERVAL { +// return -(Errno::EAGAIN as i32); // we've tried enough, return to pipe +// } + +// pipe_space = read_end.len(); +// count = count + 1; +// if pipe_space == 0 { +// interface::lind_yield(); +// } // yield on an empty pipe +// } + +// let bytes_to_read = min(length, pipe_space); +// read_end.pop_slice(&mut buf[0..bytes_to_read]); + +// bytes_to_read as i32 +// } +// } + +// impl fmt::Debug for EmulatedPipe { +// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +// f.debug_struct("EmulatedPipe") +// .field("refcount read", &self.refcount_read) +// .field("refcount write", &self.refcount_write) +// .field("eof", &self.eof) +// .finish() +// } +// } diff --git a/src/interface/timer.rs b/src/interface/timer.rs new file mode 100644 index 00000000..28e94fe3 --- /dev/null +++ b/src/interface/timer.rs @@ -0,0 +1,141 @@ +// Author: Nicholas Renner +// +// Timer functions for Rust interface. +#![allow(dead_code)] + +use std::sync::{Arc, Mutex, MutexGuard}; +use std::thread; +pub use std::time::Duration as RustDuration; +pub use std::time::Instant as RustInstant; +use std::time::SystemTime; + +use crate::interface::lind_kill_from_id; + +pub fn timestamp() -> u64 { + SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs() +} + +// Create a new timer +pub fn starttimer() -> RustInstant { + RustInstant::now() +} + +// Return time since timer was started +pub fn readtimer(now: RustInstant) -> RustDuration { + now.elapsed() +} + +// Sleep function to sleep for specified duration +pub fn sleep(dur: RustDuration) { + thread::sleep(dur); +} + +#[derive(Debug)] +struct _IntervalTimer { + pub cageid: u64, + pub init_instant: RustInstant, // The instant this process is created + + pub start_instant: RustInstant, + pub curr_duration: RustDuration, + pub next_duration: RustDuration, + + pub is_ticking: bool, +} + +#[derive(Clone, Debug)] +pub struct IntervalTimer { + _ac: Arc>, +} + +impl IntervalTimer { + pub fn new(cageid: u64) -> Self { + Self { + _ac: Arc::new(Mutex::new(_IntervalTimer { + cageid: cageid, + init_instant: RustInstant::now(), + start_instant: RustInstant::now(), + curr_duration: RustDuration::ZERO, + next_duration: RustDuration::ZERO, + is_ticking: false, + })), + } + } + + // Similar to getitimer. Returns (current value, next value) + pub fn get_itimer(&self) -> (RustDuration, RustDuration) { + let guard = self._ac.lock().unwrap(); + + (guard.curr_duration, guard.next_duration) + } + + fn _set_itimer( + &self, + guard: &mut MutexGuard<_IntervalTimer>, + curr_duration: RustDuration, + next_duration: RustDuration, + ) { + if curr_duration.is_zero() { + guard.is_ticking = false; + } else { + guard.start_instant = RustInstant::now(); + guard.curr_duration = curr_duration; + guard.next_duration = next_duration; + + if !guard.is_ticking { + guard.is_ticking = true; + + let self_dup = self.clone(); + thread::spawn(move || { + // There is a chance that there'll be two ticking threads running + // at the same time + self_dup.tick(); + }); + } + } + } + + pub fn set_itimer(&self, curr_duration: RustDuration, next_duration: RustDuration) { + let mut guard = self._ac.lock().unwrap(); + self._set_itimer(&mut guard, curr_duration, next_duration); + } + + pub fn tick(&self) { + loop { + { + let mut guard = self._ac.lock().unwrap(); + + if guard.is_ticking { + let remaining_seconds = guard + .curr_duration + .saturating_sub(guard.start_instant.elapsed()); + + if remaining_seconds == RustDuration::ZERO { + lind_kill_from_id(guard.cageid, 14); + + let new_curr_duration = guard.next_duration; + // Repeat the intervals until user cancel it + let new_next_duration = guard.next_duration; + + self._set_itimer(&mut guard, new_curr_duration, new_next_duration); + // Calling self.set_itimer will automatically turn of the timer if + // next_duration is ZERO + } + } else { + break; + } + } + + thread::sleep(RustDuration::from_millis(20)); // One jiffy + } + } + + pub fn clone_with_new_cageid(&self, cageid: u64) -> Self { + let mut guard = self._ac.lock().unwrap(); + guard.cageid = cageid; + + self.clone() + } +} diff --git a/src/interface/types.rs b/src/interface/types.rs new file mode 100644 index 00000000..0cb7ef55 --- /dev/null +++ b/src/interface/types.rs @@ -0,0 +1,1079 @@ +#![allow(dead_code)] +use crate::interface; +use crate::interface::errnos::{syscall_error, Errno}; + +use std::io::{Read, Write}; +use std::io; +use std::ptr::null; +use libc::*; + +const SIZEOF_SOCKADDR: u32 = 16; + +//redefining the FSData struct in this file so that we maintain flow of program +//derive eq attributes for testing whether the structs equal other fsdata structs from stat/fstat +#[derive(Eq, PartialEq)] +#[repr(C)] +// pub struct FSData { +// pub f_type: u64, +// pub f_bsize: i64, +// pub f_blocks: u64, +// pub f_bfree: u64, +// pub f_bavail: u64, +// //total files in the file system -- should be infinite +// pub f_files: u64, +// //free files in the file system -- should be infinite +// pub f_ffiles: u64, +// pub f_fsid: libc::fsid_t, +// //not really a limit for naming, but 254 works +// // pub f_namelen: u64, +// //arbitrary val for blocksize as well +// // pub f_frsize: u64, +// // pub f_spare: [u8; 32], +// } +pub struct FSData { + pub f_type: u64, + pub f_bsize: u64, + pub f_blocks: u64, + pub f_bfree: u64, + pub f_bavail: u64, + //total files in the file system -- should be infinite + pub f_files: u64, + //free files in the file system -- should be infinite + pub f_ffiles: u64, + pub f_fsid: u64, + //not really a limit for naming, but 254 works + pub f_namelen: u64, + //arbitrary val for blocksize as well + pub f_frsize: u64, + pub f_spare: [u8; 32], +} + +//redefining the StatData struct in this file so that we maintain flow of program +//derive eq attributes for testing whether the structs equal other statdata structs from stat/fstat +#[derive(Eq, PartialEq, Default, Debug)] +#[repr(C)] +pub struct StatData { + pub st_dev: u64, + pub st_ino: usize, + pub st_mode: u32, + pub st_nlink: u32, + pub st_uid: u32, + pub st_gid: u32, + pub st_rdev: u64, + pub st_size: usize, + pub st_blksize: i32, + pub st_blocks: u32, + //currently we don't populate or care about the time bits here + pub st_atim: (u64, u64), + pub st_mtim: (u64, u64), + pub st_ctim: (u64, u64), +} + +//R Limit for getrlimit system call +#[repr(C)] +pub struct Rlimit { + pub rlim_cur: u64, + pub rlim_max: u64, +} + +#[derive(Eq, PartialEq, Default, Copy, Clone)] +#[repr(C)] +pub struct PipeArray { + pub readfd: i32, + pub writefd: i32, +} + +#[derive(Eq, PartialEq, Default, Copy, Clone)] +#[repr(C)] +pub struct SockPair { + pub sock1: i32, + pub sock2: i32, +} + +//EPOLL +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub struct EpollEvent { + pub events: u32, + pub fd: i32, //in native this is a union which could be one of a number of things + //however, we only support EPOLL_CTL subcommands which take the fd +} + +#[derive(Debug, Default)] +#[repr(C)] +pub struct PollStruct { + pub fd: i32, + pub events: i16, + pub revents: i16, +} + +#[repr(C)] +pub struct SockaddrDummy { + pub sa_family: u16, + pub _sa_data: [u16; 14], +} + +#[repr(C)] +pub struct TimeVal { + pub tv_sec: i64, + pub tv_usec: i64, +} + +#[repr(C)] +pub struct ITimerVal { + pub it_interval: TimeVal, + pub it_value: TimeVal, +} + +#[repr(C)] +pub struct TimeSpec { + pub tv_sec: i64, + pub tv_nsec: i64, +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub union IoctlPtrUnion { + pub int_ptr: *mut i32, + pub c_char_ptr: *mut u8, //Right now, we do not support passing struct pointers to ioctl as the related call are not implemented +} + +#[derive(Copy, Clone, Default)] +#[repr(C)] +pub struct IpcPermStruct { + pub __key: i32, + pub uid: u32, + pub gid: u32, + pub cuid: u32, + pub cgid: u32, + pub mode: u16, + pub __pad1: u16, + pub __seq: u16, + pub __pad2: u16, + pub __unused1: u32, + pub __unused2: u32, +} + +#[derive(Copy, Clone, Default)] +#[repr(C)] +pub struct ShmidsStruct { + pub shm_perm: IpcPermStruct, + pub shm_segsz: u32, + pub shm_atime: isize, + pub shm_dtime: isize, + pub shm_ctime: isize, + pub shm_cpid: u32, + pub shm_lpid: u32, + pub shm_nattch: u32, +} + +pub type SigsetType = u64; + +pub type IovecStruct = libc::iovec; + +#[derive(Copy, Clone, Debug, Default)] +#[repr(C)] +pub struct SigactionStruct { + pub sa_handler: u32, + pub sa_mask: SigsetType, + pub sa_flags: i32, +} + +//redefining the Arg union to maintain the flow of the program +#[derive(Copy, Clone)] +#[repr(C)] +pub union Arg { + pub dispatch_int: i32, + pub dispatch_uint: u32, + pub dispatch_ulong: u64, + pub dispatch_long: i64, + pub dispatch_usize: usize, //For types not specified to be a given length, but often set to word size (i.e. size_t) + pub dispatch_isize: isize, //For types not specified to be a given length, but often set to word size (i.e. off_t) + pub dispatch_cbuf: *const u8, //Typically corresponds to an immutable void* pointer as in write + pub dispatch_mutcbuf: *mut u8, //Typically corresponds to a mutable void* pointer as in read + pub dispatch_cstr: *const i8, //Typically corresponds to a passed in string of type char*, as in open + pub dispatch_cstrarr: *const *const i8, //Typically corresponds to a passed in string array of type char* const[] as in execve + // pub dispatch_rlimitstruct: *mut Rlimit, + // pub dispatch_statdatastruct: *mut stat, + pub dispatch_statdatastruct: *mut StatData, + // pub dispatch_fsdatastruct: *mut statfs, + pub dispatch_fsdatastruct: *mut FSData, + pub dispatch_shmidstruct: *mut ShmidsStruct, + pub dispatch_constsockaddrstruct: *const SockaddrDummy, + pub dispatch_sockaddrstruct: *mut SockaddrDummy, + // pub dispatch_constsockaddrstruct: *const sockaddr, + // pub dispatch_sockaddrstruct: *mut sockaddr, + pub dispatch_socklen_t_ptr: *mut u32, + pub dispatch_intptr: *mut i32, + pub dispatch_pollstructarray: *mut PollStruct, + pub dispatch_epollevent: *mut EpollEvent, + // pub dispatch_epollevent: *mut epoll_event, + // pub dispatch_structtimeval: *mut TimeVal, + pub dispatch_structtimeval: *mut timeval, + pub dispatch_structtimespec_lind: *mut TimeSpec, + pub dispatch_structtimespec: *mut timespec, + pub dispatch_pipearray: *mut PipeArray, + pub dispatch_sockpair: *mut SockPair, + // pub dispatch_ioctlptrunion: IoctlPtrUnion, + pub dispatch_ioctlptrunion: *mut u8, + pub dispatch_sigactionstruct: *mut SigactionStruct, + pub dispatch_constsigactionstruct: *const SigactionStruct, + pub dispatch_sigsett: *mut SigsetType, + pub dispatch_constsigsett: *const SigsetType, + pub dispatch_structitimerval: *mut ITimerVal, + // pub dispatch_structitimerval: *mut itimerval, + pub dispatch_conststructitimerval: *const ITimerVal, + // pub dispatch_conststructitimerval: *const itimerval, + // pub dispatch_fdset: *mut BitSet, + pub dispatch_fdset: *mut libc::fd_set, + // pub dispatch_structsem: *mut sem_t, + // pub dispatch_ifaddrs: *mut ifaddrs, + pub dispatch_constiovecstruct: *const interface::IovecStruct, +} + +use std::mem::size_of; + +// Represents a Dirent struct without the string, as rust has no flexible array member support +#[repr(C, packed(1))] +pub struct ClippedDirent { + pub d_ino: u64, + pub d_off: u64, + pub d_reclen: u16, +} + +pub const CLIPPED_DIRENT_SIZE: u32 = size_of::() as u32; + +pub fn get_int(union_argument: Arg) -> Result { + let data = unsafe { union_argument.dispatch_int }; + let mut type_checker = Arg { dispatch_long: 0 }; + //turn part of the union into 0xffffffff, but, Rust + //does not like just using the hex value so we are forced to use + //a value of -1 + type_checker.dispatch_int = -1; + if (unsafe { union_argument.dispatch_long } & !unsafe { type_checker.dispatch_long }) == 0 { + return Ok(data); + } + return Err(syscall_error( + Errno::EINVAL, + "dispatcher", + "input data not valid", + )); +} + +pub fn get_uint(union_argument: Arg) -> Result { + let data = unsafe { union_argument.dispatch_uint }; + let mut type_checker = Arg { dispatch_ulong: 0 }; + type_checker.dispatch_uint = 0xffffffff; + if (unsafe { union_argument.dispatch_ulong } & !unsafe { type_checker.dispatch_ulong }) == 0 { + return Ok(data); + } + return Err(syscall_error( + Errno::EINVAL, + "dispatcher", + "input data not valid", + )); +} + +pub fn get_long(union_argument: Arg) -> Result { + return Ok(unsafe { union_argument.dispatch_long }); //this should not return error +} + +pub fn get_ulong(union_argument: Arg) -> Result { + return Ok(unsafe { union_argument.dispatch_ulong }); //this should not return error +} + +pub fn get_isize(union_argument: Arg) -> Result { + // also should not return error + return Ok(unsafe { union_argument.dispatch_isize }); +} + +pub fn get_usize(union_argument: Arg) -> Result { + //should not return an error + return Ok(unsafe { union_argument.dispatch_usize }); +} + +pub fn get_cbuf(union_argument: Arg) -> Result<*const u8, i32> { + let data = unsafe { union_argument.dispatch_cbuf }; + if !data.is_null() { + return Ok(data); + } + return Err(syscall_error( + Errno::EFAULT, + "dispatcher", + "input data not valid", + )); +} + +pub fn get_mutcbuf(union_argument: Arg) -> Result<*mut u8, i32> { + let data = unsafe { union_argument.dispatch_mutcbuf }; + if !data.is_null() { + return Ok(data); + } + return Err(syscall_error( + Errno::EFAULT, + "dispatcher", + "input data not valid", + )); +} + +// for the case where the buffer pointer being Null is normal +pub fn get_mutcbuf_null(union_argument: Arg) -> Result, i32> { + let data = unsafe { union_argument.dispatch_mutcbuf }; + if !data.is_null() { + return Ok(Some(data)); + } + return Ok(None); +} + +pub fn get_fdset(union_argument: Arg) -> Result, i32> { + let data: *mut libc::fd_set = unsafe { union_argument.dispatch_fdset }; + if !data.is_null() { + // let internal_fds: &mut interface::FdSet = interface::FdSet::new_from_ptr(data); + let internal_fds = unsafe { &mut *(data as *mut fd_set) }; + return Ok(Some(internal_fds)); + } + return Ok(None); +} + +// pub fn get_fdset(union_argument: Arg) -> Result, i32> { +// let data: *mut libc::fd_set = unsafe { union_argument.dispatch_fdset }; +// if !data.is_null() { +// let internal_fds: &mut interface::FdSet = interface::FdSet::new_from_ptr(data); +// return Ok(Some(internal_fds)); +// } +// return Ok(None); +// } + +// pub fn get_fdset<'a>(union_argument: Arg) -> Result<*mut BitSet, i32> { +// let pointer = unsafe { union_argument.dispatch_fdset }; +// // if !pointer.is_null() { +// // return Ok(pointer); +// // } +// return Ok(pointer); +// } + +pub fn get_cstr<'a>(union_argument: Arg) -> Result<&'a str, i32> { + //first we check that the pointer is not null + //and then we check so that we can get data from the memory + + let pointer = unsafe { union_argument.dispatch_cstr }; + if !pointer.is_null() { + if let Ok(ret_data) = unsafe { interface::charstar_to_ruststr(pointer) } { + return Ok(ret_data); + } else { + return Err(syscall_error( + Errno::EILSEQ, + "dispatcher", + "could not parse input data to a string", + )); + } + } + return Err(syscall_error( + Errno::EFAULT, + "dispatcher", + "input data not valid", + )); +} + +pub fn get_cstrarr<'a>(union_argument: Arg) -> Result, i32> { + //iterate though the pointers in a function and: + // 1: check that the pointer is not null + // 2: push the data from that pointer onto the vector being returned + //once we encounter a null pointer, we know that we have either hit the end of the array or another null pointer in the memory + + let mut pointer = unsafe { union_argument.dispatch_cstrarr }; + let mut data_vector: Vec<&str> = Vec::new(); + + if !pointer.is_null() { + while unsafe { !(*pointer).is_null() } { + if let Ok(character_bytes) = unsafe { interface::charstar_to_ruststr(*pointer) } { + data_vector.push(character_bytes); + pointer = pointer.wrapping_offset(1); + } else { + return Err(syscall_error( + Errno::EILSEQ, + "dispatcher", + "could not parse input data to string", + )); + } + } + return Ok(data_vector); + } + return Err(syscall_error( + Errno::EFAULT, + "dispatcher", + "input data not valid", + )); +} + +// pub fn get_statdatastruct<'a>(union_argument: Arg) -> Result<&'a mut stat, i32> { +// let pointer = unsafe { union_argument.dispatch_statdatastruct }; +// if !pointer.is_null() { +// return Ok(unsafe { &mut *pointer }); +// } +// return Err(syscall_error( +// Errno::EFAULT, +// "dispatcher", +// "input data not valid", +// )); +// } +pub fn get_statdatastruct<'a>(union_argument: Arg) -> Result<&'a mut StatData, i32> { + let pointer = unsafe { union_argument.dispatch_statdatastruct }; + if !pointer.is_null() { + return Ok(unsafe { &mut *pointer }); + } + return Err(syscall_error( + Errno::EFAULT, + "dispatcher", + "input data not valid", + )); +} + +// pub fn get_fsdatastruct<'a>(union_argument: Arg) -> Result<&'a mut statfs, i32> { +// let pointer = unsafe { union_argument.dispatch_fsdatastruct }; +// if !pointer.is_null() { +// return Ok(unsafe { &mut *pointer }); +// } +// return Err(syscall_error( +// Errno::EFAULT, +// "dispatcher", +// "input data not valid", +// )); +// } +pub fn get_fsdatastruct<'a>(union_argument: Arg) -> Result<&'a mut FSData, i32> { + let pointer = unsafe { union_argument.dispatch_fsdatastruct }; + if !pointer.is_null() { + return Ok(unsafe { &mut *pointer }); + } + return Err(syscall_error( + Errno::EFAULT, + "dispatcher", + "input data not valid", + )); +} + +pub fn get_shmidstruct<'a>(union_argument: Arg) -> Result<&'a mut ShmidsStruct, i32> { + let pointer = unsafe { union_argument.dispatch_shmidstruct }; + if !pointer.is_null() { + return Ok(unsafe { &mut *pointer }); + } + return Err(syscall_error( + Errno::EFAULT, + "dispatcher", + "input data not valid", + )); +} + +pub fn get_ioctlptrunion<'a>(union_argument: Arg) -> Result<&'a mut u8, i32> { + let pointer = unsafe { union_argument.dispatch_ioctlptrunion }; + if !pointer.is_null() { + return Ok(unsafe { + &mut *pointer + }); + } + return Err(syscall_error( + Errno::EFAULT, + "dispatcher", + "input data not valid", + )); +} + + +// pub fn get_ioctlptrunion(union_argument: Arg) -> Result { +// return Ok(unsafe { union_argument.dispatch_ioctlptrunion }); +// } + +// pub fn get_ioctl_int<'a>(ptrunion: IoctlPtrUnion) -> Result { +// let pointer = unsafe { ptrunion.int_ptr }; +// if !pointer.is_null() { +// return Ok(unsafe { *pointer }); +// } +// return Err(syscall_error(Errno::EFAULT, "ioctl", "argp is not valid")); +// } + +// pub fn get_ioctl_char<'a>(ptrunion: IoctlPtrUnion) -> Result { +// let pointer = unsafe { ptrunion.c_char_ptr }; +// if !pointer.is_null() { +// return Ok(unsafe { *pointer }); +// } +// return Err(syscall_error(Errno::EFAULT, "ioctl", "argp is not valid")); +// } + +/// Given the vector of tuples produced from getdents_syscall, each of which consists of +/// a ClippedDirent struct and a u8 vector representing the name, and also given the +/// pointer to the base of the buffer to which the getdents structs should be copied, +/// populate said buffer with these getdents structs and the names at the requisite locations +/// +/// We assume a number of things about the tuples that are input: +/// +/// 1. The name in the u8 vec is null terminated +/// 2. After being null terminated it is then padded to the next highest 8 byte boundary +/// 3. After being padded, the last byte of padding is populated with DT_UNKNOWN (0) for now, +/// as the d_type field does not have to be fully implemented for getdents to be POSIX compliant +/// 4. All fields in the clipped dirent, are correctly filled--i.e. d_off has the correct offset +/// of the next struct in the buffer and d_reclen has the length of the struct with the padded name +/// 5. The number of tuples in the vector is such that they all fit in the buffer +/// +/// There is enough information to produce a tuple vector that can satisfy these assumptions well +/// in getdents syscall, and thus all the work to satisfy these assumptions should be done there +// pub fn pack_dirents(dirtuplevec: Vec<(ClippedDirent, Vec)>, baseptr: *mut u8) { +// let mut curptr = baseptr; + +// //for each tuple we write in the ClippedDirent struct, and then the padded name vec +// for dirtuple in dirtuplevec { +// //get pointer to start of next dirent in the buffer as a ClippedDirent pointer +// let curclippedptr = curptr as *mut ClippedDirent; +// //turn that pointer into a rust reference +// let curwrappedptr = unsafe { &mut *curclippedptr }; +// //assign to the data that reference points to with the value of the ClippedDirent from the tuple +// *curwrappedptr = dirtuple.0; + +// //advance pointer by the size of one ClippedDirent, std::mem::size_of should be added into the interface +// curptr = curptr.wrapping_offset(size_of::() as isize); + +// //write, starting from this advanced location, the u8 vec representation of the name +// unsafe { curptr.copy_from(dirtuple.1.as_slice().as_ptr(), dirtuple.1.len()) }; + +// //advance pointer by the size of name, which we assume to be null terminated and padded correctly +// //and thus we are finished with this struct +// curptr = curptr.wrapping_offset(dirtuple.1.len() as isize); +// } +// } + +pub fn get_pipearray<'a>(union_argument: Arg) -> Result<&'a mut PipeArray, i32> { + let pointer = unsafe { union_argument.dispatch_pipearray }; + if !pointer.is_null() { + return Ok(unsafe { &mut *pointer }); + } + return Err(syscall_error( + Errno::EFAULT, + "dispatcher", + "input data not valid", + )); +} + +pub fn get_sockpair<'a>(union_argument: Arg) -> Result<&'a mut SockPair, i32> { + let pointer = unsafe { union_argument.dispatch_sockpair }; + if !pointer.is_null() { + return Ok(unsafe { &mut *pointer }); + } + return Err(syscall_error( + Errno::EFAULT, + "dispatcher", + "input data not valid", + )); +} + +// pub fn get_sockaddr<'a>(union_argument: Arg) -> Result<&'a mut SockaddrDummy, i32> { +// let pointer = unsafe { union_argument.dispatch_sockaddrstruct }; +// if !pointer.is_null() { +// return Ok(unsafe { &mut *pointer }); +// } +// return Err(syscall_error( +// Errno::EFAULT, +// "dispatcher", +// "input data not valid", +// )); +// } + +pub fn get_constsockaddr<'a>(union_argument: Arg) -> Result<&'a SockaddrDummy, i32> { + let pointer = unsafe { union_argument.dispatch_constsockaddrstruct }; + if !pointer.is_null() { + return Ok(unsafe { & *pointer }); + } + return Err(syscall_error( + Errno::EFAULT, + "dispatcher", + "input data not valid", + )); +} + +pub fn get_sockaddr(union_argument: Arg, addrlen: u32) -> Result { + let pointer = unsafe { union_argument.dispatch_constsockaddrstruct }; + if !pointer.is_null() { + let tmpsock = unsafe { &*pointer }; + match tmpsock.sa_family { + /*AF_UNIX*/ + 1 => { + if addrlen < SIZEOF_SOCKADDR + || addrlen > size_of::() as u32 + { + return Err(syscall_error( + Errno::EINVAL, + "dispatcher", + "input length incorrect for family of sockaddr", + )); + } + let unix_ptr = pointer as *const interface::SockaddrUnix; + return Ok(interface::GenSockaddr::Unix(unsafe { *unix_ptr })); + } + /*AF_INET*/ + 2 => { + if addrlen < size_of::() as u32 { + return Err(syscall_error( + Errno::EINVAL, + "dispatcher", + "input length too small for family of sockaddr", + )); + } + let v4_ptr = pointer as *const interface::SockaddrV4; + return Ok(interface::GenSockaddr::V4(unsafe { *v4_ptr })); + } + /*AF_INET6*/ + 30 => { + if addrlen < size_of::() as u32 { + return Err(syscall_error( + Errno::EINVAL, + "dispatcher", + "input length too small for family of sockaddr", + )); + } + let v6_ptr = pointer as *const interface::SockaddrV6; + return Ok(interface::GenSockaddr::V6(unsafe { *v6_ptr })); + } + _ => { + return Err(syscall_error( + Errno::EOPNOTSUPP, + "dispatcher", + "sockaddr family not supported", + )) + } + } + } + return Err(syscall_error( + Errno::EFAULT, + "dispatcher", + "input data not valid", + )); +} + +pub fn set_gensockaddr(union_argument: Arg, len_argument: Arg) -> Result { + let received = unsafe { union_argument.dispatch_sockaddrstruct }; + let received_addrlen = unsafe { len_argument.dispatch_socklen_t_ptr } as u32; + let tmpsock = unsafe { &*received }; + // println!("[Dispatcher set_gen] family: {:?}", tmpsock.sa_family); + // println!("[Dispathcer set_gen] len: {:?}", received_addrlen); + // io::stdout().flush().unwrap(); + match tmpsock.sa_family { + /*AF_UNIX*/ + 1 => { + if received_addrlen < SIZEOF_SOCKADDR + || received_addrlen > size_of::() as u32 + { + return Err(syscall_error( + Errno::EINVAL, + "dispatcher", + "input length incorrect for family of sockaddr", + )); + } + let unix_addr = interface::GenSockaddr::Unix(interface::SockaddrUnix::default()); + return Ok(unix_addr); + } + /*AF_INET*/ + 2 => { + if received_addrlen < size_of::() as u32 { + return Err(syscall_error( + Errno::EINVAL, + "dispatcher", + "input length too small for family of sockaddr", + )); + } + // let v4_ptr = pointer as *const interface::SockaddrV4; + let v4_addr = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + return Ok(v4_addr); + } + /*AF_INET6*/ + 30 => { + if received_addrlen < size_of::() as u32 { + return Err(syscall_error( + Errno::EINVAL, + "dispatcher", + "input length too small for family of sockaddr", + )); + } + // let v6_ptr = pointer as *const interface::SockaddrV6; + let v6_addr = interface::GenSockaddr::V6(interface::SockaddrV6::default()); + return Ok(v6_addr); + } + _ => { + // println!("[Dispatcher] tmpsock.sa_family: {:?}", tmpsock.sa_family); + // io::stdout().flush().unwrap(); + let null_addr = interface::GenSockaddr::Unix(interface::SockaddrUnix::default()); + return Ok(null_addr); + // let v4_addr = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + // return Ok(v4_addr); + // return Err(syscall_error( + // Errno::EOPNOTSUPP, + // "dispatcher", + // "sockaddr family not supported", + // )) + } + } +} + +pub fn copy_out_sockaddr(union_argument: Arg, len_argument: Arg, gensock: interface::GenSockaddr) { + let copyoutaddr = unsafe { union_argument.dispatch_sockaddrstruct } as *mut u8; + let addrlen = unsafe { len_argument.dispatch_socklen_t_ptr }; + assert!(!copyoutaddr.is_null()); + assert!(!addrlen.is_null()); + let initaddrlen = unsafe { *addrlen }; + let mut mutgensock = gensock; + match mutgensock { + interface::GenSockaddr::Unix(ref mut unixa) => { + let unixlen = size_of::() as u32; + + let fullcopylen = interface::rust_min(initaddrlen, unixlen); + // println!("[Dispatcher copy] unixlen: {:?}", unixlen); + // println!("[Dispatcher copy] initaddrlen: {:?}", initaddrlen); + // println!("[Dispatcher copy] fullcopylen: {:?}", fullcopylen); + // io::stdout().flush().unwrap(); + unsafe { + std::ptr::copy( + (unixa) as *mut interface::SockaddrUnix as *mut u8, + copyoutaddr, + // fullcopylen as usize, + initaddrlen as usize, + ) + }; + unsafe { + *addrlen = interface::rust_max(unixlen, fullcopylen); + } + } + + interface::GenSockaddr::V4(ref mut v4a) => { + let v4len = size_of::() as u32; + + let fullcopylen = interface::rust_min(initaddrlen, v4len); + // println!("[Dispatcher copy] v4len: {:?}", v4len); + // println!("[Dispatcher copy] initaddrlen: {:?}", initaddrlen); + // println!("[Dispatcher copy] fullcopylen: {:?}", fullcopylen); + // io::stdout().flush().unwrap(); + unsafe { + std::ptr::copy( + (v4a) as *mut interface::SockaddrV4 as *mut u8, + copyoutaddr, + // fullcopylen as usize, + initaddrlen as usize, + ) + }; + unsafe { + *addrlen = interface::rust_max(v4len, fullcopylen); + } + } + + interface::GenSockaddr::V6(ref mut v6a) => { + let v6len = size_of::() as u32; + + let fullcopylen = interface::rust_min(initaddrlen, v6len); + unsafe { + std::ptr::copy( + (v6a) as *mut interface::SockaddrV6 as *mut u8, + copyoutaddr, + // fullcopylen as usize, + initaddrlen as usize, + ) + }; + unsafe { + *addrlen = interface::rust_max(v6len, fullcopylen); + } + } + } +} + +pub fn get_pollstruct_slice<'a>( + union_argument: Arg, + nfds: usize, +) -> Result<&'a mut [PollStruct], i32> { + let pollstructptr = unsafe { union_argument.dispatch_pollstructarray }; + if !pollstructptr.is_null() { + return Ok(unsafe { std::slice::from_raw_parts_mut(pollstructptr, nfds) }); + } + return Err(syscall_error( + Errno::EFAULT, + "dispatcher", + "input data not valid", + )); +} + +pub fn get_epollevent_slice<'a>( + union_argument: Arg, + nfds: i32, +) -> Result<&'a mut [EpollEvent], i32> { + let epolleventptr = unsafe { union_argument.dispatch_epollevent }; + if !epolleventptr.is_null() { + return Ok(unsafe { std::slice::from_raw_parts_mut(epolleventptr, nfds as usize) }); + } + return Err(syscall_error( + Errno::EFAULT, + "dispatcher", + "input data not valid", + )); +} + +pub fn get_slice_from_string<'a>(union_argument: Arg, len: usize) -> Result<&'a mut [u8], i32> { + let bufptr = unsafe { union_argument.dispatch_mutcbuf }; + if bufptr.is_null() { + return Ok(unsafe { std::slice::from_raw_parts_mut(bufptr, len as usize) }); + } + return Err(syscall_error( + Errno::EFAULT, + "dispatcher", + "input data not valid", + )); +} + +pub fn get_epollevent<'a>(union_argument: Arg) -> Result<&'a mut EpollEvent, i32> { + let epolleventptr = unsafe { union_argument.dispatch_epollevent }; + if !epolleventptr.is_null() { + return Ok(unsafe { &mut *epolleventptr }); + } + return Err(syscall_error( + Errno::EFAULT, + "dispatcher", + "input data not valid", + )); +} +// pub fn get_epollevent<'a>(union_argument: Arg) -> Result<&'a mut epoll_event, i32> { +// let epolleventptr = unsafe { union_argument.dispatch_epollevent }; +// if !epolleventptr.is_null() { +// return Ok(unsafe { &mut *epolleventptr }); +// } +// return Err(syscall_error( +// Errno::EFAULT, +// "dispatcher", +// "input data not valid", +// )); +// } + +pub fn get_socklen_t_ptr(union_argument: Arg) -> Result { + let socklenptr = unsafe { union_argument.dispatch_socklen_t_ptr }; + if !socklenptr.is_null() { + return Ok(unsafe { *socklenptr }); + } + return Err(syscall_error( + Errno::EFAULT, + "dispatcher", + "input data not valid", + )); +} + +//arg checked for nullity beforehand +pub fn get_int_from_intptr(union_argument: Arg) -> i32 { + return unsafe { *union_argument.dispatch_intptr }; +} + +pub fn copy_out_intptr(union_argument: Arg, intval: i32) { + unsafe { + *union_argument.dispatch_intptr = intval; + } +} + +pub fn duration_fromtimeval(union_argument: Arg) -> Result, i32> { + let pointer = unsafe { union_argument.dispatch_structtimeval }; + if !pointer.is_null() { + let times = unsafe { &mut *pointer }; + return Ok(Some(interface::RustDuration::new( + times.tv_sec as u64, + times.tv_usec as u32 * 1000, + ))); + } else { + return Ok(None); + } +} + +pub fn get_timerval<'a>(union_argument: Arg) -> Result<&'a mut timeval, i32> { + let pointer = unsafe { union_argument.dispatch_structtimeval }; + if !pointer.is_null() { + return Ok(unsafe { &mut *pointer }); + } + return Err(syscall_error( + Errno::EFAULT, + "dispatcher", + "input data not valid", + )); +} + +pub fn get_itimerval<'a>(union_argument: Arg) -> Result, i32> { + let pointer = unsafe { union_argument.dispatch_structitimerval }; + if !pointer.is_null() { + Ok(Some(unsafe { &mut *pointer })) + } else { + Ok(None) + } +} +// pub fn get_itimerval<'a>(union_argument: Arg) -> Result<&'a mut itimerval, i32> { +// let pointer = unsafe { union_argument.dispatch_structitimerval }; +// if !pointer.is_null() { +// return Ok(unsafe { &mut *pointer }); +// } +// return Err(syscall_error( +// Errno::EFAULT, +// "dispatcher", +// "input data not valid", +// )); +// } + +pub fn get_constitimerval<'a>(union_argument: Arg) -> Result, i32> { + let pointer = unsafe { union_argument.dispatch_conststructitimerval }; + if !pointer.is_null() { + Ok(Some(unsafe { &*pointer })) + } else { + Ok(None) + } +} + +// pub fn get_constitimerval<'a>(union_argument: Arg) -> Result<&'a itimerval, i32> { +// let pointer = unsafe { union_argument.dispatch_conststructitimerval }; +// if !pointer.is_null() { +// return Ok(unsafe { &*pointer }); +// } +// return Err(syscall_error( +// Errno::EFAULT, +// "dispatcher", +// "input data not valid", +// )); +// } + +pub fn duration_fromtimespec(union_argument: Arg) -> Result { + let pointer = unsafe { union_argument.dispatch_structtimespec_lind }; + if !pointer.is_null() { + let times = unsafe { &mut *pointer }; + if times.tv_nsec < 0 || times.tv_nsec >= 1000000000 { + return Err(syscall_error( + Errno::EINVAL, + "timedwait", + "nanosecond count was negative or more than 1 billion", + )); + } + return Ok(interface::RustDuration::new( + times.tv_sec as u64, + times.tv_nsec as u32 * 1000000000, + )); + } else { + return Err(syscall_error( + Errno::EFAULT, + "timedwait", + "input timespec is null", + )); + } +} + +pub fn get_timespec<'a>(union_argument: Arg) -> Result<&'a timespec, i32> { + let pointer = unsafe { union_argument.dispatch_structtimespec }; + if !pointer.is_null() { + return Ok( unsafe { + &*pointer + }); + } + return Err(syscall_error( + Errno::EFAULT, + "dispatcher", + "input data not valid", + )); +} + +pub fn get_duration_from_millis( + union_argument: Arg, +) -> Result, i32> { + let posstimemillis = get_int(union_argument); + match posstimemillis { + Ok(timemillis) => { + if timemillis >= 0 { + Ok(Some(interface::RustDuration::from_millis( + timemillis as u64, + ))) + } else { + Ok(None) + } + } + Err(e) => Err(e), + } +} + +pub fn arg_nullity(union_argument: &Arg) -> bool { + unsafe { union_argument.dispatch_cbuf }.is_null() +} + +pub fn get_sigactionstruct<'a>( + union_argument: Arg, +) -> Result, i32> { + let pointer = unsafe { union_argument.dispatch_sigactionstruct }; + + if !pointer.is_null() { + Ok(Some(unsafe { &mut *pointer })) + } else { + Ok(None) + } +} + +pub fn get_constsigactionstruct<'a>( + union_argument: Arg, +) -> Result, i32> { + let pointer = unsafe { union_argument.dispatch_constsigactionstruct }; + + if !pointer.is_null() { + Ok(Some(unsafe { &*pointer })) + } else { + Ok(None) + } +} + +pub fn get_sigsett<'a>(union_argument: Arg) -> Result, i32> { + let pointer = unsafe { union_argument.dispatch_sigsett }; + + if !pointer.is_null() { + Ok(Some(unsafe { &mut *pointer })) + } else { + Ok(None) + } +} + +pub fn get_constsigsett<'a>(union_argument: Arg) -> Result, i32> { + let pointer = unsafe { union_argument.dispatch_constsigsett }; + + if !pointer.is_null() { + Ok(Some(unsafe { &*pointer })) + } else { + Ok(None) + } +} + +pub fn get_iovecstruct(union_argument: Arg) -> Result<*const interface::IovecStruct, i32> { + let data = unsafe { union_argument.dispatch_constiovecstruct }; + if !data.is_null() { + return Ok(data); + } + return Err(syscall_error( + Errno::EFAULT, + "dispatcher", + "input data not valid", + )); +} + +// pub fn get_sem<'a>(union_argument: Arg) -> Result<&'a mut sem_t, i32> { +// let pointer = unsafe { union_argument.dispatch_structsem }; +// if !pointer.is_null() { +// return Ok(unsafe { +// &mut *pointer +// }); +// } +// return Err(syscall_error( +// Errno::EFAULT, +// "dispatcher", +// "input data not valid", +// )); +// } + +// pub fn get_ifaddrs<'a>(union_argument: Arg) -> Result<&'a mut ifaddrs, i32> { +// let pointer = unsafe { *union_argument.dispatch_ifaddrs }; +// if !pointer.is_null() { +// return Ok(unsafe { +// &mut *pointer +// }); +// } +// return Err(syscall_error( +// Errno::EFAULT, +// "dispatcher", +// "input data not valid", +// )); +// } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 00000000..f2688510 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,14 @@ +#![feature(lazy_cell)] +#![feature(rustc_private)] //for private crate imports for tests +#![feature(vec_into_raw_parts)] +#![feature(thread_local)] +#![allow(unused_imports)] +#![feature(hash_extract_if)] + +// interface and safeposix are public because otherwise there isn't a great +// way to 'use' them for benchmarking. +pub mod interface; +// mod lib_fs_utils; +pub mod safeposix; +pub mod tests; +pub mod example_grates; diff --git a/src/safeposix/cage.rs b/src/safeposix/cage.rs new file mode 100644 index 00000000..60127ea6 --- /dev/null +++ b/src/safeposix/cage.rs @@ -0,0 +1,212 @@ +#![allow(dead_code)] +use crate::interface; +//going to get the datatypes and errnos from the cage file from now on +pub use crate::interface::errnos::{syscall_error, Errno}; +// pub use crate::interface::types::{ +// Arg, EpollEvent, FSData, IoctlPtrUnion, PipeArray, PollStruct, Rlimit, ShmidsStruct, StatData, +// }; + +pub use crate::interface::types::{ + Arg, EpollEvent, IoctlPtrUnion, PipeArray, PollStruct, +}; + +use super::filesystem::normpath; +// use super::net::SocketHandle; +pub use super::syscalls::fs_constants::*; +pub use super::syscalls::net_constants::*; +pub use super::syscalls::sys_constants::*; + +pub use crate::interface::CAGE_TABLE; + +// #[derive(Debug, Clone)] +// pub enum FileDescriptor { +// File(FileDesc), +// Stream(StreamDesc), +// Socket(SocketDesc), +// Pipe(PipeDesc), +// Epoll(EpollDesc), +// } + +// #[derive(Debug, Clone)] +// pub struct FileDesc { +// pub position: usize, +// pub inode: usize, +// pub flags: i32, +// pub advlock: interface::RustRfc, +// } + +// #[derive(Debug, Clone)] +// pub struct StreamDesc { +// pub position: usize, +// pub stream: i32, //0 for stdin, 1 for stdout, 2 for stderr +// pub flags: i32, +// pub advlock: interface::RustRfc, +// } + +// #[derive(Debug, Clone)] +// pub struct SocketDesc { +// pub flags: i32, +// pub domain: i32, +// pub rawfd: i32, +// pub handle: interface::RustRfc>, +// pub advlock: interface::RustRfc, +// } + +// #[derive(Debug, Clone)] +// pub struct PipeDesc { +// pub pipe: interface::RustRfc, +// pub flags: i32, +// pub advlock: interface::RustRfc, +// } + +// #[derive(Debug, Clone)] +// pub struct EpollDesc { +// pub mode: i32, +// pub registered_fds: interface::RustHashMap, +// pub advlock: interface::RustRfc, +// pub errno: i32, +// pub flags: i32, +// } + +// pub type FdTable = Vec>>>; + +#[derive(Debug)] +pub struct Cage { + pub cageid: u64, + pub cwd: interface::RustLock>, + pub parent: u64, + // pub filedescriptortable: FdTable, + pub cancelstatus: interface::RustAtomicBool, + pub getgid: interface::RustAtomicI32, + pub getuid: interface::RustAtomicI32, + pub getegid: interface::RustAtomicI32, + pub geteuid: interface::RustAtomicI32, + pub rev_shm: interface::Mutex>, //maps addr within cage to shmid + pub mutex_table: interface::RustLock>>>, + pub cv_table: interface::RustLock>>>, + pub sem_table: interface::RustHashMap>, + pub thread_table: interface::RustHashMap, + pub signalhandler: interface::RustHashMap, + pub sigset: interface::RustHashMap, + pub pendingsigset: interface::RustHashMap, + pub main_threadid: interface::RustAtomicU64, + pub interval_timer: interface::IntervalTimer, +} + +impl Cage { + // pub fn get_next_fd( + // &self, + // startfd: Option, + // ) -> ( + // i32, + // Option>>, + // ) { + // let start = match startfd { + // Some(startfd) => startfd, + // None => STARTINGFD, + // }; + + // // let's get the next available fd number. The standard says we need to return the lowest open fd number. + // for fd in start..MAXFD { + // let fdguard = self.filedescriptortable[fd as usize].try_write(); + // if let Some(ref fdopt) = fdguard { + // // we grab the lock here and if there is no occupied cage, we return the fdno and guard while keeping the fd slot locked + // if fdopt.is_none() { + // return (fd, fdguard); + // } + // } + // } + // return ( + // syscall_error( + // Errno::ENFILE, + // "get_next_fd", + // "no available file descriptor number could be found", + // ), + // None, + // ); + // } + + pub fn changedir(&self, newdir: interface::RustPathBuf) { + let newwd = interface::RustRfc::new(normpath(newdir, self)); + let mut cwdbox = self.cwd.write(); + *cwdbox = newwd; + } + + // function to signal all cvs in a cage when forcing exit + pub fn signalcvs(&self) { + let cvtable = self.cv_table.read(); + + for cv_handle in 0..cvtable.len() { + if cvtable[cv_handle as usize].is_some() { + let clonedcv = cvtable[cv_handle as usize].as_ref().unwrap().clone(); + clonedcv.broadcast(); + } + } + } + + pub fn send_pending_signals(&self, sigset: interface::SigsetType, pthreadid: u64) { + for signo in 1..SIGNAL_MAX { + if interface::lind_sigismember(sigset, signo) { + interface::lind_threadkill(pthreadid, signo); + } + } + } + + // pub fn get_filedescriptor( + // &self, + // fd: i32, + // ) -> Result>>, ()> { + // if (fd < 0) || (fd >= MAXFD) { + // Err(()) + // } else { + // Ok(self.filedescriptortable[fd as usize].clone()) + // } + // } +} + +// pub fn init_fdtable() -> FdTable { +// let mut fdtable = Vec::new(); +// // load lower handle stubs +// let stdin = interface::RustRfc::new(interface::RustLock::new(Some(FileDescriptor::Stream( +// StreamDesc { +// position: 0, +// stream: 0, +// flags: O_RDONLY, +// advlock: interface::RustRfc::new(interface::AdvisoryLock::new()), +// }, +// )))); +// let stdout = interface::RustRfc::new(interface::RustLock::new(Some(FileDescriptor::Stream( +// StreamDesc { +// position: 0, +// stream: 1, +// flags: O_WRONLY, +// advlock: interface::RustRfc::new(interface::AdvisoryLock::new()), +// }, +// )))); +// let stderr = interface::RustRfc::new(interface::RustLock::new(Some(FileDescriptor::Stream( +// StreamDesc { +// position: 0, +// stream: 2, +// flags: O_WRONLY, +// advlock: interface::RustRfc::new(interface::AdvisoryLock::new()), +// }, +// )))); +// fdtable.push(stdin); +// fdtable.push(stdout); +// fdtable.push(stderr); + +// for _fd in 3..MAXFD as usize { +// fdtable.push(interface::RustRfc::new(interface::RustLock::new(None))); +// } +// fdtable +// } + +// pub fn create_unix_sockpipes() -> ( +// interface::RustRfc, +// interface::RustRfc, +// ) { +// let pipe1 = interface::RustRfc::new(interface::new_pipe(UDSOCK_CAPACITY)); +// let pipe2 = interface::RustRfc::new(interface::new_pipe(UDSOCK_CAPACITY)); + +// (pipe1, pipe2) +// } diff --git a/src/safeposix/dispatcher.rs b/src/safeposix/dispatcher.rs new file mode 100644 index 00000000..488422c3 --- /dev/null +++ b/src/safeposix/dispatcher.rs @@ -0,0 +1,1262 @@ +#![allow(dead_code)] +#![allow(unused_variables)] +// retreive cage table + +const ACCESS_SYSCALL: i32 = 2; +const UNLINK_SYSCALL: i32 = 4; +const LINK_SYSCALL: i32 = 5; +const RENAME_SYSCALL: i32 = 6; + +const XSTAT_SYSCALL: i32 = 9; +const OPEN_SYSCALL: i32 = 10; +const CLOSE_SYSCALL: i32 = 11; +const READ_SYSCALL: i32 = 12; +const WRITE_SYSCALL: i32 = 13; +const LSEEK_SYSCALL: i32 = 14; +const IOCTL_SYSCALL: i32 = 15; +const TRUNCATE_SYSCALL: i32 = 16; +const FXSTAT_SYSCALL: i32 = 17; +const FTRUNCATE_SYSCALL: i32 = 18; +const FSTATFS_SYSCALL: i32 = 19; +const MMAP_SYSCALL: i32 = 21; +const MUNMAP_SYSCALL: i32 = 22; +const GETDENTS_SYSCALL: i32 = 23; +const DUP_SYSCALL: i32 = 24; +const DUP2_SYSCALL: i32 = 25; +const STATFS_SYSCALL: i32 = 26; +const FCNTL_SYSCALL: i32 = 28; + +const GETPPID_SYSCALL: i32 = 29; +const EXIT_SYSCALL: i32 = 30; +const GETPID_SYSCALL: i32 = 31; + +const BIND_SYSCALL: i32 = 33; +const SEND_SYSCALL: i32 = 34; +const SENDTO_SYSCALL: i32 = 35; +const RECV_SYSCALL: i32 = 36; +const RECVFROM_SYSCALL: i32 = 37; +const CONNECT_SYSCALL: i32 = 38; +const LISTEN_SYSCALL: i32 = 39; +const ACCEPT_SYSCALL: i32 = 40; + +const GETSOCKOPT_SYSCALL: i32 = 43; +const SETSOCKOPT_SYSCALL: i32 = 44; +const SHUTDOWN_SYSCALL: i32 = 45; +const SELECT_SYSCALL: i32 = 46; +const GETCWD_SYSCALL: i32 = 47; +const POLL_SYSCALL: i32 = 48; +const SOCKETPAIR_SYSCALL: i32 = 49; +const GETUID_SYSCALL: i32 = 50; +const GETEUID_SYSCALL: i32 = 51; +const GETGID_SYSCALL: i32 = 52; +const GETEGID_SYSCALL: i32 = 53; +const FLOCK_SYSCALL: i32 = 54; +const EPOLL_CREATE_SYSCALL: i32 = 56; +const EPOLL_CTL_SYSCALL: i32 = 57; +const EPOLL_WAIT_SYSCALL: i32 = 58; + +const SHMGET_SYSCALL: i32 = 62; +const SHMAT_SYSCALL: i32 = 63; +const SHMDT_SYSCALL: i32 = 64; +const SHMCTL_SYSCALL: i32 = 65; + +const PIPE_SYSCALL: i32 = 66; +const PIPE2_SYSCALL: i32 = 67; +const FORK_SYSCALL: i32 = 68; +const EXEC_SYSCALL: i32 = 69; + +const MUTEX_CREATE_SYSCALL: i32 = 70; +const MUTEX_DESTROY_SYSCALL: i32 = 71; +const MUTEX_LOCK_SYSCALL: i32 = 72; +const MUTEX_TRYLOCK_SYSCALL: i32 = 73; +const MUTEX_UNLOCK_SYSCALL: i32 = 74; +const COND_CREATE_SYSCALL: i32 = 75; +const COND_DESTROY_SYSCALL: i32 = 76; +const COND_WAIT_SYSCALL: i32 = 77; +const COND_BROADCAST_SYSCALL: i32 = 78; +const COND_SIGNAL_SYSCALL: i32 = 79; +const COND_TIMEDWAIT_SYSCALL: i32 = 80; + +const SEM_INIT_SYSCALL: i32 = 91; +const SEM_WAIT_SYSCALL: i32 = 92; +const SEM_TRYWAIT_SYSCALL: i32 = 93; +const SEM_TIMEDWAIT_SYSCALL: i32 = 94; +const SEM_POST_SYSCALL: i32 = 95; +const SEM_DESTROY_SYSCALL: i32 = 96; +const SEM_GETVALUE_SYSCALL: i32 = 97; + +const GETHOSTNAME_SYSCALL: i32 = 125; +const PREAD_SYSCALL: i32 = 126; +const PWRITE_SYSCALL: i32 = 127; +const CHDIR_SYSCALL: i32 = 130; +const MKDIR_SYSCALL: i32 = 131; +const RMDIR_SYSCALL: i32 = 132; +const CHMOD_SYSCALL: i32 = 133; +const FCHMOD_SYSCALL: i32 = 134; + +const SOCKET_SYSCALL: i32 = 136; + +const GETSOCKNAME_SYSCALL: i32 = 144; +const GETPEERNAME_SYSCALL: i32 = 145; +const GETIFADDRS_SYSCALL: i32 = 146; + +const SIGACTION_SYSCALL: i32 = 147; +const KILL_SYSCALL: i32 = 148; +const SIGPROCMASK_SYSCALL: i32 = 149; +const SETITIMER_SYSCALL: i32 = 150; + +const FCHDIR_SYSCALL: i32 = 161; +const FSYNC_SYSCALL: i32 = 162; +const FDATASYNC_SYSCALL: i32 = 163; +const SYNC_FILE_RANGE: i32 = 164; + +const WRITEV_SYSCALL: i32 = 170; + +use std::collections::HashMap; + +use super::cage::*; +use super::syscalls::kernel_close; +// use crate::example_grates::vanillaglobal::*; +use crate::example_grates::dashmapvecglobal::*; +// use crate::example_grates::muthashmaxglobal::*; +// use crate::example_grates::dashmaparrayglobal::*; + +use std::io::{Read, Write}; +use std::io; + +// use super::filesystem::{ +// incref_root, load_fs, persist_metadata, remove_domain_sock, FilesystemMetadata, FS_METADATA, +// LOGFILENAME, LOGMAP, +// }; +// use super::net::NET_METADATA; +// use super::shm::SHM_METADATA; +// use super::syscalls::{fs_constants::IPC_STAT, sys_constants::*}; +use crate::{example_grates, interface}; +use crate::interface::errnos::*; +// use crate::lib_fs_utils::{lind_deltree, visit_children}; + +macro_rules! get_onearg { + ($arg: expr) => { + match (move || Ok($arg?))() { + Ok(okval) => okval, + Err(e) => return e, + } + }; +} + +//this macro takes in a syscall invocation name (i.e. cage.fork_syscall), and all of the arguments +//to the syscall. Then it unwraps the arguments, returning the error if any one of them is an error +//value, and returning the value of the function if not. It does this by using the ? operator in +//the body of a closure within the variadic macro +macro_rules! check_and_dispatch { + ( $cage:ident . $func:ident, $($arg:expr),* ) => { + match (|| Ok($cage.$func( $($arg?),* )))() { + Ok(i) => i, Err(i) => i + } + }; +} + +// macro_rules! check_and_dispatch_socketpair { +// ( $func:expr, $cage:ident, $($arg:expr),* ) => { +// match (|| Ok($func( $cage, $($arg?),* )))() { +// Ok(i) => i, Err(i) => i +// } +// }; +// } + +// the following "quick" functions are implemented for research purposes +// to increase I/O performance by bypassing the dispatcher and type checker +#[no_mangle] +pub extern "C" fn quick_write(fd: i32, buf: *const u8, count: usize, cageid: u64) -> i32 { + interface::check_cageid(cageid); + unsafe { + CAGE_TABLE[cageid as usize] + .as_ref() + .unwrap() + .write_syscall(fd, buf, count) + } +} + +#[no_mangle] +pub extern "C" fn quick_read(fd: i32, buf: *mut u8, size: usize, cageid: u64) -> i32 { + interface::check_cageid(cageid); + unsafe { + CAGE_TABLE[cageid as usize] + .as_ref() + .unwrap() + .read_syscall(fd, buf, size) + } +} + +#[no_mangle] +pub extern "C" fn rustposix_thread_init(cageid: u64, signalflag: u64) { + let cage = interface::cagetable_getref(cageid); + let pthreadid = interface::get_pthreadid(); + cage.main_threadid + .store(pthreadid, interface::RustAtomicOrdering::Relaxed); + let inheritedsigset = cage.sigset.remove(&0); // in cases of a forked cage, we've stored the inherited sigset at entry 0 + if inheritedsigset.is_some() { + cage.sigset.insert(pthreadid, inheritedsigset.unwrap().1); + } else { + cage.sigset + .insert(pthreadid, interface::RustAtomicU64::new(0)); + } + + cage.pendingsigset + .insert(pthreadid, interface::RustAtomicU64::new(0)); + interface::signalflag_set(signalflag); +} + +#[no_mangle] +pub extern "C" fn dispatcher( + cageid: u64, + callnum: i32, + arg1: Arg, + arg2: Arg, + arg3: Arg, + arg4: Arg, + arg5: Arg, + arg6: Arg, +) -> i32 { + // need to match based on if cage exists + let cage = interface::cagetable_getref(cageid); + + match callnum { + ACCESS_SYSCALL => { + check_and_dispatch!( + cage.access_syscall, + interface::get_cstr(arg1), + interface::get_int(arg2) + ) + } + UNLINK_SYSCALL => { + check_and_dispatch!(cage.unlink_syscall, interface::get_cstr(arg1)) + } + LINK_SYSCALL => { + check_and_dispatch!( + cage.link_syscall, + interface::get_cstr(arg1), + interface::get_cstr(arg2) + ) + } + CHDIR_SYSCALL => { + check_and_dispatch!(cage.chdir_syscall, interface::get_cstr(arg1)) + } + FSYNC_SYSCALL => { + check_and_dispatch!(cage.fsync_syscall, interface::get_int(arg1)) + } + FDATASYNC_SYSCALL => { + check_and_dispatch!(cage.fdatasync_syscall, interface::get_int(arg1)) + } + SYNC_FILE_RANGE => { + check_and_dispatch!( + cage.sync_file_range_syscall, + interface::get_int(arg1), + interface::get_isize(arg2), + interface::get_isize(arg3), + interface::get_uint(arg4) + ) + } + FCHDIR_SYSCALL => { + check_and_dispatch!(cage.fchdir_syscall, interface::get_int(arg1)) + } + XSTAT_SYSCALL => { + check_and_dispatch!( + cage.stat_syscall, + interface::get_cstr(arg1), + interface::get_statdatastruct(arg2) + ) + } + OPEN_SYSCALL => { + check_and_dispatch!( + cage.open_syscall, + interface::get_cstr(arg1), + interface::get_int(arg2), + interface::get_uint(arg3) + ) + } + READ_SYSCALL => { + check_and_dispatch!( + cage.read_syscall, + interface::get_int(arg1), + interface::get_mutcbuf(arg2), + interface::get_usize(arg3) + ) + } + WRITE_SYSCALL => { + check_and_dispatch!( + cage.write_syscall, + interface::get_int(arg1), + interface::get_mutcbuf(arg2), + interface::get_usize(arg3) + ) + } + CLOSE_SYSCALL => { + check_and_dispatch!(cage.close_syscall, interface::get_int(arg1)) + } + LSEEK_SYSCALL => { + check_and_dispatch!( + cage.lseek_syscall, + interface::get_int(arg1), + interface::get_isize(arg2), + interface::get_int(arg3) + ) + } + FXSTAT_SYSCALL => { + check_and_dispatch!( + cage.fstat_syscall, + interface::get_int(arg1), + interface::get_statdatastruct(arg2) + ) + } + FSTATFS_SYSCALL => { + check_and_dispatch!( + cage.fstatfs_syscall, + interface::get_int(arg1), + interface::get_fsdatastruct(arg2) + ) + } + MMAP_SYSCALL => { + check_and_dispatch!( + cage.mmap_syscall, + interface::get_mutcbuf(arg1), + interface::get_usize(arg2), + interface::get_int(arg3), + interface::get_int(arg4), + interface::get_int(arg5), + interface::get_long(arg6) + ) + } + MUNMAP_SYSCALL => { + check_and_dispatch!( + cage.munmap_syscall, + interface::get_mutcbuf(arg1), + interface::get_usize(arg2) + ) + } + DUP_SYSCALL => { + check_and_dispatch!( + cage.dup_syscall, + interface::get_int(arg1), + Ok::, i32>(None) + ) + } + DUP2_SYSCALL => { + check_and_dispatch!( + cage.dup2_syscall, + interface::get_int(arg1), + interface::get_int(arg2) + ) + } + STATFS_SYSCALL => { + check_and_dispatch!( + cage.statfs_syscall, + interface::get_cstr(arg1), + interface::get_fsdatastruct(arg2) + ) + } + FCNTL_SYSCALL => { + check_and_dispatch!( + cage.fcntl_syscall, + interface::get_int(arg1), + interface::get_int(arg2), + interface::get_int(arg3) + ) + } + IOCTL_SYSCALL => { + check_and_dispatch!( + cage.ioctl_syscall, + interface::get_int(arg1), + interface::get_ulong(arg2), + interface::get_ioctlptrunion(arg3) + ) + } + GETPPID_SYSCALL => { + check_and_dispatch!(cage.getppid_syscall,) + } + GETPID_SYSCALL => { + check_and_dispatch!(cage.getpid_syscall,) + } + SOCKET_SYSCALL => { + check_and_dispatch!( + cage.socket_syscall, + interface::get_int(arg1), + interface::get_int(arg2), + interface::get_int(arg3) + ) + } + BIND_SYSCALL => { + let addrlen = get_onearg!(interface::get_uint(arg3)); + let addr = get_onearg!(interface::get_sockaddr(arg2, addrlen)); + check_and_dispatch!( + cage.bind_syscall, + interface::get_int(arg1), + Ok::<&interface::GenSockaddr, i32>(&addr) + ) + // check_and_dispatch!( + // cage.bind_syscall, + // interface::get_int(arg1), + // interface::get_constsockaddr(arg2), + // interface::get_uint(arg3) + // ) + } + SEND_SYSCALL => { + check_and_dispatch!( + cage.send_syscall, + interface::get_int(arg1), + interface::get_cbuf(arg2), + interface::get_usize(arg3), + interface::get_int(arg4) + ) + } + SENDTO_SYSCALL => { + let addrlen = get_onearg!(interface::get_uint(arg6)); + let addr = get_onearg!(interface::get_sockaddr(arg5, addrlen)); + check_and_dispatch!( + cage.sendto_syscall, + interface::get_int(arg1), + interface::get_cbuf(arg2), + interface::get_usize(arg3), + interface::get_int(arg4), + Ok::<&interface::GenSockaddr, i32>(&addr) + ) + // check_and_dispatch!( + // cage.sendto_syscall, + // interface::get_int(arg1), + // interface::get_cbuf(arg2), + // interface::get_usize(arg3), + // interface::get_int(arg4), + // interface::get_constsockaddr(arg5), + // interface::get_uint(arg6) + // ) + } + RECV_SYSCALL => { + check_and_dispatch!( + cage.recv_syscall, + interface::get_int(arg1), + interface::get_mutcbuf(arg2), + interface::get_usize(arg3), + interface::get_int(arg4) + ) + } + RECVFROM_SYSCALL => { + let nullity1 = interface::arg_nullity(&arg5); + let nullity2 = interface::arg_nullity(&arg6); + + if nullity1 && nullity2 { + check_and_dispatch!( + cage.recvfrom_syscall, + interface::get_int(arg1), + interface::get_mutcbuf(arg2), + interface::get_usize(arg3), + interface::get_int(arg4), + Ok::<&mut Option<&mut interface::GenSockaddr>, i32>(&mut None) + ) + } else if !(nullity1 || nullity2) { + let addrlen = get_onearg!(interface::get_socklen_t_ptr(arg6)); + let mut newsockaddr = interface::GenSockaddr::V4(interface::SockaddrV4::default()); //dummy value, rust would complain if we used an uninitialized value here + let rv = check_and_dispatch!( + cage.recvfrom_syscall, + interface::get_int(arg1), + interface::get_mutcbuf(arg2), + interface::get_usize(arg3), + interface::get_int(arg4), + Ok::<&mut Option<&mut interface::GenSockaddr>, i32>(&mut Some( + &mut newsockaddr + )) + ); + + if rv >= 0 { + interface::copy_out_sockaddr(arg5, arg6, newsockaddr); + } + rv + } else { + syscall_error( + Errno::EINVAL, + "recvfrom", + "exactly one of the last two arguments was zero", + ) + } + // check_and_dispatch!( + // cage.recvfrom_syscall, + // interface::get_int(arg1), + // interface::get_mutcbuf(arg2), + // interface::get_usize(arg3), + // interface::get_int(arg4), + // interface::get_sockaddr(arg5), + // interface::get_uint(arg6) + // ) + } + CONNECT_SYSCALL => { + let addrlen = get_onearg!(interface::get_uint(arg3)); + let addr = get_onearg!(interface::get_sockaddr(arg2, addrlen)); + check_and_dispatch!( + cage.connect_syscall, + interface::get_int(arg1), + Ok::<&interface::GenSockaddr, i32>(&addr) + ) + // check_and_dispatch!( + // cage.connect_syscall, + // interface::get_int(arg1), + // interface::get_constsockaddr(arg2), + // interface::get_uint(arg3) + // ) + } + LISTEN_SYSCALL => { + check_and_dispatch!( + cage.listen_syscall, + interface::get_int(arg1), + interface::get_int(arg2) + ) + } + ACCEPT_SYSCALL => { + /* We don't care the type of addr + * We will directly copy all stuffs in copy_out_sockaddr + */ + // let mut addr = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + + // let nullity1 = interface::arg_nullity(&arg2); + // let nullity2 = interface::arg_nullity(&arg3); + + // if nullity1 && nullity2 { + // check_and_dispatch!( + // cage.accept_syscall, + // interface::get_int(arg1), + // Ok::<&mut Option<&mut interface::GenSockaddr>, i32>(&mut Some( + // &mut addr + // )) + // ) + // } else if !(nullity1 || nullity2) { + // let rv = check_and_dispatch!( + // cage.accept_syscall, + // interface::get_int(arg1), + // Ok::<&mut Option<&mut interface::GenSockaddr>, i32>(&mut Some( + // &mut addr + // )) + // ); + // if rv >= 0 { + // interface::copy_out_sockaddr(arg2, arg3, addr); + // } + // rv + // } else { + // syscall_error( + // Errno::EINVAL, + // "accept", + // "exactly one of the last two arguments was zero", + // ) + // } + + let nullity1 = interface::arg_nullity(&arg2); + let nullity2 = interface::arg_nullity(&arg3); + + if nullity1 && nullity2 { + check_and_dispatch!( + cage.accept_syscall, + interface::get_int(arg1), + Ok::<&mut Option<&mut interface::GenSockaddr>, i32>(&mut None) + ) + } else if !(nullity1 || nullity2) { + let mut addr = interface::set_gensockaddr(arg2, arg3).unwrap(); + let rv = check_and_dispatch!( + cage.accept_syscall, + interface::get_int(arg1), + Ok::<&mut Option<&mut interface::GenSockaddr>, i32>(&mut Some( + &mut addr + )) + ); + if rv >= 0 { + interface::copy_out_sockaddr(arg2, arg3, addr); + } + rv + } else { + syscall_error( + Errno::EINVAL, + "accept", + "exactly one of the last two arguments was zero", + ) + } + } + GETPEERNAME_SYSCALL => { + // let mut addr = interface::GenSockaddr::V4(interface::SockaddrV4::default()); //value doesn't matter + if interface::arg_nullity(&arg2) || interface::arg_nullity(&arg3) { + return syscall_error( + Errno::EINVAL, + "getpeername", + "Either the address or the length were null", + ); + } + let mut addr = interface::set_gensockaddr(arg2, arg3).unwrap(); + let rv = check_and_dispatch!( + cage.getpeername_syscall, + interface::get_int(arg1), + // Ok::<&mut interface::GenSockaddr, i32>(&mut addr) + Ok::<&mut Option<&mut interface::GenSockaddr>, i32>(&mut Some( + &mut addr + )) + ); + + if rv >= 0 { + interface::copy_out_sockaddr(arg2, arg3, addr); + } + rv + // check_and_dispatch!( + // cage.getpeername_syscall, + // interface::get_int(arg1), + // interface::get_sockaddr(arg2) + // ) + } + GETSOCKNAME_SYSCALL => { + // let mut addr = interface::GenSockaddr::V4(interface::SockaddrV4::default()); //value doesn't matter + let mut addr = interface::set_gensockaddr(arg2, arg3).unwrap(); + + let len = interface::get_socklen_t_ptr(arg3).unwrap(); + // println!("[Dispatcher getsockname] socklen: {:?}", len); + // io::stdout().flush().unwrap(); + + if interface::arg_nullity(&arg2) || interface::arg_nullity(&arg3) { + return syscall_error( + Errno::EINVAL, + "getsockname", + "Either the address or the length were null", + ); + } + let rv = check_and_dispatch!( + cage.getsockname_syscall, + interface::get_int(arg1), + // Ok::<&mut interface::GenSockaddr, i32>(&mut addr) + Ok::<&mut Option<&mut interface::GenSockaddr>, i32>(&mut Some( + &mut addr + )) + ); + + if rv >= 0 { + interface::copy_out_sockaddr(arg2, arg3, addr); + } + rv + // check_and_dispatch!( + // cage.getsockname_syscall, + // interface::get_int(arg1), + // interface::get_sockaddr(arg2), + // interface::get_uint(arg3) + // ) + } + GETIFADDRS_SYSCALL => { + check_and_dispatch!( + cage.getifaddrs_syscall, + interface::get_mutcbuf(arg1), + interface::get_usize(arg2) + ) + } + GETSOCKOPT_SYSCALL => { + let mut sockval = 0; + if interface::arg_nullity(&arg4) || interface::arg_nullity(&arg5) { + return syscall_error( + Errno::EFAULT, + "getsockopt", + "Optval or optlen passed as null", + ); + } + if get_onearg!(interface::get_socklen_t_ptr(arg5)) != 4 { + return syscall_error(Errno::EINVAL, "getsockopt", "Invalid optlen passed"); + } + let rv = check_and_dispatch!( + cage.getsockopt_syscall, + interface::get_int(arg1), + interface::get_int(arg2), + interface::get_int(arg3), + Ok::<&mut i32, i32>(&mut sockval) + ); + + if rv >= 0 { + interface::copy_out_intptr(arg4, sockval); + } + //we take it as a given that the length is 4 both in and out + rv + + // let mut optval: Vec = vec![0; 4 as usize]; + // check_and_dispatch!( + // cage.getsockopt_syscall, + // interface::get_int(arg1), + // interface::get_int(arg2), + // interface::get_int(arg3), + // // interface::get_mutcbuf(arg4) + // Ok::<&mut Vec, i32>(&mut optval) + // // interface::get_uint(arg5) + // ) + } + SETSOCKOPT_SYSCALL => { + // let sockval; + // if !interface::arg_nullity(&arg4) { + // if get_onearg!(interface::get_uint(arg5)) != 4 { + // return syscall_error(Errno::EINVAL, "setsockopt", "Invalid optlen passed"); + // } + // sockval = interface::get_int_from_intptr(arg4); + // } else { + // sockval = 0; + // } + // check_and_dispatch!( + // cage.setsockopt_syscall, + // interface::get_int(arg1), + // interface::get_int(arg2), + // interface::get_int(arg3), + // Ok::(sockval) + // ) + check_and_dispatch!( + cage.setsockopt_syscall, + interface::get_int(arg1), + interface::get_int(arg2), + interface::get_int(arg3), + interface::get_mutcbuf(arg4), + interface::get_uint(arg5) + ) + } + SHUTDOWN_SYSCALL => { + check_and_dispatch!( + cage.shutdown_syscall, + interface::get_int(arg1), + interface::get_int(arg2) + ) + } + SELECT_SYSCALL => { + let nfds = get_onearg!(interface::get_int(arg1)); + if nfds < 0 { + //RLIMIT_NOFILE check as well? + return syscall_error( + Errno::EINVAL, + "select", + "The number of fds passed was invalid", + ); + } + check_and_dispatch!( + cage.select_syscall, + interface::get_int(arg1), + interface::get_fdset(arg2), + interface::get_fdset(arg3), + interface::get_fdset(arg4), + // interface::get_timerval(arg5) + interface::duration_fromtimeval(arg5) + ) + } + POLL_SYSCALL => { + let nfds = get_onearg!(interface::get_usize(arg2)); + check_and_dispatch!( + cage.poll_syscall, + interface::get_pollstruct_slice(arg1, nfds), + interface::get_ulong(arg2), + // interface::get_duration_from_millis(arg3) + interface::get_int(arg3) + ) + } + SOCKETPAIR_SYSCALL => { + /* [TODO] - WHY */ + // check_and_dispatch_socketpair!( + // Cage::socketpair_syscall, + // cage, + // interface::get_int(arg1), + // interface::get_int(arg2), + // interface::get_int(arg3), + // interface::get_sockpair(arg4) + // ) + check_and_dispatch!( + cage.socketpair_syscall, + interface::get_int(arg1), + interface::get_int(arg2), + interface::get_int(arg3), + interface::get_sockpair(arg4) + ) + } + EXIT_SYSCALL => { + check_and_dispatch!(cage.exit_syscall, interface::get_int(arg1)) + } + FLOCK_SYSCALL => { + check_and_dispatch!( + cage.flock_syscall, + interface::get_int(arg1), + interface::get_int(arg2) + ) + } + FORK_SYSCALL => { + check_and_dispatch!(cage.fork_syscall, interface::get_ulong(arg1)) + } + EXEC_SYSCALL => { + check_and_dispatch!(cage.exec_syscall, interface::get_ulong(arg1)) + } + GETUID_SYSCALL => { + check_and_dispatch!(cage.getuid_syscall,) + } + GETEUID_SYSCALL => { + check_and_dispatch!(cage.geteuid_syscall,) + } + GETGID_SYSCALL => { + check_and_dispatch!(cage.getgid_syscall,) + } + GETEGID_SYSCALL => { + check_and_dispatch!(cage.getegid_syscall,) + } + PREAD_SYSCALL => { + check_and_dispatch!( + cage.pread_syscall, + interface::get_int(arg1), + interface::get_mutcbuf(arg2), + interface::get_usize(arg3), + interface::get_long(arg4) + ) + } + PWRITE_SYSCALL => { + check_and_dispatch!( + cage.pwrite_syscall, + interface::get_int(arg1), + interface::get_mutcbuf(arg2), + interface::get_usize(arg3), + interface::get_long(arg4) + ) + } + CHMOD_SYSCALL => { + check_and_dispatch!( + cage.chmod_syscall, + interface::get_cstr(arg1), + interface::get_uint(arg2) + ) + } + FCHMOD_SYSCALL => { + check_and_dispatch!( + cage.fchmod_syscall, + interface::get_int(arg1), + interface::get_uint(arg2) + ) + } + RMDIR_SYSCALL => { + check_and_dispatch!(cage.rmdir_syscall, interface::get_cstr(arg1)) + } + RENAME_SYSCALL => { + check_and_dispatch!( + cage.rename_syscall, + interface::get_cstr(arg1), + interface::get_cstr(arg2) + ) + } + EPOLL_CREATE_SYSCALL => { + check_and_dispatch!(cage.epoll_create_syscall, interface::get_int(arg1)) + } + EPOLL_CTL_SYSCALL => { + check_and_dispatch!( + cage.epoll_ctl_syscall, + interface::get_int(arg1), + interface::get_int(arg2), + interface::get_int(arg3), + interface::get_epollevent(arg4) + ) + } + EPOLL_WAIT_SYSCALL => { + let nfds = get_onearg!(interface::get_int(arg3)); + + if nfds < 0 { + //RLIMIT_NOFILE check as well? + return syscall_error( + Errno::EINVAL, + "select", + "The number of fds passed was invalid", + ); + } + + // check_and_dispatch!( + // cage.epoll_wait_syscall, + // interface::get_int(arg1), + // interface::get_epollevent_slice(arg2, nfds), + // Ok::(nfds), + // interface::get_duration_from_millis(arg4) + // ) + check_and_dispatch!( + cage.epoll_wait_syscall, + interface::get_int(arg1), + interface::get_epollevent_slice(arg2, nfds), + interface::get_int(arg3), + interface::get_int(arg4) + ) + } + GETDENTS_SYSCALL => { + check_and_dispatch!( + cage.getdents_syscall, + interface::get_int(arg1), + interface::get_mutcbuf(arg2), + interface::get_uint(arg3) + ) + } + PIPE_SYSCALL => { + check_and_dispatch!(cage.pipe_syscall, interface::get_pipearray(arg1)) + } + PIPE2_SYSCALL => { + check_and_dispatch!( + cage.pipe2_syscall, + interface::get_pipearray(arg1), + interface::get_int(arg2) + ) + } + GETCWD_SYSCALL => { + check_and_dispatch!( + cage.getcwd_syscall, + interface::get_mutcbuf(arg1), + interface::get_uint(arg2) + ) + } + GETHOSTNAME_SYSCALL => { + check_and_dispatch!( + cage.gethostname_syscall, + interface::get_mutcbuf(arg1), + interface::get_isize(arg2) + ) + } + MKDIR_SYSCALL => { + check_and_dispatch!( + cage.mkdir_syscall, + interface::get_cstr(arg1), + interface::get_uint(arg2) + ) + } + SHMGET_SYSCALL => { + check_and_dispatch!( + cage.shmget_syscall, + interface::get_int(arg1), + interface::get_usize(arg2), + interface::get_int(arg3) + ) + } + SHMAT_SYSCALL => { + check_and_dispatch!( + cage.shmat_syscall, + interface::get_int(arg1), + interface::get_mutcbuf(arg2), + interface::get_int(arg3) + ) + } + SHMDT_SYSCALL => { + check_and_dispatch!(cage.shmdt_syscall, interface::get_mutcbuf(arg1)) + } + SHMCTL_SYSCALL => { + let cmd = get_onearg!(interface::get_int(arg2)); + let buf = if cmd == libc::IPC_STAT { + Some(get_onearg!(interface::get_shmidstruct(arg3))) + } else { + None + }; + check_and_dispatch!( + cage.shmctl_syscall, + interface::get_int(arg1), + Ok::(cmd), + Ok::, i32>(buf) + ) + } + MUTEX_CREATE_SYSCALL => { + check_and_dispatch!(cage.mutex_create_syscall,) + } + MUTEX_DESTROY_SYSCALL => { + check_and_dispatch!(cage.mutex_destroy_syscall, interface::get_int(arg1)) + } + MUTEX_LOCK_SYSCALL => { + check_and_dispatch!(cage.mutex_lock_syscall, interface::get_int(arg1)) + } + MUTEX_TRYLOCK_SYSCALL => { + check_and_dispatch!(cage.mutex_trylock_syscall, interface::get_int(arg1)) + } + MUTEX_UNLOCK_SYSCALL => { + check_and_dispatch!(cage.mutex_unlock_syscall, interface::get_int(arg1)) + } + COND_CREATE_SYSCALL => { + check_and_dispatch!(cage.cond_create_syscall,) + } + COND_DESTROY_SYSCALL => { + check_and_dispatch!(cage.cond_destroy_syscall, interface::get_int(arg1)) + } + COND_WAIT_SYSCALL => { + check_and_dispatch!( + cage.cond_wait_syscall, + interface::get_int(arg1), + interface::get_int(arg2) + ) + } + COND_BROADCAST_SYSCALL => { + check_and_dispatch!(cage.cond_broadcast_syscall, interface::get_int(arg1)) + } + COND_SIGNAL_SYSCALL => { + check_and_dispatch!(cage.cond_signal_syscall, interface::get_int(arg1)) + } + COND_TIMEDWAIT_SYSCALL => { + check_and_dispatch!( + cage.cond_timedwait_syscall, + interface::get_int(arg1), + interface::get_int(arg2), + interface::duration_fromtimespec(arg3) + ) + } + TRUNCATE_SYSCALL => { + check_and_dispatch!( + cage.truncate_syscall, + interface::get_cstr(arg1), + interface::get_isize(arg2) + ) + } + FTRUNCATE_SYSCALL => { + check_and_dispatch!( + cage.ftruncate_syscall, + interface::get_int(arg1), + interface::get_isize(arg2) + ) + } + SIGACTION_SYSCALL => { + check_and_dispatch!( + cage.sigaction_syscall, + interface::get_int(arg1), + interface::get_constsigactionstruct(arg2), + interface::get_sigactionstruct(arg3) + ) + } + KILL_SYSCALL => { + check_and_dispatch!( + cage.kill_syscall, + interface::get_int(arg1), + interface::get_int(arg2) + ) + } + SIGPROCMASK_SYSCALL => { + check_and_dispatch!( + cage.sigprocmask_syscall, + interface::get_int(arg1), + interface::get_constsigsett(arg2), + interface::get_sigsett(arg3) + ) + } + SETITIMER_SYSCALL => { + check_and_dispatch!( + cage.setitimer_syscall, + interface::get_int(arg1), + interface::get_constitimerval(arg2), + interface::get_itimerval(arg3) + ) + } + SEM_INIT_SYSCALL => { + check_and_dispatch!( + cage.sem_init_syscall, + interface::get_uint(arg1), + interface::get_int(arg2), + interface::get_uint(arg3) + ) + } + SEM_WAIT_SYSCALL => { + check_and_dispatch!(cage.sem_wait_syscall, interface::get_uint(arg1)) + } + SEM_POST_SYSCALL => { + check_and_dispatch!(cage.sem_post_syscall, interface::get_uint(arg1)) + } + SEM_DESTROY_SYSCALL => { + check_and_dispatch!(cage.sem_destroy_syscall, interface::get_uint(arg1)) + } + SEM_GETVALUE_SYSCALL => { + check_and_dispatch!(cage.sem_getvalue_syscall, interface::get_uint(arg1)) + } + SEM_TRYWAIT_SYSCALL => { + check_and_dispatch!(cage.sem_trywait_syscall, interface::get_uint(arg1)) + } + SEM_TIMEDWAIT_SYSCALL => { + check_and_dispatch!( + cage.sem_timedwait_syscall, + interface::get_uint(arg1), + interface::duration_fromtimespec(arg2) + ) + } + WRITEV_SYSCALL => { + check_and_dispatch!( + cage.writev_syscall, + interface::get_int(arg1), + interface::get_iovecstruct(arg2), + interface::get_int(arg3) + ) + } + + _ => { + //unknown syscall + -1 + } + } +} + +#[no_mangle] +pub extern "C" fn lindcancelinit(cageid: u64) { + let cage = interface::cagetable_getref(cageid); + cage.cancelstatus + .store(true, interface::RustAtomicOrdering::Relaxed); + cage.signalcvs(); +} + +#[no_mangle] +pub extern "C" fn lindsetthreadkill(cageid: u64, pthreadid: u64, kill: bool) { + let cage = interface::cagetable_getref(cageid); + cage.thread_table.insert(pthreadid, kill); + if cage + .main_threadid + .load(interface::RustAtomicOrdering::Relaxed) + == 0 + { + cage.main_threadid.store( + interface::get_pthreadid(), + interface::RustAtomicOrdering::Relaxed, + ); + } +} + +#[no_mangle] +pub extern "C" fn lindcheckthread(cageid: u64, pthreadid: u64) -> bool { + interface::check_thread(cageid, pthreadid) +} + +#[no_mangle] +pub extern "C" fn lindthreadremove(cageid: u64, pthreadid: u64) { + let cage = interface::cagetable_getref(cageid); + cage.thread_table.remove(&pthreadid); +} + +// fn cleartmp(init: bool) { +// let path = "/tmp"; + +// let cage = interface::cagetable_getref(0); +// let mut statdata = StatData::default(); + +// if cage.stat_syscall(path, &mut statdata) == 0 { +// visit_children(&cage, path, None, |childcage, childpath, isdir, _| { +// if isdir { +// lind_deltree(childcage, childpath); +// } else { +// childcage.unlink_syscall(childpath); +// } +// }); +// } else { +// if init == true { +// cage.mkdir_syscall(path, S_IRWXA); +// } +// } +// } + +#[no_mangle] +pub extern "C" fn lindgetsighandler(cageid: u64, signo: i32) -> u32 { + let cage = interface::cagetable_getref(cageid); + let pthreadid = interface::get_pthreadid(); + let sigset = cage.sigset.get(&pthreadid).unwrap(); // these lock sigset dashmaps for concurrency + let pendingset = cage.sigset.get(&pthreadid).unwrap(); + + if !interface::lind_sigismember(sigset.load(interface::RustAtomicOrdering::Relaxed), signo) { + return match cage.signalhandler.get(&signo) { + Some(action_struct) => { + action_struct.sa_handler // if we have a handler and its not blocked return it + } + None => 0, // if we dont have a handler return 0 + }; + } else { + let mutpendingset = sigset.load(interface::RustAtomicOrdering::Relaxed); + sigset.store( + interface::lind_sigaddset(mutpendingset, signo), + interface::RustAtomicOrdering::Relaxed, + ); + 1 // if its blocked add the signal to the pending set and return 1 to indicated it was blocked + // a signal handler cant be located at address 0x1 so this value is fine to return and check + } +} + +#[no_mangle] +pub extern "C" fn lindrustinit(verbosity: isize) { + let _ = interface::VERBOSE.set(verbosity); //assigned to suppress unused result warning + interface::cagetable_init(); + // load_fs(); + // incref_root(); + // incref_root(); + + // fs::chroot("/home/lind/lind_project/src/safeposix-rust/tmp/").unwrap(); + // std::env::set_current_dir("/").unwrap(); + register_close_handlers(NULL_FUNC, kernel_close, NULL_FUNC); + + let utilcage = Cage { + cageid: 0, + cwd: interface::RustLock::new(interface::RustRfc::new(interface::RustPathBuf::from("/"))), + parent: 0, + // filedescriptortable: init_fdtable(), + cancelstatus: interface::RustAtomicBool::new(false), + getgid: interface::RustAtomicI32::new(-1), + getuid: interface::RustAtomicI32::new(-1), + getegid: interface::RustAtomicI32::new(-1), + geteuid: interface::RustAtomicI32::new(-1), + rev_shm: interface::Mutex::new(vec![]), + mutex_table: interface::RustLock::new(vec![]), + cv_table: interface::RustLock::new(vec![]), + sem_table: interface::RustHashMap::new(), + thread_table: interface::RustHashMap::new(), + signalhandler: interface::RustHashMap::new(), + sigset: interface::RustHashMap::new(), + pendingsigset: interface::RustHashMap::new(), + main_threadid: interface::RustAtomicU64::new(0), + interval_timer: interface::IntervalTimer::new(0), + }; + + interface::cagetable_insert(0, utilcage); + // let mut fdtable = FDTABLE; + init_empty_cage(0); + // Set the first 3 fd to STDIN / STDOUT / STDERR + // STDIN + get_specific_virtual_fd(0, 0, 0, false, 0).unwrap(); + // STDOUT + get_specific_virtual_fd(0, 1, 1, false, 0).unwrap(); + // STDERR + get_specific_virtual_fd(0, 2, 2, false, 0).unwrap(); + + //init cage is its own parent + let initcage = Cage { + cageid: 1, + cwd: interface::RustLock::new(interface::RustRfc::new(interface::RustPathBuf::from("/"))), + parent: 1, + // filedescriptortable: init_fdtable(), + cancelstatus: interface::RustAtomicBool::new(false), + getgid: interface::RustAtomicI32::new(-1), + getuid: interface::RustAtomicI32::new(-1), + getegid: interface::RustAtomicI32::new(-1), + geteuid: interface::RustAtomicI32::new(-1), + rev_shm: interface::Mutex::new(vec![]), + mutex_table: interface::RustLock::new(vec![]), + cv_table: interface::RustLock::new(vec![]), + sem_table: interface::RustHashMap::new(), + thread_table: interface::RustHashMap::new(), + signalhandler: interface::RustHashMap::new(), + sigset: interface::RustHashMap::new(), + pendingsigset: interface::RustHashMap::new(), + main_threadid: interface::RustAtomicU64::new(0), + interval_timer: interface::IntervalTimer::new(1), + }; + interface::cagetable_insert(1, initcage); + init_empty_cage(1); + // Set the first 3 fd to STDIN / STDOUT / STDERR + // STDIN + get_specific_virtual_fd(1, 0, 0, false, 0).unwrap(); + // STDOUT + get_specific_virtual_fd(1, 1, 1, false, 0).unwrap(); + // STDERR + get_specific_virtual_fd(1, 2, 2, false, 0).unwrap(); + // make sure /tmp is clean + // cleartmp(true); +} + +#[no_mangle] +pub extern "C" fn lindrustfinalize() { + // remove any open domain socket inodes + // for truepath in NET_METADATA.get_domainsock_paths() { + // remove_domain_sock(truepath); + // } + + // clear /tmp folder + // cleartmp(false); + interface::cagetable_clear(); + // if we get here, persist and delete log + // persist_metadata(&FS_METADATA); + // if interface::pathexists(LOGFILENAME.to_string()) { + // // remove file if it exists, assigning it to nothing to avoid the compiler yelling about unused result + // let mut logobj = LOGMAP.write(); + // let log = logobj.take().unwrap(); + // let _close = log.close().unwrap(); + // let _logremove = interface::removefile(LOGFILENAME.to_string()); + // } +} diff --git a/src/safeposix/filesystem.rs b/src/safeposix/filesystem.rs new file mode 100644 index 00000000..67d1e044 --- /dev/null +++ b/src/safeposix/filesystem.rs @@ -0,0 +1,619 @@ +// Filesystem metadata struct +#![allow(dead_code)] + +// use super::net::NET_METADATA; +// use super::syscalls::fs_constants::*; +// use super::syscalls::sys_constants::*; +use crate::interface; + +use super::cage::Cage; + +// pub const METADATAFILENAME: &str = "lind.metadata"; + +// pub const LOGFILENAME: &str = "lind.md.log"; + +// pub static LOGMAP: interface::RustLazyGlobal< +// interface::RustRfc>>, +// > = interface::RustLazyGlobal::new(|| interface::RustRfc::new(interface::RustLock::new(None))); + +// pub static FS_METADATA: interface::RustLazyGlobal> = +// interface::RustLazyGlobal::new(|| { +// interface::RustRfc::new(FilesystemMetadata::init_fs_metadata()) +// }); //we want to check if fs exists before doing a blank init, but not for now + +// type FileObjectTable = interface::RustHashMap; +// pub static FILEOBJECTTABLE: interface::RustLazyGlobal = +// interface::RustLazyGlobal::new(|| interface::RustHashMap::new()); + +// #[derive(interface::SerdeSerialize, interface::SerdeDeserialize, Debug)] +// pub enum Inode { +// File(GenericInode), +// CharDev(DeviceInode), +// Socket(SocketInode), +// Dir(DirectoryInode), +// } + +// #[derive(interface::SerdeSerialize, interface::SerdeDeserialize, Debug)] +// pub struct GenericInode { +// pub size: usize, +// pub uid: u32, +// pub gid: u32, +// pub mode: u32, +// pub linkcount: u32, +// #[serde(skip)] +// //skips serializing and deserializing field, will populate with u32 default of 0 (refcount should not be persisted) +// pub refcount: u32, +// pub atime: u64, +// pub ctime: u64, +// pub mtime: u64, +// } + +// #[derive(interface::SerdeSerialize, interface::SerdeDeserialize, Debug)] +// pub struct DeviceInode { +// pub size: usize, +// pub uid: u32, +// pub gid: u32, +// pub mode: u32, +// pub linkcount: u32, +// #[serde(skip)] +// //skips serializing and deserializing field, will populate with u32 default of 0 (refcount should not be persisted) +// pub refcount: u32, +// pub atime: u64, +// pub ctime: u64, +// pub mtime: u64, +// pub dev: DevNo, +// } + +// #[derive(interface::SerdeSerialize, interface::SerdeDeserialize, Debug)] +// pub struct SocketInode { +// pub size: usize, +// pub uid: u32, +// pub gid: u32, +// pub mode: u32, +// pub linkcount: u32, +// #[serde(skip)] +// pub refcount: u32, +// pub atime: u64, +// pub ctime: u64, +// pub mtime: u64, +// } + +// #[derive(interface::SerdeSerialize, interface::SerdeDeserialize, Debug)] +// pub struct DirectoryInode { +// pub size: usize, +// pub uid: u32, +// pub gid: u32, +// pub mode: u32, +// pub linkcount: u32, +// #[serde(skip)] +// //skips serializing and deserializing field, will populate with u32 default of 0 (refcount should not be persisted) +// pub refcount: u32, +// pub atime: u64, +// pub ctime: u64, +// pub mtime: u64, +// pub filename_to_inode_dict: interface::RustHashMap, +// } + +// #[derive(interface::SerdeSerialize, interface::SerdeDeserialize, Debug)] +// pub struct FilesystemMetadata { +// pub nextinode: interface::RustAtomicUsize, +// pub dev_id: u64, +// pub inodetable: interface::RustHashMap, +// } + +// pub fn init_filename_to_inode_dict( +// curinode: usize, +// parentinode: usize, +// ) -> interface::RustHashMap { +// let retval = interface::RustHashMap::new(); +// retval.insert(".".to_string(), curinode); +// retval.insert("..".to_string(), parentinode); +// retval +// } + +// impl FilesystemMetadata { +// pub fn blank_fs_init() -> FilesystemMetadata { +// //remove open files? +// let retval = FilesystemMetadata { +// nextinode: interface::RustAtomicUsize::new(STREAMINODE + 1), +// dev_id: 20, +// inodetable: interface::RustHashMap::new(), +// }; +// let time = interface::timestamp(); //We do a real timestamp now +// let dirinode = DirectoryInode { +// size: 0, +// uid: DEFAULT_UID, +// gid: DEFAULT_GID, +// //linkcount is how many entries the directory has (as per linux kernel), . and .. making 2 for the root directory initially, +// //plus one to make sure it can never be removed (can be thought of as mount point link) +// //refcount is how many open file descriptors pointing to the directory exist, 0 as no cages exist yet +// mode: S_IFDIR as u32 | S_IRWXA, +// linkcount: 3, +// refcount: 0, +// atime: time, +// ctime: time, +// mtime: time, +// filename_to_inode_dict: init_filename_to_inode_dict( +// ROOTDIRECTORYINODE, +// ROOTDIRECTORYINODE, +// ), +// }; +// retval +// .inodetable +// .insert(ROOTDIRECTORYINODE, Inode::Dir(dirinode)); + +// retval +// } + +// // Read file, and deserialize CBOR to FS METADATA +// pub fn init_fs_metadata() -> FilesystemMetadata { +// // Read CBOR from file +// if interface::pathexists(METADATAFILENAME.to_string()) { +// let metadata_fileobj = interface::openmetadata(METADATAFILENAME.to_string()).unwrap(); +// let metadatabytes = metadata_fileobj.readfile_to_new_bytes().unwrap(); +// metadata_fileobj.close().unwrap(); + +// // Restore metadata +// interface::serde_deserialize_from_bytes(&metadatabytes).unwrap() +// } else { +// FilesystemMetadata::blank_fs_init() +// } +// } +// } + +// pub fn format_fs() { +// let newmetadata = FilesystemMetadata::blank_fs_init(); +// //Because we keep the metadata as a synclazy, it is not possible to completely wipe it and +// //reinstate something over it in-place. Thus we create a new file system, wipe the old one, and +// //then persist our new one. In order to create the new one, because the FS_METADATA does not +// //point to the same metadata that we are trying to create, we need to manually insert these +// //rather than using system calls. + +// let mut rootinode = newmetadata.inodetable.get_mut(&1).unwrap(); //get root to populate its dict +// if let Inode::Dir(ref mut rootdir) = *rootinode { +// rootdir.filename_to_inode_dict.insert("dev".to_string(), 2); +// rootdir.linkcount += 1; +// } else { +// unreachable!(); +// } +// drop(rootinode); + +// let devchildren = interface::RustHashMap::new(); +// devchildren.insert("..".to_string(), 1); +// devchildren.insert(".".to_string(), 2); +// devchildren.insert("null".to_string(), 3); +// devchildren.insert("zero".to_string(), 4); +// devchildren.insert("urandom".to_string(), 5); +// devchildren.insert("random".to_string(), 6); + +// let tmpchildren = interface::RustHashMap::new(); +// tmpchildren.insert("..".to_string(), 1); +// tmpchildren.insert(".".to_string(), 2); + +// let time = interface::timestamp(); //We do a real timestamp now +// let devdirinode = Inode::Dir(DirectoryInode { +// size: 0, +// uid: DEFAULT_UID, +// gid: DEFAULT_GID, +// mode: (S_IFDIR | 0o755) as u32, +// linkcount: 3 + 4, //3 for ., .., and the parent dir, 4 is one for each child we will create +// refcount: 0, +// atime: time, +// ctime: time, +// mtime: time, +// filename_to_inode_dict: devchildren, +// }); //inode 2 +// let nullinode = Inode::CharDev(DeviceInode { +// size: 0, +// uid: DEFAULT_UID, +// gid: DEFAULT_UID, +// mode: (S_IFCHR | 0o666) as u32, +// linkcount: 1, +// refcount: 0, +// atime: time, +// ctime: time, +// mtime: time, +// dev: DevNo { major: 1, minor: 3 }, +// }); //inode 3 +// let zeroinode = Inode::CharDev(DeviceInode { +// size: 0, +// uid: DEFAULT_UID, +// gid: DEFAULT_UID, +// mode: (S_IFCHR | 0o666) as u32, +// linkcount: 1, +// refcount: 0, +// atime: time, +// ctime: time, +// mtime: time, +// dev: DevNo { major: 1, minor: 5 }, +// }); //inode 4 +// let urandominode = Inode::CharDev(DeviceInode { +// size: 0, +// uid: DEFAULT_UID, +// gid: DEFAULT_UID, +// mode: (S_IFCHR | 0o666) as u32, +// linkcount: 1, +// refcount: 0, +// atime: time, +// ctime: time, +// mtime: time, +// dev: DevNo { major: 1, minor: 9 }, +// }); //inode 5 +// let randominode = Inode::CharDev(DeviceInode { +// size: 0, +// uid: DEFAULT_UID, +// gid: DEFAULT_UID, +// mode: (S_IFCHR | 0o666) as u32, +// linkcount: 1, +// refcount: 0, +// atime: time, +// ctime: time, +// mtime: time, +// dev: DevNo { major: 1, minor: 8 }, +// }); //inode 6 +// let tmpdirinode = Inode::Dir(DirectoryInode { +// size: 0, +// uid: DEFAULT_UID, +// gid: DEFAULT_GID, +// mode: (S_IFDIR | 0o755) as u32, +// linkcount: 3 + 4, +// refcount: 0, +// atime: time, +// ctime: time, +// mtime: time, +// filename_to_inode_dict: tmpchildren, +// }); //inode 7 +// newmetadata +// .nextinode +// .store(8, interface::RustAtomicOrdering::Relaxed); +// newmetadata.inodetable.insert(2, devdirinode); +// newmetadata.inodetable.insert(3, nullinode); +// newmetadata.inodetable.insert(4, zeroinode); +// newmetadata.inodetable.insert(5, urandominode); +// newmetadata.inodetable.insert(6, randominode); +// newmetadata.inodetable.insert(7, tmpdirinode); + +// let _logremove = interface::removefile(LOGFILENAME.to_string()); + +// persist_metadata(&newmetadata); +// } + +// pub fn load_fs() { +// // If the metadata file exists, just close the file for later restore +// // If it doesn't, lets create a new one, load special files, and persist it. +// if interface::pathexists(METADATAFILENAME.to_string()) { +// let metadata_fileobj = interface::openmetadata(METADATAFILENAME.to_string()).unwrap(); +// metadata_fileobj.close().unwrap(); + +// // if we have a log file at this point, we need to sync it with the existing metadata +// if interface::pathexists(LOGFILENAME.to_string()) { +// let log_fileobj = interface::openmetadata(LOGFILENAME.to_string()).unwrap(); +// // read log file and parse count +// let mut logread = log_fileobj.readfile_to_new_bytes().unwrap(); +// let logsize = interface::convert_bytes_to_size(&logread[0..interface::COUNTMAPSIZE]); + +// // create vec of log file bounded by indefinite encoding bytes (0x9F, 0xFF) +// let mut logbytes: Vec = Vec::new(); +// logbytes.push(0x9F); +// logbytes.extend_from_slice( +// &mut logread[interface::COUNTMAPSIZE..(interface::COUNTMAPSIZE + logsize)], +// ); +// logbytes.push(0xFF); +// let mut logvec: Vec<(usize, Option)> = +// interface::serde_deserialize_from_bytes(&logbytes).unwrap(); + +// // drain the vector and deserialize into pairs of inodenum + inodes, +// // if the inode exists, add it, if not, remove it +// // keep track of the largest inodenum we see so we can update the nextinode counter +// let mut max_inodenum = FS_METADATA +// .nextinode +// .load(interface::RustAtomicOrdering::Relaxed); +// for serialpair in logvec.drain(..) { +// let (inodenum, inode) = serialpair; +// match inode { +// Some(inode) => { +// max_inodenum = interface::rust_max(max_inodenum, inodenum); +// FS_METADATA.inodetable.insert(inodenum, inode); +// } +// None => { +// FS_METADATA.inodetable.remove(&inodenum); +// } +// } +// } + +// // update the nextinode counter to avoid collisions +// FS_METADATA +// .nextinode +// .store(max_inodenum + 1, interface::RustAtomicOrdering::Relaxed); + +// let _logclose = log_fileobj.close(); +// let _logremove = interface::removefile(LOGFILENAME.to_string()); + +// // clean up broken links +// fsck(); +// } +// } else { +// if interface::pathexists(LOGFILENAME.to_string()) { +// println!("Filesystem in very corrupted state: log existed but metadata did not!"); +// } +// format_fs(); +// } + +// // then recreate the log +// create_log(); +// } + +// pub fn fsck() { +// FS_METADATA.inodetable.retain(|_inodenum, inode_obj| { +// match inode_obj { +// Inode::File(ref mut normalfile_inode) => normalfile_inode.linkcount != 0, +// Inode::Dir(ref mut dir_inode) => { +// //2 because . and .. always contribute to the linkcount of a directory +// dir_inode.linkcount > 2 +// } +// Inode::CharDev(ref mut char_inodej) => char_inodej.linkcount != 0, +// Inode::Socket(_) => false, +// } +// }); +// } + +// pub fn create_log() { +// // reinstantiate the log file and assign it to the metadata struct +// let log_mapobj = interface::mapfilenew(LOGFILENAME.to_string()).unwrap(); +// let mut logobj = LOGMAP.write(); +// logobj.replace(log_mapobj); +// } + +// // Serialize New Metadata to CBOR, write to logfile +// pub fn log_metadata(metadata: &FilesystemMetadata, inodenum: usize) { +// let serialpair: (usize, Option<&Inode>); +// let entrybytes; + +// // pack and serialize log entry +// if let Some(inode) = metadata.inodetable.get(&inodenum) { +// serialpair = (inodenum, Some(&*inode)); +// entrybytes = interface::serde_serialize_to_bytes(&serialpair).unwrap(); +// } else { +// serialpair = (inodenum, None); +// entrybytes = interface::serde_serialize_to_bytes(&serialpair).unwrap(); +// } + +// // write to file +// let mut mapopt = LOGMAP.write(); +// let map = mapopt.as_mut().unwrap(); +// map.write_to_map(&entrybytes).unwrap(); +// } + +// // Serialize Metadata Struct to CBOR, write to file +// pub fn persist_metadata(metadata: &FilesystemMetadata) { +// // Serialize metadata to string +// let metadatabytes = interface::serde_serialize_to_bytes(&metadata).unwrap(); + +// // remove file if it exists, assigning it to nothing to avoid the compiler yelling about unused result +// let _ = interface::removefile(METADATAFILENAME.to_string()); + +// // write to file +// let mut metadata_fileobj = interface::openmetadata(METADATAFILENAME.to_string()).unwrap(); +// metadata_fileobj +// .writefile_from_bytes(&metadatabytes) +// .unwrap(); +// metadata_fileobj.close().unwrap(); +// } + +pub fn convpath(cpath: &str) -> interface::RustPathBuf { + interface::RustPathBuf::from(cpath) +} + +// /// This function resolves the absolute path of a directory from its inode number in a filesystem. +// /// Here's how it operates: +// /// +// /// - Starts from the given inode and fetches its associated metadata from the filesystem's inode table. +// /// +// /// - Verifies that the inode represents a directory. +// /// +// /// - Attempts to find the parent directory by looking for the ".." entry in the current directory's entries. +// /// +// /// - Retrieves the directory name associated with the current inode using the `filenamefrominode` function and prepends it to the `path_string`. +// /// +// /// - Continues this process recursively, updating the current inode to the parent inode, and accumulating directory names in the `path_string`. +// /// +// /// - Stops when it reaches the root directory, where it prepends a "/" to the `path_string` and returns the complete path. +// /// +// /// This function effectively constructs the absolute path by backtracking the parent directories. However, if any issues arise during this process, such as missing metadata or inability to find the parent directory, it returns `None`. +// pub fn pathnamefrominodenum(inodenum: usize) -> Option { +// let mut path_string = String::new(); +// let mut first_iteration = true; +// let mut current_inodenum = inodenum; + +// loop { +// let mut thisinode = match FS_METADATA.inodetable.get_mut(¤t_inodenum) { +// Some(inode) => inode, +// None => { +// return None; +// } +// }; + +// match *thisinode { +// Inode::Dir(ref mut dir_inode) => { +// // We try to get the parent directory inode. +// if let Some(parent_dir_inode) = dir_inode.filename_to_inode_dict.get("..") { +// // If the parent node is 1 (indicating the root directory) and this is not the first iteration, this indicates that we have arrived at the root directory. Here we add a '/' to the beginning of the path string and return it. +// if *parent_dir_inode == (1 as usize) { +// if !first_iteration { +// path_string.insert(0, '/'); +// return Some(path_string); +// } +// first_iteration = false; +// } + +// match filenamefrominode(*parent_dir_inode, current_inodenum) { +// Some(filename) => { +// path_string = filename + "/" + &path_string; +// current_inodenum = *parent_dir_inode; +// } +// None => return None, +// }; +// } else { +// return None; +// } +// } +// _ => { +// return None; +// } +// } +// } +// } + +// // Find the file by the given inode number in the given directory +// pub fn filenamefrominode(dir_inode_no: usize, target_inode: usize) -> Option { +// let cur_node = Some(FS_METADATA.inodetable.get(&dir_inode_no).unwrap()); + +// match &*cur_node.unwrap() { +// Inode::Dir(d) => { +// let mut target_variable_name: Option = None; + +// for entry in d.filename_to_inode_dict.iter() { +// if entry.value() == &target_inode { +// target_variable_name = Some(entry.key().to_owned()); +// break; +// } +// } +// return target_variable_name; +// } +// _ => return None, +// } +// } + +// //returns tuple consisting of inode number of file (if it exists), and inode number of parent (if it exists) +// pub fn metawalkandparent(path: &interface::RustPath) -> (Option, Option) { +// let mut curnode = Some(FS_METADATA.inodetable.get(&ROOTDIRECTORYINODE).unwrap()); +// let mut inodeno = Some(ROOTDIRECTORYINODE); +// let mut previnodeno = None; + +// //Iterate over the components of the pathbuf in order to walk the file tree +// for comp in path.components() { +// match comp { +// //We've already done what initialization needs to be done +// interface::RustPathComponent::RootDir => {} + +// interface::RustPathComponent::Normal(f) => { +// //If we're trying to get the child of a nonexistent directory, exit out +// if inodeno.is_none() { +// return (None, None); +// } +// match &*curnode.unwrap() { +// Inode::Dir(d) => { +// previnodeno = inodeno; + +// //populate child inode number from parent directory's inode dict +// inodeno = match d +// .filename_to_inode_dict +// .get(&f.to_str().unwrap().to_string()) +// { +// Some(num) => { +// curnode = FS_METADATA.inodetable.get(&num); +// Some(*num) +// } + +// //if no such child exists, update curnode, inodeno accordingly so that +// //we can check against none as we do at the beginning of the Normal match arm +// None => { +// curnode = None; +// None +// } +// } +// } +// //if we're trying to get a child of a non-directory inode, exit out +// _ => { +// return (None, None); +// } +// } +// } + +// //If it's a component of the pathbuf that we don't expect given a normed path, exit out +// _ => { +// return (None, None); +// } +// } +// } +// //return inode number and it's parent's number +// (inodeno, previnodeno) +// } +// pub fn metawalk(path: &interface::RustPath) -> Option { +// metawalkandparent(path).0 +// } +pub fn normpath(origp: interface::RustPathBuf, cage: &Cage) -> interface::RustPathBuf { + //If path is relative, prefix it with the current working directory, otherwise populate it with rootdir + let mut newp = if origp.is_relative() { + (**cage.cwd.read()).clone() + } else { + interface::RustPathBuf::from("/") + }; + + for comp in origp.components() { + match comp { + //if we have a normal path component, push it on to our normed path + interface::RustPathComponent::Normal(_) => { + newp.push(comp); + } + + //if we have a .. path component, pop the last component off our normed path + interface::RustPathComponent::ParentDir => { + newp.pop(); + } + + //if we have a . path component (Or a root dir or a prefix(?)) do nothing + _ => {} + }; + } + newp +} + +// pub fn remove_domain_sock(truepath: interface::RustPathBuf) { +// match metawalkandparent(truepath.as_path()) { +// //If the file does not exist +// (None, ..) => { +// panic!("path does not exist") +// } +// //If the file exists but has no parent, it's the root directory +// (Some(_), None) => { +// panic!("cannot unlink root directory") +// } + +// //If both the file and the parent directory exists +// (Some(inodenum), Some(parentinodenum)) => { +// Cage::remove_from_parent_dir(parentinodenum, &truepath); + +// FS_METADATA.inodetable.remove(&inodenum); +// NET_METADATA.domsock_paths.remove(&truepath); +// } +// } +// } + +// pub fn incref_root() { +// if let Inode::Dir(ref mut rootdir_dirinode_obj) = +// *(FS_METADATA.inodetable.get_mut(&ROOTDIRECTORYINODE).unwrap()) +// { +// rootdir_dirinode_obj.refcount += 1; +// } else { +// panic!("Root directory inode was not a directory"); +// } +// } + +// pub fn decref_dir(cwd_container: &interface::RustPathBuf) { +// if let Some(cwdinodenum) = metawalk(&cwd_container) { +// if let Inode::Dir(ref mut cwddir) = *(FS_METADATA.inodetable.get_mut(&cwdinodenum).unwrap()) +// { +// cwddir.refcount -= 1; + +// //if the directory has been removed but this cwd was the last open handle to it +// if cwddir.refcount == 0 && cwddir.linkcount == 0 { +// FS_METADATA.inodetable.remove(&cwdinodenum); +// } +// } else { +// panic!("Cage had a cwd that was not a directory!"); +// } +// } else { +// panic!("Cage had a cwd which did not exist!"); +// } //we probably want to handle this case, maybe cwd should be an inode number?? Not urgent +// } diff --git a/src/safeposix/mod.rs b/src/safeposix/mod.rs new file mode 100644 index 00000000..6f648bc8 --- /dev/null +++ b/src/safeposix/mod.rs @@ -0,0 +1,6 @@ +pub mod cage; +pub mod dispatcher; +pub mod filesystem; +pub mod net; +pub mod shm; +pub mod syscalls; diff --git a/src/safeposix/net.rs b/src/safeposix/net.rs new file mode 100644 index 00000000..2fbf9803 --- /dev/null +++ b/src/safeposix/net.rs @@ -0,0 +1,564 @@ +// use super::cage::{Cage, FileDescriptor}; +// use super::syscalls::net_constants::*; +// use crate::interface; +// use crate::interface::errnos::{syscall_error, Errno}; + +// //Because other processes on the OS may allocate ephemeral ports, we allocate them from high to +// //low whereas the OS allocates them from low to high +// //Additionally, we can't tell whether a port is truly rebindable, this is because even when a port +// //is closed sometimes there still is cleanup that the OS needs to do (for ephemeral ports which end +// //up in the TIME_WAIT state). Therefore, we will assign ephemeral ports rather than simply from the +// //highest available one, in a cyclic fashion skipping over unavailable ports. While this still may +// //cause issues if specific port adresses in the ephemeral port range are allocated and closed before +// //an ephemeral port would be bound there, it is much less likely that this will happen and is easy +// //to avoid and nonstandard in user programs. See the code for _get_available_udp_port and its tcp +// //counterpart for the implementation details. +// const EPHEMERAL_PORT_RANGE_START: u16 = 32768; //sane default on linux +// const EPHEMERAL_PORT_RANGE_END: u16 = 60999; +// pub const TCPPORT: bool = true; +// pub const UDPPORT: bool = false; + +// pub static NET_METADATA: interface::RustLazyGlobal> = +// interface::RustLazyGlobal::new(|| { +// interface::RustRfc::new(NetMetadata { +// used_port_set: interface::RustHashMap::new(), +// next_ephemeral_port_tcpv4: interface::RustRfc::new(interface::RustLock::new( +// EPHEMERAL_PORT_RANGE_END, +// )), +// next_ephemeral_port_udpv4: interface::RustRfc::new(interface::RustLock::new( +// EPHEMERAL_PORT_RANGE_END, +// )), +// next_ephemeral_port_tcpv6: interface::RustRfc::new(interface::RustLock::new( +// EPHEMERAL_PORT_RANGE_END, +// )), +// next_ephemeral_port_udpv6: interface::RustRfc::new(interface::RustLock::new( +// EPHEMERAL_PORT_RANGE_END, +// )), +// listening_port_set: interface::RustHashSet::new(), +// pending_conn_table: interface::RustHashMap::new(), +// domsock_accept_table: interface::RustHashMap::new(), // manages domain socket connection process +// domsock_paths: interface::RustHashSet::new(), // set of all currently bound domain sockets +// }) +// }); //we want to check if fs exists before doing a blank init, but not for now + +// //A list of all network devices present on the machine +// //It is populated from a file that should be present prior to running rustposix, see +// //the implementation of read_netdevs for specifics +// pub static NET_IFADDRS_STR: interface::RustLazyGlobal = +// interface::RustLazyGlobal::new(|| interface::getifaddrs_from_file()); + +// pub static NET_DEVICE_IPLIST: interface::RustLazyGlobal> = +// interface::RustLazyGlobal::new(|| ips_from_ifaddrs()); + +// fn ips_from_ifaddrs() -> Vec { +// let mut ips = vec![]; +// for net_device in NET_IFADDRS_STR.as_str().split('\n') { +// if net_device == "" { +// continue; +// } +// let ifaddrstr: Vec<&str> = net_device.split(' ').collect(); +// let genipopt = interface::GenIpaddr::from_string(ifaddrstr[2]); +// ips.push(genipopt.expect("Could not parse device ip address from net_devices file")); +// } + +// let genipopt0 = interface::GenIpaddr::from_string("0.0.0.0"); +// ips.push(genipopt0.expect("Could not parse device ip address from net_devices file")); +// return ips; +// } + +// #[derive(Debug, Hash, Eq, PartialEq, Clone)] +// pub enum PortType { +// IPv4UDP, +// IPv4TCP, +// IPv6UDP, +// IPv6TCP, +// } + +// pub fn mux_port( +// addr: interface::GenIpaddr, +// port: u16, +// domain: i32, +// istcp: bool, +// ) -> (interface::GenIpaddr, u16, PortType) { +// match domain { +// PF_INET => ( +// addr, +// port, +// if istcp { +// PortType::IPv4TCP +// } else { +// PortType::IPv4UDP +// }, +// ), +// PF_INET6 => ( +// addr, +// port, +// if istcp { +// PortType::IPv6TCP +// } else { +// PortType::IPv6UDP +// }, +// ), +// _ => panic!("How did you manage to set an unsupported domain on the socket?"), +// } +// } + +// //A substructure for information only populated in a unix domain socket +// #[derive(Debug)] +// pub struct UnixSocketInfo { +// pub mode: i32, +// pub sendpipe: Option>, +// pub receivepipe: Option>, +// pub inode: usize, +// } + +// //This structure contains all socket-associated data that is not held in the fd +// #[derive(Debug)] +// pub struct SocketHandle { +// pub innersocket: Option, +// pub socket_options: i32, +// pub tcp_options: i32, +// pub state: ConnState, +// pub protocol: i32, +// pub domain: i32, +// pub last_peek: interface::RustDeque, +// pub localaddr: Option, +// pub remoteaddr: Option, +// pub unix_info: Option, +// pub socktype: i32, +// pub sndbuf: i32, +// pub rcvbuf: i32, +// pub errno: i32, +// } + +// //This cleanup-on-drop strategy is used in lieu of manual refcounting in order to allow the close +// //syscall not to have to wait to increase the refcnt manually in case for example it is in a +// //blocking recv. This clean-on-drop strategy is made possible by the fact that file descriptors +// //hold reference to a SocketHandle via an Arc, so only when the last reference to a SocketHandle is +// //gone--that is when the last cage has closed it--do we actually attempt to shut down the inner +// //socket, which is what we could have done manually in close instead. This should be both cleaner +// //and faster, because we don't have to wait for the recv timeout like we do in shutdown +// impl Drop for SocketHandle { +// fn drop(&mut self) { +// Cage::_cleanup_socket_inner_helper(self, -1, false); +// } +// } + +// #[derive(Debug)] +// pub struct ConnCondVar { +// lock: interface::RustRfc>, +// cv: interface::Condvar, +// } + +// impl ConnCondVar { +// pub fn new() -> Self { +// Self { +// lock: interface::RustRfc::new(interface::Mutex::new(0)), +// cv: interface::Condvar::new(), +// } +// } + +// pub fn wait(&self) { +// let mut guard = self.lock.lock(); +// *guard += 1; +// self.cv.wait(&mut guard); +// } + +// pub fn broadcast(&self) -> bool { +// let guard = self.lock.lock(); +// if *guard == 1 { +// self.cv.notify_all(); +// return true; +// } else { +// return false; +// } +// } +// } + +// pub struct DomsockTableEntry { +// pub sockaddr: interface::GenSockaddr, +// pub receive_pipe: interface::RustRfc, +// pub send_pipe: interface::RustRfc, +// pub cond_var: Option>, +// } + +// impl DomsockTableEntry { +// pub fn get_cond_var(&self) -> Option<&interface::RustRfc> { +// self.cond_var.as_ref() +// } +// pub fn get_sockaddr(&self) -> &interface::GenSockaddr { +// &self.sockaddr +// } +// pub fn get_send_pipe(&self) -> &interface::RustRfc { +// &self.send_pipe +// } +// pub fn get_receive_pipe(&self) -> &interface::RustRfc { +// &self.receive_pipe +// } +// } + +// pub struct NetMetadata { +// 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 +// next_ephemeral_port_tcpv4: interface::RustRfc>, +// next_ephemeral_port_udpv4: interface::RustRfc>, +// next_ephemeral_port_tcpv6: interface::RustRfc>, +// next_ephemeral_port_udpv6: interface::RustRfc>, +// pub listening_port_set: interface::RustHashSet<(interface::GenIpaddr, u16, PortType)>, +// pub pending_conn_table: interface::RustHashMap< +// (interface::GenIpaddr, u16, PortType), +// Vec<(Result, interface::GenSockaddr)>, +// >, +// pub domsock_accept_table: interface::RustHashMap, +// pub domsock_paths: interface::RustHashSet, +// } + +// impl NetMetadata { +// fn initialize_port( +// &self, +// tup: &(interface::GenIpaddr, u16, PortType), +// rebindability: u32, +// ) -> bool { +// let used_port_tup = (tup.1, tup.2.clone()); +// if tup.0.is_unspecified() { +// let tupclone = used_port_tup.clone(); +// let entry = self.used_port_set.entry(tupclone.clone()); +// match entry { +// interface::RustHashEntry::Occupied(_) => { +// return false; +// } +// interface::RustHashEntry::Vacant(v) => { +// let mut intervec = vec![]; +// for interface_addr in &*NET_DEVICE_IPLIST { +// intervec.push((interface_addr.clone(), rebindability)); +// } +// v.insert(intervec); +// } +// } +// true +// } else { +// match self.used_port_set.entry(used_port_tup) { +// interface::RustHashEntry::Occupied(mut o) => { +// let addrsused = o.get_mut(); +// for addrtup in addrsused.clone() { +// if addrtup.0 == tup.0 { +// return false; +// } +// } +// addrsused.push((tup.0.clone(), rebindability)); +// } +// interface::RustHashEntry::Vacant(v) => { +// v.insert(vec![(tup.0.clone(), rebindability)]); +// } +// } +// true +// } +// } + +// pub fn _get_available_udp_port( +// &self, +// addr: interface::GenIpaddr, +// domain: i32, +// rebindability: bool, +// ) -> Result { +// if !NET_DEVICE_IPLIST.contains(&addr) { +// return Err(syscall_error( +// Errno::EADDRNOTAVAIL, +// "bind", +// "Specified network device is not set up for lind or does not exist!", +// )); +// } +// let mut porttuple = mux_port(addr, 0, domain, UDPPORT); + +// //start from the starting location we specified in a previous attempt to get an ephemeral port +// let mut next_ephemeral = if domain == AF_INET { +// self.next_ephemeral_port_udpv4.write() +// } else if domain == AF_INET6 { +// self.next_ephemeral_port_udpv6.write() +// } else { +// unreachable!() +// }; +// for range in [ +// (EPHEMERAL_PORT_RANGE_START..=*next_ephemeral), +// (*next_ephemeral + 1..=EPHEMERAL_PORT_RANGE_END), +// ] { +// for ne_port in range.rev() { +// let port = ne_port.to_be(); //ports are stored in network endian order +// porttuple.1 = port; + +// //if we think we can bind to this port +// if self.initialize_port(&porttuple, if rebindability { 1 } else { 0 }) { +// //rebindability of 0 means not rebindable, 1 means it's rebindable and there's 1 bound to it +// *next_ephemeral -= 1; +// if *next_ephemeral < EPHEMERAL_PORT_RANGE_START { +// *next_ephemeral = EPHEMERAL_PORT_RANGE_END; +// } +// return Ok(port); +// } +// } +// } +// return Err(syscall_error( +// Errno::EADDRINUSE, +// "bind", +// "No available ephemeral port could be found", +// )); +// } +// pub fn _get_available_tcp_port( +// &self, +// addr: interface::GenIpaddr, +// domain: i32, +// rebindability: bool, +// ) -> Result { +// if !NET_DEVICE_IPLIST.contains(&addr) { +// return Err(syscall_error( +// Errno::EADDRNOTAVAIL, +// "bind", +// "Specified network device is not set up for lind or does not exist!", +// )); +// } +// let mut porttuple = mux_port(addr.clone(), 0, domain, TCPPORT); + +// //start from the starting location we specified in a previous attempt to get an ephemeral port +// let mut next_ephemeral = if domain == AF_INET { +// self.next_ephemeral_port_tcpv4.write() +// } else if domain == AF_INET6 { +// self.next_ephemeral_port_tcpv6.write() +// } else { +// unreachable!() +// }; +// for range in [ +// (EPHEMERAL_PORT_RANGE_START..=*next_ephemeral), +// (*next_ephemeral + 1..=EPHEMERAL_PORT_RANGE_END), +// ] { +// for ne_port in range.rev() { +// let port = ne_port.to_be(); //ports are stored in network endian order +// porttuple.1 = port; + +// if self.initialize_port(&porttuple, if rebindability { 1 } else { 0 }) { +// //rebindability of 0 means not rebindable, 1 means it's rebindable and there's 1 bound to it + +// *next_ephemeral -= 1; +// if *next_ephemeral < EPHEMERAL_PORT_RANGE_START { +// *next_ephemeral = EPHEMERAL_PORT_RANGE_END; +// } + +// return Ok(port); +// } +// } +// } +// return Err(syscall_error( +// Errno::EADDRINUSE, +// "bind", +// "No available ephemeral port could be found", +// )); +// } + +// pub fn _reserve_localport( +// &self, +// addr: interface::GenIpaddr, +// port: u16, +// protocol: i32, +// domain: i32, +// rebindability: bool, +// ) -> Result { +// if !NET_DEVICE_IPLIST.contains(&addr) { +// return Err(syscall_error( +// Errno::EADDRNOTAVAIL, +// "bind", +// "Specified network device is not set up for lind or does not exist!", +// )); +// } + +// let muxed; +// if protocol == IPPROTO_UDP { +// if port == 0 { +// return self._get_available_udp_port(addr, domain, rebindability); +// //assign ephemeral port +// } else { +// muxed = mux_port(addr, port, domain, UDPPORT); +// } +// } else if protocol == IPPROTO_TCP { +// if port == 0 { +// return self._get_available_tcp_port(addr, domain, rebindability); +// //assign ephemeral port +// } else { +// muxed = mux_port(addr, port, domain, TCPPORT); +// } +// } else { +// panic!("Unknown protocol was set on socket somehow"); +// } + +// let usedport_muxed = (muxed.1, muxed.2); +// let entry = self.used_port_set.entry(usedport_muxed); +// if addr.is_unspecified() { +// match entry { +// interface::RustHashEntry::Occupied(_) => { +// return Err(syscall_error( +// Errno::EADDRINUSE, +// "reserve port", +// "port is already in use", +// )); +// } +// interface::RustHashEntry::Vacant(v) => { +// v.insert( +// NET_DEVICE_IPLIST +// .iter() +// .map(|x| (x.clone(), if rebindability { 1 } else { 0 })) +// .collect(), +// ); +// } +// } +// } else { +// match entry { +// interface::RustHashEntry::Occupied(mut userentry) => { +// for portuser in userentry.get_mut() { +// if portuser.0 == muxed.0 { +// if portuser.1 == 0 { +// return Err(syscall_error( +// Errno::EADDRINUSE, +// "reserve port", +// "port is already in use", +// )); +// } else { +// portuser.1 += 1; +// } +// break; +// } +// } +// } +// interface::RustHashEntry::Vacant(v) => { +// v.insert(vec![(muxed.0.clone(), if rebindability { 1 } else { 0 })]); +// } +// } +// } +// Ok(port) +// } + +// pub fn _release_localport( +// &self, +// addr: interface::GenIpaddr, +// port: u16, +// protocol: i32, +// domain: i32, +// ) -> Result<(), i32> { +// if !NET_DEVICE_IPLIST.contains(&addr) { +// return Err(syscall_error( +// Errno::EADDRNOTAVAIL, +// "bind", +// "Specified network device is not set up for lind or does not exist!", +// )); +// } + +// let muxed; +// if protocol == IPPROTO_TCP { +// muxed = mux_port(addr.clone(), port, domain, TCPPORT); +// } else if protocol == IPPROTO_UDP { +// muxed = mux_port(addr.clone(), port, domain, UDPPORT); +// } else { +// return Err(syscall_error( +// Errno::EINVAL, +// "release", +// "provided port has nonsensical protocol", +// )); +// } + +// let usedport_muxed = (muxed.1, muxed.2); +// let entry = self.used_port_set.entry(usedport_muxed); +// match entry { +// interface::RustHashEntry::Occupied(mut userentry) => { +// let mut index = 0; +// let userarr = userentry.get_mut(); +// if addr.is_unspecified() { +// for portuser in userarr.clone() { +// if portuser.1 <= 1 { +// userarr.swap_remove(index); +// } else { +// //if it's rebindable and there are others bound to it +// userarr[index].1 -= 1; +// } +// } +// if userarr.len() == 0 { +// userentry.remove(); +// } +// return Ok(()); +// } else { +// for portuser in userarr.clone() { +// if portuser.0 == muxed.0 { +// //if it's rebindable and we're removing the last bound port or it's just not rebindable +// if portuser.1 <= 1 { +// if userarr.len() == 1 { +// userentry.remove(); +// } else { +// userarr.swap_remove(index); +// } +// } else { +// //if it's rebindable and there are others bound to it +// userarr[index].1 -= 1; +// } +// return Ok(()); +// } +// index += 1; +// } +// unreachable!(); +// } +// } +// interface::RustHashEntry::Vacant(_) => { +// return Err(syscall_error( +// Errno::EINVAL, +// "release", +// "provided port is not being used", +// )); +// } +// } +// } + +// pub fn get_domainsock_paths(&self) -> Vec { +// let mut domainsock_paths: Vec = vec![]; +// for ds_path in self.domsock_paths.iter() { +// domainsock_paths.push(ds_path.clone()); +// } // get vector of domain sock table keys +// domainsock_paths +// } +// } + +// pub struct SelectInetInfo { +// pub rawfd_lindfd_tuples: Vec<(i32, i32)>, +// pub kernel_fds: interface::FdSet, +// pub highest_raw_fd: i32, +// } + +// impl SelectInetInfo { +// pub fn new() -> Self { +// SelectInetInfo { +// rawfd_lindfd_tuples: Vec::new(), +// kernel_fds: interface::FdSet::new(), +// highest_raw_fd: 0, +// } +// } +// } + +// pub fn update_readfds_from_kernel_select( +// readfds: &mut interface::FdSet, +// inet_info: &mut SelectInetInfo, +// retval: &mut i32, +// ) -> i32 { +// let kernel_ret; +// // note that this select call always have timeout = 0, so it doesn't block + +// kernel_ret = interface::kernel_select( +// inet_info.highest_raw_fd + 1, +// Some(&mut inet_info.kernel_fds), +// None, +// None, +// ); +// if kernel_ret > 0 { +// // increment retval of our select +// *retval += kernel_ret; +// // translate the kernel checked fds to lindfds, and add to our new_writefds +// readfds.set_from_kernelfds_and_translate( +// &mut inet_info.kernel_fds, +// inet_info.highest_raw_fd + 1, +// &inet_info.rawfd_lindfd_tuples, +// ); +// } +// return kernel_ret; +// } diff --git a/src/safeposix/shm.rs b/src/safeposix/shm.rs new file mode 100644 index 00000000..a450af6e --- /dev/null +++ b/src/safeposix/shm.rs @@ -0,0 +1,146 @@ +// Filesystem metadata struct +#![allow(dead_code)] + +use super::syscalls::fs_constants::*; +use super::syscalls::sys_constants::*; +use crate::interface; + +use libc::*; + +use super::cage::Cage; + +pub static SHM_METADATA: interface::RustLazyGlobal> = + interface::RustLazyGlobal::new(|| interface::RustRfc::new(ShmMetadata::init_shm_metadata())); + +pub struct ShmSegment { + pub shminfo: interface::ShmidsStruct, + pub key: i32, + pub size: usize, + pub filebacking: interface::ShmFile, + pub rmid: bool, + pub attached_cages: interface::RustHashMap, // attached cages, number of references in cage + pub semaphor_offsets: interface::RustHashSet, +} + +pub fn new_shm_segment( + key: i32, + size: usize, + cageid: u32, + uid: u32, + gid: u32, + mode: u16, +) -> ShmSegment { + ShmSegment::new(key, size, cageid, uid, gid, mode) +} + +impl ShmSegment { + pub fn new(key: i32, size: usize, cageid: u32, uid: u32, gid: u32, mode: u16) -> ShmSegment { + let filebacking = interface::new_shm_backing(key, size).unwrap(); + + let time = interface::timestamp() as isize; //We do a real timestamp now + let permstruct = interface::IpcPermStruct { + __key: key, + uid: uid, + gid: gid, + cuid: uid, + cgid: gid, + mode: mode, + __pad1: 0, + __seq: 0, + __pad2: 0, + __unused1: 0, + __unused2: 0, + }; + let shminfo = interface::ShmidsStruct { + shm_perm: permstruct, + shm_segsz: size as u32, + shm_atime: 0, + shm_dtime: 0, + shm_ctime: time, + shm_cpid: cageid, + shm_lpid: 0, + shm_nattch: 0, + }; + + ShmSegment { + shminfo: shminfo, + key: key, + size: size, + filebacking: filebacking, + rmid: false, + attached_cages: interface::RustHashMap::new(), + semaphor_offsets: interface::RustHashSet::new(), + } + } + // mmap shared segment into cage, and increase attachments + // increase in cage references within attached_cages map + pub fn map_shm(&mut self, shmaddr: *mut u8, prot: i32, cageid: u64) -> i32 { + let fobjfdno = self.filebacking.as_fd_handle_raw_int(); + self.shminfo.shm_nattch += 1; + self.shminfo.shm_atime = interface::timestamp() as isize; + + match self.attached_cages.entry(cageid) { + interface::RustHashEntry::Occupied(mut occupied) => { + *occupied.get_mut() += 1; + } + interface::RustHashEntry::Vacant(vacant) => { + vacant.insert(1); + } + }; + interface::libc_mmap( + shmaddr, + self.size as usize, + prot, + MAP_SHARED | MAP_FIXED, + fobjfdno, + 0, + ) + } + + // unmap shared segment, decrease attachments + // decrease references within attached cages map + pub fn unmap_shm(&mut self, shmaddr: *mut u8, cageid: u64) { + interface::libc_mmap( + shmaddr, + self.size as usize, + PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, + -1, + 0, + ); + self.shminfo.shm_nattch -= 1; + self.shminfo.shm_dtime = interface::timestamp() as isize; + match self.attached_cages.entry(cageid) { + interface::RustHashEntry::Occupied(mut occupied) => { + *occupied.get_mut() -= 1; + if *occupied.get() == 0 { + occupied.remove_entry(); + } + } + interface::RustHashEntry::Vacant(_) => { + panic!("Cage not avilable in segment attached cages"); + } + }; + } +} + +pub struct ShmMetadata { + pub nextid: interface::RustAtomicI32, + pub shmkeyidtable: interface::RustHashMap, + pub shmtable: interface::RustHashMap, +} + +impl ShmMetadata { + pub fn init_shm_metadata() -> ShmMetadata { + ShmMetadata { + nextid: interface::RustAtomicI32::new(1), + shmkeyidtable: interface::RustHashMap::new(), + shmtable: interface::RustHashMap::new(), + } + } + + pub fn new_keyid(&self) -> i32 { + self.nextid + .fetch_add(1, interface::RustAtomicOrdering::Relaxed) + } +} diff --git a/src/safeposix/syscalls/fs_calls.rs b/src/safeposix/syscalls/fs_calls.rs new file mode 100644 index 00000000..7defb615 --- /dev/null +++ b/src/safeposix/syscalls/fs_calls.rs @@ -0,0 +1,2273 @@ +#![allow(dead_code)] + +use std::fs; +use super::fs_constants; +// File system related system calls +use super::fs_constants::*; +use super::sys_constants::*; +use crate::interface; +use crate::interface::get_errno; +use crate::interface::handle_errno; +use crate::interface::FSData; +use crate::safeposix::cage::Errno::EINVAL; +use crate::safeposix::cage::*; +use crate::safeposix::filesystem::convpath; +use crate::safeposix::filesystem::normpath; +// use crate::safeposix::filesystem::*; +// use crate::safeposix::net::NET_METADATA; +use crate::safeposix::shm::*; +use crate::interface::ShmidsStruct; +use crate::interface::StatData; + +use libc::*; +use std::io::stdout; +use std::os::unix::io::RawFd; +use std::io::{self, Write}; +use std::ffi::CStr; +use std::ffi::CString; +use std::ptr; +use std::mem; + +// use crate::example_grates::vanillaglobal::*; +use crate::example_grates::dashmapvecglobal::*; +// use crate::example_grates::muthashmaxglobal::*; +// use crate::example_grates::dashmaparrayglobal::*; + +static LIND_ROOT: &str = "/home/lind/lind_project/src/safeposix-rust/tmp"; + +/* +* We will receive parameters with type u64 by default, then we will do type conversion inside +* of each syscall +* +* [Concerns] +* - cloexec in get_unused_virtual_fd()..? +* - there's no getdents() API in rust libc +* +*/ + +impl Cage { + //------------------------------------OPEN SYSCALL------------------------------------ + /* + * Open will return a file descriptor + * Mapping a new virtual fd and kernel fd that libc::socket returned + * Then return virtual fd + */ + pub fn open_syscall(&self, path: &str, oflag: i32, mode: u32) -> i32 { + + // Convert data type from &str into *const i8 + // let c_path = CString::new(path).unwrap(); + let relpath = normpath(convpath(path), self); + let relative_path = relpath.to_str().unwrap(); + let full_path = format!("{}{}", LIND_ROOT, relative_path); + let c_path = CString::new(full_path).unwrap(); + + let kernel_fd = unsafe { libc::open(c_path.as_ptr(), oflag, mode) }; + + + if kernel_fd < 0 { + // let err = unsafe { + // *libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[open] Error message: {:?}", err_msg); + // println!("[open] c_path: {:?}", c_path); + // println!("[open] oflag: {:?}", oflag); + // println!("[open] mode: {:?}", mode); + // println!("[open] kernel fd: {:?}", kernel_fd); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "open"); + } + + let should_cloexec = (oflag & O_CLOEXEC) != 0; + + let virtual_fd = get_unused_virtual_fd(self.cageid, kernel_fd as u64, should_cloexec, 0).unwrap(); + // let mut count = 0; + // FDTABLE.iter().for_each(|entry| { + // // println!("Cage ID: {}", entry.key()); + // for (index, fd_entry) in entry.value().iter().enumerate() { + // if let Some(entry) = fd_entry { + // // println!(" Index {}: {:?}", index, entry); + // count = count+1; + // } + // } + // }); + // println!("[OPEN] cageid: {:?}", self.cageid); + // println!("[OPEN] vfd: {:?}", virtual_fd); + // println!("[OPEN] realfd: {:?}", kernel_fd); + // if kernel_fd > 1000 { + // let entries = fs::read_dir("/proc/self/fd")?; + // let count = entries.count(); + // println!("!!!![OPEN]!!!! TOTAL FD: {:?}", count); + // } + // // println!("[OPEN] Total: {:?}", count); + // io::stdout().flush().unwrap(); + virtual_fd as i32 + } + + //------------------MKDIR SYSCALL------------------ + /* + * mkdir() will return 0 when success and -1 when fail + */ + pub fn mkdir_syscall(&self, path: &str, mode: u32) -> i32 { + // Convert data type from &str into *const i8 + // let c_path = CString::new(path).expect("CString::new failed"); + let relpath = normpath(convpath(path), self); + let relative_path = relpath.to_str().unwrap(); + let full_path = format!("{}{}", LIND_ROOT, relative_path); + let c_path = CString::new(full_path).unwrap(); + + let ret = unsafe { + libc::mkdir(c_path.as_ptr(), mode) + }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[mkdir] Error message: {:?}", err_msg); + // println!("[mkdir] c_path: {:?}", c_path); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "mkdir"); + } + ret + } + + //------------------MKNOD SYSCALL------------------ + /* + * mknod() will return 0 when success and -1 when fail + */ + pub fn mknod_syscall(&self, path: &str, mode: u32, dev: u64) -> i32 { + // Convert data type from &str into *const i8 + // let c_path = CString::new(path).expect("CString::new failed"); + let relpath = normpath(convpath(path), self); + let relative_path = relpath.to_str().unwrap(); + let full_path = format!("{}{}", LIND_ROOT, relative_path); + let c_path = CString::new(full_path).unwrap(); + let ret = unsafe { + libc::mknod(c_path.as_ptr(), mode, dev) + }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[mknod] Error message: {:?}", err_msg); + // println!("[mknod] c_path: {:?}", c_path); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "mknod"); + } + ret + } + + //------------------------------------LINK SYSCALL------------------------------------ + /* + * link() will return 0 when success and -1 when fail + */ + pub fn link_syscall(&self, oldpath: &str, newpath: &str) -> i32 { + // Convert data type from &str into *const i8 + let rel_oldpath = normpath(convpath(oldpath), self); + let relative_oldpath = rel_oldpath.to_str().unwrap(); + let full_oldpath = format!("{}{}", LIND_ROOT, relative_oldpath); + let old_cpath = CString::new(full_oldpath).unwrap(); + + let rel_newpath = normpath(convpath(newpath), self); + let relative_newpath = rel_newpath.to_str().unwrap(); + let full_newpath = format!("{}{}", LIND_ROOT, relative_newpath); + let new_cpath = CString::new(full_newpath).unwrap(); + + let ret = unsafe { + libc::link(old_cpath.as_ptr(), new_cpath.as_ptr()) + }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[link] Error message: {:?}", err_msg); + // println!("[link] c_path: {:?}", old_cpath); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "link"); + } + ret + } + + //------------------------------------UNLINK SYSCALL------------------------------------ + /* + * unlink() will return 0 when success and -1 when fail + */ + pub fn unlink_syscall(&self, path: &str) -> i32 { + // let (path_c, _, _) = path.to_string().into_raw_parts(); + let relpath = normpath(convpath(path), self); + let relative_path = relpath.to_str().unwrap(); + let full_path = format!("{}{}", LIND_ROOT, relative_path); + let c_path = CString::new(full_path).unwrap(); + + let ret = unsafe { + libc::unlink(c_path.as_ptr()) + }; + + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // let errno = unsafe { + // *libc::__errno_location() + // } as i32; + // println!("[unlink] Error message: {:?}", err_msg); + // println!("[unlink] c_path: {:?}", c_path); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "unlink"); + } + ret + } + + //------------------------------------CREAT SYSCALL------------------------------------ + /* + * creat() will return fd when success and -1 when fail + */ + pub fn creat_syscall(&self, path: &str, mode: u32) -> i32 { + // let c_path = CString::new(path).expect("CString::new failed"); + let relpath = normpath(convpath(path), self); + let relative_path = relpath.to_str().unwrap(); + let full_path = format!("{}{}", LIND_ROOT, relative_path); + let c_path = CString::new(full_path).unwrap(); + + let kernel_fd = unsafe { + libc::creat(c_path.as_ptr(), mode) + }; + if kernel_fd < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[creat] Error message: {:?}", err_msg); + // println!("[creat] c_path: {:?}", c_path); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "creat"); + } + + let virtual_fd = get_unused_virtual_fd(self.cageid, kernel_fd as u64, false, 0).unwrap(); + virtual_fd as i32 + } + + //------------------------------------STAT SYSCALL------------------------------------ + /* + * stat() will return 0 when success and -1 when fail + */ + // pub fn stat_syscall(&self, path: &str, statbuf: &mut stat) -> i32 { + // // let c_path = CString::new(path).expect("CString::new failed"); + // let relpath = normpath(convpath(path), self); + // let relative_path = relpath.to_str().unwrap(); + // let full_path = format!("{}{}", LIND_ROOT, relative_path); + // let c_path = CString::new(full_path).unwrap(); + // unsafe { + // libc::stat(c_path.as_ptr(), statbuf) + // } + // } + pub fn stat_syscall(&self, path: &str, rposix_statbuf: &mut StatData) -> i32 { + let relpath = normpath(convpath(path), self); + let relative_path = relpath.to_str().unwrap(); + let full_path = format!("{}{}", LIND_ROOT, relative_path); + let c_path = CString::new(full_path).unwrap(); + + // Declare statbuf by ourselves + let mut libc_statbuf: stat = unsafe { std::mem::zeroed() }; + let libcret = unsafe { + libc::stat(c_path.as_ptr(), &mut libc_statbuf) + }; + + if libcret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // let errno = unsafe { + // *libc::__errno_location() + // } as i32; + // println!("[stat] Error message: {:?}", err_msg); + // println!("[stat] c_path: {:?}", c_path); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "stat"); + } + + rposix_statbuf.st_blksize = libc_statbuf.st_blksize as i32; + rposix_statbuf.st_blocks = libc_statbuf.st_blocks as u32; + rposix_statbuf.st_dev = libc_statbuf.st_dev as u64; + rposix_statbuf.st_gid = libc_statbuf.st_gid; + rposix_statbuf.st_ino = libc_statbuf.st_ino as usize; + rposix_statbuf.st_mode = libc_statbuf.st_mode as u32; + rposix_statbuf.st_nlink = libc_statbuf.st_nlink as u32; + rposix_statbuf.st_rdev = libc_statbuf.st_rdev as u64; + rposix_statbuf.st_size = libc_statbuf.st_size as usize; + rposix_statbuf.st_uid = libc_statbuf.st_uid; + + libcret + } + + //------------------------------------FSTAT SYSCALL------------------------------------ + /* + * Get the kernel fd with provided virtual fd first + * fstat() will return 0 when success and -1 when fail + */ + // pub fn fstat_syscall(&self, virtual_fd: i32, statbuf: &mut stat) -> i32 { + // let kernel_fd = translate_virtual_fd(self.cageid, virtual_fd).unwrap(); + // unsafe { + // libc::fstat(kernel_fd, statbuf) + // } + // } + pub fn fstat_syscall(&self, virtual_fd: i32, rposix_statbuf: &mut StatData) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "fstat", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + // Declare statbuf by ourselves + let mut libc_statbuf: stat = unsafe { std::mem::zeroed() }; + let libcret = unsafe { + libc::fstat(kernel_fd as i32, &mut libc_statbuf) + }; + + if libcret < 0 { + let errno = get_errno(); + return handle_errno(errno, "fstat"); + } + + rposix_statbuf.st_blksize = libc_statbuf.st_blksize as i32; + rposix_statbuf.st_blocks = libc_statbuf.st_blocks as u32; + rposix_statbuf.st_dev = libc_statbuf.st_dev as u64; + rposix_statbuf.st_gid = libc_statbuf.st_gid; + rposix_statbuf.st_ino = libc_statbuf.st_ino as usize; + rposix_statbuf.st_mode = libc_statbuf.st_mode as u32; + rposix_statbuf.st_nlink = libc_statbuf.st_nlink as u32; + rposix_statbuf.st_rdev = libc_statbuf.st_rdev as u64; + rposix_statbuf.st_size = libc_statbuf.st_size as usize; + rposix_statbuf.st_uid = libc_statbuf.st_uid; + + libcret + + } + + //------------------------------------STATFS SYSCALL------------------------------------ + /* + * statfs() will return 0 when success and -1 when fail + */ + pub fn statfs_syscall(&self, path: &str, rposix_databuf: &mut FSData) -> i32 { + let relpath = normpath(convpath(path), self); + let relative_path = relpath.to_str().unwrap(); + let full_path = format!("{}{}", LIND_ROOT, relative_path); + let c_path = CString::new(full_path).unwrap(); + + let mut libc_databuf: statfs = unsafe { mem::zeroed() }; + let libcret = unsafe { + libc::statfs(c_path.as_ptr(), &mut libc_databuf) + }; + + if libcret < 0 { + let errno = get_errno(); + return handle_errno(errno, "statfs"); + } + + rposix_databuf.f_bavail = libc_databuf.f_bavail; + rposix_databuf.f_bfree = libc_databuf.f_bfree; + rposix_databuf.f_blocks = libc_databuf.f_blocks; + rposix_databuf.f_bsize = libc_databuf.f_bsize as u64; + rposix_databuf.f_files = libc_databuf.f_files; + /* TODO: different from libc struct */ + rposix_databuf.f_fsid = 0; + rposix_databuf.f_type = libc_databuf.f_type as u64; + rposix_databuf.f_ffiles = 1024 * 1024 * 515; + rposix_databuf.f_namelen = 254; + rposix_databuf.f_frsize = 4096; + rposix_databuf.f_spare = [0; 32]; + + libcret + } + + //------------------------------------FSTATFS SYSCALL------------------------------------ + /* + * Get the kernel fd with provided virtual fd first + * fstatfs() will return 0 when success and -1 when fail + */ + pub fn fstatfs_syscall(&self, virtual_fd: i32, rposix_databuf: &mut FSData) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "fstatfs", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + let mut libc_databuf: statfs = unsafe { mem::zeroed() }; + let libcret = unsafe { + libc::fstatfs(kernel_fd as i32, &mut libc_databuf) + }; + + if libcret < 0 { + let errno = get_errno(); + return handle_errno(errno, "fstatfs"); + } + + rposix_databuf.f_bavail = libc_databuf.f_bavail; + rposix_databuf.f_bfree = libc_databuf.f_bfree; + rposix_databuf.f_blocks = libc_databuf.f_blocks; + rposix_databuf.f_bsize = libc_databuf.f_bsize as u64; + rposix_databuf.f_files = libc_databuf.f_files; + /* TODO: different from libc struct */ + rposix_databuf.f_fsid = 0; + rposix_databuf.f_type = libc_databuf.f_type as u64; + rposix_databuf.f_ffiles = 1024 * 1024 * 515; + rposix_databuf.f_namelen = 254; + rposix_databuf.f_frsize = 4096; + rposix_databuf.f_spare = [0; 32]; + + return libcret; + } + + //------------------------------------READ SYSCALL------------------------------------ + /* + * Get the kernel fd with provided virtual fd first + * read() will return: + * - the number of bytes read is returned, success + * - -1, fail + */ + pub fn read_syscall(&self, virtual_fd: i32, readbuf: *mut u8, count: usize) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "read", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + let ret = unsafe { + libc::read(kernel_fd as i32, readbuf as *mut c_void, count) as i32 + }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[READ] Error message: {:?}", err_msg); + // println!("kernel_fd: {:?}", kernel_fd); + // println!("vfd: {:?}", virtual_fd); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "read"); + } + return ret; + + } + + //------------------------------------PREAD SYSCALL------------------------------------ + /* + * Get the kernel fd with provided virtual fd first + * pread() will return: + * - the number of bytes read is returned, success + * - -1, fail + */ + pub fn pread_syscall(&self, virtual_fd: i32, buf: *mut u8, count: usize, offset: i64) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "pread", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + let ret = unsafe { + libc::pread(kernel_fd as i32, buf as *mut c_void, count, offset) as i32 + }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[pread] Error message: {:?}", err_msg); + // println!("[pread] virtual fd: {:?}", virtual_fd); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "pread"); + } + return ret; + + } + + //------------------------------------WRITE SYSCALL------------------------------------ + /* + * Get the kernel fd with provided virtual fd first + * write() will return: + * - the number of bytes writen is returned, success + * - -1, fail + */ + pub fn write_syscall(&self, virtual_fd: i32, buf: *const u8, count: usize) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "write", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + let ret = unsafe { + libc::write(kernel_fd as i32, buf as *const c_void, count) as i32 + }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[write] Error message: {:?}", err_msg); + // println!("[write] virtual fd: {:?}", virtual_fd); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "write"); + } + return ret; + + } + + //------------------------------------PWRITE SYSCALL------------------------------------ + /* + * Get the kernel fd with provided virtual fd first + * pwrite() will return: + * - the number of bytes read is returned, success + * - -1, fail + */ + pub fn pwrite_syscall(&self, virtual_fd: i32, buf: *const u8, count: usize, offset: i64) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "pwrite", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + let ret = unsafe { + libc::pwrite(kernel_fd as i32, buf as *const c_void, count, offset) as i32 + }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[pwrite] Error message: {:?}", err_msg); + // println!("[pwrite] virtual fd: {:?}", virtual_fd); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "pwrite"); + } + return ret; + + } + + //------------------------------------WRITEV SYSCALL------------------------------------ + + pub fn writev_syscall( + &self, + virtual_fd: i32, + iovec: *const interface::IovecStruct, + iovcnt: i32, + ) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "writev", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + let ret = unsafe { + libc::writev(kernel_fd as i32, iovec, iovcnt) + }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[writev] Error message: {:?}", err_msg); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "writev"); + } + return ret as i32; + + } + + //------------------------------------LSEEK SYSCALL------------------------------------ + /* + * Get the kernel fd with provided virtual fd first + * lseek() will return: + * - the resulting offset location as measured in bytes from the beginning of the file + * - -1, fail + */ + pub fn lseek_syscall(&self, virtual_fd: i32, offset: isize, whence: i32) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "lseek", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + let ret = unsafe { + libc::lseek(kernel_fd as i32, offset as i64, whence) as i32 + }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[lseek] Error message: {:?}", err_msg); + // println!("[lseek] virtual fd: {:?}", virtual_fd); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "lseek"); + } + return ret; + + } + + //------------------------------------ACCESS SYSCALL------------------------------------ + /* + * access() will return 0 when sucess, -1 when fail + */ + pub fn access_syscall(&self, path: &str, amode: i32) -> i32 { + // let c_path = CString::new(path).expect("CString::new failed"); + let relpath = normpath(convpath(path), self); + let relative_path = relpath.to_str().unwrap(); + let full_path = format!("{}{}", LIND_ROOT, relative_path); + let c_path = CString::new(full_path).unwrap(); + let ret = unsafe { + libc::access(c_path.as_ptr(), amode) + }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[access] Error message: {:?}", err_msg); + // println!("[access] c_path: {:?}", c_path); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "access"); + } + ret + } + + //------------------------------------FCHDIR SYSCALL------------------------------------ + /* + * Get the kernel fd with provided virtual fd first + * fchdir() will return 0 when sucess, -1 when fail + */ + pub fn fchdir_syscall(&self, virtual_fd: i32) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "fchdir", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + let ret = unsafe { + libc::fchdir(kernel_fd as i32) + }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[fchdir] Error message: {:?}", err_msg); + // println!("[fchdir] vfd: {:?}", virtual_fd); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "fchdir"); + } + return ret; + + } + + //------------------------------------CHDIR SYSCALL------------------------------------ + /* + * chdir() will return 0 when sucess, -1 when fail + */ + pub fn chdir_syscall(&self, path: &str) -> i32 { + let truepath = normpath(convpath(path), self); + + //at this point, syscall isn't an error + let mut cwd_container = self.cwd.write(); + + *cwd_container = interface::RustRfc::new(truepath); + 0 + } + + //------------------------------------DUP & DUP2 SYSCALLS------------------------------------ + /* + * dup() / dup2() will return a file descriptor + * Mapping a new virtual fd and kernel fd that libc::dup returned + * Then return virtual fd + */ + pub fn dup_syscall(&self, virtual_fd: i32, _start_desc: Option) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "dup", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + let ret_kernelfd = unsafe{ libc::dup(kernel_fd as i32) }; + let ret_virtualfd = get_unused_virtual_fd(self.cageid, ret_kernelfd as u64, false, 0).unwrap(); + return ret_virtualfd as i32; + + } + + /* + */ + pub fn dup2_syscall(&self, old_virtualfd: i32, new_virtualfd: i32) -> i32 { + match translate_virtual_fd(self.cageid, old_virtualfd as u64) { + Ok(old_kernelfd) => { + let new_kernelfd = unsafe { + libc::dup(old_kernelfd as i32) + }; + // Map new kernel fd with provided kernel fd + let _ret_kernelfd = unsafe{ libc::dup2(old_kernelfd as i32, new_kernelfd) }; + let optinfo = get_optionalinfo(self.cageid, old_virtualfd as u64).unwrap(); + let _ = get_specific_virtual_fd(self.cageid, new_virtualfd as u64, new_kernelfd as u64, false, optinfo).unwrap(); + return new_virtualfd; + }, + Err(_e) => { + return syscall_error(Errno::EBADF, "dup2", "Bad File Descriptor"); + } + } + + } + + //------------------------------------CLOSE SYSCALL------------------------------------ + /* + * Get the kernel fd with provided virtual fd first + * close() will return 0 when sucess, -1 when fail + */ + pub fn close_syscall(&self, virtual_fd: i32) -> i32 { + // println!(); + // println!("[CLOSE] REALFDCOUNT:"); + // io::stdout().flush().unwrap(); + // for entry in REALFDCOUNT.iter() { + // let key = entry.key(); + // let value = entry.value(); + // println!("realfd: {:?}, Value: {:?}", key, value); + // io::stdout().flush().unwrap(); + // } + // println!("[CLOSE] FDTABLE: "); + // io::stdout().flush().unwrap(); + // for entry in FDTABLE.iter() { + // let cageid = entry.key(); + // let fds = entry.value(); + // println!("cageid: {:?}", cageid); + // for (vfd, fd_entry) in fds.iter().enumerate() { + // if let Some(fd_entry) = fd_entry { + // println!(" virtual_fd: {}, FDTableEntry: {:?}", vfd, fd_entry); + // } + // } + // } + // io::stdout().flush().unwrap(); + // println!("[CLOSE] cageid: {:?}", self.cageid); + // println!("[CLOSE] vfd: {:?}", virtual_fd); + // io::stdout().flush().unwrap(); + match close_virtualfd(self.cageid, virtual_fd as u64) { + Ok(()) => { + return 0; + } + Err(_) => { + return -1; + } + } + + } + + + + //------------------------------------FCNTL SYSCALL------------------------------------ + /* + * For a successful call, the return value depends on the operation: + + F_DUPFD + The new file descriptor. + + F_GETFD + Value of file descriptor flags. + + F_GETFL + Value of file status flags. + + F_GETLEASE + Type of lease held on file descriptor. + + F_GETOWN + Value of file descriptor owner. + + F_GETSIG + Value of signal sent when read or write becomes possible, + or zero for traditional SIGIO behavior. + + F_GETPIPE_SZ, F_SETPIPE_SZ + The pipe capacity. + + F_GET_SEALS + A bit mask identifying the seals that have been set for + the inode referred to by fd. + + All other commands + Zero. + + On error, -1 is returned + */ + pub fn fcntl_syscall(&self, virtual_fd: i32, cmd: i32, arg: i32) -> i32 { + match (cmd, arg) { + (F_GETOWN, ..) => { + // + 1000 + } + (F_SETOWN, arg) if arg >= 0 => { + 0 + } + _ => { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "fcntl", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + if cmd == libc::F_DUPFD { + let new_kernelfd = unsafe { libc::fcntl(kernel_fd as i32, cmd, arg) }; + // Get status + let new_virtualfd = get_unused_virtual_fd(self.cageid, new_kernelfd as u64, false, 0).unwrap(); + return new_virtualfd as i32; + } + let ret = unsafe { libc::fcntl(kernel_fd as i32, cmd, arg) }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[fcntl] Error message: {:?}", err_msg); + // println!("[fcntl] vfd: {:?}", virtual_fd); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "fcntl"); + } + ret + } + } + + } + + //------------------------------------IOCTL SYSCALL------------------------------------ + /* + * The third argument is an untyped pointer to memory. It's traditionally char *argp + * (from the days before void * was valid C), and will be so named for this discussion. + * ioctl() will return 0 when success and -1 when fail. + * Note: A few ioctl() requests use the return value as an output parameter and return + * a nonnegative value on success. + */ + pub fn ioctl_syscall(&self, virtual_fd: i32, request: u64, ptrunion: *mut u8) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "ioctl", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + let ret = unsafe { libc::ioctl(kernel_fd as i32, request, ptrunion as *mut c_void) }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[ioctl] Error message: {:?}", err_msg); + // println!("[ioctl] vfd: {:?}", virtual_fd); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "ioctl"); + } + return ret; + + } + + + //------------------------------------CHMOD SYSCALL------------------------------------ + /* + * chmod() will return 0 when success and -1 when fail + */ + pub fn chmod_syscall(&self, path: &str, mode: u32) -> i32 { + // let c_path = CString::new(path).expect("CString::new failed"); + let relpath = normpath(convpath(path), self); + let relative_path = relpath.to_str().unwrap(); + let full_path = format!("{}{}", LIND_ROOT, relative_path); + let c_path = CString::new(full_path).unwrap(); + let ret = unsafe { + libc::chmod(c_path.as_ptr(), mode) + }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[chmod] Error message: {:?}", err_msg); + // println!("[chmod] c_path: {:?}", c_path); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "chmod"); + } + ret + } + + //------------------------------------FCHMOD SYSCALL------------------------------------ + /* + * Get the kernel fd with provided virtual fd first + * fchmod() will return 0 when sucess, -1 when fail + */ + pub fn fchmod_syscall(&self, virtual_fd: i32, mode: u32) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "fchmod", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + let ret = unsafe { + libc::fchmod(kernel_fd as i32, mode) + }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[fchmod] Error message: {:?}", err_msg); + // println!("[fchmod] vfd: {:?}", virtual_fd); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "fchmod"); + } + return ret; + + } + + //------------------------------------MMAP SYSCALL------------------------------------ + /* + * Get the kernel fd with provided virtual fd first + * mmap() will return: + * - a pointer to the mapped area, success + * - -1, fail + */ + pub fn mmap_syscall( + &self, + addr: *mut u8, + len: usize, + prot: i32, + flags: i32, + virtual_fd: i32, + off: i64, + ) -> i32 { + if virtual_fd != -1 { + match translate_virtual_fd(self.cageid, virtual_fd as u64) { + Ok(kernel_fd) => { + let ret = unsafe { + ((libc::mmap(addr as *mut c_void, len, prot, flags, kernel_fd as i32, off) as i64) + & 0xffffffff) as i32 + }; + // if ret < 0 { + // let errno = get_errno(); + // return handle_errno(errno, "mmap"); + // } + return ret; + }, + Err(_e) => { + return syscall_error(Errno::EBADF, "mmap", "Bad File Descriptor"); + } + } + } + + // Do type conversion to translate from c_void into i32 + unsafe { + ((libc::mmap(addr as *mut c_void, len, prot, flags, -1, off) as i64) + & 0xffffffff) as i32 + } + } + + //------------------------------------MUNMAP SYSCALL------------------------------------ + /* + * munmap() will return: + * - 0, success + * - -1, fail + */ + pub fn munmap_syscall(&self, addr: *mut u8, len: usize) -> i32 { + let ret = unsafe { + libc::munmap(addr as *mut c_void, len) + }; + if ret < 0 { + let errno = get_errno(); + return handle_errno(errno, "munmap"); + } + ret + } + + //------------------------------------FLOCK SYSCALL------------------------------------ + /* + * Get the kernel fd with provided virtual fd first + * flock() will return 0 when sucess, -1 when fail + */ + pub fn flock_syscall(&self, virtual_fd: i32, operation: i32) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "flock", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + let ret = unsafe { + libc::flock(kernel_fd as i32, operation) + }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[flock] Error message: {:?}", err_msg); + // println!("[flock] vfd: {:?}", virtual_fd); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "flock"); + } + return ret; + + } + + //------------------RMDIR SYSCALL------------------ + /* + * rmdir() will return 0 when sucess, -1 when fail + */ + pub fn rmdir_syscall(&self, path: &str) -> i32 { + // let c_path = CString::new(path).expect("CString::new failed"); + let relpath = normpath(convpath(path), self); + let relative_path = relpath.to_str().unwrap(); + let full_path = format!("{}{}", LIND_ROOT, relative_path); + let c_path = CString::new(full_path).unwrap(); + let ret = unsafe { + libc::rmdir(c_path.as_ptr()) + }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[rmdir] Error message: {:?}", err_msg); + // println!("[rmdir] c_path: {:?}", c_path); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "rmdir"); + } + ret + } + + //------------------RENAME SYSCALL------------------ + /* + * rename() will return 0 when sucess, -1 when fail + */ + pub fn rename_syscall(&self, oldpath: &str, newpath: &str) -> i32 { + let rel_oldpath = normpath(convpath(oldpath), self); + let relative_oldpath = rel_oldpath.to_str().unwrap(); + let full_oldpath = format!("{}{}", LIND_ROOT, relative_oldpath); + let old_cpath = CString::new(full_oldpath).unwrap(); + + let rel_newpath = normpath(convpath(newpath), self); + let relative_newpath = rel_newpath.to_str().unwrap(); + let full_newpath = format!("{}{}", LIND_ROOT, relative_newpath); + let new_cpath = CString::new(full_newpath).unwrap(); + + let ret = unsafe { + libc::rename(old_cpath.as_ptr(), new_cpath.as_ptr()) + }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[rename] Error message: {:?}", err_msg); + // println!("[rename] old: {:?}", old_cpath); + // println!("[rename] new: {:?}", new_cpath); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "rename"); + } + ret + } + + //------------------------------------FSYNC SYSCALL------------------------------------ + /* + * Get the kernel fd with provided virtual fd first + * fsync() will return 0 when sucess, -1 when fail + */ + pub fn fsync_syscall(&self, virtual_fd: i32) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "fsync", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + let ret = unsafe { + libc::fsync(kernel_fd as i32) + }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[fsync] Error message: {:?}", err_msg); + // println!("[fsync] vfd: {:?}", virtual_fd); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "fsync"); + } + return ret; + + } + + //------------------------------------FDATASYNC SYSCALL------------------------------------ + /* + * Get the kernel fd with provided virtual fd first + * fdatasync() will return 0 when sucess, -1 when fail + */ + pub fn fdatasync_syscall(&self, virtual_fd: i32) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "fdatasync", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + let ret = unsafe { + libc::fdatasync(kernel_fd as i32) + }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[fdatasync] Error message: {:?}", err_msg); + // println!("[fdatasync] vfd: {:?}", virtual_fd); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "fdatasync"); + } + return ret; + + } + + //------------------------------------SYNC_FILE_RANGE SYSCALL------------------------------------ + /* + * Get the kernel fd with provided virtual fd first + * sync_file_range() will return 0 when sucess, -1 when fail + */ + pub fn sync_file_range_syscall( + &self, + virtual_fd: i32, + offset: isize, + nbytes: isize, + flags: u32, + ) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "sync", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + let ret = unsafe { + libc::sync_file_range(kernel_fd as i32, offset as i64, nbytes as i64, flags) + }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[sync file range] Error message: {:?}", err_msg); + // println!("[sync file range] vfd: {:?}", virtual_fd); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "sync_file_range"); + } + ret + } + + //------------------FTRUNCATE SYSCALL------------------ + /* + * Get the kernel fd with provided virtual fd first + * ftruncate() will return 0 when sucess, -1 when fail + */ + pub fn ftruncate_syscall(&self, virtual_fd: i32, length: isize) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "ftruncate", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + let ret = unsafe { + libc::ftruncate(kernel_fd as i32, length as i64) + }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[ftruncate] Error message: {:?}", err_msg); + // println!("[ftruncate] vfd: {:?}", virtual_fd); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "ftruncate"); + } + ret + } + + //------------------TRUNCATE SYSCALL------------------ + /* + * truncate() will return 0 when sucess, -1 when fail + */ + pub fn truncate_syscall(&self, path: &str, length: isize) -> i32 { + // let c_path = CString::new(path).expect("CString::new failed"); + let relpath = normpath(convpath(path), self); + let relative_path = relpath.to_str().unwrap(); + let full_path = format!("{}{}", LIND_ROOT, relative_path); + let c_path = CString::new(full_path).unwrap(); + let ret = unsafe { + libc::truncate(c_path.as_ptr(), length as i64) + }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[truncate] Error message: {:?}", err_msg); + // println!("[truncate] c_path: {:?}", c_path); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "truncate"); + } + ret + } + + //------------------PIPE SYSCALL------------------ + /* + * When using the rust libc, a pipe file descriptor (pipefd) is typically an array + * containing two integers. This array is populated by the pipe system call, with one + * integer for the read end and the other for the write end. + * + * pipe() will return 0 when sucess, -1 when fail + */ + pub fn pipe_syscall(&self, pipefd: &mut PipeArray) -> i32 { + let mut kernel_fds = [0; 2]; + + let ret = unsafe { libc::pipe(kernel_fds.as_mut_ptr() as *mut i32) }; + if ret < 0 { + let errno = get_errno(); + return handle_errno(errno, "pipe"); + } + + pipefd.readfd = get_unused_virtual_fd(self.cageid, kernel_fds[0], false, 0).unwrap() as i32; + pipefd.writefd = get_unused_virtual_fd(self.cageid, kernel_fds[1], false, 0).unwrap() as i32; + + return ret; + } + + pub fn pipe2_syscall(&self, pipefd: &mut PipeArray, flags: i32) -> i32 { + let mut kernel_fds:[i32; 2] = [0; 2]; + + let ret = unsafe { libc::pipe2(kernel_fds.as_mut_ptr() as *mut i32, flags as i32) }; + if ret < 0 { + let errno = get_errno(); + return handle_errno(errno, "pipe2"); + } + + if flags == libc::O_CLOEXEC { + pipefd.readfd = get_unused_virtual_fd(self.cageid, kernel_fds[0] as u64, true, 0).unwrap() as i32; + pipefd.writefd = get_unused_virtual_fd(self.cageid, kernel_fds[1] as u64, true, 0).unwrap() as i32; + } else { + pipefd.readfd = get_unused_virtual_fd(self.cageid, kernel_fds[0] as u64, false, 0).unwrap() as i32; + pipefd.writefd = get_unused_virtual_fd(self.cageid, kernel_fds[1] as u64, false, 0).unwrap() as i32; + } + + return ret; + } + + //------------------GETDENTS SYSCALL------------------ + /* + * Get the kernel fd with provided virtual fd first + * getdents() will return: + * - the number of bytes read is returned, success + * - 0, EOF + * - -1, fail + */ + + pub fn getdents_syscall(&self, virtual_fd: i32, buf: *mut u8, nbytes: u32) -> i32 { + // let kernel_fd = translate_virtual_fd(self.cageid, virtual_fd as u64); + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "getdents", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + let ret = unsafe { libc::syscall(libc::SYS_getdents as c_long, kernel_fd as i32, buf as *mut c_void, nbytes) as i32 }; + if ret < 0 { + let errno = get_errno(); + return handle_errno(errno, "getdents"); + } + ret + } + + //------------------------------------GETCWD SYSCALL------------------------------------ + /* + * getcwd() will return: + * - a pointer to a string containing the pathname of the current working directory, success + * - null, fail + */ + pub fn getcwd_syscall(&self, buf: *mut u8, bufsize: u32) -> i32 { + let cwd_container = self.cwd.read(); + let path = cwd_container.to_str().unwrap(); + if path.len() >= bufsize as usize { + return -1; + } + unsafe { + ptr::copy(path.as_ptr(), buf, path.len()); + *buf.add(path.len()) = 0; + } + 0 + } + + //------------------SHMHELPERS---------------------- + + pub fn rev_shm_find_index_by_addr(rev_shm: &Vec<(u32, i32)>, shmaddr: u32) -> Option { + for (index, val) in rev_shm.iter().enumerate() { + if val.0 == shmaddr as u32 { + return Some(index); + } + } + None + } + + pub fn rev_shm_find_addrs_by_shmid(rev_shm: &Vec<(u32, i32)>, shmid: i32) -> Vec { + let mut addrvec = Vec::new(); + for val in rev_shm.iter() { + if val.1 == shmid as i32 { + addrvec.push(val.0); + } + } + + return addrvec; + } + + pub fn search_for_addr_in_region( + rev_shm: &Vec<(u32, i32)>, + search_addr: u32, + ) -> Option<(u32, i32)> { + let metadata = &SHM_METADATA; + for val in rev_shm.iter() { + let addr = val.0; + let shmid = val.1; + if let Some(segment) = metadata.shmtable.get_mut(&shmid) { + let range = addr..(addr + segment.size as u32); + if range.contains(&search_addr) { + return Some((addr, shmid)); + } + } + } + None + } + + //------------------SHMGET SYSCALL------------------ + + pub fn shmget_syscall(&self, key: i32, size: usize, shmflg: i32) -> i32 { + if key == IPC_PRIVATE { + return syscall_error(Errno::ENOENT, "shmget", "IPC_PRIVATE not implemented"); + } + let shmid: i32; + let metadata = &SHM_METADATA; + + match metadata.shmkeyidtable.entry(key) { + interface::RustHashEntry::Occupied(occupied) => { + if (IPC_CREAT | IPC_EXCL) == (shmflg & (IPC_CREAT | IPC_EXCL)) { + return syscall_error( + Errno::EEXIST, + "shmget", + "key already exists and IPC_CREAT and IPC_EXCL were used", + ); + } + shmid = *occupied.get(); + } + interface::RustHashEntry::Vacant(vacant) => { + if 0 == (shmflg & IPC_CREAT) { + return syscall_error( + Errno::ENOENT, + "shmget", + "tried to use a key that did not exist, and IPC_CREAT was not specified", + ); + } + + if (size as u32) < SHMMIN || (size as u32) > SHMMAX { + return syscall_error( + Errno::EINVAL, + "shmget", + "Size is less than SHMMIN or more than SHMMAX", + ); + } + + shmid = metadata.new_keyid(); + vacant.insert(shmid); + let mode = (shmflg & 0x1FF) as u16; // mode is 9 least signficant bits of shmflag, even if we dont really do anything with them + + let segment = new_shm_segment( + key, + size, + self.cageid as u32, + DEFAULT_UID, + DEFAULT_GID, + mode, + ); + metadata.shmtable.insert(shmid, segment); + } + }; + shmid // return the shmid + } + + //------------------SHMAT SYSCALL------------------ + + pub fn shmat_syscall(&self, shmid: i32, shmaddr: *mut u8, shmflg: i32) -> i32 { + let metadata = &SHM_METADATA; + let prot: i32; + if let Some(mut segment) = metadata.shmtable.get_mut(&shmid) { + if 0 != (shmflg & fs_constants::SHM_RDONLY) { + prot = PROT_READ; + } else { + prot = PROT_READ | PROT_WRITE; + } + let mut rev_shm = self.rev_shm.lock(); + rev_shm.push((shmaddr as u32, shmid)); + drop(rev_shm); + + // update semaphores + if !segment.semaphor_offsets.is_empty() { + // lets just look at the first cage in the set, since we only need to grab the ref from one + if let Some(cageid) = segment + .attached_cages + .clone() + .into_read_only() + .keys() + .next() + { + let cage2 = interface::cagetable_getref(*cageid); + let cage2_rev_shm = cage2.rev_shm.lock(); + let addrs = Self::rev_shm_find_addrs_by_shmid(&cage2_rev_shm, shmid); // find all the addresses assoc. with shmid + for offset in segment.semaphor_offsets.iter() { + let sementry = cage2.sem_table.get(&(addrs[0] + *offset)).unwrap().clone(); //add semaphors into semtable at addr + offsets + self.sem_table.insert(shmaddr as u32 + *offset, sementry); + } + } + } + + segment.map_shm(shmaddr, prot, self.cageid) + } else { + syscall_error(Errno::EINVAL, "shmat", "Invalid shmid value") + } + } + + //------------------SHMDT SYSCALL------------------ + + pub fn shmdt_syscall(&self, shmaddr: *mut u8) -> i32 { + let metadata = &SHM_METADATA; + let mut rm = false; + let mut rev_shm = self.rev_shm.lock(); + let rev_shm_index = Self::rev_shm_find_index_by_addr(&rev_shm, shmaddr as u32); + + if let Some(index) = rev_shm_index { + let shmid = rev_shm[index].1; + match metadata.shmtable.entry(shmid) { + interface::RustHashEntry::Occupied(mut occupied) => { + let segment = occupied.get_mut(); + + // update semaphores + for offset in segment.semaphor_offsets.iter() { + self.sem_table.remove(&(shmaddr as u32 + *offset)); + } + + segment.unmap_shm(shmaddr, self.cageid); + + if segment.rmid && segment.shminfo.shm_nattch == 0 { + rm = true; + } + rev_shm.swap_remove(index); + + if rm { + let key = segment.key; + occupied.remove_entry(); + metadata.shmkeyidtable.remove(&key); + } + + return shmid; //NaCl relies on this non-posix behavior of returning the shmid on success + } + interface::RustHashEntry::Vacant(_) => { + panic!("Inode not created for some reason"); + } + }; + } else { + return syscall_error( + Errno::EINVAL, + "shmdt", + "No shared memory segment at shmaddr", + ); + } + } + + //------------------SHMCTL SYSCALL------------------ + + pub fn shmctl_syscall(&self, shmid: i32, cmd: i32, buf: Option<&mut ShmidsStruct>) -> i32 { + let metadata = &SHM_METADATA; + + if let Some(mut segment) = metadata.shmtable.get_mut(&shmid) { + match cmd { + IPC_STAT => { + *buf.unwrap() = segment.shminfo; + } + IPC_RMID => { + segment.rmid = true; + segment.shminfo.shm_perm.mode |= SHM_DEST as u16; + if segment.shminfo.shm_nattch == 0 { + let key = segment.key; + drop(segment); + metadata.shmtable.remove(&shmid); + metadata.shmkeyidtable.remove(&key); + } + } + _ => { + return syscall_error( + Errno::EINVAL, + "shmctl", + "Arguments provided do not match implemented parameters", + ); + } + } + } else { + return syscall_error(Errno::EINVAL, "shmctl", "Invalid identifier"); + } + + 0 //shmctl has succeeded! + } + + //------------------MUTEX SYSCALLS------------------ + pub fn mutex_create_syscall(&self) -> i32 { + let mut mutextable = self.mutex_table.write(); + let mut index_option = None; + for i in 0..mutextable.len() { + if mutextable[i].is_none() { + index_option = Some(i); + break; + } + } + + let index = if let Some(ind) = index_option { + ind + } else { + mutextable.push(None); + mutextable.len() - 1 + }; + + let mutex_result = interface::RawMutex::create(); + match mutex_result { + Ok(mutex) => { + mutextable[index] = Some(interface::RustRfc::new(mutex)); + index as i32 + } + Err(_) => match Errno::from_discriminant(interface::get_errno()) { + Ok(i) => syscall_error( + i, + "mutex_create", + "The libc call to pthread_mutex_init failed!", + ), + Err(()) => panic!("Unknown errno value from pthread_mutex_init returned!"), + }, + } + } + + pub fn mutex_destroy_syscall(&self, mutex_handle: i32) -> i32 { + let mut mutextable = self.mutex_table.write(); + if mutex_handle < mutextable.len() as i32 + && mutex_handle >= 0 + && mutextable[mutex_handle as usize].is_some() + { + mutextable[mutex_handle as usize] = None; + 0 + } else { + //undefined behavior + syscall_error( + Errno::EBADF, + "mutex_destroy", + "Mutex handle does not refer to a valid mutex!", + ) + } + //the RawMutex is destroyed on Drop + + //this is currently assumed to always succeed, as the man page does not list possible + //errors for pthread_mutex_destroy + } + + pub fn mutex_lock_syscall(&self, mutex_handle: i32) -> i32 { + let mutextable = self.mutex_table.read(); + if mutex_handle < mutextable.len() as i32 + && mutex_handle >= 0 + && mutextable[mutex_handle as usize].is_some() + { + let clonedmutex = mutextable[mutex_handle as usize].as_ref().unwrap().clone(); + drop(mutextable); + let retval = clonedmutex.lock(); + + if retval < 0 { + match Errno::from_discriminant(interface::get_errno()) { + Ok(i) => { + return syscall_error( + i, + "mutex_lock", + "The libc call to pthread_mutex_lock failed!", + ); + } + Err(()) => panic!("Unknown errno value from pthread_mutex_lock returned!"), + }; + } + + retval + } else { + //undefined behavior + syscall_error( + Errno::EBADF, + "mutex_lock", + "Mutex handle does not refer to a valid mutex!", + ) + } + } + + pub fn mutex_trylock_syscall(&self, mutex_handle: i32) -> i32 { + let mutextable = self.mutex_table.read(); + if mutex_handle < mutextable.len() as i32 + && mutex_handle >= 0 + && mutextable[mutex_handle as usize].is_some() + { + let clonedmutex = mutextable[mutex_handle as usize].as_ref().unwrap().clone(); + drop(mutextable); + let retval = clonedmutex.trylock(); + + if retval < 0 { + match Errno::from_discriminant(interface::get_errno()) { + Ok(i) => { + return syscall_error( + i, + "mutex_trylock", + "The libc call to pthread_mutex_trylock failed!", + ); + } + Err(()) => panic!("Unknown errno value from pthread_mutex_trylock returned!"), + }; + } + + retval + } else { + //undefined behavior + syscall_error( + Errno::EBADF, + "mutex_trylock", + "Mutex handle does not refer to a valid mutex!", + ) + } + } + + pub fn mutex_unlock_syscall(&self, mutex_handle: i32) -> i32 { + let mutextable = self.mutex_table.read(); + if mutex_handle < mutextable.len() as i32 + && mutex_handle >= 0 + && mutextable[mutex_handle as usize].is_some() + { + let clonedmutex = mutextable[mutex_handle as usize].as_ref().unwrap().clone(); + drop(mutextable); + let retval = clonedmutex.unlock(); + + if retval < 0 { + match Errno::from_discriminant(interface::get_errno()) { + Ok(i) => { + return syscall_error( + i, + "mutex_unlock", + "The libc call to pthread_mutex_unlock failed!", + ); + } + Err(()) => panic!("Unknown errno value from pthread_mutex_unlock returned!"), + }; + } + + retval + } else { + //undefined behavior + syscall_error( + Errno::EBADF, + "mutex_unlock", + "Mutex handle does not refer to a valid mutex!", + ) + } + } + + //------------------CONDVAR SYSCALLS------------------ + + pub fn cond_create_syscall(&self) -> i32 { + let mut cvtable = self.cv_table.write(); + let mut index_option = None; + for i in 0..cvtable.len() { + if cvtable[i].is_none() { + index_option = Some(i); + break; + } + } + + let index = if let Some(ind) = index_option { + ind + } else { + cvtable.push(None); + cvtable.len() - 1 + }; + + let cv_result = interface::RawCondvar::create(); + match cv_result { + Ok(cv) => { + cvtable[index] = Some(interface::RustRfc::new(cv)); + index as i32 + } + Err(_) => match Errno::from_discriminant(interface::get_errno()) { + Ok(i) => syscall_error( + i, + "cond_create", + "The libc call to pthread_cond_init failed!", + ), + Err(()) => panic!("Unknown errno value from pthread_cond_init returned!"), + }, + } + } + + pub fn cond_destroy_syscall(&self, cv_handle: i32) -> i32 { + let mut cvtable = self.cv_table.write(); + if cv_handle < cvtable.len() as i32 + && cv_handle >= 0 + && cvtable[cv_handle as usize].is_some() + { + cvtable[cv_handle as usize] = None; + 0 + } else { + //undefined behavior + syscall_error( + Errno::EBADF, + "cond_destroy", + "Condvar handle does not refer to a valid condvar!", + ) + } + //the RawCondvar is destroyed on Drop + + //this is currently assumed to always succeed, as the man page does not list possible + //errors for pthread_cv_destroy + } + + pub fn cond_signal_syscall(&self, cv_handle: i32) -> i32 { + let cvtable = self.cv_table.read(); + if cv_handle < cvtable.len() as i32 + && cv_handle >= 0 + && cvtable[cv_handle as usize].is_some() + { + let clonedcv = cvtable[cv_handle as usize].as_ref().unwrap().clone(); + drop(cvtable); + let retval = clonedcv.signal(); + + if retval < 0 { + match Errno::from_discriminant(interface::get_errno()) { + Ok(i) => { + return syscall_error( + i, + "cond_signal", + "The libc call to pthread_cond_signal failed!", + ); + } + Err(()) => panic!("Unknown errno value from pthread_cond_signal returned!"), + }; + } + + retval + } else { + //undefined behavior + syscall_error( + Errno::EBADF, + "cond_signal", + "Condvar handle does not refer to a valid condvar!", + ) + } + } + + pub fn cond_broadcast_syscall(&self, cv_handle: i32) -> i32 { + let cvtable = self.cv_table.read(); + if cv_handle < cvtable.len() as i32 + && cv_handle >= 0 + && cvtable[cv_handle as usize].is_some() + { + let clonedcv = cvtable[cv_handle as usize].as_ref().unwrap().clone(); + drop(cvtable); + let retval = clonedcv.broadcast(); + + if retval < 0 { + match Errno::from_discriminant(interface::get_errno()) { + Ok(i) => { + return syscall_error( + i, + "cond_broadcast", + "The libc call to pthread_cond_broadcast failed!", + ); + } + Err(()) => panic!("Unknown errno value from pthread_cond_broadcast returned!"), + }; + } + + retval + } else { + //undefined behavior + syscall_error( + Errno::EBADF, + "cond_broadcast", + "Condvar handle does not refer to a valid condvar!", + ) + } + } + + pub fn cond_wait_syscall(&self, cv_handle: i32, mutex_handle: i32) -> i32 { + let cvtable = self.cv_table.read(); + if cv_handle < cvtable.len() as i32 + && cv_handle >= 0 + && cvtable[cv_handle as usize].is_some() + { + let clonedcv = cvtable[cv_handle as usize].as_ref().unwrap().clone(); + drop(cvtable); + + let mutextable = self.mutex_table.read(); + if mutex_handle < mutextable.len() as i32 + && mutex_handle >= 0 + && mutextable[mutex_handle as usize].is_some() + { + let clonedmutex = mutextable[mutex_handle as usize].as_ref().unwrap().clone(); + drop(mutextable); + let retval = clonedcv.wait(&*clonedmutex); + + // if the cancel status is set in the cage, we trap around a cancel point + // until the individual thread is signaled to cancel itself + if self + .cancelstatus + .load(interface::RustAtomicOrdering::Relaxed) + { + loop { + interface::cancelpoint(self.cageid); + } // we check cancellation status here without letting the function return + } + + if retval < 0 { + match Errno::from_discriminant(interface::get_errno()) { + Ok(i) => { + return syscall_error( + i, + "cond_wait", + "The libc call to pthread_cond_wait failed!", + ); + } + Err(()) => panic!("Unknown errno value from pthread_cond_wait returned!"), + }; + } + + retval + } else { + //undefined behavior + syscall_error( + Errno::EBADF, + "cond_wait", + "Mutex handle does not refer to a valid mutex!", + ) + } + } else { + //undefined behavior + syscall_error( + Errno::EBADF, + "cond_wait", + "Condvar handle does not refer to a valid condvar!", + ) + } + } + + pub fn cond_timedwait_syscall( + &self, + cv_handle: i32, + mutex_handle: i32, + time: interface::RustDuration, + ) -> i32 { + let cvtable = self.cv_table.read(); + if cv_handle < cvtable.len() as i32 + && cv_handle >= 0 + && cvtable[cv_handle as usize].is_some() + { + let clonedcv = cvtable[cv_handle as usize].as_ref().unwrap().clone(); + drop(cvtable); + + let mutextable = self.mutex_table.read(); + if mutex_handle < mutextable.len() as i32 + && mutex_handle >= 0 + && mutextable[mutex_handle as usize].is_some() + { + let clonedmutex = mutextable[mutex_handle as usize].as_ref().unwrap().clone(); + drop(mutextable); + let retval = clonedcv.timedwait(&*clonedmutex, time); + if retval < 0 { + match Errno::from_discriminant(interface::get_errno()) { + Ok(i) => { + return syscall_error( + i, + "cond_wait", + "The libc call to pthread_cond_wait failed!", + ); + } + Err(()) => panic!("Unknown errno value from pthread_cond_wait returned!"), + }; + } + + retval + } else { + //undefined behavior + syscall_error( + Errno::EBADF, + "cond_wait", + "Mutex handle does not refer to a valid mutex!", + ) + } + } else { + //undefined behavior + syscall_error( + Errno::EBADF, + "cond_wait", + "Condvar handle does not refer to a valid condvar!", + ) + } + } + + //------------------SEMAPHORE SYSCALLS------------------ + /* + * Initialize semaphore object SEM to value + * pshared used to indicate whether the semaphore is shared in threads (when equals to 0) + * or shared between processes (when nonzero) + */ + pub fn sem_init_syscall(&self, sem_handle: u32, pshared: i32, value: u32) -> i32 { + // Boundary check + if value > SEM_VALUE_MAX { + return syscall_error(Errno::EINVAL, "sem_init", "value exceeds SEM_VALUE_MAX"); + } + + let metadata = &SHM_METADATA; + let is_shared = pshared != 0; + + // Iterate semaphore table, if semaphore is already initialzed return error + let semtable = &self.sem_table; + + // Will initialize only it's new + if !semtable.contains_key(&sem_handle) { + let new_semaphore = + interface::RustRfc::new(interface::RustSemaphore::new(value, is_shared)); + semtable.insert(sem_handle, new_semaphore.clone()); + + if is_shared { + let rev_shm = self.rev_shm.lock(); + // if its shared and exists in an existing mapping we need to add it to other cages + if let Some((mapaddr, shmid)) = + Self::search_for_addr_in_region(&rev_shm, sem_handle) + { + let offset = mapaddr - sem_handle; + if let Some(segment) = metadata.shmtable.get_mut(&shmid) { + for cageid in segment.attached_cages.clone().into_read_only().keys() { + // iterate through all cages with segment attached and add semaphor in segments at attached addr + offset + let cage = interface::cagetable_getref(*cageid); + let addrs = Self::rev_shm_find_addrs_by_shmid(&rev_shm, shmid); + for addr in addrs.iter() { + cage.sem_table.insert(addr + offset, new_semaphore.clone()); + } + } + segment.semaphor_offsets.insert(offset); + } + } + } + return 0; + } + + return syscall_error(Errno::EBADF, "sem_init", "semaphore already initialized"); + } + + pub fn sem_wait_syscall(&self, sem_handle: u32) -> i32 { + let semtable = &self.sem_table; + // Check whether semaphore exists + if let Some(sementry) = semtable.get_mut(&sem_handle) { + let semaphore = sementry.clone(); + drop(sementry); + semaphore.lock(); + } else { + return syscall_error(Errno::EINVAL, "sem_wait", "sem is not a valid semaphore"); + } + return 0; + } + + pub fn sem_post_syscall(&self, sem_handle: u32) -> i32 { + let semtable = &self.sem_table; + if let Some(sementry) = semtable.get_mut(&sem_handle) { + let semaphore = sementry.clone(); + drop(sementry); + if !semaphore.unlock() { + return syscall_error( + Errno::EOVERFLOW, + "sem_post", + "The maximum allowable value for a semaphore would be exceeded", + ); + } + } else { + return syscall_error(Errno::EINVAL, "sem_wait", "sem is not a valid semaphore"); + } + return 0; + } + + pub fn sem_destroy_syscall(&self, sem_handle: u32) -> i32 { + let metadata = &SHM_METADATA; + + let semtable = &self.sem_table; + // remove entry from semaphore table + if let Some(sementry) = semtable.remove(&sem_handle) { + if sementry + .1 + .is_shared + .load(interface::RustAtomicOrdering::Relaxed) + { + // if its shared we'll need to remove it from other attachments + let rev_shm = self.rev_shm.lock(); + if let Some((mapaddr, shmid)) = + Self::search_for_addr_in_region(&rev_shm, sem_handle) + { + // find all segments that contain semaphore + let offset = mapaddr - sem_handle; + if let Some(segment) = metadata.shmtable.get_mut(&shmid) { + for cageid in segment.attached_cages.clone().into_read_only().keys() { + // iterate through all cages containing segment + let cage = interface::cagetable_getref(*cageid); + let addrs = Self::rev_shm_find_addrs_by_shmid(&rev_shm, shmid); + for addr in addrs.iter() { + cage.sem_table.remove(&(addr + offset)); //remove semapoores at attached addresses + the offset + } + } + } + } + } + return 0; + } else { + return syscall_error(Errno::EINVAL, "sem_destroy", "sem is not a valid semaphore"); + } + } + + /* + * Take only sem_t *sem as argument, and return int *sval + */ + pub fn sem_getvalue_syscall(&self, sem_handle: u32) -> i32 { + let semtable = &self.sem_table; + if let Some(sementry) = semtable.get_mut(&sem_handle) { + let semaphore = sementry.clone(); + drop(sementry); + return semaphore.get_value(); + } + return syscall_error( + Errno::EINVAL, + "sem_getvalue", + "sem is not a valid semaphore", + ); + } + + pub fn sem_trywait_syscall(&self, sem_handle: u32) -> i32 { + let semtable = &self.sem_table; + // Check whether semaphore exists + if let Some(sementry) = semtable.get_mut(&sem_handle) { + let semaphore = sementry.clone(); + drop(sementry); + if !semaphore.trylock() { + return syscall_error( + Errno::EAGAIN, + "sem_trywait", + "The operation could not be performed without blocking", + ); + } + } else { + return syscall_error(Errno::EINVAL, "sem_trywait", "sem is not a valid semaphore"); + } + return 0; + } + + pub fn sem_timedwait_syscall(&self, sem_handle: u32, time: interface::RustDuration) -> i32 { + let abstime = libc::timespec { + tv_sec: time.as_secs() as i64, + tv_nsec: (time.as_nanos() % 1000000000) as i64, + }; + if abstime.tv_nsec < 0 { + return syscall_error(Errno::EINVAL, "sem_timedwait", "Invalid timedout"); + } + let semtable = &self.sem_table; + // Check whether semaphore exists + if let Some(sementry) = semtable.get_mut(&sem_handle) { + let semaphore = sementry.clone(); + drop(sementry); + if !semaphore.timedlock(time) { + return syscall_error( + Errno::ETIMEDOUT, + "sem_timedwait", + "The call timed out before the semaphore could be locked", + ); + } + } else { + return syscall_error( + Errno::EINVAL, + "sem_timedwait", + "sem is not a valid semaphore", + ); + } + return 0; + } +} + +pub fn kernel_close(kernelfd: u64) { + // println!("[KERNEL CLOSE] cageid: {:?}", self.cageid); + // println!("[KERNEL CLOSE] realfd: {:?}", kernelfd); + // io::stdout().flush().unwrap(); + + let ret = unsafe { + libc::close(kernelfd as i32) + }; + if ret != 0 { + let err = unsafe { + *libc::__errno_location() + }; + let err_str = unsafe { + libc::strerror(err) + }; + let err_msg = unsafe { + CStr::from_ptr(err_str).to_string_lossy().into_owned() + }; + println!("errno: {:?}", err); + println!("Error message: {:?}", err_msg); + println!("kernel fd: {:?}", kernelfd); + io::stdout().flush().unwrap(); + panic!("kernel close failed! "); + } +} \ No newline at end of file diff --git a/src/safeposix/syscalls/fs_constants.rs b/src/safeposix/syscalls/fs_constants.rs new file mode 100644 index 00000000..e316539b --- /dev/null +++ b/src/safeposix/syscalls/fs_constants.rs @@ -0,0 +1,196 @@ +// File system related constants +#![allow(dead_code)] +#![allow(unused_variables)] + +use crate::interface; + +// Define constants using static or const +// Imported into fs_calls file +// pub const DT_UNKNOWN: u8 = 0; + +// pub const STARTINGFD: i32 = 0; +// pub const MAXFD: i32 = 1024; +// pub const STARTINGPIPE: i32 = 0; +// pub const MAXPIPE: i32 = 1024; + +// pub const ROOTDIRECTORYINODE: usize = 1; +// pub const STREAMINODE: usize = 2; + +// pub const PIPE_CAPACITY: usize = 65536; + +// pub const F_OK: u32 = 0; +// pub const X_OK: u32 = 1; +// pub const W_OK: u32 = 2; +// pub const R_OK: u32 = 4; + +// pub const O_RDONLY: i32 = 0o0; +// pub const O_WRONLY: i32 = 0o1; +// pub const O_RDWR: i32 = 0o2; +// pub const O_RDWRFLAGS: i32 = 0o3; + +// pub const O_CREAT: i32 = 0o100; +// pub const O_EXCL: i32 = 0o200; +// pub const O_NOCTTY: i32 = 0o400; +// pub const O_TRUNC: i32 = 0o1000; +// pub const O_APPEND: i32 = 0o2000; +// pub const O_NONBLOCK: i32 = 0o4000; +// // O_NDELAY=O_NONBLOCK +// pub const O_SYNC: i32 = 0o10000; +// // O_FSYNC=O_SYNC +// pub const O_ASYNC: i32 = 0o20000; +// pub const O_CLOEXEC: i32 = 0o2000000; + +// pub const DEFAULTTIME: u64 = 1323630836; + +// //Standard flag combinations +// pub const S_IRWXA: u32 = 0o777; +// pub const S_IRWXU: u32 = 0o700; +// pub const S_IRUSR: u32 = 0o400; +// pub const S_IWUSR: u32 = 0o200; +// pub const S_IXUSR: u32 = 0o100; +// pub const S_IRWXG: u32 = 0o070; +// pub const S_IRGRP: u32 = 0o040; +// pub const S_IWGRP: u32 = 0o020; +// pub const S_IXGRP: u32 = 0o010; +// pub const S_IRWXO: u32 = 0o007; +// pub const S_IROTH: u32 = 0o004; +// pub const S_IWOTH: u32 = 0o002; +// pub const S_IXOTH: u32 = 0o001; + +// //Commands for FCNTL +// pub const F_DUPFD: i32 = 0; +// pub const F_GETFD: i32 = 1; +// pub const F_SETFD: i32 = 2; +// pub const F_GETFL: i32 = 3; +// pub const F_SETFL: i32 = 4; +// pub const F_GETLK: i32 = 5; +// pub const F_GETLK64: i32 = 5; +// pub const F_SETLK: i32 = 6; +// pub const F_SETLK64: i32 = 6; +// pub const F_SETLKW: i32 = 7; +// pub const F_SETLKW64: i32 = 7; +// pub const F_SETOWN: i32 = 8; +// pub const F_GETOWN: i32 = 9; +// pub const F_SETSIG: i32 = 10; +// pub const F_GETSIG: i32 = 11; +// pub const F_SETLEASE: i32 = 1024; +// pub const F_GETLEASE: i32 = 1025; +// pub const F_NOTIFY: i32 = 1026; + +// //Commands for IOCTL +// pub const FIONBIO: u32 = 21537; +// pub const FIOASYNC: u32 = 21586; + +// //File types for open/stat etc. +// pub const S_IFBLK: i32 = 0o60000; +// pub const S_IFCHR: i32 = 0o20000; +// pub const S_IFDIR: i32 = 0o40000; +// pub const S_IFIFO: i32 = 0o10000; +// pub const S_IFLNK: i32 = 0o120000; +// pub const S_IFREG: i32 = 0o100000; +// pub const S_IFSOCK: i32 = 0o140000; +// pub const S_FILETYPEFLAGS: i32 = 0o170000; + +// //for flock syscall +// pub const LOCK_SH: i32 = 1; +// pub const LOCK_EX: i32 = 2; +// pub const LOCK_UN: i32 = 8; +// pub const LOCK_NB: i32 = 4; +// //for mmap/munmap syscall +// pub const MAP_SHARED: i32 = 1; +// pub const MAP_PRIVATE: i32 = 2; +// pub const MAP_FIXED: i32 = 16; +// pub const MAP_ANONYMOUS: i32 = 32; +// pub const MAP_HUGE_SHIFT: i32 = 26; +// pub const MAP_HUGETLB: i32 = 262144; + +// pub const PROT_NONE: i32 = 0; +// pub const PROT_READ: i32 = 1; +// pub const PROT_WRITE: i32 = 2; +// pub const PROT_EXEC: i32 = 4; + +// pub const SEEK_SET: i32 = 0; +// pub const SEEK_CUR: i32 = 1; +// pub const SEEK_END: i32 = 2; + +// pub const IPC_PRIVATE: i32 = 0o0; +// pub const IPC_CREAT: i32 = 0o1000; +// pub const IPC_EXCL: i32 = 0o2000; + +// pub const IPC_RMID: i32 = 0; +// pub const IPC_SET: i32 = 1; +// pub const IPC_STAT: i32 = 2; + +pub const SHM_DEST: i32 = 0o1000; +pub const SHM_LOCKED: i32 = 0o2000; +pub const SHM_HUGETLB: i32 = 0o4000; + +pub const SHM_R: i32 = 0o400; +pub const SHM_W: i32 = 0o200; +pub const SHM_RDONLY: i32 = 0o10000; +pub const SHM_RND: i32 = 0o20000; +pub const SHM_REMAP: i32 = 0o40000; +pub const SHM_EXEC: i32 = 0o100000; + +pub const SHMMIN: u32 = 1; +pub const SHMMNI: u32 = 4096; +pub const SHMMAX: u32 = 4278190079; // (ULONG_MAX - (1UL << 24)) +pub const SHMALL: u32 = 4278190079; // (ULONG_MAX - (1UL << 24)); +pub const SHMSEG: u32 = SHMMNI; + +pub const SEM_VALUE_MAX: u32 = 2147483647; + +// //device info for char files +// #[derive(interface::SerdeSerialize, interface::SerdeDeserialize, PartialEq, Eq, Debug)] +// pub struct DevNo { +// pub major: u32, +// pub minor: u32, +// } +// pub const NULLDEVNO: DevNo = DevNo { major: 1, minor: 3 }; +// pub const ZERODEVNO: DevNo = DevNo { major: 1, minor: 5 }; +// pub const RANDOMDEVNO: DevNo = DevNo { major: 1, minor: 8 }; +// pub const URANDOMDEVNO: DevNo = DevNo { major: 1, minor: 9 }; + +// pub const FILEDATAPREFIX: &str = "linddata."; + +// pub fn is_reg(mode: u32) -> bool { +// (mode as i32 & S_FILETYPEFLAGS) == S_IFREG +// } + +// pub fn is_chr(mode: u32) -> bool { +// (mode as i32 & S_FILETYPEFLAGS) == S_IFCHR +// } + +// pub fn is_dir(mode: u32) -> bool { +// (mode as i32 & S_FILETYPEFLAGS) == S_IFDIR +// } + +// pub fn is_wronly(flags: i32) -> bool { +// (flags & O_RDWRFLAGS) == O_WRONLY +// } +// pub fn is_rdonly(flags: i32) -> bool { +// (flags & O_RDWRFLAGS) == O_RDONLY +// } + +// //the same as the glibc makedev +// pub fn makedev(dev: &DevNo) -> u64 { +// ((dev.major as u64 & 0x00000fff) << 8) +// | ((dev.major as u64 & 0xfffff000) << 32) +// | ((dev.minor as u64 & 0x000000ff) << 0) +// | ((dev.minor as u64 & 0xffffff00) << 12) +// } + +// //the same as the glibc major and minor functions +// pub fn major(devnum: u64) -> u32 { +// (((devnum & 0x00000000000fff00) >> 8) | ((devnum & 0xfffff00000000000) >> 32)) as u32 +// } +// pub fn minor(devnum: u64) -> u32 { +// (((devnum & 0x00000000000000ff) >> 0) | ((devnum & 0x00000ffffff00000) >> 12)) as u32 +// } + +// pub fn devtuple(devnum: u64) -> DevNo { +// DevNo { +// major: major(devnum), +// minor: minor(devnum), +// } +// } diff --git a/src/safeposix/syscalls/mod.rs b/src/safeposix/syscalls/mod.rs new file mode 100644 index 00000000..51b60b43 --- /dev/null +++ b/src/safeposix/syscalls/mod.rs @@ -0,0 +1,12 @@ +pub mod fs_calls; +pub mod fs_constants; +pub mod net_calls; +pub mod net_constants; +pub mod sys_calls; +pub mod sys_constants; +pub use fs_calls::*; +pub use fs_constants::*; +pub use net_calls::*; +pub use net_constants::*; +pub use sys_calls::*; +pub use sys_constants::*; diff --git a/src/safeposix/syscalls/net_calls.rs b/src/safeposix/syscalls/net_calls.rs new file mode 100644 index 00000000..1989130d --- /dev/null +++ b/src/safeposix/syscalls/net_calls.rs @@ -0,0 +1,1450 @@ +#![allow(dead_code)] +// Network related system calls +// outlines and implements all of the networking system calls that are being emulated/faked in Lind + +use super::net_constants::*; +use crate::{interface::FdSet, safeposix::cage::*}; +use crate::interface::*; + +// use crate::example_grates::vanillaglobal::*; +use crate::example_grates::dashmapvecglobal::*; +// use crate::example_grates::muthashmaxglobal::*; +// use crate::example_grates::dashmaparrayglobal::*; + +use std::collections::HashSet; +use std::collections::HashMap; +use std::convert::TryInto; +use parking_lot::Mutex; +use lazy_static::lazy_static; +use std::io::{Read, Write}; +use std::io; +use std::mem::size_of; +use libc::*; +use std::ffi::CString; +use std::ffi::CStr; + +use libc::*; +use std::{os::fd::RawFd, ptr}; +use bit_set::BitSet; + +static LIND_ROOT: &str = "/home/lind/lind_project/src/safeposix-rust/tmp/"; + +lazy_static! { + // A hashmap used to store epoll mapping relationships + // > + static ref REAL_EPOLL_MAP: Mutex>> = Mutex::new(HashMap::new()); +} + +impl Cage { + /* + * Mapping a new virtual fd and kernel fd that libc::socket returned + * Then return virtual fd + */ + pub fn socket_syscall(&self, domain: i32, socktype: i32, protocol: i32) -> i32 { + let kernel_fd = unsafe { libc::socket(domain, socktype, protocol) }; + /* + get_unused_virtual_fd(cageid,realfd,is_cloexec,optionalinfo) -> Result + */ + if kernel_fd < 0 { + let errno = get_errno(); + let err_str = unsafe { + libc::strerror(errno) + }; + let err_msg = unsafe { + CStr::from_ptr(err_str).to_string_lossy().into_owned() + }; + println!("[socket] Error message: {:?}", err_msg); + io::stdout().flush().unwrap(); + return handle_errno(errno, "socket"); + } + + return get_unused_virtual_fd(self.cageid, kernel_fd as u64, false, 0).unwrap() as i32; + } + + /* + * Get the kernel fd with provided virtual fd first + * bind() will return 0 when success and -1 when fail + */ + pub fn bind_syscall(&self, virtual_fd: i32, addr: &GenSockaddr) -> i32 { + /* + translate_virtual_fd(cageid: u64, virtualfd: u64) -> Result + */ + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "bind", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + + // println!("[Bind] Before GenSockaddr: {:?}", addr); + // io::stdout().flush().unwrap(); + let mut new_addr = SockaddrUnix::default(); + + let (finalsockaddr, addrlen) = match addr { + GenSockaddr::V6(addrref6) => ( + (addrref6 as *const SockaddrV6).cast::(), + size_of::(), + ), + GenSockaddr::V4(addrref) => ( + (addrref as *const SockaddrV4).cast::(), + size_of::(), + ), + GenSockaddr::Unix(addrrefu) => { + // let original_path_ptr; + // let original_path_len; + // unsafe { + // // Skip the first '/' + // original_path_ptr = addrrefu.sun_path.as_ptr().add(1); + + // original_path_len = libc::strlen(original_path_ptr as *const i8); + // } + + + // // Create new path + // let lind_root_len = LIND_ROOT.len(); + // let new_path_len = lind_root_len + original_path_len; + + // if new_path_len >= addrrefu.sun_path.len() { + // panic!("New path is too long to fit in sun_path"); + // } + + // let mut new_addr = SockaddrUnix { + // sun_family: addrrefu.sun_family, + // sun_path: [0; 108], + // }; + + // // First copy LIND_ROOT and then copy original path + // unsafe { + // std::ptr::copy_nonoverlapping( + // LIND_ROOT.as_ptr(), + // new_addr.sun_path.as_mut_ptr(), + // lind_root_len + // ); + + + // std::ptr::copy_nonoverlapping( + // original_path_ptr, + // new_addr.sun_path.as_mut_ptr().add(lind_root_len), + // original_path_len + // ); + + // // End with NULL + // // *new_addr.sun_path.get_unchecked_mut(new_path_len) = 0; + // } + + // ( + // (&new_addr as *const SockaddrUnix).cast::(), + // size_of::(), + // ) + // Convert sun_path to LIND_ROOT path + let original_path = unsafe { CStr::from_ptr(addrrefu.sun_path.as_ptr() as *const i8).to_str().unwrap() }; + let lind_path = format!("{}{}", LIND_ROOT, &original_path[..]); // Skip the initial '/' in original path + + // Ensure the length of lind_path does not exceed sun_path capacity + if lind_path.len() >= addrrefu.sun_path.len() { + panic!("New path is too long to fit in sun_path"); + } + + new_addr = SockaddrUnix { + sun_family: addrrefu.sun_family, + sun_path: [0; 108], + }; + + // Copy the new path into sun_path + unsafe { + ptr::copy_nonoverlapping( + lind_path.as_ptr(), + new_addr.sun_path.as_mut_ptr() as *mut u8, + lind_path.len() + ); + *new_addr.sun_path.get_unchecked_mut(lind_path.len()) = 0; // Null-terminate the string + } + + // println!("[bind] new_addr:{:?} ", new_addr); + // io::stdout().flush().unwrap(); + + ( + (&new_addr as *const SockaddrUnix).cast::(), + size_of::(), + ) + + } + + }; + + let ret = unsafe { libc::bind(kernel_fd as i32, finalsockaddr, addrlen as u32) }; + if ret < 0 { + let errno = get_errno(); + let err_str = unsafe { + libc::strerror(errno) + }; + let err_msg = unsafe { + CStr::from_ptr(err_str).to_string_lossy().into_owned() + }; + unsafe { + let sockaddr_un_ptr = finalsockaddr as *const sockaddr_un; + + let sun_path_ptr = (*sockaddr_un_ptr).sun_path.as_ptr(); + + let c_str = CStr::from_ptr(sun_path_ptr); + let str_slice = c_str.to_str().expect("Failed to convert CStr to str"); + + println!("[bind] addr: {:?}", addr); + println!("[bind] sun_path: {}", str_slice); + io::stdout().flush().unwrap(); + } + println!("[Bind] Error message: {:?}", err_msg); + io::stdout().flush().unwrap(); + return handle_errno(errno, "bind"); + } + ret + } + + /* + * Get the kernel fd with provided virtual fd first + * connect() will return 0 when success and -1 when fail + */ + pub fn connect_syscall(&self, virtual_fd: i32, addr: &GenSockaddr) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "connect", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + + let mut new_addr = SockaddrUnix::default(); + + let (finalsockaddr, addrlen) = match addr { + GenSockaddr::V6(addrref6) => ( + (addrref6 as *const SockaddrV6).cast::(), + size_of::(), + ), + GenSockaddr::V4(addrref) => ( + (addrref as *const SockaddrV4).cast::(), + size_of::(), + ), + GenSockaddr::Unix(addrrefu) => { + // let original_path_ptr; + // let original_path_len; + // unsafe { + // // Skip the first '/' + // original_path_ptr = addrrefu.sun_path.as_ptr().add(1); + + // original_path_len = libc::strlen(original_path_ptr as *const i8); + // } + + + // // Create new path + // let lind_root_len = LIND_ROOT.len(); + // let new_path_len = lind_root_len + original_path_len; + + // if new_path_len >= addrrefu.sun_path.len() { + // panic!("New path is too long to fit in sun_path"); + // } + + // let mut new_addr = SockaddrUnix { + // sun_family: addrrefu.sun_family, + // sun_path: [0; 108], + // }; + + // // First copy LIND_ROOT and then copy original path + // unsafe { + // std::ptr::copy_nonoverlapping( + // LIND_ROOT.as_ptr(), + // new_addr.sun_path.as_mut_ptr(), + // lind_root_len + // ); + + + // std::ptr::copy_nonoverlapping( + // original_path_ptr, + // new_addr.sun_path.as_mut_ptr().add(lind_root_len), + // original_path_len + // ); + + // // End with NULL + // *new_addr.sun_path.get_unchecked_mut(new_path_len) = 0; + // } + + // ( + // (&new_addr as *const SockaddrUnix).cast::(), + // size_of::(), + // ) + // Convert sun_path to LIND_ROOT path + let original_path = unsafe { CStr::from_ptr(addrrefu.sun_path.as_ptr() as *const i8).to_str().unwrap() }; + let lind_path = format!("{}{}", LIND_ROOT, &original_path[..]); // Skip the initial '/' in original path + + // Ensure the length of lind_path does not exceed sun_path capacity + if lind_path.len() >= addrrefu.sun_path.len() { + panic!("New path is too long to fit in sun_path"); + } + + new_addr = SockaddrUnix { + sun_family: addrrefu.sun_family, + sun_path: [0; 108], + }; + + // Copy the new path into sun_path + unsafe { + ptr::copy_nonoverlapping( + lind_path.as_ptr(), + new_addr.sun_path.as_mut_ptr() as *mut u8, + lind_path.len() + ); + *new_addr.sun_path.get_unchecked_mut(lind_path.len()) = 0; // Null-terminate the string + } + // println!("[connect] new_addr:{:?} ", new_addr); + // io::stdout().flush().unwrap(); + ( + (&new_addr as *const SockaddrUnix).cast::(), + size_of::(), + ) + + } + }; + + let ret = unsafe { libc::connect(kernel_fd as i32, finalsockaddr, addrlen as u32) }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[Connect] Error message: {:?}", err_msg); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "connect"); + } + ret + } + + /* + * Get the kernel fd with provided virtual fd first + * sendto() will return the number of bytes sent, and -1 when fail + */ + pub fn sendto_syscall( + &self, + virtual_fd: i32, + buf: *const u8, + buflen: usize, + flags: i32, + dest_addr: &GenSockaddr, + ) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "sendto", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + + let (finalsockaddr, addrlen) = match dest_addr { + GenSockaddr::V6(addrref6) => ( + (addrref6 as *const SockaddrV6).cast::(), + size_of::(), + ), + GenSockaddr::V4(addrref) => ( + (addrref as *const SockaddrV4).cast::(), + size_of::(), + ), + GenSockaddr::Unix(addrrefu) => { + // Convert sun_path to LIND_ROOT path + let original_path = unsafe { CStr::from_ptr(addrrefu.sun_path.as_ptr() as *const i8).to_str().unwrap() }; + let lind_path = format!("{}{}", LIND_ROOT, &original_path[..]); // Skip the initial '/' in original path + + // Ensure the length of lind_path does not exceed sun_path capacity + if lind_path.len() >= addrrefu.sun_path.len() { + panic!("New path is too long to fit in sun_path"); + } + + let mut new_addr = SockaddrUnix { + sun_family: addrrefu.sun_family, + sun_path: [0; 108], + }; + + // Copy the new path into sun_path + unsafe { + ptr::copy_nonoverlapping( + lind_path.as_ptr(), + new_addr.sun_path.as_mut_ptr() as *mut u8, + lind_path.len() + ); + *new_addr.sun_path.get_unchecked_mut(lind_path.len()) = 0; // Null-terminate the string + } + + ( + (&new_addr as *const SockaddrUnix).cast::(), + size_of::(), + ) + + } + }; + + let ret = unsafe { + libc::sendto( + kernel_fd as i32, + buf as *const c_void, + buflen, + flags, + finalsockaddr, + addrlen as u32, + ) as i32 + }; + + if ret < 0 { + let errno = get_errno(); + return handle_errno(errno, "sendto"); + } + + ret + } + + /* + * Get the kernel fd with provided virtual fd first + * send() will return the number of bytes sent, and -1 when fail + */ + pub fn send_syscall( + &self, + virtual_fd: i32, + buf: *const u8, + buflen: usize, + flags: i32, + ) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "send", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + + let ret = unsafe { libc::send(kernel_fd as i32, buf as *const c_void, buflen, flags) as i32}; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[Send] Error message: {:?}", err_msg); + // println!("[Send] kernel fd: {:?}", kernel_fd); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "send"); + } + ret + } + + /* + * Get the kernel fd with provided virtual fd first + * recvfrom() will return + * - Success: the length of the message in bytes + * - No messages are available to be received and the + * peer has performed an orderly shutdown: 0 + * - Fail: -1 + */ + pub fn recvfrom_syscall( + &self, + virtual_fd: i32, + buf: *mut u8, + buflen: usize, + flags: i32, + addr: &mut Option<&mut GenSockaddr>, + ) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "recvfrom", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + + let (finalsockaddr, mut addrlen) = match addr { + Some(GenSockaddr::V6(ref mut addrref6)) => ( + (addrref6 as *mut SockaddrV6).cast::(), + size_of::() as u32, + ), + Some(GenSockaddr::V4(ref mut addrref)) => ( + (addrref as *mut SockaddrV4).cast::(), + size_of::() as u32, + ), + Some(GenSockaddr::Unix(ref mut addrrefu)) => ( + (addrrefu as *mut SockaddrUnix).cast::(), + size_of::() as u32, + ), + None => (std::ptr::null::() as *mut libc::sockaddr, 0), + }; + + let ret = unsafe { libc::recvfrom(kernel_fd as i32, buf as *mut c_void, buflen, flags, finalsockaddr, &mut addrlen as *mut u32) as i32 }; + + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[Recvfrom] Error message: {:?}", err_msg); + // println!("[Recvfrom] addr: {:?}", addr); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "recvfrom"); + } + ret + } + + /* + * Get the kernel fd with provided virtual fd first + * recv() will return + * - Success: the length of the message in bytes + * - No messages are available to be received and the + * peer has performed an orderly shutdown: 0 + * - Fail: -1 + */ + pub fn recv_syscall(&self, virtual_fd: i32, buf: *mut u8, len: usize, flags: i32) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "recv", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + + let ret = unsafe { libc::recv(kernel_fd as i32, buf as *mut c_void, len, flags) as i32 }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[Recv] Error message: {:?}", err_msg); + // println!("[Recv] kernel fd: {:?}", kernel_fd); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "recv"); + } + ret + } + + /* + * Get the kernel fd with provided virtual fd first + * listen() will return 0 when success and -1 when fail + */ + pub fn listen_syscall(&self, virtual_fd: i32, backlog: i32) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "listen", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + + let ret = unsafe { libc::listen(kernel_fd as i32, backlog) }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[Listen] Error message: {:?}", err_msg); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "listen"); + } + ret + } + + /* + * Get the kernel fd with provided virtual fd first + * shutdown() will return 0 when success and -1 when fail + */ + pub fn shutdown_syscall(&self, virtual_fd: i32, how: i32) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "shutdown", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + + let ret = unsafe { libc::shutdown(kernel_fd as i32, how) }; + + if ret < 0 { + let errno = get_errno(); + return handle_errno(errno, "bind"); + } + + ret + } + + /* + * We pass a default addr to libc::accept and then fill the GenSockaddr when return to + * dispatcher + * + * Get the kernel fd with provided virtual fd first + * accept() will return a file descriptor for the accepted socket + * Mapping a new virtual fd in this cage (virtual fd is different per cage) and kernel + * fd that libc::accept returned + * Return the virtual fd + */ + pub fn accept_syscall( + &self, + virtual_fd: i32, + addr: &mut Option<&mut GenSockaddr>, + ) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "accept", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + + let (finalsockaddr, mut addrlen) = match addr { + Some(GenSockaddr::V6(ref mut addrref6)) => ( + (addrref6 as *mut SockaddrV6).cast::(), + size_of::() as u32, + ), + Some(GenSockaddr::V4(ref mut addrref)) => ( + (addrref as *mut SockaddrV4).cast::(), + size_of::() as u32, + ), + Some(GenSockaddr::Unix(ref mut addrrefu)) => ( + (addrrefu as *mut SockaddrUnix).cast::(), + size_of::() as u32, + ), + None => (std::ptr::null::() as *mut libc::sockaddr, 0), + }; + + // println!("[Accept] Before GenSockaddr: {:?}", addr); + // io::stdout().flush().unwrap(); + + let ret_kernelfd = unsafe { libc::accept(kernel_fd as i32, finalsockaddr, &mut addrlen as *mut u32) }; + + if ret_kernelfd < 0 { + // let errno = unsafe { + // *libc::__errno_location() + // } as i32; + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[Accept] Error message: {:?}", err_msg); + // io::stdout().flush().unwrap(); + // println!("[Accept] errno: {:?}", errno); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "accept"); + } + + // change the GenSockaddr type according to the sockaddr we received + // GenSockAddr will be modified after libc::accept returns + // So we only need to modify values in GenSockAddr, and rest of the things will be finished in dispatcher stage + // println!("[Accept] After GenSockaddr: {:?}", addr); + // println!("[Accept] finalsockaddr: {:?}", finalsockaddr); + // io::stdout().flush().unwrap(); + + if let Some(sockaddr) = addr { + if let GenSockaddr::Unix(ref mut sockaddr_unix) = sockaddr{ + unsafe { + if std::slice::from_raw_parts(sockaddr_unix.sun_path.as_ptr() as *const u8, LIND_ROOT.len()) == LIND_ROOT.as_bytes() { + // Move ptr to exclue LIND_ROOT + let new_path_ptr = sockaddr_unix.sun_path.as_ptr().add(LIND_ROOT.len()); + + // sun_path in RawPOSIX will always be 108 + let new_path_len = 108 - LIND_ROOT.len(); + + let mut temp_path = vec![0u8; sockaddr_unix.sun_path.len()]; + + std::ptr::copy_nonoverlapping(new_path_ptr, temp_path.as_mut_ptr(), new_path_len); + + for i in 0..sockaddr_unix.sun_path.len() { + sockaddr_unix.sun_path[i] = 0; + } + + std::ptr::copy_nonoverlapping(temp_path.as_ptr(), sockaddr_unix.sun_path.as_mut_ptr(), new_path_len); + } + } + } + } + + let ret_virtualfd = get_unused_virtual_fd(self.cageid, ret_kernelfd as u64, false, 0).unwrap(); + + ret_virtualfd as i32 + } + + /* + * fd_set is used in the Linux select system call to specify the file descriptor + * to be monitored. fd_set is actually a bit array, each bit of which represents + * a file descriptor. fd_set is a specific data type used by the kernel, so we need + * to make sure the final variable we pass to the kernel is in the format that the + * kernel expects. That's why we choose to use FD_SET function instead of doing + * bitmask by ourself. We use Vec to express the fd_set of the virtual file descriptor + * in Lind, and expand the conversion function between lind fd_set and kernel fd_set. + * + * We chose to use bit-set to implement our own fd_set data structure because bit-set + * provides efficient set operations, allowing us to effectively represent and manipulate + * file descriptor sets. These operations can maximize the fidelity to the POSIX fd_set + * characteristics. + * Reference: https://docs.rs/bit-set/latest/bit_set/struct.BitSet.html + * + * select() will return: + * - the total number of bits that are set in readfds, writefds, errorfds + * - 0, if the timeout expired before any file descriptors became ready + * - -1, fail + */ + pub fn select_syscall( + &self, + nfds: i32, + mut readfds: Option<&mut fd_set>, + mut writefds: Option<&mut fd_set>, + mut errorfds: Option<&mut fd_set>, + // timeout: *mut timeval, + rposix_timeout: Option, + ) -> i32 { + // println!("[Select] nfds: {:?}", nfds); + // println!("[Select] readfds: {:?}", readfds); + // println!("[Select] writefds: {:?}", writefds); + // println!("[Select] errorfds: {:?}", errorfds); + // io::stdout().flush().unwrap(); + + let mut timeout; + if rposix_timeout.is_none() { + timeout = libc::timeval { + tv_sec: 0, + tv_usec: 0, + }; + } else { + timeout = libc::timeval { + tv_sec: rposix_timeout.unwrap().as_secs() as i64, + tv_usec: rposix_timeout.unwrap().subsec_micros() as i64, + }; + } + + + let orfds = readfds.as_mut().map(|fds| &mut **fds); + let owfds = writefds.as_mut().map(|fds| &mut **fds); + let oefds = errorfds.as_mut().map(|fds| &mut **fds); + + // println!("[Select] orfds: {:?}", orfds); + // println!("[Select] owfds: {:?}", owfds); + // println!("[Select] oefds: {:?}", oefds); + // io::stdout().flush().unwrap(); + + let (newnfds, mut real_readfds, mut real_writefds, mut real_errorfds, _unrealset, mappingtable) + = get_real_bitmasks_for_select( + self.cageid, + nfds as u64, + orfds.copied(), + owfds.copied(), + oefds.copied(), + ).unwrap(); + + // println!("[Select] real_readfds: {:?}", real_readfds); + // println!("[Select] real_writefds: {:?}", real_writefds); + // println!("[Select] real_errorfds: {:?}", real_errorfds); + // io::stdout().flush().unwrap(); + + // println!("[Select] Before kernel select real_readfds: {:?}", real_readfds); + // println!("[Select] Before kernel select timeout: {:?}\nrposix_timeout: {:?}", timeout, rposix_timeout); + // io::stdout().flush().unwrap(); + + // Ensured that null_mut is used if the Option is None for fd_set parameters. + let ret = unsafe { + libc::select( + newnfds as i32, + real_readfds.as_mut().map_or(std::ptr::null_mut(), |fds| fds as *mut fd_set), + real_writefds.as_mut().map_or(std::ptr::null_mut(), |fds| fds as *mut fd_set), + real_errorfds.as_mut().map_or(std::ptr::null_mut(), |fds| fds as *mut fd_set), + &mut timeout as *mut timeval) + }; + + // println!("[Select] After kernel select real_readfds: {:?}", real_readfds); + // io::stdout().flush().unwrap(); + + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[Select] Error message: {:?}", err_msg); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "select"); + } + + // Revert result + // let (_retnfds, Some(retreadfds), Some(retwritefds), Some(reterrorfds)) = .unwrap(); + match get_virtual_bitmasks_from_select_result( + newnfds as u64, + real_readfds, + real_writefds, + real_errorfds, + HashSet::new(), + HashSet::new(), + HashSet::new(), + &mappingtable, + // mappingtable, + ) { + Ok((_retnfds, retreadfds, retwritefds, reterrorfds)) => { + if let Some(rfds) = readfds.as_mut() { + if let Some(ret_rfds) = retreadfds { + **rfds = ret_rfds; + } else { + // Clear the fd_set if result is None + unsafe { libc::FD_ZERO(&mut **rfds); } + } + } + + if let Some(wfds) = writefds.as_mut() { + if let Some(ret_wfds) = retwritefds { + **wfds = ret_wfds; + } else { + // Clear the fd_set if result is None + unsafe { libc::FD_ZERO(&mut **wfds); } + } + } + + if let Some(efds) = errorfds.as_mut() { + if let Some(ret_efds) = reterrorfds { + **efds = ret_efds; + } else { + // Clear the fd_set if result is None + unsafe { libc::FD_ZERO(&mut **efds); } + } + } + }, + Err(e) => { + panic!(""); + } + } + // println!("[Select] retreadfds: {:?}", retreadfds); + // println!("[Select] mappingtable: {:?}", mappingtable); + // io::stdout().flush().unwrap(); + + + // println!("[Select] readfds: {:?}", readfds); + // io::stdout().flush().unwrap(); + // println!("[Select] ret: {:?}", ret); + // io::stdout().flush().unwrap(); + // let mut count = 0; + // FDTABLE.iter().for_each(|entry| { + // // println!("Cage ID: {}", entry.key()); + // for (index, fd_entry) in entry.value().iter().enumerate() { + // if let Some(entry) = fd_entry { + // // println!(" Index {}: {:?}", index, entry); + // count = count+1; + // } + // } + // }); + // println!("[SELECT] Total: {:?}", count); + // io::stdout().flush().unwrap(); + + ret + } + + /* + * Get the kernel fd with provided virtual fd first + * getsockopt() will return 0 when success and -1 when fail + */ + pub fn getsockopt_syscall( + &self, + virtual_fd: i32, + level: i32, + optname: i32, + // optval: *mut u8, + optval: &mut i32, + // optlen: u32, + ) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "getsockopt", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + + // let mut optlen: u32 = 4; + let mut optlen: socklen_t = 4; + + // let ret = unsafe { libc::getsockopt(kernel_fd as i32, level, optname, optval as *mut c_void, optlen as *mut u32) }; + let ret = unsafe { libc::getsockopt(kernel_fd as i32, level, optname, optval as *mut c_int as *mut c_void, &mut optlen as *mut socklen_t) }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[Getsockopt] Error message: {:?}", err_msg); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "getsockopt"); + } + + + // println!("[Getsockopt] optval: {:?}", optval); + // io::stdout().flush().unwrap(); + ret + } + + /* + * Get the kernel fd with provided virtual fd first + * setsockopt() will return 0 when success and -1 when fail + */ + pub fn setsockopt_syscall( + &self, + virtual_fd: i32, + level: i32, + optname: i32, + optval: *mut u8, + optlen: u32, + ) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "setsockopt", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + + let ret = unsafe { + libc::setsockopt(kernel_fd as i32, level, optname, optval as *mut c_void, optlen) + }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[Setsockopt] Error message: {:?}", err_msg); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "setsockopt"); + } + ret + } + + /* + * Get the kernel fd with provided virtual fd first + * getpeername() will return 0 when success and -1 when fail + */ + pub fn getpeername_syscall( + &self, + virtual_fd: i32, + address: &mut Option<&mut GenSockaddr> + ) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "getpeername", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + + let (finalsockaddr, mut addrlen) = match address { + Some(GenSockaddr::V6(ref mut addrref6)) => ( + (addrref6 as *mut SockaddrV6).cast::(), + size_of::() as u32, + ), + Some(GenSockaddr::V4(ref mut addrref)) => ( + (addrref as *mut SockaddrV4).cast::(), + size_of::() as u32, + ), + Some(GenSockaddr::Unix(ref mut addrrefu)) => ( + (addrrefu as *mut SockaddrUnix).cast::(), + size_of::() as u32, + ), + None => (std::ptr::null::() as *mut libc::sockaddr, 0), + }; + + // println!("[getpeername] addr BEFORE: {:?}", address); + // println!("[getpeername] finalsockaddr BEFORE: {:?}", finalsockaddr); + // io::stdout().flush().unwrap(); + + let ret = unsafe { libc::getpeername(kernel_fd as i32, finalsockaddr, &mut addrlen as *mut u32) }; + + if ret < 0 { + let err = unsafe { + libc::__errno_location() + }; + let err_str = unsafe { + libc::strerror(*err) + }; + let err_msg = unsafe { + CStr::from_ptr(err_str).to_string_lossy().into_owned() + }; + println!("[getpeername] Error message: {:?}", err_msg); + + let errno = get_errno(); + println!("[getpeername] Errno: {:?}", errno); + io::stdout().flush().unwrap(); + return handle_errno(errno, "getpeername"); + } + // println!("[getpeername] finalsockaddr After: {:?}", finalsockaddr); + // io::stdout().flush().unwrap(); + + if let Some(sockaddr) = address { + if let GenSockaddr::Unix(ref mut sockaddr_unix) = sockaddr{ + unsafe { + if std::slice::from_raw_parts(sockaddr_unix.sun_path.as_ptr() as *const u8, LIND_ROOT.len()) == LIND_ROOT.as_bytes() { + // Move ptr to exclue LIND_ROOT + let new_path_ptr = sockaddr_unix.sun_path.as_ptr().add(LIND_ROOT.len()); + + // sun_path in RawPOSIX will always be 108 + let new_path_len = 108 - LIND_ROOT.len(); + + let mut temp_path = vec![0u8; sockaddr_unix.sun_path.len()]; + + std::ptr::copy_nonoverlapping(new_path_ptr, temp_path.as_mut_ptr(), new_path_len); + + for i in 0..sockaddr_unix.sun_path.len() { + sockaddr_unix.sun_path[i] = 0; + } + + std::ptr::copy_nonoverlapping(temp_path.as_ptr(), sockaddr_unix.sun_path.as_mut_ptr(), new_path_len); + } + } + } + } + + // println!("[getpeername] addr: {:?}", address); + // io::stdout().flush().unwrap(); + + ret + } + + /* + * Get the kernel fd with provided virtual fd first + * getsockname() will return 0 when success and -1 when fail + */ + pub fn getsockname_syscall( + &self, + virtual_fd: i32, + address: &mut Option<&mut GenSockaddr>, + ) -> i32 { + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() { + return syscall_error(Errno::EBADF, "getsockname", "Bad File Descriptor"); + } + let kernel_fd = kfd.unwrap(); + + let (finalsockaddr, mut addrlen) = match address { + Some(GenSockaddr::V6(ref mut addrref6)) => ( + (addrref6 as *mut SockaddrV6).cast::(), + size_of::() as u32, + ), + Some(GenSockaddr::V4(ref mut addrref)) => ( + (addrref as *mut SockaddrV4).cast::(), + size_of::() as u32, + ), + Some(GenSockaddr::Unix(ref mut addrrefu)) => ( + (addrrefu as *mut SockaddrUnix).cast::(), + size_of::() as u32, + ), + None => (std::ptr::null::() as *mut libc::sockaddr, 0), + }; + + let mut testlen = 128 as u32; + // let ret = unsafe { libc::getsockname(kernel_fd as i32, finalsockaddr, addrlen as *mut u32) }; + let ret = unsafe { libc::getsockname(kernel_fd as i32, finalsockaddr, &mut testlen as *mut u32) }; + + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[getsockname] Error message: {:?}", err_msg); + // println!("[getsockname] address: {:?}", address); + // println!("[getsockname] finalsockaddr: {:?}", finalsockaddr); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "getsockname"); + } + + ret + } + + /* + * gethostname() will return 0 when success and -1 when fail + */ + pub fn gethostname_syscall(&self, name: *mut u8, len: isize) -> i32 { + let ret = unsafe { libc::gethostname(name as *mut i8, len as usize) }; + if ret < 0 { + let errno = get_errno(); + return handle_errno(errno, "gethostname"); + } + ret + } + + + /* + * In Linux, there is a specific structure pollfd used to pass file descriptors and their + * related event information. Through the poll() function, multiple file descriptors can be + * monitored at the same time, and different event monitoring can be set for each file + * descriptor. We implement our PollStruct and related helper functions to do translation + * between virtual fd and kernel fd, in order to use kernel system calls. The ownership of + * poll_fd should be moved once the functions returns. + * + * poll() will return: + * - a nonnegative value which is the number of elements in the pollfds whose revents + * fields have been set to a nonzero value (indicating an event or an error) + * - the system call timed out before any file descriptors became ready + * - -1, fail + */ + pub fn poll_syscall( + &self, + virtual_fds: &mut [PollStruct], // lots of fds, a ptr + nfds: u64, + timeout: i32, + ) -> i32 { + let mut real_fd = virtual_to_real_poll(self.cageid, virtual_fds); + let ret = unsafe { libc::poll(real_fd.as_mut_ptr(), nfds as u64, timeout) }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[POLL] Error message: {:?}", err_msg); + // println!("[POLL] kernel fd: {:?}", real_fd); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "poll"); + } + + // Convert back to PollStruct + for (i, libcpoll) in real_fd.iter().enumerate() { + if let Some(rposix_poll) = virtual_fds.get_mut(i) { + rposix_poll.revents = libcpoll.revents; + } + } + + ret + } + + /* EPOLL + * In normal Linux, epoll will perform the listed behaviors + * + * epoll_create: + * - This function creates an epfd, which is an epoll file descriptor used to manage + * multiple file behaviors. + * epoll_ctl: + * - This function associates the events and the file descriptors that need to be + * monitored with the specific epfd. + * epoll_wait: + * - This function waits for events on the epfd and returns a list of epoll_events + * that have been triggered. + * + * Then the processing workflow in RawPOSIX is: + * + * epoll_create: + * When epoll_create is called, we use epoll_create_helper to create a virtual epfd. + * Add this virtual epfd to the global mapping table. + * + * epoll_ctl: + * (Use try_epoll_ctl to handle the association between the virtual epfd and the + * events with the file descriptors.) This step involves updating the global table + * with the appropriate mappings. + * + * epoll_wait: + * When epoll_wait is called, you need to convert the virtual epfd to the real epfd. + * Call libc::epoll_wait to perform the actual wait operation on the real epfd. + * Convert the resulting real events back to the virtual events using the mapping in + * the global table. + */ + + /* + * Mapping a new virtual fd and kernel fd that libc::epoll_create returned + * Then return virtual fd + */ + pub fn epoll_create_syscall(&self, size: i32) -> i32 { + // Create the kernel instance + let kernel_fd = unsafe { libc::epoll_create(size) }; + + if kernel_fd < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("Error message: {:?}", err_msg); + // println!("[EPOLL] size: {:?}", size); + // println!("[EPOLL] kernelfd: {:?}", kernel_fd); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "epoll_create"); + } + + // Get the virtual epfd + let virtual_epfd = get_unused_virtual_fd(self.cageid, kernel_fd as u64, false, 0).unwrap(); + // println!("[epoll_create] virtual_epfd: {:?}", virtual_epfd); + // io::stdout().flush().unwrap(); + + // We don't need to update mapping table at now + // Return virtual epfd + virtual_epfd as i32 + + } + + /* + * Translate before calling, and updating the glocal mapping tables according to + * the op. + * epoll_ctl() will return 0 when success and -1 when fail + */ + pub fn epoll_ctl_syscall( + &self, + virtual_epfd: i32, + op: i32, + virtual_fd: i32, + epollevent: &mut EpollEvent, + ) -> i32 { + let k_epfd = translate_virtual_fd(self.cageid, virtual_epfd as u64); + let kfd = translate_virtual_fd(self.cageid, virtual_fd as u64); + if kfd.is_err() || k_epfd.is_err() { + return syscall_error(Errno::EBADF, "epoll", "Bad File Descriptor"); + } + + let kernel_epfd = k_epfd.unwrap(); + let kernel_fd = kfd.unwrap(); + // EpollEvent conversion + let event = epollevent.events; + let mut epoll_event = epoll_event { + events: event, + u64: kernel_fd as u64, + }; + + let ret = unsafe { libc::epoll_ctl(kernel_epfd as i32, op, kernel_fd as i32, &mut epoll_event) }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[epoll_ctl] Error message: {:?}", err_msg); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "epoll_ctl"); + } + + // Update the virtual list -- but we only handle the non-real fd case + // try_epoll_ctl will directly return a real fd in libc case + // - maybe we could create a new mapping table to handle the mapping relationship..? + // ceate inside the fdtable interface? or we could handle inside rawposix..? + + // Update the mapping table for epoll + if op == libc::EPOLL_CTL_DEL { + let mut epollmapping = REAL_EPOLL_MAP.lock(); + if let Some(fdmap) = epollmapping.get_mut(&(virtual_epfd as u64)) { + if fdmap.remove(&(kernel_fd as i32)).is_some() { + if fdmap.is_empty() { + epollmapping.remove(&(virtual_epfd as u64)); + } + return ret; + } + } + } else { + let mut epollmapping = REAL_EPOLL_MAP.lock(); + epollmapping.entry(virtual_epfd as u64).or_insert_with(HashMap::new).insert(kernel_fd as i32, virtual_fd as u64); + return ret; + } + + // [TODO] + // should be op not support + -1 + } + + /* + * Get the kernel fd with provided virtual fd first, and then convert back to virtual + * epoll_wait() will return: + * 1. the number of file descriptors ready for the requested I/O + * 2. 0, if none + * 3. -1, fail + */ + pub fn epoll_wait_syscall( + &self, + virtual_epfd: i32, + events: &mut [EpollEvent], + maxevents: i32, + timeout: i32, + ) -> i32 { + let k_epfd = translate_virtual_fd(self.cageid, virtual_epfd as u64); + if k_epfd.is_err() { + return syscall_error(Errno::EBADF, "epoll_wait", "Bad File Descriptor"); + } + let kernel_epfd = k_epfd.unwrap(); + + let mut kernel_events: Vec = Vec::with_capacity(maxevents as usize); + + // Should always be null value before we call libc::epoll_wait + kernel_events.push( + epoll_event { + events: 0, + u64: 0, + } + ); + + let ret = unsafe { libc::epoll_wait(kernel_epfd as i32, kernel_events.as_mut_ptr(), maxevents, timeout as i32) }; + if ret < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("[epoll_wait] Error message: {:?}", err_msg); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "epoll_wait"); + } + + // Convert back to rustposix's data structure + // Loop over virtual_epollfd to find corresponding mapping relationship between kernel fd and virtual fd + for i in 0..ret as usize { + + let ret_kernelfd = kernel_events[i].u64; + let epollmapping = REAL_EPOLL_MAP.lock(); + let ret_virtualfd = epollmapping.get(&(virtual_epfd as u64)).and_then(|kernel_map| kernel_map.get(&(ret_kernelfd as i32)).copied()); + + events[i].fd = ret_virtualfd.unwrap() as i32; + events[i].events = kernel_events[i].events; + } + + ret + } + + /* + * socketpair() will return 0 when success and -1 when fail + */ + pub fn socketpair_syscall( + &self, + domain: i32, + type_: i32, + protocol: i32, + virtual_socket_vector: &mut SockPair, + ) -> i32 { + + let mut kernel_socket_vector: [i32; 2] = [0, 0]; + + let ret = unsafe { libc::socketpair(domain, type_, protocol, kernel_socket_vector.as_mut_ptr()) }; + if ret < 0 { + let errno = get_errno(); + return handle_errno(errno, "sockpair"); + } + + let ksv_1 = kernel_socket_vector[0]; + let ksv_2 = kernel_socket_vector[1]; + let vsv_1 = get_unused_virtual_fd(self.cageid, ksv_1 as u64, false, 0).unwrap(); + let vsv_2 = get_unused_virtual_fd(self.cageid, ksv_2 as u64, false, 0).unwrap(); + virtual_socket_vector.sock1 = vsv_1 as i32; + virtual_socket_vector.sock2 = vsv_2 as i32; + return 0; + } + + /* + * Get result back from libc::getifaddrs and fill the content of name field into a buf + * as rustposix, so that i don’t need to change the dispatcher interface + */ + pub fn getifaddrs_syscall(&self, buf: *mut u8, count: usize) -> i32 { + let mut ifaddr: *mut ifaddrs = ptr::null_mut(); + + unsafe { + if getifaddrs(&mut ifaddr) < 0 { + // let err = libc::__errno_location(); + // let err_str = libc::strerror(*err); + // let err_msg = CStr::from_ptr(err_str).to_string_lossy().into_owned(); + // println!("Error message: {:?}", err_msg); + // io::stdout().flush().unwrap(); + let errno = get_errno(); + return handle_errno(errno, "getifaddrs"); + } + let mut ifa = ifaddr; + let mut offset = 0; + while !ifa.is_null() { + let ifa_ref = &*ifa; + let name_cstr = CStr::from_ptr(ifa_ref.ifa_name); + let name_bytes = name_cstr.to_bytes(); + + // Check if there's enough space in the buffer + if offset + name_bytes.len() + 1 > count { + println!("Buffer size exceeded"); + freeifaddrs(ifaddr); + return -1; + } + + let name_vec = name_bytes.to_vec(); + fill(buf.add(offset), name_vec.len(), &name_vec); + + // Add a null terminator to separate names + *buf.add(offset + name_vec.len()) = 0; + offset += name_vec.len() + 1; // Move offset past the name and null terminator + + ifa = ifa_ref.ifa_next; + + } + freeifaddrs(ifaddr); + 0 + } + } + +} + +/* POLL() +*/ +pub fn virtual_to_real_poll(cageid: u64, virtual_poll: &mut [PollStruct]) -> Vec { + // Change from ptr to reference + // let virtual_fds = unsafe { &mut *virtual_poll }; + + let mut real_fds = Vec::with_capacity(virtual_poll.len()); + + for vfd in &mut *virtual_poll { + + let rfd = translate_virtual_fd(cageid, vfd.fd as u64); + if rfd.is_err() { + // return syscall_error(Errno::EBADF, "poll", "Bad File Descriptor"); + panic!(); + } + let real_fd = rfd.unwrap(); + let kernel_poll = pollfd { + fd: real_fd as i32, + events: vfd.events, + revents: vfd.revents, + }; + real_fds.push(kernel_poll); + } + + real_fds +} diff --git a/src/safeposix/syscalls/net_constants.rs b/src/safeposix/syscalls/net_constants.rs new file mode 100644 index 00000000..c600aeeb --- /dev/null +++ b/src/safeposix/syscalls/net_constants.rs @@ -0,0 +1,392 @@ +// Network related constants +#![allow(dead_code)] +#![allow(non_upper_case_globals)] + +use crate::interface; + +//used for gethostname syscall +pub const DEFAULT_HOSTNAME: &str = "Lind"; +pub const BLOCK_TIME: interface::RustDuration = interface::RustDuration::from_micros(100); + +pub const UDSOCK_CAPACITY: usize = 212992; + +// Define constants using static or const +// Imported into net_calls file + +pub const SOCK_STREAM: i32 = 1; //stream socket +pub const SOCK_DGRAM: i32 = 2; //datagram socket +pub const SOCK_RAW: i32 = 3; //raw protocol interface +pub const SOCK_RDM: i32 = 4; //reliably delivered message +pub const SOCK_SEQPACKET: i32 = 5; //sequenced packet stream +pub const SOCK_CLOEXEC: i32 = 0o02000000; // Atomically set close-on-exec +pub const SOCK_NONBLOCK: i32 = 0o00004000; // Mark as non-blocking + +/* Supported address families. */ +pub const AF_UNSPEC: i32 = 0; +pub const AF_UNIX: i32 = 1; /* Unix domain sockets */ +pub const AF_LOCAL: i32 = 1; /* POSIX name for AF_UNIX */ +pub const AF_INET: i32 = 2; /* Internet IP Protocol */ +pub const AF_AX25: i32 = 3; /* Amateur Radio AX.25 */ +pub const AF_IPX: i32 = 4; /* Novell IPX */ +pub const AF_APPLETALK: i32 = 5; /* AppleTalk DDP */ +pub const AF_NETROM: i32 = 6; /* Amateur Radio NET/ROM */ +pub const AF_BRIDGE: i32 = 7; /* Multiprotocol bridge */ +pub const AF_ATMPVC: i32 = 8; /* ATM PVCs */ +pub const AF_X25: i32 = 9; /* Reserved for X.25 project */ +pub const AF_INET6: i32 = 10; /* IP version 6 */ +pub const AF_ROSE: i32 = 11; /* Amateur Radio X.25 PLP */ +pub const AF_DECnet: i32 = 12; /* Reserved for DECnet project */ +pub const AF_NETBEUI: i32 = 13; /* Reserved for 802.2LLC project*/ +pub const AF_SECURITY: i32 = 14; /* Security callback pseudo AF */ +pub const AF_KEY: i32 = 15; /* PF_KEY key management API */ +pub const AF_NETLINK: i32 = 16; +pub const AF_ROUTE: i32 = AF_NETLINK; /* Alias to emulate 4.4BSD */ +pub const AF_PACKET: i32 = 17; /* Packet family */ +pub const AF_ASH: i32 = 18; /* Ash */ +pub const AF_ECONET: i32 = 19; /* Acorn Econet */ +pub const AF_ATMSVC: i32 = 20; /* ATM SVCs */ +pub const AF_RDS: i32 = 21; /* RDS sockets */ +pub const AF_SNA: i32 = 22; /* Linux SNA Project (nutters!) */ +pub const AF_IRDA: i32 = 23; /* IRDA sockets */ +pub const AF_PPPOX: i32 = 24; /* PPPoX sockets */ +pub const AF_WANPIPE: i32 = 25; /* Wanpipe API Sockets */ +pub const AF_LLC: i32 = 26; /* Linux LLC */ +pub const AF_IB: i32 = 27; /* Native InfiniBand address */ +pub const AF_MPLS: i32 = 28; /* MPLS */ +pub const AF_CAN: i32 = 29; /* Controller Area Network */ +pub const AF_TIPC: i32 = 30; /* TIPC sockets */ +pub const AF_BLUETOOTH: i32 = 31; /* Bluetooth sockets */ +pub const AF_IUCV: i32 = 32; /* IUCV sockets */ +pub const AF_RXRPC: i32 = 33; /* RxRPC sockets */ +pub const AF_ISDN: i32 = 34; /* mISDN sockets */ +pub const AF_PHONET: i32 = 35; /* Phonet sockets */ +pub const AF_IEEE802154: i32 = 36; /* IEEE802154 sockets */ +pub const AF_CAIF: i32 = 37; /* CAIF sockets */ +pub const AF_ALG: i32 = 38; /* Algorithm sockets */ +pub const AF_NFC: i32 = 39; /* NFC sockets */ +pub const AF_VSOCK: i32 = 40; /* vSockets */ +pub const AF_KCM: i32 = 41; /* Kernel Connection Multiplexor*/ +pub const AF_QIPCRTR: i32 = 42; /* Qualcomm IPC Router */ +pub const AF_SMC: i32 = 43; /* smc sockets: reserve number for + * PF_SMC protocol family that + * reuses AF_INET address family + */ +pub const AF_XDP: i32 = 44; /* XDP sockets */ +pub const AF_MCTP: i32 = 45; /* Management component + * transport protocol + */ + +pub const AF_MAX: i32 = 46; /* For now.. */ + +/* Protocol families, same as address families. */ +pub const PF_UNSPEC: i32 = AF_UNSPEC; +pub const PF_UNIX: i32 = AF_UNIX; +pub const PF_LOCAL: i32 = AF_LOCAL; +pub const PF_INET: i32 = AF_INET; +pub const PF_AX25: i32 = AF_AX25; +pub const PF_IPX: i32 = AF_IPX; +pub const PF_APPLETALK: i32 = AF_APPLETALK; +pub const PF_NETROM: i32 = AF_NETROM; +pub const PF_BRIDGE: i32 = AF_BRIDGE; +pub const PF_ATMPVC: i32 = AF_ATMPVC; +pub const PF_X25: i32 = AF_X25; +pub const PF_INET6: i32 = AF_INET6; +pub const PF_ROSE: i32 = AF_ROSE; +pub const PF_DECnet: i32 = AF_DECnet; +pub const PF_NETBEUI: i32 = AF_NETBEUI; +pub const PF_SECURITY: i32 = AF_SECURITY; +pub const PF_KEY: i32 = AF_KEY; +pub const PF_NETLINK: i32 = AF_NETLINK; +pub const PF_ROUTE: i32 = AF_ROUTE; +pub const PF_PACKET: i32 = AF_PACKET; +pub const PF_ASH: i32 = AF_ASH; +pub const PF_ECONET: i32 = AF_ECONET; +pub const PF_ATMSVC: i32 = AF_ATMSVC; +pub const PF_RDS: i32 = AF_RDS; +pub const PF_SNA: i32 = AF_SNA; +pub const PF_IRDA: i32 = AF_IRDA; +pub const PF_PPPOX: i32 = AF_PPPOX; +pub const PF_WANPIPE: i32 = AF_WANPIPE; +pub const PF_LLC: i32 = AF_LLC; +pub const PF_IB: i32 = AF_IB; +pub const PF_MPLS: i32 = AF_MPLS; +pub const PF_CAN: i32 = AF_CAN; +pub const PF_TIPC: i32 = AF_TIPC; +pub const PF_BLUETOOTH: i32 = AF_BLUETOOTH; +pub const PF_IUCV: i32 = AF_IUCV; +pub const PF_RXRPC: i32 = AF_RXRPC; +pub const PF_ISDN: i32 = AF_ISDN; +pub const PF_PHONET: i32 = AF_PHONET; +pub const PF_IEEE802154: i32 = AF_IEEE802154; +pub const PF_CAIF: i32 = AF_CAIF; +pub const PF_ALG: i32 = AF_ALG; +pub const PF_NFC: i32 = AF_NFC; +pub const PF_VSOCK: i32 = AF_VSOCK; +pub const PF_KCM: i32 = AF_KCM; +pub const PF_QIPCRTR: i32 = AF_QIPCRTR; +pub const PF_SMC: i32 = AF_SMC; +pub const PF_XDP: i32 = AF_XDP; +pub const PF_MCTP: i32 = AF_MCTP; +pub const PF_MAX: i32 = AF_MAX; + +// protocols... + +pub const IPPROTO_IP: i32 = 0; // dummy for IP +pub const IPPROTO_ICMP: i32 = 1; // control message protocol +pub const IPPROTO_IGMP: i32 = 2; // group mgmt protocol +pub const IPPROTO_GGP: i32 = 3; // gateway^2 (deprecated) +pub const IPPROTO_IPV4: i32 = 4; // IPv4 encapsulation +pub const IPPROTO_IPIP: i32 = IPPROTO_IPV4; // for compatibility +pub const IPPROTO_TCP: i32 = 6; // tcp +pub const IPPROTO_ST: i32 = 7; // Stream protocol II +pub const IPPROTO_EGP: i32 = 8; // exterior gateway protocol +pub const IPPROTO_PIGP: i32 = 9; // private interior gateway +pub const IPPROTO_RCCMON: i32 = 10; // BBN RCC Monitoring +pub const IPPROTO_NVPII: i32 = 11; // network voice protocol +pub const IPPROTO_PUP: i32 = 12; // pup +pub const IPPROTO_ARGUS: i32 = 13; // Argus +pub const IPPROTO_EMCON: i32 = 14; // EMCON +pub const IPPROTO_XNET: i32 = 15; // Cross Net Debugger +pub const IPPROTO_CHAOS: i32 = 16; // Chaos +pub const IPPROTO_UDP: i32 = 17; // user datagram protocol +pub const IPPROTO_MUX: i32 = 18; // Multiplexing +pub const IPPROTO_MEAS: i32 = 19; // DCN Measurement Subsystems +pub const IPPROTO_HMP: i32 = 20; // Host Monitoring +pub const IPPROTO_PRM: i32 = 21; // Packet Radio Measurement +pub const IPPROTO_IDP: i32 = 22; // xns idp +pub const IPPROTO_TRUNK1: i32 = 23; // Trunk-1 +pub const IPPROTO_TRUNK2: i32 = 24; // Trunk-2 +pub const IPPROTO_LEAF1: i32 = 25; // Leaf-1 +pub const IPPROTO_LEAF2: i32 = 26; // Leaf-2 +pub const IPPROTO_RDP: i32 = 27; // Reliable Data +pub const IPPROTO_IRTP: i32 = 28; // Reliable Transaction +pub const IPPROTO_TP: i32 = 29; // tp-4 w/ class negotiation +pub const IPPROTO_BLT: i32 = 30; // Bulk Data Transfer +pub const IPPROTO_NSP: i32 = 31; // Network Services +pub const IPPROTO_INP: i32 = 32; // Merit Internodal +pub const IPPROTO_SEP: i32 = 33; // Sequential Exchange +pub const IPPROTO_3PC: i32 = 34; // Third Party Connect +pub const IPPROTO_IDPR: i32 = 35; // InterDomain Policy Routing +pub const IPPROTO_XTP: i32 = 36; // XTP +pub const IPPROTO_DDP: i32 = 37; // Datagram Delivery +pub const IPPROTO_CMTP: i32 = 38; // Control Message Transport +pub const IPPROTO_TPXX: i32 = 39; // TP++ Transport +pub const IPPROTO_IL: i32 = 40; // IL transport protocol +pub const IPPROTO_IPV6: i32 = 41; // IP6 header +pub const IPPROTO_SDRP: i32 = 42; // Source Demand Routing +pub const IPPROTO_ROUTING: i32 = 43; // IP6 routing header +pub const IPPROTO_FRAGMENT: i32 = 44; // IP6 fragmentation header +pub const IPPROTO_IDRP: i32 = 45; // InterDomain Routing +pub const IPPROTO_RSVP: i32 = 46; // resource reservation +pub const IPPROTO_GRE: i32 = 47; // General Routing Encap. +pub const IPPROTO_MHRP: i32 = 48; // Mobile Host Routing +pub const IPPROTO_BHA: i32 = 49; // BHA +pub const IPPROTO_ESP: i32 = 50; // IP6 Encap Sec. Payload +pub const IPPROTO_AH: i32 = 51; // IP6 Auth Header +pub const IPPROTO_INLSP: i32 = 52; // Integ. Net Layer Security +pub const IPPROTO_SWIPE: i32 = 53; // IP with encryption +pub const IPPROTO_NHRP: i32 = 54; // Next Hop Resolution + // 55-57: Unassigned +pub const IPPROTO_ICMPV6: i32 = 58; // ICMP6 +pub const IPPROTO_NONE: i32 = 59; // IP6 no next header +pub const IPPROTO_DSTOPTS: i32 = 60; // IP6 destination option +pub const IPPROTO_AHIP: i32 = 61; // any host internal protocol +pub const IPPROTO_CFTP: i32 = 62; // CFTP +pub const IPPROTO_HELLO: i32 = 63; // "hello" routing protocol +pub const IPPROTO_SATEXPAK: i32 = 64; // SATNET/Backroom EXPAK +pub const IPPROTO_KRYPTOLAN: i32 = 65; // Kryptolan +pub const IPPROTO_RVD: i32 = 66; // Remote Virtual Disk +pub const IPPROTO_IPPC: i32 = 67; // Pluribus Packet Core +pub const IPPROTO_ADFS: i32 = 68; // Any distributed FS +pub const IPPROTO_SATMON: i32 = 69; // Satnet Monitoring +pub const IPPROTO_VISA: i32 = 70; // VISA Protocol +pub const IPPROTO_IPCV: i32 = 71; // Packet Core Utility +pub const IPPROTO_CPNX: i32 = 72; // Comp. Prot. Net. Executive +pub const IPPROTO_CPHB: i32 = 73; // Comp. Prot. HeartBeat +pub const IPPROTO_WSN: i32 = 74; // Wang Span Network +pub const IPPROTO_PVP: i32 = 75; // Packet Video Protocol +pub const IPPROTO_BRSATMON: i32 = 76; // BackRoom SATNET Monitoring +pub const IPPROTO_ND: i32 = 77; // Sun net disk proto (temp.) +pub const IPPROTO_WBMON: i32 = 78; // WIDEBAND Monitoring +pub const IPPROTO_WBEXPAK: i32 = 79; // WIDEBAND EXPAK +pub const IPPROTO_EON: i32 = 80; // ISO cnlp +pub const IPPROTO_VMTP: i32 = 81; // VMTP +pub const IPPROTO_SVMTP: i32 = 82; // Secure VMTP +pub const IPPROTO_VINES: i32 = 83; // Banyon VINES +pub const IPPROTO_TTP: i32 = 84; // TTP +pub const IPPROTO_IGP: i32 = 85; // NSFNET-IGP +pub const IPPROTO_DGP: i32 = 86; // dissimilar gateway prot. +pub const IPPROTO_TCF: i32 = 87; // TCF +pub const IPPROTO_IGRP: i32 = 88; // Cisco/GXS IGRP +pub const IPPROTO_OSPFIGP: i32 = 89; // OSPFIGP +pub const IPPROTO_SRPC: i32 = 90; // Strite RPC protocol +pub const IPPROTO_LARP: i32 = 91; // Locus Address Resoloution +pub const IPPROTO_MTP: i32 = 92; // Multicast Transport +pub const IPPROTO_AX25: i32 = 93; // AX.25 Frames +pub const IPPROTO_IPEIP: i32 = 94; // IP encapsulated in IP +pub const IPPROTO_MICP: i32 = 95; // Mobile Int.ing control +pub const IPPROTO_SCCSP: i32 = 96; // Semaphore Comm. security +pub const IPPROTO_ETHERIP: i32 = 97; // Ethernet IP encapsulation +pub const IPPROTO_ENCAP: i32 = 98; // encapsulation header +pub const IPPROTO_APES: i32 = 99; // any private encr. scheme +pub const IPPROTO_GMTP: i32 = 100; // GMTP +pub const IPPROTO_PIM: i32 = 103; // Protocol Independent Mcast +pub const IPPROTO_IPCOMP: i32 = 108; // payload compression (IPComp) +pub const IPPROTO_PGM: i32 = 113; // PGM +pub const IPPROTO_SCTP: i32 = 132; // SCTP +pub const IPPROTO_DIVERT: i32 = 254; // divert pseudo-protocol +pub const IPPROTO_RAW: i32 = 255; // raw IP packet +pub const IPPROTO_MAX: i32 = 256; +// last return value of *_input(), meaning "all job for this pkt is done". +pub const IPPROTO_DONE: i32 = 257; + +pub const MSG_OOB: i32 = 1; +pub const MSG_PEEK: i32 = 2; +pub const MSG_DONTROUTE: i32 = 4; +pub const MSG_TRYHARD: i32 = 4; /* Synonym for MSG_DONTROUTE for DECnet */ +pub const MSG_CTRUNC: i32 = 8; +pub const MSG_PROBE: i32 = 0x10; /* Do not send. Only probe path f.e. for MTU */ +pub const MSG_TRUNC: i32 = 0x20; +pub const MSG_DONTWAIT: i32 = 0x40; /* Nonblocking io */ +pub const MSG_EOR: i32 = 0x80; /* End of record */ +pub const MSG_WAITALL: i32 = 0x100; /* Wait for a full request */ +pub const MSG_FIN: i32 = 0x200; +pub const MSG_SYN: i32 = 0x400; +pub const MSG_CONFIRM: i32 = 0x800; /* Confirm path validity */ +pub const MSG_RST: i32 = 0x1000; +pub const MSG_ERRQUEUE: i32 = 0x2000; /* Fetch message from error queue */ +pub const MSG_NOSIGNAL: i32 = 0x4000; /* Do not generate SIGPIPE */ +pub const MSG_MORE: i32 = 0x8000; /* Sender will send more */ +pub const MSG_WAITFORONE: i32 = 0x10000; /* recvmmsg(): block until 1+ packets avail */ +pub const MSG_SENDPAGE_NOPOLICY: i32 = 0x10000; /* sendpage() internal : do no apply policy */ +pub const MSG_SENDPAGE_NOTLAST: i32 = 0x20000; /* sendpage() internal : not the last page */ +pub const MSG_BATCH: i32 = 0x40000; /* sendmmsg(): more messages coming */ +pub const MSG_EOF: i32 = MSG_FIN; +pub const MSG_NO_SHARED_FRAGS: i32 = 0x80000; /* sendpage() internal : page frags are not shared */ +pub const MSG_SENDPAGE_DECRYPTED: i32 = 0x100000; /* sendpage() internal : page may carry + * plain text and require encryption + */ + +//shutdown +pub const SHUT_RD: i32 = 0; +pub const SHUT_WR: i32 = 1; +pub const SHUT_RDWR: i32 = 2; + +////////////////////// setsockopt / getsockopt... +pub const SOL_SOCKET: i32 = 1; + +pub const SO_DEBUG: i32 = 1; +pub const SO_REUSEADDR: i32 = 2; +pub const SO_TYPE: i32 = 3; +pub const SO_ERROR: i32 = 4; +pub const SO_DONTROUTE: i32 = 5; +pub const SO_BROADCAST: i32 = 6; +pub const SO_SNDBUF: i32 = 7; +pub const SO_RCVBUF: i32 = 8; +pub const SO_SNDBUFFORCE: i32 = 32; +pub const SO_RCVBUFFORCE: i32 = 33; +pub const SO_KEEPALIVE: i32 = 9; +pub const SO_OOBINLINE: i32 = 10; +pub const SO_NO_CHECK: i32 = 11; +pub const SO_PRIORITY: i32 = 12; +pub const SO_LINGER: i32 = 13; +pub const SO_BSDCOMPAT: i32 = 14; +pub const SO_REUSEPORT: i32 = 15; +pub const SO_PASSCRED: i32 = 16; +pub const SO_PEERCRED: i32 = 17; +pub const SO_RCVLOWAT: i32 = 18; +pub const SO_SNDLOWAT: i32 = 19; +pub const SO_RCVTIMEO_OLD: i32 = 20; +pub const SO_SNDTIMEO_OLD: i32 = 21; +pub const SO_PEERNAME: i32 = 28; +pub const SO_ACCEPTCONN: i32 = 30; + +// pub const SO_SECURITY_AUTHENTICATION: i32 = 22; +// pub const SO_SECURITY_ENCRYPTION_TRANSPORT: i32 = 23; +// pub const SO_SECURITY_ENCRYPTION_NETWORK: i32 = 24; + +// pub const SO_BINDTODEVICE: i32 = 25; + +// /* Socket filtering */ +// pub const SO_ATTACH_FILTER: i32 = 26; +// pub const SO_DETACH_FILTER: i32 = 27; + +// pub const SO_TIMESTAMP: i32 = 29; +// pub const SCM_TIMESTAMP: i32 = SO_TIMESTAMP; + +// pub const SO_PEERSEC: i32 = 31; +// pub const SO_PASSSEC: i32 = 34; +// pub const SO_TIMESTAMPNS: i32 = 35; +// pub const SCM_TIMESTAMPNS: i32 = SO_TIMESTAMPNS; + +// pub const SO_MARK: i32 = 36; + +// pub const SO_TIMESTAMPING: i32 = 37; +// pub const SCM_TIMESTAMPING: i32 = SO_TIMESTAMPING; + +// pub const SO_PROTOCOL: i32 = 38; +// pub const SO_DOMAIN: i32 = 39; + +// pub const SO_RXQ_OVFL: i32 = 40; + +// Use this to specify options on a socket. Use the protocol with setsockopt +// to specify something for all sockets with a protocol +pub const SOL_TCP: i32 = IPPROTO_TCP; +pub const SOL_UDP: i32 = IPPROTO_UDP; + +pub const TCP_NODELAY: i32 = 0x01; // don't delay send to coalesce packets +pub const TCP_MAXSEG: i32 = 0x02; // set maximum segment size +pub const TCP_NOPUSH: i32 = 0x04; // don't push last block of write +pub const TCP_NOOPT: i32 = 0x08; // don't use TCP options +pub const TCP_KEEPALIVE: i32 = 0x10; // idle time used when SO_KEEPALIVE is enabled +pub const TCP_CONNECTIONTIMEOUT: i32 = 0x20; // connection timeout +pub const PERSIST_TIMEOUT: i32 = 0x40; // time after which a connection in persist timeout + // will terminate. + // see draft-ananth-tcpm-persist-02.txt +pub const TCP_RXT_CONNDROPTIME: i32 = 0x80; // time after which tcp retransmissions will be + // stopped and the connection will be dropped +pub const TCP_RXT_FINDROP: i32 = 0x100; // When set, a connection is dropped after 3 FINs + +pub const MINSOCKOBJID: i32 = 0; +pub const MAXSOCKOBJID: i32 = 1024; + +//POLL CONSTANTS +pub const POLLIN: i16 = 0o1; // There is data to read. +pub const POLLPRI: i16 = 0o2; //There is urgent data to read. +pub const POLLOUT: i16 = 0o4; // Writing now will not block. +pub const POLLERR: i16 = 0o10; // Error condition. +pub const POLLHUP: i16 = 0o20; // Hung up. +pub const POLLNVAL: i16 = 0o40; // Invalid polling request. + +//EPOLL CONSTANTS +pub const EPOLLIN: i32 = 0x001; +pub const EPOLLPRI: i32 = 0x002; +pub const EPOLLOUT: i32 = 0x004; +pub const EPOLLRDNORM: i32 = 0x040; +pub const EPOLLRDBAND: i32 = 0x080; +pub const EPOLLWRNORM: i32 = 0x100; +pub const EPOLLWRBAND: i32 = 0x200; +pub const EPOLLMSG: i32 = 0x400; +pub const EPOLLERR: i32 = 0x008; +pub const EPOLLHUP: i32 = 0x010; +pub const EPOLLRDHUP: i32 = 0x2000; +pub const EPOLLWAKEUP: i32 = 1 << 29; +pub const EPOLLONESHOT: i32 = 1 << 30; +pub const EPOLLET: i32 = 1 << 31; + +pub const EPOLL_CTL_ADD: i32 = 1; +pub const EPOLL_CTL_DEL: i32 = 2; +pub const EPOLL_CTL_MOD: i32 = 3; + +pub const FD_SET_MAX_FD: i32 = 1024; + +//for internal use +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum ConnState { + NOTCONNECTED, + CONNECTED, + CONNRDONLY, + CONNWRONLY, + LISTEN, + INPROGRESS, +} diff --git a/src/safeposix/syscalls/sys_calls.rs b/src/safeposix/syscalls/sys_calls.rs new file mode 100644 index 00000000..bea5f03c --- /dev/null +++ b/src/safeposix/syscalls/sys_calls.rs @@ -0,0 +1,517 @@ +#![allow(dead_code)] + +// System related system calls +use super::fs_constants::*; +use super::net_constants::*; +use super::sys_constants; +use super::sys_constants::*; +use crate::interface; +use crate::safeposix::cage; +use crate::safeposix::cage::*; +use crate::safeposix::shm::*; + +// use crate::example_grates::vanillaglobal::*; +use crate::example_grates::dashmapvecglobal::*; +// use crate::example_grates::muthashmaxglobal::*; +// use crate::example_grates::dashmaparrayglobal::*; + +use libc::*; + +use std::io::Write; +use std::io; + +use std::sync::Arc as RustRfc; + +impl Cage { + fn unmap_shm_mappings(&self) { + //unmap shm mappings on exit or exec + for rev_mapping in self.rev_shm.lock().iter() { + let shmid = rev_mapping.1; + let metadata = &SHM_METADATA; + match metadata.shmtable.entry(shmid) { + interface::RustHashEntry::Occupied(mut occupied) => { + let segment = occupied.get_mut(); + segment.shminfo.shm_nattch -= 1; + segment.shminfo.shm_dtime = interface::timestamp() as isize; + segment.attached_cages.remove(&self.cageid); + + if segment.rmid && segment.shminfo.shm_nattch == 0 { + let key = segment.key; + occupied.remove_entry(); + metadata.shmkeyidtable.remove(&key); + } + } + interface::RustHashEntry::Vacant(_) => { + panic!("Shm entry not created for some reason"); + } + }; + } + } + + pub fn fork_syscall(&self, child_cageid: u64) -> i32 { + // Modify the fdtable manually + copy_fdtable_for_cage(self.cageid, child_cageid).unwrap(); + + // println!("[FORK]"); + // io::stdout().flush().unwrap(); + // if child_cageid == 22 { + // let mut count = 0; + // FDTABLE.iter().for_each(|entry| { + // println!("Cage ID: {}", entry.key()); + // for (index, fd_entry) in entry.value().iter().enumerate() { + // if let Some(entry) = fd_entry { + // println!(" Index {}: {:?}", index, entry); + // count = count+1; + // } + // } + // }); + // println!("Total: {:?}", count); + // io::stdout().flush().unwrap(); + // } + + //construct a new mutex in the child cage where each initialized mutex is in the parent cage + let mutextable = self.mutex_table.read(); + let mut new_mutex_table = vec![]; + for elem in mutextable.iter() { + if elem.is_some() { + let new_mutex_result = interface::RawMutex::create(); + match new_mutex_result { + Ok(new_mutex) => new_mutex_table.push(Some(interface::RustRfc::new(new_mutex))), + Err(_) => { + match Errno::from_discriminant(interface::get_errno()) { + Ok(i) => { + return syscall_error( + i, + "fork", + "The libc call to pthread_mutex_init failed!", + ); + } + Err(()) => { + panic!("Unknown errno value from pthread_mutex_init returned!") + } + }; + } + } + } else { + new_mutex_table.push(None); + } + } + drop(mutextable); + + //construct a new condvar in the child cage where each initialized condvar is in the parent cage + let cvtable = self.cv_table.read(); + let mut new_cv_table = vec![]; + for elem in cvtable.iter() { + if elem.is_some() { + let new_cv_result = interface::RawCondvar::create(); + match new_cv_result { + Ok(new_cv) => new_cv_table.push(Some(interface::RustRfc::new(new_cv))), + Err(_) => { + match Errno::from_discriminant(interface::get_errno()) { + Ok(i) => { + return syscall_error( + i, + "fork", + "The libc call to pthread_cond_init failed!", + ); + } + Err(()) => { + panic!("Unknown errno value from pthread_cond_init returned!") + } + }; + } + } + } else { + new_cv_table.push(None); + } + } + drop(cvtable); + + // let cwd_container = self.cwd.read(); + // if let Some(cwdinodenum) = metawalk(&cwd_container) { + // if let Inode::Dir(ref mut cwddir) = + // *(FS_METADATA.inodetable.get_mut(&cwdinodenum).unwrap()) + // { + // cwddir.refcount += 1; + // } else { + // panic!("We changed from a directory that was not a directory in chdir!"); + // } + // } else { + // panic!("We changed from a directory that was not a directory in chdir!"); + // } + + // we grab the parent cages main threads sigset and store it at 0 + // we do this because we haven't established a thread for the cage yet, and dont have a threadid to store it at + // this way the child can initialize the sigset properly when it establishes its own mainthreadid + let newsigset = interface::RustHashMap::new(); + if !interface::RUSTPOSIX_TESTSUITE.load(interface::RustAtomicOrdering::Relaxed) { + // we don't add these for the test suite + let mainsigsetatomic = self + .sigset + .get( + &self + .main_threadid + .load(interface::RustAtomicOrdering::Relaxed), + ) + .unwrap(); + let mainsigset = interface::RustAtomicU64::new( + mainsigsetatomic.load(interface::RustAtomicOrdering::Relaxed), + ); + newsigset.insert(0, mainsigset); + } + + /* + * Construct a new semaphore table in child cage which equals to the one in the parent cage + */ + let semtable = &self.sem_table; + let new_semtable: interface::RustHashMap< + u32, + interface::RustRfc, + > = interface::RustHashMap::new(); + // Loop all pairs + for pair in semtable.iter() { + new_semtable.insert((*pair.key()).clone(), pair.value().clone()); + } + + let cageobj = Cage { + cageid: child_cageid, + cwd: interface::RustLock::new(self.cwd.read().clone()), + parent: self.cageid, + // filedescriptortable: newfdtable, + cancelstatus: interface::RustAtomicBool::new(false), + // This happens because self.getgid tries to copy atomic value which does not implement "Copy" trait; self.getgid.load returns i32. + getgid: interface::RustAtomicI32::new( + self.getgid.load(interface::RustAtomicOrdering::Relaxed), + ), + getuid: interface::RustAtomicI32::new( + self.getuid.load(interface::RustAtomicOrdering::Relaxed), + ), + getegid: interface::RustAtomicI32::new( + self.getegid.load(interface::RustAtomicOrdering::Relaxed), + ), + geteuid: interface::RustAtomicI32::new( + self.geteuid.load(interface::RustAtomicOrdering::Relaxed), + ), + rev_shm: interface::Mutex::new((*self.rev_shm.lock()).clone()), + mutex_table: interface::RustLock::new(new_mutex_table), + cv_table: interface::RustLock::new(new_cv_table), + sem_table: new_semtable, + thread_table: interface::RustHashMap::new(), + signalhandler: self.signalhandler.clone(), + sigset: newsigset, + pendingsigset: interface::RustHashMap::new(), + main_threadid: interface::RustAtomicU64::new(0), + interval_timer: interface::IntervalTimer::new(child_cageid), + }; + + let shmtable = &SHM_METADATA.shmtable; + //update fields for shared mappings in cage + for rev_mapping in cageobj.rev_shm.lock().iter() { + let mut shment = shmtable.get_mut(&rev_mapping.1).unwrap(); + shment.shminfo.shm_nattch += 1; + let refs = shment.attached_cages.get(&self.cageid).unwrap(); + let childrefs = refs.clone(); + drop(refs); + shment.attached_cages.insert(child_cageid, childrefs); + } + + interface::cagetable_insert(child_cageid, cageobj); + + 0 + } + + /* + * exec() will only return if error happens + */ + pub fn exec_syscall(&self, child_cageid: u64) -> i32 { + // Empty fd with flag should_cloexec + empty_fds_for_exec(self.cageid); + // Add the new one to fdtable + let _ = copy_fdtable_for_cage(self.cageid, child_cageid); + // Delete the original one + let _newfdtable = remove_cage_from_fdtable(self.cageid); + + interface::cagetable_remove(self.cageid); + + self.unmap_shm_mappings(); + + // we grab the parent cages main threads sigset and store it at 0 + // this way the child can initialize the sigset properly when it establishes its own mainthreadid + let newsigset = interface::RustHashMap::new(); + if !interface::RUSTPOSIX_TESTSUITE.load(interface::RustAtomicOrdering::Relaxed) { + // we don't add these for the test suite + let mainsigsetatomic = self + .sigset + .get( + &self + .main_threadid + .load(interface::RustAtomicOrdering::Relaxed), + ) + .unwrap(); + let mainsigset = interface::RustAtomicU64::new( + mainsigsetatomic.load(interface::RustAtomicOrdering::Relaxed), + ); + newsigset.insert(0, mainsigset); + } + + let newcage = Cage { + cageid: child_cageid, + cwd: interface::RustLock::new(self.cwd.read().clone()), + parent: self.parent, + // filedescriptortable: self.filedescriptortable.clone(), + cancelstatus: interface::RustAtomicBool::new(false), + getgid: interface::RustAtomicI32::new(-1), + getuid: interface::RustAtomicI32::new(-1), + getegid: interface::RustAtomicI32::new(-1), + geteuid: interface::RustAtomicI32::new(-1), + rev_shm: interface::Mutex::new(vec![]), + mutex_table: interface::RustLock::new(vec![]), + cv_table: interface::RustLock::new(vec![]), + sem_table: interface::RustHashMap::new(), + thread_table: interface::RustHashMap::new(), + signalhandler: interface::RustHashMap::new(), + sigset: newsigset, + pendingsigset: interface::RustHashMap::new(), + main_threadid: interface::RustAtomicU64::new(0), + interval_timer: self.interval_timer.clone_with_new_cageid(child_cageid), + }; + //wasteful clone of fdtable, but mutability constraints exist + + interface::cagetable_insert(child_cageid, newcage); + 0 + } + + pub fn exit_syscall(&self, status: i32) -> i32 { + // println!("[[EXIT]] - {:?}", self.cageid); + // io::stdout().flush().unwrap(); + + //flush anything left in stdout + interface::flush_stdout(); + self.unmap_shm_mappings(); + + let _ = remove_cage_from_fdtable(self.cageid); + + //may not be removable in case of lindrustfinalize, we don't unwrap the remove result + interface::cagetable_remove(self.cageid); + + // Trigger SIGCHLD + if !interface::RUSTPOSIX_TESTSUITE.load(interface::RustAtomicOrdering::Relaxed) { + // dont trigger SIGCHLD for test suite + if self.cageid != self.parent { + interface::lind_kill_from_id(self.parent, libc::SIGCHLD); + } + } + + //fdtable will be dropped at end of dispatcher scope because of Arc + status + } + + pub fn getpid_syscall(&self) -> i32 { + self.cageid as i32 //not sure if this is quite what we want but it's easy enough to change later + } + pub fn getppid_syscall(&self) -> i32 { + self.parent as i32 // mimicing the call above -- easy to change later if necessary + } + + /*if its negative 1 + return -1, but also set the values in the cage struct to the DEFAULTs for future calls*/ + pub fn getgid_syscall(&self) -> i32 { + if self.getgid.load(interface::RustAtomicOrdering::Relaxed) == -1 { + self.getgid + .store(DEFAULT_GID as i32, interface::RustAtomicOrdering::Relaxed); + return -1; + } + DEFAULT_GID as i32 //Lind is only run in one group so a default value is returned + } + pub fn getegid_syscall(&self) -> i32 { + if self.getegid.load(interface::RustAtomicOrdering::Relaxed) == -1 { + self.getegid + .store(DEFAULT_GID as i32, interface::RustAtomicOrdering::Relaxed); + return -1; + } + DEFAULT_GID as i32 //Lind is only run in one group so a default value is returned + } + + pub fn getuid_syscall(&self) -> i32 { + if self.getuid.load(interface::RustAtomicOrdering::Relaxed) == -1 { + self.getuid + .store(DEFAULT_UID as i32, interface::RustAtomicOrdering::Relaxed); + return -1; + } + DEFAULT_UID as i32 //Lind is only run as one user so a default value is returned + } + pub fn geteuid_syscall(&self) -> i32 { + if self.geteuid.load(interface::RustAtomicOrdering::Relaxed) == -1 { + self.geteuid + .store(DEFAULT_UID as i32, interface::RustAtomicOrdering::Relaxed); + return -1; + } + DEFAULT_UID as i32 //Lind is only run as one user so a default value is returned + } + + pub fn sigaction_syscall( + &self, + sig: i32, + act: Option<&interface::SigactionStruct>, + oact: Option<&mut interface::SigactionStruct>, + ) -> i32 { + if let Some(some_oact) = oact { + let old_sigactionstruct = self.signalhandler.get(&sig); + + if let Some(entry) = old_sigactionstruct { + some_oact.clone_from(entry.value()); + } else { + some_oact.clone_from(&interface::SigactionStruct::default()); // leave handler field as NULL + } + } + + if let Some(some_act) = act { + if sig == 9 || sig == 19 { + // Disallow changing the action for SIGKILL and SIGSTOP + return syscall_error( + Errno::EINVAL, + "sigaction", + "Cannot modify the action of SIGKILL or SIGSTOP", + ); + } + + self.signalhandler.insert(sig, some_act.clone()); + } + + 0 + } + + pub fn kill_syscall(&self, cage_id: i32, sig: i32) -> i32 { + if (cage_id < 0) || (cage_id >= interface::MAXCAGEID) { + return syscall_error(Errno::EINVAL, "sigkill", "Invalid cage id."); + } + + if let Some(cage) = interface::cagetable_getref_opt(cage_id as u64) { + interface::lind_threadkill( + cage.main_threadid + .load(interface::RustAtomicOrdering::Relaxed), + sig, + ); + return 0; + } else { + return syscall_error(Errno::ESRCH, "kill", "Target cage does not exist"); + } + } + + pub fn sigprocmask_syscall( + &self, + how: i32, + set: Option<&interface::SigsetType>, + oldset: Option<&mut interface::SigsetType>, + ) -> i32 { + let mut res = 0; + let pthreadid = interface::get_pthreadid(); + + let sigset = self.sigset.get(&pthreadid).unwrap(); + + if let Some(some_oldset) = oldset { + *some_oldset = sigset.load(interface::RustAtomicOrdering::Relaxed); + } + + if let Some(some_set) = set { + let curr_sigset = sigset.load(interface::RustAtomicOrdering::Relaxed); + res = match how { + cage::SIG_BLOCK => { + // Block signals in set + sigset.store( + curr_sigset | *some_set, + interface::RustAtomicOrdering::Relaxed, + ); + 0 + } + cage::SIG_UNBLOCK => { + // Unblock signals in set + let newset = curr_sigset & !*some_set; + let pendingsignals = curr_sigset & some_set; + sigset.store(newset, interface::RustAtomicOrdering::Relaxed); + self.send_pending_signals(pendingsignals, pthreadid); + 0 + } + cage::SIG_SETMASK => { + // Set sigset to set + sigset.store(*some_set, interface::RustAtomicOrdering::Relaxed); + 0 + } + _ => syscall_error(Errno::EINVAL, "sigprocmask", "Invalid value for how"), + } + } + res + } + + // pub fn setitimer_syscall( + // &self, + // which: i32, + // new_value: &itimerval, + // old_value: &mut itimerval, + // ) -> i32 { + // unsafe { libc::syscall(SYS_setitimer, which, new_value, old_value) as i32 } + // } + pub fn setitimer_syscall( + &self, + which: i32, + new_value: Option<&interface::ITimerVal>, + old_value: Option<&mut interface::ITimerVal>, + ) -> i32 { + match which { + sys_constants::ITIMER_REAL => { + if let Some(some_old_value) = old_value { + let (curr_duration, next_duration) = self.interval_timer.get_itimer(); + some_old_value.it_value.tv_sec = curr_duration.as_secs() as i64; + some_old_value.it_value.tv_usec = curr_duration.subsec_millis() as i64; + some_old_value.it_interval.tv_sec = next_duration.as_secs() as i64; + some_old_value.it_interval.tv_usec = next_duration.subsec_millis() as i64; + } + + if let Some(some_new_value) = new_value { + let curr_duration = interface::RustDuration::new( + some_new_value.it_value.tv_sec as u64, + some_new_value.it_value.tv_usec as u32, + ); + let next_duration = interface::RustDuration::new( + some_new_value.it_interval.tv_sec as u64, + some_new_value.it_interval.tv_usec as u32, + ); + + self.interval_timer.set_itimer(curr_duration, next_duration); + } + } + + _ => { /* ITIMER_VIRTUAL and ITIMER_PROF is not implemented*/ } + } + 0 + } + + pub fn getrlimit(&self, res_type: u64, rlimit: &mut interface::Rlimit) -> i32 { + match res_type { + sys_constants::RLIMIT_NOFILE => { + rlimit.rlim_cur = NOFILE_CUR; + rlimit.rlim_max = NOFILE_MAX; + } + sys_constants::RLIMIT_STACK => { + rlimit.rlim_cur = STACK_CUR; + rlimit.rlim_max = STACK_MAX; + } + _ => return -1, + } + 0 + } + + pub fn setrlimit(&self, res_type: u64, _limit_value: u64) -> i32 { + match res_type { + sys_constants::RLIMIT_NOFILE => { + if NOFILE_CUR > NOFILE_MAX { + -1 + } else { + 0 + } + //FIXME: not implemented yet to update value in program + } + _ => -1, + } + } +} diff --git a/src/safeposix/syscalls/sys_constants.rs b/src/safeposix/syscalls/sys_constants.rs new file mode 100644 index 00000000..89feb715 --- /dev/null +++ b/src/safeposix/syscalls/sys_constants.rs @@ -0,0 +1,77 @@ +// System related constants +#![allow(dead_code)] +#![allow(unused_variables)] + +use crate::interface; + +// Define constants using static or const +// Imported into fs_calls file + +//GID AND UID DEFAULT VALUES + +pub const DEFAULT_UID: u32 = 1000; +pub const DEFAULT_GID: u32 = 1000; + +// RESOURCE LIMITS + +pub const SIGNAL_MAX: i32 = 64; + +pub const NOFILE_CUR: u64 = 1024; +pub const NOFILE_MAX: u64 = 4 * 1024; + +pub const STACK_CUR: u64 = 8192 * 1024; +pub const STACK_MAX: u64 = 1 << 32; + +pub const RLIMIT_STACK: u64 = 0; +pub const RLIMIT_NOFILE: u64 = 1; + +// Constants for exit_syscall status + +pub const EXIT_SUCCESS: i32 = 0; +pub const EXIT_FAILURE: i32 = 1; + +// Signal Table (x86/ARM) +// Based on https://man7.org/linux/man-pages/man7/signal.7.html +pub const SIGHUP: i32 = 1; +pub const SIGINT: i32 = 2; +pub const SIGQUIT: i32 = 3; +pub const SIGILL: i32 = 4; +pub const SIGTRAP: i32 = 5; +pub const SIGABRT: i32 = 6; +pub const SIGIOT: i32 = 6; +pub const SIGBUS: i32 = 7; +// pub const SIGEMT: i32 +pub const SIGFPE: i32 = 8; +pub const SIGKILL: i32 = 9; +pub const SIGUSR1: i32 = 10; +pub const SIGSEGV: i32 = 11; +pub const SIGUSR2: i32 = 12; +pub const SIGPIPE: i32 = 13; +pub const SIGALRM: i32 = 14; +pub const SIGTERM: i32 = 15; +pub const SIGSTKFLT: i32 = 16; +pub const SIGCHLD: i32 = 17; +// pub const SIGCLD: i32 +pub const SIGCONT: i32 = 18; +pub const SIGSTOP: i32 = 19; +pub const SIGTSTP: i32 = 20; +pub const SIGTTIN: i32 = 21; +pub const SIGTTOU: i32 = 22; +pub const SIGURG: i32 = 23; +pub const SIGXCPU: i32 = 24; +pub const SIGXFSZ: i32 = 25; +pub const SIGVTALRM: i32 = 26; +pub const SIGPROF: i32 = 27; +pub const SIGWINCH: i32 = 28; +pub const SIGIO: i32 = 29; +pub const SIGPOLL: i32 = 29; +pub const SIGPWR: i32 = 30; +// pub const SIGINFO: i32 +// pub const SIGLOST: i32 +pub const SIGSYS: i32 = 31; +pub const SIGUNUSED: i32 = 31; + +pub const SIG_BLOCK: i32 = 0; +pub const SIG_UNBLOCK: i32 = 1; +pub const SIG_SETMASK: i32 = 2; +pub const ITIMER_REAL: i32 = 0; diff --git a/src/tests/fs_tests.rs b/src/tests/fs_tests.rs new file mode 100644 index 00000000..afb5ca78 --- /dev/null +++ b/src/tests/fs_tests.rs @@ -0,0 +1,1404 @@ +#[allow(unused_parens)] +#[cfg(test)] +pub mod fs_tests { + use super::super::*; + use crate::{interface, safeposix}; + use crate::safeposix::syscalls::fs_calls::*; + // use crate::safeposix::{cage::*, dispatcher::*, filesystem}; + use crate::safeposix::{cage::*, dispatcher::*, shm}; + use std::fs::OpenOptions; + use std::os::unix::fs::PermissionsExt; + use libc::*; + use std::mem; + + use std::io::Write; + use std::io; + use std::ptr; + use libc::*; + use std::ffi::CStr; + use crate::example_grates::translate_virtual_fd; + + use crate::tests::fs_tests::fs_tests::shm::SHM_METADATA; + use crate::interface::StatData; + + static S_IRWXA: u32 = 0o777; + static S_LIND: u32 = 0o755; + + pub fn test_fs() { + // ut_lind_fs_open(); + // ut_lind_fs_fork(); + // ut_lind_fs_simple(); // has to go first, else the data files created screw with link count test + // rdwrtest(); + + // ut_lind_fs_broken_close(); + // ut_lind_fs_chmod(); + // ut_lind_fs_fchmod(); + // ut_lind_fs_dir_chdir(); + // ut_lind_fs_dir_mode(); + // ut_lind_fs_dir_multiple(); + // ut_lind_fs_dup(); + // ut_lind_fs_dup2(); + // ut_lind_fs_fcntl(); + + // ut_lind_fs_ioctl(); + + // ut_lind_fs_fdflags(); + // ut_lind_fs_file_link_unlink(); + // ut_lind_fs_file_lseek_past_end(); + // ut_lind_fs_fstat_complex(); + // ut_lind_fs_getuid(); + // ut_lind_fs_load_fs(); + // ut_lind_fs_mknod(); + // ut_lind_fs_multiple_open(); + // ut_lind_fs_rename(); + // ut_lind_fs_rmdir(); + // ut_lind_fs_stat_file_complex(); + // ut_lind_fs_stat_file_mode(); + // ut_lind_fs_statfs(); + // ut_lind_fs_fstatfs(); + // ut_lind_fs_ftruncate(); + // ut_lind_fs_truncate(); + // ut_lind_fs_getdents(); + // ut_lind_fs_dir_chdir_getcwd(); + // prdwrtest(); + // chardevtest(); + // ut_lind_fs_exec_cloexec(); + // ut_lind_fs_shm(); + // ut_lind_fs_getpid_getppid(); + ut_lind_fs_sem_fork(); + // ut_lind_fs_sem_trytimed(); + // ut_lind_fs_sem_test(); + // ut_lind_fs_tmp_file_test(); + } + + // pub fn ut_lind_fs_open() { + // lindrustinit(0); + // let cage = interface::cagetable_getref(1); + // let fd = cage.open_syscall("/fcntl_file", O_RDWR | O_CREAT | O_EXCL, S_IWUSR); + // if fd < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("errno: {:?}", err); + // println!("Error message: {:?}", err_msg); + // println!("VirtualFD: {:?}", fd); + // io::stdout().flush().unwrap(); + // // panic!(); + // } + // assert_eq!(fd, 3); + + // let kernel_fd = translate_virtual_fd(1, fd).unwrap(); + // if cage.fcntl_syscall(fd, F_GETFD, 0) < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("errno: {:?}", err); + // println!("Error message: {:?}", err_msg); + // println!("VirtualFD: {:?}", fd); + // println!("KernelFD: {:?}", kernel_fd); + // io::stdout().flush().unwrap(); + // // panic!(); + // } + // assert_eq!(kernel_fd, 3); + + // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + // lindrustfinalize(); + // } + + // pub fn ut_lind_fs_fork() { + // lindrustinit(0); + // let cage = interface::cagetable_getref(1); + + // // Fork child process + // assert_eq!(cage.fork_syscall(2), 0); + // // Child process + // let thread_child = interface::helper_thread(move || { + // interface::sleep(interface::RustDuration::from_millis(500)); + // let cage1 = interface::cagetable_getref(2); + // let fd1 = cage1.open_syscall("/foobar", O_RDWR, S_LIND); + // assert_eq!(cage1.lseek_syscall(fd1, 5, SEEK_SET), 5); + // assert_eq!(cage1.write_syscall(fd1, str2cbuf(" world"), 6), 6); + // assert_eq!(cage1.lseek_syscall(fd1, 0, SEEK_SET), 0); + // let mut read_buf2 = sizecbuf(12); + // assert_eq!(cage1.read_syscall(fd1, read_buf2.as_mut_ptr(), 12), 12); + // assert_eq!(cbuf2str(&read_buf2), "hello world!"); + // cage1.exit_syscall(libc::EXIT_SUCCESS); + // }); + // //Parent processes + // let thread_parent = interface::helper_thread(move || { + // let fd = cage.open_syscall("/foobar", O_CREAT | O_RDWR, S_LIND); + // assert!(fd >= 0); + // assert_eq!(cage.write_syscall(fd, str2cbuf("hello there!"), 12), 12); + + // assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); + // let mut read_buf1 = sizecbuf(5); + // assert_eq!(cage.read_syscall(fd, read_buf1.as_mut_ptr(), 5), 5); + // assert_eq!(cbuf2str(&read_buf1), "hello"); + // // interface::sleep(interface::RustDuration::from_millis(1000)); + // cage.exit_syscall(libc::EXIT_SUCCESS); + // }); + // thread_child.join().unwrap(); + // thread_parent.join().unwrap(); + // lindrustfinalize(); + // } + + // pub fn ut_lind_fs_simple() { + // lindrustinit(0); + // let cage = interface::cagetable_getref(1); + + // assert_eq!(cage.access_syscall("/foobar", F_OK), 0); + // assert_eq!(cage.access_syscall("/foobar", X_OK | R_OK), 0); + + // let mut statdata2 = StatData::default(); + + // assert_eq!(cage.stat_syscall("/foobar", &mut statdata2), 0); + // //ensure that there are two hard links + + // assert_eq!(statdata2.st_nlink, 1); //2 for . and .., one for dev, and one so that it can never be removed + + // //ensure that there is no associated size + // assert_eq!(statdata2.st_size, 4); + // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + // lindrustfinalize(); + // } + + // pub fn rdwrtest() { + // println!("RDWTEST begin"); + // io::stdout().flush().unwrap(); + + // lindrustinit(0); + // let cage = interface::cagetable_getref(1); + + // let fd = cage.open_syscall( + // "/foobar", + // O_CREAT | O_TRUNC | O_RDWR, + // (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH) as u32); + // if fd < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("errno: {:?}", err); + // println!("Error message: {:?}", err_msg); + // println!("VirtualFD: {:?}", fd); + // io::stdout().flush().unwrap(); + // // panic!(); + // } + + // println!("open finished"); + // io::stdout().flush().unwrap(); + + // assert_eq!(cage.write_syscall(fd, str2cbuf("hello there!"), 12), 12); + + // println!("write finished"); + // io::stdout().flush().unwrap(); + + // assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); + // let mut read_buf1 = sizecbuf(5); + // assert_eq!(cage.read_syscall(fd, read_buf1.as_mut_ptr(), 5), 5); + // assert_eq!(cbuf2str(&read_buf1), "hello"); + + // assert_eq!(cage.write_syscall(fd, str2cbuf(" world"), 6), 6); + + // assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); + // let mut read_buf2 = sizecbuf(12); + // assert_eq!(cage.read_syscall(fd, read_buf2.as_mut_ptr(), 12), 12); + // assert_eq!(cbuf2str(&read_buf2), "hello world!"); + + // println!("read finished"); + // io::stdout().flush().unwrap(); + + // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + + // println!("exit finished"); + // io::stdout().flush().unwrap(); + + // lindrustfinalize(); + // } + + // pub fn prdwrtest() { + // lindrustinit(0); + // let cage = interface::cagetable_getref(1); + + // let fd = cage.open_syscall("/foobar2", O_CREAT | O_TRUNC | O_RDWR, (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH) as u32); + // assert!(fd >= 0); + + // assert_eq!(cage.pwrite_syscall(fd, str2cbuf("hello there!"), 12, 0), 12); + + // let mut read_buf1 = sizecbuf(5); + // assert_eq!(cage.pread_syscall(fd, read_buf1.as_mut_ptr(), 5, 0), 5); + // assert_eq!(cbuf2str(&read_buf1), "hello"); + + // assert_eq!(cage.pwrite_syscall(fd, str2cbuf(" world"), 6, 5), 6); + + // let mut read_buf2 = sizecbuf(12); + // assert_eq!(cage.pread_syscall(fd, read_buf2.as_mut_ptr(), 12, 0), 12); + // assert_eq!(cbuf2str(&read_buf2), "hello world!"); + + // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + // lindrustfinalize(); + // } + +// pub fn chardevtest() { +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); + +// let fd = cage.open_syscall("/dev/zero", O_RDWR, S_IRWXA); +// assert!(fd >= 0); + +// assert_eq!( +// cage.pwrite_syscall( +// fd, +// str2cbuf("Lorem ipsum dolor sit amet, consectetur adipiscing elit"), +// 55, +// 0 +// ), +// 55 +// ); + +// let mut read_bufzero = sizecbuf(1000); +// assert_eq!( +// cage.pread_syscall(fd, read_bufzero.as_mut_ptr(), 1000, 0), +// 1000 +// ); +// assert_eq!( +// cbuf2str(&read_bufzero), +// std::iter::repeat("\0") +// .take(1000) +// .collect::() +// .as_str() +// ); + +// assert_eq!(cage.chdir_syscall("dev"), 0); +// assert_eq!(cage.close_syscall(fd), 0); + +// let fd2 = cage.open_syscall("./urandom", O_RDWR, S_IRWXA); +// assert!(fd2 >= 0); +// let mut read_bufrand = sizecbuf(1000); +// assert_eq!( +// cage.read_syscall(fd2, read_bufrand.as_mut_ptr(), 1000), +// 1000 +// ); +// assert_eq!(cage.close_syscall(fd2), 0); +// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); +// lindrustfinalize(); +// } + + // pub fn ut_lind_fs_broken_close() { + // //testing a muck up with the inode table where the regular close does not work as intended + + // lindrustinit(0); + // let cage = interface::cagetable_getref(1); + + // //write should work + // let mut fd = cage.open_syscall("/broken_close_file", O_CREAT | O_EXCL | O_RDWR, S_IRWXA); + // assert_eq!(cage.write_syscall(fd, str2cbuf("Hello There!"), 12), 12); + // assert_eq!(cage.close_syscall(fd), 0); + + // //close the file and then open it again... and then close it again + // fd = cage.open_syscall("/broken_close_file", O_RDWR, S_IRWXA); + // assert_eq!(cage.close_syscall(fd), 0); + + // //let's try some things with connect + // //we are going to open a socket with a UDP specification... + // // let sockfd = cage.socket_syscall(AF_INET, SOCK_STREAM, 0); + + // // //bind should not be interesting + // // let mut sockad = interface::GenSockaddr::V4(interface::SockaddrV4::default()); + // // sockad.set_family(AF_INET as u16); + // // assert_eq!(cage.bind_syscall(sockfd, &sockad), 0); + + // fd = cage.open_syscall( + // "/broken_close_file", + // O_RDWR, + // (S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH) as u32); + // assert_eq!(cage.close_syscall(fd), 0); + + // fd = cage.open_syscall("/broken_close_file", O_RDWR, S_IRWXA); + // assert_eq!(cage.close_syscall(fd), 0); + + // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + // lindrustfinalize(); + // } + + // pub fn ut_lind_fs_chmod() { + // lindrustinit(0); + // let cage = interface::cagetable_getref(1); + + // let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; + // let filepath = "/chmodTestFile"; + + // let mut statdata: stat = unsafe { std::mem::zeroed() }; + + // let fd = cage.open_syscall(filepath, flags, S_LIND); + // assert_eq!(cage.stat_syscall(filepath, &mut statdata), 0); + // assert_eq!(statdata.st_mode, S_LIND | S_IFREG); + // // assert_eq!(statdata.st_mode & !S_IFMT, S_IRWXA); + + // assert_eq!(cage.chmod_syscall(filepath, S_IRUSR | S_IRGRP), 0); + // assert_eq!(cage.stat_syscall(filepath, &mut statdata), 0); + // assert_eq!(statdata.st_mode, S_IRUSR | S_IRGRP | S_IFREG); + + // assert_eq!(cage.chmod_syscall(filepath, S_LIND), 0); + // assert_eq!(cage.stat_syscall(filepath, &mut statdata), 0); + // assert_eq!(statdata.st_mode, S_LIND | S_IFREG as u32); + + // assert_eq!(cage.close_syscall(fd), 0); + // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + // lindrustfinalize(); + // } + + // pub fn ut_lind_fs_fchmod() { + // lindrustinit(0); + // let cage = interface::cagetable_getref(1); + + // let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; + // let filepath = "/fchmodTestFile"; + + // let mut statdata: stat = unsafe { std::mem::zeroed() }; + + // let fd = cage.open_syscall(filepath, flags, S_LIND); + // assert_eq!(cage.fstat_syscall(fd, &mut statdata), 0); + // assert_eq!(statdata.st_mode, S_LIND | S_IFREG as u32); + + // assert_eq!(cage.fchmod_syscall(fd, S_IRUSR | S_IRGRP), 0); + // assert_eq!(cage.fstat_syscall(fd, &mut statdata), 0); + // assert_eq!(statdata.st_mode, S_IRUSR | S_IRGRP | S_IFREG as u32); + + // assert_eq!(cage.fchmod_syscall(fd, S_LIND), 0); + // assert_eq!(cage.fstat_syscall(fd, &mut statdata), 0); + // assert_eq!(statdata.st_mode, S_LIND | S_IFREG as u32); + + // assert_eq!(cage.close_syscall(fd), 0); + // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + // lindrustfinalize(); + // } + + // pub fn ut_lind_fs_dir_chdir() { + // lindrustinit(0); + // let cage = interface::cagetable_getref(1); + + // //testing the ability to make and change to directories + + // assert_eq!(cage.mkdir_syscall("/subdir1", S_LIND), 0); + // assert_eq!(cage.mkdir_syscall("/subdir1/subdir2", S_LIND), 0); + // assert_eq!(cage.mkdir_syscall("/subdir1/subdir2/subdir3", S_LIND), 0); + + // assert_eq!(cage.access_syscall("/subdir1", F_OK), 0); + // assert_eq!(cage.chdir_syscall("/subdir1"), 0); + + // assert_eq!(cage.access_syscall("/subdir1/subdir2", F_OK), 0); + // assert_eq!(cage.chdir_syscall(".."), 0); + + // assert_eq!(cage.access_syscall("/subdir1", F_OK), 0); + // assert_eq!(cage.chdir_syscall("/subdir1/subdir2/subdir3"), 0); + // assert_eq!(cage.access_syscall("../../../subdir1", F_OK), 0); + + // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + // lindrustfinalize(); + // } + + // pub fn ut_lind_fs_dir_mode() { + // lindrustinit(0); + // let cage = interface::cagetable_getref(1); + + // let filepath1 = "/subdirDirMode1"; + // let filepath2 = "/subdirDirMode2"; + + // let mut statdata: stat = unsafe { std::mem::zeroed() }; + + // assert_eq!(cage.mkdir_syscall(filepath1, S_LIND), 0); + // assert_eq!(cage.stat_syscall(filepath1, &mut statdata), 0); + // assert_eq!(statdata.st_mode, S_LIND | S_IFDIR as u32); + + // assert_eq!(cage.mkdir_syscall(filepath2, 0), 0); + // assert_eq!(cage.stat_syscall(filepath2, &mut statdata), 0); + // assert_eq!(statdata.st_mode, S_IFDIR as u32); + + // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + // lindrustfinalize(); + // } + + // pub fn ut_lind_fs_dir_multiple() { + // lindrustinit(0); + // let cage = interface::cagetable_getref(1); + + // assert_eq!(cage.mkdir_syscall("/subdirMultiple1", S_LIND), 0); + // assert_eq!( + // cage.mkdir_syscall("/subdirMultiple1/subdirMultiple2", S_LIND), + // 0 + // ); + // assert_eq!( + // cage.mkdir_syscall("/subdirMultiple1/subdirMultiple2/subdirMultiple3", 0), + // 0 + // ); + + // let mut statdata: stat = unsafe { std::mem::zeroed() }; + + // //ensure that the file is a dir with all of the correct bits on for nodes + // assert_eq!( + // cage.stat_syscall("/subdirMultiple1/subdirMultiple2", &mut statdata), + // 0 + // ); + // assert_eq!(statdata.st_mode, S_LIND | S_IFDIR as u32); + + // assert_eq!( + // cage.stat_syscall( + // "/subdirMultiple1/subdirMultiple2/subdirMultiple3", + // &mut statdata + // ), + // 0 + // ); + // assert_eq!(statdata.st_mode, S_IFDIR as u32); + + // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + // lindrustfinalize(); + // } + + // pub fn ut_lind_fs_dup() { + // lindrustinit(0); + // let cage = interface::cagetable_getref(1); + + // let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; + // let filepath = "/dupfile"; + + // let fd = cage.open_syscall(filepath, flags, S_IRWXA); + // let mut temp_buffer = sizecbuf(2); + // assert!(fd >= 0); + // assert_eq!(cage.write_syscall(fd, str2cbuf("12"), 2), 2); + // assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); + // assert_eq!(cage.read_syscall(fd, temp_buffer.as_mut_ptr(), 2), 2); + // assert_eq!(cbuf2str(&temp_buffer), "12"); + + // //duplicate the file descriptor + // let fd2 = cage.dup_syscall(fd, None); + // assert!(fd != fd2); + + // //essentially a no-op, but duplicate again -- they should be diff &fd's + // let fd3 = cage.dup_syscall(fd, None); + // assert!(fd != fd2 && fd != fd3); + + // //We don't need all three, though: + // assert_eq!(cage.close_syscall(fd3), 0); + + // assert_eq!(cage.lseek_syscall(fd, 0, SEEK_END), 2); + // assert_eq!(cage.lseek_syscall(fd2, 0, SEEK_END), 2); + + // // write some data to move the first position + // assert_eq!(cage.write_syscall(fd, str2cbuf("34"), 2), 2); + + // //Make sure that they are still in the same place: + // let mut buffer = sizecbuf(4); + // assert_eq!( + // cage.lseek_syscall(fd, 0, SEEK_SET), + // cage.lseek_syscall(fd2, 0, SEEK_SET) + // ); + // assert_eq!(cage.read_syscall(fd, buffer.as_mut_ptr(), 4), 4); + // assert_eq!(cbuf2str(&buffer), "1234"); + + // assert_eq!(cage.close_syscall(fd), 0); + + // //the other &fd should still work + // assert_eq!(cage.lseek_syscall(fd2, 0, SEEK_END), 4); + // assert_eq!(cage.write_syscall(fd2, str2cbuf("5678"), 4), 4); + + // assert_eq!(cage.lseek_syscall(fd2, 0, SEEK_SET), 0); + // let mut buffer2 = sizecbuf(8); + // assert_eq!(cage.read_syscall(fd2, buffer2.as_mut_ptr(), 8), 8); + // assert_eq!(cage.close_syscall(fd2), 0); + // assert_eq!(cbuf2str(&buffer2), "12345678"); + + // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + // lindrustfinalize(); + // } + + // pub fn ut_lind_fs_dup2() { + // lindrustinit(0); + // let cage = interface::cagetable_getref(1); + + // let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; + // let filepath = "/dup2file"; + + // let fd = cage.open_syscall(filepath, flags, S_IRWXA); + + // assert_eq!(cage.write_syscall(fd, str2cbuf("12"), 2), 2); + + // //trying to dup fd into fd + 1 + // let _fd2: i32 = cage.dup2_syscall(fd, fd + 1 as i32); + + // //should be a no-op since the last line did the same thing + // let fd2: i32 = cage.dup2_syscall(fd, fd + 1 as i32); + + // //read/write tests for the files + // assert_eq!( + // cage.lseek_syscall(fd, 0, SEEK_END), + // cage.lseek_syscall(fd2, 0, SEEK_END) + // ); + // assert_eq!(cage.write_syscall(fd, str2cbuf("34"), 2), 2); + // assert_eq!( + // cage.lseek_syscall(fd, 0, SEEK_SET), + // cage.lseek_syscall(fd2, 0, SEEK_SET) + // ); + + // let mut buffer = sizecbuf(4); + // assert_eq!(cage.lseek_syscall(fd2, 0, SEEK_SET), 0); + // assert_eq!(cage.read_syscall(fd, buffer.as_mut_ptr(), 4), 4); + // assert_eq!(cbuf2str(&buffer), "1234"); + + // assert_eq!(cage.close_syscall(fd), 0); + + // let mut buffer2 = sizecbuf(8); + // assert_eq!(cage.lseek_syscall(fd2, 0, SEEK_END), 4); + // assert_eq!(cage.write_syscall(fd2, str2cbuf("5678"), 4), 4); + + // assert_eq!(cage.lseek_syscall(fd2, 0, SEEK_SET), 0); + // assert_eq!(cage.read_syscall(fd2, buffer2.as_mut_ptr(), 8), 8); + // assert_eq!(cbuf2str(&buffer2), "12345678"); + + // assert_eq!(cage.close_syscall(fd2), 0); + // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + // lindrustfinalize(); + // } + + // pub fn ut_lind_fs_fcntl() { + // lindrustinit(0); + // let cage = interface::cagetable_getref(1); + + // let sockfd = cage.socket_syscall(libc::AF_INET, libc::SOCK_STREAM, 0); + // let filefd = cage.open_syscall("/fcntl_file", O_CREAT | O_EXCL, S_IRWXA); + + // //set the setfd flag + // assert_eq!(cage.fcntl_syscall(sockfd, F_SETFD, libc::FD_CLOEXEC), 0); + + // //checking to see if the wrong flag was set or not + // assert_eq!(cage.fcntl_syscall(sockfd, F_GETFD, 0), libc::FD_CLOEXEC); + + // //let's get some more flags on the filefd + // assert_eq!( + // cage.fcntl_syscall(filefd, F_SETFL, O_RDONLY | O_NONBLOCK), + // 0 + // ); + + // //checking if the flags are updated... + // // assert_eq!(cage.fcntl_syscall(filefd, F_GETFL, 0), 2048); + // let flags = cage.fcntl_syscall(filefd, F_GETFL, 0); + + // assert_ne!(flags & libc::O_NONBLOCK, 0); + + // assert_eq!(cage.close_syscall(filefd), 0); + // assert_eq!(cage.close_syscall(sockfd), 0); + + // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + // lindrustfinalize(); + // } + + // pub fn ut_lind_fs_ioctl() { + // lindrustinit(0); + // let cage = interface::cagetable_getref(1); + + // let mut union0: winsize = unsafe { mem::zeroed() }; + // let mut union1: winsize = unsafe { mem::zeroed() }; + // union1.ws_col = 1; + // union1.ws_row = 1; + + // let union0_ptr: *mut winsize = &mut union0; + // let union1_ptr: *mut winsize = &mut union1; + + // let sockfd = cage.socket_syscall(libc::AF_INET, libc::SOCK_STREAM, 0); + // let filefd = cage.open_syscall("/ioctl_file", O_CREAT | O_EXCL, S_LIND); + + // //try to use FIONBIO for a non-socket + // // assert_eq!( + // // cage.ioctl_syscall(filefd, FIONBIO, union0_ptr as *mut u8), + // // 0 + // // ); + // if cage.ioctl_syscall(filefd, FIONBIO, union0_ptr as *mut u8) < 0 { + // let err = unsafe { + // libc::__errno_location() + // }; + // let err_str = unsafe { + // libc::strerror(*err) + // }; + // let err_msg = unsafe { + // CStr::from_ptr(err_str).to_string_lossy().into_owned() + // }; + // println!("errno: {:?}", err); + // println!("Error message: {:?}", err_msg); + // io::stdout().flush().unwrap(); + // panic!(); + // } + + // //clear the O_NONBLOCK flag + // assert_eq!(cage.ioctl_syscall(sockfd, FIONBIO, union0_ptr as *mut u8), 0); + + // //checking to see if the flag was updated + // assert_eq!(cage.fcntl_syscall(sockfd, F_GETFL, 0) & O_NONBLOCK, 0); + + // //set the O_NONBLOCK flag + // assert_eq!(cage.ioctl_syscall(sockfd, FIONBIO, union1_ptr as *mut u8), 0); + + // //checking to see if the flag was updated + // assert_eq!( + // cage.fcntl_syscall(sockfd, F_GETFL, 0) & O_NONBLOCK, + // O_NONBLOCK + // ); + + // //clear the O_NONBLOCK flag + // assert_eq!(cage.ioctl_syscall(sockfd, FIONBIO, union0_ptr as *mut u8), 0); + + // //checking to see if the flag was updated + // assert_eq!(cage.fcntl_syscall(sockfd, F_GETFL, 0) & O_NONBLOCK, 0); + + // assert_eq!(cage.close_syscall(filefd), 0); + // assert_eq!(cage.close_syscall(sockfd), 0); + + // assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + // lindrustfinalize(); + // } + +// pub fn ut_lind_fs_fdflags() { +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); + +// let path = "/fdFlagsFile"; + +// let fd = cage.creat_syscall(path, S_IRWXA); +// assert_eq!(cage.close_syscall(fd), 0); + +// let read_fd = cage.open_syscall(path, O_RDONLY, S_IRWXA); +// assert_eq!(cage.lseek_syscall(read_fd, 0, SEEK_SET), 0); +// assert_eq!( +// cage.write_syscall(read_fd, str2cbuf("Hello! This should not write."), 28), +// -(Errno::EBADF as i32) +// ); + +// let mut buf = sizecbuf(100); +// assert_eq!(cage.lseek_syscall(read_fd, 0, SEEK_SET), 0); + +// //this fails because nothing is written to the readfd (the previous write was unwritable) +// assert_eq!(cage.read_syscall(read_fd, buf.as_mut_ptr(), 100), 0); +// assert_eq!(cage.close_syscall(read_fd), 0); + +// let write_fd = cage.open_syscall(path, O_WRONLY, S_IRWXA); +// let mut buf2 = sizecbuf(100); +// assert_eq!(cage.lseek_syscall(write_fd, 0, SEEK_SET), 0); +// assert_eq!( +// cage.read_syscall(write_fd, buf2.as_mut_ptr(), 100), +// -(Errno::EBADF as i32) +// ); + +// assert_eq!(cage.lseek_syscall(write_fd, 0, SEEK_SET), 0); +// assert_eq!( +// cage.write_syscall(write_fd, str2cbuf("Hello! This should write."), 24), +// 24 +// ); +// assert_eq!(cage.close_syscall(write_fd), 0); + +// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); +// lindrustfinalize(); +// } + +// pub fn ut_lind_fs_file_link_unlink() { +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); + +// let path = "/fileLink"; +// let path2 = "/fileLink2"; + +// let fd = cage.open_syscall(path, O_CREAT | O_EXCL | O_WRONLY, S_IRWXA); +// assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); +// assert_eq!(cage.write_syscall(fd, str2cbuf("hi"), 2), 2); + +// let mut statdata = StatData::default(); + +// assert_eq!(cage.stat_syscall(path, &mut statdata), 0); +// assert_eq!(statdata.st_size, 2); +// assert_eq!(statdata.st_nlink, 1); + +// let mut statdata2 = StatData::default(); + +// //make sure that this has the same traits as the other file that we linked +// // and make sure that the link count on the orig file has increased +// assert_eq!(cage.link_syscall(path, path2), 0); +// assert_eq!(cage.stat_syscall(path, &mut statdata), 0); +// assert_eq!(cage.stat_syscall(path2, &mut statdata2), 0); +// assert!(statdata == statdata2); +// assert_eq!(statdata.st_nlink, 2); + +// //now we unlink +// assert_eq!(cage.unlink_syscall(path), 0); +// assert_eq!(cage.stat_syscall(path2, &mut statdata2), 0); +// assert_eq!(statdata2.st_nlink, 1); + +// //it shouldn't work to stat the orig since it is gone +// assert_ne!(cage.stat_syscall(path, &mut statdata), 0); +// assert_eq!(cage.unlink_syscall(path2), 0); + +// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); +// lindrustfinalize(); +// } + +// pub fn ut_lind_fs_file_lseek_past_end() { +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); + +// let path = "/lseekPastEnd"; + +// let fd = cage.open_syscall(path, O_CREAT | O_EXCL | O_RDWR, S_IRWXA); +// assert_eq!(cage.write_syscall(fd, str2cbuf("hello"), 5), 5); + +// //seek past the end and then write +// assert_eq!(cage.lseek_syscall(fd, 10, SEEK_SET), 10); +// assert_eq!(cage.write_syscall(fd, str2cbuf("123456"), 6), 6); + +// let mut buf = sizecbuf(16); +// assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); +// assert_eq!(cage.read_syscall(fd, buf.as_mut_ptr(), 20), 16); +// assert_eq!(cbuf2str(&buf), "hello\0\0\0\0\0123456"); + +// assert_eq!(cage.close_syscall(fd), 0); +// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); +// lindrustfinalize(); +// } + +// pub fn ut_lind_fs_fstat_complex() { +// lindrustinit(0); + +// let cage = interface::cagetable_getref(1); +// let path = "/complexFile"; + +// let fd = cage.open_syscall(path, O_CREAT | O_WRONLY, S_IRWXA); +// assert_eq!(cage.write_syscall(fd, str2cbuf("testing"), 4), 4); + +// let mut statdata = StatData::default(); + +// assert_eq!(cage.fstat_syscall(fd, &mut statdata), 0); +// assert_eq!(statdata.st_size, 4); +// assert_eq!(statdata.st_nlink, 1); + +// assert_eq!(cage.close_syscall(fd), 0); +// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); +// lindrustfinalize(); +// } + +// pub fn ut_lind_fs_getuid() { +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); + +// //let's get the initial -1s out of the way +// cage.getgid_syscall(); +// cage.getegid_syscall(); +// cage.getuid_syscall(); +// cage.geteuid_syscall(); + +// //testing to make sure that all of the gid and uid values are good to go when system is initialized +// assert_eq!(cage.getgid_syscall() as u32, DEFAULT_GID); +// assert_eq!(cage.getegid_syscall() as u32, DEFAULT_GID); +// assert_eq!(cage.getuid_syscall() as u32, DEFAULT_UID); +// assert_eq!(cage.geteuid_syscall() as u32, DEFAULT_UID); + +// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); +// lindrustfinalize(); +// } + +// pub fn ut_lind_fs_load_fs() { +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); + +// let mut statdata = StatData::default(); + +// //testing that all of the dev files made it out safe and sound +// cage.stat_syscall("/dev", &mut statdata); + +// assert_eq!(cage.stat_syscall("/dev/null", &mut statdata), 0); +// assert_eq!(statdata.st_rdev, makedev(&DevNo { major: 1, minor: 3 })); + +// assert_eq!(cage.stat_syscall("/dev/random", &mut statdata), 0); +// assert_eq!(statdata.st_rdev, makedev(&DevNo { major: 1, minor: 8 })); + +// assert_eq!(cage.stat_syscall("/dev/urandom", &mut statdata), 0); +// assert_eq!(statdata.st_rdev, makedev(&DevNo { major: 1, minor: 9 })); + +// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); +// lindrustfinalize(); +// } + +// pub fn ut_lind_fs_mknod() { +// // let's create /dev/null +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); +// let dev = makedev(&DevNo { major: 1, minor: 3 }); +// let path = "/null"; + +// //now we are going to mknod /dev/null with create, read and write flags and permissions +// //and then makr sure that it exists +// assert_eq!(cage.mknod_syscall(path, S_IFCHR as u32, dev), 0); +// let fd = cage.open_syscall(path, O_RDWR, S_IRWXA); + +// //checking the metadata of the file: +// let mut statdata = StatData::default(); + +// //should be a chr file, so let's check this +// let mut buf = sizecbuf(4); +// assert_eq!(cage.fstat_syscall(fd, &mut statdata), 0); +// assert_eq!(statdata.st_mode & S_FILETYPEFLAGS as u32, S_IFCHR as u32); +// assert_eq!(statdata.st_rdev, dev); +// assert_eq!(cage.write_syscall(fd, str2cbuf("test"), 4), 4); +// assert_eq!(cage.read_syscall(fd, buf.as_mut_ptr(), 4), 0); +// assert_eq!(cbuf2str(&buf), "\0\0\0\0"); +// assert_eq!(cage.close_syscall(fd), 0); + +// let mut statdata2 = StatData::default(); + +// //try it again with /dev/random +// let dev2 = makedev(&DevNo { major: 1, minor: 8 }); +// let path2 = "/random"; + +// //making the node and then making sure that it exists +// assert_eq!(cage.mknod_syscall(path2, S_IFCHR as u32, dev2), 0); +// let fd2 = cage.open_syscall(path2, O_RDWR, S_IRWXA); + +// let mut buf2 = sizecbuf(4); +// assert_eq!(cage.fstat_syscall(fd2, &mut statdata2), 0); +// assert_eq!(statdata2.st_mode & S_FILETYPEFLAGS as u32, S_IFCHR as u32); +// assert_eq!(statdata2.st_rdev, dev2); +// assert_eq!(cage.write_syscall(fd2, str2cbuf("testing"), 7), 7); +// assert_ne!(cage.read_syscall(fd2, buf2.as_mut_ptr(), 7), 0); +// assert_eq!(cage.close_syscall(fd2), 0); + +// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); +// lindrustfinalize(); +// } + +// pub fn ut_lind_fs_multiple_open() { +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); + +// //try to open several files at once -- the fd's should not be overwritten +// let fd1 = cage.open_syscall("/foo", O_CREAT | O_EXCL | O_RDWR, S_IRWXA); +// let fd2 = cage.open_syscall("/foo", O_RDWR, S_IRWXA); +// assert_ne!(fd1, fd2); + +// let flags: i32 = O_TRUNC | O_CREAT | O_RDWR; +// let mode: u32 = 0o666; // 0666 +// let name = "double_open_file"; + +// let mut read_buf = sizecbuf(2); +// let fd3 = cage.open_syscall(name, flags, mode); +// assert_eq!(cage.write_syscall(fd3, str2cbuf("hi"), 2), 2); +// assert_eq!(cage.lseek_syscall(fd3, 0, SEEK_SET), 0); +// assert_eq!(cage.read_syscall(fd3, read_buf.as_mut_ptr(), 2), 2); +// assert_eq!(cbuf2str(&read_buf), "hi"); + +// let _fd4 = cage.open_syscall(name, flags, mode); +// let mut buf = sizecbuf(5); +// assert_eq!(cage.lseek_syscall(fd3, 2, SEEK_SET), 2); +// assert_eq!(cage.write_syscall(fd3, str2cbuf("boo"), 3), 3); +// assert_eq!(cage.lseek_syscall(fd3, 0, SEEK_SET), 0); +// assert_eq!(cage.read_syscall(fd3, buf.as_mut_ptr(), 5), 5); +// assert_eq!(cbuf2str(&buf), "\0\0boo"); + +// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); +// lindrustfinalize(); +// } + +// pub fn ut_lind_fs_rmdir() { +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); + +// let path = "/parent_dir/dir"; +// assert_eq!(cage.mkdir_syscall("/parent_dir", S_IRWXA), 0); +// assert_eq!(cage.mkdir_syscall(path, S_IRWXA), 0); +// assert_eq!(cage.rmdir_syscall(path), 0); + +// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); +// lindrustfinalize(); +// } + +// pub fn ut_lind_fs_stat_file_complex() { +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); +// let fd = cage.open_syscall("/fooComplex", O_CREAT | O_EXCL | O_WRONLY, S_IRWXA); + +// assert_eq!(cage.write_syscall(fd, str2cbuf("hi"), 2), 2); + +// let mut statdata = StatData::default(); +// let mut statdata2 = StatData::default(); + +// assert_eq!(cage.fstat_syscall(fd, &mut statdata), 0); +// assert_eq!(statdata.st_size, 2); +// assert_eq!(statdata.st_nlink, 1); + +// assert_eq!(cage.link_syscall("/fooComplex", "/barComplex"), 0); +// assert_eq!(cage.stat_syscall("/fooComplex", &mut statdata), 0); +// assert_eq!(cage.stat_syscall("/barComplex", &mut statdata2), 0); + +// //check that they are the same and that the link count is 0 +// assert!(statdata == statdata2); +// assert_eq!(statdata.st_nlink, 2); + +// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); +// lindrustfinalize(); +// } + +// pub fn ut_lind_fs_stat_file_mode() { +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); +// let path = "/fooFileMode"; +// let _fd = cage.open_syscall(path, O_CREAT | O_EXCL | O_WRONLY, S_IRWXA); + +// let mut statdata = StatData::default(); +// assert_eq!(cage.stat_syscall(path, &mut statdata), 0); +// assert_eq!(statdata.st_mode, S_IRWXA | S_IFREG as u32); + +// //make a file without permissions and check that it is a reg file without permissions +// let path2 = "/fooFileMode2"; +// let _fd2 = cage.open_syscall(path2, O_CREAT | O_EXCL | O_WRONLY, 0); +// assert_eq!(cage.stat_syscall(path2, &mut statdata), 0); +// assert_eq!(statdata.st_mode, S_IFREG as u32); + +// //check that stat can be done on the current (root) dir +// assert_eq!(cage.stat_syscall(".", &mut statdata), 0); + +// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); +// lindrustfinalize(); +// } + +// pub fn ut_lind_fs_statfs() { +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); +// let mut fsdata = FSData::default(); + +// assert_eq!(cage.statfs_syscall("/", &mut fsdata), 0); +// assert_eq!(fsdata.f_type, 0xBEEFC0DE); +// assert_eq!(fsdata.f_bsize, 4096); + +// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); +// lindrustfinalize(); +// } + +// pub fn ut_lind_fs_fstatfs() { +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); +// let mut fsdata = FSData::default(); + +// // Get fd +// let fd = cage.open_syscall("/", O_RDONLY, 0); +// assert!(fd >= 0); +// // fstatfs +// assert_eq!(cage.fstatfs_syscall(fd, &mut fsdata), 0); +// // Check the output +// assert_eq!(fsdata.f_type, 0xBEEFC0DE); +// assert_eq!(fsdata.f_bsize, 4096); +// // Close the file +// assert_eq!(cage.close_syscall(fd), 0); + +// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); +// lindrustfinalize(); +// } + +// pub fn ut_lind_fs_rename() { +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); + +// let old_path = "/test_dir"; +// assert_eq!(cage.mkdir_syscall(old_path, S_IRWXA), 0); +// assert_eq!(cage.rename_syscall(old_path, "/test_dir_renamed"), 0); + +// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); +// lindrustfinalize(); +// } + +// pub fn ut_lind_fs_ftruncate() { +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); + +// let fd = cage.open_syscall("/ftruncate", O_CREAT | O_TRUNC | O_RDWR, S_IRWXA); +// assert!(fd >= 0); + +// // check if ftruncate() works for extending file with null bytes +// assert_eq!(cage.write_syscall(fd, str2cbuf("Hello there!"), 12), 12); +// assert_eq!(cage.ftruncate_syscall(fd, 15), 0); +// assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); +// let mut buf = sizecbuf(15); +// assert_eq!(cage.read_syscall(fd, buf.as_mut_ptr(), 15), 15); +// assert_eq!(cbuf2str(&buf), "Hello there!\0\0\0"); + +// // check if ftruncate() works for cutting off extra bytes +// assert_eq!(cage.ftruncate_syscall(fd, 5), 0); +// assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); +// let mut buf1 = sizecbuf(7); +// assert_eq!(cage.read_syscall(fd, buf1.as_mut_ptr(), 7), 5); +// assert_eq!(cbuf2str(&buf1), "Hello\0\0"); + +// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); +// lindrustfinalize(); +// } + +// pub fn ut_lind_fs_truncate() { +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); + +// let path = String::from("/truncate"); +// let fd = cage.open_syscall(&path, O_CREAT | O_TRUNC | O_RDWR, S_IRWXA); +// assert!(fd >= 0); + +// // check if truncate() works for extending file with null bytes +// assert_eq!(cage.write_syscall(fd, str2cbuf("Hello there!"), 12), 12); +// assert_eq!(cage.truncate_syscall(&path, 15), 0); +// assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); +// let mut buf = sizecbuf(15); +// assert_eq!(cage.read_syscall(fd, buf.as_mut_ptr(), 15), 15); +// assert_eq!(cbuf2str(&buf), "Hello there!\0\0\0"); + +// // check if truncate() works for cutting off extra bytes +// assert_eq!(cage.truncate_syscall(&path, 5), 0); +// assert_eq!(cage.lseek_syscall(fd, 0, SEEK_SET), 0); +// let mut buf1 = sizecbuf(7); +// assert_eq!(cage.read_syscall(fd, buf1.as_mut_ptr(), 7), 5); +// assert_eq!(cbuf2str(&buf1), "Hello\0\0"); + +// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); +// lindrustfinalize(); +// } + +// #[cfg(target_os = "macos")] +// type CharPtr = *const u8; + +// #[cfg(not(target_os = "macos"))] +// type CharPtr = *const i8; + +// pub fn ut_lind_fs_getdents() { +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); + +// let bufsize = 50; +// let mut vec = vec![0u8; bufsize as usize]; +// let baseptr: *mut u8 = &mut vec[0]; + +// assert_eq!(cage.mkdir_syscall("/getdents", S_IRWXA), 0); +// let fd = cage.open_syscall("/getdents", O_RDWR, S_IRWXA); +// assert_eq!(cage.getdents_syscall(fd, baseptr, bufsize as u32), 48); + +// unsafe { +// let first_dirent = baseptr as *mut interface::ClippedDirent; +// assert!((*first_dirent).d_off == 24); +// let reclen_matched: bool = ((*first_dirent).d_reclen == 24); +// assert_eq!(reclen_matched, true); + +// let nameoffset = baseptr.wrapping_offset(interface::CLIPPED_DIRENT_SIZE as isize); +// let returnedname = interface::RustCStr::from_ptr(nameoffset as *const _); +// let name_matched: bool = (returnedname +// == interface::RustCStr::from_bytes_with_nul(b".\0").unwrap()) +// | (returnedname == interface::RustCStr::from_bytes_with_nul(b"..\0").unwrap()); +// assert_eq!(name_matched, true); + +// let second_dirent = baseptr.wrapping_offset(24) as *mut interface::ClippedDirent; +// assert!((*second_dirent).d_off >= 48); +// } + +// assert_eq!(cage.close_syscall(fd), 0); +// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); +// lindrustfinalize(); +// } + +// pub fn ut_lind_fs_dir_chdir_getcwd() { +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); +// let needed = "/subdir1\0".as_bytes().to_vec().len(); + +// let needed_u32: u32 = needed as u32; + +// let mut buf = vec![0u8; needed]; +// let bufptr: *mut u8 = &mut buf[0]; + +// assert_eq!(cage.chdir_syscall("/"), 0); +// assert_eq!(cage.getcwd_syscall(bufptr, 0), -(Errno::ERANGE as i32)); +// assert_eq!(cage.getcwd_syscall(bufptr, 1), -(Errno::ERANGE as i32)); +// assert_eq!(cage.getcwd_syscall(bufptr, 2), 0); +// assert_eq!(std::str::from_utf8(&buf).unwrap(), "/\0\0\0\0\0\0\0\0"); + +// cage.mkdir_syscall("/subdir1", S_IRWXA); +// assert_eq!(cage.access_syscall("subdir1", F_OK), 0); +// assert_eq!(cage.chdir_syscall("subdir1"), 0); + +// assert_eq!(cage.getcwd_syscall(bufptr, 0), -(Errno::ERANGE as i32)); +// assert_eq!( +// cage.getcwd_syscall(bufptr, needed_u32 - 1), +// -(Errno::ERANGE as i32) +// ); +// assert_eq!(cage.getcwd_syscall(bufptr, needed_u32), 0); +// assert_eq!(std::str::from_utf8(&buf).unwrap(), "/subdir1\0"); + +// assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); +// lindrustfinalize(); +// } + +// pub fn ut_lind_fs_exec_cloexec() { +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); +// let mut uselessstatdata = StatData::default(); + +// let fd1 = cage.open_syscall( +// "/cloexecuted", +// O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, +// S_IRWXA, +// ); +// let fd2 = cage.open_syscall("/cloexekept", O_CREAT | O_TRUNC | O_RDWR, S_IRWXA); +// assert!(fd1 > 0); +// assert!(fd2 > 0); +// assert_eq!(cage.fstat_syscall(fd1, &mut uselessstatdata), 0); +// assert_eq!(cage.fstat_syscall(fd2, &mut uselessstatdata), 0); + +// assert_eq!(cage.exec_syscall(2), 0); + +// let execcage = interface::cagetable_getref(2); +// assert_eq!( +// execcage.fstat_syscall(fd1, &mut uselessstatdata), +// -(Errno::EBADF as i32) +// ); +// assert_eq!(execcage.fstat_syscall(fd2, &mut uselessstatdata), 0); + +// assert_eq!(execcage.close_syscall(fd2), 0); +// assert_eq!(cage.unlink_syscall("/cloexecuted"), 0); +// assert_eq!(cage.unlink_syscall("/cloexekept"), 0); + +// assert_eq!(execcage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); +// lindrustfinalize(); +// } + + // use libc::c_void; + // pub fn ut_lind_fs_shm() { + // lindrustinit(0); + // let cage = interface::cagetable_getref(1); + // let key = 31337; + // let mut shmidstruct = interface::ShmidsStruct::default(); + + // // shmget returns an identifier in shmid + // let shmid = cage.shmget_syscall(key, 1024, 0666 | IPC_CREAT); + + // // shmat to attach to shared memory + // let shmatret = cage.shmat_syscall(shmid, 0xfffff000 as *mut u8, 0); + + // // assert_ne!(shmatret, -1); + // assert_ne!(shmatret, 0); + + // // get struct info + // let shmctlret1 = cage.shmctl_syscall(shmid, IPC_STAT, Some(&mut shmidstruct)); + + // assert_eq!(shmctlret1, 0); + + // assert_eq!(shmidstruct.shm_nattch, 1); + + // // mark the shared memory to be rmoved + // let shmctlret2 = cage.shmctl_syscall(shmid, IPC_RMID, None); + + // assert_eq!(shmctlret2, 0); + + // //detach from shared memory + // let shmdtret = cage.shmdt_syscall(0xfffff000 as *mut u8); + + // assert_eq!(shmdtret, shmid); //NaCl requires shmdt to return the shmid, so this is non-posixy + + // lindrustfinalize(); + // } + +// pub fn ut_lind_fs_getpid_getppid() { +// lindrustinit(0); + +// let cage1 = interface::cagetable_getref(1); +// let pid1 = cage1.getpid_syscall(); + +// assert_eq!(cage1.fork_syscall(2), 0); + +// let child = std::thread::spawn(move || { +// let cage2 = interface::cagetable_getref(2); +// let pid2 = cage2.getpid_syscall(); +// let ppid2 = cage2.getppid_syscall(); + +// assert_ne!(pid2, pid1); // make sure the child and the parent have different pids +// assert_eq!(ppid2, pid1); // make sure the child's getppid is correct + +// assert_eq!(cage2.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); +// }); + +// child.join().unwrap(); +// assert_eq!(cage1.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); +// lindrustfinalize(); +// } + + pub fn ut_lind_fs_sem_fork() { + lindrustinit(0); + let cage = interface::cagetable_getref(1); + let key = 31337; + // Create a shared memory region + let shmid = cage.shmget_syscall(key, 1024, 0666 | IPC_CREAT); + // Attach the shared memory region + let shmatret = cage.shmat_syscall(shmid, 0xfffff000 as *mut u8, 0); + assert_ne!(shmatret, -1); + // Initialize the semaphore with shared between process + let ret_init = cage.sem_init_syscall(shmatret as u32, 1, 1); + assert_eq!(ret_init, 0); + assert_eq!(cage.sem_getvalue_syscall(shmatret as u32), 1); + // Fork child process + assert_eq!(cage.fork_syscall(2), 0); + // Child process + let thread_child = interface::helper_thread(move || { + let cage1 = interface::cagetable_getref(2); + // Child waits for the semaphore + assert_eq!(cage1.sem_wait_syscall(shmatret as u32), 0); + interface::sleep(interface::RustDuration::from_millis(40)); + // Release the semaphore + assert_eq!(cage1.sem_post_syscall(shmatret as u32), 0); + cage1.exit_syscall(libc::EXIT_SUCCESS); + }); + //Parent processes + // Parents waits for the semaphore + assert_eq!(cage.sem_wait_syscall(shmatret as u32), 0); + assert_eq!(cage.sem_getvalue_syscall(shmatret as u32), 0); + interface::sleep(interface::RustDuration::from_millis(100)); + // Parents release the semaphore + assert_eq!(cage.sem_post_syscall(shmatret as u32), 0); + interface::sleep(interface::RustDuration::from_millis(100)); + assert_eq!(cage.sem_getvalue_syscall(shmatret as u32), 1); + // Destroy the semaphore + assert_eq!(cage.sem_destroy_syscall(shmatret as u32), 0); + // mark the shared memory to be rmoved + let shmctlret2 = cage.shmctl_syscall(shmid, IPC_RMID, None); + assert_eq!(shmctlret2, 0); + //detach from shared memory + // panic!(&SHM_METADATA); + let shmdtret = cage.shmdt_syscall(0xfffff000 as *mut u8); + assert_eq!(shmdtret, shmid); + + thread_child.join().unwrap(); + cage.exit_syscall(libc::EXIT_SUCCESS); + lindrustfinalize(); + } + +// pub fn ut_lind_fs_sem_trytimed() { +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); +// let key = 31337; +// // Create a shared memory region +// let shmid = cage.shmget_syscall(key, 1024, 0666 | IPC_CREAT); +// // Attach the shared memory region +// let shmatret = cage.shmat_syscall(shmid, 0xfffff000 as *mut u8, 0); +// assert_ne!(shmatret, -1); +// // Initialize the semaphore with shared between process +// let ret_init = cage.sem_init_syscall(shmatret as u32, 1, 1); +// // assert_eq!(shmatret as u32, 0); +// assert_eq!(ret_init, 0); +// assert_eq!(cage.sem_getvalue_syscall(shmatret as u32), 1); +// // Fork child process +// assert_eq!(cage.fork_syscall(2), 0); +// // Child process +// let thread_child = interface::helper_thread(move || { +// let cage1 = interface::cagetable_getref(2); +// // Child waits for the semaphore +// assert_eq!(cage1.sem_trywait_syscall(shmatret as u32), 0); +// // Wait +// interface::sleep(interface::RustDuration::from_millis(20)); +// // Release the semaphore +// assert_eq!(cage1.sem_post_syscall(shmatret as u32), 0); +// cage1.exit_syscall(libc::EXIT_SUCCESS); +// }); +// //Parent processes +// let thread_parent = interface::helper_thread(move || { +// // Parents waits for the semaphore +// assert_eq!( +// cage.sem_timedwait_syscall( +// shmatret as u32, +// interface::RustDuration::from_millis(100) +// ), +// 0 +// ); +// assert_eq!(cage.sem_getvalue_syscall(shmatret as u32), 0); +// interface::sleep(interface::RustDuration::from_millis(10)); +// // Parents release the semaphore +// assert_eq!(cage.sem_post_syscall(shmatret as u32), 0); +// assert_eq!(cage.sem_getvalue_syscall(shmatret as u32), 1); +// // Destroy the semaphore +// assert_eq!(cage.sem_destroy_syscall(shmatret as u32), 0); +// // mark the shared memory to be rmoved +// let shmctlret2 = cage.shmctl_syscall(shmid, IPC_RMID, None); +// assert_eq!(shmctlret2, 0); +// //detach from shared memory +// let shmdtret = cage.shmdt_syscall(0xfffff000 as *mut u8); +// assert_eq!(shmdtret, shmid); +// cage.exit_syscall(libc::EXIT_SUCCESS); +// }); +// thread_child.join().unwrap(); +// thread_parent.join().unwrap(); +// lindrustfinalize(); +// } + +// pub fn ut_lind_fs_sem_test() { +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); +// let key = 31337; +// // Create a shared memory region +// let shmid = cage.shmget_syscall(key, 1024, 0666 | IPC_CREAT); +// // Attach the shared memory region +// let shmatret = cage.shmat_syscall(shmid, 0xfffff000 as *mut u8, 0); +// assert_ne!(shmatret, -1); +// assert_eq!(cage.sem_destroy_syscall(shmatret as u32), -22); +// assert_eq!(cage.sem_getvalue_syscall(shmatret as u32), -22); +// assert_eq!(cage.sem_post_syscall(shmatret as u32), -22); +// // Initialize the semaphore with shared between process +// let ret_init = cage.sem_init_syscall(shmatret as u32, 1, 0); +// assert_eq!(ret_init, 0); +// // Should return errno +// assert_eq!( +// cage.sem_timedwait_syscall(shmatret as u32, interface::RustDuration::from_millis(100)), +// -110 +// ); +// assert_eq!(cage.sem_trywait_syscall(shmatret as u32), -11); +// lindrustfinalize(); +// } + +// pub fn ut_lind_fs_tmp_file_test() { +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); + +// // Check if /tmp is there +// assert_eq!(cage.access_syscall("/tmp", F_OK), 0); + +// // Open file in /tmp +// let file_path = "/tmp/testfile"; +// let fd = cage.open_syscall(file_path, O_CREAT | O_TRUNC | O_RDWR, S_IRWXA); + +// assert_eq!(cage.write_syscall(fd, str2cbuf("Hello world"), 6), 6); +// assert_eq!(cage.close_syscall(fd), 0); + +// lindrustfinalize(); + +// // Init again +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); + +// // Check if /tmp is there +// assert_eq!(cage.access_syscall("/tmp", F_OK), 0); +// // Check if file is still there (it shouldn't be, assert no) +// assert_eq!(cage.access_syscall(file_path, F_OK), -2); + +// lindrustfinalize(); +// } +} diff --git a/src/tests/ipc_tests.rs b/src/tests/ipc_tests.rs new file mode 100644 index 00000000..31918279 --- /dev/null +++ b/src/tests/ipc_tests.rs @@ -0,0 +1,342 @@ +// #[cfg(test)] +// pub mod ipc_tests { +// use super::super::*; +// use crate::interface; +// use crate::safeposix::{cage::*, dispatcher::*, filesystem}; +// use std::fs::OpenOptions; +// use std::os::unix::fs::PermissionsExt; +// use std::time::Instant; + +// //#[test] +// pub fn test_ipc() { +// ut_lind_ipc_pipe(); +// ut_lind_ipc_domain_socket(); +// ut_lind_ipc_socketpair(); +// } + +// pub fn ut_lind_ipc_pipe() { +// let byte_chunk: usize = 131072; // 128 KB +// let num_writes: usize = 8192; // 8 KB + +// lindrustinit(0); + +// let cage1 = interface::cagetable_getref(1); + +// let mut pipefds = PipeArray { +// readfd: -1, +// writefd: -1, +// }; +// assert_eq!(cage1.pipe_syscall(&mut pipefds), 0); +// assert_eq!(cage1.fork_syscall(2), 0); + +// let sender = std::thread::spawn(move || { +// let cage2 = interface::cagetable_getref(2); + +// assert_eq!(cage2.close_syscall(pipefds.writefd), 0); +// assert_eq!(cage2.dup2_syscall(pipefds.readfd, 0), 0); +// assert_eq!(cage2.close_syscall(pipefds.readfd), 0); + +// let mut bytes_read: usize = 1; + +// let mut buf: Vec = Vec::with_capacity(byte_chunk * num_writes); +// let mut bufptr = buf.as_mut_ptr(); +// let mut buflen: usize = 0; + +// while bytes_read != 0 { +// bytes_read = cage2.read_syscall(0, bufptr, byte_chunk) as usize; +// unsafe { +// bufptr = bufptr.add(bytes_read); +// } +// buf.resize(buflen + bytes_read, 0); +// buflen += bytes_read; +// } +// assert_eq!(cage2.close_syscall(0), 0); + +// assert_eq!(cage2.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); +// }); + +// assert_eq!(cage1.close_syscall(pipefds.readfd), 0); +// assert_eq!(cage1.dup2_syscall(pipefds.writefd, 1), 1); +// assert_eq!(cage1.close_syscall(pipefds.writefd), 0); + +// for _i in 0..num_writes { +// let mut buf: Vec = vec!['A' as u8; byte_chunk]; +// let bufptr = buf.as_mut_ptr(); +// buf.resize(byte_chunk, 0); +// cage1.write_syscall(1, bufptr, byte_chunk); +// } + +// assert_eq!(cage1.close_syscall(1), 0); + +// sender.join().unwrap(); + +// assert_eq!(cage1.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + +// lindrustfinalize(); +// } + +// pub fn ut_lind_ipc_domain_socket() { +// //bind net zero test reformatted for domain sockets + +// let clientsockfilename = "/client.sock"; +// let serversockfilename = "/server.sock"; + +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); + +// //both the server and the socket are run from this file +// let serversockfd = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); +// let clientsockfd = cage.socket_syscall(AF_UNIX, SOCK_STREAM, 0); + +// //making sure that the assigned fd's are valid +// assert!(serversockfd > 0); +// assert!(clientsockfd > 0); + +// //binding to a socket +// let serversockaddr = +// interface::new_sockaddr_unix(AF_UNIX as u16, serversockfilename.as_bytes()); +// let serversocket = interface::GenSockaddr::Unix(serversockaddr); +// let clientsockaddr = +// interface::new_sockaddr_unix(AF_UNIX as u16, clientsockfilename.as_bytes()); +// let clientsocket = interface::GenSockaddr::Unix(clientsockaddr); + +// assert_eq!(cage.bind_syscall(serversockfd, &serversocket), 0); +// assert_eq!(cage.bind_syscall(clientsockfd, &clientsocket), 0); +// assert_eq!(cage.listen_syscall(serversockfd, 1), 0); //we are only allowing for one client at a time + +// //forking the cage to get another cage with the same information +// assert_eq!(cage.fork_syscall(2), 0); + +// //creating a thread for the server so that the information can be sent between the two threads +// let thread = interface::helper_thread(move || { +// let cage2 = interface::cagetable_getref(2); +// let mut socket2 = interface::GenSockaddr::Unix(interface::new_sockaddr_unix( +// AF_UNIX as u16, +// "".as_bytes(), +// )); // blank unix sockaddr + +// let sockfd = cage2.accept_syscall(serversockfd, &mut socket2); //really can only make sure that the fd is valid +// assert!(sockfd > 0); + +// interface::sleep(interface::RustDuration::from_millis(100)); + +// //process the first test... +// //Writing 100, then peek 100, then read 100 +// let mut buf = sizecbuf(100); +// assert_eq!( +// cage2.recvfrom_syscall( +// sockfd, +// buf.as_mut_ptr(), +// 100, +// MSG_PEEK, +// &mut Some(&mut socket2) +// ), +// 100 +// ); //peeking at the input message +// assert_eq!(cbuf2str(&buf), &"A".repeat(100)); +// buf = sizecbuf(100); +// assert_eq!( +// cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 100, 0, &mut Some(&mut socket2)), +// 100 +// ); //reading the input message +// assert_eq!(cbuf2str(&buf), &"A".repeat(100)); +// buf = sizecbuf(100); + +// interface::sleep(interface::RustDuration::from_millis(200)); + +// //process the second test... +// //Writing 100, read 20, peek 20, read 80 +// assert_eq!( +// cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 20, 0, &mut Some(&mut socket2)), +// 20 +// ); +// assert_eq!(cbuf2str(&buf), "A".repeat(20) + &"\0".repeat(80)); +// buf = sizecbuf(100); +// assert_eq!( +// cage2.recvfrom_syscall( +// sockfd, +// buf.as_mut_ptr(), +// 20, +// MSG_PEEK, +// &mut Some(&mut socket2) +// ), +// 20 +// ); +// assert_eq!(cbuf2str(&buf), "A".repeat(20) + &"\0".repeat(80)); +// buf = sizecbuf(100); +// assert_eq!( +// cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 80, 0, &mut Some(&mut socket2)), +// 80 +// ); +// assert_eq!(cbuf2str(&buf), "A".repeat(80) + &"\0".repeat(20)); +// buf = sizecbuf(100); + +// interface::sleep(interface::RustDuration::from_millis(200)); + +// //process the third test... +// //Writing 100, peek several times, read 100 +// for _ in 0..4 { +// assert_eq!( +// cage2.recvfrom_syscall( +// sockfd, +// buf.as_mut_ptr(), +// 10, +// MSG_PEEK, +// &mut Some(&mut socket2) +// ), +// 10 +// ); +// assert_eq!(cbuf2str(&buf), "A".repeat(10) + &"\0".repeat(90)); +// buf = sizecbuf(100); +// } +// for _ in 0..4 { +// assert_eq!( +// cage2.recvfrom_syscall( +// sockfd, +// buf.as_mut_ptr(), +// 20, +// MSG_PEEK, +// &mut Some(&mut socket2) +// ), +// 20 +// ); +// assert_eq!(cbuf2str(&buf), "A".repeat(20) + &"\0".repeat(80)); +// buf = sizecbuf(100); +// } +// for _ in 0..4 { +// assert_eq!( +// cage2.recvfrom_syscall( +// sockfd, +// buf.as_mut_ptr(), +// 30, +// MSG_PEEK, +// &mut Some(&mut socket2) +// ), +// 30 +// ); +// assert_eq!(cbuf2str(&buf), "A".repeat(30) + &"\0".repeat(70)); +// buf = sizecbuf(100); +// } +// for _ in 0..4 { +// assert_eq!( +// cage2.recvfrom_syscall( +// sockfd, +// buf.as_mut_ptr(), +// 40, +// MSG_PEEK, +// &mut Some(&mut socket2) +// ), +// 40 +// ); +// assert_eq!(cbuf2str(&buf), "A".repeat(40) + &"\0".repeat(60)); +// buf = sizecbuf(100); +// } +// assert_eq!( +// cage2.recvfrom_syscall(sockfd, buf.as_mut_ptr(), 100, 0, &mut Some(&mut socket2)), +// 100 +// ); +// assert_eq!(cbuf2str(&buf), &"A".repeat(100)); +// buf = sizecbuf(100); + +// interface::sleep(interface::RustDuration::from_millis(200)); + +// //process the fourth test... +// //Writing 50, peek 50 +// assert_eq!( +// cage2.recvfrom_syscall( +// sockfd, +// buf.as_mut_ptr(), +// 50, +// MSG_PEEK, +// &mut Some(&mut socket2) +// ), +// 50 +// ); +// assert_eq!(cbuf2str(&buf), "A".repeat(50) + &"\0".repeat(50)); +// assert_eq!(cage2.close_syscall(sockfd), 0); +// assert_eq!(cage2.close_syscall(serversockfd), 0); + +// assert_eq!(cage2.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); +// }); + +// //connect to the server +// interface::sleep(interface::RustDuration::from_millis(20)); + +// assert_eq!(cage.connect_syscall(clientsockfd, &serversocket), 0); + +// //send the data with delays so that the server can process the information cleanly +// assert_eq!( +// cage.send_syscall(clientsockfd, str2cbuf(&"A".repeat(100)), 100, 0), +// 100 +// ); +// interface::sleep(interface::RustDuration::from_millis(100)); + +// assert_eq!( +// cage.send_syscall(clientsockfd, str2cbuf(&"A".repeat(100)), 100, 0), +// 100 +// ); +// interface::sleep(interface::RustDuration::from_millis(100)); + +// assert_eq!( +// cage.send_syscall(clientsockfd, str2cbuf(&"A".repeat(100)), 100, 0), +// 100 +// ); +// interface::sleep(interface::RustDuration::from_millis(100)); + +// assert_eq!( +// cage.send_syscall(clientsockfd, str2cbuf(&"A".repeat(50)), 50, 0), +// 50 +// ); +// interface::sleep(interface::RustDuration::from_millis(100)); + +// assert_eq!(cage.close_syscall(clientsockfd), 0); + +// thread.join().unwrap(); + +// cage.unlink_syscall(serversockfilename); +// cage.unlink_syscall(clientsockfilename); + +// assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); +// lindrustfinalize(); +// } + +// pub fn ut_lind_ipc_socketpair() { +// lindrustinit(0); +// let cage = interface::cagetable_getref(1); +// let mut socketpair = interface::SockPair::default(); +// assert_eq!( +// Cage::socketpair_syscall(cage.clone(), AF_UNIX, SOCK_STREAM, 0, &mut socketpair), +// 0 +// ); +// let cage2 = cage.clone(); + +// let thread = interface::helper_thread(move || { +// let mut buf = sizecbuf(10); +// cage2.recv_syscall(socketpair.sock2, buf.as_mut_ptr(), 10, 0); +// assert_eq!(cbuf2str(&buf), "test\0\0\0\0\0\0"); + +// interface::sleep(interface::RustDuration::from_millis(30)); +// assert_eq!( +// cage2.send_syscall(socketpair.sock2, str2cbuf("Socketpair Test"), 15, 0), +// 15 +// ); +// }); + +// assert_eq!( +// cage.send_syscall(socketpair.sock1, str2cbuf("test"), 4, 0), +// 4 +// ); + +// let mut buf2 = sizecbuf(15); +// cage.recv_syscall(socketpair.sock1, buf2.as_mut_ptr(), 15, 0); +// assert_eq!(cbuf2str(&buf2), "Socketpair Test"); + +// thread.join().unwrap(); + +// assert_eq!(cage.close_syscall(socketpair.sock1), 0); +// assert_eq!(cage.close_syscall(socketpair.sock2), 0); + +// assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); +// lindrustfinalize(); +// } +// } diff --git a/src/tests/mod.rs b/src/tests/mod.rs new file mode 100644 index 00000000..534d6f45 --- /dev/null +++ b/src/tests/mod.rs @@ -0,0 +1,98 @@ +#![allow(dead_code)] //suppress warning for these functions not being used in targets other than the tests + +mod fs_tests; +// mod ipc_tests; +// mod networking_tests; + +use crate::interface; +// use crate::safeposix::{cage::*, filesystem::*}; +use crate::safeposix::cage::*; + +#[cfg(test)] +mod main_tests { + use crate::tests::fs_tests::fs_tests::test_fs; + // use crate::tests::ipc_tests::ipc_tests::test_ipc; + // use crate::tests::networking_tests::net_tests::net_tests; + + use crate::interface; + // use crate::safeposix::{cage::*, dispatcher::*, filesystem::*}; + use crate::safeposix::{cage::*, dispatcher::*}; + use std::process::Command; + use std::io::Write; + use std::io; + + #[test] + pub fn tests() { + println!("TEST begin"); + io::stdout().flush().unwrap(); + + interface::RUSTPOSIX_TESTSUITE.store(true, interface::RustAtomicOrdering::Relaxed); + + // lindrustinit(0); + // { + // let cage = interface::cagetable_getref(1); + // crate::lib_fs_utils::lind_deltree(&cage, "/"); + // assert_eq!(cage.mkdir_syscall("/dev", S_IRWXA), 0); + // assert_eq!( + // cage.mknod_syscall( + // "/dev/null", + // S_IFCHR as u32 | 0o777, + // makedev(&DevNo { major: 1, minor: 3 }) + // ), + // 0 + // ); + // assert_eq!( + // cage.mknod_syscall( + // "/dev/zero", + // S_IFCHR as u32 | 0o777, + // makedev(&DevNo { major: 1, minor: 5 }) + // ), + // 0 + // ); + // assert_eq!( + // cage.mknod_syscall( + // "/dev/urandom", + // S_IFCHR as u32 | 0o777, + // makedev(&DevNo { major: 1, minor: 9 }) + // ), + // 0 + // ); + // assert_eq!( + // cage.mknod_syscall( + // "/dev/random", + // S_IFCHR as u32 | 0o777, + // makedev(&DevNo { major: 1, minor: 8 }) + // ), + // 0 + // ); + // assert_eq!(cage.exit_syscall(EXIT_SUCCESS), EXIT_SUCCESS); + // } + // lindrustfinalize(); + + println!("FS TESTS"); + test_fs(); + + // println!("NET TESTS"); + // net_tests(); + + // println!("IPC TESTS"); + // test_ipc(); + } +} + +pub fn str2cbuf(ruststr: &str) -> *mut u8 { + let cbuflenexpected = ruststr.len(); + let (ptr, len, _) = ruststr.to_string().into_raw_parts(); + assert_eq!(len, cbuflenexpected); + return ptr; +} + +pub fn sizecbuf<'a>(size: usize) -> Box<[u8]> { + let v = vec![0u8; size]; + v.into_boxed_slice() + //buf.as_mut_ptr() as *mut u8 +} + +pub fn cbuf2str(buf: &[u8]) -> &str { + std::str::from_utf8(buf).unwrap() +} diff --git a/src/tests/networking_tests.rs b/src/tests/networking_tests.rs new file mode 100644 index 00000000..c8e64a60 --- /dev/null +++ b/src/tests/networking_tests.rs @@ -0,0 +1,355 @@ +#[cfg(test)] +pub mod net_tests { + use super::super::*; + use crate::interface; + use crate::safeposix::{cage::*, dispatcher::*, filesystem}; + use std::mem::size_of; + use std::sync::{Arc, Barrier}; + + use std::io::Write; + use std::io; + use std::ptr; + use libc::*; + use std::ffi::CString; + use std::ffi::CStr; + + use std::net::SocketAddrV4; + use std::net::Ipv4Addr; + + use crate::example_grates::fdtable::*; + + use libc::*; + + pub fn net_tests() { + ut_lind_net_bind(); + ut_lind_net_socketpair(); + ut_lind_net_connect(); + ut_lind_net_socket(); + ut_lind_net_epoll(); + } + + pub fn ut_lind_net_bind() { + lindrustinit(0); + let cage = interface::cagetable_getref(1); + let sockfd = cage.socket_syscall(libc::AF_INET, libc::SOCK_STREAM, 0); + + let mut addr: sockaddr_in = unsafe { std::mem::zeroed() }; + addr.sin_family = libc::AF_INET as u16; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = 8080_u16.to_be();//8080 + + //first bind should work... but second bind should not + assert_eq!(cage.bind_syscall(sockfd, &addr as *const _ as *const _, std::mem::size_of::() as u32), 0); + + //trying to bind another to the same IP/PORT + let sockfd2 = cage.socket_syscall(libc::AF_INET, libc::SOCK_STREAM, 0); + assert_eq!( + cage.bind_syscall(sockfd2, &addr as *const _ as *const _, std::mem::size_of::() as u32), + -1 + ); //already bound so should fail + + //UDP should still work... + let sockfd3 = cage.socket_syscall(libc::AF_INET, libc::SOCK_DGRAM, 0); + assert_eq!(cage.bind_syscall(sockfd3, &addr as *const _ as *const _, std::mem::size_of::() as u32), 0); + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + + pub fn ut_lind_net_socket() { + lindrustinit(0); + let cage = interface::cagetable_getref(1); + + let mut sockfd = cage.socket_syscall(libc::AF_INET, libc::SOCK_STREAM, 0); + let sockfd2 = cage.socket_syscall(libc::AF_INET, libc::SOCK_STREAM, libc::IPPROTO_TCP); + + let sockfd3 = cage.socket_syscall(libc::AF_INET, libc::SOCK_DGRAM, 0); + let sockfd4 = cage.socket_syscall(libc::AF_INET, libc::SOCK_DGRAM, libc::IPPROTO_UDP); + + //checking that the fd's are correct + assert!(sockfd >= 0); + assert!(sockfd2 >= 0); + assert!(sockfd3 >= 0); + assert!(sockfd4 >= 0); + + //let's check an illegal operation... + let sockfddomain = cage.socket_syscall(libc::AF_UNIX, libc::SOCK_DGRAM, 0); + assert!(sockfddomain > 0); + + sockfd = cage.socket_syscall(libc::AF_INET, libc::SOCK_STREAM, 0); + assert!(sockfd > 0); + + assert_eq!(cage.close_syscall(sockfd), 0); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + pub fn ut_lind_net_connect() { + lindrustinit(0); + let cage = interface::cagetable_getref(1); + + let server_fd = cage.socket_syscall(libc::AF_INET, libc::SOCK_STREAM, 0); + assert!(server_fd >= 0); + let mut server_addr: libc::sockaddr_in = unsafe { + std::mem::zeroed() + }; + server_addr.sin_family = libc::AF_INET as u16; + server_addr.sin_addr.s_addr = libc::INADDR_ANY; + server_addr.sin_port = 7878_u16.to_be(); + + let bind_result = cage.bind_syscall( + server_fd, + &server_addr as *const _ as *const _, + std::mem::size_of::() as u32, + ); + + if bind_result < 0 { + let err = unsafe { + libc::__errno_location() + }; + let err_str = unsafe { + libc::strerror(*err) + }; + let err_msg = unsafe { + CStr::from_ptr(err_str).to_string_lossy().into_owned() + }; + println!("errno: {:?}", err); + println!("Error message: {:?}", err_msg); + io::stdout().flush().unwrap(); + } + + let listen_result = cage.listen_syscall(server_fd, 128); + if listen_result < 0 { + panic!("listen_result"); + } + + cage.fork_syscall(2); + + let thread = interface::helper_thread(move || { + // Client + let cage2 = interface::cagetable_getref(2); + let clientfd = cage2.socket_syscall(libc::AF_INET, libc::SOCK_STREAM, 0); + if clientfd < 0 { + panic!("Failed to create socket"); + } + + let connect_result = cage2.connect_syscall( + clientfd, + &server_addr as *const libc::sockaddr_in as *const libc::sockaddr, + std::mem::size_of::() as u32, + ); + if connect_result < 0 { + panic!("Failed to connect to server"); + } + let message = CString::new("Hello from client").unwrap(); + let sendret = cage2.send_syscall(clientfd, message.as_ptr() as *const u8, message.to_bytes().len(), 0); + assert_ne!(sendret, 0); + cage2.close_syscall(clientfd); + assert_eq!(cage2.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + }); + + let mut client_addr: libc::sockaddr_in = unsafe { + std::mem::zeroed() + }; + let addr_len = std::mem::size_of::() as u32; + let client_fd = cage.accept_syscall( + server_fd, + &mut client_addr as *mut libc::sockaddr_in as *mut libc::sockaddr, + addr_len, + ); + if client_fd < 0 { + panic!("client_fd"); + } + + let mut buffer = [0u8; 1024]; + let len = cage.recv_syscall(client_fd, buffer.as_mut_ptr() as *mut u8, buffer.len(), 0); + if len == 0 { + panic!("Fail on child recv"); + } + cage.close_syscall(client_fd); + cage.close_syscall(server_fd); + thread.join().unwrap(); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + pub fn ut_lind_net_socketpair() { + lindrustinit(0); + let cage = interface::cagetable_getref(1); + let mut socketpair = interface::SockPair::default(); + cage.socketpair_syscall(libc::AF_UNIX, libc::SOCK_STREAM, 0, &mut socketpair); + // assert_eq!( + // Cage::socketpair_syscall(&cage.clone(), libc::AF_UNIX, libc::SOCK_STREAM, 0, &mut socketpair), + // 0 + // ); + let cage2 = cage.clone(); + + let thread = interface::helper_thread(move || { + let mut buf = sizecbuf(10); + loop { + let result = cage2.recv_syscall(socketpair.sock2, buf.as_mut_ptr(), 10, 0); + if result != -libc::EINTR { + break; // if the error was EINTR, retry the syscall + } + } + assert_eq!(cbuf2str(&buf), "test\0\0\0\0\0\0"); + + interface::sleep(interface::RustDuration::from_millis(30)); + assert_eq!( + cage2.send_syscall(socketpair.sock2, str2cbuf("Socketpair Test"), 15, 0), + 15 + ); + }); + + assert_eq!( + cage.send_syscall(socketpair.sock1, str2cbuf("test"), 4, 0), + 4 + ); + + let mut buf2 = sizecbuf(15); + loop { + let result = cage.recv_syscall(socketpair.sock1, buf2.as_mut_ptr(), 15, 0); + if result != -libc::EINTR { + break; // if the error was EINTR, retry the syscall + } + } + let str2 = cbuf2str(&buf2); + assert_eq!(str2, "Socketpair Test"); + + thread.join().unwrap(); + + assert_eq!(cage.close_syscall(socketpair.sock1), 0); + assert_eq!(cage.close_syscall(socketpair.sock2), 0); + + // end of the socket pair test (note we are only supporting AF_UNIX and TCP) + + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } + + + pub fn ut_lind_net_epoll() { + lindrustinit(0); + let cage = interface::cagetable_getref(1); + + let epoll_fd = cage.epoll_create_syscall(1); + assert_ne!(epoll_fd, -1); + + println!("epoll_fd: {:?}", epoll_fd); + io::stdout().flush().unwrap(); + + let mut pipefds = PipeArray { + readfd: 0, + writefd: 0, + }; + assert_eq!(cage.pipe_syscall(&mut pipefds), 0); + + println!("pipe finished"); + io::stdout().flush().unwrap(); + + assert_eq!(cage.fork_syscall(2), 0); + let sender = std::thread::spawn(move || { + let cage2 = interface::cagetable_getref(2); + + println!("child start"); + io::stdout().flush().unwrap(); + + if cage2.close_syscall(pipefds.readfd) < 0 { + let err = unsafe { + libc::__errno_location() + }; + let err_str = unsafe { + libc::strerror(*err) + }; + let err_msg = unsafe { + CStr::from_ptr(err_str).to_string_lossy().into_owned() + }; + println!("errno: {:?}", err); + println!("Error message: {:?}", err_msg); + println!("pipefds.readfd: {:?}", pipefds.readfd); + io::stdout().flush().unwrap(); + panic!(); + } + + let message = b"Hello from child"; + + println!("child - before write"); + io::stdout().flush().unwrap(); + + if cage2.write_syscall(pipefds.writefd, message.as_ptr(), message.len()) < 0 { + let err = unsafe { + libc::__errno_location() + }; + let err_str = unsafe { + libc::strerror(*err) + }; + let err_msg = unsafe { + CStr::from_ptr(err_str).to_string_lossy().into_owned() + }; + println!("errno: {:?}", err); + println!("Error message: {:?}", err_msg); + println!("pipefds.writefd: {:?}", pipefds.writefd); + io::stdout().flush().unwrap(); + panic!(); + } + println!("child - after write"); + io::stdout().flush().unwrap(); + + assert_eq!(cage2.close_syscall(pipefds.writefd), 0); + + println!("child - after close"); + io::stdout().flush().unwrap(); + + assert_eq!(cage2.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + }); + + println!("parent - before close"); + io::stdout().flush().unwrap(); + + assert_eq!(cage.close_syscall(pipefds.writefd), 0); + let mut epollevent = EpollEvent { + events: libc::EPOLLIN as u32, + fd: pipefds.readfd, + }; + + if cage.epoll_ctl_syscall(epoll_fd, libc::EPOLL_CTL_ADD, pipefds.readfd, &mut epollevent) < 0 { + let err = unsafe { + libc::__errno_location() + }; + let err_str = unsafe { + libc::strerror(*err) + }; + let err_msg = unsafe { + CStr::from_ptr(err_str).to_string_lossy().into_owned() + }; + let kernelfd = translate_virtual_fd(1, pipefds.readfd).unwrap(); + println!("kernel fd: {:?}", kernelfd); + println!("Error message: {:?}", err_msg); + println!("pipefds.readfd: {:?}", pipefds.readfd); + io::stdout().flush().unwrap(); + panic!(); + } + + + let epoll_events = [EpollEvent{ + events: 0, + fd: 0, + }; 10]; + assert_ne!(cage.epoll_wait_syscall(epoll_fd, &epoll_events, epoll_events.len() as i32, -1), -1); + let mut buffer = [0; 128]; + assert_ne!(cage.read_syscall(pipefds.readfd, buffer.as_mut_ptr(), buffer.len()), -1); + + println!("parent - after read"); + io::stdout().flush().unwrap(); + + assert_ne!(cage.close_syscall(pipefds.readfd), -1); + assert_ne!(cage.close_syscall(epoll_fd), -1); + println!("parent - after close"); + io::stdout().flush().unwrap(); + + sender.join().unwrap(); + assert_eq!(cage.exit_syscall(libc::EXIT_SUCCESS), libc::EXIT_SUCCESS); + lindrustfinalize(); + } +} diff --git a/src/tools/fs_utils.rs b/src/tools/fs_utils.rs new file mode 100644 index 00000000..8cdc9548 --- /dev/null +++ b/src/tools/fs_utils.rs @@ -0,0 +1,250 @@ +// #![feature(lazy_cell)] +// #![feature(rustc_private)] //for private crate imports for tests +// #![feature(vec_into_raw_parts)] +// #![feature(duration_constants)] +// #![allow(unused)] + +// /// Author: Jonathan Singer +// /// +// /// This file provides a command line interface for interacting in certain ways with the lind file +// /// system from the host, such as copying files from the host into lind, removing files and +// /// directories, and listing files in the lind fs, and more +// /// +// /// This interface should be sufficient for anything we'd need to do between lind and the host +// use std::env; +// use std::iter::repeat; + +// mod interface; +// mod lib_fs_utils; +// mod safeposix; +// use lib_fs_utils::*; +// use safeposix::{ +// cage::*, +// dispatcher::{lindrustfinalize, lindrustinit}, +// filesystem::*, +// }; + +// fn lind_tree(cage: &Cage, path: &str, indentlevel: usize) { +// let mut lindstat_res: StatData = StatData::default(); +// let stat_us = cage.stat_syscall(path, &mut lindstat_res); +// if stat_us == 0 { +// if !is_dir(lindstat_res.st_mode) { +// eprintln!("Tree must be run on a directory!"); +// return; +// } + +// //visit the children of this directory, and show them all as being children of this directory in the tree +// visit_children( +// cage, +// path, +// Some(indentlevel), +// |childcage, childpath, isdir, childindentlevelopt| { +// let childindentlevel = childindentlevelopt.unwrap(); +// //lines to connect non-parent ancestors to their remaining children(if any) +// print!("{}", "| ".repeat(childindentlevel)); +// //line to connect parent to its child +// print!("{}", "|---"); +// //actually print out file name +// println!("{}", childpath); + +// //recursive call for child +// if isdir { +// lind_tree(childcage, childpath, childindentlevel + 1); +// } +// }, +// ); +// } else { +// eprintln!("No such directory exists!"); +// } +// } + +// fn lind_ls(cage: &Cage, path: &str) { +// let mut lindstat_res: StatData = StatData::default(); +// let stat_us = cage.stat_syscall(path, &mut lindstat_res); + +// if stat_us == 0 { +// if is_dir(lindstat_res.st_mode) { +// //for each child, if it's a directory, print its name with a slash, otherwise omit the slash +// visit_children(cage, path, None, |_childcage, childpath, isdir, _| { +// if isdir { +// print!("{}/ ", childpath); +// } else { +// print!("{} ", childpath); +// } +// }); +// } else { +// print!("{} ", path); +// } +// println!(); +// } else { +// eprintln!("No such file exists!"); +// } +// } + +// fn print_usage() { +// println!( +// " +// Usage: lind_fs_utils [commandname] [arguments...] + +// Where commandname is one of the following: + +// cp [hostsource] [linddest] : Copies files from the host file system into the lind filesystem. +// For example, cp bar/etc/passwd /etc/passwd will copy the +// former file in the host file system to the latter in lind's fs. +// Directories are handled recursively, cp bar/etc /etc/ will make a +// directory at /etc in the lind fs, and then populate it with all +// of the files in the root fs. +// deltree [linddir] : Delete a directory on the lind file system and all it contains +// format : Make a new blank fs, removing the current one +// help : Print this message +// ls [lindpath] : List the contents of a lind file system directory +// mkdir [linddir1...] : Create a lind file system directory (for each arg) +// rm [lindfile1...] : Delete a file on the lind file system +// rmdir [linddir1...] : Delete a directory on the lind file system +// tree [startlindpath] : Print the lindfs file tree starting at the specified directory +// Assumes root directory if no starting path is specified. +// update [hostsource] [linddest] : Copies files from the host file system into the lind filesystem. +// Will not copy files if the host and lind files are identical. +// For example, update bar/etc/passwd /etc/passwd will copy the +// former file in the host file system to the latter in lind's fs if +// the latter does not exist or is not identical to the former. +// Directories are handled recursively, cp bar/etc /etc/ will make a +// directory at /etc in the lind fs, and then populate it with all +// of the files in the root fs, with identical files being skipped. +// " +// ); +// } + +// fn main() { +// lindrustinit(0); // no verbosity +// let mut args = env::args(); +// let utilcage = Cage { +// cageid: 0, +// cwd: interface::RustLock::new(interface::RustRfc::new(interface::RustPathBuf::from("/"))), +// parent: 0, +// filedescriptortable: init_fdtable(), +// cancelstatus: interface::RustAtomicBool::new(false), +// getgid: interface::RustAtomicI32::new(-1), +// getuid: interface::RustAtomicI32::new(-1), +// getegid: interface::RustAtomicI32::new(-1), +// geteuid: interface::RustAtomicI32::new(-1), +// rev_shm: interface::Mutex::new(vec![]), +// mutex_table: interface::RustLock::new(vec![]), +// cv_table: interface::RustLock::new(vec![]), +// sem_table: interface::RustHashMap::new(), +// thread_table: interface::RustHashMap::new(), +// signalhandler: interface::RustHashMap::new(), +// sigset: interface::RustHashMap::new(), +// pendingsigset: interface::RustHashMap::new(), +// main_threadid: interface::RustAtomicU64::new(0), +// interval_timer: interface::IntervalTimer::new(0), +// }; + +// args.next(); //first arg is executable, we don't care +// let command = if let Some(cmd) = args.next() { +// cmd +// } else { +// print_usage(); +// return; //print usage +// }; + +// match command.as_str() { +// "help" | "usage" => { +// print_usage(); +// } + +// "cp" => { +// let source = args.next().expect("cp needs 2 arguments"); +// let dest = args.next().expect("cp needs 2 arguments"); +// args.next() +// .and_then:: Option>(|_| { +// panic!("cp cannot take more than 2 arguments") +// }); +// cp_dir_into_lind( +// &utilcage, +// interface::RustPath::new(&source), +// dest.as_str(), +// true, +// ); +// } + +// "update" => { +// let source = args.next().expect("update needs 2 arguments"); +// let dest = args.next().expect("update needs 2 arguments"); +// args.next() +// .and_then:: Option>(|_| { +// panic!("update cannot take more than 2 arguments") +// }); +// update_dir_into_lind(&utilcage, interface::RustPath::new(&source), dest.as_str()); +// } + +// "ls" => { +// let file = args.next().expect("ls needs 1 argument"); +// args.next() +// .and_then:: Option>(|_| { +// panic!("ls cannot take more than 1 argument") +// }); +// lind_ls(&utilcage, file.as_str()); +// } + +// "tree" => { +// let rootdir = if let Some(dirstr) = args.next() { +// dirstr +// } else { +// "/".to_owned() +// }; +// println!("{}", rootdir); +// lind_tree(&utilcage, rootdir.as_str(), 0); +// } + +// "format" => { +// lind_deltree(&utilcage, "/"); //This doesn't actually fully remove all of the linddata files... TODO: debug + +// let mut logobj = LOGMAP.write(); +// let log = logobj.take().unwrap(); +// let _close = log.close().unwrap(); +// drop(logobj); +// let _logremove = interface::removefile(LOGFILENAME.to_string()); + +// format_fs(); +// return; +// } + +// "deltree" => { +// let rootdir = args.next().expect("deltree needs 1 argument"); +// args.next() +// .and_then:: Option>(|_| { +// panic!("deltree cannot take more than 1 argument") +// }); +// lind_deltree(&utilcage, rootdir.as_str()); +// } + +// "rm" => { +// for file in args { +// utilcage.unlink_syscall(file.as_str()); +// } +// } + +// "mkdir" => { +// for dir in args { +// utilcage.mkdir_syscall(dir.as_str(), S_IRWXA); +// } +// } + +// "rmdir" => { +// for dir in args { +// utilcage.chmod_syscall(dir.as_str(), S_IRWXA); +// utilcage.rmdir_syscall(dir.as_str()); +// } +// } + +// _ => { +// eprintln!("Error, command unknown"); +// return; +// } +// } +// lindrustfinalize(); +// } +fn main() { + +} \ No newline at end of file diff --git a/src/tools/interface b/src/tools/interface new file mode 120000 index 00000000..340b894b --- /dev/null +++ b/src/tools/interface @@ -0,0 +1 @@ +../interface/ \ No newline at end of file diff --git a/src/tools/lib_fs_utils.rs b/src/tools/lib_fs_utils.rs new file mode 100644 index 00000000..4ffbb538 --- /dev/null +++ b/src/tools/lib_fs_utils.rs @@ -0,0 +1,324 @@ +// #![allow(dead_code)] //suppress warning for these functions not being used in main library target + +// use std::ffi::CStr; +// use std::fs::File; +// use std::io::{prelude, Read}; +// use std::os::raw::c_char; + +// use crate::interface; +// use crate::interface::errnos::{syscall_error, Errno}; +// use crate::interface::types::{ClippedDirent, CLIPPED_DIRENT_SIZE}; +// use crate::safeposix::{cage::*, filesystem::*}; + +// const LINUX_MAX_RW_COUNT: usize = 0x7FFFF000; + +// //we currently handle symlinks as normal files + +// pub fn update_dir_into_lind(cage: &Cage, hostfilepath: &interface::RustPath, lindfilepath: &str) { +// if hostfilepath.exists() { +// if let Ok(_) = hostfilepath.read_link() { +// println!("following symlink at {:?} on host fs", hostfilepath); +// } //if read_link succeeds it's a symlink, whose destination must exist because of the nature of the .exists function +// } else { +// eprintln!("Cannot locate file on host fs: {:?}", hostfilepath); +// return; +// } + +// //update directly if not a directory on the host, otherwise recursively handle children +// if hostfilepath.is_file() { +// update_into_lind(cage, hostfilepath, lindfilepath); +// } else { +// let children = hostfilepath.read_dir().unwrap(); +// for wrappedchild in children { +// let child = wrappedchild.unwrap(); +// let newlindpath = if lindfilepath.ends_with("/") { +// format!("{}{}", lindfilepath, child.file_name().to_str().unwrap()) +// } else { +// format!("{}/{}", lindfilepath, child.file_name().to_str().unwrap()) +// }; +// update_dir_into_lind(cage, child.path().as_path(), newlindpath.as_str()); +// } +// } +// } + +// fn update_into_lind(cage: &Cage, hostfilepath: &interface::RustPath, lindfilepath: &str) { +// if !hostfilepath.exists() || !hostfilepath.is_file() { +// println!( +// "{:?} does not exist or is not a regular file, skipping", +// hostfilepath +// ); +// return; +// } +// let fmetadata = hostfilepath.metadata().unwrap(); + +// let host_size = fmetadata.len(); +// let mut lindstat_res: StatData = StatData::default(); +// let stat_us = cage.stat_syscall(lindfilepath, &mut lindstat_res); + +// let lind_exists; +// let lind_isfile; +// let lind_size; +// if stat_us < 0 { +// lind_exists = false; +// lind_isfile = false; +// lind_size = 0; +// } else { +// lind_exists = true; +// lind_isfile = is_reg(lindstat_res.st_mode); +// lind_size = lindstat_res.st_size; +// } + +// if lind_exists && !lind_isfile { +// println!( +// "{:?} on lind file system is not a regular file, skipping", +// hostfilepath +// ); +// return; +// } + +// //compare files to tell whether they are identical +// let samefile = if host_size as usize == lind_size { +// let mut hostslice = vec![0u8; lind_size]; +// let mut lindslice = vec![0u8; lind_size]; +// let mut hostfile = File::open(hostfilepath).unwrap(); +// hostfile.read(hostslice.as_mut_slice()).unwrap(); +// let lindfd = cage.open_syscall(lindfilepath, O_RDONLY | O_CREAT, S_IRWXA); +// cage.read_syscall(lindfd, lindslice.as_mut_ptr(), lind_size); +// cage.close_syscall(lindfd); +// hostslice == lindslice +// } else { +// false +// }; + +// //if they are not the same file, remove the lind file and replace it with the host file +// if !samefile { +// if lind_exists { +// cage.unlink_syscall(lindfilepath); +// println!("removing {} on lind file system", lindfilepath); +// } +// cp_into_lind(cage, hostfilepath, lindfilepath, true); +// } else { +// println!( +// "Same files on host and lind--{:?} and {}, skipping", +// hostfilepath, lindfilepath +// ); +// } +// } + +// pub fn cp_dir_into_lind( +// cage: &Cage, +// hostfilepath: &interface::RustPath, +// lindfilepath: &str, +// create_missing_dirs: bool, +// ) { +// if hostfilepath.exists() { +// if let Ok(_) = hostfilepath.read_link() { +// println!("following symlink at {:?} on host fs", hostfilepath); +// } //if read_link succeeds it's a symlink, whose destination must exist because of the nature of the .exists function +// } else { +// eprintln!("Cannot locate file on host fs: {:?}", hostfilepath); +// return; +// } + +// //update directly if not a directory on the host, otherwise recursively handle children +// if hostfilepath.is_file() { +// cp_into_lind(cage, hostfilepath, lindfilepath, create_missing_dirs); +// } else if hostfilepath.is_dir() { +// let children = hostfilepath.read_dir().unwrap(); +// for wrappedchild in children { +// let child = wrappedchild.unwrap(); +// let newlindpath = if lindfilepath.ends_with("/") { +// format!("{}{}", lindfilepath, child.file_name().to_str().unwrap()) +// } else { +// format!("{}/{}", lindfilepath, child.file_name().to_str().unwrap()) +// }; +// cp_dir_into_lind( +// cage, +// child.path().as_path(), +// newlindpath.as_str(), +// create_missing_dirs, +// ); +// } +// } +// } + +// fn cp_into_lind( +// cage: &Cage, +// hostfilepath: &interface::RustPath, +// lindfilepath: &str, +// create_missing_dirs: bool, +// ) { +// if !hostfilepath.exists() { +// eprintln!("Cannot locate file on host fs: {:?}", hostfilepath); +// return; +// } +// if !hostfilepath.is_file() { +// eprintln!("File is not a regular file on host fs: {:?}", hostfilepath); +// return; +// } + +// let lindtruepath = normpath(convpath(lindfilepath), cage); + +// //if a directory in the lindfilepath does not exist in the lind file system, create it! +// let mut ancestor = interface::RustPathBuf::from("/"); +// for component in lindtruepath.parent().unwrap().components() { +// ancestor.push(component); +// let mut lindstat_res: StatData = StatData::default(); + +// //check whether file exists +// let stat_us = cage.stat_syscall(ancestor.to_str().unwrap(), &mut lindstat_res); +// if stat_us == 0 { +// if !is_dir(lindstat_res.st_mode) { +// eprintln!("Fatal error in trying to create child of non-directory file"); +// return; +// } +// continue; +// } +// if stat_us != -(Errno::ENOENT as i32) { +// eprintln!("Fatal error in trying to get lind file path"); +// return; +// } + +// //check whether we are supposed to create missing directories, and whether we'd be +// //clobbering anything to do so (if so error out) +// if create_missing_dirs { +// if cage.mkdir_syscall(ancestor.to_str().unwrap(), S_IRWXA) != 0 { +// //let's not mirror stat data +// eprintln!("Lind fs path does not exist but should not be created (is rooted at non-directory) {:?}", ancestor); +// return; +// } +// } else { +// eprintln!( +// "Lind fs path does not exist but should not be created {:?}", +// ancestor +// ); +// return; +// } +// } + +// //copy file contents into lind file system +// let mut host_fileobj = File::open(hostfilepath).unwrap(); +// let mut filecontents: Vec = Vec::new(); +// host_fileobj.read_to_end(&mut filecontents).unwrap(); + +// let lindfd = cage.open_syscall( +// lindtruepath.to_str().unwrap(), +// O_CREAT | O_TRUNC | O_WRONLY, +// S_IRWXA, +// ); +// assert!(lindfd >= 0); + +// let veclen = filecontents.len(); +// let mut writtenlen: usize = 0; + +// //on Linux, write() (and similar system calls) will transfer at most 0x7ffff000 (2,147,479,552) bytes +// //dividing filecontents into chunks of 0x7ffff000 (2,147,479,552) bytes and writing each chunk +// for chunk in filecontents.chunks(LINUX_MAX_RW_COUNT) { +// writtenlen += cage.write_syscall(lindfd, chunk.as_ptr(), chunk.len()) as usize; +// } +// //confirm that write succeeded +// assert_eq!(veclen, writtenlen); + +// //get diagnostic data to print +// let mut lindstat_res: StatData = StatData::default(); +// let _stat_us = cage.fstat_syscall(lindfd, &mut lindstat_res); +// let inode = lindstat_res.st_ino; + +// assert_eq!(cage.close_syscall(lindfd), 0); + +// println!("Copied {:?} as {} ({})", hostfilepath, lindfilepath, inode); +// } + +// pub fn visit_children( +// cage: &Cage, +// path: &str, +// arg: Option, +// visitor: fn(&Cage, &str, bool, Option), +// ) { +// //get buffer in which getdents will write its stuff +// let mut bigbuffer = [0u8; 65536]; +// let dentptr = bigbuffer.as_mut_ptr(); + +// let dirfd = cage.open_syscall(path, O_RDONLY, 0); +// assert!(dirfd >= 0); + +// loop { +// let direntres = cage.getdents_syscall(dirfd, dentptr, 65536); + +// //if we've read every entry in this directory, we're done +// if direntres == 0 { +// break; +// } + +// let mut dentptrindex = 0isize; + +// //while there are still more entries to read +// while dentptrindex < direntres as isize { +// //get information for where the next entry is (if relevant) +// let clipped_dirent_ptr = dentptr.wrapping_offset(dentptrindex) as *mut ClippedDirent; +// let clipped_dirent = unsafe { &*clipped_dirent_ptr }; + +// //get the file name for the child +// let cstrptr = dentptr.wrapping_offset(dentptrindex + CLIPPED_DIRENT_SIZE as isize); +// let filenamecstr = unsafe { CStr::from_ptr(cstrptr as *const c_char) }; +// let filenamestr = filenamecstr.to_str().unwrap(); + +// dentptrindex += clipped_dirent.d_reclen as isize; + +// //ignore these entries +// if filenamestr == "." || filenamestr == ".." { +// continue; +// } + +// let fullstatpath = if path.ends_with("/") { +// [path, filenamestr].join("") +// } else { +// [path, "/", filenamestr].join("") +// }; + +// //stat to tell whether it's a directory +// let mut lindstat_res: StatData = StatData::default(); +// let _stat_us = cage.stat_syscall(fullstatpath.as_str(), &mut lindstat_res); + +// //call the visitor function on the child path +// visitor( +// cage, +// fullstatpath.as_str(), +// is_dir(lindstat_res.st_mode), +// arg, +// ); +// } +// } +// cage.close_syscall(dirfd); +// } + +// pub fn lind_deltree(cage: &Cage, path: &str) { +// let mut lindstat_res: StatData = StatData::default(); +// let stat_us = cage.stat_syscall(path, &mut lindstat_res); + +// if stat_us == 0 { +// if !is_dir(lindstat_res.st_mode) { +// cage.unlink_syscall(path); +// return; +// } else { +// //remove all children recursively +// visit_children(cage, path, None, |childcage, childpath, isdir, _| { +// if isdir { +// lind_deltree(childcage, childpath); +// } else { +// childcage.unlink_syscall(childpath); +// } +// }); + +// //remove specified directory now that it is empty +// cage.chmod_syscall(path, S_IRWXA); +// cage.rmdir_syscall(path); +// } +// } else { +// eprintln!("No such directory exists!"); +// } +// } + +fn main() { + +} diff --git a/src/tools/safeposix b/src/tools/safeposix new file mode 120000 index 00000000..3b39e91d --- /dev/null +++ b/src/tools/safeposix @@ -0,0 +1 @@ +../safeposix/ \ No newline at end of file