diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 18:31:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 18:31:44 +0000 |
commit | c23a457e72abe608715ac76f076f47dc42af07a5 (patch) | |
tree | 2772049aaf84b5c9d0ed12ec8d86812f7a7904b6 /library/std/src/io | |
parent | Releasing progress-linux version 1.73.0+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-c23a457e72abe608715ac76f076f47dc42af07a5.tar.xz rustc-c23a457e72abe608715ac76f076f47dc42af07a5.zip |
Merging upstream version 1.74.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/std/src/io')
-rw-r--r-- | library/std/src/io/buffered/bufwriter.rs | 2 | ||||
-rw-r--r-- | library/std/src/io/copy.rs | 51 | ||||
-rw-r--r-- | library/std/src/io/copy/tests.rs | 12 | ||||
-rw-r--r-- | library/std/src/io/error.rs | 25 | ||||
-rw-r--r-- | library/std/src/io/error/repr_bitpacked.rs | 3 | ||||
-rw-r--r-- | library/std/src/io/mod.rs | 99 |
6 files changed, 97 insertions, 95 deletions
diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index 0f04f2911..95ba82e1e 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -237,7 +237,7 @@ impl<W: ?Sized + Write> BufWriter<W> { )); } Ok(n) => guard.consume(n), - Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(ref e) if e.is_interrupted() => {} Err(e) => return Err(e), } } diff --git a/library/std/src/io/copy.rs b/library/std/src/io/copy.rs index 3322940d2..eafd078a7 100644 --- a/library/std/src/io/copy.rs +++ b/library/std/src/io/copy.rs @@ -1,6 +1,5 @@ -use super::{BorrowedBuf, BufReader, BufWriter, ErrorKind, Read, Result, Write, DEFAULT_BUF_SIZE}; +use super::{BorrowedBuf, BufReader, BufWriter, Read, Result, Write, DEFAULT_BUF_SIZE}; use crate::alloc::Allocator; -use crate::cmp; use crate::collections::VecDeque; use crate::io::IoSlice; use crate::mem::MaybeUninit; @@ -30,6 +29,7 @@ mod tests; /// /// [`read`]: Read::read /// [`write`]: Write::write +/// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted /// /// # Examples /// @@ -163,7 +163,7 @@ where // from adding I: Read match self.read(&mut []) { Ok(_) => {} - Err(e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) if e.is_interrupted() => continue, Err(e) => return Err(e), } let buf = self.buffer(); @@ -243,7 +243,7 @@ impl<I: Write + ?Sized> BufferedWriterSpec for BufWriter<I> { // Read again if the buffer still has enough capacity, as BufWriter itself would do // This will occur if the reader returns short reads } - Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(ref e) if e.is_interrupted() => {} Err(e) => return Err(e), } } else { @@ -254,47 +254,6 @@ impl<I: Write + ?Sized> BufferedWriterSpec for BufWriter<I> { } } -impl<A: Allocator> BufferedWriterSpec for Vec<u8, A> { - fn buffer_size(&self) -> usize { - cmp::max(DEFAULT_BUF_SIZE, self.capacity() - self.len()) - } - - fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> { - let mut bytes = 0; - - // avoid allocating before we have determined that there's anything to read - if self.capacity() == 0 { - bytes = stack_buffer_copy(&mut reader.take(DEFAULT_BUF_SIZE as u64), self)?; - if bytes == 0 { - return Ok(0); - } - } - - loop { - self.reserve(DEFAULT_BUF_SIZE); - let mut buf: BorrowedBuf<'_> = self.spare_capacity_mut().into(); - match reader.read_buf(buf.unfilled()) { - Ok(()) => {} - Err(e) if e.kind() == ErrorKind::Interrupted => continue, - Err(e) => return Err(e), - }; - - let read = buf.filled().len(); - if read == 0 { - break; - } - - // SAFETY: BorrowedBuf guarantees all of its filled bytes are init - // and the number of read bytes can't exceed the spare capacity since - // that's what the buffer is borrowing from. - unsafe { self.set_len(self.len() + read) }; - bytes += read as u64; - } - - Ok(bytes) - } -} - fn stack_buffer_copy<R: Read + ?Sized, W: Write + ?Sized>( reader: &mut R, writer: &mut W, @@ -307,7 +266,7 @@ fn stack_buffer_copy<R: Read + ?Sized, W: Write + ?Sized>( loop { match reader.read_buf(buf.unfilled()) { Ok(()) => {} - Err(e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) if e.is_interrupted() => continue, Err(e) => return Err(e), }; diff --git a/library/std/src/io/copy/tests.rs b/library/std/src/io/copy/tests.rs index af137eaf8..d9998e87c 100644 --- a/library/std/src/io/copy/tests.rs +++ b/library/std/src/io/copy/tests.rs @@ -81,18 +81,6 @@ fn copy_specializes_bufreader() { } #[test] -fn copy_specializes_to_vec() { - let cap = 123456; - let mut source = ShortReader { cap, observed_buffer: 0, read_size: 1337 }; - let mut sink = Vec::new(); - assert_eq!(cap as u64, io::copy(&mut source, &mut sink).unwrap()); - assert!( - source.observed_buffer > DEFAULT_BUF_SIZE, - "expected a large buffer to be provided to the reader" - ); -} - -#[test] fn copy_specializes_from_vecdeque() { let mut source = VecDeque::with_capacity(100 * 1024); for _ in 0..20 * 1024 { diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 34c0ce9dc..b63091dea 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -1,14 +1,14 @@ #[cfg(test)] mod tests; -#[cfg(target_pointer_width = "64")] +#[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))] mod repr_bitpacked; -#[cfg(target_pointer_width = "64")] +#[cfg(all(target_pointer_width = "64", not(target_os = "uefi")))] use repr_bitpacked::Repr; -#[cfg(not(target_pointer_width = "64"))] +#[cfg(any(not(target_pointer_width = "64"), target_os = "uefi"))] mod repr_unpacked; -#[cfg(not(target_pointer_width = "64"))] +#[cfg(any(not(target_pointer_width = "64"), target_os = "uefi"))] use repr_unpacked::Repr; use crate::error; @@ -102,7 +102,7 @@ enum ErrorData<C> { /// /// [`into`]: Into::into #[unstable(feature = "raw_os_error_ty", issue = "107792")] -pub type RawOsError = i32; +pub type RawOsError = sys::RawOsError; // `#[repr(align(4))]` is probably redundant, it should have that value or // higher already. We include it just because repr_bitpacked.rs's encoding @@ -511,6 +511,7 @@ impl Error { /// let eof_error = Error::from(ErrorKind::UnexpectedEof); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline(never)] pub fn new<E>(kind: ErrorKind, error: E) -> Error where E: Into<Box<dyn error::Error + Send + Sync>>, @@ -527,8 +528,6 @@ impl Error { /// # Examples /// /// ``` - /// #![feature(io_error_other)] - /// /// use std::io::Error; /// /// // errors can be created from strings @@ -537,7 +536,7 @@ impl Error { /// // errors can also be created from other errors /// let custom_error2 = Error::other(custom_error); /// ``` - #[unstable(feature = "io_error_other", issue = "91946")] + #[stable(feature = "io_error_other", since = "1.74.0")] pub fn other<E>(error: E) -> Error where E: Into<Box<dyn error::Error + Send + Sync>>, @@ -916,6 +915,16 @@ impl Error { ErrorData::SimpleMessage(m) => m.kind, } } + + #[inline] + pub(crate) fn is_interrupted(&self) -> bool { + match self.repr.data() { + ErrorData::Os(code) => sys::is_interrupted(code), + ErrorData::Custom(c) => c.kind == ErrorKind::Interrupted, + ErrorData::Simple(kind) => kind == ErrorKind::Interrupted, + ErrorData::SimpleMessage(m) => m.kind == ErrorKind::Interrupted, + } + } } impl fmt::Debug for Repr { diff --git a/library/std/src/io/error/repr_bitpacked.rs b/library/std/src/io/error/repr_bitpacked.rs index f94f88bac..6e7366b36 100644 --- a/library/std/src/io/error/repr_bitpacked.rs +++ b/library/std/src/io/error/repr_bitpacked.rs @@ -374,9 +374,6 @@ static_assert!((TAG_MASK + 1).is_power_of_two()); static_assert!(align_of::<SimpleMessage>() >= TAG_MASK + 1); static_assert!(align_of::<Custom>() >= TAG_MASK + 1); -// `RawOsError` must be an alias for `i32`. -const _: fn(RawOsError) -> i32 = |os| os; - static_assert!(@usize_eq: TAG_MASK & TAG_SIMPLE_MESSAGE, TAG_SIMPLE_MESSAGE); static_assert!(@usize_eq: TAG_MASK & TAG_CUSTOM, TAG_CUSTOM); static_assert!(@usize_eq: TAG_MASK & TAG_OS, TAG_OS); diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 71d91f213..604b795cd 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -5,7 +5,7 @@ //! the [`Read`] and [`Write`] traits, which provide the //! most general interface for reading and writing input and output. //! -//! # Read and Write +//! ## Read and Write //! //! Because they are traits, [`Read`] and [`Write`] are implemented by a number //! of other types, and you can implement them for your types too. As such, @@ -238,6 +238,47 @@ //! contract. The implementation of many of these functions are subject to change over //! time and may call fewer or more syscalls/library functions. //! +//! ## I/O Safety +//! +//! Rust follows an I/O safety discipline that is comparable to its memory safety discipline. This +//! means that file descriptors can be *exclusively owned*. (Here, "file descriptor" is meant to +//! subsume similar concepts that exist across a wide range of operating systems even if they might +//! use a different name, such as "handle".) An exclusively owned file descriptor is one that no +//! other code is allowed to access in any way, but the owner is allowed to access and even close +//! it any time. A type that owns its file descriptor should usually close it in its `drop` +//! function. Types like [`File`] own their file descriptor. Similarly, file descriptors +//! can be *borrowed*, granting the temporary right to perform operations on this file descriptor. +//! This indicates that the file descriptor will not be closed for the lifetime of the borrow, but +//! it does *not* imply any right to close this file descriptor, since it will likely be owned by +//! someone else. +//! +//! The platform-specific parts of the Rust standard library expose types that reflect these +//! concepts, see [`os::unix`] and [`os::windows`]. +//! +//! To uphold I/O safety, it is crucial that no code acts on file descriptors it does not own or +//! borrow, and no code closes file descriptors it does not own. In other words, a safe function +//! that takes a regular integer, treats it as a file descriptor, and acts on it, is *unsound*. +//! +//! Not upholding I/O safety and acting on a file descriptor without proof of ownership can lead to +//! misbehavior and even Undefined Behavior in code that relies on ownership of its file +//! descriptors: a closed file descriptor could be re-allocated, so the original owner of that file +//! descriptor is now working on the wrong file. Some code might even rely on fully encapsulating +//! its file descriptors with no operations being performed by any other part of the program. +//! +//! Note that exclusive ownership of a file descriptor does *not* imply exclusive ownership of the +//! underlying kernel object that the file descriptor references (also called "file description" on +//! some operating systems). File descriptors basically work like [`Arc`]: when you receive an owned +//! file descriptor, you cannot know whether there are any other file descriptors that reference the +//! same kernel object. However, when you create a new kernel object, you know that you are holding +//! the only reference to it. Just be careful not to lend it to anyone, since they can obtain a +//! clone and then you can no longer know what the reference count is! In that sense, [`OwnedFd`] is +//! like `Arc` and [`BorrowedFd<'a>`] is like `&'a Arc` (and similar for the Windows types). In +//! particular, given a `BorrowedFd<'a>`, you are not allowed to close the file descriptor -- just +//! like how, given a `&'a Arc`, you are not allowed to decrement the reference count and +//! potentially free the underlying object. There is no equivalent to `Box` for file descriptors in +//! the standard library (that would be a type that guarantees that the reference count is `1`), +//! however, it would be possible for a crate to define a type with those semantics. +//! //! [`File`]: crate::fs::File //! [`TcpStream`]: crate::net::TcpStream //! [`io::stdout`]: stdout @@ -245,6 +286,11 @@ //! [`?` operator]: ../../book/appendix-02-operators.html //! [`Result`]: crate::result::Result //! [`.unwrap()`]: crate::result::Result::unwrap +//! [`os::unix`]: ../os/unix/io/index.html +//! [`os::windows`]: ../os/windows/io/index.html +//! [`OwnedFd`]: ../os/fd/struct.OwnedFd.html +//! [`BorrowedFd<'a>`]: ../os/fd/struct.BorrowedFd.html +//! [`Arc`]: crate::sync::Arc #![stable(feature = "rust1", since = "1.0.0")] @@ -390,7 +436,7 @@ pub(crate) fn default_read_to_end<R: Read + ?Sized>( let mut cursor = read_buf.unfilled(); match r.read_buf(cursor.reborrow()) { Ok(()) => {} - Err(e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) if e.is_interrupted() => continue, Err(e) => return Err(e), } @@ -421,7 +467,7 @@ pub(crate) fn default_read_to_end<R: Read + ?Sized>( buf.extend_from_slice(&probe[..n]); break; } - Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, + Err(ref e) if e.is_interrupted() => continue, Err(e) => return Err(e), } } @@ -470,7 +516,7 @@ pub(crate) fn default_read_exact<R: Read + ?Sized>(this: &mut R, mut buf: &mut [ let tmp = buf; buf = &mut tmp[n..]; } - Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(ref e) if e.is_interrupted() => {} Err(e) => return Err(e), } } @@ -860,7 +906,7 @@ pub trait Read { let prev_written = cursor.written(); match self.read_buf(cursor.reborrow()) { Ok(()) => {} - Err(e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) if e.is_interrupted() => continue, Err(e) => return Err(e), } @@ -1190,22 +1236,22 @@ impl<'a> IoSliceMut<'a> { pub fn advance_slices(bufs: &mut &mut [IoSliceMut<'a>], n: usize) { // Number of buffers to remove. let mut remove = 0; - // Total length of all the to be removed buffers. - let mut accumulated_len = 0; + // Remaining length before reaching n. + let mut left = n; for buf in bufs.iter() { - if accumulated_len + buf.len() > n { - break; - } else { - accumulated_len += buf.len(); + if let Some(remainder) = left.checked_sub(buf.len()) { + left = remainder; remove += 1; + } else { + break; } } *bufs = &mut take(bufs)[remove..]; if bufs.is_empty() { - assert!(n == accumulated_len, "advancing io slices beyond their length"); + assert!(left == 0, "advancing io slices beyond their length"); } else { - bufs[0].advance(n - accumulated_len) + bufs[0].advance(left); } } } @@ -1333,22 +1379,25 @@ impl<'a> IoSlice<'a> { pub fn advance_slices(bufs: &mut &mut [IoSlice<'a>], n: usize) { // Number of buffers to remove. let mut remove = 0; - // Total length of all the to be removed buffers. - let mut accumulated_len = 0; + // Remaining length before reaching n. This prevents overflow + // that could happen if the length of slices in `bufs` were instead + // accumulated. Those slice may be aliased and, if they are large + // enough, their added length may overflow a `usize`. + let mut left = n; for buf in bufs.iter() { - if accumulated_len + buf.len() > n { - break; - } else { - accumulated_len += buf.len(); + if let Some(remainder) = left.checked_sub(buf.len()) { + left = remainder; remove += 1; + } else { + break; } } *bufs = &mut take(bufs)[remove..]; if bufs.is_empty() { - assert!(n == accumulated_len, "advancing io slices beyond their length"); + assert!(left == 0, "advancing io slices beyond their length"); } else { - bufs[0].advance(n - accumulated_len) + bufs[0].advance(left); } } } @@ -1579,7 +1628,7 @@ pub trait Write { )); } Ok(n) => buf = &buf[n..], - Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(ref e) if e.is_interrupted() => {} Err(e) => return Err(e), } } @@ -1647,7 +1696,7 @@ pub trait Write { )); } Ok(n) => IoSlice::advance_slices(&mut bufs, n), - Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + Err(ref e) if e.is_interrupted() => {} Err(e) => return Err(e), } } @@ -1943,7 +1992,7 @@ fn read_until<R: BufRead + ?Sized>(r: &mut R, delim: u8, buf: &mut Vec<u8>) -> R let (done, used) = { let available = match r.fill_buf() { Ok(n) => n, - Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, + Err(ref e) if e.is_interrupted() => continue, Err(e) => return Err(e), }; match memchr::memchr(delim, available) { @@ -2734,7 +2783,7 @@ impl<R: Read> Iterator for Bytes<R> { return match self.inner.read(slice::from_mut(&mut byte)) { Ok(0) => None, Ok(..) => Some(Ok(byte)), - Err(ref e) if e.kind() == ErrorKind::Interrupted => continue, + Err(ref e) if e.is_interrupted() => continue, Err(e) => Some(Err(e)), }; } |