Coverage Report

Created: 2025-10-29 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/nix-0.30.1/src/fcntl.rs
Line
Count
Source
1
//! File control options
2
use crate::errno::Errno;
3
#[cfg(all(target_os = "freebsd", target_arch = "x86_64"))]
4
use core::slice;
5
use 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
))]
12
use std::ffi::CStr;
13
use std::ffi::OsString;
14
#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
15
use std::ops::{Deref, DerefMut};
16
use std::os::unix::ffi::OsStringExt;
17
#[cfg(not(target_os = "redox"))]
18
use std::os::unix::io::OwnedFd;
19
use 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
))]
26
use std::path::PathBuf;
27
#[cfg(any(linux_android, target_os = "freebsd"))]
28
use std::ptr;
29
30
#[cfg(feature = "fs")]
31
use 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")]
42
pub 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
77
pub 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"))]
82
libc_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
))]
116
libc_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
236
feature! {
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)]
245
0
pub fn open<P: ?Sized + NixPath>(
246
0
    path: &P,
247
0
    oflag: OFlag,
248
0
    mode: Mode,
249
0
) -> Result<std::os::fd::OwnedFd> {
250
    use std::os::fd::FromRawFd;
251
252
0
    let fd = path.with_nix_path(|cstr| unsafe {
253
0
        libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint)
254
0
    })?;
255
0
    Errno::result(fd)?;
256
257
    // SAFETY:
258
    //
259
    // `open(2)` should return a valid owned fd on success
260
0
    Ok( unsafe { std::os::fd::OwnedFd::from_raw_fd(fd)  } )
261
0
}
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"))]
274
0
pub fn openat<P: ?Sized + NixPath, Fd: std::os::fd::AsFd>(
275
0
    dirfd: Fd,
276
0
    path: &P,
277
0
    oflag: OFlag,
278
0
    mode: Mode,
279
0
) -> Result<OwnedFd> {
280
    use std::os::fd::AsRawFd;
281
    use std::os::fd::FromRawFd;
282
283
0
    let fd = path.with_nix_path(|cstr| unsafe {
284
0
        libc::openat(dirfd.as_fd().as_raw_fd(), cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint)
285
0
    })?;
286
0
    Errno::result(fd)?;
287
288
    // SAFETY:
289
    //
290
    // `openat(2)` should return a valid owned fd on success
291
0
    Ok( unsafe { OwnedFd::from_raw_fd(fd)  } )
292
0
}
293
294
cfg_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
0
            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
0
                Self(unsafe {
344
0
                    std::mem::MaybeUninit::zeroed().assume_init()
345
0
                })
346
0
            }
347
348
            /// Set the open flags used to open a file, completely overwriting any
349
            /// existing flags.
350
0
            pub fn flags(mut self, flags: OFlag) -> Self {
351
0
                let flags = flags.bits() as libc::c_ulonglong;
352
0
                self.0.flags = flags;
353
0
                self
354
0
            }
355
356
            /// Set the file mode new files will be created with, overwriting any
357
            /// existing flags.
358
0
            pub fn mode(mut self, mode: Mode) -> Self {
359
0
                let mode = mode.bits() as libc::c_ulonglong;
360
0
                self.0.mode = mode;
361
0
                self
362
0
            }
363
364
            /// Set resolve flags, completely overwriting any existing flags.
365
            ///
366
            /// See [ResolveFlag] for more detail.
367
0
            pub fn resolve(mut self, resolve: ResolveFlag) -> Self {
368
0
                let resolve = resolve.bits();
369
0
                self.0.resolve = resolve;
370
0
                self
371
0
            }
372
        }
373
374
        // safety: default isn't derivable because libc::open_how must be zeroed
375
        impl Default for OpenHow {
376
0
            fn default() -> Self {
377
0
                Self::new()
378
0
            }
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
0
        pub fn openat2<P: ?Sized + NixPath, Fd: std::os::fd::AsFd>(
390
0
            dirfd: Fd,
391
0
            path: &P,
392
0
            mut how: OpenHow,
393
0
        ) -> Result<OwnedFd> {
394
            use std::os::fd::AsRawFd;
395
            use std::os::fd::FromRawFd;
396
397
0
            let fd = path.with_nix_path(|cstr| unsafe {
398
0
                libc::syscall(
399
                    libc::SYS_openat2,
400
0
                    dirfd.as_fd().as_raw_fd(),
401
0
                    cstr.as_ptr(),
402
0
                    &mut how as *mut OpenHow,
403
0
                    std::mem::size_of::<libc::open_how>(),
404
                )
405
0
            })? as RawFd;
406
0
            Errno::result(fd)?;
407
408
            // SAFETY:
409
            //
410
            // `openat2(2)` should return a valid owned fd on success
411
0
            Ok( unsafe { OwnedFd::from_raw_fd(fd)  } )
412
0
        }
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"))]
425
0
pub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath, Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>(
426
0
    old_dirfd: Fd1,
427
0
    old_path: &P1,
428
0
    new_dirfd: Fd2,
429
0
    new_path: &P2,
430
0
) -> Result<()> {
431
    use std::os::fd::AsRawFd;
432
433
0
    let res = old_path.with_nix_path(|old_cstr| {
434
0
        new_path.with_nix_path(|new_cstr| unsafe {
435
0
            libc::renameat(
436
0
                old_dirfd.as_fd().as_raw_fd(),
437
0
                old_cstr.as_ptr(),
438
0
                new_dirfd.as_fd().as_raw_fd(),
439
0
                new_cstr.as_ptr(),
440
            )
441
0
        })
442
0
    })??;
443
0
    Errno::result(res).map(drop)
444
0
}
445
}
446
447
#[cfg(all(target_os = "linux", target_env = "gnu"))]
448
#[cfg(feature = "fs")]
449
libc_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
466
feature! {
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"))]
475
0
pub fn renameat2<P1: ?Sized + NixPath, P2: ?Sized + NixPath, Fd1: std::os::fd::AsFd, Fd2: std::os::fd::AsFd>(
476
0
    old_dirfd: Fd1,
477
0
    old_path: &P1,
478
0
    new_dirfd: Fd2,
479
0
    new_path: &P2,
480
0
    flags: RenameFlags,
481
0
) -> Result<()> {
482
    use std::os::fd::AsRawFd;
483
484
0
    let res = old_path.with_nix_path(|old_cstr| {
485
0
        new_path.with_nix_path(|new_cstr| unsafe {
486
0
            libc::renameat2(
487
0
                old_dirfd.as_fd().as_raw_fd(),
488
0
                old_cstr.as_ptr(),
489
0
                new_dirfd.as_fd().as_raw_fd(),
490
0
                new_cstr.as_ptr(),
491
0
                flags.bits(),
492
            )
493
0
        })
494
0
    })??;
495
0
    Errno::result(res).map(drop)
496
0
}
497
498
0
fn wrap_readlink_result(mut v: Vec<u8>, len: ssize_t) -> Result<OsString> {
499
0
    unsafe { v.set_len(len as usize) }
500
0
    v.shrink_to_fit();
501
0
    Ok(OsString::from_vec(v.to_vec()))
502
0
}
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.
513
0
unsafe fn readlink_maybe_at<P: ?Sized + NixPath>(
514
0
    dirfd: Option<RawFd>,
515
0
    path: &P,
516
0
    v: &mut Vec<u8>,
517
0
) -> Result<libc::ssize_t> {
518
0
    path.with_nix_path(|cstr| unsafe {
519
0
        match dirfd {
520
            #[cfg(target_os = "redox")]
521
            Some(_) => unreachable!("redox does not have readlinkat(2)"),
522
            #[cfg(not(target_os = "redox"))]
523
0
            Some(dirfd) => libc::readlinkat(
524
0
                dirfd,
525
0
                cstr.as_ptr(),
526
0
                v.as_mut_ptr().cast(),
527
0
                v.capacity() as size_t,
528
            ),
529
0
            None => libc::readlink(
530
0
                cstr.as_ptr(),
531
0
                v.as_mut_ptr().cast(),
532
0
                v.capacity() as size_t,
533
            ),
534
        }
535
0
    })
536
0
}
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`.
546
0
unsafe fn inner_readlink<P: ?Sized + NixPath>(
547
0
    dirfd: Option<RawFd>,
548
0
    path: &P,
549
0
) -> 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
0
    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
0
        let res = unsafe { readlink_maybe_at(dirfd, path, &mut v)? };
564
0
        let len = Errno::result(res)?;
565
0
        debug_assert!(len >= 0);
566
0
        if (len as usize) < v.capacity() {
567
0
            return wrap_readlink_result(v, res);
568
0
        }
569
    }
570
571
    // Uh oh, the result is too long...
572
    // Let's try to ask lstat how many bytes to allocate.
573
0
    let mut try_size = {
574
0
        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
0
            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
0
                let dirfd = unsafe {
584
0
                    std::os::fd::BorrowedFd::borrow_raw(dirfd)
585
                };
586
0
                let flags = if path.is_empty() {
587
0
                    AtFlags::AT_EMPTY_PATH
588
                } else {
589
0
                    AtFlags::empty()
590
                };
591
0
                super::sys::stat::fstatat(
592
0
                    dirfd,
593
0
                    path,
594
0
                    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
0
            None => super::sys::stat::lstat(path),
614
        }
615
0
        .map(|x| x.st_size)
616
0
        .unwrap_or(0);
617
618
0
        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
0
            reported_size as usize + 1
622
        } else {
623
            // If lstat doesn't cooperate, or reports an error, be a little less
624
            // precise.
625
0
            PATH_MAX.max(128) << 1
626
        }
627
    };
628
629
    loop {
630
        {
631
0
            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
0
            let res = unsafe { readlink_maybe_at(dirfd, path, &mut v)? };
637
0
            let len = Errno::result(res)?;
638
0
            debug_assert!(len >= 0);
639
0
            if (len as usize) < v.capacity() {
640
0
                return wrap_readlink_result(v, res);
641
0
            }
642
        }
643
644
        // Ugh! Still not big enough!
645
0
        match try_size.checked_shl(1) {
646
0
            Some(next_size) => try_size = next_size,
647
            // It's absurd that this would happen, but handle it sanely
648
            // anyway.
649
0
            None => break Err(Errno::ENAMETOOLONG),
650
        }
651
    }
652
0
}
653
654
/// Read value of a symbolic link
655
///
656
/// # See Also
657
/// * [`readlink`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html)
658
0
pub 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
0
    unsafe { inner_readlink(None, path) }
669
0
}
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"))]
680
0
pub fn readlinkat<Fd: std::os::fd::AsFd,P: ?Sized + NixPath>(
681
0
    dirfd: Fd,
682
0
    path: &P,
683
0
) -> 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
0
    unsafe { inner_readlink(Some(dirfd.as_fd().as_raw_fd()), path) }
692
0
}
693
}
694
695
#[cfg(any(linux_android, target_os = "freebsd"))]
696
#[cfg(feature = "fs")]
697
libc_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")]
717
libc_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
726
feature! {
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]
733
pub 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]
841
pub 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
}
855
pub 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
862
0
pub fn fcntl<Fd: std::os::fd::AsFd>(fd: Fd, arg: FcntlArg) -> Result<c_int> {
863
    use std::os::fd::AsRawFd;
864
865
0
    let fd = fd.as_fd().as_raw_fd();
866
0
    let res = unsafe {
867
0
        match arg {
868
0
            F_DUPFD(rawfd) => libc::fcntl(fd, libc::F_DUPFD, rawfd),
869
0
            F_DUPFD_CLOEXEC(rawfd) => {
870
0
                libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, rawfd)
871
            }
872
0
            F_GETFD => libc::fcntl(fd, libc::F_GETFD),
873
0
            F_SETFD(flag) => libc::fcntl(fd, libc::F_SETFD, flag.bits()),
874
0
            F_GETFL => libc::fcntl(fd, libc::F_GETFL),
875
0
            F_SETFL(flag) => libc::fcntl(fd, libc::F_SETFL, flag.bits()),
876
            #[cfg(not(target_os = "redox"))]
877
0
            F_SETLK(flock) => libc::fcntl(fd, libc::F_SETLK, flock),
878
            #[cfg(not(target_os = "redox"))]
879
0
            F_SETLKW(flock) => libc::fcntl(fd, libc::F_SETLKW, flock),
880
            #[cfg(not(target_os = "redox"))]
881
0
            F_GETLK(flock) => libc::fcntl(fd, libc::F_GETLK, flock),
882
            #[cfg(linux_android)]
883
0
            F_OFD_SETLK(flock) => libc::fcntl(fd, libc::F_OFD_SETLK, flock),
884
            #[cfg(linux_android)]
885
0
            F_OFD_SETLKW(flock) => libc::fcntl(fd, libc::F_OFD_SETLKW, flock),
886
            #[cfg(linux_android)]
887
0
            F_OFD_GETLK(flock) => libc::fcntl(fd, libc::F_OFD_GETLK, flock),
888
            #[cfg(any(
889
                linux_android,
890
                target_os = "freebsd"
891
            ))]
892
0
            F_ADD_SEALS(flag) => {
893
0
                libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits())
894
            }
895
            #[cfg(any(
896
                linux_android,
897
                target_os = "freebsd"
898
            ))]
899
0
            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
0
            F_GETPIPE_SZ => libc::fcntl(fd, libc::F_GETPIPE_SZ),
906
            #[cfg(linux_android)]
907
0
            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
0
    Errno::result(res)
979
0
}
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]
985
pub 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.")]
1004
0
pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> {
1005
    use self::FlockArg::*;
1006
1007
0
    let res = unsafe {
1008
0
        match arg {
1009
0
            LockShared => libc::flock(fd, libc::LOCK_SH),
1010
0
            LockExclusive => libc::flock(fd, libc::LOCK_EX),
1011
0
            Unlock => libc::flock(fd, libc::LOCK_UN),
1012
            LockSharedNonblock => {
1013
0
                libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB)
1014
            }
1015
            LockExclusiveNonblock => {
1016
0
                libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB)
1017
            }
1018
            #[allow(deprecated)]
1019
0
            UnlockNonblock => libc::flock(fd, libc::LOCK_UN | libc::LOCK_NB),
1020
        }
1021
    };
1022
1023
0
    Errno::result(res).map(drop)
1024
0
}
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")))]
1031
pub 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)]
1038
pub struct Flock<T: Flockable>(T);
1039
1040
#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
1041
impl<T: Flockable> Drop for Flock<T> {
1042
0
    fn drop(&mut self) {
1043
0
        let res = Errno::result(unsafe { libc::flock(self.0.as_raw_fd(), libc::LOCK_UN) });
1044
0
        if res.is_err() && !std::thread::panicking() {
1045
0
            panic!("Failed to remove flock: {}", res.unwrap_err());
1046
0
        }
1047
0
    }
1048
}
1049
1050
#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
1051
impl<T: Flockable> Deref for Flock<T> {
1052
    type Target = T;
1053
1054
0
    fn deref(&self) -> &Self::Target {
1055
0
        &self.0
1056
0
    }
1057
}
1058
#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
1059
impl<T: Flockable> DerefMut for Flock<T> {
1060
0
    fn deref_mut(&mut self) -> &mut Self::Target {
1061
0
        &mut self.0
1062
0
    }
1063
}
1064
1065
#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
1066
impl<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
0
    pub fn lock(t: T, args: FlockArg) -> std::result::Result<Self, (T, Errno)> {
1086
0
        let flags = match args {
1087
0
            FlockArg::LockShared => libc::LOCK_SH,
1088
0
            FlockArg::LockExclusive => libc::LOCK_EX,
1089
0
            FlockArg::LockSharedNonblock => libc::LOCK_SH | libc::LOCK_NB,
1090
0
            FlockArg::LockExclusiveNonblock => libc::LOCK_EX | libc::LOCK_NB,
1091
            #[allow(deprecated)]
1092
0
            FlockArg::Unlock | FlockArg::UnlockNonblock => return Err((t, Errno::EINVAL)),
1093
        };
1094
0
        match Errno::result(unsafe { libc::flock(t.as_raw_fd(), flags) }) {
1095
0
            Ok(_) => Ok(Self(t)),
1096
0
            Err(errno) => Err((t, errno)),
1097
        }
1098
0
    }
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
0
    pub fn unlock(self) -> std::result::Result<T, (Self, Errno)> {
1125
0
        let inner = unsafe { match Errno::result(libc::flock(self.0.as_raw_fd(), libc::LOCK_UN)) {
1126
0
            Ok(_) => std::ptr::read(&self.0),
1127
0
            Err(errno) => return Err((self, errno)),
1128
        }};
1129
1130
0
        std::mem::forget(self);
1131
0
        Ok(inner)
1132
0
    }
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
0
    pub fn relock(&self, arg: FlockArg) -> Result<()> {
1147
0
         let flags = match arg {
1148
0
            FlockArg::LockShared => libc::LOCK_SH,
1149
0
            FlockArg::LockExclusive => libc::LOCK_EX,
1150
0
            FlockArg::LockSharedNonblock => libc::LOCK_SH | libc::LOCK_NB,
1151
0
            FlockArg::LockExclusiveNonblock => libc::LOCK_EX | libc::LOCK_NB,
1152
            #[allow(deprecated)]
1153
0
            FlockArg::Unlock | FlockArg::UnlockNonblock => return Err(Errno::EINVAL),
1154
        };
1155
0
        Errno::result(unsafe { libc::flock(self.as_raw_fd(), flags) }).map(drop)
1156
0
    }
1157
}
1158
1159
// Safety: `File` is not [std::clone::Clone].
1160
#[cfg(not(any(target_os = "redox", target_os = "solaris")))]
1161
unsafe 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")))]
1165
unsafe impl Flockable for OwnedFd {}
1166
}
1167
1168
#[cfg(linux_android)]
1169
#[cfg(feature = "zerocopy")]
1170
libc_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
1191
feature! {
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"))]
1215
pub 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)]
1267
pub 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)]
1295
pub 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)]
1312
pub 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")]
1333
libc_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
1364
feature! {
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")]
1373
0
pub fn fallocate<Fd: std::os::fd::AsFd>(
1374
0
    fd: Fd,
1375
0
    mode: FallocateFlags,
1376
0
    offset: libc::off_t,
1377
0
    len: libc::off_t,
1378
0
) -> Result<()> {
1379
    use std::os::fd::AsRawFd;
1380
1381
0
    let res = unsafe { libc::fallocate(fd.as_fd().as_raw_fd(), mode.bits(), offset, len) };
1382
0
    Errno::result(res).map(drop)
1383
0
}
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)]
1389
pub struct SpacectlRange(pub libc::off_t, pub libc::off_t);
1390
1391
#[cfg(any(target_os = "freebsd"))]
1392
impl 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
1456
pub 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
1507
pub 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
))]
1541
mod 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
0
    pub fn posix_fadvise<Fd: std::os::fd::AsFd>(
1576
0
        fd: Fd,
1577
0
        offset: libc::off_t,
1578
0
        len: libc::off_t,
1579
0
        advice: PosixFadviseAdvice,
1580
0
    ) -> Result<()> {
1581
        use std::os::fd::AsRawFd;
1582
1583
0
        let res = unsafe { libc::posix_fadvise(fd.as_fd().as_raw_fd(), offset, len, advice as libc::c_int) };
1584
1585
0
        if res == 0 {
1586
0
            Ok(())
1587
        } else {
1588
0
            Err(Errno::from_raw(res))
1589
        }
1590
0
    }
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
))]
1605
0
pub fn posix_fallocate<Fd: std::os::fd::AsFd>(
1606
0
    fd: Fd,
1607
0
    offset: libc::off_t,
1608
0
    len: libc::off_t,
1609
0
) -> Result<()> {
1610
    use std::os::fd::AsRawFd;
1611
1612
0
    let res = unsafe { libc::posix_fallocate(fd.as_fd().as_raw_fd(), offset, len) };
1613
0
    match Errno::result(res) {
1614
0
        Err(err) => Err(err),
1615
0
        Ok(0) => Ok(()),
1616
0
        Ok(errno) => Err(Errno::from_raw(errno)),
1617
    }
1618
0
}
1619
}