diff --git a/Cargo.lock b/Cargo.lock index 55ac709b980cff5af21a853958b6703c2e45c34b..222e6749cacc31b2ed2f1c26df8e806fa4b6530d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,16 +33,16 @@ checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "cbindgen" -version = "0.24.5" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b922faaf31122819ec80c4047cc684c6979a087366c069611e33649bf98e18d" +checksum = "da6bc11b07529f16944307272d5bd9b22530bc7d05751717c9d416586cedab49" dependencies = [ "clap", "heck", "indexmap", "log", - "proc-macro2 1.0.69", - "quote 1.0.33", + "proc-macro2", + "quote", "serde", "serde_json", "syn 1.0.109", @@ -52,9 +52,9 @@ dependencies = [ [[package]] name = "cbitset" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9afa72f63942dd7e7f01c67b863ce9df35c523ae10e3dddd3eec8f1e07eac" +checksum = "29b6ad25ae296159fb0da12b970b2fe179b234584d7cd294c891e2bbb284466b" dependencies = [ "num-traits", ] @@ -102,7 +102,7 @@ dependencies = [ name = "core_io" version = "0.1.20181107" dependencies = [ - "rustc_version 0.1.7", + "rustc_version", ] [[package]] @@ -135,9 +135,9 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "goblin" -version = "0.0.21" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a4013e9182f2345c6b7829b9ef6e670bce0dfca12c6f974457ed2160c2c7fe9" +checksum = "f27c1b4369c2cd341b5de549380158b105a04c331be5db9110eef7b6d2742134" dependencies = [ "log", "plain", @@ -181,15 +181,6 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -dependencies = [ - "spin 0.5.2", -] - [[package]] name = "ld_so" version = "0.1.0" @@ -206,16 +197,6 @@ version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" -[[package]] -name = "lock_api" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.20" @@ -230,9 +211,9 @@ checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memoffset" -version = "0.5.6" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] @@ -263,13 +244,10 @@ name = "posix-regex" version = "0.1.0" [[package]] -name = "proc-macro2" -version = "0.4.30" +name = "ppv-lite86" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -dependencies = [ - "unicode-xid", -] +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" @@ -280,22 +258,13 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "quote" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" -dependencies = [ - "proc-macro2 0.4.30", -] - [[package]] name = "quote" version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ - "proc-macro2 1.0.69", + "proc-macro2", ] [[package]] @@ -316,27 +285,59 @@ dependencies = [ [[package]] name = "rand" -version = "0.5.6" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "rand_core 0.3.1", + "rand_chacha", + "rand_core", + "rand_hc", ] [[package]] -name = "rand_core" -version = "0.3.1" +name = "rand_chacha" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" dependencies = [ - "rand_core 0.4.2", + "ppv-lite86", + "rand_core", ] [[package]] name = "rand_core" -version = "0.4.2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" + +[[package]] +name = "rand_hc" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_jitter" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a404fd88e0f817fc1c3351b9ba2207ffa65038cdde464405308a5f5d254835fe" +dependencies = [ + "libc", + "rand_core", + "winapi", +] + +[[package]] +name = "rand_xorshift" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8" +dependencies = [ + "rand_core", +] [[package]] name = "redox-exec" @@ -353,15 +354,6 @@ version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -374,13 +366,12 @@ dependencies = [ name = "relibc" version = "0.2.5" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.1", "cbindgen", "cbitset", "cc", "core_io", "goblin", - "lazy_static", "libc", "memchr", "memoffset", @@ -388,10 +379,11 @@ dependencies = [ "posix-regex", "ralloc", "rand", + "rand_jitter", + "rand_xorshift", "redox-exec", "redox_syscall 0.4.1", "sc", - "spin 0.9.8", "unicode-width", ] @@ -401,23 +393,14 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" dependencies = [ - "semver 0.1.20", -] - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", + "semver", ] [[package]] name = "rustix" -version = "0.38.20" +version = "0.38.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ce50cb2e16c2903e30d1cbccfd8387a74b9d4c938b6a4c5ec6cc7556f7a8a0" +checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" dependencies = [ "bitflags 2.4.1", "errno", @@ -438,31 +421,24 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "010e18bd3bfd1d45a7e666b236c78720df0d9a7698ebaa9c1c559961eb60a38b" -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "scroll" -version = "0.9.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f84d114ef17fd144153d608fba7c446b0145d038985e7a8cc5d08bb0ce20383" +checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" dependencies = [ - "rustc_version 0.2.3", "scroll_derive", ] [[package]] name = "scroll_derive" -version = "0.9.5" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1aa96c45e7f5a91cb7fabe7b279f02fea7126239fc40b732316e8b6a2d0fcb" +checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "syn 0.15.44", + "proc-macro2", + "quote", + "syn 2.0.38", ] [[package]] @@ -471,21 +447,6 @@ version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac" -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "serde" version = "1.0.190" @@ -501,8 +462,8 @@ version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", + "proc-macro2", + "quote", "syn 2.0.38", ] @@ -517,46 +478,20 @@ dependencies = [ "serde", ] -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "syn" -version = "0.15.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" -dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", - "unicode-xid", -] - [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", + "proc-macro2", + "quote", "unicode-ident", ] @@ -566,20 +501,20 @@ version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ - "proc-macro2 1.0.69", - "quote 1.0.33", + "proc-macro2", + "quote", "unicode-ident", ] [[package]] name = "tempfile" -version = "3.8.0" +version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.3.5", + "redox_syscall 0.4.1", "rustix", "windows-sys", ] @@ -626,12 +561,6 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" - [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index b2a3040a34194257a3244c268e2fe6d36d57d3dc..aa8cf18f3f91afb70250e2ffa8609a0f08c6923a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,25 +13,29 @@ members = ["src/crt0", "src/crti", "src/crtn", "src/ld_so", "src/platform/redox/ exclude = ["core_io", "ralloc", "tests"] [build-dependencies] -cbindgen = "0.24.3" +cbindgen = "0.26" cc = "1.0.25" #env_logger = "0.10" [dependencies] -bitflags = "1" -cbitset = "0.1.0" +bitflags = "2" +cbitset = "0.2" core_io = { path = "core_io", features = ["collections"] } -lazy_static = { version = "1.4.0", default-features = false, features = ["spin_no_std"] } -memoffset = "0.5.1" +memoffset = "0.9" posix-regex = { path = "posix-regex", features = ["no_std"] } -rand = { version = "0.5.5", default-features = false } + +# TODO: For some reason, rand_jitter hasn't been updated to use the latest rand_core +rand = { version = "0.7", default-features = false } +rand_xorshift = "0.2" +rand_jitter = "0.3" + memchr = { version = "2.2.0", default-features = false } plain = "0.2" unicode-width = "0.1" __libc_only_for_layout_checks = { package = "libc", version = "0.2.149", optional = true } [dependencies.goblin] -version = "0.0.21" +version = "0.7" default-features = false features = ["elf32", "elf64", "endian_fd"] @@ -45,7 +49,6 @@ sc = "0.2.3" [target.'cfg(target_os = "redox")'.dependencies] redox_syscall = "0.4" -spin = "0.9.0" redox-exec = { path = "src/platform/redox/redox-exec" } [features] diff --git a/src/c_str.rs b/src/c_str.rs index 3d28d49d00202c205f179c561d58c9e06d80b236..56f1b084f785477f195ae178b5e72e318ae1a4b3 100644 --- a/src/c_str.rs +++ b/src/c_str.rs @@ -1,1255 +1,89 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or -// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license -// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. +use core::{marker::PhantomData, ptr::NonNull, str::Utf8Error}; use alloc::{ - borrow::{Borrow, Cow, ToOwned}, - boxed::Box, - rc::Rc, + borrow::{Cow, ToOwned}, string::String, - sync::Arc, - vec::Vec, }; -use core::{ - ascii, - cmp::Ordering, - fmt::{self, Write}, - mem, ops, ptr, slice, - str::{self, Utf8Error}, -}; - -use crate::{header::string::strlen, platform::types::*}; - -pub fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> { - use crate::header::string; - - let p = unsafe { - string::memchr( - haystack.as_ptr() as *const c_void, - needle as c_int, - haystack.len(), - ) - }; - if p.is_null() { - None - } else { - Some(p as usize - (haystack.as_ptr() as usize)) - } -} - -/// A type representing an owned, C-compatible, nul-terminated string with no nul bytes in the -/// middle. -/// -/// This type serves the purpose of being able to safely generate a -/// C-compatible string from a Rust byte slice or vector. An instance of this -/// type is a static guarantee that the underlying bytes contain no interior 0 -/// bytes ("nul characters") and that the final byte is 0 ("nul terminator"). -/// -/// `CString` is to [`CStr`] as [`String`] is to [`&str`]: the former -/// in each pair are owned strings; the latter are borrowed -/// references. -/// -/// # Creating a `CString` -/// -/// A `CString` is created from either a byte slice or a byte vector, -/// or anything that implements [`Into`]`<`[`Vec`]`<`[`u8`]`>>` (for -/// example, you can build a `CString` straight out of a [`String`] or -/// a [`&str`], since both implement that trait). -/// -/// The [`new`] method will actually check that the provided `&[u8]` -/// does not have 0 bytes in the middle, and return an error if it -/// finds one. -/// -/// # Extracting a raw pointer to the whole C string -/// -/// `CString` implements a [`as_ptr`] method through the [`Deref`] -/// trait. This method will give you a `*const c_char` which you can -/// feed directly to extern functions that expect a nul-terminated -/// string, like C's `strdup()`. -/// -/// # Extracting a slice of the whole C string -/// -/// Alternatively, you can obtain a `&[`[`u8`]`]` slice from a -/// `CString` with the [`as_bytes`] method. Slices produced in this -/// way do *not* contain the trailing nul terminator. This is useful -/// when you will be calling an extern function that takes a `*const -/// u8` argument which is not necessarily nul-terminated, plus another -/// argument with the length of the string — like C's `strndup()`. -/// You can of course get the slice's length with its -/// [`len`][slice.len] method. -/// -/// If you need a `&[`[`u8`]`]` slice *with* the nul terminator, you -/// can use [`as_bytes_with_nul`] instead. -/// -/// Once you have the kind of slice you need (with or without a nul -/// terminator), you can call the slice's own -/// [`as_ptr`][slice.as_ptr] method to get a raw pointer to pass to -/// extern functions. See the documentation for that function for a -/// discussion on ensuring the lifetime of the raw pointer. -/// -/// [`Into`]: ../convert/trait.Into.html -/// [`Vec`]: ../vec/struct.Vec.html -/// [`String`]: ../string/struct.String.html -/// [`&str`]: ../primitive.str.html -/// [`u8`]: ../primitive.u8.html -/// [`new`]: #method.new -/// [`as_bytes`]: #method.as_bytes -/// [`as_bytes_with_nul`]: #method.as_bytes_with_nul -/// [`as_ptr`]: #method.as_ptr -/// [slice.as_ptr]: ../primitive.slice.html#method.as_ptr -/// [slice.len]: ../primitive.slice.html#method.len -/// [`Deref`]: ../ops/trait.Deref.html -/// [`CStr`]: struct.CStr.html -/// -/// # Examples -/// -/// ```ignore (extern-declaration) -/// # fn main() { -/// use std::ffi::CString; -/// use std::os::raw::c_char; -/// -/// extern { -/// fn my_printer(s: *const c_char); -/// } -/// -/// // We are certain that our string doesn't have 0 bytes in the middle, -/// // so we can .unwrap() -/// let c_to_print = CString::new("Hello, world!").unwrap(); -/// unsafe { -/// my_printer(c_to_print.as_ptr()); -/// } -/// # } -/// ``` -/// -/// # Safety -/// -/// `CString` is intended for working with traditional C-style strings -/// (a sequence of non-nul bytes terminated by a single nul byte); the -/// primary use case for these kinds of strings is interoperating with C-like -/// code. Often you will need to transfer ownership to/from that external -/// code. It is strongly recommended that you thoroughly read through the -/// documentation of `CString` before use, as improper ownership management -/// of `CString` instances can lead to invalid memory accesses, memory leaks, -/// and other memory errors. - -#[derive(PartialEq, PartialOrd, Eq, Ord, Hash, Clone)] -pub struct CString { - // Invariant 1: the slice ends with a zero byte and has a length of at least one. - // Invariant 2: the slice contains only one zero byte. - // Improper usage of unsafe function can break Invariant 2, but not Invariant 1. - inner: Box<[u8]>, -} - -/// Representation of a borrowed C string. -/// -/// This type represents a borrowed reference to a nul-terminated -/// array of bytes. It can be constructed safely from a `&[`[`u8`]`]` -/// slice, or unsafely from a raw `*const c_char`. It can then be -/// converted to a Rust [`&str`] by performing UTF-8 validation, or -/// into an owned [`CString`]. -/// -/// `CStr` is to [`CString`] as [`&str`] is to [`String`]: the former -/// in each pair are borrowed references; the latter are owned -/// strings. -/// -/// Note that this structure is **not** `repr(C)` and is not recommended to be -/// placed in the signatures of FFI functions. Instead, safe wrappers of FFI -/// functions may leverage the unsafe [`from_ptr`] constructor to provide a safe -/// interface to other consumers. -/// -/// # Examples -/// -/// Inspecting a foreign C string: -/// -/// ```ignore (extern-declaration) -/// use std::ffi::CStr; -/// use std::os::raw::c_char; -/// -/// extern { fn my_string() -> *const c_char; } -/// -/// unsafe { -/// let slice = CStr::from_ptr(my_string()); -/// println!("string buffer size without nul terminator: {}", slice.to_bytes().len()); -/// } -/// ``` -/// -/// Passing a Rust-originating C string: -/// -/// ```ignore (extern-declaration) -/// use std::ffi::{CString, CStr}; -/// use std::os::raw::c_char; -/// -/// fn work(data: &CStr) { -/// extern { fn work_with(data: *const c_char); } -/// -/// unsafe { work_with(data.as_ptr()) } -/// } -/// -/// let s = CString::new("data data data data").unwrap(); -/// work(&s); -/// ``` -/// -/// Converting a foreign C string into a Rust [`String`]: -/// -/// ```ignore (extern-declaration) -/// use std::ffi::CStr; -/// use std::os::raw::c_char; -/// -/// extern { fn my_string() -> *const c_char; } -/// -/// fn my_string_safe() -> String { -/// unsafe { -/// CStr::from_ptr(my_string()).to_string_lossy().into_owned() -/// } -/// } -/// -/// println!("string: {}", my_string_safe()); -/// ``` -/// -/// [`u8`]: ../primitive.u8.html -/// [`&str`]: ../primitive.str.html -/// [`String`]: ../string/struct.String.html -/// [`CString`]: struct.CString.html -/// [`from_ptr`]: #method.from_ptr -#[derive(Hash)] -pub struct CStr { - // FIXME: this should not be represented with a DST slice but rather with - // just a raw `c_char` along with some form of marker to make - // this an unsized type. Essentially `sizeof(&CStr)` should be the - // same as `sizeof(&c_char)` but `CStr` should be an unsized type. - inner: [c_char], -} - -/// An error indicating that an interior nul byte was found. -/// -/// While Rust strings may contain nul bytes in the middle, C strings -/// can't, as that byte would effectively truncate the string. -/// -/// This error is created by the [`new`][`CString::new`] method on -/// [`CString`]. See its documentation for more. -/// -/// [`CString`]: struct.CString.html -/// [`CString::new`]: struct.CString.html#method.new -/// -/// # Examples -/// -/// ``` -/// use std::ffi::{CString, NulError}; -/// -/// let _: NulError = CString::new(b"f\0oo".to_vec()).unwrap_err(); -/// ``` -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct NulError(usize, Vec<u8>); - -/// An error indicating that a nul byte was not in the expected position. -/// -/// The slice used to create a [`CStr`] must have one and only one nul -/// byte at the end of the slice. -/// -/// This error is created by the -/// [`from_bytes_with_nul`][`CStr::from_bytes_with_nul`] method on -/// [`CStr`]. See its documentation for more. -/// -/// [`CStr`]: struct.CStr.html -/// [`CStr::from_bytes_with_nul`]: struct.CStr.html#method.from_bytes_with_nul -/// -/// # Examples -/// -/// ``` -/// use std::ffi::{CStr, FromBytesWithNulError}; -/// -/// let _: FromBytesWithNulError = CStr::from_bytes_with_nul(b"f\0oo").unwrap_err(); -/// ``` -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct FromBytesWithNulError { - kind: FromBytesWithNulErrorKind, -} - -#[derive(Clone, PartialEq, Eq, Debug)] -enum FromBytesWithNulErrorKind { - InteriorNul(usize), - NotNulTerminated, -} - -impl FromBytesWithNulError { - fn interior_nul(pos: usize) -> FromBytesWithNulError { - FromBytesWithNulError { - kind: FromBytesWithNulErrorKind::InteriorNul(pos), - } - } - fn not_nul_terminated() -> FromBytesWithNulError { - FromBytesWithNulError { - kind: FromBytesWithNulErrorKind::NotNulTerminated, - } - } - fn description(&self) -> &str { - match self.kind { - FromBytesWithNulErrorKind::InteriorNul(..) => { - "data provided contains an interior nul byte" - } - FromBytesWithNulErrorKind::NotNulTerminated => "data provided is not nul terminated", - } - } -} +use crate::{header::string::strlen, platform::types::c_char}; -/// An error indicating invalid UTF-8 when converting a [`CString`] into a [`String`]. -/// -/// `CString` is just a wrapper over a buffer of bytes with a nul -/// terminator; [`into_string`][`CString::into_string`] performs UTF-8 -/// validation on those bytes and may return this error. -/// -/// This `struct` is created by the -/// [`into_string`][`CString::into_string`] method on [`CString`]. See -/// its documentation for more. -/// -/// [`String`]: ../string/struct.String.html -/// [`CString`]: struct.CString.html -/// [`CString::into_string`]: struct.CString.html#method.into_string -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct IntoStringError { - inner: CString, - error: Utf8Error, +/// C string wrapper, guaranteed to be +#[derive(Clone, Copy)] +#[repr(transparent)] +pub struct CStr<'a> { + ptr: NonNull<c_char>, + _marker: PhantomData<&'a [u8]>, } -impl CString { - /// Creates a new C-compatible string from a container of bytes. - /// - /// This function will consume the provided data and use the - /// underlying bytes to construct a new string, ensuring that - /// there is a trailing 0 byte. This trailing 0 byte will be - /// appended by this function; the provided data should *not* - /// contain any 0 bytes in it. - /// - /// # Examples - /// - /// ```ignore (extern-declaration) - /// use std::ffi::CString; - /// use std::os::raw::c_char; - /// - /// extern { fn puts(s: *const c_char); } - /// - /// let to_print = CString::new("Hello!").unwrap(); - /// unsafe { - /// puts(to_print.as_ptr()); - /// } - /// ``` - /// - /// # Errors +impl<'a> CStr<'a> { + /// Safety /// - /// This function will return an error if the supplied bytes contain an - /// internal 0 byte. The [`NulError`] returned will contain the bytes as well as - /// the position of the nul byte. - /// - /// [`NulError`]: struct.NulError.html - pub fn new<T: Into<Vec<u8>>>(t: T) -> Result<CString, NulError> { - Self::_new(t.into()) - } - - fn _new(bytes: Vec<u8>) -> Result<CString, NulError> { - match memchr(0, &bytes) { - Some(i) => Err(NulError(i, bytes)), - None => Ok(unsafe { CString::from_vec_unchecked(bytes) }), + /// The ptr must be valid up to and including the first NUL byte from the base ptr. + pub const unsafe fn from_ptr(ptr: *const c_char) -> Self { + Self { + ptr: NonNull::new_unchecked(ptr as *mut c_char), + _marker: PhantomData, } } - - /// Creates a C-compatible string by consuming a byte vector, - /// without checking for interior 0 bytes. - /// - /// This method is equivalent to [`new`] except that no runtime assertion - /// is made that `v` contains no 0 bytes, and it requires an actual - /// byte vector, not anything that can be converted to one with Into. - /// - /// [`new`]: #method.new - /// - /// # Examples - /// - /// ``` - /// use std::ffi::CString; - /// - /// let raw = b"foo".to_vec(); - /// unsafe { - /// let c_string = CString::from_vec_unchecked(raw); - /// } - /// ``` - pub unsafe fn from_vec_unchecked(mut v: Vec<u8>) -> CString { - v.reserve_exact(1); - v.push(0); - CString { - inner: v.into_boxed_slice(), - } - } - - /// Retakes ownership of a `CString` that was transferred to C via [`into_raw`]. - /// - /// Additionally, the length of the string will be recalculated from the pointer. - /// - /// # Safety - /// - /// This should only ever be called with a pointer that was earlier - /// obtained by calling [`into_raw`] on a `CString`. Other usage (e.g. trying to take - /// ownership of a string that was allocated by foreign code) is likely to lead - /// to undefined behavior or allocator corruption. - /// - /// > **Note:** If you need to borrow a string that was allocated by - /// > foreign code, use [`CStr`]. If you need to take ownership of - /// > a string that was allocated by foreign code, you will need to - /// > make your own provisions for freeing it appropriately, likely - /// > with the foreign code's API to do that. - /// - /// [`into_raw`]: #method.into_raw - /// [`CStr`]: struct.CStr.html - /// - /// # Examples - /// - /// Create a `CString`, pass ownership to an `extern` function (via raw pointer), then retake - /// ownership with `from_raw`: - /// - /// ```ignore (extern-declaration) - /// use std::ffi::CString; - /// use std::os::raw::c_char; - /// - /// extern { - /// fn some_extern_function(s: *mut c_char); - /// } - /// - /// let c_string = CString::new("Hello!").unwrap(); - /// let raw = c_string.into_raw(); - /// unsafe { - /// some_extern_function(raw); - /// let c_string = CString::from_raw(raw); - /// } - /// ``` - pub unsafe fn from_raw(ptr: *mut c_char) -> CString { - let len = strlen(ptr) + 1; // Including the NUL byte - let slice = slice::from_raw_parts_mut(ptr, len as usize); - CString { - inner: Box::from_raw(slice as *mut [c_char] as *mut [u8]), - } - } - - /// Consumes the `CString` and transfers ownership of the string to a C caller. - /// - /// The pointer which this function returns must be returned to Rust and reconstituted using - /// [`from_raw`] to be properly deallocated. Specifically, one - /// should *not* use the standard C `free()` function to deallocate - /// this string. - /// - /// Failure to call [`from_raw`] will lead to a memory leak. - /// - /// [`from_raw`]: #method.from_raw - /// - /// # Examples - /// - /// ``` - /// use std::ffi::CString; - /// - /// let c_string = CString::new("foo").unwrap(); - /// - /// let ptr = c_string.into_raw(); - /// - /// unsafe { - /// assert_eq!(b'f', *ptr as u8); - /// assert_eq!(b'o', *ptr.offset(1) as u8); - /// assert_eq!(b'o', *ptr.offset(2) as u8); - /// assert_eq!(b'\0', *ptr.offset(3) as u8); - /// - /// // retake pointer to free memory - /// let _ = CString::from_raw(ptr); - /// } - /// ``` - #[inline] - pub fn into_raw(self) -> *mut c_char { - Box::into_raw(self.into_inner()) as *mut c_char - } - - /// Converts the `CString` into a [`String`] if it contains valid UTF-8 data. - /// - /// On failure, ownership of the original `CString` is returned. - /// - /// [`String`]: ../string/struct.String.html - /// - /// # Examples - /// - /// ``` - /// use std::ffi::CString; - /// - /// let valid_utf8 = vec![b'f', b'o', b'o']; - /// let cstring = CString::new(valid_utf8).unwrap(); - /// assert_eq!(cstring.into_string().unwrap(), "foo"); - /// - /// let invalid_utf8 = vec![b'f', 0xff, b'o', b'o']; - /// let cstring = CString::new(invalid_utf8).unwrap(); - /// let err = cstring.into_string().err().unwrap(); - /// assert_eq!(err.utf8_error().valid_up_to(), 1); - /// ``` - - pub fn into_string(self) -> Result<String, IntoStringError> { - String::from_utf8(self.into_bytes()).map_err(|e| IntoStringError { - error: e.utf8_error(), - inner: unsafe { CString::from_vec_unchecked(e.into_bytes()) }, - }) - } - - /// Consumes the `CString` and returns the underlying byte buffer. - /// - /// The returned buffer does **not** contain the trailing nul - /// terminator, and it is guaranteed to not have any interior nul - /// bytes. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::CString; - /// - /// let c_string = CString::new("foo").unwrap(); - /// let bytes = c_string.into_bytes(); - /// assert_eq!(bytes, vec![b'f', b'o', b'o']); - /// ``` - pub fn into_bytes(self) -> Vec<u8> { - let mut vec = self.into_inner().into_vec(); - let _nul = vec.pop(); - debug_assert_eq!(_nul, Some(0u8)); - vec - } - - /// Equivalent to the [`into_bytes`] function except that the returned vector - /// includes the trailing nul terminator. - /// - /// [`into_bytes`]: #method.into_bytes - /// - /// # Examples - /// - /// ``` - /// use std::ffi::CString; - /// - /// let c_string = CString::new("foo").unwrap(); - /// let bytes = c_string.into_bytes_with_nul(); - /// assert_eq!(bytes, vec![b'f', b'o', b'o', b'\0']); - /// ``` - pub fn into_bytes_with_nul(self) -> Vec<u8> { - self.into_inner().into_vec() - } - - /// Returns the contents of this `CString` as a slice of bytes. - /// - /// The returned slice does **not** contain the trailing nul - /// terminator, and it is guaranteed to not have any interior nul - /// bytes. If you need the nul terminator, use - /// [`as_bytes_with_nul`] instead. - /// - /// [`as_bytes_with_nul`]: #method.as_bytes_with_nul - /// - /// # Examples - /// - /// ``` - /// use std::ffi::CString; - /// - /// let c_string = CString::new("foo").unwrap(); - /// let bytes = c_string.as_bytes(); - /// assert_eq!(bytes, &[b'f', b'o', b'o']); - /// ``` - #[inline] - pub fn as_bytes(&self) -> &[u8] { - &self.inner[..self.inner.len() - 1] - } - - /// Equivalent to the [`as_bytes`] function except that the returned slice - /// includes the trailing nul terminator. - /// - /// [`as_bytes`]: #method.as_bytes - /// - /// # Examples - /// - /// ``` - /// use std::ffi::CString; - /// - /// let c_string = CString::new("foo").unwrap(); - /// let bytes = c_string.as_bytes_with_nul(); - /// assert_eq!(bytes, &[b'f', b'o', b'o', b'\0']); - /// ``` - #[inline] - pub fn as_bytes_with_nul(&self) -> &[u8] { - &self.inner - } - - /// Extracts a [`CStr`] slice containing the entire string. - /// - /// [`CStr`]: struct.CStr.html - /// - /// # Examples - /// - /// ``` - /// use std::ffi::{CString, CStr}; - /// - /// let c_string = CString::new(b"foo".to_vec()).unwrap(); - /// let c_str = c_string.as_c_str(); - /// assert_eq!(c_str, CStr::from_bytes_with_nul(b"foo\0").unwrap()); - /// ``` - #[inline] - pub fn as_c_str(&self) -> &CStr { - &*self - } - - /// Converts this `CString` into a boxed [`CStr`]. - /// - /// [`CStr`]: struct.CStr.html - /// - /// # Examples - /// - /// ``` - /// use std::ffi::{CString, CStr}; - /// - /// let c_string = CString::new(b"foo".to_vec()).unwrap(); - /// let boxed = c_string.into_boxed_c_str(); - /// assert_eq!(&*boxed, CStr::from_bytes_with_nul(b"foo\0").unwrap()); - /// ``` - pub fn into_boxed_c_str(self) -> Box<CStr> { - unsafe { Box::from_raw(Box::into_raw(self.into_inner()) as *mut CStr) } - } - - // Bypass "move out of struct which implements [`Drop`] trait" restriction. - /// - /// [`Drop`]: ../ops/trait.Drop.html - fn into_inner(self) -> Box<[u8]> { - unsafe { - let result = ptr::read(&self.inner); - mem::forget(self); - result + pub unsafe fn from_nullable_ptr(ptr: *const c_char) -> Option<Self> { + if ptr.is_null() { + None + } else { + Some(Self::from_ptr(ptr)) } } -} - -// Turns this `CString` into an empty string to prevent -// memory unsafe code from working by accident. Inline -// to prevent LLVM from optimizing it away in debug builds. -impl Drop for CString { - #[inline] - fn drop(&mut self) { + pub fn to_bytes_with_nul(self) -> &'a [u8] { unsafe { - *self.inner.get_unchecked_mut(0) = 0; - } - } -} - -impl ops::Deref for CString { - type Target = CStr; - - #[inline] - fn deref(&self) -> &CStr { - unsafe { CStr::from_bytes_with_nul_unchecked(self.as_bytes_with_nul()) } - } -} - -impl fmt::Debug for CString { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -impl From<CString> for Vec<u8> { - /// Converts a [`CString`] into a [`Vec`]`<u8>`. - /// - /// The conversion consumes the [`CString`], and removes the terminating NUL byte. - /// - /// [`Vec`]: ../vec/struct.Vec.html - /// [`CString`]: ../ffi/struct.CString.html - #[inline] - fn from(s: CString) -> Vec<u8> { - s.into_bytes() - } -} - -impl fmt::Debug for CStr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "\"")?; - for byte in self - .to_bytes() - .iter() - .flat_map(|&b| ascii::escape_default(b)) - { - f.write_char(byte as char)?; + // SAFETY: The string must be valid at least until (and including) the NUL byte. + let len = strlen(self.ptr.as_ptr()); + core::slice::from_raw_parts(self.ptr.as_ptr().cast(), len + 1) } - write!(f, "\"") } -} - -impl<'a> Default for &'a CStr { - fn default() -> &'a CStr { - const SLICE: &[c_char] = &[0]; - unsafe { CStr::from_ptr(SLICE.as_ptr()) } - } -} - -impl Default for CString { - /// Creates an empty `CString`. - fn default() -> CString { - let a: &CStr = Default::default(); - a.to_owned() + pub fn to_bytes(self) -> &'a [u8] { + let s = self.to_bytes_with_nul(); + &s[..s.len() - 1] } -} - -impl Borrow<CStr> for CString { - #[inline] - fn borrow(&self) -> &CStr { - self - } -} - -impl<'a> From<Cow<'a, CStr>> for CString { - #[inline] - fn from(s: Cow<'a, CStr>) -> Self { - s.into_owned() - } -} - -impl<'a> From<&'a CStr> for Box<CStr> { - fn from(s: &'a CStr) -> Box<CStr> { - let boxed: Box<[u8]> = Box::from(s.to_bytes_with_nul()); - unsafe { Box::from_raw(Box::into_raw(boxed) as *mut CStr) } - } -} - -impl From<Box<CStr>> for CString { - /// Converts a [`Box`]`<CStr>` into a [`CString`] without copying or allocating. - /// - /// [`Box`]: ../boxed/struct.Box.html - /// [`CString`]: ../ffi/struct.CString.html - #[inline] - fn from(s: Box<CStr>) -> CString { - s.into_c_string() - } -} - -impl Clone for Box<CStr> { - #[inline] - fn clone(&self) -> Self { - (**self).into() - } -} - -impl From<CString> for Box<CStr> { - /// Converts a [`CString`] into a [`Box`]`<CStr>` without copying or allocating. - /// - /// [`CString`]: ../ffi/struct.CString.html - /// [`Box`]: ../boxed/struct.Box.html - #[inline] - fn from(s: CString) -> Box<CStr> { - s.into_boxed_c_str() + pub fn to_str(self) -> Result<&'a str, Utf8Error> { + core::str::from_utf8(self.to_bytes()) } -} - -impl<'a> From<CString> for Cow<'a, CStr> { - #[inline] - fn from(s: CString) -> Cow<'a, CStr> { - Cow::Owned(s) - } -} - -impl<'a> From<&'a CStr> for Cow<'a, CStr> { - #[inline] - fn from(s: &'a CStr) -> Cow<'a, CStr> { - Cow::Borrowed(s) - } -} - -impl<'a> From<&'a CString> for Cow<'a, CStr> { - #[inline] - fn from(s: &'a CString) -> Cow<'a, CStr> { - Cow::Borrowed(s.as_c_str()) - } -} - -impl From<CString> for Arc<CStr> { - /// Converts a [`CString`] into a [`Arc`]`<CStr>` without copying or allocating. - /// - /// [`CString`]: ../ffi/struct.CString.html - /// [`Arc`]: ../sync/struct.Arc.html - #[inline] - fn from(s: CString) -> Arc<CStr> { - let arc: Arc<[u8]> = Arc::from(s.into_inner()); - unsafe { Arc::from_raw(Arc::into_raw(arc) as *const CStr) } - } -} - -impl<'a> From<&'a CStr> for Arc<CStr> { - #[inline] - fn from(s: &CStr) -> Arc<CStr> { - let arc: Arc<[u8]> = Arc::from(s.to_bytes_with_nul()); - unsafe { Arc::from_raw(Arc::into_raw(arc) as *const CStr) } - } -} - -impl From<CString> for Rc<CStr> { - /// Converts a [`CString`] into a [`Rc`]`<CStr>` without copying or allocating. - /// - /// [`CString`]: ../ffi/struct.CString.html - /// [`Rc`]: ../rc/struct.Rc.html - #[inline] - fn from(s: CString) -> Rc<CStr> { - let rc: Rc<[u8]> = Rc::from(s.into_inner()); - unsafe { Rc::from_raw(Rc::into_raw(rc) as *const CStr) } - } -} - -impl<'a> From<&'a CStr> for Rc<CStr> { - #[inline] - fn from(s: &CStr) -> Rc<CStr> { - let rc: Rc<[u8]> = Rc::from(s.to_bytes_with_nul()); - unsafe { Rc::from_raw(Rc::into_raw(rc) as *const CStr) } - } -} - -impl Default for Box<CStr> { - fn default() -> Box<CStr> { - let boxed: Box<[u8]> = Box::from([0]); - unsafe { Box::from_raw(Box::into_raw(boxed) as *mut CStr) } - } -} - -impl NulError { - /// Returns the position of the nul byte in the slice that caused - /// [`CString::new`] to fail. - /// - /// [`CString::new`]: struct.CString.html#method.new - /// - /// # Examples - /// - /// ``` - /// use std::ffi::CString; - /// - /// let nul_error = CString::new("foo\0bar").unwrap_err(); - /// assert_eq!(nul_error.nul_position(), 3); - /// - /// let nul_error = CString::new("foo bar\0").unwrap_err(); - /// assert_eq!(nul_error.nul_position(), 7); - /// ``` - pub fn nul_position(&self) -> usize { - self.0 - } - - /// Consumes this error, returning the underlying vector of bytes which - /// generated the error in the first place. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::CString; - /// - /// let nul_error = CString::new("foo\0bar").unwrap_err(); - /// assert_eq!(nul_error.into_vec(), b"foo\0bar"); - /// ``` - pub fn into_vec(self) -> Vec<u8> { - self.1 - } -} - -impl fmt::Display for NulError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "nul byte found in provided data at position: {}", self.0) - } -} - -impl fmt::Display for FromBytesWithNulError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(self.description())?; - if let FromBytesWithNulErrorKind::InteriorNul(pos) = self.kind { - write!(f, " at byte pos {}", pos)?; - } - Ok(()) - } -} - -impl IntoStringError { - /// Consumes this error, returning original [`CString`] which generated the - /// error. - /// - /// [`CString`]: struct.CString.html - pub fn into_cstring(self) -> CString { - self.inner + pub fn to_string_lossy(self) -> Cow<'a, str> { + String::from_utf8_lossy(self.to_bytes()) } - - /// Access the underlying UTF-8 error that was the cause of this error. - pub fn utf8_error(&self) -> Utf8Error { - self.error + pub fn as_ptr(self) -> *const c_char { + self.ptr.as_ptr() } - - fn description(&self) -> &str { - "C string contained non-utf8 bytes" - } -} - -impl fmt::Display for IntoStringError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.description().fmt(f) + pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &'a [u8]) -> Self { + Self::from_ptr(bytes.as_ptr().cast()) } -} - -impl CStr { - /// Wraps a raw C string with a safe C string wrapper. - /// - /// This function will wrap the provided `ptr` with a `CStr` wrapper, which - /// allows inspection and interoperation of non-owned C strings. This method - /// is unsafe for a number of reasons: - /// - /// * There is no guarantee to the validity of `ptr`. - /// * The returned lifetime is not guaranteed to be the actual lifetime of - /// `ptr`. - /// * There is no guarantee that the memory pointed to by `ptr` contains a - /// valid nul terminator byte at the end of the string. - /// * It is not guaranteed that the memory pointed by `ptr` won't change - /// before the `CStr` has been destroyed. - /// - /// > **Note**: This operation is intended to be a 0-cost cast but it is - /// > currently implemented with an up-front calculation of the length of - /// > the string. This is not guaranteed to always be the case. - /// - /// # Examples - /// - /// ```ignore (extern-declaration) - /// # fn main() { - /// use std::ffi::CStr; - /// use std::os::raw::c_char; - /// - /// extern { - /// fn my_string() -> *const c_char; - /// } - /// - /// unsafe { - /// let slice = CStr::from_ptr(my_string()); - /// println!("string returned: {}", slice.to_str().unwrap()); - /// } - /// # } - /// ``` - pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a CStr { - let len = strlen(ptr); - let ptr = ptr as *const u8; - CStr::from_bytes_with_nul_unchecked(slice::from_raw_parts(ptr, len as usize + 1)) - } - - /// Creates a C string wrapper from a byte slice. - /// - /// This function will cast the provided `bytes` to a `CStr` - /// wrapper after ensuring that the byte slice is nul-terminated - /// and does not contain any interior nul bytes. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::CStr; - /// - /// let cstr = CStr::from_bytes_with_nul(b"hello\0"); - /// assert!(cstr.is_ok()); - /// ``` - /// - /// Creating a `CStr` without a trailing nul terminator is an error: - /// - /// ``` - /// use std::ffi::CStr; - /// - /// let c_str = CStr::from_bytes_with_nul(b"hello"); - /// assert!(c_str.is_err()); - /// ``` - /// - /// Creating a `CStr` with an interior nul byte is an error: - /// - /// ``` - /// use std::ffi::CStr; - /// - /// let c_str = CStr::from_bytes_with_nul(b"he\0llo\0"); - /// assert!(c_str.is_err()); - /// ``` - pub fn from_bytes_with_nul(bytes: &[u8]) -> Result<&CStr, FromBytesWithNulError> { - let nul_pos = memchr(0, bytes); - if let Some(nul_pos) = nul_pos { - if nul_pos + 1 != bytes.len() { - return Err(FromBytesWithNulError::interior_nul(nul_pos)); - } - Ok(unsafe { CStr::from_bytes_with_nul_unchecked(bytes) }) - } else { - Err(FromBytesWithNulError::not_nul_terminated()) + pub fn from_bytes_with_nul(bytes: &'a [u8]) -> Result<Self, FromBytesWithNulError> { + if bytes.last() != Some(&b'\0') || bytes[..bytes.len() - 1].contains(&b'\0') { + return Err(FromBytesWithNulError); } - } - /// Unsafely creates a C string wrapper from a byte slice. - /// - /// This function will cast the provided `bytes` to a `CStr` wrapper without - /// performing any sanity checks. The provided slice **must** be nul-terminated - /// and not contain any interior nul bytes. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::{CStr, CString}; - /// - /// unsafe { - /// let cstring = CString::new("hello").unwrap(); - /// let cstr = CStr::from_bytes_with_nul_unchecked(cstring.to_bytes_with_nul()); - /// assert_eq!(cstr, &*cstring); - /// } - /// ``` - #[inline] - pub const unsafe fn from_bytes_with_nul_unchecked(bytes: &[u8]) -> &CStr { - &*(bytes as *const [u8] as *const CStr) - } - - /// Returns the inner pointer to this C string. - /// - /// The returned pointer will be valid for as long as `self` is, and points - /// to a contiguous region of memory terminated with a 0 byte to represent - /// the end of the string. - /// - /// **WARNING** - /// - /// It is your responsibility to make sure that the underlying memory is not - /// freed too early. For example, the following code will cause undefined - /// behavior when `ptr` is used inside the `unsafe` block: - /// - /// ```no_run - /// # #![allow(unused_must_use)] - /// use std::ffi::{CString}; - /// - /// let ptr = CString::new("Hello").unwrap().as_ptr(); - /// unsafe { - /// // `ptr` is dangling - /// *ptr; - /// } - /// ``` - /// - /// This happens because the pointer returned by `as_ptr` does not carry any - /// lifetime information and the [`CString`] is deallocated immediately after - /// the `CString::new("Hello").unwrap().as_ptr()` expression is evaluated. - /// To fix the problem, bind the `CString` to a local variable: - /// - /// ```no_run - /// # #![allow(unused_must_use)] - /// use std::ffi::{CString}; - /// - /// let hello = CString::new("Hello").unwrap(); - /// let ptr = hello.as_ptr(); - /// unsafe { - /// // `ptr` is valid because `hello` is in scope - /// *ptr; - /// } - /// ``` - /// - /// This way, the lifetime of the `CString` in `hello` encompasses - /// the lifetime of `ptr` and the `unsafe` block. - /// - /// [`CString`]: struct.CString.html - #[inline] - pub fn as_ptr(&self) -> *const c_char { - self.inner.as_ptr() - } - - /// Converts this C string to a byte slice. - /// - /// The returned slice will **not** contain the trailing nul terminator that this C - /// string has. - /// - /// > **Note**: This method is currently implemented as a constant-time - /// > cast, but it is planned to alter its definition in the future to - /// > perform the length calculation whenever this method is called. - /// - /// # Examples - /// - /// ``` - /// use std::ffi::CStr; - /// - /// let c_str = CStr::from_bytes_with_nul(b"foo\0").unwrap(); - /// assert_eq!(c_str.to_bytes(), b"foo"); - /// ``` - #[inline] - pub fn to_bytes(&self) -> &[u8] { - let bytes = self.to_bytes_with_nul(); - &bytes[..bytes.len() - 1] - } - - /// Converts this C string to a byte slice containing the trailing 0 byte. - /// - /// This function is the equivalent of [`to_bytes`] except that it will retain - /// the trailing nul terminator instead of chopping it off. - /// - /// > **Note**: This method is currently implemented as a 0-cost cast, but - /// > it is planned to alter its definition in the future to perform the - /// > length calculation whenever this method is called. - /// - /// [`to_bytes`]: #method.to_bytes - /// - /// # Examples - /// - /// ``` - /// use std::ffi::CStr; - /// - /// let c_str = CStr::from_bytes_with_nul(b"foo\0").unwrap(); - /// assert_eq!(c_str.to_bytes_with_nul(), b"foo\0"); - /// ``` - #[inline] - pub fn to_bytes_with_nul(&self) -> &[u8] { - unsafe { &*(&self.inner as *const [c_char] as *const [u8]) } - } - - /// Yields a [`&str`] slice if the `CStr` contains valid UTF-8. - /// - /// If the contents of the `CStr` are valid UTF-8 data, this - /// function will return the corresponding [`&str`] slice. Otherwise, - /// it will return an error with details of where UTF-8 validation failed. - /// - /// > **Note**: This method is currently implemented to check for validity - /// > after a constant-time cast, but it is planned to alter its definition - /// > in the future to perform the length calculation in addition to the - /// > UTF-8 check whenever this method is called. - /// - /// [`&str`]: ../primitive.str.html - /// - /// # Examples - /// - /// ``` - /// use std::ffi::CStr; - /// - /// let c_str = CStr::from_bytes_with_nul(b"foo\0").unwrap(); - /// assert_eq!(c_str.to_str(), Ok("foo")); - /// ``` - pub fn to_str(&self) -> Result<&str, str::Utf8Error> { - // NB: When CStr is changed to perform the length check in .to_bytes() - // instead of in from_ptr(), it may be worth considering if this should - // be rewritten to do the UTF-8 check inline with the length calculation - // instead of doing it afterwards. - str::from_utf8(self.to_bytes()) + Ok(unsafe { Self::from_bytes_with_nul_unchecked(bytes) }) } - - /// Converts a `CStr` into a [`Cow`]`<`[`str`]`>`. - /// - /// If the contents of the `CStr` are valid UTF-8 data, this - /// function will return a [`Cow`]`::`[`Borrowed`]`(`[`&str`]`)` - /// with the the corresponding [`&str`] slice. Otherwise, it will - /// replace any invalid UTF-8 sequences with - /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD] and return a - /// [`Cow`]`::`[`Owned`]`(`[`String`]`)` with the result. - /// - /// > **Note**: This method is currently implemented to check for validity - /// > after a constant-time cast, but it is planned to alter its definition - /// > in the future to perform the length calculation in addition to the - /// > UTF-8 check whenever this method is called. - /// - /// [`Cow`]: ../borrow/enum.Cow.html - /// [`Borrowed`]: ../borrow/enum.Cow.html#variant.Borrowed - /// [`Owned`]: ../borrow/enum.Cow.html#variant.Owned - /// [`str`]: ../primitive.str.html - /// [`String`]: ../string/struct.String.html - /// [U+FFFD]: ../char/constant.REPLACEMENT_CHARACTER.html - /// - /// # Examples - /// - /// Calling `to_string_lossy` on a `CStr` containing valid UTF-8: - /// - /// ``` - /// use std::borrow::Cow; - /// use std::ffi::CStr; - /// - /// let c_str = CStr::from_bytes_with_nul(b"Hello World\0").unwrap(); - /// assert_eq!(c_str.to_string_lossy(), Cow::Borrowed("Hello World")); - /// ``` - /// - /// Calling `to_string_lossy` on a `CStr` containing invalid UTF-8: - /// - /// ``` - /// use std::borrow::Cow; - /// use std::ffi::CStr; - /// - /// let c_str = CStr::from_bytes_with_nul(b"Hello \xF0\x90\x80World\0").unwrap(); - /// assert_eq!( - /// c_str.to_string_lossy(), - /// Cow::Owned(String::from("Hello �World")) as Cow<str> - /// ); - /// ``` - pub fn to_string_lossy(&self) -> Cow<str> { - String::from_utf8_lossy(self.to_bytes()) - } - - /// Converts a [`Box`]`<CStr>` into a [`CString`] without copying or allocating. - /// - /// [`Box`]: ../boxed/struct.Box.html - /// [`CString`]: struct.CString.html - /// - /// # Examples - /// - /// ``` - /// use std::ffi::CString; - /// - /// let c_string = CString::new(b"foo".to_vec()).unwrap(); - /// let boxed = c_string.into_boxed_c_str(); - /// assert_eq!(boxed.into_c_string(), CString::new("foo").unwrap()); - /// ``` - pub fn into_c_string(self: Box<CStr>) -> CString { - let raw = Box::into_raw(self) as *mut [u8]; - CString { - inner: unsafe { Box::from_raw(raw) }, + pub fn from_bytes_until_nul(bytes: &'a [u8]) -> Result<Self, FromBytesUntilNulError> { + if !bytes.contains(&b'\0') { + return Err(FromBytesUntilNulError); } - } -} -impl PartialEq for CStr { - fn eq(&self, other: &CStr) -> bool { - self.to_bytes().eq(other.to_bytes()) + Ok(unsafe { Self::from_bytes_with_nul_unchecked(bytes) }) } -} - -impl Eq for CStr {} - -impl PartialOrd for CStr { - fn partial_cmp(&self, other: &CStr) -> Option<Ordering> { - self.to_bytes().partial_cmp(&other.to_bytes()) + pub fn to_owned_cstring(self) -> CString { + CString::from(unsafe { core::ffi::CStr::from_ptr(self.ptr.as_ptr()) }) } -} - -impl Ord for CStr { - fn cmp(&self, other: &CStr) -> Ordering { - self.to_bytes().cmp(&other.to_bytes()) + pub fn borrow(string: &'a CString) -> Self { + unsafe { Self::from_ptr(string.as_ptr()) } } } -impl ToOwned for CStr { - type Owned = CString; +unsafe impl Send for CStr<'_> {} +unsafe impl Sync for CStr<'_> {} - fn to_owned(&self) -> CString { - CString { - inner: self.to_bytes_with_nul().into(), - } - } -} +#[derive(Debug)] +pub struct FromBytesWithNulError; -impl<'a> From<&'a CStr> for CString { - fn from(s: &'a CStr) -> CString { - s.to_owned() - } -} +#[derive(Debug)] +pub struct FromBytesUntilNulError; -impl ops::Index<ops::RangeFull> for CString { - type Output = CStr; - - #[inline] - fn index(&self, _index: ops::RangeFull) -> &CStr { - self - } -} - -impl AsRef<CStr> for CStr { - #[inline] - fn as_ref(&self) -> &CStr { - self - } -} - -impl AsRef<CStr> for CString { - #[inline] - fn as_ref(&self) -> &CStr { - self - } -} +pub use alloc::ffi::CString; diff --git a/src/db.rs b/src/db.rs index 6fa6b8b325dd996af58c730f8a1cf4c66952ae78..777fbba8d8468b9e4eb5ed1fa351e5a8acc04a38 100644 --- a/src/db.rs +++ b/src/db.rs @@ -44,7 +44,7 @@ impl<R: BufRead> Db<R> { pub type FileDb = Db<BufReader<File>>; impl FileDb { - pub fn open(path: &CStr, separator: Separator) -> io::Result<Self> { + pub fn open(path: CStr, separator: Separator) -> io::Result<Self> { let file = File::open(path, fcntl::O_RDONLY | fcntl::O_CLOEXEC)?; Ok(Db::new(BufReader::new(file), separator)) } diff --git a/src/fs.rs b/src/fs.rs index 47f7c7422e9f82676da2873d04ba1f06a2533c75..37ad5174fb90f5b3ecc502fa924c48724d140967 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -24,14 +24,14 @@ impl File { } } - pub fn open(path: &CStr, oflag: c_int) -> io::Result<Self> { + pub fn open(path: CStr, oflag: c_int) -> io::Result<Self> { match Sys::open(path, oflag, 0) { -1 => Err(io::last_os_error()), ok => Ok(Self::new(ok)), } } - pub fn create(path: &CStr, oflag: c_int, mode: mode_t) -> io::Result<Self> { + pub fn create(path: CStr, oflag: c_int, mode: mode_t) -> io::Result<Self> { match Sys::open(path, oflag | O_CREAT, mode) { -1 => Err(io::last_os_error()), ok => Ok(Self::new(ok)), diff --git a/src/header/dlfcn/mod.rs b/src/header/dlfcn/mod.rs index 0f7ab20ec84d9f52b1bf70be838adbf8957de611..b592e7efe4c732fbbd5ed85f51a32481770dad51 100644 --- a/src/header/dlfcn/mod.rs +++ b/src/header/dlfcn/mod.rs @@ -12,7 +12,7 @@ pub const RTLD_NOW: c_int = 0x0002; pub const RTLD_GLOBAL: c_int = 0x0100; pub const RTLD_LOCAL: c_int = 0x0000; -static ERROR_NOT_SUPPORTED: &'static CStr = c_str!("dlfcn not supported"); +static ERROR_NOT_SUPPORTED: CStr = c_str!("dlfcn not supported"); #[thread_local] static ERROR: AtomicUsize = AtomicUsize::new(0); diff --git a/src/header/grp/mod.rs b/src/header/grp/mod.rs index 282ffb6cf588bfb4ca7df8f5060a021529e08f8f..8dc0e53436597ead12390edd21bdc1ac19b25fbf 100644 --- a/src/header/grp/mod.rs +++ b/src/header/grp/mod.rs @@ -1,8 +1,9 @@ -//! grp implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/grp.h.html +//! grp implementation, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/grp.h.html use core::{ + cell::SyncUnsafeCell, convert::{TryFrom, TryInto}, - mem, + mem::{self, MaybeUninit}, num::ParseIntError, ops::{Deref, DerefMut}, pin::Pin, @@ -11,8 +12,6 @@ use core::{ str::Matches, }; -use lazy_static::lazy_static; - use alloc::{ borrow::ToOwned, string::{FromUtf8Error, String}, @@ -80,9 +79,7 @@ static mut GROUP: group = group { gr_mem: ptr::null_mut(), }; -lazy_static! { - static ref LINE_READER: Mutex<Option<Lines<BufReader<File>>>> = Mutex::new(None); -} +static LINE_READER: SyncUnsafeCell<Option<Lines<BufReader<File>>>> = SyncUnsafeCell::new(None); #[repr(C)] #[derive(Debug)] @@ -307,7 +304,7 @@ pub unsafe extern "C" fn getgrnam_r( // MT-Unsafe race:grent race:grentbuf locale #[no_mangle] pub unsafe extern "C" fn getgrent() -> *mut group { - let mut line_reader = LINE_READER.lock(); + let mut line_reader = &mut *LINE_READER.get(); if line_reader.is_none() { let Ok(db) = File::open(c_str!("/etc/group"), fcntl::O_RDONLY) else { return ptr::null_mut() }; @@ -331,60 +328,74 @@ pub unsafe extern "C" fn getgrent() -> *mut group { // MT-Unsafe race:grent locale #[no_mangle] pub unsafe extern "C" fn endgrent() { - let mut line_reader = LINE_READER.lock(); - *line_reader = None; + *(&mut *LINE_READER.get()) = None; } // MT-Unsafe race:grent locale #[no_mangle] pub unsafe extern "C" fn setgrent() { - let mut line_reader = LINE_READER.lock(); + let mut line_reader = &mut *LINE_READER.get(); let Ok(db) = File::open(c_str!("/etc/group"), fcntl::O_RDONLY) else { return }; *line_reader = Some(BufReader::new(db).lines()); } +// MT-Safe locale +// Not POSIX #[no_mangle] pub unsafe extern "C" fn getgrouplist( user: *const c_char, group: gid_t, groups: *mut gid_t, - ngroups: i32, -) -> i32 { - let mut grps = Vec::<gid_t>::from_raw_parts(groups, 0, ngroups as usize); - let Ok(usr) = (crate::c_str::CStr::from_ptr(user).to_str()) else { return 0 }; + ngroups: *mut c_int, +) -> c_int { + let mut grps = + slice::from_raw_parts_mut(groups.cast::<MaybeUninit<gid_t>>(), ngroups.read() as usize); + + // FIXME: This API probably expects the group database to already exist in memory, as it + // doesn't seem to have any documented error handling. + + let Ok(user) = (crate::c_str::CStr::from_ptr(user).to_str()) else { return 0 }; let Ok(db) = File::open(c_str!("/etc/group"), fcntl::O_RDONLY) else { return 0; }; + let mut groups_found: c_int = 0; + for line in BufReader::new(db).lines() { - if grps.len() >= ngroups as usize { - return ngroups; + let Ok(line) = line else { + return 0; + }; + + let mut parts = line.split(SEPARATOR); + + let group_name = parts.next().unwrap_or(""); + let group_password = parts.next().unwrap_or(""); + let group_id = parts.next().unwrap_or("-1").parse::<c_int>().unwrap(); + let members = parts + .next() + .unwrap_or("") + .split(",") + .map(|i| i.trim()) + .collect::<Vec<_>>(); + + if !members.iter().any(|i| *i == user) { + continue; } - match line { - Err(_) => return 0, - Ok(line) => { - let mut parts = line.split(SEPARATOR); - - let group_name = parts.next().unwrap_or(""); - let group_password = parts.next().unwrap_or(""); - let group_id = parts.next().unwrap_or("-1").parse::<i32>().unwrap(); - let members = parts - .next() - .unwrap_or("") - .split(",") - .map(|i| i.trim()) - .collect::<Vec<_>>(); - - if members.iter().any(|i| *i == usr) { - grps.push(group_id); - } - } + if let Some(dst) = grps.get_mut(groups_found as usize) { + dst.write(group_id); + } + + groups_found = match groups_found.checked_add(1) { + Some(g) => g, + None => break, }; } - if grps.len() <= 0 { - grps.push(group); - } + ngroups.write(groups_found); - return grps.len() as i32; + if groups_found as usize > grps.len() { + -1 + } else { + grps.len() as c_int + } } diff --git a/src/header/netdb/host.rs b/src/header/netdb/host.rs index 1b4ba101632ea849513f94c85f4fdbe6258fcf9e..c80327fec1bd50575f5ee9360132bfa1b1aafe60 100644 --- a/src/header/netdb/host.rs +++ b/src/header/netdb/host.rs @@ -45,7 +45,7 @@ pub unsafe extern "C" fn endhostent() { pub unsafe extern "C" fn sethostent(stayopen: c_int) { HOST_STAYOPEN = stayopen; if HOSTDB < 0 { - HOSTDB = Sys::open(&CString::new("/etc/hosts").unwrap(), O_RDONLY, 0) + HOSTDB = Sys::open(c_str!("/etc/hosts"), O_RDONLY, 0) } else { Sys::lseek(HOSTDB, 0, SEEK_SET); } @@ -55,7 +55,7 @@ pub unsafe extern "C" fn sethostent(stayopen: c_int) { #[no_mangle] pub unsafe extern "C" fn gethostent() -> *mut hostent { if HOSTDB < 0 { - HOSTDB = Sys::open(&CString::new("/etc/hosts").unwrap(), O_RDONLY, 0); + HOSTDB = Sys::open(c_str!("/etc/hosts"), O_RDONLY, 0); } let mut rlb = RawLineBuffer::new(HOSTDB); rlb.seek(H_POS); diff --git a/src/header/netdb/linux.rs b/src/header/netdb/linux.rs index f174170164cc34f3f0ecd3bcfb1f59eb5bacd588..e7455c6e56c8f0b03048d92449e01b5453a3c95e 100644 --- a/src/header/netdb/linux.rs +++ b/src/header/netdb/linux.rs @@ -7,7 +7,7 @@ use crate::{ use alloc::string::String; pub fn get_dns_server() -> String { - let file = match File::open(&CString::new("/etc/resolv.conf").unwrap(), fcntl::O_RDONLY) { + let file = match File::open(c_str!("/etc/resolv.conf"), fcntl::O_RDONLY) { Ok(file) => file, Err(_) => return String::new(), // TODO: better error handling }; diff --git a/src/header/netdb/mod.rs b/src/header/netdb/mod.rs index 4f7f47e75eb9b66cf479cc890d77fe5799984823..9bd4dfb780d3e6fd91c71852bd38f83000451ca2 100644 --- a/src/header/netdb/mod.rs +++ b/src/header/netdb/mod.rs @@ -370,7 +370,7 @@ pub unsafe extern "C" fn getnetbyname(name: *const c_char) -> *mut netent { #[no_mangle] pub unsafe extern "C" fn getnetent() -> *mut netent { if NETDB == 0 { - NETDB = Sys::open(&CString::new("/etc/networks").unwrap(), O_RDONLY, 0); + NETDB = Sys::open(c_str!("/etc/networks"), O_RDONLY, 0); } let mut rlb = RawLineBuffer::new(NETDB); @@ -484,7 +484,7 @@ pub unsafe extern "C" fn getprotobynumber(number: c_int) -> *mut protoent { #[no_mangle] pub unsafe extern "C" fn getprotoent() -> *mut protoent { if PROTODB == 0 { - PROTODB = Sys::open(&CString::new("/etc/protocols").unwrap(), O_RDONLY, 0); + PROTODB = Sys::open(c_str!("/etc/protocols"), O_RDONLY, 0); } let mut rlb = RawLineBuffer::new(PROTODB); @@ -603,7 +603,7 @@ pub unsafe extern "C" fn getservbyport(port: c_int, proto: *const c_char) -> *mu #[no_mangle] pub unsafe extern "C" fn getservent() -> *mut servent { if SERVDB == 0 { - SERVDB = Sys::open(&CString::new("/etc/services").unwrap(), O_RDONLY, 0); + SERVDB = Sys::open(c_str!("/etc/services"), O_RDONLY, 0); } let mut rlb = RawLineBuffer::new(SERVDB); rlb.seek(S_POS); @@ -690,7 +690,7 @@ pub unsafe extern "C" fn getservent() -> *mut servent { pub unsafe extern "C" fn setnetent(stayopen: c_int) { NET_STAYOPEN = stayopen; if NETDB == 0 { - NETDB = Sys::open(&CString::new("/etc/networks").unwrap(), O_RDONLY, 0) + NETDB = Sys::open(c_str!("/etc/networks"), O_RDONLY, 0) } else { Sys::lseek(NETDB, 0, SEEK_SET); N_POS = 0; @@ -701,7 +701,7 @@ pub unsafe extern "C" fn setnetent(stayopen: c_int) { pub unsafe extern "C" fn setprotoent(stayopen: c_int) { PROTO_STAYOPEN = stayopen; if PROTODB == 0 { - PROTODB = Sys::open(&CString::new("/etc/protocols").unwrap(), O_RDONLY, 0) + PROTODB = Sys::open(c_str!("/etc/protocols"), O_RDONLY, 0) } else { Sys::lseek(PROTODB, 0, SEEK_SET); P_POS = 0; @@ -712,7 +712,7 @@ pub unsafe extern "C" fn setprotoent(stayopen: c_int) { pub unsafe extern "C" fn setservent(stayopen: c_int) { SERV_STAYOPEN = stayopen; if SERVDB == 0 { - SERVDB = Sys::open(&CString::new("/etc/services").unwrap(), O_RDONLY, 0) + SERVDB = Sys::open(c_str!("/etc/services"), O_RDONLY, 0) } else { Sys::lseek(SERVDB, 0, SEEK_SET); S_POS = 0; @@ -727,17 +727,8 @@ pub unsafe extern "C" fn getaddrinfo( res: *mut *mut addrinfo, ) -> c_int { //TODO: getaddrinfo - let node_opt = if node.is_null() { - None - } else { - Some(CStr::from_ptr(node)) - }; - - let service_opt = if service.is_null() { - None - } else { - Some(CStr::from_ptr(service)) - }; + let node_opt = CStr::from_nullable_ptr(node); + let service_opt = CStr::from_nullable_ptr(service); let hints_opt = if hints.is_null() { None } else { Some(&*hints) }; @@ -791,7 +782,7 @@ pub unsafe extern "C" fn getaddrinfo( let ai_canonname = if ai_flags & AI_CANONNAME > 0 { ai_flags &= !AI_CANONNAME; - node.to_owned().into_raw() + node.to_owned_cstring().into_raw() } else { ptr::null_mut() }; @@ -859,7 +850,7 @@ pub unsafe extern "C" fn freeaddrinfo(res: *mut addrinfo) { while !ai.is_null() { let bai = Box::from_raw(ai); if !bai.ai_canonname.is_null() { - CString::from_raw(bai.ai_canonname); + drop(CString::from_raw(bai.ai_canonname)); } if !bai.ai_addr.is_null() { if bai.ai_addrlen == mem::size_of::<sockaddr_in>() { diff --git a/src/header/netdb/redox.rs b/src/header/netdb/redox.rs index d52628b186ee6a7eee90081af25a89222f12e4c7..5c307eb894e0dbd2deb3196fb26bc5113108bf3a 100644 --- a/src/header/netdb/redox.rs +++ b/src/header/netdb/redox.rs @@ -1,9 +1,9 @@ -use crate::{c_str::CString, fs::File, header::fcntl, io::Read}; +use crate::{fs::File, header::fcntl, io::Read}; use alloc::string::String; pub fn get_dns_server() -> String { let mut string = String::new(); - let mut file = File::open(&CString::new("/etc/net/dns").unwrap(), fcntl::O_RDONLY).unwrap(); // TODO: error handling + let mut file = File::open(c_str!("/etc/net/dns"), fcntl::O_RDONLY).unwrap(); // TODO: error handling file.read_to_string(&mut string).unwrap(); // TODO: error handling string } diff --git a/src/header/stdio/default.rs b/src/header/stdio/default.rs index 341d76bd161e870467f89affe3dc5d739e68191e..624efeadf5855e993d467d4e896c02d4ac43a219 100644 --- a/src/header/stdio/default.rs +++ b/src/header/stdio/default.rs @@ -1,10 +1,17 @@ use super::{constants, Buffer, BUFSIZ, FILE}; use core::{cell::UnsafeCell, ptr}; -use crate::{fs::File, io::LineWriter, platform::types::*, sync::Mutex}; +use crate::{ + fs::File, + io::LineWriter, + platform::types::*, + sync::{Mutex, Once}, +}; use alloc::{boxed::Box, vec::Vec}; +// TODO: Change FILE to allow const fn initialization? pub struct GlobalFile(UnsafeCell<FILE>); + impl GlobalFile { fn new(file: c_int, flags: c_int) -> Self { let file = File::new(file); @@ -32,15 +39,19 @@ impl GlobalFile { // statics need to be Sync unsafe impl Sync for GlobalFile {} -lazy_static! { - #[allow(non_upper_case_globals)] - pub static ref default_stdin: GlobalFile = GlobalFile::new(0, constants::F_NOWR); - - #[allow(non_upper_case_globals)] - pub static ref default_stdout: GlobalFile = GlobalFile::new(1, constants::F_NORD); +// TODO: Allow const fn initialization of FILE +static DEFAULT_STDIN: Once<GlobalFile> = Once::new(); +static DEFAULT_STDOUT: Once<GlobalFile> = Once::new(); +static DEFAULT_STDERR: Once<GlobalFile> = Once::new(); - #[allow(non_upper_case_globals)] - pub static ref default_stderr: GlobalFile = GlobalFile::new(2, constants::F_NORD); +pub fn default_stdin() -> &'static GlobalFile { + DEFAULT_STDIN.call_once(|| GlobalFile::new(0, constants::F_NOWR)) +} +pub fn default_stdout() -> &'static GlobalFile { + DEFAULT_STDOUT.call_once(|| GlobalFile::new(1, constants::F_NORD)) +} +pub fn default_stderr() -> &'static GlobalFile { + DEFAULT_STDERR.call_once(|| GlobalFile::new(2, constants::F_NORD)) } #[no_mangle] diff --git a/src/header/stdio/mod.rs b/src/header/stdio/mod.rs index 5a43bd8d35145ff39c32ee66a6062e644d9a61ba..b8bb2b5b8f33076ffda2447f88e0cc64ecabc267 100644 --- a/src/header/stdio/mod.rs +++ b/src/header/stdio/mod.rs @@ -107,11 +107,16 @@ pub struct FILE { file: File, // pub for stdio_ext pub(crate) flags: c_int, + + // TODO: Is the read_buf dropped? read_buf: Buffer<'static>, + read_pos: usize, read_size: usize, unget: Vec<u8>, // pub for stdio_ext + + // TODO: To support const fn initialization, use static dispatch (perhaps partially)? pub(crate) writer: Box<dyn Writer + Send>, // Optional pid for use with popen/pclose diff --git a/src/header/stdlib/mod.rs b/src/header/stdlib/mod.rs index 7ce198255975fc1711fb7a37101c3c4023cbbef1..921faf6f5320ec72f59ad3c4bf053bca0e5106c8 100644 --- a/src/header/stdlib/mod.rs +++ b/src/header/stdlib/mod.rs @@ -3,10 +3,10 @@ use core::{convert::TryFrom, intrinsics, iter, mem, ptr, slice}; use rand::{ distributions::{Alphanumeric, Distribution, Uniform}, - prng::XorShiftRng, - rngs::JitterRng, Rng, SeedableRng, }; +use rand_jitter::JitterRng; +use rand_xorshift::XorShiftRng; use crate::{ c_str::CStr, @@ -25,6 +25,7 @@ use crate::{ }, ld_so, platform::{self, types::*, Pal, Sys}, + sync::Once, }; mod rand48; @@ -44,8 +45,11 @@ static mut ATEXIT_FUNCS: [Option<extern "C" fn()>; 32] = [None; 32]; static mut L64A_BUFFER: [c_char; 7] = [0; 7]; // up to 6 digits plus null terminator static mut RNG: Option<XorShiftRng> = None; -lazy_static! { - static ref RNG_SAMPLER: Uniform<c_int> = Uniform::new_inclusive(0, RAND_MAX); +// TODO: This could be const fn, but the trait system won't allow that. +static RNG_SAMPLER: Once<Uniform<c_int>> = Once::new(); + +fn rng_sampler() -> &'static Uniform<c_int> { + RNG_SAMPLER.call_once(|| Uniform::new_inclusive(0, RAND_MAX)) } #[no_mangle] @@ -131,55 +135,53 @@ pub unsafe extern "C" fn atof(s: *const c_char) -> c_double { } macro_rules! dec_num_from_ascii { - ($s:expr, $t:ty) => { - unsafe { - let mut s = $s; - // Iterate past whitespace - while ctype::isspace(*s as c_int) != 0 { + ($s:expr, $t:ty) => {{ + let mut s = $s; + // Iterate past whitespace + while ctype::isspace(*s as c_int) != 0 { + s = s.offset(1); + } + + // Find out if there is a - sign + let neg_sign = match *s { + 0x2d => { s = s.offset(1); + true } - - // Find out if there is a - sign - let neg_sign = match *s { - 0x2d => { - s = s.offset(1); - true - } - // '+' increment s and continue parsing - 0x2b => { - s = s.offset(1); - false - } - _ => false, - }; - - let mut n: $t = 0; - while ctype::isdigit(*s as c_int) != 0 { - n = 10 * n - (*s as $t - 0x30); + // '+' increment s and continue parsing + 0x2b => { s = s.offset(1); + false } + _ => false, + }; - if neg_sign { - n - } else { - -n - } + let mut n: $t = 0; + while ctype::isdigit(*s as c_int) != 0 { + n = 10 * n - (*s as $t - 0x30); + s = s.offset(1); } - }; + + if neg_sign { + n + } else { + -n + } + }}; } #[no_mangle] -pub extern "C" fn atoi(s: *const c_char) -> c_int { +pub unsafe extern "C" fn atoi(s: *const c_char) -> c_int { dec_num_from_ascii!(s, c_int) } #[no_mangle] -pub extern "C" fn atol(s: *const c_char) -> c_long { +pub unsafe extern "C" fn atol(s: *const c_char) -> c_long { dec_num_from_ascii!(s, c_long) } #[no_mangle] -pub extern "C" fn atoll(s: *const c_char) -> c_longlong { +pub unsafe extern "C" fn atoll(s: *const c_char) -> c_longlong { dec_num_from_ascii!(s, c_longlong) } @@ -643,12 +645,16 @@ fn get_nstime() -> u64 { } #[no_mangle] -pub extern "C" fn mkostemps(name: *mut c_char, suffix_len: c_int, mut flags: c_int) -> c_int { +pub unsafe extern "C" fn mkostemps( + name: *mut c_char, + suffix_len: c_int, + mut flags: c_int, +) -> c_int { flags &= !O_ACCMODE; flags |= O_RDWR | O_CREAT | O_EXCL; inner_mktemp(name, suffix_len, || { - let name = unsafe { CStr::from_ptr(name) }; + let name = CStr::from_ptr(name); let fd = Sys::open(name, flags, 0o600); if fd >= 0 { @@ -661,15 +667,15 @@ pub extern "C" fn mkostemps(name: *mut c_char, suffix_len: c_int, mut flags: c_i } #[no_mangle] -pub extern "C" fn mkstemp(name: *mut c_char) -> c_int { +pub unsafe extern "C" fn mkstemp(name: *mut c_char) -> c_int { mkostemps(name, 0, 0) } #[no_mangle] -pub extern "C" fn mkostemp(name: *mut c_char, flags: c_int) -> c_int { +pub unsafe extern "C" fn mkostemp(name: *mut c_char, flags: c_int) -> c_int { mkostemps(name, 0, flags) } #[no_mangle] -pub extern "C" fn mkstemps(name: *mut c_char, suffix_len: c_int) -> c_int { +pub unsafe extern "C" fn mkstemps(name: *mut c_char, suffix_len: c_int) -> c_int { mkostemps(name, suffix_len, 0) } @@ -744,7 +750,7 @@ pub unsafe extern "C" fn putenv(insert: *mut c_char) -> c_int { } #[no_mangle] -pub extern "C" fn qsort( +pub unsafe extern "C" fn qsort( base: *mut c_void, nel: size_t, width: size_t, @@ -763,10 +769,10 @@ pub extern "C" fn qsort( #[no_mangle] pub unsafe extern "C" fn rand() -> c_int { match RNG { - Some(ref mut rng) => RNG_SAMPLER.sample(rng), + Some(ref mut rng) => rng_sampler().sample(rng), None => { let mut rng = XorShiftRng::from_seed([1; 16]); - let ret = RNG_SAMPLER.sample(&mut rng); + let ret = rng_sampler().sample(&mut rng); RNG = Some(rng); ret } @@ -782,7 +788,7 @@ pub unsafe extern "C" fn rand_r(seed: *mut c_uint) -> c_int { let seed_arr: [u8; 16] = mem::transmute([*seed; 16 / mem::size_of::<c_uint>()]); let mut rng = XorShiftRng::from_seed(seed_arr); - let ret = RNG_SAMPLER.sample(&mut rng); + let ret = rng_sampler().sample(&mut rng); *seed = ret as _; @@ -861,7 +867,7 @@ pub unsafe extern "C" fn realpath(pathname: *const c_char, resolved: *mut c_char let out = slice::from_raw_parts_mut(ptr as *mut u8, limits::PATH_MAX); { - let file = match File::open(&CStr::from_ptr(pathname), O_PATH | O_CLOEXEC) { + let file = match File::open(CStr::from_ptr(pathname), O_PATH | O_CLOEXEC) { Ok(file) => file, Err(_) => return ptr::null_mut(), }; @@ -945,7 +951,7 @@ pub unsafe extern "C" fn setenv( } // #[no_mangle] -pub extern "C" fn setkey(key: *const c_char) { +pub unsafe extern "C" fn setkey(key: *const c_char) { unimplemented!(); } diff --git a/src/ld_so/dso.rs b/src/ld_so/dso.rs index 3161c3af9a79b42380ade46ca395fc3c250644f3..7dfb5482c520660f7631fa3ce815557e4c13ac32 100644 --- a/src/ld_so/dso.rs +++ b/src/ld_so/dso.rs @@ -18,17 +18,17 @@ use core::{ }; #[cfg(target_pointer_width = "32")] use goblin::elf32::{ + dynamic::{Dyn, DT_DEBUG, DT_RUNPATH}, header::ET_DYN, program_header, - r#dyn::{Dyn, DT_DEBUG, DT_RUNPATH}, section_header::{SHN_UNDEF, SHT_FINI_ARRAY, SHT_INIT_ARRAY}, sym, }; #[cfg(target_pointer_width = "64")] use goblin::elf64::{ + dynamic::{Dyn, DT_DEBUG, DT_RUNPATH}, header::ET_DYN, program_header, - r#dyn::{Dyn, DT_DEBUG, DT_RUNPATH}, section_header::{SHN_UNDEF, SHT_FINI_ARRAY, SHT_INIT_ARRAY}, sym, }; diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs index acf22736aef5b1fb1c97565d3fef63e67da9cee0..6e76e53b848405f100cf0ebd4c357316ca2cbdb4 100644 --- a/src/ld_so/linker.rs +++ b/src/ld_so/linker.rs @@ -11,7 +11,7 @@ use goblin::{ }; use crate::{ - c_str::CString, + c_str::{CStr, CString}, fs::File, header::{ dl_tls::{__tls_get_addr, dl_tls_index}, @@ -293,7 +293,7 @@ impl Linker { let path_c = CString::new(path) .map_err(|err| Error::Malformed(format!("invalid path '{}': {}", path, err)))?; let flags = fcntl::O_RDONLY | fcntl::O_CLOEXEC; - let mut file = File::open(&path_c, flags) + let mut file = File::open(CStr::borrow(&path_c), flags) .map_err(|err| Error::Malformed(format!("failed to open '{}': {}", path, err)))?; file.read_to_end(&mut data) .map_err(|err| Error::Malformed(format!("failed to read '{}': {}", path, err)))?; diff --git a/src/lib.rs b/src/lib.rs index fcddb73f0cdea430c738cde56a59cd0b1235b76b..7b04c82c52cbf842d945b06c78bb163de49ce9be 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ #![feature(linkage)] #![feature(stmt_expr_attributes)] #![feature(str_internals)] +#![feature(sync_unsafe_cell)] #![feature(thread_local)] #![feature(vec_into_raw_parts)] #![allow(clippy::cast_lossless)] @@ -28,8 +29,6 @@ extern crate alloc; extern crate cbitset; extern crate core_io; extern crate goblin; -#[macro_use] -extern crate lazy_static; extern crate memchr; #[macro_use] extern crate memoffset; @@ -43,9 +42,6 @@ extern crate sc; #[cfg(target_os = "redox")] extern crate syscall; -#[cfg(target_os = "redox")] -extern crate spin; - #[macro_use] mod macros; pub mod c_str; diff --git a/src/platform/linux/mod.rs b/src/platform/linux/mod.rs index 5bdb26f87a97dba2e72db6c100d3996eec60ea1e..5d28143139a8153e3aa4ae14fd1239ff15dd6b49 100644 --- a/src/platform/linux/mod.rs +++ b/src/platform/linux/mod.rs @@ -89,7 +89,7 @@ impl Sys { } impl Pal for Sys { - fn access(path: &CStr, mode: c_int) -> c_int { + fn access(path: CStr, mode: c_int) -> c_int { e(unsafe { syscall!(ACCESS, path.as_ptr(), mode) }) as c_int } @@ -97,15 +97,15 @@ impl Pal for Sys { unsafe { syscall!(BRK, addr) as *mut c_void } } - fn chdir(path: &CStr) -> c_int { + fn chdir(path: CStr) -> c_int { e(unsafe { syscall!(CHDIR, path.as_ptr()) }) as c_int } - fn chmod(path: &CStr, mode: mode_t) -> c_int { + fn chmod(path: CStr, mode: mode_t) -> c_int { e(unsafe { syscall!(FCHMODAT, AT_FDCWD, path.as_ptr(), mode, 0) }) as c_int } - fn chown(path: &CStr, owner: uid_t, group: gid_t) -> c_int { + fn chown(path: CStr, owner: uid_t, group: gid_t) -> c_int { e(unsafe { syscall!( FCHOWNAT, @@ -141,7 +141,7 @@ impl Pal for Sys { e(unsafe { syscall!(DUP3, fildes, fildes2, 0) }) as c_int } - unsafe fn execve(path: &CStr, argv: *const *mut c_char, envp: *const *mut c_char) -> c_int { + unsafe fn execve(path: CStr, argv: *const *mut c_char, envp: *const *mut c_char) -> c_int { e(syscall!(EXECVE, path.as_ptr(), argv, envp)) as c_int } unsafe fn fexecve(fildes: c_int, argv: *const *mut c_char, envp: *const *mut c_char) -> c_int { @@ -252,7 +252,7 @@ impl Pal for Sys { e(unsafe { syscall!(UTIMENSAT, fd, ptr::null::<c_char>(), times, 0) }) as c_int } - fn utimens(path: &CStr, times: *const timespec) -> c_int { + fn utimens(path: CStr, times: *const timespec) -> c_int { e(unsafe { syscall!(UTIMENSAT, AT_FDCWD, path.as_ptr(), times, 0) }) as c_int } @@ -332,11 +332,11 @@ impl Pal for Sys { e(unsafe { syscall!(GETUID) }) as uid_t } - fn lchown(path: &CStr, owner: uid_t, group: gid_t) -> c_int { + fn lchown(path: CStr, owner: uid_t, group: gid_t) -> c_int { e(unsafe { syscall!(LCHOWN, path.as_ptr(), owner, group) }) as c_int } - fn link(path1: &CStr, path2: &CStr) -> c_int { + fn link(path1: CStr, path2: CStr) -> c_int { e(unsafe { syscall!( LINKAT, @@ -353,11 +353,11 @@ impl Pal for Sys { e(unsafe { syscall!(LSEEK, fildes, offset, whence) }) as off_t } - fn mkdir(path: &CStr, mode: mode_t) -> c_int { + fn mkdir(path: CStr, mode: mode_t) -> c_int { e(unsafe { syscall!(MKDIRAT, AT_FDCWD, path.as_ptr(), mode) }) as c_int } - fn mkfifo(path: &CStr, mode: mode_t) -> c_int { + fn mkfifo(path: CStr, mode: mode_t) -> c_int { e(unsafe { syscall!(MKNODAT, AT_FDCWD, path.as_ptr(), mode | S_IFIFO, 0) }) as c_int } @@ -408,7 +408,7 @@ impl Pal for Sys { e(unsafe { syscall!(NANOSLEEP, rqtp, rmtp) }) as c_int } - fn open(path: &CStr, oflag: c_int, mode: mode_t) -> c_int { + fn open(path: CStr, oflag: c_int, mode: mode_t) -> c_int { e(unsafe { syscall!(OPENAT, AT_FDCWD, path.as_ptr(), oflag, mode) }) as c_int } @@ -491,7 +491,7 @@ impl Pal for Sys { e(unsafe { syscall!(READ, fildes, buf.as_mut_ptr(), buf.len()) }) as ssize_t } - fn readlink(pathname: &CStr, out: &mut [u8]) -> ssize_t { + fn readlink(pathname: CStr, out: &mut [u8]) -> ssize_t { e(unsafe { syscall!( READLINKAT, @@ -503,11 +503,11 @@ impl Pal for Sys { }) as ssize_t } - fn rename(old: &CStr, new: &CStr) -> c_int { + fn rename(old: CStr, new: CStr) -> c_int { e(unsafe { syscall!(RENAMEAT, AT_FDCWD, old.as_ptr(), AT_FDCWD, new.as_ptr()) }) as c_int } - fn rmdir(path: &CStr) -> c_int { + fn rmdir(path: CStr) -> c_int { e(unsafe { syscall!(UNLINKAT, AT_FDCWD, path.as_ptr(), AT_REMOVEDIR) }) as c_int } @@ -539,7 +539,7 @@ impl Pal for Sys { e(unsafe { syscall!(SETSID) }) as c_int } - fn symlink(path1: &CStr, path2: &CStr) -> c_int { + fn symlink(path1: CStr, path2: CStr) -> c_int { e(unsafe { syscall!(SYMLINKAT, path1.as_ptr(), AT_FDCWD, path2.as_ptr()) }) as c_int } @@ -555,7 +555,7 @@ impl Pal for Sys { e(unsafe { syscall!(UNAME, utsname, 0) }) as c_int } - fn unlink(path: &CStr) -> c_int { + fn unlink(path: CStr) -> c_int { e(unsafe { syscall!(UNLINKAT, AT_FDCWD, path.as_ptr(), 0) }) as c_int } diff --git a/src/platform/pal/mod.rs b/src/platform/pal/mod.rs index d4268820d941a18a95c2cf8cb0dd62395d1f7f33..5de748d85d2287e5071c82c140b044ec26b03e8a 100644 --- a/src/platform/pal/mod.rs +++ b/src/platform/pal/mod.rs @@ -25,15 +25,15 @@ pub use self::socket::PalSocket; mod socket; pub trait Pal { - fn access(path: &CStr, mode: c_int) -> c_int; + fn access(path: CStr, mode: c_int) -> c_int; fn brk(addr: *mut c_void) -> *mut c_void; - fn chdir(path: &CStr) -> c_int; + fn chdir(path: CStr) -> c_int; - fn chmod(path: &CStr, mode: mode_t) -> c_int; + fn chmod(path: CStr, mode: mode_t) -> c_int; - fn chown(path: &CStr, owner: uid_t, group: gid_t) -> c_int; + fn chown(path: CStr, owner: uid_t, group: gid_t) -> c_int; fn clock_getres(clk_id: clockid_t, tp: *mut timespec) -> c_int; @@ -47,7 +47,7 @@ pub trait Pal { fn dup2(fildes: c_int, fildes2: c_int) -> c_int; - unsafe fn execve(path: &CStr, argv: *const *mut c_char, envp: *const *mut c_char) -> c_int; + unsafe fn execve(path: CStr, argv: *const *mut c_char, envp: *const *mut c_char) -> c_int; unsafe fn fexecve(fildes: c_int, argv: *const *mut c_char, envp: *const *mut c_char) -> c_int; fn exit(status: c_int) -> !; @@ -87,7 +87,7 @@ pub trait Pal { fn futimens(fd: c_int, times: *const timespec) -> c_int; - fn utimens(path: &CStr, times: *const timespec) -> c_int; + fn utimens(path: CStr, times: *const timespec) -> c_int; fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char; @@ -128,15 +128,15 @@ pub trait Pal { fn getuid() -> uid_t; - fn lchown(path: &CStr, owner: uid_t, group: gid_t) -> c_int; + fn lchown(path: CStr, owner: uid_t, group: gid_t) -> c_int; - fn link(path1: &CStr, path2: &CStr) -> c_int; + fn link(path1: CStr, path2: CStr) -> c_int; fn lseek(fildes: c_int, offset: off_t, whence: c_int) -> off_t; - fn mkdir(path: &CStr, mode: mode_t) -> c_int; + fn mkdir(path: CStr, mode: mode_t) -> c_int; - fn mkfifo(path: &CStr, mode: mode_t) -> c_int; + fn mkfifo(path: CStr, mode: mode_t) -> c_int; unsafe fn mlock(addr: *const c_void, len: usize) -> c_int; @@ -165,7 +165,7 @@ pub trait Pal { fn nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> c_int; - fn open(path: &CStr, oflag: c_int, mode: mode_t) -> c_int; + fn open(path: CStr, oflag: c_int, mode: mode_t) -> c_int; fn pipe2(fildes: &mut [c_int], flags: c_int) -> c_int; @@ -179,11 +179,11 @@ pub trait Pal { fn read(fildes: c_int, buf: &mut [u8]) -> ssize_t; - fn readlink(pathname: &CStr, out: &mut [u8]) -> ssize_t; + fn readlink(pathname: CStr, out: &mut [u8]) -> ssize_t; - fn rename(old: &CStr, new: &CStr) -> c_int; + fn rename(old: CStr, new: CStr) -> c_int; - fn rmdir(path: &CStr) -> c_int; + fn rmdir(path: CStr) -> c_int; fn sched_yield() -> c_int; @@ -199,7 +199,7 @@ pub trait Pal { fn setsid() -> c_int; - fn symlink(path1: &CStr, path2: &CStr) -> c_int; + fn symlink(path1: CStr, path2: CStr) -> c_int; fn sync() -> c_int; @@ -207,7 +207,7 @@ pub trait Pal { fn uname(utsname: *mut utsname) -> c_int; - fn unlink(path: &CStr) -> c_int; + fn unlink(path: CStr) -> c_int; fn waitpid(pid: pid_t, stat_loc: *mut c_int, options: c_int) -> pid_t; diff --git a/src/platform/redox/exec.rs b/src/platform/redox/exec.rs index f0375437300a5891905893efe17d55d9b36a0963..5379ea788285f416a424444ba2780a892f3dbbe1 100644 --- a/src/platform/redox/exec.rs +++ b/src/platform/redox/exec.rs @@ -81,7 +81,7 @@ pub enum ArgEnv<'a> { } pub enum Executable<'a> { - AtPath(&'a CStr), + AtPath(CStr<'a>), InFd { file: File, arg0: &'a [u8] }, } @@ -187,7 +187,8 @@ pub fn execve( interpreter.pop().unwrap(); } let cstring = CString::new(interpreter).map_err(|_| Error::new(ENOEXEC))?; - image_file = File::open(&cstring, O_RDONLY as c_int).map_err(|_| Error::new(ENOENT))?; + image_file = File::open(CStr::borrow(&cstring), O_RDONLY as c_int) + .map_err(|_| Error::new(ENOENT))?; // Make sure path is kept alive long enough, and push it to the arguments _interpreter_path = Some(cstring); diff --git a/src/platform/redox/mod.rs b/src/platform/redox/mod.rs index a532de7fbbbc7f5b44038e1975d390311660e93d..56b2053796e24b25b79e4485e0edf146ff660356 100644 --- a/src/platform/redox/mod.rs +++ b/src/platform/redox/mod.rs @@ -80,7 +80,7 @@ pub fn e(sys: Result<usize>) -> usize { pub struct Sys; impl Pal for Sys { - fn access(path: &CStr, mode: c_int) -> c_int { + fn access(path: CStr, mode: c_int) -> c_int { let fd = match File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC) { Ok(fd) => fd, Err(_) => return -1, @@ -165,19 +165,19 @@ impl Pal for Sys { } } - fn chdir(path: &CStr) -> c_int { + fn chdir(path: CStr) -> c_int { let path = path_from_c_str!(path); e(path::chdir(path).map(|()| 0)) as c_int } - fn chmod(path: &CStr, mode: mode_t) -> c_int { + fn chmod(path: CStr, mode: mode_t) -> c_int { match File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC) { Ok(file) => Self::fchmod(*file, mode), Err(_) => -1, } } - fn chown(path: &CStr, owner: uid_t, group: gid_t) -> c_int { + fn chown(path: CStr, owner: uid_t, group: gid_t) -> c_int { match File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC) { Ok(file) => Self::fchown(*file, owner, group), Err(_) => -1, @@ -232,7 +232,7 @@ impl Pal for Sys { loop {} } - unsafe fn execve(path: &CStr, argv: *const *mut c_char, envp: *const *mut c_char) -> c_int { + unsafe fn execve(path: CStr, argv: *const *mut c_char, envp: *const *mut c_char) -> c_int { e(self::exec::execve( Executable::AtPath(path), self::exec::ArgEnv::C { argv, envp }, @@ -257,7 +257,7 @@ impl Pal for Sys { !0 } else { match str::from_utf8(&buf[..res]) { - Ok(path) => Sys::chdir(&CString::new(path).unwrap()), + Ok(path) => e(path::chdir(path).map(|()| 0)) as c_int, Err(_) => { unsafe { errno = EINVAL }; return -1; @@ -387,7 +387,7 @@ impl Pal for Sys { e(syscall::futimens(fd as usize, ×)) as c_int } - fn utimens(path: &CStr, times: *const timespec) -> c_int { + fn utimens(path: CStr, times: *const timespec) -> c_int { match File::open(path, fcntl::O_PATH | fcntl::O_CLOEXEC) { Ok(file) => Self::futimens(*file, times), Err(_) => -1, @@ -637,7 +637,7 @@ impl Pal for Sys { e(syscall::getuid()) as pid_t } - fn lchown(path: &CStr, owner: uid_t, group: gid_t) -> c_int { + fn lchown(path: CStr, owner: uid_t, group: gid_t) -> c_int { // TODO: Is it correct for regular chown to use O_PATH? On Linux the meaning of that flag // is to forbid file operations, including fchown. @@ -648,7 +648,7 @@ impl Pal for Sys { } } - fn link(path1: &CStr, path2: &CStr) -> c_int { + fn link(path1: CStr, path2: CStr) -> c_int { e(unsafe { syscall::link(path1.as_ptr() as *const u8, path2.as_ptr() as *const u8) }) as c_int } @@ -661,7 +661,7 @@ impl Pal for Sys { )) as off_t } - fn mkdir(path: &CStr, mode: mode_t) -> c_int { + fn mkdir(path: CStr, mode: mode_t) -> c_int { match File::create( path, fcntl::O_DIRECTORY | fcntl::O_EXCL | fcntl::O_CLOEXEC, @@ -672,7 +672,7 @@ impl Pal for Sys { } } - fn mkfifo(path: &CStr, mode: mode_t) -> c_int { + fn mkfifo(path: CStr, mode: mode_t) -> c_int { match File::create( path, fcntl::O_CREAT | fcntl::O_CLOEXEC, @@ -788,7 +788,7 @@ impl Pal for Sys { } } - fn open(path: &CStr, oflag: c_int, mode: mode_t) -> c_int { + fn open(path: CStr, oflag: c_int, mode: mode_t) -> c_int { let path = path_from_c_str!(path); e(libredox::open(path, oflag, mode)) as c_int @@ -828,7 +828,7 @@ impl Pal for Sys { e(syscall::fpath(fildes as usize, out)) as ssize_t } - fn readlink(pathname: &CStr, out: &mut [u8]) -> ssize_t { + fn readlink(pathname: CStr, out: &mut [u8]) -> ssize_t { match File::open( pathname, fcntl::O_RDONLY | fcntl::O_SYMLINK | fcntl::O_CLOEXEC, @@ -838,7 +838,7 @@ impl Pal for Sys { } } - fn rename(oldpath: &CStr, newpath: &CStr) -> c_int { + fn rename(oldpath: CStr, newpath: CStr) -> c_int { let newpath = path_from_c_str!(newpath); match File::open(oldpath, fcntl::O_PATH | fcntl::O_CLOEXEC) { Ok(file) => e(syscall::frename(*file as usize, newpath)) as c_int, @@ -846,7 +846,7 @@ impl Pal for Sys { } } - fn rmdir(path: &CStr) -> c_int { + fn rmdir(path: CStr) -> c_int { let path = path_from_c_str!(path); e(canonicalize(path).and_then(|path| syscall::rmdir(&path))) as c_int } @@ -891,7 +891,7 @@ impl Pal for Sys { e(syscall::setreuid(ruid as usize, euid as usize)) as c_int } - fn symlink(path1: &CStr, path2: &CStr) -> c_int { + fn symlink(path1: CStr, path2: CStr) -> c_int { let mut file = match File::create( path2, fcntl::O_WRONLY | fcntl::O_SYMLINK | fcntl::O_CLOEXEC, @@ -922,10 +922,7 @@ impl Pal for Sys { return Ok(()); } - let mut file = File::open( - &CString::new("/etc/hostname").unwrap(), - fcntl::O_RDONLY | fcntl::O_CLOEXEC, - )?; + let mut file = File::open(c_str!("/etc/hostname"), fcntl::O_RDONLY | fcntl::O_CLOEXEC)?; let mut read = 0; let name_len = name.len(); @@ -1001,7 +998,7 @@ impl Pal for Sys { } } - fn unlink(path: &CStr) -> c_int { + fn unlink(path: CStr) -> c_int { let path = path_from_c_str!(path); e(canonicalize(path).and_then(|path| syscall::unlink(&path))) as c_int } diff --git a/src/platform/redox/ptrace.rs b/src/platform/redox/ptrace.rs index 3d8dba4f329d4cd6e1103d439e3dbcbbf49e9501..7013afd07d3e81baebbdc77cd2eff6142496cb6a 100644 --- a/src/platform/redox/ptrace.rs +++ b/src/platform/redox/ptrace.rs @@ -10,7 +10,7 @@ use crate::header::arch_aarch64_user::user_regs_struct; #[cfg(target_arch = "x86_64")] use crate::header::arch_x64_user::user_regs_struct; use crate::{ - c_str::CString, + c_str::{CStr, CString}, fs::File, header::{errno as errnoh, fcntl, signal, sys_ptrace}, io::{self, prelude::*}, @@ -57,7 +57,7 @@ pub fn is_traceme(pid: pid_t) -> bool { return false; } File::open( - &CString::new(format!("chan:ptrace-relibc/{}/traceme", pid)).unwrap(), + CStr::borrow(&CString::new(format!("chan:ptrace-relibc/{}/traceme", pid)).unwrap()), fcntl::O_PATH, ) .is_ok() @@ -74,19 +74,19 @@ pub fn get_session( Ok(entry.insert(Session { first: true, tracer: File::open( - &CString::new(format!("proc:{}/trace", pid)).unwrap(), + CStr::borrow(&CString::new(format!("proc:{}/trace", pid)).unwrap()), NEW_FLAGS, )?, mem: File::open( - &CString::new(format!("proc:{}/mem", pid)).unwrap(), + CStr::borrow(&CString::new(format!("proc:{}/mem", pid)).unwrap()), NEW_FLAGS, )?, regs: File::open( - &CString::new(format!("proc:{}/regs/int", pid)).unwrap(), + CStr::borrow(&CString::new(format!("proc:{}/regs/int", pid)).unwrap()), NEW_FLAGS, )?, fpregs: File::open( - &CString::new(format!("proc:{}/regs/float", pid)).unwrap(), + CStr::borrow(&CString::new(format!("proc:{}/regs/float", pid)).unwrap()), NEW_FLAGS, )?, })) @@ -136,7 +136,7 @@ fn inner_ptrace( // Mark this child as traced, parent will check for this marker file let pid = Sys::getpid(); mem::forget(File::open( - &CString::new(format!("chan:ptrace-relibc/{}/traceme", pid)).unwrap(), + CStr::borrow(&CString::new(format!("chan:ptrace-relibc/{}/traceme", pid)).unwrap()), fcntl::O_CREAT | fcntl::O_PATH | fcntl::O_EXCL, )?); return Ok(0); diff --git a/src/platform/redox/redox-exec/Cargo.toml b/src/platform/redox/redox-exec/Cargo.toml index 1a47f79812f1e2925bf8ad5fcacfa34c54a47589..b2f9570d7d2c262a245ac75d9e8a981e66933941 100644 --- a/src/platform/redox/redox-exec/Cargo.toml +++ b/src/platform/redox/redox-exec/Cargo.toml @@ -9,6 +9,5 @@ license = "MIT" [dependencies] redox_syscall = "0.4" -# TODO: Update -goblin = { version = "0.0.21", default-features = false, features = ["elf32", "elf64", "endian_fd"] } +goblin = { version = "0.7", default-features = false, features = ["elf32", "elf64", "endian_fd"] } plain = "0.2" diff --git a/src/start.rs b/src/start.rs index 360b54ff60a82eef88fbc3a99977ab6c74203745..5c3d0fd203734074bfbf547ab5169d0cc4b02659 100644 --- a/src/start.rs +++ b/src/start.rs @@ -119,10 +119,11 @@ extern "C" fn init_array() { } fn io_init() { unsafe { - // Initialize stdin/stdout/stderr, see https://github.com/rust-lang/rust/issues/51718 - stdio::stdin = stdio::default_stdin.get(); - stdio::stdout = stdio::default_stdout.get(); - stdio::stderr = stdio::default_stderr.get(); + // Initialize stdin/stdout/stderr. + // TODO: const fn initialization of FILE + stdio::stdin = stdio::default_stdin().get(); + stdio::stdout = stdio::default_stdout().get(); + stdio::stderr = stdio::default_stderr().get(); } }