1 // Copyright 2017 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 //! Small system utility modules for usage by other modules.
6
7 // Fail sys_util compilation on windows.
8 // This will make any unintentional windows code submitted to the crate unusable.
9 #[cfg(windows)]
10 compile_error!("sys_util is not windows friendly crate. See/use win_sys_util.");
11
12 #[cfg(target_os = "android")]
13 mod android;
14 #[cfg(target_os = "android")]
15 use android as target_os;
16 #[cfg(target_os = "linux")]
17 mod linux;
18 #[cfg(target_os = "linux")]
19 use linux as target_os;
20 #[macro_use]
21 pub mod handle_eintr;
22 #[macro_use]
23 pub mod ioctl;
24 #[macro_use]
25 pub mod syslog;
26 mod acpi_event;
27 mod capabilities;
28 mod clock;
29 mod descriptor;
30 mod eventfd;
31 mod file_flags;
32 pub mod file_traits;
33 mod get_filesystem_type;
34 mod mmap;
35 pub mod net;
36 mod netlink;
37 mod poll;
38 mod priority;
39 pub mod rand;
40 mod raw_fd;
41 pub mod read_dir;
42 mod sched;
43 pub mod scoped_signal_handler;
44 mod shm;
45 pub mod signal;
46 mod signalfd;
47 mod sock_ctrl_msg;
48 mod terminal;
49 mod timerfd;
50 pub mod vsock;
51 mod write_zeroes;
52
53 pub use crate::descriptor_reflection::{
54 deserialize_with_descriptors, with_as_descriptor, with_raw_descriptor, FileSerdeWrapper,
55 SerializeDescriptors,
56 };
57 pub use crate::{
58 errno::{Error, Result, *},
59 generate_scoped_event,
60 };
61 pub use acpi_event::*;
62 pub use base_poll_token_derive::*;
63 pub use capabilities::drop_capabilities;
64 pub use clock::{Clock, FakeClock};
65 pub use descriptor::*;
66 pub use eventfd::*;
67 pub use file_flags::*;
68 pub use get_filesystem_type::*;
69 pub use ioctl::*;
70 pub use mmap::*;
71 pub use netlink::*;
72 pub use poll::*;
73 pub use priority::*;
74 pub use raw_fd::*;
75 pub use sched::*;
76 pub use scoped_signal_handler::*;
77 pub use shm::*;
78 pub use signal::*;
79 pub use signalfd::*;
80 pub use sock_ctrl_msg::*;
81 pub use terminal::*;
82 pub use timerfd::*;
83
84 use crate::descriptor::{FromRawDescriptor, SafeDescriptor};
85 pub use file_traits::{
86 AsRawFds, FileAllocate, FileGetLen, FileReadWriteAtVolatile, FileReadWriteVolatile, FileSetLen,
87 FileSync,
88 };
89 pub use mmap::Error as MmapError;
90 pub use signalfd::Error as SignalFdError;
91 pub use write_zeroes::{PunchHole, WriteZeroes, WriteZeroesAt};
92
93 use std::{
94 cell::Cell,
95 convert::TryFrom,
96 ffi::CStr,
97 fs::{remove_file, File, OpenOptions},
98 mem,
99 ops::Deref,
100 os::unix::{
101 io::{AsRawFd, FromRawFd, RawFd},
102 net::{UnixDatagram, UnixListener},
103 },
104 path::Path,
105 ptr,
106 time::Duration,
107 };
108
109 use libc::{
110 c_int, c_long, fcntl, pipe2, syscall, sysconf, waitpid, SYS_getpid, SYS_gettid, EINVAL,
111 F_GETFL, F_SETFL, O_CLOEXEC, SIGKILL, WNOHANG, _SC_IOV_MAX, _SC_PAGESIZE,
112 };
113
114 /// Re-export libc types that are part of the API.
115 pub type Pid = libc::pid_t;
116 pub type Uid = libc::uid_t;
117 pub type Gid = libc::gid_t;
118 pub type Mode = libc::mode_t;
119
120 /// Used to mark types as !Sync.
121 pub type UnsyncMarker = std::marker::PhantomData<Cell<usize>>;
122
123 #[macro_export]
124 macro_rules! syscall {
125 ($e:expr) => {{
126 let res = $e;
127 if res < 0 {
128 $crate::platform::errno_result()
129 } else {
130 Ok(res)
131 }
132 }};
133 }
134
135 /// Safe wrapper for `sysconf(_SC_PAGESIZE)`.
136 #[inline(always)]
pagesize() -> usize137 pub fn pagesize() -> usize {
138 // Trivially safe
139 unsafe { sysconf(_SC_PAGESIZE) as usize }
140 }
141
142 /// Safe wrapper for `sysconf(_SC_IOV_MAX)`.
iov_max() -> usize143 pub fn iov_max() -> usize {
144 // Trivially safe
145 unsafe { sysconf(_SC_IOV_MAX) as usize }
146 }
147
148 /// Uses the system's page size in bytes to round the given value up to the nearest page boundary.
149 #[inline(always)]
round_up_to_page_size(v: usize) -> usize150 pub fn round_up_to_page_size(v: usize) -> usize {
151 let page_mask = pagesize() - 1;
152 (v + page_mask) & !page_mask
153 }
154
155 /// This bypasses `libc`'s caching `getpid(2)` wrapper which can be invalid if a raw clone was used
156 /// elsewhere.
157 #[inline(always)]
getpid() -> Pid158 pub fn getpid() -> Pid {
159 // Safe because this syscall can never fail and we give it a valid syscall number.
160 unsafe { syscall(SYS_getpid as c_long) as Pid }
161 }
162
163 /// Safe wrapper for the gettid Linux systemcall.
gettid() -> Pid164 pub fn gettid() -> Pid {
165 // Calling the gettid() sycall is always safe.
166 unsafe { syscall(SYS_gettid as c_long) as Pid }
167 }
168
169 /// Safe wrapper for `getsid(2)`.
getsid(pid: Option<Pid>) -> Result<Pid>170 pub fn getsid(pid: Option<Pid>) -> Result<Pid> {
171 // Calling the getsid() sycall is always safe.
172 syscall!(unsafe { libc::getsid(pid.unwrap_or(0)) } as Pid)
173 }
174
175 /// Wrapper for `setsid(2)`.
setsid() -> Result<Pid>176 pub fn setsid() -> Result<Pid> {
177 // Safe because the return code is checked.
178 syscall!(unsafe { libc::setsid() as Pid })
179 }
180
181 /// Safe wrapper for `geteuid(2)`.
182 #[inline(always)]
geteuid() -> Uid183 pub fn geteuid() -> Uid {
184 // trivially safe
185 unsafe { libc::geteuid() }
186 }
187
188 /// Safe wrapper for `getegid(2)`.
189 #[inline(always)]
getegid() -> Gid190 pub fn getegid() -> Gid {
191 // trivially safe
192 unsafe { libc::getegid() }
193 }
194
195 /// Safe wrapper for chown(2).
196 #[inline(always)]
chown(path: &CStr, uid: Uid, gid: Gid) -> Result<()>197 pub fn chown(path: &CStr, uid: Uid, gid: Gid) -> Result<()> {
198 // Safe since we pass in a valid string pointer and check the return value.
199 syscall!(unsafe { libc::chown(path.as_ptr(), uid, gid) }).map(|_| ())
200 }
201
202 /// Safe wrapper for fchmod(2).
203 #[inline(always)]
fchmod<A: AsRawFd>(fd: &A, mode: Mode) -> Result<()>204 pub fn fchmod<A: AsRawFd>(fd: &A, mode: Mode) -> Result<()> {
205 // Safe since the function does not operate on pointers and check the return value.
206 syscall!(unsafe { libc::fchmod(fd.as_raw_fd(), mode) }).map(|_| ())
207 }
208
209 /// Safe wrapper for fchown(2).
210 #[inline(always)]
fchown<A: AsRawFd>(fd: &A, uid: Uid, gid: Gid) -> Result<()>211 pub fn fchown<A: AsRawFd>(fd: &A, uid: Uid, gid: Gid) -> Result<()> {
212 // Safe since the function does not operate on pointers and check the return value.
213 syscall!(unsafe { libc::fchown(fd.as_raw_fd(), uid, gid) }).map(|_| ())
214 }
215
216 /// The operation to perform with `flock`.
217 pub enum FlockOperation {
218 LockShared,
219 LockExclusive,
220 Unlock,
221 }
222
223 /// Safe wrapper for flock(2) with the operation `op` and optionally `nonblocking`. The lock will be
224 /// dropped automatically when `file` is dropped.
225 #[inline(always)]
flock(file: &dyn AsRawFd, op: FlockOperation, nonblocking: bool) -> Result<()>226 pub fn flock(file: &dyn AsRawFd, op: FlockOperation, nonblocking: bool) -> Result<()> {
227 let mut operation = match op {
228 FlockOperation::LockShared => libc::LOCK_SH,
229 FlockOperation::LockExclusive => libc::LOCK_EX,
230 FlockOperation::Unlock => libc::LOCK_UN,
231 };
232
233 if nonblocking {
234 operation |= libc::LOCK_NB;
235 }
236
237 // Safe since we pass in a valid fd and flock operation, and check the return value.
238 syscall!(unsafe { libc::flock(file.as_raw_fd(), operation) }).map(|_| ())
239 }
240
241 /// The operation to perform with `fallocate`.
242 pub enum FallocateMode {
243 PunchHole,
244 ZeroRange,
245 Allocate,
246 }
247
248 /// Safe wrapper for `fallocate()`.
fallocate( file: &dyn AsRawFd, mode: FallocateMode, keep_size: bool, offset: u64, len: u64, ) -> Result<()>249 pub fn fallocate(
250 file: &dyn AsRawFd,
251 mode: FallocateMode,
252 keep_size: bool,
253 offset: u64,
254 len: u64,
255 ) -> Result<()> {
256 let offset = if offset > libc::off64_t::max_value() as u64 {
257 return Err(Error::new(libc::EINVAL));
258 } else {
259 offset as libc::off64_t
260 };
261
262 let len = if len > libc::off64_t::max_value() as u64 {
263 return Err(Error::new(libc::EINVAL));
264 } else {
265 len as libc::off64_t
266 };
267
268 let mut mode = match mode {
269 FallocateMode::PunchHole => libc::FALLOC_FL_PUNCH_HOLE,
270 FallocateMode::ZeroRange => libc::FALLOC_FL_ZERO_RANGE,
271 FallocateMode::Allocate => 0,
272 };
273
274 if keep_size {
275 mode |= libc::FALLOC_FL_KEEP_SIZE;
276 }
277
278 // Safe since we pass in a valid fd and fallocate mode, validate offset and len,
279 // and check the return value.
280 syscall!(unsafe { libc::fallocate64(file.as_raw_fd(), mode, offset, len) }).map(|_| ())
281 }
282
283 /// A trait used to abstract types that provide a process id that can be operated on.
284 pub trait AsRawPid {
as_raw_pid(&self) -> Pid285 fn as_raw_pid(&self) -> Pid;
286 }
287
288 impl AsRawPid for Pid {
as_raw_pid(&self) -> Pid289 fn as_raw_pid(&self) -> Pid {
290 *self
291 }
292 }
293
294 impl AsRawPid for std::process::Child {
as_raw_pid(&self) -> Pid295 fn as_raw_pid(&self) -> Pid {
296 self.id() as Pid
297 }
298 }
299
300 /// A logical set of the values *status can take from libc::wait and libc::waitpid.
301 pub enum WaitStatus {
302 Continued,
303 Exited(u8),
304 Running,
305 Signaled(Signal),
306 Stopped(Signal),
307 }
308
309 impl From<c_int> for WaitStatus {
from(status: c_int) -> WaitStatus310 fn from(status: c_int) -> WaitStatus {
311 use WaitStatus::*;
312 if libc::WIFEXITED(status) {
313 Exited(libc::WEXITSTATUS(status) as u8)
314 } else if libc::WIFSIGNALED(status) {
315 Signaled(Signal::try_from(libc::WTERMSIG(status)).unwrap())
316 } else if libc::WIFSTOPPED(status) {
317 Stopped(Signal::try_from(libc::WSTOPSIG(status)).unwrap())
318 } else if libc::WIFCONTINUED(status) {
319 Continued
320 } else {
321 Running
322 }
323 }
324 }
325
326 /// A safe wrapper around waitpid.
327 ///
328 /// On success if a process was reaped, it will be returned as the first value.
329 /// The second returned value is the WaitStatus from the libc::waitpid() call.
330 ///
331 /// Note: this can block if libc::WNOHANG is not set and EINTR is not handled internally.
wait_for_pid<A: AsRawPid>(pid: A, options: c_int) -> Result<(Option<Pid>, WaitStatus)>332 pub fn wait_for_pid<A: AsRawPid>(pid: A, options: c_int) -> Result<(Option<Pid>, WaitStatus)> {
333 let pid = pid.as_raw_pid();
334 let mut status: c_int = 1;
335 // Safe because status is owned and the error is checked.
336 let ret = unsafe { libc::waitpid(pid, &mut status, options) };
337 if ret < 0 {
338 return errno_result();
339 }
340 Ok((
341 if ret == 0 { None } else { Some(ret) },
342 WaitStatus::from(status),
343 ))
344 }
345
346 /// Reaps a child process that has terminated.
347 ///
348 /// Returns `Ok(pid)` where `pid` is the process that was reaped or `Ok(0)` if none of the children
349 /// have terminated. An `Error` is with `errno == ECHILD` if there are no children left to reap.
350 ///
351 /// # Examples
352 ///
353 /// Reaps all child processes until there are no terminated children to reap.
354 ///
355 /// ```
356 /// fn reap_children() {
357 /// loop {
358 /// match crate::platform::reap_child() {
359 /// Ok(0) => println!("no children ready to reap"),
360 /// Ok(pid) => {
361 /// println!("reaped {}", pid);
362 /// continue
363 /// },
364 /// Err(e) if e.errno() == libc::ECHILD => println!("no children left"),
365 /// Err(e) => println!("error reaping children: {}", e),
366 /// }
367 /// break
368 /// }
369 /// }
370 /// ```
reap_child() -> Result<Pid>371 pub fn reap_child() -> Result<Pid> {
372 // Safe because we pass in no memory, prevent blocking with WNOHANG, and check for error.
373 let ret = unsafe { waitpid(-1, ptr::null_mut(), WNOHANG) };
374 if ret == -1 {
375 errno_result()
376 } else {
377 Ok(ret)
378 }
379 }
380
381 /// Kill all processes in the current process group.
382 ///
383 /// On success, this kills all processes in the current process group, including the current
384 /// process, meaning this will not return. This is equivalent to a call to `kill(0, SIGKILL)`.
kill_process_group() -> Result<()>385 pub fn kill_process_group() -> Result<()> {
386 unsafe { kill(0, SIGKILL) }?;
387 // Kill succeeded, so this process never reaches here.
388 unreachable!();
389 }
390
391 /// Spawns a pipe pair where the first pipe is the read end and the second pipe is the write end.
392 ///
393 /// If `close_on_exec` is true, the `O_CLOEXEC` flag will be set during pipe creation.
pipe(close_on_exec: bool) -> Result<(File, File)>394 pub fn pipe(close_on_exec: bool) -> Result<(File, File)> {
395 let flags = if close_on_exec { O_CLOEXEC } else { 0 };
396 let mut pipe_fds = [-1; 2];
397 // Safe because pipe2 will only write 2 element array of i32 to the given pointer, and we check
398 // for error.
399 let ret = unsafe { pipe2(&mut pipe_fds[0], flags) };
400 if ret == -1 {
401 errno_result()
402 } else {
403 // Safe because both fds must be valid for pipe2 to have returned sucessfully and we have
404 // exclusive ownership of them.
405 Ok(unsafe {
406 (
407 File::from_raw_fd(pipe_fds[0]),
408 File::from_raw_fd(pipe_fds[1]),
409 )
410 })
411 }
412 }
413
414 /// Sets the pipe signified with fd to `size`.
415 ///
416 /// Returns the new size of the pipe or an error if the OS fails to set the pipe size.
set_pipe_size(fd: RawFd, size: usize) -> Result<usize>417 pub fn set_pipe_size(fd: RawFd, size: usize) -> Result<usize> {
418 // Safe because fcntl with the `F_SETPIPE_SZ` arg doesn't touch memory.
419 syscall!(unsafe { fcntl(fd, libc::F_SETPIPE_SZ, size as c_int) }).map(|ret| ret as usize)
420 }
421
422 /// Test-only function used to create a pipe that is full. The pipe is created, has its size set to
423 /// the minimum and then has that much data written to it. Use `new_pipe_full` to test handling of
424 /// blocking `write` calls in unit tests.
new_pipe_full() -> Result<(File, File)>425 pub fn new_pipe_full() -> Result<(File, File)> {
426 use std::io::Write;
427
428 let (rx, mut tx) = pipe(true)?;
429 // The smallest allowed size of a pipe is the system page size on linux.
430 let page_size = set_pipe_size(tx.as_raw_fd(), round_up_to_page_size(1))?;
431
432 // Fill the pipe with page_size zeros so the next write call will block.
433 let buf = vec![0u8; page_size];
434 tx.write_all(&buf)?;
435
436 Ok((rx, tx))
437 }
438
439 /// Used to attempt to clean up a named pipe after it is no longer used.
440 pub struct UnlinkUnixDatagram(pub UnixDatagram);
441 impl AsRef<UnixDatagram> for UnlinkUnixDatagram {
as_ref(&self) -> &UnixDatagram442 fn as_ref(&self) -> &UnixDatagram {
443 &self.0
444 }
445 }
446 impl Drop for UnlinkUnixDatagram {
drop(&mut self)447 fn drop(&mut self) {
448 if let Ok(addr) = self.0.local_addr() {
449 if let Some(path) = addr.as_pathname() {
450 if let Err(e) = remove_file(path) {
451 warn!("failed to remove control socket file: {}", e);
452 }
453 }
454 }
455 }
456 }
457
458 /// Used to attempt to clean up a named pipe after it is no longer used.
459 pub struct UnlinkUnixListener(pub UnixListener);
460
461 impl AsRef<UnixListener> for UnlinkUnixListener {
as_ref(&self) -> &UnixListener462 fn as_ref(&self) -> &UnixListener {
463 &self.0
464 }
465 }
466
467 impl Deref for UnlinkUnixListener {
468 type Target = UnixListener;
469
deref(&self) -> &UnixListener470 fn deref(&self) -> &UnixListener {
471 &self.0
472 }
473 }
474
475 impl Drop for UnlinkUnixListener {
drop(&mut self)476 fn drop(&mut self) {
477 if let Ok(addr) = self.0.local_addr() {
478 if let Some(path) = addr.as_pathname() {
479 if let Err(e) = remove_file(path) {
480 warn!("failed to remove control socket file: {}", e);
481 }
482 }
483 }
484 }
485 }
486
487 /// Verifies that |raw_descriptor| is actually owned by this process and duplicates it
488 /// to ensure that we have a unique handle to it.
validate_raw_descriptor(raw_descriptor: RawDescriptor) -> Result<RawDescriptor>489 pub fn validate_raw_descriptor(raw_descriptor: RawDescriptor) -> Result<RawDescriptor> {
490 validate_raw_fd(raw_descriptor)
491 }
492
493 /// Verifies that |raw_fd| is actually owned by this process and duplicates it to ensure that
494 /// we have a unique handle to it.
validate_raw_fd(raw_fd: RawFd) -> Result<RawFd>495 pub fn validate_raw_fd(raw_fd: RawFd) -> Result<RawFd> {
496 // Checking that close-on-exec isn't set helps filter out FDs that were opened by
497 // crosvm as all crosvm FDs are close on exec.
498 // Safe because this doesn't modify any memory and we check the return value.
499 let flags = unsafe { libc::fcntl(raw_fd, libc::F_GETFD) };
500 if flags < 0 || (flags & libc::FD_CLOEXEC) != 0 {
501 return Err(Error::new(libc::EBADF));
502 }
503
504 // Duplicate the fd to ensure that we don't accidentally close an fd previously
505 // opened by another subsystem. Safe because this doesn't modify any memory and
506 // we check the return value.
507 let dup_fd = unsafe { libc::fcntl(raw_fd, libc::F_DUPFD_CLOEXEC, 0) };
508 if dup_fd < 0 {
509 return Err(Error::last());
510 }
511 Ok(dup_fd as RawFd)
512 }
513
514 /// Utility function that returns true if the given FD is readable without blocking.
515 ///
516 /// On an error, such as an invalid or incompatible FD, this will return false, which can not be
517 /// distinguished from a non-ready to read FD.
poll_in(fd: &dyn AsRawFd) -> bool518 pub fn poll_in(fd: &dyn AsRawFd) -> bool {
519 let mut fds = libc::pollfd {
520 fd: fd.as_raw_fd(),
521 events: libc::POLLIN,
522 revents: 0,
523 };
524 // Safe because we give a valid pointer to a list (of 1) FD and check the return value.
525 let ret = unsafe { libc::poll(&mut fds, 1, 0) };
526 // An error probably indicates an invalid FD, or an FD that can't be polled. Returning false in
527 // that case is probably correct as such an FD is unlikely to be readable, although there are
528 // probably corner cases in which that is wrong.
529 if ret == -1 {
530 return false;
531 }
532 fds.revents & libc::POLLIN != 0
533 }
534
535 /// Returns the file flags set for the given `RawFD`
536 ///
537 /// Returns an error if the OS indicates the flags can't be retrieved.
get_fd_flags(fd: RawFd) -> Result<c_int>538 fn get_fd_flags(fd: RawFd) -> Result<c_int> {
539 // Safe because no third parameter is expected and we check the return result.
540 syscall!(unsafe { fcntl(fd, F_GETFL) })
541 }
542
543 /// Sets the file flags set for the given `RawFD`.
544 ///
545 /// Returns an error if the OS indicates the flags can't be retrieved.
set_fd_flags(fd: RawFd, flags: c_int) -> Result<()>546 fn set_fd_flags(fd: RawFd, flags: c_int) -> Result<()> {
547 // Safe because we supply the third parameter and we check the return result.
548 // fcntlt is trusted not to modify the memory of the calling process.
549 syscall!(unsafe { fcntl(fd, F_SETFL, flags) }).map(|_| ())
550 }
551
552 /// Performs a logical OR of the given flags with the FD's flags, setting the given bits for the
553 /// FD.
554 ///
555 /// Returns an error if the OS indicates the flags can't be retrieved or set.
add_fd_flags(fd: RawFd, set_flags: c_int) -> Result<()>556 pub fn add_fd_flags(fd: RawFd, set_flags: c_int) -> Result<()> {
557 let start_flags = get_fd_flags(fd)?;
558 set_fd_flags(fd, start_flags | set_flags)
559 }
560
561 /// Clears the given flags in the FD's flags.
562 ///
563 /// Returns an error if the OS indicates the flags can't be retrieved or set.
clear_fd_flags(fd: RawFd, clear_flags: c_int) -> Result<()>564 pub fn clear_fd_flags(fd: RawFd, clear_flags: c_int) -> Result<()> {
565 let start_flags = get_fd_flags(fd)?;
566 set_fd_flags(fd, start_flags & !clear_flags)
567 }
568
569 /// Return a timespec filed with the specified Duration `duration`.
duration_to_timespec(duration: Duration) -> libc::timespec570 pub fn duration_to_timespec(duration: Duration) -> libc::timespec {
571 // Safe because we are zero-initializing a struct with only primitive member fields.
572 let mut ts: libc::timespec = unsafe { mem::zeroed() };
573
574 ts.tv_sec = duration.as_secs() as libc::time_t;
575 // nsec always fits in i32 because subsec_nanos is defined to be less than one billion.
576 let nsec = duration.subsec_nanos() as i32;
577 ts.tv_nsec = libc::c_long::from(nsec);
578 ts
579 }
580
581 /// Return the maximum Duration that can be used with libc::timespec.
max_timeout() -> Duration582 pub fn max_timeout() -> Duration {
583 Duration::new(libc::time_t::max_value() as u64, 999999999)
584 }
585
586 /// If the given path is of the form /proc/self/fd/N for some N, returns `Ok(Some(N))`. Otherwise
587 /// returns `Ok(None)`.
safe_descriptor_from_path<P: AsRef<Path>>(path: P) -> Result<Option<SafeDescriptor>>588 pub fn safe_descriptor_from_path<P: AsRef<Path>>(path: P) -> Result<Option<SafeDescriptor>> {
589 let path = path.as_ref();
590 if path.parent() == Some(Path::new("/proc/self/fd")) {
591 let raw_descriptor = path
592 .file_name()
593 .and_then(|fd_osstr| fd_osstr.to_str())
594 .and_then(|fd_str| fd_str.parse::<RawFd>().ok())
595 .ok_or_else(|| Error::new(EINVAL))?;
596 let validated_fd = validate_raw_fd(raw_descriptor)?;
597 Ok(Some(
598 // Safe because nothing else has access to validated_fd after this call.
599 unsafe { SafeDescriptor::from_raw_descriptor(validated_fd) },
600 ))
601 } else {
602 Ok(None)
603 }
604 }
605
606 /// Open the file with the given path, or if it is of the form `/proc/self/fd/N` then just use the
607 /// file descriptor.
608 ///
609 /// Note that this will not work properly if the same `/proc/self/fd/N` path is used twice in
610 /// different places, as the metadata (including the offset) will be shared between both file
611 /// descriptors.
open_file<P: AsRef<Path>>(path: P, options: &OpenOptions) -> Result<File>612 pub fn open_file<P: AsRef<Path>>(path: P, options: &OpenOptions) -> Result<File> {
613 let path = path.as_ref();
614 // Special case '/proc/self/fd/*' paths. The FD is already open, just use it.
615 Ok(if let Some(fd) = safe_descriptor_from_path(path)? {
616 fd.into()
617 } else {
618 options.open(path)?
619 })
620 }
621
622 /// Get the max number of open files allowed by the environment.
get_max_open_files() -> Result<u64>623 pub fn get_max_open_files() -> Result<u64> {
624 let mut buf = mem::MaybeUninit::<libc::rlimit64>::zeroed();
625
626 // Safe because this will only modify `buf` and we check the return value.
627 let res = unsafe { libc::prlimit64(0, libc::RLIMIT_NOFILE, ptr::null(), buf.as_mut_ptr()) };
628 if res == 0 {
629 // Safe because the kernel guarantees that the struct is fully initialized.
630 let limit = unsafe { buf.assume_init() };
631 Ok(limit.rlim_max)
632 } else {
633 errno_result()
634 }
635 }
636
637 #[cfg(test)]
638 mod tests {
639 use libc::EBADF;
640 use std::io::Write;
641
642 use super::*;
643
644 #[test]
pipe_size_and_fill()645 fn pipe_size_and_fill() {
646 let (_rx, mut tx) = new_pipe_full().expect("Failed to pipe");
647
648 // To check that setting the size worked, set the descriptor to non blocking and check that
649 // write returns an error.
650 add_fd_flags(tx.as_raw_fd(), libc::O_NONBLOCK).expect("Failed to set tx non blocking");
651 tx.write(&[0u8; 8])
652 .expect_err("Write after fill didn't fail");
653 }
654
655 #[test]
safe_descriptor_from_path_valid()656 fn safe_descriptor_from_path_valid() {
657 assert!(safe_descriptor_from_path(Path::new("/proc/self/fd/2"))
658 .unwrap()
659 .is_some());
660 }
661
662 #[test]
safe_descriptor_from_path_invalid_integer()663 fn safe_descriptor_from_path_invalid_integer() {
664 assert_eq!(
665 safe_descriptor_from_path(Path::new("/proc/self/fd/blah")),
666 Err(Error::new(EINVAL))
667 );
668 }
669
670 #[test]
safe_descriptor_from_path_invalid_fd()671 fn safe_descriptor_from_path_invalid_fd() {
672 assert_eq!(
673 safe_descriptor_from_path(Path::new("/proc/self/fd/42")),
674 Err(Error::new(EBADF))
675 );
676 }
677
678 #[test]
safe_descriptor_from_path_none()679 fn safe_descriptor_from_path_none() {
680 assert_eq!(
681 safe_descriptor_from_path(Path::new("/something/else")).unwrap(),
682 None
683 );
684 }
685 }
686