diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 18:31:36 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 18:31:36 +0000 |
commit | e02c5b5930c2c9ba3e5423fe12e2ef0155017297 (patch) | |
tree | fd60ebbbb5299e16e5fca8c773ddb74f764760db /library/std/src/io/mod.rs | |
parent | Adding debian version 1.73.0+dfsg1-1. (diff) | |
download | rustc-e02c5b5930c2c9ba3e5423fe12e2ef0155017297.tar.xz rustc-e02c5b5930c2c9ba3e5423fe12e2ef0155017297.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/mod.rs')
-rw-r--r-- | library/std/src/io/mod.rs | 99 |
1 files changed, 74 insertions, 25 deletions
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)), }; } |