]> git.proxmox.com Git - rustc.git/blame - library/std/src/sys/unix/fd.rs
New upstream version 1.69.0+dfsg1
[rustc.git] / library / std / src / sys / unix / fd.rs
CommitLineData
dfeec247 1#![unstable(reason = "not public", issue = "none", feature = "fd")]
54a0048b 2
1b1a35ee
XL
3#[cfg(test)]
4mod tests;
5
532ac7d7 6use crate::cmp;
f2b60f7d 7use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, Read};
94222f64 8use crate::os::unix::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
532ac7d7 9use crate::sys::cvt;
94222f64 10use crate::sys_common::{AsInner, FromInner, IntoInner};
532ac7d7 11
04454e1e
FG
12#[cfg(any(
13 target_os = "android",
14 target_os = "linux",
15 target_os = "emscripten",
16 target_os = "l4re"
17))]
18use libc::off64_t;
19#[cfg(not(any(
20 target_os = "linux",
21 target_os = "emscripten",
22 target_os = "l4re",
23 target_os = "android"
24)))]
25use libc::off_t as off64_t;
26
32a655c1 27#[derive(Debug)]
94222f64 28pub struct FileDesc(OwnedFd);
85aaf69f 29
3dfed10e
XL
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")]
923072b8 39const READ_LIMIT: usize = libc::c_int::MAX as usize - 1;
3dfed10e
XL
40#[cfg(not(target_os = "macos"))]
41const READ_LIMIT: usize = libc::ssize_t::MAX as usize;
42
1b1a35ee
XL
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",
064997fb 50 target_os = "watchos",
1b1a35ee
XL
51))]
52const fn max_iov() -> usize {
53 libc::IOV_MAX as usize
54}
3dfed10e 55
9ffffee4
FG
56#[cfg(any(
57 target_os = "android",
58 target_os = "emscripten",
59 target_os = "linux",
60 target_os = "nto",
61))]
1b1a35ee
XL
62const fn max_iov() -> usize {
63 libc::UIO_MAXIOV as usize
3dfed10e
XL
64}
65
1b1a35ee
XL
66#[cfg(not(any(
67 target_os = "android",
68 target_os = "dragonfly",
69 target_os = "emscripten",
70 target_os = "freebsd",
71 target_os = "ios",
72 target_os = "linux",
73 target_os = "macos",
74 target_os = "netbsd",
9ffffee4 75 target_os = "nto",
1b1a35ee 76 target_os = "openbsd",
064997fb
FG
77 target_os = "horizon",
78 target_os = "watchos",
1b1a35ee
XL
79)))]
80const fn max_iov() -> usize {
3dfed10e 81 16 // The minimum value required by POSIX.
32a655c1
SL
82}
83
85aaf69f 84impl FileDesc {
85aaf69f 85 pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
54a0048b 86 let ret = cvt(unsafe {
94222f64
XL
87 libc::read(
88 self.as_raw_fd(),
923072b8 89 buf.as_mut_ptr() as *mut libc::c_void,
94222f64
XL
90 cmp::min(buf.len(), READ_LIMIT),
91 )
54a0048b 92 })?;
85aaf69f
SL
93 Ok(ret as usize)
94 }
95
923072b8 96 #[cfg(not(any(target_os = "espidf", target_os = "horizon")))]
48663c56 97 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
9fa01778 98 let ret = cvt(unsafe {
60c5eb7d 99 libc::readv(
94222f64 100 self.as_raw_fd(),
9ffffee4 101 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
923072b8 102 cmp::min(bufs.len(), max_iov()) as libc::c_int,
60c5eb7d 103 )
9fa01778
XL
104 })?;
105 Ok(ret as usize)
106 }
107
923072b8 108 #[cfg(any(target_os = "espidf", target_os = "horizon"))]
94222f64 109 pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
9ffffee4 110 io::default_read_vectored(|b| self.read(b), bufs)
94222f64
XL
111 }
112
f9f354fc
XL
113 #[inline]
114 pub fn is_read_vectored(&self) -> bool {
923072b8 115 cfg!(not(any(target_os = "espidf", target_os = "horizon")))
f9f354fc
XL
116 }
117
54a0048b
SL
118 pub fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
119 let mut me = self;
120 (&mut me).read_to_end(buf)
121 }
122
c30ab7b3 123 pub fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
a2a8927a
XL
124 #[cfg(not(any(target_os = "linux", target_os = "android")))]
125 use libc::pread as pread64;
126 #[cfg(any(target_os = "linux", target_os = "android"))]
127 use libc::pread64;
c30ab7b3
SL
128
129 unsafe {
a2a8927a 130 cvt(pread64(
94222f64 131 self.as_raw_fd(),
923072b8 132 buf.as_mut_ptr() as *mut libc::c_void,
3dfed10e 133 cmp::min(buf.len(), READ_LIMIT),
04454e1e 134 offset as off64_t,
a2a8927a 135 ))
60c5eb7d 136 .map(|n| n as usize)
c30ab7b3
SL
137 }
138 }
139
f2b60f7d 140 pub fn read_buf(&self, mut cursor: BorrowedCursor<'_>) -> io::Result<()> {
a2a8927a
XL
141 let ret = cvt(unsafe {
142 libc::read(
143 self.as_raw_fd(),
f2b60f7d
FG
144 cursor.as_mut().as_mut_ptr() as *mut libc::c_void,
145 cmp::min(cursor.capacity(), READ_LIMIT),
a2a8927a
XL
146 )
147 })?;
148
149 // Safety: `ret` bytes were written to the initialized portion of the buffer
150 unsafe {
f2b60f7d 151 cursor.advance(ret as usize);
a2a8927a 152 }
a2a8927a
XL
153 Ok(())
154 }
155
9ffffee4
FG
156 #[cfg(any(
157 target_os = "emscripten",
158 target_os = "freebsd",
159 target_os = "fuchsia",
160 target_os = "illumos",
161 target_os = "linux",
162 target_os = "netbsd",
163 ))]
164 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
165 let ret = cvt(unsafe {
166 libc::preadv(
167 self.as_raw_fd(),
168 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
169 cmp::min(bufs.len(), max_iov()) as libc::c_int,
170 offset as _,
171 )
172 })?;
173 Ok(ret as usize)
174 }
175
176 #[cfg(not(any(
177 target_os = "android",
178 target_os = "emscripten",
179 target_os = "freebsd",
180 target_os = "fuchsia",
181 target_os = "illumos",
182 target_os = "ios",
183 target_os = "linux",
184 target_os = "macos",
185 target_os = "netbsd",
186 )))]
187 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
188 io::default_read_vectored(|b| self.read_at(b, offset), bufs)
189 }
190
191 // We support some old Android versions that do not have `preadv` in libc,
192 // so we use weak linkage and fallback to a direct syscall if not available.
193 //
194 // On 32-bit targets, we don't want to deal with weird ABI issues around
195 // passing 64-bits parameters to syscalls, so we fallback to the default
196 // implementation if `preadv` is not available.
197 #[cfg(all(target_os = "android", target_pointer_width = "64"))]
198 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
199 super::weak::syscall! {
200 fn preadv(
201 fd: libc::c_int,
202 iovec: *const libc::iovec,
203 n_iovec: libc::c_int,
204 offset: off64_t
205 ) -> isize
206 }
207
208 let ret = cvt(unsafe {
209 preadv(
210 self.as_raw_fd(),
211 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
212 cmp::min(bufs.len(), max_iov()) as libc::c_int,
213 offset as _,
214 )
215 })?;
216 Ok(ret as usize)
217 }
218
219 // We support old MacOS and iOS versions that do not have `preadv`. There is
220 // no `syscall` possible in these platform.
221 #[cfg(any(
222 all(target_os = "android", target_pointer_width = "32"),
223 target_os = "ios",
224 target_os = "macos",
225 ))]
226 pub fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
227 super::weak::weak!(fn preadv64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
228
229 match preadv64.get() {
230 Some(preadv) => {
231 let ret = cvt(unsafe {
232 preadv(
233 self.as_raw_fd(),
234 bufs.as_mut_ptr() as *mut libc::iovec as *const libc::iovec,
235 cmp::min(bufs.len(), max_iov()) as libc::c_int,
236 offset as _,
237 )
238 })?;
239 Ok(ret as usize)
240 }
241 None => io::default_read_vectored(|b| self.read_at(b, offset), bufs),
242 }
243 }
244
85aaf69f 245 pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
54a0048b 246 let ret = cvt(unsafe {
94222f64
XL
247 libc::write(
248 self.as_raw_fd(),
923072b8 249 buf.as_ptr() as *const libc::c_void,
94222f64
XL
250 cmp::min(buf.len(), READ_LIMIT),
251 )
54a0048b 252 })?;
85aaf69f
SL
253 Ok(ret as usize)
254 }
9346a6ac 255
923072b8 256 #[cfg(not(any(target_os = "espidf", target_os = "horizon")))]
48663c56 257 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
9fa01778 258 let ret = cvt(unsafe {
60c5eb7d 259 libc::writev(
94222f64 260 self.as_raw_fd(),
60c5eb7d 261 bufs.as_ptr() as *const libc::iovec,
923072b8 262 cmp::min(bufs.len(), max_iov()) as libc::c_int,
60c5eb7d 263 )
9fa01778
XL
264 })?;
265 Ok(ret as usize)
266 }
267
923072b8 268 #[cfg(any(target_os = "espidf", target_os = "horizon"))]
94222f64 269 pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
9ffffee4 270 io::default_write_vectored(|b| self.write(b), bufs)
94222f64
XL
271 }
272
f9f354fc
XL
273 #[inline]
274 pub fn is_write_vectored(&self) -> bool {
923072b8 275 cfg!(not(any(target_os = "espidf", target_os = "horizon")))
f9f354fc
XL
276 }
277
c30ab7b3 278 pub fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
a2a8927a
XL
279 #[cfg(not(any(target_os = "linux", target_os = "android")))]
280 use libc::pwrite as pwrite64;
281 #[cfg(any(target_os = "linux", target_os = "android"))]
282 use libc::pwrite64;
c30ab7b3
SL
283
284 unsafe {
a2a8927a 285 cvt(pwrite64(
94222f64 286 self.as_raw_fd(),
923072b8 287 buf.as_ptr() as *const libc::c_void,
3dfed10e 288 cmp::min(buf.len(), READ_LIMIT),
04454e1e 289 offset as off64_t,
a2a8927a 290 ))
60c5eb7d 291 .map(|n| n as usize)
c30ab7b3
SL
292 }
293 }
294
9ffffee4
FG
295 #[cfg(any(
296 target_os = "emscripten",
297 target_os = "freebsd",
298 target_os = "fuchsia",
299 target_os = "illumos",
300 target_os = "linux",
301 target_os = "netbsd",
302 ))]
303 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
304 let ret = cvt(unsafe {
305 libc::pwritev(
306 self.as_raw_fd(),
307 bufs.as_ptr() as *const libc::iovec,
308 cmp::min(bufs.len(), max_iov()) as libc::c_int,
309 offset as _,
310 )
311 })?;
312 Ok(ret as usize)
313 }
314
315 #[cfg(not(any(
316 target_os = "android",
317 target_os = "emscripten",
318 target_os = "freebsd",
319 target_os = "fuchsia",
320 target_os = "illumos",
321 target_os = "ios",
322 target_os = "linux",
323 target_os = "macos",
324 target_os = "netbsd",
325 )))]
326 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
327 io::default_write_vectored(|b| self.write_at(b, offset), bufs)
328 }
329
330 // We support some old Android versions that do not have `pwritev` in libc,
331 // so we use weak linkage and fallback to a direct syscall if not available.
332 //
333 // On 32-bit targets, we don't want to deal with weird ABI issues around
334 // passing 64-bits parameters to syscalls, so we fallback to the default
335 // implementation if `pwritev` is not available.
336 #[cfg(all(target_os = "android", target_pointer_width = "64"))]
337 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
338 super::weak::syscall! {
339 fn pwritev(
340 fd: libc::c_int,
341 iovec: *const libc::iovec,
342 n_iovec: libc::c_int,
343 offset: off64_t
344 ) -> isize
345 }
346
347 let ret = cvt(unsafe {
348 pwritev(
349 self.as_raw_fd(),
350 bufs.as_ptr() as *const libc::iovec,
351 cmp::min(bufs.len(), max_iov()) as libc::c_int,
352 offset as _,
353 )
354 })?;
355 Ok(ret as usize)
356 }
357
358 // We support old MacOS and iOS versions that do not have `pwritev`. There is
359 // no `syscall` possible in these platform.
360 #[cfg(any(
361 all(target_os = "android", target_pointer_width = "32"),
362 target_os = "ios",
363 target_os = "macos",
364 ))]
365 pub fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
366 super::weak::weak!(fn pwritev64(libc::c_int, *const libc::iovec, libc::c_int, off64_t) -> isize);
367
368 match pwritev64.get() {
369 Some(pwritev) => {
370 let ret = cvt(unsafe {
371 pwritev(
372 self.as_raw_fd(),
373 bufs.as_ptr() as *const libc::iovec,
374 cmp::min(bufs.len(), max_iov()) as libc::c_int,
375 offset as _,
376 )
377 })?;
378 Ok(ret as usize)
379 }
380 None => io::default_write_vectored(|b| self.write_at(b, offset), bufs),
381 }
94b46f34
XL
382 }
383
60c5eb7d
XL
384 #[cfg(not(any(
385 target_env = "newlib",
386 target_os = "solaris",
ba9703b0 387 target_os = "illumos",
60c5eb7d
XL
388 target_os = "emscripten",
389 target_os = "fuchsia",
390 target_os = "l4re",
391 target_os = "linux",
392 target_os = "haiku",
29967ef6 393 target_os = "redox",
9ffffee4
FG
394 target_os = "vxworks",
395 target_os = "nto",
60c5eb7d 396 )))]
3157f602 397 pub fn set_cloexec(&self) -> io::Result<()> {
9346a6ac 398 unsafe {
94222f64 399 cvt(libc::ioctl(self.as_raw_fd(), libc::FIOCLEX))?;
3157f602 400 Ok(())
92a42be0
SL
401 }
402 }
60c5eb7d 403 #[cfg(any(
923072b8 404 all(target_env = "newlib", not(any(target_os = "espidf", target_os = "horizon"))),
60c5eb7d 405 target_os = "solaris",
ba9703b0 406 target_os = "illumos",
60c5eb7d
XL
407 target_os = "emscripten",
408 target_os = "fuchsia",
409 target_os = "l4re",
410 target_os = "linux",
411 target_os = "haiku",
29967ef6 412 target_os = "redox",
9ffffee4
FG
413 target_os = "vxworks",
414 target_os = "nto",
60c5eb7d 415 ))]
3157f602 416 pub fn set_cloexec(&self) -> io::Result<()> {
92a42be0 417 unsafe {
94222f64 418 let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFD))?;
8bb4bdeb
XL
419 let new = previous | libc::FD_CLOEXEC;
420 if new != previous {
94222f64 421 cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFD, new))?;
8bb4bdeb 422 }
3157f602 423 Ok(())
9346a6ac
AL
424 }
425 }
923072b8 426 #[cfg(any(target_os = "espidf", target_os = "horizon"))]
94222f64 427 pub fn set_cloexec(&self) -> io::Result<()> {
923072b8
FG
428 // FD_CLOEXEC is not supported in ESP-IDF and Horizon OS but there's no need to,
429 // because neither supports spawning processes.
94222f64
XL
430 Ok(())
431 }
7453a54e 432
8bb4bdeb
XL
433 #[cfg(target_os = "linux")]
434 pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
435 unsafe {
923072b8 436 let v = nonblocking as libc::c_int;
94222f64 437 cvt(libc::ioctl(self.as_raw_fd(), libc::FIONBIO, &v))?;
8bb4bdeb
XL
438 Ok(())
439 }
440 }
441
442 #[cfg(not(target_os = "linux"))]
3157f602 443 pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
54a0048b 444 unsafe {
94222f64 445 let previous = cvt(libc::fcntl(self.as_raw_fd(), libc::F_GETFL))?;
54a0048b
SL
446 let new = if nonblocking {
447 previous | libc::O_NONBLOCK
448 } else {
449 previous & !libc::O_NONBLOCK
450 };
8bb4bdeb 451 if new != previous {
94222f64 452 cvt(libc::fcntl(self.as_raw_fd(), libc::F_SETFL, new))?;
8bb4bdeb 453 }
3157f602 454 Ok(())
54a0048b
SL
455 }
456 }
457
5099ac24 458 #[inline]
7453a54e 459 pub fn duplicate(&self) -> io::Result<FileDesc> {
5099ac24 460 Ok(Self(self.0.try_clone()?))
7453a54e 461 }
85aaf69f
SL
462}
463
54a0048b
SL
464impl<'a> Read for &'a FileDesc {
465 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
466 (**self).read(buf)
467 }
9ffffee4
FG
468
469 fn read_buf(&mut self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
470 (**self).read_buf(cursor)
471 }
54a0048b
SL
472}
473
94222f64
XL
474impl AsInner<OwnedFd> for FileDesc {
475 fn as_inner(&self) -> &OwnedFd {
476 &self.0
477 }
478}
479
480impl IntoInner<OwnedFd> for FileDesc {
481 fn into_inner(self) -> OwnedFd {
482 self.0
483 }
484}
485
486impl FromInner<OwnedFd> for FileDesc {
487 fn from_inner(owned_fd: OwnedFd) -> Self {
488 Self(owned_fd)
489 }
490}
491
492impl AsFd for FileDesc {
493 fn as_fd(&self) -> BorrowedFd<'_> {
494 self.0.as_fd()
495 }
496}
497
498impl AsRawFd for FileDesc {
499 fn as_raw_fd(&self) -> RawFd {
500 self.0.as_raw_fd()
501 }
502}
503
504impl IntoRawFd for FileDesc {
505 fn into_raw_fd(self) -> RawFd {
506 self.0.into_raw_fd()
60c5eb7d 507 }
85aaf69f
SL
508}
509
94222f64
XL
510impl FromRawFd for FileDesc {
511 unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
512 Self(FromRawFd::from_raw_fd(raw_fd))
85aaf69f
SL
513 }
514}