summaryrefslogtreecommitdiffstats
path: root/library/std/src/io/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'library/std/src/io/mod.rs')
-rw-r--r--library/std/src/io/mod.rs99
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)),
};
}