From 4038ca0965151b42afa4692b23b2eeaa4236a29f Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 7 Jan 2022 18:11:59 -0800 Subject: [PATCH 01/18] Refactor tests of Write for Cursor<_> --- library/std/src/io/cursor/tests.rs | 72 ++++++++++++------------------ 1 file changed, 28 insertions(+), 44 deletions(-) diff --git a/library/std/src/io/cursor/tests.rs b/library/std/src/io/cursor/tests.rs index 5da31ce0ba761..2d0836d9a8595 100644 --- a/library/std/src/io/cursor/tests.rs +++ b/library/std/src/io/cursor/tests.rs @@ -50,9 +50,11 @@ fn test_mem_mut_writer() { assert_eq!(&writer.get_ref()[..], b); } -#[test] -fn test_box_slice_writer() { - let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice()); +fn test_slice_writer(writer: &mut Cursor) +where + T: AsRef<[u8]>, + Cursor: Write, +{ assert_eq!(writer.position(), 0); assert_eq!(writer.write(&[0]).unwrap(), 1); assert_eq!(writer.position(), 1); @@ -65,12 +67,14 @@ fn test_box_slice_writer() { assert_eq!(writer.write(&[8, 9]).unwrap(), 1); assert_eq!(writer.write(&[10]).unwrap(), 0); let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; - assert_eq!(&**writer.get_ref(), b); + assert_eq!(writer.get_ref().as_ref(), b); } -#[test] -fn test_box_slice_writer_vectored() { - let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice()); +fn test_slice_writer_vectored(writer: &mut Cursor) +where + T: AsRef<[u8]>, + Cursor: Write, +{ assert_eq!(writer.position(), 0); assert_eq!(writer.write_vectored(&[IoSlice::new(&[0])]).unwrap(), 1); assert_eq!(writer.position(), 1); @@ -85,53 +89,33 @@ fn test_box_slice_writer_vectored() { assert_eq!(writer.write_vectored(&[IoSlice::new(&[8, 9])]).unwrap(), 1); assert_eq!(writer.write_vectored(&[IoSlice::new(&[10])]).unwrap(), 0); let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; - assert_eq!(&**writer.get_ref(), b); + assert_eq!(writer.get_ref().as_ref(), b); +} + +#[test] +fn test_box_slice_writer() { + let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice()); + test_slice_writer(&mut writer); +} + +#[test] +fn test_box_slice_writer_vectored() { + let mut writer = Cursor::new(vec![0u8; 9].into_boxed_slice()); + test_slice_writer_vectored(&mut writer); } #[test] fn test_buf_writer() { let mut buf = [0 as u8; 9]; - { - let mut writer = Cursor::new(&mut buf[..]); - assert_eq!(writer.position(), 0); - assert_eq!(writer.write(&[0]).unwrap(), 1); - assert_eq!(writer.position(), 1); - assert_eq!(writer.write(&[1, 2, 3]).unwrap(), 3); - assert_eq!(writer.write(&[4, 5, 6, 7]).unwrap(), 4); - assert_eq!(writer.position(), 8); - assert_eq!(writer.write(&[]).unwrap(), 0); - assert_eq!(writer.position(), 8); - - assert_eq!(writer.write(&[8, 9]).unwrap(), 1); - assert_eq!(writer.write(&[10]).unwrap(), 0); - } - let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; - assert_eq!(buf, b); + let mut writer = Cursor::new(&mut buf[..]); + test_slice_writer(&mut writer); } #[test] fn test_buf_writer_vectored() { let mut buf = [0 as u8; 9]; - { - let mut writer = Cursor::new(&mut buf[..]); - assert_eq!(writer.position(), 0); - assert_eq!(writer.write_vectored(&[IoSlice::new(&[0])]).unwrap(), 1); - assert_eq!(writer.position(), 1); - assert_eq!( - writer - .write_vectored(&[IoSlice::new(&[1, 2, 3]), IoSlice::new(&[4, 5, 6, 7])],) - .unwrap(), - 7, - ); - assert_eq!(writer.position(), 8); - assert_eq!(writer.write_vectored(&[]).unwrap(), 0); - assert_eq!(writer.position(), 8); - - assert_eq!(writer.write_vectored(&[IoSlice::new(&[8, 9])]).unwrap(), 1); - assert_eq!(writer.write_vectored(&[IoSlice::new(&[10])]).unwrap(), 0); - } - let b: &[_] = &[0, 1, 2, 3, 4, 5, 6, 7, 8]; - assert_eq!(buf, b); + let mut writer = Cursor::new(&mut buf[..]); + test_slice_writer_vectored(&mut writer); } #[test] From a04d553e882196146aaa632cf00877c4af37d447 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 19 Jan 2022 15:57:36 -0800 Subject: [PATCH 02/18] impl Write for Cursor<[u8; N]> --- library/std/src/io/cursor.rs | 23 +++++++++++++++++++++++ library/std/src/io/cursor/tests.rs | 12 ++++++++++++ 2 files changed, 35 insertions(+) diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 416cc906e65a5..7670a8592925f 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -525,3 +525,26 @@ impl Write for Cursor> { Ok(()) } } + +#[stable(feature = "cursor_array", since = "1.60.0")] +impl Write for Cursor<[u8; N]> { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + slice_write(&mut self.pos, &mut self.inner, buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + slice_write_vectored(&mut self.pos, &mut self.inner, bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} diff --git a/library/std/src/io/cursor/tests.rs b/library/std/src/io/cursor/tests.rs index 2d0836d9a8595..f1ee177b7f3bc 100644 --- a/library/std/src/io/cursor/tests.rs +++ b/library/std/src/io/cursor/tests.rs @@ -104,6 +104,18 @@ fn test_box_slice_writer_vectored() { test_slice_writer_vectored(&mut writer); } +#[test] +fn test_array_writer() { + let mut writer = Cursor::new([0u8; 9]); + test_slice_writer(&mut writer); +} + +#[test] +fn test_array_writer_vectored() { + let mut writer = Cursor::new([0u8; 9]); + test_slice_writer_vectored(&mut writer); +} + #[test] fn test_buf_writer() { let mut buf = [0 as u8; 9]; From 38cef656d8df6e77630f923e2e8e20862adfc4e4 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 19 Jan 2022 16:07:14 -0800 Subject: [PATCH 03/18] Write for Cursor with a custom Allocator --- library/std/src/io/cursor.rs | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 7670a8592925f..ea22ebd3e68e8 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -3,6 +3,7 @@ mod tests; use crate::io::prelude::*; +use crate::alloc::Allocator; use crate::cmp; use crate::io::{self, Error, ErrorKind, IoSlice, IoSliceMut, ReadBuf, SeekFrom}; @@ -398,7 +399,10 @@ fn slice_write_vectored( } // Resizing write implementation -fn vec_write(pos_mut: &mut u64, vec: &mut Vec, buf: &[u8]) -> io::Result { +fn vec_write(pos_mut: &mut u64, vec: &mut Vec, buf: &[u8]) -> io::Result +where + A: Allocator, +{ let pos: usize = (*pos_mut).try_into().map_err(|_| { Error::new_const( ErrorKind::InvalidInput, @@ -426,11 +430,14 @@ fn vec_write(pos_mut: &mut u64, vec: &mut Vec, buf: &[u8]) -> io::Result( pos_mut: &mut u64, - vec: &mut Vec, + vec: &mut Vec, bufs: &[IoSlice<'_>], -) -> io::Result { +) -> io::Result +where + A: Allocator, +{ let mut nwritten = 0; for buf in bufs { nwritten += vec_write(pos_mut, vec, buf)?; @@ -462,7 +469,10 @@ impl Write for Cursor<&mut [u8]> { } #[stable(feature = "cursor_mut_vec", since = "1.25.0")] -impl Write for Cursor<&mut Vec> { +impl Write for Cursor<&mut Vec> +where + A: Allocator, +{ fn write(&mut self, buf: &[u8]) -> io::Result { vec_write(&mut self.pos, self.inner, buf) } @@ -483,7 +493,10 @@ impl Write for Cursor<&mut Vec> { } #[stable(feature = "rust1", since = "1.0.0")] -impl Write for Cursor> { +impl Write for Cursor> +where + A: Allocator, +{ fn write(&mut self, buf: &[u8]) -> io::Result { vec_write(&mut self.pos, &mut self.inner, buf) } @@ -504,7 +517,10 @@ impl Write for Cursor> { } #[stable(feature = "cursor_box_slice", since = "1.5.0")] -impl Write for Cursor> { +impl Write for Cursor> +where + A: Allocator, +{ #[inline] fn write(&mut self, buf: &[u8]) -> io::Result { slice_write(&mut self.pos, &mut self.inner, buf) From 6706ab8cfbe2bd3d093b5f995f5e59e42a1285c2 Mon Sep 17 00:00:00 2001 From: Muhammad Falak R Wani Date: Sun, 6 Feb 2022 11:23:02 +0530 Subject: [PATCH 04/18] keyword_docs: document use of `in` with `pub` keyword Signed-off-by: Muhammad Falak R Wani --- library/std/src/keyword_docs.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index 35d230eee9627..8733e5190eefe 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -728,6 +728,20 @@ mod impl_keyword {} /// [`IntoIterator`]: ../book/ch13-04-performance.html /// [range patterns]: ../reference/patterns.html?highlight=range#range-patterns /// [`for`]: keyword.for.html +/// +/// The other use of `in` is with the keyword `pub`. It allows users to declare an item as visible +/// only within a given scope. +/// +/// ## Literal Example: +/// +/// * `pub(in crate::outer_mod) fn outer_mod_visible_fn() {}` - fn is visible in `outer_mod` +/// +/// Starting with the 2018 edition, paths for `pub(in path)` must start with `crate`, `self` or +/// `super`. The 2015 edition may also use paths starting with `::` or modules from the crate root. +/// +/// For more information, see the [Reference]. +/// +/// [Reference]: ../reference/visibility-and-privacy.html#pubin-path-pubcrate-pubsuper-and-pubself mod in_keyword {} #[doc(keyword = "let")] From d4686c60669591ea9ae6e4391fc7a6628ce8062a Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Wed, 5 Jan 2022 17:05:58 +0000 Subject: [PATCH 05/18] Use verbatim paths for `process::Command` if necessary --- library/std/src/sys/windows/process.rs | 32 +++++++++++++------- library/std/src/sys/windows/process/tests.rs | 7 +++-- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs index fafd1412d4cb3..e3fd04fe20f7c 100644 --- a/library/std/src/sys/windows/process.rs +++ b/library/std/src/sys/windows/process.rs @@ -19,12 +19,12 @@ use crate::path::{Path, PathBuf}; use crate::ptr; use crate::sys::c; use crate::sys::c::NonZeroDWORD; +use crate::sys::cvt; use crate::sys::fs::{File, OpenOptions}; use crate::sys::handle::Handle; use crate::sys::path; use crate::sys::pipe::{self, AnonPipe}; use crate::sys::stdio; -use crate::sys::{cvt, to_u16s}; use crate::sys_common::mutex::StaticMutex; use crate::sys_common::process::{CommandEnv, CommandEnvs}; use crate::sys_common::{AsInner, IntoInner}; @@ -269,8 +269,13 @@ impl Command { None }; let program = resolve_exe(&self.program, || env::var_os("PATH"), child_paths)?; + let is_batch_file = program + .extension() + .map(|ext| ext.eq_ignore_ascii_case("cmd") || ext.eq_ignore_ascii_case("bat")) + .unwrap_or(false); + let program = path::maybe_verbatim(&program)?; let mut cmd_str = - make_command_line(program.as_os_str(), &self.args, self.force_quotes_enabled)?; + make_command_line(&program, &self.args, self.force_quotes_enabled, is_batch_file)?; cmd_str.push(0); // add null terminator // stolen from the libuv code. @@ -309,7 +314,6 @@ impl Command { si.hStdOutput = stdout.as_raw_handle(); si.hStdError = stderr.as_raw_handle(); - let program = to_u16s(&program)?; unsafe { cvt(c::CreateProcessW( program.as_ptr(), @@ -730,7 +734,12 @@ enum Quote { // Produces a wide string *without terminating null*; returns an error if // `prog` or any of the `args` contain a nul. -fn make_command_line(prog: &OsStr, args: &[Arg], force_quotes: bool) -> io::Result> { +fn make_command_line( + prog: &[u16], + args: &[Arg], + force_quotes: bool, + is_batch_file: bool, +) -> io::Result> { // Encode the command and arguments in a command line string such // that the spawned process may recover them using CommandLineToArgvW. let mut cmd: Vec = Vec::new(); @@ -739,17 +748,18 @@ fn make_command_line(prog: &OsStr, args: &[Arg], force_quotes: bool) -> io::Resu // need to add an extra pair of quotes surrounding the whole command line // so they are properly passed on to the script. // See issue #91991. - let is_batch_file = Path::new(prog) - .extension() - .map(|ext| ext.eq_ignore_ascii_case("cmd") || ext.eq_ignore_ascii_case("bat")) - .unwrap_or(false); if is_batch_file { cmd.push(b'"' as u16); } - // Always quote the program name so CreateProcess doesn't interpret args as - // part of the name if the binary wasn't found first time. - append_arg(&mut cmd, prog, Quote::Always)?; + // Always quote the program name so CreateProcess to avoid ambiguity when + // the child process parses its arguments. + // Note that quotes aren't escaped here because they can't be used in arg0. + // But that's ok because file paths can't contain quotes. + cmd.push(b'"' as u16); + cmd.extend_from_slice(prog.strip_suffix(&[0]).unwrap_or(prog)); + cmd.push(b'"' as u16); + for arg in args { cmd.push(' ' as u16); let (arg, quote) = match arg { diff --git a/library/std/src/sys/windows/process/tests.rs b/library/std/src/sys/windows/process/tests.rs index d18c3d855bcce..a199bb8b5678a 100644 --- a/library/std/src/sys/windows/process/tests.rs +++ b/library/std/src/sys/windows/process/tests.rs @@ -3,11 +3,12 @@ use super::Arg; use crate::env; use crate::ffi::{OsStr, OsString}; use crate::process::Command; +use crate::sys::to_u16s; #[test] fn test_raw_args() { let command_line = &make_command_line( - OsStr::new("quoted exe"), + &to_u16s("quoted exe").unwrap(), &[ Arg::Regular(OsString::from("quote me")), Arg::Raw(OsString::from("quote me *not*")), @@ -16,6 +17,7 @@ fn test_raw_args() { Arg::Regular(OsString::from("optional-quotes")), ], false, + false, ) .unwrap(); assert_eq!( @@ -28,9 +30,10 @@ fn test_raw_args() { fn test_make_command_line() { fn test_wrapper(prog: &str, args: &[&str], force_quotes: bool) -> String { let command_line = &make_command_line( - OsStr::new(prog), + &to_u16s(prog).unwrap(), &args.iter().map(|a| Arg::Regular(OsString::from(a))).collect::>(), force_quotes, + false, ) .unwrap(); String::from_utf16(command_line).unwrap() From 93f627daa53677f76ad50bcfa3c8eb618f5ca89f Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Thu, 17 Feb 2022 13:17:19 +0000 Subject: [PATCH 06/18] Keep the path after `program_exists` succeeds --- library/std/src/sys/windows/process.rs | 45 +++++++++++++------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs index e3fd04fe20f7c..a13585a02224a 100644 --- a/library/std/src/sys/windows/process.rs +++ b/library/std/src/sys/windows/process.rs @@ -269,11 +269,11 @@ impl Command { None }; let program = resolve_exe(&self.program, || env::var_os("PATH"), child_paths)?; - let is_batch_file = program - .extension() - .map(|ext| ext.eq_ignore_ascii_case("cmd") || ext.eq_ignore_ascii_case("bat")) - .unwrap_or(false); - let program = path::maybe_verbatim(&program)?; + // Case insensitive "ends_with" of UTF-16 encoded ".bat" or ".cmd" + let is_batch_file = matches!( + program.len().checked_sub(5).and_then(|i| program.get(i..)), + Some([46, 98 | 66, 97 | 65, 116 | 84, 0] | [46, 99 | 67, 109 | 77, 100 | 68, 0]) + ); let mut cmd_str = make_command_line(&program, &self.args, self.force_quotes_enabled, is_batch_file)?; cmd_str.push(0); // add null terminator @@ -370,7 +370,7 @@ fn resolve_exe<'a>( exe_path: &'a OsStr, parent_paths: impl FnOnce() -> Option, child_paths: Option<&OsStr>, -) -> io::Result { +) -> io::Result> { // Early return if there is no filename. if exe_path.is_empty() || path::has_trailing_slash(exe_path) { return Err(io::const_io_error!( @@ -392,19 +392,19 @@ fn resolve_exe<'a>( if has_exe_suffix { // The application name is a path to a `.exe` file. // Let `CreateProcessW` figure out if it exists or not. - return Ok(exe_path.into()); + return path::maybe_verbatim(Path::new(exe_path)); } let mut path = PathBuf::from(exe_path); // Append `.exe` if not already there. path = path::append_suffix(path, EXE_SUFFIX.as_ref()); - if program_exists(&path) { + if let Some(path) = program_exists(&path) { return Ok(path); } else { // It's ok to use `set_extension` here because the intent is to // remove the extension that was just added. path.set_extension(""); - return Ok(path); + return path::maybe_verbatim(&path); } } else { ensure_no_nuls(exe_path)?; @@ -419,7 +419,7 @@ fn resolve_exe<'a>( if !has_extension { path.set_extension(EXE_EXTENSION); } - if program_exists(&path) { Some(path) } else { None } + program_exists(&path) }); if let Some(path) = result { return Ok(path); @@ -435,10 +435,10 @@ fn search_paths( parent_paths: Paths, child_paths: Option<&OsStr>, mut exists: Exists, -) -> Option +) -> Option> where Paths: FnOnce() -> Option, - Exists: FnMut(PathBuf) -> Option, + Exists: FnMut(PathBuf) -> Option>, { // 1. Child paths // This is for consistency with Rust's historic behaviour. @@ -490,17 +490,18 @@ where } /// Check if a file exists without following symlinks. -fn program_exists(path: &Path) -> bool { +fn program_exists(path: &Path) -> Option> { unsafe { - to_u16s(path) - .map(|path| { - // Getting attributes using `GetFileAttributesW` does not follow symlinks - // and it will almost always be successful if the link exists. - // There are some exceptions for special system files (e.g. the pagefile) - // but these are not executable. - c::GetFileAttributesW(path.as_ptr()) != c::INVALID_FILE_ATTRIBUTES - }) - .unwrap_or(false) + let path = path::maybe_verbatim(path).ok()?; + // Getting attributes using `GetFileAttributesW` does not follow symlinks + // and it will almost always be successful if the link exists. + // There are some exceptions for special system files (e.g. the pagefile) + // but these are not executable. + if c::GetFileAttributesW(path.as_ptr()) == c::INVALID_FILE_ATTRIBUTES { + None + } else { + Some(path) + } } } From ee02f01ea6caa0dd56393012aa84ca4d09549702 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Fri, 21 Jan 2022 12:43:07 -0800 Subject: [PATCH 07/18] Consistently present absent stdio handles on Windows as NULL handles. This addresses #90964 by making the std API consistent about presenting absent stdio handles on Windows as NULL handles. Stdio handles may be absent due to `#![windows_subsystem = "windows"]`, due to the console being detached, or due to a child process having been launched from a parent where stdio handles are absent. Specifically, this fixes the case of child processes of parents with absent stdio, which previously ended up with `stdin().as_raw_handle()` returning `INVALID_HANDLE_VALUE`, which was surprising, and which overlapped with an unrelated valid handle value. With this patch, `stdin().as_raw_handle()` now returns null in these situation, which is consistent with what it does in the parent process. And, document this in the "Windows Portability Considerations" sections of the relevant documentation. --- library/std/src/io/stdio.rs | 79 ++++++++++++++++++++++--- library/std/src/os/windows/io/handle.rs | 12 +++- library/std/src/os/windows/io/raw.rs | 25 ++++++-- library/std/src/sys/windows/process.rs | 13 ++++ 4 files changed, 113 insertions(+), 16 deletions(-) diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 5414ff648d4d5..5bb70ae472b33 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -202,12 +202,18 @@ fn handle_ebadf(r: io::Result, default: T) -> io::Result { /// /// [`io::stdin`]: stdin /// -/// ### Note: Windows Portability Consideration +/// ### Note: Windows Portability Considerations /// /// When operating in a console, the Windows implementation of this stream does not support /// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return /// an error. /// +/// In a process with a detached console, such as one using +/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, +/// the contained handle will be null. In such cases, the standard library's `Read` and +/// `Write` will do nothing and silently succeed. All other I/O operations, via the +/// standard library or via raw Windows API calls, will fail. +/// /// # Examples /// /// ```no_run @@ -230,12 +236,18 @@ pub struct Stdin { /// This handle implements both the [`Read`] and [`BufRead`] traits, and /// is constructed via the [`Stdin::lock`] method. /// -/// ### Note: Windows Portability Consideration +/// ### Note: Windows Portability Considerations /// /// When operating in a console, the Windows implementation of this stream does not support /// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return /// an error. /// +/// In a process with a detached console, such as one using +/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, +/// the contained handle will be null. In such cases, the standard library's `Read` and +/// `Write` will do nothing and silently succeed. All other I/O operations, via the +/// standard library or via raw Windows API calls, will fail. +/// /// # Examples /// /// ```no_run @@ -263,11 +275,18 @@ pub struct StdinLock<'a> { /// is synchronized via a mutex. If you need more explicit control over /// locking, see the [`Stdin::lock`] method. /// -/// ### Note: Windows Portability Consideration +/// ### Note: Windows Portability Considerations +/// /// When operating in a console, the Windows implementation of this stream does not support /// non-UTF-8 byte sequences. Attempting to read bytes that are not valid UTF-8 will return /// an error. /// +/// In a process with a detached console, such as one using +/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, +/// the contained handle will be null. In such cases, the standard library's `Read` and +/// `Write` will do nothing and silently succeed. All other I/O operations, via the +/// standard library or via raw Windows API calls, will fail. +/// /// # Examples /// /// Using implicit synchronization: @@ -490,11 +509,18 @@ impl fmt::Debug for StdinLock<'_> { /// /// Created by the [`io::stdout`] method. /// -/// ### Note: Windows Portability Consideration +/// ### Note: Windows Portability Considerations +/// /// When operating in a console, the Windows implementation of this stream does not support /// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return /// an error. /// +/// In a process with a detached console, such as one using +/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, +/// the contained handle will be null. In such cases, the standard library's `Read` and +/// `Write` will do nothing and silently succeed. All other I/O operations, via the +/// standard library or via raw Windows API calls, will fail. +/// /// [`lock`]: Stdout::lock /// [`io::stdout`]: stdout #[stable(feature = "rust1", since = "1.0.0")] @@ -510,10 +536,17 @@ pub struct Stdout { /// This handle implements the [`Write`] trait, and is constructed via /// the [`Stdout::lock`] method. See its documentation for more. /// -/// ### Note: Windows Portability Consideration +/// ### Note: Windows Portability Considerations +/// /// When operating in a console, the Windows implementation of this stream does not support /// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return /// an error. +/// +/// In a process with a detached console, such as one using +/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, +/// the contained handle will be null. In such cases, the standard library's `Read` and +/// `Write` will do nothing and silently succeed. All other I/O operations, via the +/// standard library or via raw Windows API calls, will fail. #[must_use = "if unused stdout will immediately unlock"] #[stable(feature = "rust1", since = "1.0.0")] pub struct StdoutLock<'a> { @@ -528,11 +561,18 @@ static STDOUT: SyncOnceCell>>> = Sy /// is synchronized via a mutex. If you need more explicit control over /// locking, see the [`Stdout::lock`] method. /// -/// ### Note: Windows Portability Consideration +/// ### Note: Windows Portability Considerations +/// /// When operating in a console, the Windows implementation of this stream does not support /// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return /// an error. /// +/// In a process with a detached console, such as one using +/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, +/// the contained handle will be null. In such cases, the standard library's `Read` and +/// `Write` will do nothing and silently succeed. All other I/O operations, via the +/// standard library or via raw Windows API calls, will fail. +/// /// # Examples /// /// Using implicit synchronization: @@ -710,10 +750,17 @@ impl fmt::Debug for StdoutLock<'_> { /// /// [`io::stderr`]: stderr /// -/// ### Note: Windows Portability Consideration +/// ### Note: Windows Portability Considerations +/// /// When operating in a console, the Windows implementation of this stream does not support /// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return /// an error. +/// +/// In a process with a detached console, such as one using +/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, +/// the contained handle will be null. In such cases, the standard library's `Read` and +/// `Write` will do nothing and silently succeed. All other I/O operations, via the +/// standard library or via raw Windows API calls, will fail. #[stable(feature = "rust1", since = "1.0.0")] pub struct Stderr { inner: Pin<&'static ReentrantMutex>>, @@ -724,10 +771,17 @@ pub struct Stderr { /// This handle implements the [`Write`] trait and is constructed via /// the [`Stderr::lock`] method. See its documentation for more. /// -/// ### Note: Windows Portability Consideration +/// ### Note: Windows Portability Considerations +/// /// When operating in a console, the Windows implementation of this stream does not support /// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return /// an error. +/// +/// In a process with a detached console, such as one using +/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, +/// the contained handle will be null. In such cases, the standard library's `Read` and +/// `Write` will do nothing and silently succeed. All other I/O operations, via the +/// standard library or via raw Windows API calls, will fail. #[must_use = "if unused stderr will immediately unlock"] #[stable(feature = "rust1", since = "1.0.0")] pub struct StderrLock<'a> { @@ -738,11 +792,18 @@ pub struct StderrLock<'a> { /// /// This handle is not buffered. /// -/// ### Note: Windows Portability Consideration +/// ### Note: Windows Portability Considerations +/// /// When operating in a console, the Windows implementation of this stream does not support /// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return /// an error. /// +/// In a process with a detached console, such as one using +/// `#![windows_subsystem = "windows"]`, or in a child process spawned from such a process, +/// the contained handle will be null. In such cases, the standard library's `Read` and +/// `Write` will do nothing and silently succeed. All other I/O operations, via the +/// standard library or via raw Windows API calls, will fail. +/// /// # Examples /// /// Using implicit synchronization: diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index 14b94d8dcdf92..bd8cb8f20d179 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -164,12 +164,22 @@ impl OwnedHandle { inherit: bool, options: c::DWORD, ) -> io::Result { + let handle = self.as_raw_handle(); + + // `Stdin`, `Stdout`, and `Stderr` can all hold null handles, such as + // in a process with a detached console. `DuplicateHandle` would fail + // if we passed it a null handle, but we can treat null as a valid + // handle which doesn't do any I/O, and allow it to be duplicated. + if handle.is_null() { + return unsafe { Ok(Handle::from_raw_handle(handle)) }; + } + let mut ret = 0 as c::HANDLE; cvt(unsafe { let cur_proc = c::GetCurrentProcess(); c::DuplicateHandle( cur_proc, - self.as_raw_handle(), + handle, cur_proc, &mut ret, access, diff --git a/library/std/src/os/windows/io/raw.rs b/library/std/src/os/windows/io/raw.rs index 48c5fd358d9db..68fa8918a56a0 100644 --- a/library/std/src/os/windows/io/raw.rs +++ b/library/std/src/os/windows/io/raw.rs @@ -9,6 +9,7 @@ use crate::net; use crate::os::windows::io::{AsHandle, AsSocket}; use crate::os::windows::io::{OwnedHandle, OwnedSocket}; use crate::os::windows::raw; +use crate::ptr; use crate::sys; use crate::sys::c; use crate::sys_common::{self, AsInner, FromInner, IntoInner}; @@ -96,45 +97,57 @@ impl AsRawHandle for fs::File { #[stable(feature = "asraw_stdio", since = "1.21.0")] impl AsRawHandle for io::Stdin { fn as_raw_handle(&self) -> RawHandle { - unsafe { c::GetStdHandle(c::STD_INPUT_HANDLE) as RawHandle } + stdio_handle(unsafe { c::GetStdHandle(c::STD_INPUT_HANDLE) as RawHandle }) } } #[stable(feature = "asraw_stdio", since = "1.21.0")] impl AsRawHandle for io::Stdout { fn as_raw_handle(&self) -> RawHandle { - unsafe { c::GetStdHandle(c::STD_OUTPUT_HANDLE) as RawHandle } + stdio_handle(unsafe { c::GetStdHandle(c::STD_OUTPUT_HANDLE) as RawHandle }) } } #[stable(feature = "asraw_stdio", since = "1.21.0")] impl AsRawHandle for io::Stderr { fn as_raw_handle(&self) -> RawHandle { - unsafe { c::GetStdHandle(c::STD_ERROR_HANDLE) as RawHandle } + stdio_handle(unsafe { c::GetStdHandle(c::STD_ERROR_HANDLE) as RawHandle }) } } #[stable(feature = "asraw_stdio_locks", since = "1.35.0")] impl<'a> AsRawHandle for io::StdinLock<'a> { fn as_raw_handle(&self) -> RawHandle { - unsafe { c::GetStdHandle(c::STD_INPUT_HANDLE) as RawHandle } + stdio_handle(unsafe { c::GetStdHandle(c::STD_INPUT_HANDLE) as RawHandle }) } } #[stable(feature = "asraw_stdio_locks", since = "1.35.0")] impl<'a> AsRawHandle for io::StdoutLock<'a> { fn as_raw_handle(&self) -> RawHandle { - unsafe { c::GetStdHandle(c::STD_OUTPUT_HANDLE) as RawHandle } + stdio_handle(unsafe { c::GetStdHandle(c::STD_OUTPUT_HANDLE) as RawHandle }) } } #[stable(feature = "asraw_stdio_locks", since = "1.35.0")] impl<'a> AsRawHandle for io::StderrLock<'a> { fn as_raw_handle(&self) -> RawHandle { - unsafe { c::GetStdHandle(c::STD_ERROR_HANDLE) as RawHandle } + stdio_handle(unsafe { c::GetStdHandle(c::STD_ERROR_HANDLE) as RawHandle }) } } +// Translate a handle returned from `GetStdHandle` into a handle to return to +// the user. +fn stdio_handle(raw: RawHandle) -> RawHandle { + // `GetStdHandle` isn't expected to actually fail, so when it returns + // `INVALID_HANDLE_VALUE`, it means we were launched from a parent which + // didn't provide us with stdio handles, such as a parent with a detached + // console. In that case, return null to the user, which is consistent + // with what they'd get in the parent, and which avoids the problem that + // `INVALID_HANDLE_VALUE` aliases the current process handle. + if raw == c::INVALID_HANDLE_VALUE { ptr::null_mut() } else { raw } +} + #[stable(feature = "from_raw_os", since = "1.1.0")] impl FromRawHandle for fs::File { #[inline] diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs index fafd1412d4cb3..ec53c46cd7b4e 100644 --- a/library/std/src/sys/windows/process.rs +++ b/library/std/src/sys/windows/process.rs @@ -309,6 +309,19 @@ impl Command { si.hStdOutput = stdout.as_raw_handle(); si.hStdError = stderr.as_raw_handle(); + // `CreateProcessW` fails with `ERROR_FILE_NOT_FOUND` if any of the + // stdio handles are null, so if we have null handles, set them to + // `INVALID_HANDLE_VALUE`. + if si.hStdInput.is_null() { + si.hStdInput = c::INVALID_HANDLE_VALUE; + } + if si.hStdOutput.is_null() { + si.hStdOutput = c::INVALID_HANDLE_VALUE; + } + if si.hStdError.is_null() { + si.hStdError = c::INVALID_HANDLE_VALUE; + } + let program = to_u16s(&program)?; unsafe { cvt(c::CreateProcessW( From 7dd32469e5f994cec98969b420e8846d5f4c6c34 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Tue, 25 Jan 2022 11:40:11 -0800 Subject: [PATCH 08/18] Fix a compilation error. --- library/std/src/os/windows/io/handle.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/os/windows/io/handle.rs b/library/std/src/os/windows/io/handle.rs index bd8cb8f20d179..be2ccbd98e9c2 100644 --- a/library/std/src/os/windows/io/handle.rs +++ b/library/std/src/os/windows/io/handle.rs @@ -171,7 +171,7 @@ impl OwnedHandle { // if we passed it a null handle, but we can treat null as a valid // handle which doesn't do any I/O, and allow it to be duplicated. if handle.is_null() { - return unsafe { Ok(Handle::from_raw_handle(handle)) }; + return unsafe { Ok(Self::from_raw_handle(handle)) }; } let mut ret = 0 as c::HANDLE; From 7ddf41c7b10e46c231180cecbbb5e78e0dd12ff8 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Wed, 2 Mar 2022 11:48:50 -0800 Subject: [PATCH 09/18] Remove redundant code for handling NULL handles on Windows. Before calling `CreateProcessW`, stdio handles are passed through `stdio::get_handle`, which already converts NULL to `INVALID_HANDLE_VALUE`, so we don't need extra checks for NULL after that point. --- library/std/src/sys/windows/process.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs index ec53c46cd7b4e..fafd1412d4cb3 100644 --- a/library/std/src/sys/windows/process.rs +++ b/library/std/src/sys/windows/process.rs @@ -309,19 +309,6 @@ impl Command { si.hStdOutput = stdout.as_raw_handle(); si.hStdError = stderr.as_raw_handle(); - // `CreateProcessW` fails with `ERROR_FILE_NOT_FOUND` if any of the - // stdio handles are null, so if we have null handles, set them to - // `INVALID_HANDLE_VALUE`. - if si.hStdInput.is_null() { - si.hStdInput = c::INVALID_HANDLE_VALUE; - } - if si.hStdOutput.is_null() { - si.hStdOutput = c::INVALID_HANDLE_VALUE; - } - if si.hStdError.is_null() { - si.hStdError = c::INVALID_HANDLE_VALUE; - } - let program = to_u16s(&program)?; unsafe { cvt(c::CreateProcessW( From 7a74d28c389e750b0876e65859108adc842ab5bd Mon Sep 17 00:00:00 2001 From: Sebastian Humenda Date: Thu, 18 Jun 2020 18:46:43 +0200 Subject: [PATCH 10/18] fix return values in L4Re networking stub [Benjamin Lamowski: Reworded commit message after split commit.] --- library/std/src/sys/unix/l4re.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/std/src/sys/unix/l4re.rs b/library/std/src/sys/unix/l4re.rs index d13e1ecbbfed4..cf101f934e121 100644 --- a/library/std/src/sys/unix/l4re.rs +++ b/library/std/src/sys/unix/l4re.rs @@ -59,7 +59,7 @@ pub mod net { } pub fn is_read_vectored(&self) -> bool { - unimpl!(); + false } pub fn peek(&self, _: &mut [u8]) -> io::Result { @@ -83,7 +83,7 @@ pub mod net { } pub fn is_write_vectored(&self) -> bool { - unimpl!(); + false } pub fn set_timeout(&self, _: Option, _: libc::c_int) -> io::Result<()> { @@ -191,7 +191,7 @@ pub mod net { } pub fn is_read_vectored(&self) -> bool { - unimpl!(); + false } pub fn write(&self, _: &[u8]) -> io::Result { @@ -203,7 +203,7 @@ pub mod net { } pub fn is_write_vectored(&self) -> bool { - unimpl!(); + false } pub fn peer_addr(&self) -> io::Result { From 11b717647ef9a772a3a4b9e4dd6282db0b69ff32 Mon Sep 17 00:00:00 2001 From: Sebastian Humenda Date: Wed, 5 Dec 2018 15:53:14 +0100 Subject: [PATCH 11/18] fix return value of LookupHost::port() [Benjamin Lamowski: Reworded commit message after split commit.] --- library/std/src/sys/unix/l4re.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/sys/unix/l4re.rs b/library/std/src/sys/unix/l4re.rs index cf101f934e121..2bc37fc56b291 100644 --- a/library/std/src/sys/unix/l4re.rs +++ b/library/std/src/sys/unix/l4re.rs @@ -497,7 +497,7 @@ pub mod net { impl LookupHost { pub fn port(&self) -> u16 { - unimpl!(); + 0 // unimplemented } } From 898f379817602376442b814109e1abc42457fbbb Mon Sep 17 00:00:00 2001 From: Benjamin Lamowski Date: Mon, 31 May 2021 15:00:11 +0200 Subject: [PATCH 12/18] drop unused libc imports on L4Re As a capability-based microkernel OS, L4Re only has incomplete support for POSIX APIs, in particular it does not implement UIDs and GIDs. --- library/std/src/sys/unix/process/process_unix.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs index 07a0339c066bc..c8fe1a7378dd1 100644 --- a/library/std/src/sys/unix/process/process_unix.rs +++ b/library/std/src/sys/unix/process/process_unix.rs @@ -27,7 +27,10 @@ use crate::sys::weak::weak; use libc::RTP_ID as pid_t; #[cfg(not(target_os = "vxworks"))] -use libc::{c_int, gid_t, pid_t, uid_t}; +use libc::{c_int, pid_t}; + +#[cfg(not(any(target_os = "vxworks", target_os = "l4re")))] +use libc::{gid_t, uid_t}; //////////////////////////////////////////////////////////////////////////////// // Command From c0dc41f5ff00474d0c692a18330bd32f44d15d67 Mon Sep 17 00:00:00 2001 From: Benjamin Lamowski Date: Wed, 2 Jun 2021 19:46:27 +0200 Subject: [PATCH 13/18] L4Re does not support sanitizing standard streams L4Re provides limited POSIX support which includes support for standard I/O streams, and a limited implementation of the standard file handling API. However, because as a capability based OS it strives to only make a local view available to each application, there are currently no standardized special files like /dev/null that could serve to sanitize closed standard FDs. For now, skip any attempts to sanitize standard streams until a more complete POSIX runtime is available. --- library/std/src/sys/unix/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/src/sys/unix/mod.rs b/library/std/src/sys/unix/mod.rs index 605cc499b3c92..7423d90263dc2 100644 --- a/library/std/src/sys/unix/mod.rs +++ b/library/std/src/sys/unix/mod.rs @@ -80,6 +80,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8) { target_os = "macos", target_os = "ios", target_os = "redox", + target_os = "l4re", )))] { use crate::sys::os::errno; let pfds: &mut [_] = &mut [ From 997dc5899a0549ca6a6fe007ae1150ae4fccd728 Mon Sep 17 00:00:00 2001 From: Benjamin Lamowski Date: Mon, 18 Oct 2021 13:59:36 +0200 Subject: [PATCH 14/18] adapt L4Re network interface mock to #87329 Copy the relevant trait implementations from the Unix default. --- library/std/src/sys/unix/l4re.rs | 43 +++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/library/std/src/sys/unix/l4re.rs b/library/std/src/sys/unix/l4re.rs index 2bc37fc56b291..d45c51030f67c 100644 --- a/library/std/src/sys/unix/l4re.rs +++ b/library/std/src/sys/unix/l4re.rs @@ -13,6 +13,7 @@ pub mod net { use crate::fmt; use crate::io::{self, IoSlice, IoSliceMut}; use crate::net::{Ipv4Addr, Ipv6Addr, Shutdown, SocketAddr}; + use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd}; use crate::sys::fd::FileDesc; use crate::sys_common::{AsInner, FromInner, IntoInner}; use crate::time::Duration; @@ -123,21 +124,45 @@ pub mod net { } } - impl AsInner for Socket { - fn as_inner(&self) -> &libc::c_int { - self.0.as_inner() + impl AsInner for Socket { + fn as_inner(&self) -> &FileDesc { + &self.0 } } - impl FromInner for Socket { - fn from_inner(fd: libc::c_int) -> Socket { - Socket(FileDesc::new(fd)) + impl FromInner for Socket { + fn from_inner(file_desc: FileDesc) -> Socket { + Socket(file_desc) } } - impl IntoInner for Socket { - fn into_inner(self) -> libc::c_int { - self.0.into_raw() + impl IntoInner for Socket { + fn into_inner(self) -> FileDesc { + self.0 + } + } + + impl AsFd for Socket { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } + } + + impl AsRawFd for Socket { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } + } + + impl IntoRawFd for Socket { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } + } + + impl FromRawFd for Socket { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(FromRawFd::from_raw_fd(raw_fd)) } } From cb013d480252a77233781f45273f190759471e3d Mon Sep 17 00:00:00 2001 From: Benjamin Lamowski Date: Mon, 18 Oct 2021 17:17:54 +0200 Subject: [PATCH 15/18] put L4Re specifics into their own platform The initial stdlib modifications for L4Re just used the linux specifics directly because they were reasonably close to L4Re's behavior. However, this breaks when Linux-specific code relies on code that is only available for the linux target, such as in #81825. Put L4Re into its own platform to avoid such breakage in the future. This uses the Linux-specific code as a starting point, which seems to be in line with other OSes with a unix-y interface such as Fuchsia. --- library/std/src/os/l4re/fs.rs | 382 +++++++++++++++++++++++++++++++++ library/std/src/os/l4re/mod.rs | 7 + library/std/src/os/l4re/raw.rs | 365 +++++++++++++++++++++++++++++++ library/std/src/os/mod.rs | 4 +- library/std/src/os/unix/mod.rs | 4 +- 5 files changed, 760 insertions(+), 2 deletions(-) create mode 100644 library/std/src/os/l4re/fs.rs create mode 100644 library/std/src/os/l4re/mod.rs create mode 100644 library/std/src/os/l4re/raw.rs diff --git a/library/std/src/os/l4re/fs.rs b/library/std/src/os/l4re/fs.rs new file mode 100644 index 0000000000000..b9b6918292fae --- /dev/null +++ b/library/std/src/os/l4re/fs.rs @@ -0,0 +1,382 @@ +//! L4Re-specific extensions to primitives in the [`std::fs`] module. +//! +//! [`std::fs`]: crate::fs + +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +#[allow(deprecated)] +use crate::os::l4re::raw; + +/// OS-specific extensions to [`fs::Metadata`]. +/// +/// [`fs::Metadata`]: crate::fs::Metadata +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + /// Gain a reference to the underlying `stat` structure which contains + /// the raw information returned by the OS. + /// + /// The contents of the returned [`stat`] are **not** consistent across + /// Unix platforms. The `os::unix::fs::MetadataExt` trait contains the + /// cross-Unix abstractions contained within the raw stat. + /// + /// [`stat`]: struct@crate::os::linux::raw::stat + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::linux::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// let stat = meta.as_raw_stat(); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext", since = "1.1.0")] + #[rustc_deprecated(since = "1.8.0", reason = "other methods of this trait are now preferred")] + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat; + + /// Returns the device ID on which this file resides. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::linux::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_dev()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + /// Returns the inode number. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::linux::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_ino()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + /// Returns the file type and mode. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::linux::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_mode()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mode(&self) -> u32; + /// Returns the number of hard links to file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::linux::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_nlink()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + /// Returns the user ID of the file owner. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::linux::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_uid()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + /// Returns the group ID of the file owner. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::linux::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_gid()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + /// Returns the device ID that this file represents. Only relevant for special file. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::linux::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_rdev()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + /// Returns the size of the file (if it is a regular file or a symbolic link) in bytes. + /// + /// The size of a symbolic link is the length of the pathname it contains, + /// without a terminating null byte. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::linux::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_size()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + /// Returns the last access time of the file, in seconds since Unix Epoch. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::linux::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_atime()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + /// Returns the last access time of the file, in nanoseconds since [`st_atime`]. + /// + /// [`st_atime`]: Self::st_atime + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::linux::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_atime_nsec()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + /// Returns the last modification time of the file, in seconds since Unix Epoch. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::linux::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_mtime()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + /// Returns the last modification time of the file, in nanoseconds since [`st_mtime`]. + /// + /// [`st_mtime`]: Self::st_mtime + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::linux::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_mtime_nsec()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + /// Returns the last status change time of the file, in seconds since Unix Epoch. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::linux::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_ctime()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + /// Returns the last status change time of the file, in nanoseconds since [`st_ctime`]. + /// + /// [`st_ctime`]: Self::st_ctime + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::linux::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_ctime_nsec()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + /// Returns the "preferred" block size for efficient filesystem I/O. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::linux::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_blksize()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + /// Returns the number of blocks allocated to the file, 512-byte units. + /// + /// # Examples + /// + /// ```no_run + /// use std::fs; + /// use std::io; + /// use std::os::linux::fs::MetadataExt; + /// + /// fn main() -> io::Result<()> { + /// let meta = fs::metadata("some_file")?; + /// println!("{}", meta.st_blocks()); + /// Ok(()) + /// } + /// ``` + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blocks(&self) -> u64; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + #[allow(deprecated)] + fn as_raw_stat(&self) -> &raw::stat { + unsafe { &*(self.as_inner().as_inner() as *const libc::stat64 as *const raw::stat) } + } + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atime as i64 + } + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atime_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtime as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtime_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctime as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctime_nsec as i64 + } + fn st_blksize(&self) -> u64 { + self.as_inner().as_inner().st_blksize as u64 + } + fn st_blocks(&self) -> u64 { + self.as_inner().as_inner().st_blocks as u64 + } +} diff --git a/library/std/src/os/l4re/mod.rs b/library/std/src/os/l4re/mod.rs new file mode 100644 index 0000000000000..14c2425c16517 --- /dev/null +++ b/library/std/src/os/l4re/mod.rs @@ -0,0 +1,7 @@ +//! L4Re-specific definitions. + +#![stable(feature = "raw_ext", since = "1.1.0")] +#![doc(cfg(target_os = "l4re"))] + +pub mod fs; +pub mod raw; diff --git a/library/std/src/os/l4re/raw.rs b/library/std/src/os/l4re/raw.rs new file mode 100644 index 0000000000000..5efd6301fc92b --- /dev/null +++ b/library/std/src/os/l4re/raw.rs @@ -0,0 +1,365 @@ +//! L4Re-specific raw type definitions. + +#![stable(feature = "raw_ext", since = "1.1.0")] +#![rustc_deprecated( + since = "1.8.0", + reason = "these type aliases are no longer supported by \ + the standard library, the `libc` crate on \ + crates.io should be used instead for the correct \ + definitions" +)] +#![allow(deprecated)] + +use crate::os::raw::c_ulong; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type dev_t = u64; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type mode_t = u32; + +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = c_ulong; + +#[doc(inline)] +#[stable(feature = "raw_ext", since = "1.1.0")] +pub use self::arch::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t}; + +#[cfg(any( + target_arch = "x86", + target_arch = "le32", + target_arch = "m68k", + target_arch = "powerpc", + target_arch = "sparc", + target_arch = "arm", + target_arch = "asmjs", + target_arch = "wasm32" +))] +mod arch { + use crate::os::raw::{c_long, c_short, c_uint}; + + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blkcnt_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blksize_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type ino_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type nlink_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type off_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type time_t = i64; + + #[repr(C)] + #[derive(Clone)] + #[stable(feature = "raw_ext", since = "1.1.0")] + pub struct stat { + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_dev: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad1: c_short, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __st_ino: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mode: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_nlink: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_uid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_gid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_rdev: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad2: c_uint, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_size: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blksize: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blocks: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ino: u64, + } +} + +#[cfg(target_arch = "mips")] +mod arch { + use crate::os::raw::{c_long, c_ulong}; + + #[cfg(target_env = "musl")] + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blkcnt_t = i64; + #[cfg(not(target_env = "musl"))] + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blkcnt_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blksize_t = u64; + #[cfg(target_env = "musl")] + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type ino_t = u64; + #[cfg(not(target_env = "musl"))] + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type ino_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type nlink_t = u64; + #[cfg(target_env = "musl")] + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type off_t = u64; + #[cfg(not(target_env = "musl"))] + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type off_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type time_t = i64; + + #[repr(C)] + #[derive(Clone)] + #[stable(feature = "raw_ext", since = "1.1.0")] + pub struct stat { + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_dev: c_ulong, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_pad1: [c_long; 3], + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ino: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mode: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_nlink: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_uid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_gid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_rdev: c_ulong, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_pad2: [c_long; 2], + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_size: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blksize: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blocks: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_pad5: [c_long; 14], + } +} + +#[cfg(target_arch = "hexagon")] +mod arch { + use crate::os::raw::{c_int, c_long, c_uint}; + + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blkcnt_t = i64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blksize_t = c_long; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type ino_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type nlink_t = c_uint; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type off_t = i64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type time_t = i64; + + #[repr(C)] + #[derive(Clone)] + #[stable(feature = "raw_ext", since = "1.1.0")] + pub struct stat { + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_dev: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ino: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mode: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_nlink: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_uid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_gid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_rdev: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad1: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_size: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blksize: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad2: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blocks: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad3: [c_int; 2], + } +} + +#[cfg(any( + target_arch = "mips64", + target_arch = "s390x", + target_arch = "sparc64", + target_arch = "riscv64", + target_arch = "riscv32" +))] +mod arch { + pub use libc::{blkcnt_t, blksize_t, ino_t, nlink_t, off_t, stat, time_t}; +} + +#[cfg(target_arch = "aarch64")] +mod arch { + use crate::os::raw::{c_int, c_long}; + + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blkcnt_t = i64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blksize_t = i32; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type ino_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type nlink_t = u32; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type off_t = i64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type time_t = c_long; + + #[repr(C)] + #[derive(Clone)] + #[stable(feature = "raw_ext", since = "1.1.0")] + pub struct stat { + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_dev: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ino: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mode: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_nlink: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_uid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_gid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_rdev: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad1: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_size: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blksize: i32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad2: c_int, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blocks: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime: time_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime: time_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime: time_t, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __unused: [c_int; 2], + } +} + +#[cfg(any(target_arch = "x86_64", target_arch = "powerpc64"))] +mod arch { + use crate::os::raw::{c_int, c_long}; + + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blkcnt_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type blksize_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type ino_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type nlink_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type off_t = u64; + #[stable(feature = "raw_ext", since = "1.1.0")] + pub type time_t = i64; + + #[repr(C)] + #[derive(Clone)] + #[stable(feature = "raw_ext", since = "1.1.0")] + pub struct stat { + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_dev: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ino: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_nlink: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mode: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_uid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_gid: u32, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __pad0: c_int, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_rdev: u64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_size: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blksize: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_blocks: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_atime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_mtime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime: i64, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub st_ctime_nsec: c_long, + #[stable(feature = "raw_ext", since = "1.1.0")] + pub __unused: [c_long; 3], + } +} diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index 90c30313dbbda..029f131c40be0 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -81,7 +81,7 @@ pub mod unix; all(target_vendor = "fortanix", target_env = "sgx") ) )))] -#[cfg(any(target_os = "linux", target_os = "l4re", doc))] +#[cfg(any(target_os = "linux", doc))] pub mod linux; // wasi @@ -127,6 +127,8 @@ pub mod haiku; pub mod illumos; #[cfg(target_os = "ios")] pub mod ios; +#[cfg(target_os = "l4re")] +pub mod l4re; #[cfg(target_os = "macos")] pub mod macos; #[cfg(target_os = "netbsd")] diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs index 62f750fa607c9..7b8ca79eeb846 100644 --- a/library/std/src/os/unix/mod.rs +++ b/library/std/src/os/unix/mod.rs @@ -55,7 +55,9 @@ mod platform { pub use crate::os::illumos::*; #[cfg(target_os = "ios")] pub use crate::os::ios::*; - #[cfg(any(target_os = "linux", target_os = "l4re"))] + #[cfg(target_os = "l4re")] + pub use crate::os::l4re::*; + #[cfg(target_os = "linux")] pub use crate::os::linux::*; #[cfg(target_os = "macos")] pub use crate::os::macos::*; From bc199b57784ec60bc9492c1a046c585af7ee8ea4 Mon Sep 17 00:00:00 2001 From: Benjamin Lamowski Date: Fri, 14 Jan 2022 17:42:40 +0100 Subject: [PATCH 16/18] add as_raw() method to L4Re's Socket mock Minimally comply with with #87329 to avoid breaking tests on L4Re. --- library/std/src/sys/unix/l4re.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/std/src/sys/unix/l4re.rs b/library/std/src/sys/unix/l4re.rs index d45c51030f67c..f052d8f7f055f 100644 --- a/library/std/src/sys/unix/l4re.rs +++ b/library/std/src/sys/unix/l4re.rs @@ -122,6 +122,11 @@ pub mod net { pub fn take_error(&self) -> io::Result> { unimpl!(); } + + // This is used by sys_common code to abstract over Windows and Unix. + pub fn as_raw(&self) -> RawFd { + self.as_raw_fd() + } } impl AsInner for Socket { From 7d44316bcf3fbe220412589f08befb3116328b21 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 18 Mar 2022 15:04:37 -0700 Subject: [PATCH 17/18] Bump impl Write for Cursor<[u8; N]> to 1.61 --- library/std/src/io/cursor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index ea22ebd3e68e8..ddba9f8e9e380 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -542,7 +542,7 @@ where } } -#[stable(feature = "cursor_array", since = "1.60.0")] +#[stable(feature = "cursor_array", since = "1.61.0")] impl Write for Cursor<[u8; N]> { #[inline] fn write(&mut self, buf: &[u8]) -> io::Result { From d5fe4cad5a68a305b9fa7d421b0ffa358b14b0a9 Mon Sep 17 00:00:00 2001 From: Eric Seppanen Date: Tue, 15 Mar 2022 16:47:26 -0700 Subject: [PATCH 18/18] add CStr::from_bytes_until_nul This adds a member fn that converts a slice into a CStr; it is intended to be safer than from_ptr (which is unsafe and may read out of bounds), and more useful than from_bytes_with_nul (which requires that the caller already know where the nul byte is). feature gate: cstr_from_bytes_until_nul Also add an error type FromBytesUntilNulError for this fn. --- library/std/src/ffi/c_str.rs | 69 ++++++++++++++++++++++++++++++ library/std/src/ffi/c_str/tests.rs | 37 ++++++++++++++++ 2 files changed, 106 insertions(+) diff --git a/library/std/src/ffi/c_str.rs b/library/std/src/ffi/c_str.rs index b833d0e2ca507..a68def1e83dbb 100644 --- a/library/std/src/ffi/c_str.rs +++ b/library/std/src/ffi/c_str.rs @@ -328,6 +328,27 @@ impl FromVecWithNulError { } } +/// An error indicating that no nul byte was present. +/// +/// A slice used to create a [`CStr`] must contain a nul byte somewhere +/// within the slice. +/// +/// This error is created by the [`CStr::from_bytes_until_nul`] method. +/// +#[derive(Clone, PartialEq, Eq, Debug)] +#[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")] +pub struct FromBytesUntilNulError(()); + +#[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")] +impl Error for FromBytesUntilNulError {} + +#[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")] +impl fmt::Display for FromBytesUntilNulError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "data provided does not contain a nul") + } +} + /// 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; @@ -1239,12 +1260,60 @@ impl CStr { } } + /// Creates a C string wrapper from a byte slice. + /// + /// This method will create a `CStr` from any byte slice that contains at + /// least one nul byte. The caller does not need to know or specify where + /// the nul byte is located. + /// + /// If the first byte is a nul character, this method will return an + /// empty `CStr`. If multiple nul characters are present, the `CStr` will + /// end at the first one. + /// + /// If the slice only has a single nul byte at the end, this method is + /// equivalent to [`CStr::from_bytes_with_nul`]. + /// + /// # Examples + /// ``` + /// #![feature(cstr_from_bytes_until_nul)] + /// + /// use std::ffi::CStr; + /// + /// let mut buffer = [0u8; 16]; + /// unsafe { + /// // Here we might call an unsafe C function that writes a string + /// // into the buffer. + /// let buf_ptr = buffer.as_mut_ptr(); + /// buf_ptr.write_bytes(b'A', 8); + /// } + /// // Attempt to extract a C nul-terminated string from the buffer. + /// let c_str = CStr::from_bytes_until_nul(&buffer[..]).unwrap(); + /// assert_eq!(c_str.to_str().unwrap(), "AAAAAAAA"); + /// ``` + /// + #[unstable(feature = "cstr_from_bytes_until_nul", issue = "95027")] + pub fn from_bytes_until_nul(bytes: &[u8]) -> Result<&CStr, FromBytesUntilNulError> { + let nul_pos = memchr::memchr(0, bytes); + match nul_pos { + Some(nul_pos) => { + // SAFETY: We know there is a nul byte at nul_pos, so this slice + // (ending at the nul byte) is a well-formed C string. + let subslice = &bytes[..nul_pos + 1]; + Ok(unsafe { CStr::from_bytes_with_nul_unchecked(subslice) }) + } + None => Err(FromBytesUntilNulError(())), + } + } + /// 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. /// + /// If the nul byte may not be at the end, + /// [`CStr::from_bytes_until_nul`] can be used instead. + /// /// # Examples /// /// ``` diff --git a/library/std/src/ffi/c_str/tests.rs b/library/std/src/ffi/c_str/tests.rs index 8d603229315c0..c20da138a18d0 100644 --- a/library/std/src/ffi/c_str/tests.rs +++ b/library/std/src/ffi/c_str/tests.rs @@ -117,6 +117,43 @@ fn from_bytes_with_nul_interior() { assert!(cstr.is_err()); } +#[test] +fn cstr_from_bytes_until_nul() { + // Test an empty slice. This should fail because it + // does not contain a nul byte. + let b = b""; + assert_eq!(CStr::from_bytes_until_nul(&b[..]), Err(FromBytesUntilNulError(()))); + + // Test a non-empty slice, that does not contain a nul byte. + let b = b"hello"; + assert_eq!(CStr::from_bytes_until_nul(&b[..]), Err(FromBytesUntilNulError(()))); + + // Test an empty nul-terminated string + let b = b"\0"; + let r = CStr::from_bytes_until_nul(&b[..]).unwrap(); + assert_eq!(r.to_bytes(), b""); + + // Test a slice with the nul byte in the middle + let b = b"hello\0world!"; + let r = CStr::from_bytes_until_nul(&b[..]).unwrap(); + assert_eq!(r.to_bytes(), b"hello"); + + // Test a slice with the nul byte at the end + let b = b"hello\0"; + let r = CStr::from_bytes_until_nul(&b[..]).unwrap(); + assert_eq!(r.to_bytes(), b"hello"); + + // Test a slice with two nul bytes at the end + let b = b"hello\0\0"; + let r = CStr::from_bytes_until_nul(&b[..]).unwrap(); + assert_eq!(r.to_bytes(), b"hello"); + + // Test a slice containing lots of nul bytes + let b = b"\0\0\0\0"; + let r = CStr::from_bytes_until_nul(&b[..]).unwrap(); + assert_eq!(r.to_bytes(), b""); +} + #[test] fn into_boxed() { let orig: &[u8] = b"Hello, world!\0";