summaryrefslogtreecommitdiffstats
path: root/library/std/src/io
diff options
context:
space:
mode:
Diffstat (limited to 'library/std/src/io')
-rw-r--r--library/std/src/io/buffered/bufwriter.rs2
-rw-r--r--library/std/src/io/copy.rs51
-rw-r--r--library/std/src/io/copy/tests.rs12
-rw-r--r--library/std/src/io/error.rs25
-rw-r--r--library/std/src/io/error/repr_bitpacked.rs3
-rw-r--r--library/std/src/io/mod.rs99
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)),
};
}