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`](FcntlArg::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`](FcntlArg::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")))]
1041impl<T: Flockable> Drop for Flock<T> {
1042 fn drop(&mut self) {
1043 let res = Errno::result(unsafe { libc::flock(self.0.as_raw_fd(), libc::LOCK_UN) });
1044 if res.is_err() && !std::thread::panicking() {
1045 panic!("Failed to remove flock: {}", res.unwrap_err());
1046 }
1047 }
1048}
1049
1050#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
1051impl<T: Flockable> Deref for Flock<T> {
1052 type Target = T;
1053
1054 fn deref(&self) -> &Self::Target {
1055 &self.0
1056 }
1057}
1058#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
1059impl<T: Flockable> DerefMut for Flock<T> {
1060 fn deref_mut(&mut self) -> &mut Self::Target {
1061 &mut self.0
1062 }
1063}
1064
1065#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
1066impl<T: Flockable> Flock<T> {
1067 /// Obtain a/an flock.
1068 ///
1069 /// # Example
1070 /// ```
1071 /// # use std::io::Write;
1072 /// # use std::fs::File;
1073 /// # use nix::fcntl::{Flock, FlockArg};
1074 /// # fn do_stuff(file: File) {
1075 /// let mut file = match Flock::lock(file, FlockArg::LockExclusive) {
1076 /// Ok(l) => l,
1077 /// Err(_) => return,
1078 /// };
1079 ///
1080 /// // Do stuff
1081 /// let data = "Foo bar";
1082 /// _ = file.write(data.as_bytes());
1083 /// _ = file.sync_data();
1084 /// # }
1085 pub fn lock(t: T, args: FlockArg) -> std::result::Result<Self, (T, Errno)> {
1086 let flags = match args {
1087 FlockArg::LockShared => libc::LOCK_SH,
1088 FlockArg::LockExclusive => libc::LOCK_EX,
1089 FlockArg::LockSharedNonblock => libc::LOCK_SH | libc::LOCK_NB,
1090 FlockArg::LockExclusiveNonblock => libc::LOCK_EX | libc::LOCK_NB,
1091 #[allow(deprecated)]
1092 FlockArg::Unlock | FlockArg::UnlockNonblock => return Err((t, Errno::EINVAL)),
1093 };
1094 match Errno::result(unsafe { libc::flock(t.as_raw_fd(), flags) }) {
1095 Ok(_) => Ok(Self(t)),
1096 Err(errno) => Err((t, errno)),
1097 }
1098 }
1099
1100 /// Remove the lock and return the object wrapped within.
1101 ///
1102 /// # Example
1103 /// ```
1104 /// # use std::fs::File;
1105 /// # use nix::fcntl::{Flock, FlockArg};
1106 /// fn do_stuff(file: File) -> nix::Result<()> {
1107 /// let mut lock = match Flock::lock(file, FlockArg::LockExclusive) {
1108 /// Ok(l) => l,
1109 /// Err((_,e)) => return Err(e),
1110 /// };
1111 ///
1112 /// // Do critical section
1113 ///
1114 /// // Unlock
1115 /// let file = match lock.unlock() {
1116 /// Ok(f) => f,
1117 /// Err((_, e)) => return Err(e),
1118 /// };
1119 ///
1120 /// // Do anything else
1121 ///
1122 /// Ok(())
1123 /// }
1124 pub fn unlock(self) -> std::result::Result<T, (Self, Errno)> {
1125 let inner = unsafe { match Errno::result(libc::flock(self.0.as_raw_fd(), libc::LOCK_UN)) {
1126 Ok(_) => std::ptr::read(&self.0),
1127 Err(errno) => return Err((self, errno)),
1128 }};
1129
1130 std::mem::forget(self);
1131 Ok(inner)
1132 }
1133
1134 /// Relock the file. This can upgrade or downgrade the lock type.
1135 ///
1136 /// # Example
1137 /// ```
1138 /// # use std::fs::File;
1139 /// # use nix::fcntl::{Flock, FlockArg};
1140 /// # use tempfile::tempfile;
1141 /// let f: std::fs::File = tempfile().unwrap();
1142 /// let locked_file = Flock::lock(f, FlockArg::LockExclusive).unwrap();
1143 /// // Do stuff, then downgrade the lock
1144 /// locked_file.relock(FlockArg::LockShared).unwrap();
1145 /// ```
1146 pub fn relock(&self, arg: FlockArg) -> Result<()> {
1147 let flags = match arg {
1148 FlockArg::LockShared => libc::LOCK_SH,
1149 FlockArg::LockExclusive => libc::LOCK_EX,
1150 FlockArg::LockSharedNonblock => libc::LOCK_SH | libc::LOCK_NB,
1151 FlockArg::LockExclusiveNonblock => libc::LOCK_EX | libc::LOCK_NB,
1152 #[allow(deprecated)]
1153 FlockArg::Unlock | FlockArg::UnlockNonblock => return Err(Errno::EINVAL),
1154 };
1155 Errno::result(unsafe { libc::flock(self.as_raw_fd(), flags) }).map(drop)
1156 }
1157}
1158
1159// Safety: `File` is not [std::clone::Clone].
1160#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
1161unsafe impl Flockable for std::fs::File {}
1162
1163// Safety: `OwnedFd` is not [std::clone::Clone].
1164#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
1165unsafe impl Flockable for OwnedFd {}
1166}
1167
1168#[cfg(linux_android)]
1169#[cfg(feature = "zerocopy")]
1170libc_bitflags! {
1171 /// Additional flags to `splice` and friends.
1172 #[cfg_attr(docsrs, doc(cfg(feature = "zerocopy")))]
1173 pub struct SpliceFFlags: c_uint {
1174 /// Request that pages be moved instead of copied.
1175 ///
1176 /// Not applicable to `vmsplice`.
1177 SPLICE_F_MOVE;
1178 /// Do not block on I/O.
1179 SPLICE_F_NONBLOCK;
1180 /// Hint that more data will be coming in a subsequent splice.
1181 ///
1182 /// Not applicable to `vmsplice`.
1183 SPLICE_F_MORE;
1184 /// Gift the user pages to the kernel.
1185 ///
1186 /// Not applicable to `splice`.
1187 SPLICE_F_GIFT;
1188 }
1189}
1190
1191feature! {
1192#![feature = "zerocopy"]
1193
1194/// Copy a range of data from one file to another
1195///
1196/// The `copy_file_range` system call performs an in-kernel copy between
1197/// file descriptors `fd_in` and `fd_out` without the additional cost of
1198/// transferring data from the kernel to user space and back again. There may be
1199/// additional optimizations for specific file systems. It copies up to `len`
1200/// bytes of data from file descriptor `fd_in` to file descriptor `fd_out`,
1201/// overwriting any data that exists within the requested range of the target
1202/// file.
1203///
1204/// If the `off_in` and/or `off_out` arguments are used, the values
1205/// will be mutated to reflect the new position within the file after
1206/// copying. If they are not used, the relevant file descriptors will be seeked
1207/// to the new position.
1208///
1209/// On successful completion the number of bytes actually copied will be
1210/// returned.
1211// Note: FreeBSD defines the offset argument as "off_t". Linux and Android
1212// define it as "loff_t". But on both OSes, on all supported platforms, those
1213// are 64 bits. So Nix uses i64 to make the docs simple and consistent.
1214#[cfg(any(linux_android, target_os = "freebsd"))]
1215pub fn copy_file_range<Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>(
1216 fd_in: Fd1,
1217 off_in: Option<&mut i64>,
1218 fd_out: Fd2,
1219 off_out: Option<&mut i64>,
1220 len: usize,
1221) -> Result<usize> {
1222 use std::os::fd::AsRawFd;
1223
1224 let off_in = off_in
1225 .map(|offset| offset as *mut i64)
1226 .unwrap_or(ptr::null_mut());
1227 let off_out = off_out
1228 .map(|offset| offset as *mut i64)
1229 .unwrap_or(ptr::null_mut());
1230
1231 cfg_if::cfg_if! {
1232 if #[cfg(target_os = "freebsd")] {
1233 let ret = unsafe {
1234 libc::copy_file_range(
1235 fd_in.as_fd().as_raw_fd(),
1236 off_in,
1237 fd_out.as_fd().as_raw_fd(),
1238 off_out,
1239 len,
1240 0,
1241 )
1242 };
1243 } else {
1244 // May Linux distros still don't include copy_file_range in their
1245 // libc implementations, so we need to make a direct syscall.
1246 let ret = unsafe {
1247 libc::syscall(
1248 libc::SYS_copy_file_range,
1249 fd_in.as_fd().as_raw_fd(),
1250 off_in,
1251 fd_out.as_fd().as_raw_fd(),
1252 off_out,
1253 len,
1254 0,
1255 )
1256 };
1257 }
1258 }
1259 Errno::result(ret).map(|r| r as usize)
1260}
1261
1262/// Splice data to/from a pipe
1263///
1264/// # See Also
1265/// *[`splice`](https://man7.org/linux/man-pages/man2/splice.2.html)
1266#[cfg(linux_android)]
1267pub fn splice<Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>(
1268 fd_in: Fd1,
1269 off_in: Option<&mut libc::loff_t>,
1270 fd_out: Fd2,
1271 off_out: Option<&mut libc::loff_t>,
1272 len: usize,
1273 flags: SpliceFFlags,
1274) -> Result<usize> {
1275 use std::os::fd::AsRawFd;
1276
1277 let off_in = off_in
1278 .map(|offset| offset as *mut libc::loff_t)
1279 .unwrap_or(ptr::null_mut());
1280 let off_out = off_out
1281 .map(|offset| offset as *mut libc::loff_t)
1282 .unwrap_or(ptr::null_mut());
1283
1284 let ret = unsafe {
1285 libc::splice(fd_in.as_fd().as_raw_fd(), off_in, fd_out.as_fd().as_raw_fd(), off_out, len, flags.bits())
1286 };
1287 Errno::result(ret).map(|r| r as usize)
1288}
1289
1290/// Duplicate pipe content
1291///
1292/// # See Also
1293/// *[`tee`](https://man7.org/linux/man-pages/man2/tee.2.html)
1294#[cfg(linux_android)]
1295pub fn tee<Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>(
1296 fd_in: Fd1,
1297 fd_out: Fd2,
1298 len: usize,
1299 flags: SpliceFFlags,
1300) -> Result<usize> {
1301 use std::os::fd::AsRawFd;
1302
1303 let ret = unsafe { libc::tee(fd_in.as_fd().as_raw_fd(), fd_out.as_fd().as_raw_fd(), len, flags.bits()) };
1304 Errno::result(ret).map(|r| r as usize)
1305}
1306
1307/// Splice user pages to/from a pipe
1308///
1309/// # See Also
1310/// *[`vmsplice`](https://man7.org/linux/man-pages/man2/vmsplice.2.html)
1311#[cfg(linux_android)]
1312pub fn vmsplice<F: std::os::fd::AsFd>(
1313 fd: F,
1314 iov: &[std::io::IoSlice<'_>],
1315 flags: SpliceFFlags,
1316) -> Result<usize> {
1317 use std::os::fd::AsRawFd;
1318
1319 let ret = unsafe {
1320 libc::vmsplice(
1321 fd.as_fd().as_raw_fd(),
1322 iov.as_ptr().cast(),
1323 iov.len(),
1324 flags.bits(),
1325 )
1326 };
1327 Errno::result(ret).map(|r| r as usize)
1328}
1329}
1330
1331#[cfg(target_os = "linux")]
1332#[cfg(feature = "fs")]
1333libc_bitflags!(
1334 /// Mode argument flags for fallocate determining operation performed on a given range.
1335 #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
1336 pub struct FallocateFlags: c_int {
1337 /// File size is not changed.
1338 ///
1339 /// offset + len can be greater than file size.
1340 FALLOC_FL_KEEP_SIZE;
1341 /// Deallocates space by creating a hole.
1342 ///
1343 /// Must be ORed with FALLOC_FL_KEEP_SIZE. Byte range starts at offset and continues for len bytes.
1344 FALLOC_FL_PUNCH_HOLE;
1345 /// Removes byte range from a file without leaving a hole.
1346 ///
1347 /// Byte range to collapse starts at offset and continues for len bytes.
1348 FALLOC_FL_COLLAPSE_RANGE;
1349 /// Zeroes space in specified byte range.
1350 ///
1351 /// Byte range starts at offset and continues for len bytes.
1352 FALLOC_FL_ZERO_RANGE;
1353 /// Increases file space by inserting a hole within the file size.
1354 ///
1355 /// Does not overwrite existing data. Hole starts at offset and continues for len bytes.
1356 FALLOC_FL_INSERT_RANGE;
1357 /// Shared file data extants are made private to the file.
1358 ///
1359 /// Guarantees that a subsequent write will not fail due to lack of space.
1360 FALLOC_FL_UNSHARE_RANGE;
1361 }
1362);
1363
1364feature! {
1365#![feature = "fs"]
1366
1367/// Manipulates file space.
1368///
1369/// Allows the caller to directly manipulate the allocated disk space for the
1370/// file referred to by fd.
1371#[cfg(target_os = "linux")]
1372#[cfg(feature = "fs")]
1373pub fn fallocate<Fd: std::os::fd::AsFd>(
1374 fd: Fd,
1375 mode: FallocateFlags,
1376 offset: libc::off_t,
1377 len: libc::off_t,
1378) -> Result<()> {
1379 use std::os::fd::AsRawFd;
1380
1381 let res = unsafe { libc::fallocate(fd.as_fd().as_raw_fd(), mode.bits(), offset, len) };
1382 Errno::result(res).map(drop)
1383}
1384
1385/// Argument to [`fspacectl`] describing the range to zero. The first member is
1386/// the file offset, and the second is the length of the region.
1387#[cfg(any(target_os = "freebsd"))]
1388#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1389pub struct SpacectlRange(pub libc::off_t, pub libc::off_t);
1390
1391#[cfg(any(target_os = "freebsd"))]
1392impl SpacectlRange {
1393 /// Is the range empty?
1394 ///
1395 /// After a successful call to [`fspacectl`], A value of `true` for `SpacectlRange::is_empty`
1396 /// indicates that the operation is complete.
1397 #[inline]
1398 pub fn is_empty(&self) -> bool {
1399 self.1 == 0
1400 }
1401
1402 /// Remaining length of the range
1403 #[inline]
1404 pub fn len(&self) -> libc::off_t {
1405 self.1
1406 }
1407
1408 /// Next file offset to operate on
1409 #[inline]
1410 pub fn offset(&self) -> libc::off_t {
1411 self.0
1412 }
1413}
1414
1415/// Punch holes in a file.
1416///
1417/// `fspacectl` instructs the file system to deallocate a portion of a file.
1418/// After a successful operation, this region of the file will return all zeroes
1419/// if read. If the file system supports deallocation, then it may free the
1420/// underlying storage, too.
1421///
1422/// # Arguments
1423///
1424/// - `fd` - File to operate on
1425/// - `range.0` - File offset at which to begin deallocation
1426/// - `range.1` - Length of the region to deallocate
1427///
1428/// # Returns
1429///
1430/// The operation may deallocate less than the entire requested region. On
1431/// success, it returns the region that still remains to be deallocated. The
1432/// caller should loop until the returned region is empty.
1433///
1434/// # Example
1435///
1436#[cfg_attr(fbsd14, doc = " ```")]
1437#[cfg_attr(not(fbsd14), doc = " ```no_run")]
1438/// # use std::io::Write;
1439/// # use std::os::unix::fs::FileExt;
1440/// # use std::os::unix::io::AsRawFd;
1441/// # use nix::fcntl::*;
1442/// # use tempfile::tempfile;
1443/// const INITIAL: &[u8] = b"0123456789abcdef";
1444/// let mut f = tempfile().unwrap();
1445/// f.write_all(INITIAL).unwrap();
1446/// let mut range = SpacectlRange(3, 6);
1447/// while (!range.is_empty()) {
1448/// range = fspacectl(&f, range).unwrap();
1449/// }
1450/// let mut buf = vec![0; INITIAL.len()];
1451/// f.read_exact_at(&mut buf, 0).unwrap();
1452/// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
1453/// ```
1454#[cfg(target_os = "freebsd")]
1455#[inline] // Delays codegen, preventing linker errors with dylibs and --no-allow-shlib-undefined
1456pub fn fspacectl<Fd: std::os::fd::AsFd>(fd: Fd, range: SpacectlRange) -> Result<SpacectlRange> {
1457 use std::os::fd::AsRawFd;
1458
1459 let mut rqsr = libc::spacectl_range {
1460 r_offset: range.0,
1461 r_len: range.1,
1462 };
1463 let res = unsafe {
1464 libc::fspacectl(
1465 fd.as_fd().as_raw_fd(),
1466 libc::SPACECTL_DEALLOC, // Only one command is supported ATM
1467 &rqsr,
1468 0, // No flags are currently supported
1469 &mut rqsr,
1470 )
1471 };
1472 Errno::result(res).map(|_| SpacectlRange(rqsr.r_offset, rqsr.r_len))
1473}
1474
1475/// Like [`fspacectl`], but will never return incomplete.
1476///
1477/// # Arguments
1478///
1479/// - `fd` - File to operate on
1480/// - `offset` - File offset at which to begin deallocation
1481/// - `len` - Length of the region to deallocate
1482///
1483/// # Returns
1484///
1485/// Returns `()` on success. On failure, the region may or may not be partially
1486/// deallocated.
1487///
1488/// # Example
1489///
1490#[cfg_attr(fbsd14, doc = " ```")]
1491#[cfg_attr(not(fbsd14), doc = " ```no_run")]
1492/// # use std::io::Write;
1493/// # use std::os::unix::fs::FileExt;
1494/// # use std::os::unix::io::AsRawFd;
1495/// # use nix::fcntl::*;
1496/// # use tempfile::tempfile;
1497/// const INITIAL: &[u8] = b"0123456789abcdef";
1498/// let mut f = tempfile().unwrap();
1499/// f.write_all(INITIAL).unwrap();
1500/// fspacectl_all(&f, 3, 6).unwrap();
1501/// let mut buf = vec![0; INITIAL.len()];
1502/// f.read_exact_at(&mut buf, 0).unwrap();
1503/// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
1504/// ```
1505#[cfg(target_os = "freebsd")]
1506#[inline] // Delays codegen, preventing linker errors with dylibs and --no-allow-shlib-undefined
1507pub fn fspacectl_all<Fd: std::os::fd::AsFd>(
1508 fd: Fd,
1509 offset: libc::off_t,
1510 len: libc::off_t,
1511) -> Result<()> {
1512 use std::os::fd::AsRawFd;
1513
1514 let mut rqsr = libc::spacectl_range {
1515 r_offset: offset,
1516 r_len: len,
1517 };
1518 while rqsr.r_len > 0 {
1519 let res = unsafe {
1520 libc::fspacectl(
1521 fd.as_fd().as_raw_fd(),
1522 libc::SPACECTL_DEALLOC, // Only one command is supported ATM
1523 &rqsr,
1524 0, // No flags are currently supported
1525 &mut rqsr,
1526 )
1527 };
1528 Errno::result(res)?;
1529 }
1530 Ok(())
1531}
1532
1533#[cfg(any(
1534 linux_android,
1535 target_os = "emscripten",
1536 target_os = "fuchsia",
1537 target_os = "wasi",
1538 target_env = "uclibc",
1539 target_os = "freebsd"
1540))]
1541mod posix_fadvise {
1542 use crate::errno::Errno;
1543 use crate::Result;
1544
1545 #[cfg(feature = "fs")]
1546 libc_enum! {
1547 /// The specific advice provided to [`posix_fadvise`].
1548 #[repr(i32)]
1549 #[non_exhaustive]
1550 #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
1551 pub enum PosixFadviseAdvice {
1552 /// Revert to the default data access behavior.
1553 POSIX_FADV_NORMAL,
1554 /// The file data will be accessed sequentially.
1555 POSIX_FADV_SEQUENTIAL,
1556 /// A hint that file data will be accessed randomly, and prefetching is likely not
1557 /// advantageous.
1558 POSIX_FADV_RANDOM,
1559 /// The specified data will only be accessed once and then not reused.
1560 POSIX_FADV_NOREUSE,
1561 /// The specified data will be accessed in the near future.
1562 POSIX_FADV_WILLNEED,
1563 /// The specified data will not be accessed in the near future.
1564 POSIX_FADV_DONTNEED,
1565 }
1566 }
1567
1568 feature! {
1569 #![feature = "fs"]
1570 /// Allows a process to describe to the system its data access behavior for an open file
1571 /// descriptor.
1572 ///
1573 /// # See Also
1574 /// * [`posix_fadvise`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_fadvise.html)
1575 pub fn posix_fadvise<Fd: std::os::fd::AsFd>(
1576 fd: Fd,
1577 offset: libc::off_t,
1578 len: libc::off_t,
1579 advice: PosixFadviseAdvice,
1580 ) -> Result<()> {
1581 use std::os::fd::AsRawFd;
1582
1583 let res = unsafe { libc::posix_fadvise(fd.as_fd().as_raw_fd(), offset, len, advice as libc::c_int) };
1584
1585 if res == 0 {
1586 Ok(())
1587 } else {
1588 Err(Errno::from_raw(res))
1589 }
1590 }
1591 }
1592}
1593
1594/// Pre-allocate storage for a range in a file
1595///
1596/// # See Also
1597/// * [`posix_fallocate`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_fallocate.html)
1598#[cfg(any(
1599 linux_android,
1600 freebsdlike,
1601 target_os = "emscripten",
1602 target_os = "fuchsia",
1603 target_os = "wasi",
1604))]
1605pub fn posix_fallocate<Fd: std::os::fd::AsFd>(
1606 fd: Fd,
1607 offset: libc::off_t,
1608 len: libc::off_t,
1609) -> Result<()> {
1610 use std::os::fd::AsRawFd;
1611
1612 let res = unsafe { libc::posix_fallocate(fd.as_fd().as_raw_fd(), offset, len) };
1613 match Errno::result(res) {
1614 Err(err) => Err(err),
1615 Ok(0) => Ok(()),
1616 Ok(errno) => Err(Errno::from_raw(errno)),
1617 }
1618}
1619}