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