nix/fcntl.rs
1//! File control options
2use crate::errno::Errno;
3#[cfg(all(target_os = "freebsd", target_arch = "x86_64"))]
4use core::slice;
5use libc::{c_int, c_uint, size_t, ssize_t};
6#[cfg(any(
7 target_os = "netbsd",
8 apple_targets,
9 target_os = "dragonfly",
10 all(target_os = "freebsd", target_arch = "x86_64"),
11))]
12use std::ffi::CStr;
13use std::ffi::OsString;
14#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
15use std::ops::{Deref, DerefMut};
16use std::os::unix::ffi::OsStringExt;
17#[cfg(not(target_os = "redox"))]
18use std::os::unix::io::OwnedFd;
19use std::os::unix::io::RawFd;
20#[cfg(any(
21 target_os = "netbsd",
22 apple_targets,
23 target_os = "dragonfly",
24 all(target_os = "freebsd", target_arch = "x86_64"),
25))]
26use std::path::PathBuf;
27#[cfg(any(linux_android, target_os = "freebsd"))]
28use std::ptr;
29
30#[cfg(feature = "fs")]
31use crate::{sys::stat::Mode, NixPath, Result};
32
33#[cfg(any(
34 linux_android,
35 target_os = "emscripten",
36 target_os = "fuchsia",
37 target_os = "wasi",
38 target_env = "uclibc",
39 target_os = "freebsd"
40))]
41#[cfg(feature = "fs")]
42pub use self::posix_fadvise::{posix_fadvise, PosixFadviseAdvice};
43
44/// A file descriptor referring to the working directory of the current process
45/// **that should be ONLY passed to the `dirfd` argument of those `xxat()` functions**.
46///
47/// # Examples
48///
49/// Use it in [`openat()`]:
50///
51/// ```no_run
52/// use nix::fcntl::AT_FDCWD;
53/// use nix::fcntl::openat;
54/// use nix::fcntl::OFlag;
55/// use nix::sys::stat::Mode;
56///
57/// let fd = openat(AT_FDCWD, "foo", OFlag::O_RDONLY | OFlag::O_CLOEXEC, Mode::empty()).unwrap();
58/// ```
59///
60/// # WARNING
61///
62/// Do NOT pass this symbol to non-`xxat()` functions, it won't work:
63///
64/// ```should_panic
65/// use nix::errno::Errno;
66/// use nix::fcntl::AT_FDCWD;
67/// use nix::sys::stat::fstat;
68///
69/// let never = fstat(AT_FDCWD).unwrap();
70/// ```
71//
72// SAFETY:
73// 1. `AT_FDCWD` is usable for the whole process life, so it is `'static`.
74// 2. It is not a valid file descriptor, but OS will handle it for us when passed
75// to `xxat(2)` calls.
76#[cfg(not(target_os = "redox"))] // Redox does not have this
77pub const AT_FDCWD: std::os::fd::BorrowedFd<'static> =
78 unsafe { std::os::fd::BorrowedFd::borrow_raw(libc::AT_FDCWD) };
79
80#[cfg(not(target_os = "redox"))]
81#[cfg(any(feature = "fs", feature = "process", feature = "user"))]
82libc_bitflags! {
83 /// Flags that control how the various *at syscalls behave.
84 #[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "process"))))]
85 pub struct AtFlags: c_int {
86 #[allow(missing_docs)]
87 #[doc(hidden)]
88 // Should not be used by the public API, but only internally.
89 AT_REMOVEDIR;
90 /// Used with [`linkat`](crate::unistd::linkat`) to create a link to a symbolic link's
91 /// target, instead of to the symbolic link itself.
92 AT_SYMLINK_FOLLOW;
93 /// Used with functions like [`fstatat`](crate::sys::stat::fstatat`) to operate on a link
94 /// itself, instead of the symbolic link's target.
95 AT_SYMLINK_NOFOLLOW;
96 /// Don't automount the terminal ("basename") component of pathname if it is a directory
97 /// that is an automount point.
98 #[cfg(linux_android)]
99 AT_NO_AUTOMOUNT;
100 /// If the provided path is an empty string, operate on the provided directory file
101 /// descriptor instead.
102 #[cfg(any(linux_android, target_os = "freebsd", target_os = "hurd"))]
103 AT_EMPTY_PATH;
104 /// Used with [`faccessat`](crate::unistd::faccessat), the checks for accessibility are
105 /// performed using the effective user and group IDs instead of the real user and group ID
106 #[cfg(not(target_os = "android"))]
107 AT_EACCESS;
108 }
109}
110
111#[cfg(any(
112 feature = "fs",
113 feature = "term",
114 all(feature = "fanotify", target_os = "linux")
115))]
116libc_bitflags!(
117 /// Configuration options for opened files.
118 #[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "term", all(feature = "fanotify", target_os = "linux")))))]
119 pub struct OFlag: c_int {
120 /// Mask for the access mode of the file.
121 O_ACCMODE;
122 /// Use alternate I/O semantics.
123 #[cfg(target_os = "netbsd")]
124 O_ALT_IO;
125 /// Open the file in append-only mode.
126 O_APPEND;
127 /// Generate a signal when input or output becomes possible.
128 #[cfg(not(any(
129 solarish,
130 target_os = "aix",
131 target_os = "haiku",
132 target_os = "cygwin"
133 )))]
134 O_ASYNC;
135 /// Closes the file descriptor once an `execve` call is made.
136 ///
137 /// Also sets the file offset to the beginning of the file.
138 O_CLOEXEC;
139 /// Create the file if it does not exist.
140 O_CREAT;
141 /// Try to minimize cache effects of the I/O for this file.
142 #[cfg(any(
143 freebsdlike,
144 linux_android,
145 target_os = "illumos",
146 target_os = "netbsd"
147 ))]
148 O_DIRECT;
149 /// If the specified path isn't a directory, fail.
150 O_DIRECTORY;
151 /// Implicitly follow each `write()` with an `fdatasync()`.
152 #[cfg(any(linux_android, apple_targets, target_os = "freebsd", netbsdlike))]
153 O_DSYNC;
154 /// Error out if a file was not created.
155 O_EXCL;
156 /// Open for execute only.
157 #[cfg(target_os = "freebsd")]
158 O_EXEC;
159 /// Open with an exclusive file lock.
160 #[cfg(any(bsd, target_os = "redox"))]
161 O_EXLOCK;
162 /// Same as `O_SYNC`.
163 #[cfg(any(bsd,
164 all(target_os = "linux", not(target_env = "musl"), not(target_env = "ohos")),
165 target_os = "redox"))]
166 O_FSYNC;
167 /// Allow files whose sizes can't be represented in an `off_t` to be opened.
168 #[cfg(linux_android)]
169 O_LARGEFILE;
170 /// Do not update the file last access time during `read(2)`s.
171 #[cfg(linux_android)]
172 O_NOATIME;
173 /// Don't attach the device as the process' controlling terminal.
174 #[cfg(not(target_os = "redox"))]
175 O_NOCTTY;
176 /// Same as `O_NONBLOCK`.
177 #[cfg(not(any(target_os = "redox", target_os = "haiku", target_os = "cygwin")))]
178 O_NDELAY;
179 /// `open()` will fail if the given path is a symbolic link.
180 O_NOFOLLOW;
181 /// When possible, open the file in nonblocking mode.
182 O_NONBLOCK;
183 /// Don't deliver `SIGPIPE`.
184 #[cfg(target_os = "netbsd")]
185 O_NOSIGPIPE;
186 /// Obtain a file descriptor for low-level access.
187 ///
188 /// The file itself is not opened and other file operations will fail.
189 #[cfg(any(linux_android, target_os = "redox", target_os = "freebsd", target_os = "fuchsia"))]
190 O_PATH;
191 /// Only allow reading.
192 ///
193 /// This should not be combined with `O_WRONLY` or `O_RDWR`.
194 O_RDONLY;
195 /// Allow both reading and writing.
196 ///
197 /// This should not be combined with `O_WRONLY` or `O_RDONLY`.
198 O_RDWR;
199 /// Similar to `O_DSYNC` but applies to `read`s instead.
200 #[cfg(any(target_os = "linux", netbsdlike))]
201 O_RSYNC;
202 /// Open directory for search only. Skip search permission checks on
203 /// later `openat()` calls using the obtained file descriptor.
204 #[cfg(any(
205 apple_targets,
206 solarish,
207 target_os = "netbsd",
208 target_os = "freebsd",
209 target_os = "fuchsia",
210 target_os = "emscripten",
211 target_os = "aix",
212 target_os = "wasi"
213 ))]
214 O_SEARCH;
215 /// Open with a shared file lock.
216 #[cfg(any(bsd, target_os = "redox"))]
217 O_SHLOCK;
218 /// Implicitly follow each `write()` with an `fsync()`.
219 #[cfg(not(target_os = "redox"))]
220 O_SYNC;
221 /// Create an unnamed temporary file.
222 #[cfg(linux_android)]
223 O_TMPFILE;
224 /// Truncate an existing regular file to 0 length if it allows writing.
225 O_TRUNC;
226 /// Restore default TTY attributes.
227 #[cfg(target_os = "freebsd")]
228 O_TTY_INIT;
229 /// Only allow writing.
230 ///
231 /// This should not be combined with `O_RDONLY` or `O_RDWR`.
232 O_WRONLY;
233 }
234);
235
236feature! {
237#![feature = "fs"]
238
239/// open or create a file for reading, writing or executing
240///
241/// # See Also
242/// [`open`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html)
243// The conversion is not identical on all operating systems.
244#[allow(clippy::useless_conversion)]
245pub fn open<P: ?Sized + NixPath>(
246 path: &P,
247 oflag: OFlag,
248 mode: Mode,
249) -> Result<std::os::fd::OwnedFd> {
250 use std::os::fd::FromRawFd;
251
252 let fd = path.with_nix_path(|cstr| unsafe {
253 libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint)
254 })?;
255 Errno::result(fd)?;
256
257 // SAFETY:
258 //
259 // `open(2)` should return a valid owned fd on success
260 Ok( unsafe { std::os::fd::OwnedFd::from_raw_fd(fd) } )
261}
262
263/// open or create a file for reading, writing or executing
264///
265/// The `openat` function is equivalent to the [`open`] function except in the case where the path
266/// specifies a relative path. In that case, the file to be opened is determined relative to the
267/// directory associated with the file descriptor `dirfd`.
268///
269/// # See Also
270/// [`openat`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/openat.html)
271// The conversion is not identical on all operating systems.
272#[allow(clippy::useless_conversion)]
273#[cfg(not(target_os = "redox"))]
274pub fn openat<P: ?Sized + NixPath, Fd: std::os::fd::AsFd>(
275 dirfd: Fd,
276 path: &P,
277 oflag: OFlag,
278 mode: Mode,
279) -> Result<OwnedFd> {
280 use std::os::fd::AsRawFd;
281 use std::os::fd::FromRawFd;
282
283 let fd = path.with_nix_path(|cstr| unsafe {
284 libc::openat(dirfd.as_fd().as_raw_fd(), cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint)
285 })?;
286 Errno::result(fd)?;
287
288 // SAFETY:
289 //
290 // `openat(2)` should return a valid owned fd on success
291 Ok( unsafe { OwnedFd::from_raw_fd(fd) } )
292}
293
294cfg_if::cfg_if! {
295 if #[cfg(target_os = "linux")] {
296 libc_bitflags! {
297 /// Path resolution flags.
298 ///
299 /// See [path resolution(7)](https://man7.org/linux/man-pages/man7/path_resolution.7.html)
300 /// for details of the resolution process.
301 pub struct ResolveFlag: libc::c_ulonglong {
302 /// Do not permit the path resolution to succeed if any component of
303 /// the resolution is not a descendant of the directory indicated by
304 /// dirfd. This causes absolute symbolic links (and absolute values of
305 /// pathname) to be rejected.
306 RESOLVE_BENEATH;
307
308 /// Treat the directory referred to by dirfd as the root directory
309 /// while resolving pathname.
310 RESOLVE_IN_ROOT;
311
312 /// Disallow all magic-link resolution during path resolution. Magic
313 /// links are symbolic link-like objects that are most notably found
314 /// in proc(5); examples include `/proc/[pid]/exe` and `/proc/[pid]/fd/*`.
315 ///
316 /// See symlink(7) for more details.
317 RESOLVE_NO_MAGICLINKS;
318
319 /// Disallow resolution of symbolic links during path resolution. This
320 /// option implies RESOLVE_NO_MAGICLINKS.
321 RESOLVE_NO_SYMLINKS;
322
323 /// Disallow traversal of mount points during path resolution (including
324 /// all bind mounts).
325 RESOLVE_NO_XDEV;
326 }
327 }
328
329 /// Specifies how [`openat2()`] should open a pathname.
330 ///
331 /// # Reference
332 ///
333 /// * [Linux](https://man7.org/linux/man-pages/man2/open_how.2type.html)
334 #[repr(transparent)]
335 #[derive(Clone, Copy, Debug)]
336 pub struct OpenHow(libc::open_how);
337
338 impl OpenHow {
339 /// Create a new zero-filled `open_how`.
340 pub fn new() -> Self {
341 // safety: according to the man page, open_how MUST be zero-initialized
342 // on init so that unknown fields are also zeroed.
343 Self(unsafe {
344 std::mem::MaybeUninit::zeroed().assume_init()
345 })
346 }
347
348 /// Set the open flags used to open a file, completely overwriting any
349 /// existing flags.
350 pub fn flags(mut self, flags: OFlag) -> Self {
351 let flags = flags.bits() as libc::c_ulonglong;
352 self.0.flags = flags;
353 self
354 }
355
356 /// Set the file mode new files will be created with, overwriting any
357 /// existing flags.
358 pub fn mode(mut self, mode: Mode) -> Self {
359 let mode = mode.bits() as libc::c_ulonglong;
360 self.0.mode = mode;
361 self
362 }
363
364 /// Set resolve flags, completely overwriting any existing flags.
365 ///
366 /// See [ResolveFlag] for more detail.
367 pub fn resolve(mut self, resolve: ResolveFlag) -> Self {
368 let resolve = resolve.bits();
369 self.0.resolve = resolve;
370 self
371 }
372 }
373
374 // safety: default isn't derivable because libc::open_how must be zeroed
375 impl Default for OpenHow {
376 fn default() -> Self {
377 Self::new()
378 }
379 }
380
381 /// Open or create a file for reading, writing or executing.
382 ///
383 /// `openat2` is an extension of the [`openat`] function that allows the caller
384 /// to control how path resolution happens.
385 ///
386 /// # See also
387 ///
388 /// [openat2](https://man7.org/linux/man-pages/man2/openat2.2.html)
389 pub fn openat2<P: ?Sized + NixPath, Fd: std::os::fd::AsFd>(
390 dirfd: Fd,
391 path: &P,
392 mut how: OpenHow,
393 ) -> Result<OwnedFd> {
394 use std::os::fd::AsRawFd;
395 use std::os::fd::FromRawFd;
396
397 let fd = path.with_nix_path(|cstr| unsafe {
398 libc::syscall(
399 libc::SYS_openat2,
400 dirfd.as_fd().as_raw_fd(),
401 cstr.as_ptr(),
402 &mut how as *mut OpenHow,
403 std::mem::size_of::<libc::open_how>(),
404 )
405 })? as RawFd;
406 Errno::result(fd)?;
407
408 // SAFETY:
409 //
410 // `openat2(2)` should return a valid owned fd on success
411 Ok( unsafe { OwnedFd::from_raw_fd(fd) } )
412 }
413 }
414}
415
416/// Change the name of a file.
417///
418/// The `renameat` function is equivalent to `rename` except in the case where either `old_path`
419/// or `new_path` specifies a relative path. In such cases, the file to be renamed (or the its new
420/// name, respectively) is located relative to `old_dirfd` or `new_dirfd`, respectively
421///
422/// # See Also
423/// [`renameat`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html)
424#[cfg(not(target_os = "redox"))]
425pub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath, Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>(
426 old_dirfd: Fd1,
427 old_path: &P1,
428 new_dirfd: Fd2,
429 new_path: &P2,
430) -> Result<()> {
431 use std::os::fd::AsRawFd;
432
433 let res = old_path.with_nix_path(|old_cstr| {
434 new_path.with_nix_path(|new_cstr| unsafe {
435 libc::renameat(
436 old_dirfd.as_fd().as_raw_fd(),
437 old_cstr.as_ptr(),
438 new_dirfd.as_fd().as_raw_fd(),
439 new_cstr.as_ptr(),
440 )
441 })
442 })??;
443 Errno::result(res).map(drop)
444}
445}
446
447#[cfg(all(target_os = "linux", target_env = "gnu"))]
448#[cfg(feature = "fs")]
449libc_bitflags! {
450 /// Flags for use with [`renameat2`].
451 #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
452 pub struct RenameFlags: u32 {
453 /// Atomically exchange `old_path` and `new_path`.
454 RENAME_EXCHANGE;
455 /// Don't overwrite `new_path` of the rename. Return an error if `new_path` already
456 /// exists.
457 RENAME_NOREPLACE;
458 /// creates a "whiteout" object at the source of the rename at the same time as performing
459 /// the rename.
460 ///
461 /// This operation makes sense only for overlay/union filesystem implementations.
462 RENAME_WHITEOUT;
463 }
464}
465
466feature! {
467#![feature = "fs"]
468/// Like [`renameat`], but with an additional `flags` argument.
469///
470/// A `renameat2` call with an empty flags argument is equivalent to `renameat`.
471///
472/// # See Also
473/// * [`rename`](https://man7.org/linux/man-pages/man2/rename.2.html)
474#[cfg(all(target_os = "linux", target_env = "gnu"))]
475pub fn renameat2<P1: ?Sized + NixPath, P2: ?Sized + NixPath, Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>(
476 old_dirfd: Fd1,
477 old_path: &P1,
478 new_dirfd: Fd2,
479 new_path: &P2,
480 flags: RenameFlags,
481) -> Result<()> {
482 use std::os::fd::AsRawFd;
483
484 let res = old_path.with_nix_path(|old_cstr| {
485 new_path.with_nix_path(|new_cstr| unsafe {
486 libc::renameat2(
487 old_dirfd.as_fd().as_raw_fd(),
488 old_cstr.as_ptr(),
489 new_dirfd.as_fd().as_raw_fd(),
490 new_cstr.as_ptr(),
491 flags.bits(),
492 )
493 })
494 })??;
495 Errno::result(res).map(drop)
496}
497
498fn wrap_readlink_result(mut v: Vec<u8>, len: ssize_t) -> Result<OsString> {
499 unsafe { v.set_len(len as usize) }
500 v.shrink_to_fit();
501 Ok(OsString::from_vec(v.to_vec()))
502}
503
504/// Read the symlink specified by `path` and `dirfd` and put the contents in `v`.
505/// Return the number of bytes placed in `v`.
506///
507/// This function can call `readlink(2)` or `readlinkat(2)` depending on if `dirfd`
508/// is some, if it is, then `readlinkat(2)` is called, otherwise, call `readlink(2)`.
509///
510/// # Safety
511///
512/// This function is not I/O-safe considering it employs the `RawFd` type.
513unsafe fn readlink_maybe_at<P: ?Sized + NixPath>(
514 dirfd: Option<RawFd>,
515 path: &P,
516 v: &mut Vec<u8>,
517) -> Result<libc::ssize_t> {
518 path.with_nix_path(|cstr| unsafe {
519 match dirfd {
520 #[cfg(target_os = "redox")]
521 Some(_) => unreachable!("redox does not have readlinkat(2)"),
522 #[cfg(not(target_os = "redox"))]
523 Some(dirfd) => libc::readlinkat(
524 dirfd,
525 cstr.as_ptr(),
526 v.as_mut_ptr().cast(),
527 v.capacity() as size_t,
528 ),
529 None => libc::readlink(
530 cstr.as_ptr(),
531 v.as_mut_ptr().cast(),
532 v.capacity() as size_t,
533 ),
534 }
535 })
536}
537
538/// The actual implementation of [`readlink(2)`] or [`readlinkat(2)`].
539///
540/// This function can call `readlink(2)` or `readlinkat(2)` depending on if `dirfd`
541/// is some, if it is, then `readlinkat(2)` is called, otherwise, call `readlink(2)`.
542///
543/// # Safety
544///
545/// This function is marked unsafe because it uses `RawFd`.
546unsafe fn inner_readlink<P: ?Sized + NixPath>(
547 dirfd: Option<RawFd>,
548 path: &P,
549) -> Result<OsString> {
550 #[cfg(not(target_os = "hurd"))]
551 const PATH_MAX: usize = libc::PATH_MAX as usize;
552 #[cfg(target_os = "hurd")]
553 const PATH_MAX: usize = 1024; // Hurd does not define a hard limit, so try a guess first
554 let mut v = Vec::with_capacity(PATH_MAX);
555
556 {
557 // simple case: result is strictly less than `PATH_MAX`
558
559 // SAFETY:
560 //
561 // If this call of `readlink_maybe_at()` is safe or not depends on the
562 // usage of `unsafe fn inner_readlink()`.
563 let res = unsafe { readlink_maybe_at(dirfd, path, &mut v)? };
564 let len = Errno::result(res)?;
565 debug_assert!(len >= 0);
566 if (len as usize) < v.capacity() {
567 return wrap_readlink_result(v, res);
568 }
569 }
570
571 // Uh oh, the result is too long...
572 // Let's try to ask lstat how many bytes to allocate.
573 let mut try_size = {
574 let reported_size = match dirfd {
575 #[cfg(target_os = "redox")]
576 Some(_) => unreachable!("redox does not have readlinkat(2)"),
577 #[cfg(any(linux_android, target_os = "freebsd", target_os = "hurd"))]
578 Some(dirfd) => {
579 // SAFETY:
580 //
581 // If this call of `borrow_raw()` is safe or not depends on the
582 // usage of `unsafe fn inner_readlink()`.
583 let dirfd = unsafe {
584 std::os::fd::BorrowedFd::borrow_raw(dirfd)
585 };
586 let flags = if path.is_empty() {
587 AtFlags::AT_EMPTY_PATH
588 } else {
589 AtFlags::empty()
590 };
591 super::sys::stat::fstatat(
592 dirfd,
593 path,
594 flags | AtFlags::AT_SYMLINK_NOFOLLOW,
595 )
596 }
597 #[cfg(not(any(
598 linux_android,
599 target_os = "redox",
600 target_os = "freebsd",
601 target_os = "hurd"
602 )))]
603 Some(dirfd) => {
604 // SAFETY:
605 //
606 // If this call of `borrow_raw()` is safe or not depends on the
607 // usage of `unsafe fn inner_readlink()`.
608 let dirfd = unsafe {
609 std::os::fd::BorrowedFd::borrow_raw(dirfd)
610 };
611 super::sys::stat::fstatat(dirfd, path, AtFlags::AT_SYMLINK_NOFOLLOW)
612 },
613 None => super::sys::stat::lstat(path),
614 }
615 .map(|x| x.st_size)
616 .unwrap_or(0);
617
618 if reported_size > 0 {
619 // Note: even if `lstat`'s apparently valid answer turns out to be
620 // wrong, we will still read the full symlink no matter what.
621 reported_size as usize + 1
622 } else {
623 // If lstat doesn't cooperate, or reports an error, be a little less
624 // precise.
625 PATH_MAX.max(128) << 1
626 }
627 };
628
629 loop {
630 {
631 v.reserve_exact(try_size);
632 // SAFETY:
633 //
634 // If this call of `readlink_maybe_at()` is safe or not depends on the
635 // usage of `unsafe fn inner_readlink()`.
636 let res = unsafe { readlink_maybe_at(dirfd, path, &mut v)? };
637 let len = Errno::result(res)?;
638 debug_assert!(len >= 0);
639 if (len as usize) < v.capacity() {
640 return wrap_readlink_result(v, res);
641 }
642 }
643
644 // Ugh! Still not big enough!
645 match try_size.checked_shl(1) {
646 Some(next_size) => try_size = next_size,
647 // It's absurd that this would happen, but handle it sanely
648 // anyway.
649 None => break Err(Errno::ENAMETOOLONG),
650 }
651 }
652}
653
654/// Read value of a symbolic link
655///
656/// # See Also
657/// * [`readlink`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html)
658pub fn readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString> {
659 // argument `dirfd` should be `None` since we call it from `readlink()`
660 //
661 // Do NOT call it with `Some(AT_CWD)` as in that way, we are emulating
662 // `readlink(2)` with `readlinkat(2)`, which will make us lose `readlink(2)`
663 // on Redox.
664 //
665 // SAFETY:
666 //
667 // It is definitely safe because the argument involving `RawFd` is `None`
668 unsafe { inner_readlink(None, path) }
669}
670
671/// Read value of a symbolic link.
672///
673/// Equivalent to [`readlink` ] except for the case where `path` specifies a
674/// relative path, `path` will be interpreted relative to the path specified
675/// by `dirfd`. (Use [`AT_FDCWD`] to make it relative to the working directory).
676///
677/// # See Also
678/// * [`readlink`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html)
679#[cfg(not(target_os = "redox"))]
680pub fn readlinkat<Fd: std::os::fd::AsFd,P: ?Sized + NixPath>(
681 dirfd: Fd,
682 path: &P,
683) -> Result<OsString> {
684 use std::os::fd::AsRawFd;
685
686 // argument `dirfd` should be `Some` since we call it from `readlinkat()`
687 //
688 // SAFETY:
689 //
690 // The passed `RawFd` should be valid since it is borrowed from `Fd: AsFd`.
691 unsafe { inner_readlink(Some(dirfd.as_fd().as_raw_fd()), path) }
692}
693}
694
695#[cfg(any(linux_android, target_os = "freebsd"))]
696#[cfg(feature = "fs")]
697libc_bitflags!(
698 /// Additional flags for file sealing, which allows for limiting operations on a file.
699 #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
700 pub struct SealFlag: c_int {
701 /// Prevents further calls to `fcntl()` with `F_ADD_SEALS`.
702 F_SEAL_SEAL;
703 /// The file cannot be reduced in size.
704 F_SEAL_SHRINK;
705 /// The size of the file cannot be increased.
706 F_SEAL_GROW;
707 /// The file contents cannot be modified.
708 F_SEAL_WRITE;
709 /// The file contents cannot be modified, except via shared writable mappings that were
710 /// created prior to the seal being set. Since Linux 5.1.
711 #[cfg(linux_android)]
712 F_SEAL_FUTURE_WRITE;
713 }
714);
715
716#[cfg(feature = "fs")]
717libc_bitflags!(
718 /// Additional configuration flags for `fcntl`'s `F_SETFD`.
719 #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
720 pub struct FdFlag: c_int {
721 /// The file descriptor will automatically be closed during a successful `execve(2)`.
722 FD_CLOEXEC;
723 }
724);
725
726feature! {
727#![feature = "fs"]
728
729/// Commands for use with [`fcntl`].
730#[cfg(not(target_os = "redox"))]
731#[derive(Debug, Eq, Hash, PartialEq)]
732#[non_exhaustive]
733pub enum FcntlArg<'a> {
734 /// Duplicate the provided file descriptor
735 F_DUPFD(RawFd),
736 /// Duplicate the provided file descriptor and set the `FD_CLOEXEC` flag on it.
737 F_DUPFD_CLOEXEC(RawFd),
738 /// Get the close-on-exec flag associated with the file descriptor
739 F_GETFD,
740 /// Set the close-on-exec flag associated with the file descriptor
741 F_SETFD(FdFlag), // FD_FLAGS
742 /// Get descriptor status flags
743 F_GETFL,
744 /// Set descriptor status flags
745 F_SETFL(OFlag), // O_NONBLOCK
746 /// Set or clear a file segment lock
747 F_SETLK(&'a libc::flock),
748 /// Like [`F_SETLK`] except that if a shared or exclusive lock is blocked by
749 /// other locks, the process waits until the request can be satisfied.
750 F_SETLKW(&'a libc::flock),
751 /// Get the first lock that blocks the lock description
752 F_GETLK(&'a mut libc::flock),
753 /// Acquire or release an open file description lock
754 #[cfg(linux_android)]
755 F_OFD_SETLK(&'a libc::flock),
756 /// Like [`F_OFD_SETLK`] except that if a conflicting lock is held on
757 /// the file, then wait for that lock to be released.
758 #[cfg(linux_android)]
759 F_OFD_SETLKW(&'a libc::flock),
760 /// Determine whether it would be possible to create the given lock. If not, return details
761 /// about one existing lock that would prevent it.
762 #[cfg(linux_android)]
763 F_OFD_GETLK(&'a mut libc::flock),
764 /// Add seals to the file
765 #[cfg(any(
766 linux_android,
767 target_os = "freebsd"
768 ))]
769 F_ADD_SEALS(SealFlag),
770 /// Get seals associated with the file
771 #[cfg(any(
772 linux_android,
773 target_os = "freebsd"
774 ))]
775 F_GET_SEALS,
776 /// Asks the drive to flush all buffered data to permanent storage.
777 #[cfg(apple_targets)]
778 F_FULLFSYNC,
779 /// fsync + issue barrier to drive
780 #[cfg(apple_targets)]
781 F_BARRIERFSYNC,
782 /// Return the capacity of a pipe
783 #[cfg(linux_android)]
784 F_GETPIPE_SZ,
785 /// Change the capacity of a pipe
786 #[cfg(linux_android)]
787 F_SETPIPE_SZ(c_int),
788 /// Look up the path of an open file descriptor, if possible.
789 #[cfg(any(
790 target_os = "netbsd",
791 target_os = "dragonfly",
792 apple_targets,
793 ))]
794 F_GETPATH(&'a mut PathBuf),
795 /// Look up the path of an open file descriptor, if possible.
796 #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))]
797 F_KINFO(&'a mut PathBuf),
798 /// Return the full path without firmlinks of the fd.
799 #[cfg(apple_targets)]
800 F_GETPATH_NOFIRMLINK(&'a mut PathBuf),
801 /// Issue an advisory read async with no copy to user
802 #[cfg(apple_targets)]
803 F_RDADVISE(libc::radvisory),
804 /// Turn read ahead off/on
805 #[cfg(apple_targets)]
806 F_RDAHEAD(bool),
807 /// Pre-allocate storage with different policies on fd.
808 /// Note that we want a mutable reference for the OUT
809 /// fstore_t field fst_bytesalloc.
810 #[cfg(apple_targets)]
811 F_PREALLOCATE(&'a mut libc::fstore_t),
812 #[cfg(apple_targets)]
813 /// Get disk device information. In practice,
814 /// only the file offset data is set.
815 F_LOG2PHYS(&'a mut libc::off_t),
816 #[cfg(apple_targets)]
817 /// Get disk device information. In practice,
818 /// only the file offset data is set.
819 /// The difference with F_LOG2PHYS is the struct passed
820 /// is used as both IN/OUT as both its l2p_devoffset and
821 /// l2p_contigbytes can be used for more specific queries.
822 F_LOG2PHYS_EXT(&'a mut libc::log2phys),
823 /// Transfer any extra space in the file past the logical EOF
824 /// (as previously allocated via F_PREALLOCATE) to another file.
825 /// The other file is specified via a file descriptor as the lone extra argument.
826 /// Both descriptors must reference regular files in the same volume.
827 #[cfg(apple_targets)]
828 F_TRANSFEREXTENTS(RawFd),
829 /// Set or clear the read ahead (pre-fetch) amount for sequential access or
830 /// disable it with 0 or to system default for any value < 0.
831 /// It manages how the kernel caches file data.
832 #[cfg(target_os = "freebsd")]
833 F_READAHEAD(c_int),
834 // TODO: Rest of flags
835}
836
837/// Commands for use with [`fcntl`].
838#[cfg(target_os = "redox")]
839#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
840#[non_exhaustive]
841pub enum FcntlArg {
842 /// Duplicate the provided file descriptor
843 F_DUPFD(RawFd),
844 /// Duplicate the provided file descriptor and set the `FD_CLOEXEC` flag on it.
845 F_DUPFD_CLOEXEC(RawFd),
846 /// Get the close-on-exec flag associated with the file descriptor
847 F_GETFD,
848 /// Set the close-on-exec flag associated with the file descriptor
849 F_SETFD(FdFlag), // FD_FLAGS
850 /// Get descriptor status flags
851 F_GETFL,
852 /// Set descriptor status flags
853 F_SETFL(OFlag), // O_NONBLOCK
854}
855pub use self::FcntlArg::*;
856
857/// Perform various operations on open file descriptors.
858///
859/// # See Also
860/// * [`fcntl`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fcntl.html)
861// TODO: Figure out how to handle value fcntl returns
862pub fn fcntl<Fd: std::os::fd::AsFd>(fd: Fd, arg: FcntlArg) -> Result<c_int> {
863 use std::os::fd::AsRawFd;
864
865 let fd = fd.as_fd().as_raw_fd();
866 let res = unsafe {
867 match arg {
868 F_DUPFD(rawfd) => libc::fcntl(fd, libc::F_DUPFD, rawfd),
869 F_DUPFD_CLOEXEC(rawfd) => {
870 libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, rawfd)
871 }
872 F_GETFD => libc::fcntl(fd, libc::F_GETFD),
873 F_SETFD(flag) => libc::fcntl(fd, libc::F_SETFD, flag.bits()),
874 F_GETFL => libc::fcntl(fd, libc::F_GETFL),
875 F_SETFL(flag) => libc::fcntl(fd, libc::F_SETFL, flag.bits()),
876 #[cfg(not(target_os = "redox"))]
877 F_SETLK(flock) => libc::fcntl(fd, libc::F_SETLK, flock),
878 #[cfg(not(target_os = "redox"))]
879 F_SETLKW(flock) => libc::fcntl(fd, libc::F_SETLKW, flock),
880 #[cfg(not(target_os = "redox"))]
881 F_GETLK(flock) => libc::fcntl(fd, libc::F_GETLK, flock),
882 #[cfg(linux_android)]
883 F_OFD_SETLK(flock) => libc::fcntl(fd, libc::F_OFD_SETLK, flock),
884 #[cfg(linux_android)]
885 F_OFD_SETLKW(flock) => libc::fcntl(fd, libc::F_OFD_SETLKW, flock),
886 #[cfg(linux_android)]
887 F_OFD_GETLK(flock) => libc::fcntl(fd, libc::F_OFD_GETLK, flock),
888 #[cfg(any(
889 linux_android,
890 target_os = "freebsd"
891 ))]
892 F_ADD_SEALS(flag) => {
893 libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits())
894 }
895 #[cfg(any(
896 linux_android,
897 target_os = "freebsd"
898 ))]
899 F_GET_SEALS => libc::fcntl(fd, libc::F_GET_SEALS),
900 #[cfg(apple_targets)]
901 F_FULLFSYNC => libc::fcntl(fd, libc::F_FULLFSYNC),
902 #[cfg(apple_targets)]
903 F_BARRIERFSYNC => libc::fcntl(fd, libc::F_BARRIERFSYNC),
904 #[cfg(linux_android)]
905 F_GETPIPE_SZ => libc::fcntl(fd, libc::F_GETPIPE_SZ),
906 #[cfg(linux_android)]
907 F_SETPIPE_SZ(size) => libc::fcntl(fd, libc::F_SETPIPE_SZ, size),
908 #[cfg(any(
909 target_os = "dragonfly",
910 target_os = "netbsd",
911 apple_targets,
912 ))]
913 F_GETPATH(path) => {
914 let mut buffer = vec![0; libc::PATH_MAX as usize];
915 let res = libc::fcntl(fd, libc::F_GETPATH, buffer.as_mut_ptr());
916 let ok_res = Errno::result(res)?;
917 let optr = CStr::from_bytes_until_nul(&buffer).unwrap();
918 *path = PathBuf::from(OsString::from(optr.to_str().unwrap()));
919 return Ok(ok_res)
920 },
921 #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))]
922 F_KINFO(path) => {
923 let mut info: libc::kinfo_file = std::mem::zeroed();
924 info.kf_structsize = std::mem::size_of::<libc::kinfo_file>() as i32;
925 let res = libc::fcntl(fd, libc::F_KINFO, &mut info);
926 let ok_res = Errno::result(res)?;
927 let p = info.kf_path;
928 let u8_slice = slice::from_raw_parts(p.as_ptr().cast(), p.len());
929 let optr = CStr::from_bytes_until_nul(u8_slice).unwrap();
930 *path = PathBuf::from(OsString::from(optr.to_str().unwrap()));
931 return Ok(ok_res)
932 },
933 #[cfg(apple_targets)]
934 F_GETPATH_NOFIRMLINK(path) => {
935 let mut buffer = vec![0; libc::PATH_MAX as usize];
936 let res = libc::fcntl(fd, libc::F_GETPATH_NOFIRMLINK, buffer.as_mut_ptr());
937 let ok_res = Errno::result(res)?;
938 let optr = CStr::from_bytes_until_nul(&buffer).unwrap();
939 *path = PathBuf::from(OsString::from(optr.to_str().unwrap()));
940 return Ok(ok_res)
941 },
942 #[cfg(apple_targets)]
943 F_RDADVISE(rad) => {
944 libc::fcntl(fd, libc::F_RDADVISE, &rad)
945 },
946 #[cfg(apple_targets)]
947 F_LOG2PHYS(offset) => {
948 let mut info: libc::log2phys = std::mem::zeroed();
949 let res = libc::fcntl(fd, libc::F_LOG2PHYS, &mut info);
950 let ok_res = Errno::result(res)?;
951 *offset = info.l2p_devoffset;
952 return Ok(ok_res)
953 }
954 #[cfg(apple_targets)]
955 F_LOG2PHYS_EXT(info) => {
956 libc::fcntl(fd, libc::F_LOG2PHYS_EXT, info)
957 }
958 #[cfg(apple_targets)]
959 F_RDAHEAD(on) => {
960 let val = if on { 1 } else { 0 };
961 libc::fcntl(fd, libc::F_RDAHEAD, val)
962 },
963 #[cfg(apple_targets)]
964 F_PREALLOCATE(st) => {
965 libc::fcntl(fd, libc::F_PREALLOCATE, st)
966 },
967 #[cfg(apple_targets)]
968 F_TRANSFEREXTENTS(rawfd) => {
969 libc::fcntl(fd, libc::F_TRANSFEREXTENTS, rawfd)
970 },
971 #[cfg(target_os = "freebsd")]
972 F_READAHEAD(val) => {
973 libc::fcntl(fd, libc::F_READAHEAD, val)
974 },
975 }
976 };
977
978 Errno::result(res)
979}
980
981/// Operations for use with [`Flock::lock`].
982#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
983#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
984#[non_exhaustive]
985pub enum FlockArg {
986 /// shared file lock
987 LockShared,
988 /// exclusive file lock
989 LockExclusive,
990 /// Unlock file
991 Unlock,
992 /// Shared lock. Do not block when locking.
993 LockSharedNonblock,
994 /// Exclusive lock. Do not block when locking.
995 LockExclusiveNonblock,
996 #[allow(missing_docs)]
997 #[deprecated(since = "0.28.0", note = "Use FlockArg::Unlock instead")]
998 UnlockNonblock,
999}
1000
1001#[allow(missing_docs)]
1002#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
1003#[deprecated(since = "0.28.0", note = "`fcntl::Flock` should be used instead.")]
1004pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> {
1005 use self::FlockArg::*;
1006
1007 let res = unsafe {
1008 match arg {
1009 LockShared => libc::flock(fd, libc::LOCK_SH),
1010 LockExclusive => libc::flock(fd, libc::LOCK_EX),
1011 Unlock => libc::flock(fd, libc::LOCK_UN),
1012 LockSharedNonblock => {
1013 libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB)
1014 }
1015 LockExclusiveNonblock => {
1016 libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB)
1017 }
1018 #[allow(deprecated)]
1019 UnlockNonblock => libc::flock(fd, libc::LOCK_UN | libc::LOCK_NB),
1020 }
1021 };
1022
1023 Errno::result(res).map(drop)
1024}
1025
1026/// Represents valid types for flock.
1027///
1028/// # Safety
1029/// Types implementing this must not be `Clone`.
1030#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
1031pub unsafe trait Flockable: std::os::fd::AsRawFd {}
1032
1033/// Represents an owned flock, which unlocks on drop.
1034///
1035/// See [flock(2)](https://linux.die.net/man/2/flock) for details on locking semantics.
1036#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
1037#[derive(Debug)]
1038pub struct Flock<T: Flockable>(T);
1039
1040#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
1041#[allow(clippy::unnecessary_unwrap)] // https://github.com/rust-lang/rust-clippy/issues/15744
1042impl<T: Flockable> Drop for Flock<T> {
1043 fn drop(&mut self) {
1044 let res = Errno::result(unsafe { libc::flock(self.0.as_raw_fd(), libc::LOCK_UN) });
1045 if res.is_err() && !std::thread::panicking() {
1046 panic!("Failed to remove flock: {}", res.unwrap_err());
1047 }
1048 }
1049}
1050
1051#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
1052impl<T: Flockable> Deref for Flock<T> {
1053 type Target = T;
1054
1055 fn deref(&self) -> &Self::Target {
1056 &self.0
1057 }
1058}
1059#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
1060impl<T: Flockable> DerefMut for Flock<T> {
1061 fn deref_mut(&mut self) -> &mut Self::Target {
1062 &mut self.0
1063 }
1064}
1065
1066#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
1067impl<T: Flockable> Flock<T> {
1068 /// Obtain a/an flock.
1069 ///
1070 /// # Example
1071 /// ```
1072 /// # use std::io::Write;
1073 /// # use std::fs::File;
1074 /// # use nix::fcntl::{Flock, FlockArg};
1075 /// # fn do_stuff(file: File) {
1076 /// let mut file = match Flock::lock(file, FlockArg::LockExclusive) {
1077 /// Ok(l) => l,
1078 /// Err(_) => return,
1079 /// };
1080 ///
1081 /// // Do stuff
1082 /// let data = "Foo bar";
1083 /// _ = file.write(data.as_bytes());
1084 /// _ = file.sync_data();
1085 /// # }
1086 pub fn lock(t: T, args: FlockArg) -> std::result::Result<Self, (T, Errno)> {
1087 let flags = match args {
1088 FlockArg::LockShared => libc::LOCK_SH,
1089 FlockArg::LockExclusive => libc::LOCK_EX,
1090 FlockArg::LockSharedNonblock => libc::LOCK_SH | libc::LOCK_NB,
1091 FlockArg::LockExclusiveNonblock => libc::LOCK_EX | libc::LOCK_NB,
1092 #[allow(deprecated)]
1093 FlockArg::Unlock | FlockArg::UnlockNonblock => return Err((t, Errno::EINVAL)),
1094 };
1095 match Errno::result(unsafe { libc::flock(t.as_raw_fd(), flags) }) {
1096 Ok(_) => Ok(Self(t)),
1097 Err(errno) => Err((t, errno)),
1098 }
1099 }
1100
1101 /// Remove the lock and return the object wrapped within.
1102 ///
1103 /// # Example
1104 /// ```
1105 /// # use std::fs::File;
1106 /// # use nix::fcntl::{Flock, FlockArg};
1107 /// fn do_stuff(file: File) -> nix::Result<()> {
1108 /// let mut lock = match Flock::lock(file, FlockArg::LockExclusive) {
1109 /// Ok(l) => l,
1110 /// Err((_,e)) => return Err(e),
1111 /// };
1112 ///
1113 /// // Do critical section
1114 ///
1115 /// // Unlock
1116 /// let file = match lock.unlock() {
1117 /// Ok(f) => f,
1118 /// Err((_, e)) => return Err(e),
1119 /// };
1120 ///
1121 /// // Do anything else
1122 ///
1123 /// Ok(())
1124 /// }
1125 pub fn unlock(self) -> std::result::Result<T, (Self, Errno)> {
1126 let inner = unsafe { match Errno::result(libc::flock(self.0.as_raw_fd(), libc::LOCK_UN)) {
1127 Ok(_) => std::ptr::read(&self.0),
1128 Err(errno) => return Err((self, errno)),
1129 }};
1130
1131 std::mem::forget(self);
1132 Ok(inner)
1133 }
1134
1135 /// Relock the file. This can upgrade or downgrade the lock type.
1136 ///
1137 /// # Example
1138 /// ```
1139 /// # use std::fs::File;
1140 /// # use nix::fcntl::{Flock, FlockArg};
1141 /// # use tempfile::tempfile;
1142 /// let f: std::fs::File = tempfile().unwrap();
1143 /// let locked_file = Flock::lock(f, FlockArg::LockExclusive).unwrap();
1144 /// // Do stuff, then downgrade the lock
1145 /// locked_file.relock(FlockArg::LockShared).unwrap();
1146 /// ```
1147 pub fn relock(&self, arg: FlockArg) -> Result<()> {
1148 let flags = match arg {
1149 FlockArg::LockShared => libc::LOCK_SH,
1150 FlockArg::LockExclusive => libc::LOCK_EX,
1151 FlockArg::LockSharedNonblock => libc::LOCK_SH | libc::LOCK_NB,
1152 FlockArg::LockExclusiveNonblock => libc::LOCK_EX | libc::LOCK_NB,
1153 #[allow(deprecated)]
1154 FlockArg::Unlock | FlockArg::UnlockNonblock => return Err(Errno::EINVAL),
1155 };
1156 Errno::result(unsafe { libc::flock(self.as_raw_fd(), flags) }).map(drop)
1157 }
1158}
1159
1160// Safety: `File` is not [std::clone::Clone].
1161#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
1162unsafe impl Flockable for std::fs::File {}
1163
1164// Safety: `OwnedFd` is not [std::clone::Clone].
1165#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
1166unsafe impl Flockable for OwnedFd {}
1167}
1168
1169#[cfg(linux_android)]
1170#[cfg(feature = "zerocopy")]
1171libc_bitflags! {
1172 /// Additional flags to `splice` and friends.
1173 #[cfg_attr(docsrs, doc(cfg(feature = "zerocopy")))]
1174 pub struct SpliceFFlags: c_uint {
1175 /// Request that pages be moved instead of copied.
1176 ///
1177 /// Not applicable to `vmsplice`.
1178 #[cfg(not(target_env = "uclibc"))]
1179 SPLICE_F_MOVE;
1180 /// Do not block on I/O.
1181 #[cfg(not(target_env = "uclibc"))]
1182 SPLICE_F_NONBLOCK;
1183 /// Hint that more data will be coming in a subsequent splice.
1184 ///
1185 /// Not applicable to `vmsplice`.
1186 #[cfg(not(target_env = "uclibc"))]
1187 SPLICE_F_MORE;
1188 /// Gift the user pages to the kernel.
1189 ///
1190 /// Not applicable to `splice`.
1191 #[cfg(not(target_env = "uclibc"))]
1192 SPLICE_F_GIFT;
1193 }
1194}
1195
1196feature! {
1197#![feature = "zerocopy"]
1198
1199/// Copy a range of data from one file to another
1200///
1201/// The `copy_file_range` system call performs an in-kernel copy between
1202/// file descriptors `fd_in` and `fd_out` without the additional cost of
1203/// transferring data from the kernel to user space and back again. There may be
1204/// additional optimizations for specific file systems. It copies up to `len`
1205/// bytes of data from file descriptor `fd_in` to file descriptor `fd_out`,
1206/// overwriting any data that exists within the requested range of the target
1207/// file.
1208///
1209/// If the `off_in` and/or `off_out` arguments are used, the values
1210/// will be mutated to reflect the new position within the file after
1211/// copying. If they are not used, the relevant file descriptors will be seeked
1212/// to the new position.
1213///
1214/// On successful completion the number of bytes actually copied will be
1215/// returned.
1216// Note: FreeBSD defines the offset argument as "off_t". Linux and Android
1217// define it as "loff_t". But on both OSes, on all supported platforms, those
1218// are 64 bits. So Nix uses i64 to make the docs simple and consistent.
1219#[cfg(any(linux_android, target_os = "freebsd"))]
1220pub fn copy_file_range<Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>(
1221 fd_in: Fd1,
1222 off_in: Option<&mut i64>,
1223 fd_out: Fd2,
1224 off_out: Option<&mut i64>,
1225 len: usize,
1226) -> Result<usize> {
1227 use std::os::fd::AsRawFd;
1228
1229 let off_in = off_in
1230 .map(|offset| offset as *mut i64)
1231 .unwrap_or(ptr::null_mut());
1232 let off_out = off_out
1233 .map(|offset| offset as *mut i64)
1234 .unwrap_or(ptr::null_mut());
1235
1236 cfg_if::cfg_if! {
1237 if #[cfg(target_os = "freebsd")] {
1238 let ret = unsafe {
1239 libc::copy_file_range(
1240 fd_in.as_fd().as_raw_fd(),
1241 off_in,
1242 fd_out.as_fd().as_raw_fd(),
1243 off_out,
1244 len,
1245 0,
1246 )
1247 };
1248 } else {
1249 // May Linux distros still don't include copy_file_range in their
1250 // libc implementations, so we need to make a direct syscall.
1251 let ret = unsafe {
1252 libc::syscall(
1253 libc::SYS_copy_file_range,
1254 fd_in.as_fd().as_raw_fd(),
1255 off_in,
1256 fd_out.as_fd().as_raw_fd(),
1257 off_out,
1258 len,
1259 0,
1260 )
1261 };
1262 }
1263 }
1264 Errno::result(ret).map(|r| r as usize)
1265}
1266
1267/// Splice data to/from a pipe
1268///
1269/// # See Also
1270/// *[`splice`](https://man7.org/linux/man-pages/man2/splice.2.html)
1271#[cfg(linux_android)]
1272pub fn splice<Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>(
1273 fd_in: Fd1,
1274 off_in: Option<&mut libc::loff_t>,
1275 fd_out: Fd2,
1276 off_out: Option<&mut libc::loff_t>,
1277 len: usize,
1278 flags: SpliceFFlags,
1279) -> Result<usize> {
1280 use std::os::fd::AsRawFd;
1281
1282 let off_in = off_in
1283 .map(|offset| offset as *mut libc::loff_t)
1284 .unwrap_or(ptr::null_mut());
1285 let off_out = off_out
1286 .map(|offset| offset as *mut libc::loff_t)
1287 .unwrap_or(ptr::null_mut());
1288
1289 let ret = unsafe {
1290 libc::splice(fd_in.as_fd().as_raw_fd(), off_in, fd_out.as_fd().as_raw_fd(), off_out, len, flags.bits())
1291 };
1292 Errno::result(ret).map(|r| r as usize)
1293}
1294
1295/// Duplicate pipe content
1296///
1297/// # See Also
1298/// *[`tee`](https://man7.org/linux/man-pages/man2/tee.2.html)
1299#[cfg(linux_android)]
1300pub fn tee<Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>(
1301 fd_in: Fd1,
1302 fd_out: Fd2,
1303 len: usize,
1304 flags: SpliceFFlags,
1305) -> Result<usize> {
1306 use std::os::fd::AsRawFd;
1307
1308 let ret = unsafe { libc::tee(fd_in.as_fd().as_raw_fd(), fd_out.as_fd().as_raw_fd(), len, flags.bits()) };
1309 Errno::result(ret).map(|r| r as usize)
1310}
1311
1312/// Splice user pages to/from a pipe
1313///
1314/// # See Also
1315/// *[`vmsplice`](https://man7.org/linux/man-pages/man2/vmsplice.2.html)
1316#[cfg(linux_android)]
1317pub fn vmsplice<F: std::os::fd::AsFd>(
1318 fd: F,
1319 iov: &[std::io::IoSlice<'_>],
1320 flags: SpliceFFlags,
1321) -> Result<usize> {
1322 use std::os::fd::AsRawFd;
1323
1324 let ret = unsafe {
1325 libc::vmsplice(
1326 fd.as_fd().as_raw_fd(),
1327 iov.as_ptr().cast(),
1328 iov.len(),
1329 flags.bits(),
1330 )
1331 };
1332 Errno::result(ret).map(|r| r as usize)
1333}
1334}
1335
1336#[cfg(target_os = "linux")]
1337#[cfg(feature = "fs")]
1338libc_bitflags!(
1339 /// Mode argument flags for fallocate determining operation performed on a given range.
1340 #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
1341 pub struct FallocateFlags: c_int {
1342 /// File size is not changed.
1343 ///
1344 /// offset + len can be greater than file size.
1345 FALLOC_FL_KEEP_SIZE;
1346 /// Deallocates space by creating a hole.
1347 ///
1348 /// Must be ORed with FALLOC_FL_KEEP_SIZE. Byte range starts at offset and continues for len bytes.
1349 FALLOC_FL_PUNCH_HOLE;
1350 /// Removes byte range from a file without leaving a hole.
1351 ///
1352 /// Byte range to collapse starts at offset and continues for len bytes.
1353 FALLOC_FL_COLLAPSE_RANGE;
1354 /// Zeroes space in specified byte range.
1355 ///
1356 /// Byte range starts at offset and continues for len bytes.
1357 FALLOC_FL_ZERO_RANGE;
1358 /// Increases file space by inserting a hole within the file size.
1359 ///
1360 /// Does not overwrite existing data. Hole starts at offset and continues for len bytes.
1361 FALLOC_FL_INSERT_RANGE;
1362 /// Shared file data extants are made private to the file.
1363 ///
1364 /// Guarantees that a subsequent write will not fail due to lack of space.
1365 FALLOC_FL_UNSHARE_RANGE;
1366 }
1367);
1368
1369feature! {
1370#![feature = "fs"]
1371
1372/// Manipulates file space.
1373///
1374/// Allows the caller to directly manipulate the allocated disk space for the
1375/// file referred to by fd.
1376#[cfg(target_os = "linux")]
1377#[cfg(feature = "fs")]
1378pub fn fallocate<Fd: std::os::fd::AsFd>(
1379 fd: Fd,
1380 mode: FallocateFlags,
1381 offset: libc::off_t,
1382 len: libc::off_t,
1383) -> Result<()> {
1384 use std::os::fd::AsRawFd;
1385
1386 let res = unsafe { libc::fallocate(fd.as_fd().as_raw_fd(), mode.bits(), offset, len) };
1387 Errno::result(res).map(drop)
1388}
1389
1390/// Argument to [`fspacectl`] describing the range to zero. The first member is
1391/// the file offset, and the second is the length of the region.
1392#[cfg(any(target_os = "freebsd"))]
1393#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1394pub struct SpacectlRange(pub libc::off_t, pub libc::off_t);
1395
1396#[cfg(any(target_os = "freebsd"))]
1397impl SpacectlRange {
1398 /// Is the range empty?
1399 ///
1400 /// After a successful call to [`fspacectl`], A value of `true` for `SpacectlRange::is_empty`
1401 /// indicates that the operation is complete.
1402 #[inline]
1403 pub fn is_empty(&self) -> bool {
1404 self.1 == 0
1405 }
1406
1407 /// Remaining length of the range
1408 #[inline]
1409 pub fn len(&self) -> libc::off_t {
1410 self.1
1411 }
1412
1413 /// Next file offset to operate on
1414 #[inline]
1415 pub fn offset(&self) -> libc::off_t {
1416 self.0
1417 }
1418}
1419
1420/// Punch holes in a file.
1421///
1422/// `fspacectl` instructs the file system to deallocate a portion of a file.
1423/// After a successful operation, this region of the file will return all zeroes
1424/// if read. If the file system supports deallocation, then it may free the
1425/// underlying storage, too.
1426///
1427/// # Arguments
1428///
1429/// - `fd` - File to operate on
1430/// - `range.0` - File offset at which to begin deallocation
1431/// - `range.1` - Length of the region to deallocate
1432///
1433/// # Returns
1434///
1435/// The operation may deallocate less than the entire requested region. On
1436/// success, it returns the region that still remains to be deallocated. The
1437/// caller should loop until the returned region is empty.
1438///
1439/// # Example
1440///
1441#[cfg_attr(fbsd14, doc = " ```")]
1442#[cfg_attr(not(fbsd14), doc = " ```no_run")]
1443/// # use std::io::Write;
1444/// # use std::os::unix::fs::FileExt;
1445/// # use std::os::unix::io::AsRawFd;
1446/// # use nix::fcntl::*;
1447/// # use tempfile::tempfile;
1448/// const INITIAL: &[u8] = b"0123456789abcdef";
1449/// let mut f = tempfile().unwrap();
1450/// f.write_all(INITIAL).unwrap();
1451/// let mut range = SpacectlRange(3, 6);
1452/// while (!range.is_empty()) {
1453/// range = fspacectl(&f, range).unwrap();
1454/// }
1455/// let mut buf = vec![0; INITIAL.len()];
1456/// f.read_exact_at(&mut buf, 0).unwrap();
1457/// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
1458/// ```
1459#[cfg(target_os = "freebsd")]
1460#[inline] // Delays codegen, preventing linker errors with dylibs and --no-allow-shlib-undefined
1461pub fn fspacectl<Fd: std::os::fd::AsFd>(fd: Fd, range: SpacectlRange) -> Result<SpacectlRange> {
1462 use std::os::fd::AsRawFd;
1463
1464 let mut rqsr = libc::spacectl_range {
1465 r_offset: range.0,
1466 r_len: range.1,
1467 };
1468 let res = unsafe {
1469 libc::fspacectl(
1470 fd.as_fd().as_raw_fd(),
1471 libc::SPACECTL_DEALLOC, // Only one command is supported ATM
1472 &rqsr,
1473 0, // No flags are currently supported
1474 &mut rqsr,
1475 )
1476 };
1477 Errno::result(res).map(|_| SpacectlRange(rqsr.r_offset, rqsr.r_len))
1478}
1479
1480/// Like [`fspacectl`], but will never return incomplete.
1481///
1482/// # Arguments
1483///
1484/// - `fd` - File to operate on
1485/// - `offset` - File offset at which to begin deallocation
1486/// - `len` - Length of the region to deallocate
1487///
1488/// # Returns
1489///
1490/// Returns `()` on success. On failure, the region may or may not be partially
1491/// deallocated.
1492///
1493/// # Example
1494///
1495#[cfg_attr(fbsd14, doc = " ```")]
1496#[cfg_attr(not(fbsd14), doc = " ```no_run")]
1497/// # use std::io::Write;
1498/// # use std::os::unix::fs::FileExt;
1499/// # use std::os::unix::io::AsRawFd;
1500/// # use nix::fcntl::*;
1501/// # use tempfile::tempfile;
1502/// const INITIAL: &[u8] = b"0123456789abcdef";
1503/// let mut f = tempfile().unwrap();
1504/// f.write_all(INITIAL).unwrap();
1505/// fspacectl_all(&f, 3, 6).unwrap();
1506/// let mut buf = vec![0; INITIAL.len()];
1507/// f.read_exact_at(&mut buf, 0).unwrap();
1508/// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
1509/// ```
1510#[cfg(target_os = "freebsd")]
1511#[inline] // Delays codegen, preventing linker errors with dylibs and --no-allow-shlib-undefined
1512pub fn fspacectl_all<Fd: std::os::fd::AsFd>(
1513 fd: Fd,
1514 offset: libc::off_t,
1515 len: libc::off_t,
1516) -> Result<()> {
1517 use std::os::fd::AsRawFd;
1518
1519 let mut rqsr = libc::spacectl_range {
1520 r_offset: offset,
1521 r_len: len,
1522 };
1523 while rqsr.r_len > 0 {
1524 let res = unsafe {
1525 libc::fspacectl(
1526 fd.as_fd().as_raw_fd(),
1527 libc::SPACECTL_DEALLOC, // Only one command is supported ATM
1528 &rqsr,
1529 0, // No flags are currently supported
1530 &mut rqsr,
1531 )
1532 };
1533 Errno::result(res)?;
1534 }
1535 Ok(())
1536}
1537
1538#[cfg(any(
1539 linux_android,
1540 target_os = "emscripten",
1541 target_os = "fuchsia",
1542 target_os = "wasi",
1543 target_env = "uclibc",
1544 target_os = "freebsd"
1545))]
1546mod posix_fadvise {
1547 use crate::errno::Errno;
1548 use crate::Result;
1549
1550 #[cfg(feature = "fs")]
1551 libc_enum! {
1552 /// The specific advice provided to [`posix_fadvise`].
1553 #[repr(i32)]
1554 #[non_exhaustive]
1555 #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
1556 pub enum PosixFadviseAdvice {
1557 /// Revert to the default data access behavior.
1558 POSIX_FADV_NORMAL,
1559 /// The file data will be accessed sequentially.
1560 POSIX_FADV_SEQUENTIAL,
1561 /// A hint that file data will be accessed randomly, and prefetching is likely not
1562 /// advantageous.
1563 POSIX_FADV_RANDOM,
1564 /// The specified data will only be accessed once and then not reused.
1565 POSIX_FADV_NOREUSE,
1566 /// The specified data will be accessed in the near future.
1567 POSIX_FADV_WILLNEED,
1568 /// The specified data will not be accessed in the near future.
1569 POSIX_FADV_DONTNEED,
1570 }
1571 }
1572
1573 feature! {
1574 #![feature = "fs"]
1575 /// Allows a process to describe to the system its data access behavior for an open file
1576 /// descriptor.
1577 ///
1578 /// # See Also
1579 /// * [`posix_fadvise`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_fadvise.html)
1580 pub fn posix_fadvise<Fd: std::os::fd::AsFd>(
1581 fd: Fd,
1582 offset: libc::off_t,
1583 len: libc::off_t,
1584 advice: PosixFadviseAdvice,
1585 ) -> Result<()> {
1586 use std::os::fd::AsRawFd;
1587
1588 let res = unsafe { libc::posix_fadvise(fd.as_fd().as_raw_fd(), offset, len, advice as libc::c_int) };
1589
1590 if res == 0 {
1591 Ok(())
1592 } else {
1593 Err(Errno::from_raw(res))
1594 }
1595 }
1596 }
1597}
1598
1599/// Pre-allocate storage for a range in a file
1600///
1601/// # See Also
1602/// * [`posix_fallocate`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_fallocate.html)
1603#[cfg(any(
1604 linux_android,
1605 freebsdlike,
1606 target_os = "emscripten",
1607 target_os = "fuchsia",
1608 target_os = "wasi",
1609))]
1610pub fn posix_fallocate<Fd: std::os::fd::AsFd>(
1611 fd: Fd,
1612 offset: libc::off_t,
1613 len: libc::off_t,
1614) -> Result<()> {
1615 use std::os::fd::AsRawFd;
1616
1617 let res = unsafe { libc::posix_fallocate(fd.as_fd().as_raw_fd(), offset, len) };
1618 match Errno::result(res) {
1619 Err(err) => Err(err),
1620 Ok(0) => Ok(()),
1621 Ok(errno) => Err(Errno::from_raw(errno)),
1622 }
1623}
1624}