]> git.proxmox.com Git - rustc.git/blob - library/std/src/sys/unix/fd.rs
New upstream version 1.63.0+dfsg1
[rustc.git] / library / std / src / sys / unix / fd.rs
1 #![unstable(reason = "not public", issue = "none", feature = "fd")]
2
3 #[cfg(test)]
4 mod tests;
5
6 use crate::cmp;
7 use crate::io::{self, IoSlice, IoSliceMut, Read, ReadBuf};
8 use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
9 use crate::sys::cvt;
10 use crate::sys_common::{AsInner, FromInner, IntoInner};
11
12 #[cfg(any(
13 target_os = "android",
14 target_os = "linux",
15 target_os = "emscripten",
16 target_os = "l4re"
17 ))]
18 use libc::off64_t;
19 #[cfg(not(any(
20 target_os = "linux",
21 target_os = "emscripten",
22 target_os = "l4re",
23 target_os = "android"
24 )))]
25 use libc::off_t as off64_t;
26
27 #[derive(Debug)]
28 pub struct FileDesc(OwnedFd);
29
30 // The maximum read limit on most POSIX-like systems is `SSIZE_MAX`,
31 // with the man page quoting that if the count of bytes to read is
32 // greater than `SSIZE_MAX` the result is "unspecified".
33 //
34 // On macOS, however, apparently the 64-bit libc is either buggy or
35 // intentionally showing odd behavior by rejecting any read with a size
36 // larger than or equal to INT_MAX. To handle both of these the read
37 // size is capped on both platforms.
38 #[cfg(target_os = "macos")]
39 const READ_LIMIT: usize = libc::c_int::MAX as usize - 1;
40 #[cfg(not(target_os = "macos"))]
41 const READ_LIMIT: usize = libc::ssize_t::MAX as usize;
42
43 #[cfg(any(
44 target_os = "dragonfly",
45 target_os = "freebsd",
46 target_os = "ios",
47 target_os = "macos",
48 target_os = "netbsd",
49 target_os = "openbsd",
50 ))]
51 const fn max_iov() -> usize {
52 libc::IOV_MAX as usize
53 }
54
55 #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "linux"))]
56 const fn max_iov() -> usize {
57 libc::UIO_MAXIOV as usize
58 }
59
60 #[cfg(not(any(
61 target_os = "android",
62 target_os = "dragonfly",
63 target_os = "emscripten",
64 target_os = "freebsd",
65 target_os = "ios",
66 target_os = "linux",
67 target_os = "macos",
68 target_os = "netbsd",
69 target_os = "openbsd",
70 target_os = "horizon"
71 )))]
72 const fn max_iov() -> usize {
73 16 // The minimum value required by POSIX.
74 }
75
76 impl FileDesc {
77 pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
78 let ret = cvt(unsafe {
79 libc::read(
80 self.as_raw_fd(),
81 buf.as_mut_ptr() as *mut libc::c_void,
82 cmp::min(buf.len(), READ_LIMIT),
83 )
84 })?;
85 Ok(ret as usize)
86 }
87
88 #[cfg(not(any(target_os = "espidf", target_os = "horizon")))]
89 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
90 let ret = cvt(unsafe {
91 libc::readv(
92 self.as_raw_fd(),
93 bufs.as_ptr() as *const libc::iovec,
94 cmp::min(bufs.len(), max_iov()) as libc::c_int,
95 )
96 })?;
97 Ok(ret as usize)
98 }
99
100 #[cfg(any(target_os = "espidf", target_os = "horizon"))]
101 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
102 return crate::io::default_read_vectored(|b| self.read(b), bufs);
103 }
104
105 #[inline]
106 pub fn is_read_vectored(&self) -> bool {
107 cfg!(not(any(target_os = "espidf", target_os = "horizon")))
108 }
109
110 pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
111 let mut me = self;
112 (&mut me).read_to_end(buf)
113 }
114
115 pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
116 #[cfg(not(any(target_os = "linux", target_os = "android")))]
117 use libc::pread as pread64;
118 #[cfg(any(target_os = "linux", target_os = "android"))]
119 use libc::pread64;
120
121 unsafe {
122 cvt(pread64(
123 self.as_raw_fd(),
124 buf.as_mut_ptr() as *mut libc::c_void,
125 cmp::min(buf.len(), READ_LIMIT),
126 offset as off64_t,
127 ))
128 .map(|n| n as usize)
129 }
130 }
131
132 pub fn read_buf(&self, buf: &mut ReadBuf<'_>) -> io::Result<()> {
133 let ret = cvt(unsafe {
134 libc::read(
135 self.as_raw_fd(),
136 buf.unfilled_mut().as_mut_ptr() as *mut libc::c_void,
137 cmp::min(buf.remaining(), READ_LIMIT),
138 )
139 })?;
140
141 // Safety: `ret` bytes were written to the initialized portion of the buffer
142 unsafe {
143 buf.assume_init(ret as usize);
144 }
145 buf.add_filled(ret as usize);
146 Ok(())
147 }
148
149 pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
150 let ret = cvt(unsafe {
151 libc::write(
152 self.as_raw_fd(),
153 buf.as_ptr() as *const libc::c_void,
154 cmp::min(buf.len(), READ_LIMIT),
155 )
156 })?;
157 Ok(ret as usize)
158 }
159
160 #[cfg(not(any(target_os = "espidf", target_os = "horizon")))]
161 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
162 let ret = cvt(unsafe {
163 libc::writev(
164 self.as_raw_fd(),
165 bufs.as_ptr() as *const libc::iovec,
166 cmp::min(bufs.len(), max_iov()) as libc::c_int,
167 )
168 })?;
169 Ok(ret as usize)
170 }
171
172 #[cfg(any(target_os = "espidf", target_os = "horizon"))]
173 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
174 return crate::io::default_write_vectored(|b| self.write(b), bufs);
175 }
176
177 #[inline]
178 pub fn is_write_vectored(&self) -> bool {
179 cfg!(not(any(target_os = "espidf", target_os = "horizon")))
180 }
181
182 pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
183 #[cfg(not(any(target_os = "linux", target_os = "android")))]
184 use libc::pwrite as pwrite64;
185 #[cfg(any(target_os = "linux", target_os = "android"))]
186 use libc::pwrite64;
187
188 unsafe {
189 cvt(pwrite64(
190 self.as_raw_fd(),
191 buf.as_ptr() as *const libc::c_void,
192 cmp::min(buf.len(), READ_LIMIT),
193 offset as off64_t,
194 ))
195 .map(|n| n as usize)
196 }
197 }
198
199 #[cfg(target_os = "linux")]
200 pub fn get_cloexec(&self) -> io::Result<bool> {
201 unsafe { Ok((cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))? & libc::FD_CLOEXEC) != 0) }
202 }
203
204 #[cfg(not(any(
205 target_env = "newlib",
206 target_os = "solaris",
207 target_os = "illumos",
208 target_os = "emscripten",
209 target_os = "fuchsia",
210 target_os = "l4re",
211 target_os = "linux",
212 target_os = "haiku",
213 target_os = "redox",
214 target_os = "vxworks"
215 )))]
216 pub fn set_cloexec(&self) -> io::Result<()> {
217 unsafe {
218 cvt(libc::ioctl(self.as_raw_fd(), libc::FIOCLEX))?;
219 Ok(())
220 }
221 }
222 #[cfg(any(
223 all(target_env = "newlib", not(any(target_os = "espidf", target_os = "horizon"))),
224 target_os = "solaris",
225 target_os = "illumos",
226 target_os = "emscripten",
227 target_os = "fuchsia",
228 target_os = "l4re",
229 target_os = "linux",
230 target_os = "haiku",
231 target_os = "redox",
232 target_os = "vxworks"
233 ))]
234 pub fn set_cloexec(&self) -> io::Result<()> {
235 unsafe {
236 let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))?;
237 let new = previous | libc::FD_CLOEXEC;
238 if new != previous {
239 cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFD, new))?;
240 }
241 Ok(())
242 }
243 }
244 #[cfg(any(target_os = "espidf", target_os = "horizon"))]
245 pub fn set_cloexec(&self) -> io::Result<()> {
246 // FD_CLOEXEC is not supported in ESP-IDF and Horizon OS but there's no need to,
247 // because neither supports spawning processes.
248 Ok(())
249 }
250
251 #[cfg(target_os = "linux")]
252 pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
253 unsafe {
254 let v = nonblocking as libc::c_int;
255 cvt(libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &v))?;
256 Ok(())
257 }
258 }
259
260 #[cfg(not(target_os = "linux"))]
261 pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
262 unsafe {
263 let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFL))?;
264 let new = if nonblocking {
265 previous | libc::O_NONBLOCK
266 } else {
267 previous & !libc::O_NONBLOCK
268 };
269 if new != previous {
270 cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFL, new))?;
271 }
272 Ok(())
273 }
274 }
275
276 #[inline]
277 pub fn duplicate(&self) -> io::Result<FileDesc> {
278 Ok(Self(self.0.try_clone()?))
279 }
280 }
281
282 impl<'a> Read for &'a FileDesc {
283 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
284 (**self).read(buf)
285 }
286 }
287
288 impl AsInner<OwnedFd> for FileDesc {
289 fn as_inner(&self) -> &OwnedFd {
290 &self.0
291 }
292 }
293
294 impl IntoInner<OwnedFd> for FileDesc {
295 fn into_inner(self) -> OwnedFd {
296 self.0
297 }
298 }
299
300 impl FromInner<OwnedFd> for FileDesc {
301 fn from_inner(owned_fd: OwnedFd) -> Self {
302 Self(owned_fd)
303 }
304 }
305
306 impl AsFd for FileDesc {
307 fn as_fd(&self) -> BorrowedFd<'_> {
308 self.0.as_fd()
309 }
310 }
311
312 impl AsRawFd for FileDesc {
313 fn as_raw_fd(&self) -> RawFd {
314 self.0.as_raw_fd()
315 }
316 }
317
318 impl IntoRawFd for FileDesc {
319 fn into_raw_fd(self) -> RawFd {
320 self.0.into_raw_fd()
321 }
322 }
323
324 impl FromRawFd for FileDesc {
325 unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
326 Self(FromRawFd::from_raw_fd(raw_fd))
327 }
328 }