]> git.lizzy.rs Git - rust.git/blob - library/std/src/sys/unix/fd.rs
84c4d662161bca7fac8a938c71e5799618787e10
[rust.git] / library / std / src / sys / unix / fd.rs
1 #![unstable(reason = "not public", issue = "none", feature = "fd")]
2
3 use crate::cmp;
4 use crate::io::{self, Initializer, IoSlice, IoSliceMut, Read};
5 use crate::mem;
6 use crate::sys::cvt;
7 use crate::sys_common::AsInner;
8
9 use libc::{c_int, c_void};
10
11 #[derive(Debug)]
12 pub struct FileDesc {
13     fd: c_int,
14 }
15
16 // The maximum read limit on most POSIX-like systems is `SSIZE_MAX`,
17 // with the man page quoting that if the count of bytes to read is
18 // greater than `SSIZE_MAX` the result is "unspecified".
19 //
20 // On macOS, however, apparently the 64-bit libc is either buggy or
21 // intentionally showing odd behavior by rejecting any read with a size
22 // larger than or equal to INT_MAX. To handle both of these the read
23 // size is capped on both platforms.
24 #[cfg(target_os = "macos")]
25 const READ_LIMIT: usize = c_int::MAX as usize - 1;
26 #[cfg(not(target_os = "macos"))]
27 const READ_LIMIT: usize = libc::ssize_t::MAX as usize;
28
29 impl FileDesc {
30     pub fn new(fd: c_int) -> FileDesc {
31         FileDesc { fd }
32     }
33
34     pub fn raw(&self) -> c_int {
35         self.fd
36     }
37
38     /// Extracts the actual file descriptor without closing it.
39     pub fn into_raw(self) -> c_int {
40         let fd = self.fd;
41         mem::forget(self);
42         fd
43     }
44
45     pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
46         let ret = cvt(unsafe {
47             libc::read(self.fd, buf.as_mut_ptr() as *mut c_void, cmp::min(buf.len(), READ_LIMIT))
48         })?;
49         Ok(ret as usize)
50     }
51
52     pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
53         let ret = cvt(unsafe {
54             libc::readv(
55                 self.fd,
56                 bufs.as_ptr() as *const libc::iovec,
57                 cmp::min(bufs.len(), c_int::MAX as usize) as c_int,
58             )
59         })?;
60         Ok(ret as usize)
61     }
62
63     #[inline]
64     pub fn is_read_vectored(&self) -> bool {
65         true
66     }
67
68     pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
69         let mut me = self;
70         (&mut me).read_to_end(buf)
71     }
72
73     pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
74         #[cfg(target_os = "android")]
75         use super::android::cvt_pread64;
76
77         #[cfg(not(target_os = "android"))]
78         unsafe fn cvt_pread64(
79             fd: c_int,
80             buf: *mut c_void,
81             count: usize,
82             offset: i64,
83         ) -> io::Result<isize> {
84             #[cfg(not(target_os = "linux"))]
85             use libc::pread as pread64;
86             #[cfg(target_os = "linux")]
87             use libc::pread64;
88             cvt(pread64(fd, buf, count, offset))
89         }
90
91         unsafe {
92             cvt_pread64(
93                 self.fd,
94                 buf.as_mut_ptr() as *mut c_void,
95                 cmp::min(buf.len(), READ_LIMIT),
96                 offset as i64,
97             )
98             .map(|n| n as usize)
99         }
100     }
101
102     pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
103         let ret = cvt(unsafe {
104             libc::write(self.fd, buf.as_ptr() as *const c_void, cmp::min(buf.len(), READ_LIMIT))
105         })?;
106         Ok(ret as usize)
107     }
108
109     pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
110         let ret = cvt(unsafe {
111             libc::writev(
112                 self.fd,
113                 bufs.as_ptr() as *const libc::iovec,
114                 cmp::min(bufs.len(), c_int::MAX as usize) as c_int,
115             )
116         })?;
117         Ok(ret as usize)
118     }
119
120     #[inline]
121     pub fn is_write_vectored(&self) -> bool {
122         true
123     }
124
125     pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
126         #[cfg(target_os = "android")]
127         use super::android::cvt_pwrite64;
128
129         #[cfg(not(target_os = "android"))]
130         unsafe fn cvt_pwrite64(
131             fd: c_int,
132             buf: *const c_void,
133             count: usize,
134             offset: i64,
135         ) -> io::Result<isize> {
136             #[cfg(not(target_os = "linux"))]
137             use libc::pwrite as pwrite64;
138             #[cfg(target_os = "linux")]
139             use libc::pwrite64;
140             cvt(pwrite64(fd, buf, count, offset))
141         }
142
143         unsafe {
144             cvt_pwrite64(
145                 self.fd,
146                 buf.as_ptr() as *const c_void,
147                 cmp::min(buf.len(), READ_LIMIT),
148                 offset as i64,
149             )
150             .map(|n| n as usize)
151         }
152     }
153
154     #[cfg(target_os = "linux")]
155     pub fn get_cloexec(&self) -> io::Result<bool> {
156         unsafe { Ok((cvt(libc::fcntl(self.fd, libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) }
157     }
158
159     #[cfg(not(any(
160         target_env = "newlib",
161         target_os = "solaris",
162         target_os = "illumos",
163         target_os = "emscripten",
164         target_os = "fuchsia",
165         target_os = "l4re",
166         target_os = "linux",
167         target_os = "haiku",
168         target_os = "redox"
169     )))]
170     pub fn set_cloexec(&self) -> io::Result<()> {
171         unsafe {
172             cvt(libc::ioctl(self.fd, libc::FIOCLEX))?;
173             Ok(())
174         }
175     }
176     #[cfg(any(
177         target_env = "newlib",
178         target_os = "solaris",
179         target_os = "illumos",
180         target_os = "emscripten",
181         target_os = "fuchsia",
182         target_os = "l4re",
183         target_os = "linux",
184         target_os = "haiku",
185         target_os = "redox"
186     ))]
187     pub fn set_cloexec(&self) -> io::Result<()> {
188         unsafe {
189             let previous = cvt(libc::fcntl(self.fd, libc::F_GETFD))?;
190             let new = previous | libc::FD_CLOEXEC;
191             if new != previous {
192                 cvt(libc::fcntl(self.fd, libc::F_SETFD, new))?;
193             }
194             Ok(())
195         }
196     }
197
198     #[cfg(target_os = "linux")]
199     pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
200         unsafe {
201             let v = nonblocking as c_int;
202             cvt(libc::ioctl(self.fd, libc::FIONBIO, &v))?;
203             Ok(())
204         }
205     }
206
207     #[cfg(not(target_os = "linux"))]
208     pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
209         unsafe {
210             let previous = cvt(libc::fcntl(self.fd, libc::F_GETFL))?;
211             let new = if nonblocking {
212                 previous | libc::O_NONBLOCK
213             } else {
214                 previous & !libc::O_NONBLOCK
215             };
216             if new != previous {
217                 cvt(libc::fcntl(self.fd, libc::F_SETFL, new))?;
218             }
219             Ok(())
220         }
221     }
222
223     pub fn duplicate(&self) -> io::Result<FileDesc> {
224         // We want to atomically duplicate this file descriptor and set the
225         // CLOEXEC flag, and currently that's done via F_DUPFD_CLOEXEC. This
226         // is a POSIX flag that was added to Linux in 2.6.24.
227         let fd = cvt(unsafe { libc::fcntl(self.raw(), libc::F_DUPFD_CLOEXEC, 0) })?;
228         Ok(FileDesc::new(fd))
229     }
230 }
231
232 impl<'a> Read for &'a FileDesc {
233     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
234         (**self).read(buf)
235     }
236
237     #[inline]
238     unsafe fn initializer(&self) -> Initializer {
239         Initializer::nop()
240     }
241 }
242
243 impl AsInner<c_int> for FileDesc {
244     fn as_inner(&self) -> &c_int {
245         &self.fd
246     }
247 }
248
249 impl Drop for FileDesc {
250     fn drop(&mut self) {
251         // Note that errors are ignored when closing a file descriptor. The
252         // reason for this is that if an error occurs we don't actually know if
253         // the file descriptor was closed or not, and if we retried (for
254         // something like EINTR), we might close another valid file descriptor
255         // opened after we closed ours.
256         let _ = unsafe { libc::close(self.fd) };
257     }
258 }