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