Coverage Report

Created: 2026-02-14 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/nix-0.26.4/src/fcntl.rs
Line
Count
Source
1
use crate::errno::Errno;
2
use libc::{self, c_char, c_int, c_uint, size_t, ssize_t};
3
use std::ffi::OsString;
4
#[cfg(not(target_os = "redox"))]
5
use std::os::raw;
6
use std::os::unix::ffi::OsStringExt;
7
use std::os::unix::io::RawFd;
8
9
#[cfg(feature = "fs")]
10
use crate::{sys::stat::Mode, NixPath, Result};
11
#[cfg(any(target_os = "android", target_os = "linux"))]
12
use std::ptr; // For splice and copy_file_range
13
14
#[cfg(any(
15
    target_os = "linux",
16
    target_os = "android",
17
    target_os = "emscripten",
18
    target_os = "fuchsia",
19
    target_os = "wasi",
20
    target_env = "uclibc",
21
    target_os = "freebsd"
22
))]
23
#[cfg(feature = "fs")]
24
pub use self::posix_fadvise::{posix_fadvise, PosixFadviseAdvice};
25
26
#[cfg(not(target_os = "redox"))]
27
#[cfg(any(feature = "fs", feature = "process"))]
28
libc_bitflags! {
29
    #[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "process"))))]
30
    pub struct AtFlags: c_int {
31
        AT_REMOVEDIR;
32
        AT_SYMLINK_FOLLOW;
33
        AT_SYMLINK_NOFOLLOW;
34
        #[cfg(any(target_os = "android", target_os = "linux"))]
35
        AT_NO_AUTOMOUNT;
36
        #[cfg(any(target_os = "android", target_os = "linux"))]
37
        AT_EMPTY_PATH;
38
        #[cfg(any(target_os = "illumos", target_os = "solaris"))]
39
        AT_EACCESS;
40
    }
41
}
42
43
#[cfg(any(feature = "fs", feature = "term"))]
44
libc_bitflags!(
45
    /// Configuration options for opened files.
46
    #[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "term"))))]
47
    pub struct OFlag: c_int {
48
        /// Mask for the access mode of the file.
49
        O_ACCMODE;
50
        /// Use alternate I/O semantics.
51
        #[cfg(target_os = "netbsd")]
52
        #[cfg_attr(docsrs, doc(cfg(all())))]
53
        O_ALT_IO;
54
        /// Open the file in append-only mode.
55
        O_APPEND;
56
        /// Generate a signal when input or output becomes possible.
57
        #[cfg(not(any(target_os = "illumos", target_os = "solaris", target_os = "haiku")))]
58
        #[cfg_attr(docsrs, doc(cfg(all())))]
59
        O_ASYNC;
60
        /// Closes the file descriptor once an `execve` call is made.
61
        ///
62
        /// Also sets the file offset to the beginning of the file.
63
        O_CLOEXEC;
64
        /// Create the file if it does not exist.
65
        O_CREAT;
66
        /// Try to minimize cache effects of the I/O for this file.
67
        #[cfg(any(target_os = "android",
68
                  target_os = "dragonfly",
69
                  target_os = "freebsd",
70
                  target_os = "linux",
71
                  target_os = "netbsd"))]
72
        #[cfg_attr(docsrs, doc(cfg(all())))]
73
        O_DIRECT;
74
        /// If the specified path isn't a directory, fail.
75
        #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
76
        #[cfg_attr(docsrs, doc(cfg(all())))]
77
        O_DIRECTORY;
78
        /// Implicitly follow each `write()` with an `fdatasync()`.
79
        #[cfg(any(target_os = "android",
80
                  target_os = "ios",
81
                  target_os = "linux",
82
                  target_os = "macos",
83
                  target_os = "netbsd",
84
                  target_os = "openbsd"))]
85
        #[cfg_attr(docsrs, doc(cfg(all())))]
86
        O_DSYNC;
87
        /// Error out if a file was not created.
88
        O_EXCL;
89
        /// Open for execute only.
90
        #[cfg(target_os = "freebsd")]
91
        #[cfg_attr(docsrs, doc(cfg(all())))]
92
        O_EXEC;
93
        /// Open with an exclusive file lock.
94
        #[cfg(any(target_os = "dragonfly",
95
                  target_os = "freebsd",
96
                  target_os = "ios",
97
                  target_os = "macos",
98
                  target_os = "netbsd",
99
                  target_os = "openbsd",
100
                  target_os = "redox"))]
101
        #[cfg_attr(docsrs, doc(cfg(all())))]
102
        O_EXLOCK;
103
        /// Same as `O_SYNC`.
104
        #[cfg(any(target_os = "dragonfly",
105
                  target_os = "freebsd",
106
                  target_os = "ios",
107
                  all(target_os = "linux", not(target_env = "musl")),
108
                  target_os = "macos",
109
                  target_os = "netbsd",
110
                  target_os = "openbsd",
111
                  target_os = "redox"))]
112
        #[cfg_attr(docsrs, doc(cfg(all())))]
113
        O_FSYNC;
114
        /// Allow files whose sizes can't be represented in an `off_t` to be opened.
115
        #[cfg(any(target_os = "android", target_os = "linux"))]
116
        #[cfg_attr(docsrs, doc(cfg(all())))]
117
        O_LARGEFILE;
118
        /// Do not update the file last access time during `read(2)`s.
119
        #[cfg(any(target_os = "android", target_os = "linux"))]
120
        #[cfg_attr(docsrs, doc(cfg(all())))]
121
        O_NOATIME;
122
        /// Don't attach the device as the process' controlling terminal.
123
        #[cfg(not(target_os = "redox"))]
124
        #[cfg_attr(docsrs, doc(cfg(all())))]
125
        O_NOCTTY;
126
        /// Same as `O_NONBLOCK`.
127
        #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
128
        #[cfg_attr(docsrs, doc(cfg(all())))]
129
        O_NDELAY;
130
        /// `open()` will fail if the given path is a symbolic link.
131
        O_NOFOLLOW;
132
        /// When possible, open the file in nonblocking mode.
133
        O_NONBLOCK;
134
        /// Don't deliver `SIGPIPE`.
135
        #[cfg(target_os = "netbsd")]
136
        #[cfg_attr(docsrs, doc(cfg(all())))]
137
        O_NOSIGPIPE;
138
        /// Obtain a file descriptor for low-level access.
139
        ///
140
        /// The file itself is not opened and other file operations will fail.
141
        #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
142
        #[cfg_attr(docsrs, doc(cfg(all())))]
143
        O_PATH;
144
        /// Only allow reading.
145
        ///
146
        /// This should not be combined with `O_WRONLY` or `O_RDWR`.
147
        O_RDONLY;
148
        /// Allow both reading and writing.
149
        ///
150
        /// This should not be combined with `O_WRONLY` or `O_RDONLY`.
151
        O_RDWR;
152
        /// Similar to `O_DSYNC` but applies to `read`s instead.
153
        #[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "openbsd"))]
154
        #[cfg_attr(docsrs, doc(cfg(all())))]
155
        O_RSYNC;
156
        /// Skip search permission checks.
157
        #[cfg(target_os = "netbsd")]
158
        #[cfg_attr(docsrs, doc(cfg(all())))]
159
        O_SEARCH;
160
        /// Open with a shared file lock.
161
        #[cfg(any(target_os = "dragonfly",
162
                  target_os = "freebsd",
163
                  target_os = "ios",
164
                  target_os = "macos",
165
                  target_os = "netbsd",
166
                  target_os = "openbsd",
167
                  target_os = "redox"))]
168
        #[cfg_attr(docsrs, doc(cfg(all())))]
169
        O_SHLOCK;
170
        /// Implicitly follow each `write()` with an `fsync()`.
171
        #[cfg(not(target_os = "redox"))]
172
        #[cfg_attr(docsrs, doc(cfg(all())))]
173
        O_SYNC;
174
        /// Create an unnamed temporary file.
175
        #[cfg(any(target_os = "android", target_os = "linux"))]
176
        #[cfg_attr(docsrs, doc(cfg(all())))]
177
        O_TMPFILE;
178
        /// Truncate an existing regular file to 0 length if it allows writing.
179
        O_TRUNC;
180
        /// Restore default TTY attributes.
181
        #[cfg(target_os = "freebsd")]
182
        #[cfg_attr(docsrs, doc(cfg(all())))]
183
        O_TTY_INIT;
184
        /// Only allow writing.
185
        ///
186
        /// This should not be combined with `O_RDONLY` or `O_RDWR`.
187
        O_WRONLY;
188
    }
189
);
190
191
feature! {
192
#![feature = "fs"]
193
194
// The conversion is not identical on all operating systems.
195
#[allow(clippy::useless_conversion)]
196
0
pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
197
0
    let fd = path.with_nix_path(|cstr| {
198
0
        unsafe { libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
199
0
    })?;
200
201
0
    Errno::result(fd)
202
0
}
203
204
// The conversion is not identical on all operating systems.
205
#[allow(clippy::useless_conversion)]
206
#[cfg(not(target_os = "redox"))]
207
0
pub fn openat<P: ?Sized + NixPath>(
208
0
    dirfd: RawFd,
209
0
    path: &P,
210
0
    oflag: OFlag,
211
0
    mode: Mode,
212
0
) -> Result<RawFd> {
213
0
    let fd = path.with_nix_path(|cstr| {
214
0
        unsafe { libc::openat(dirfd, cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
215
0
    })?;
216
0
    Errno::result(fd)
217
0
}
218
219
#[cfg(not(target_os = "redox"))]
220
0
pub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
221
0
    old_dirfd: Option<RawFd>,
222
0
    old_path: &P1,
223
0
    new_dirfd: Option<RawFd>,
224
0
    new_path: &P2,
225
0
) -> Result<()> {
226
0
    let res = old_path.with_nix_path(|old_cstr| {
227
0
        new_path.with_nix_path(|new_cstr| unsafe {
228
0
            libc::renameat(
229
0
                at_rawfd(old_dirfd),
230
0
                old_cstr.as_ptr(),
231
0
                at_rawfd(new_dirfd),
232
0
                new_cstr.as_ptr(),
233
            )
234
0
        })
235
0
    })??;
236
0
    Errno::result(res).map(drop)
237
0
}
238
}
239
240
#[cfg(all(target_os = "linux", target_env = "gnu",))]
241
#[cfg(feature = "fs")]
242
libc_bitflags! {
243
    #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
244
    pub struct RenameFlags: u32 {
245
        RENAME_EXCHANGE;
246
        RENAME_NOREPLACE;
247
        RENAME_WHITEOUT;
248
    }
249
}
250
251
feature! {
252
#![feature = "fs"]
253
#[cfg(all(
254
    target_os = "linux",
255
    target_env = "gnu",
256
))]
257
0
pub fn renameat2<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
258
0
    old_dirfd: Option<RawFd>,
259
0
    old_path: &P1,
260
0
    new_dirfd: Option<RawFd>,
261
0
    new_path: &P2,
262
0
    flags: RenameFlags,
263
0
) -> Result<()> {
264
0
    let res = old_path.with_nix_path(|old_cstr| {
265
0
        new_path.with_nix_path(|new_cstr| unsafe {
266
0
            libc::renameat2(
267
0
                at_rawfd(old_dirfd),
268
0
                old_cstr.as_ptr(),
269
0
                at_rawfd(new_dirfd),
270
0
                new_cstr.as_ptr(),
271
0
                flags.bits(),
272
            )
273
0
        })
274
0
    })??;
275
0
    Errno::result(res).map(drop)
276
0
}
277
278
0
fn wrap_readlink_result(mut v: Vec<u8>, len: ssize_t) -> Result<OsString> {
279
0
    unsafe { v.set_len(len as usize) }
280
0
    v.shrink_to_fit();
281
0
    Ok(OsString::from_vec(v.to_vec()))
282
0
}
283
284
0
fn readlink_maybe_at<P: ?Sized + NixPath>(
285
0
    dirfd: Option<RawFd>,
286
0
    path: &P,
287
0
    v: &mut Vec<u8>,
288
0
) -> Result<libc::ssize_t> {
289
0
    path.with_nix_path(|cstr| unsafe {
290
0
        match dirfd {
291
            #[cfg(target_os = "redox")]
292
            Some(_) => unreachable!(),
293
            #[cfg(not(target_os = "redox"))]
294
0
            Some(dirfd) => libc::readlinkat(
295
0
                dirfd,
296
0
                cstr.as_ptr(),
297
0
                v.as_mut_ptr() as *mut c_char,
298
0
                v.capacity() as size_t,
299
            ),
300
0
            None => libc::readlink(
301
0
                cstr.as_ptr(),
302
0
                v.as_mut_ptr() as *mut c_char,
303
0
                v.capacity() as size_t,
304
            ),
305
        }
306
0
    })
307
0
}
308
309
0
fn inner_readlink<P: ?Sized + NixPath>(dirfd: Option<RawFd>, path: &P) -> Result<OsString> {
310
0
    let mut v = Vec::with_capacity(libc::PATH_MAX as usize);
311
    // simple case: result is strictly less than `PATH_MAX`
312
0
    let res = readlink_maybe_at(dirfd, path, &mut v)?;
313
0
    let len = Errno::result(res)?;
314
0
    debug_assert!(len >= 0);
315
0
    if (len as usize) < v.capacity() {
316
0
        return wrap_readlink_result(v, res);
317
0
    }
318
    // Uh oh, the result is too long...
319
    // Let's try to ask lstat how many bytes to allocate.
320
0
    let reported_size = match dirfd {
321
        #[cfg(target_os = "redox")]
322
        Some(_) => unreachable!(),
323
        #[cfg(any(target_os = "android", target_os = "linux"))]
324
0
        Some(dirfd) => {
325
0
            let flags = if path.is_empty() { AtFlags::AT_EMPTY_PATH } else { AtFlags::empty() };
326
0
            super::sys::stat::fstatat(dirfd, path, flags | AtFlags::AT_SYMLINK_NOFOLLOW)
327
        },
328
        #[cfg(not(any(target_os = "android", target_os = "linux", target_os = "redox")))]
329
        Some(dirfd) => super::sys::stat::fstatat(dirfd, path, AtFlags::AT_SYMLINK_NOFOLLOW),
330
0
        None => super::sys::stat::lstat(path)
331
    }
332
0
        .map(|x| x.st_size)
333
0
        .unwrap_or(0);
334
0
    let mut try_size = if reported_size > 0 {
335
        // Note: even if `lstat`'s apparently valid answer turns out to be
336
        // wrong, we will still read the full symlink no matter what.
337
0
        reported_size as usize + 1
338
    } else {
339
        // If lstat doesn't cooperate, or reports an error, be a little less
340
        // precise.
341
0
        (libc::PATH_MAX as usize).max(128) << 1
342
    };
343
    loop {
344
0
        v.reserve_exact(try_size);
345
0
        let res = readlink_maybe_at(dirfd, path, &mut v)?;
346
0
        let len = Errno::result(res)?;
347
0
        debug_assert!(len >= 0);
348
0
        if (len as usize) < v.capacity() {
349
0
            break wrap_readlink_result(v, res);
350
        } else {
351
            // Ugh! Still not big enough!
352
0
            match try_size.checked_shl(1) {
353
0
                Some(next_size) => try_size = next_size,
354
                // It's absurd that this would happen, but handle it sanely
355
                // anyway.
356
0
                None => break Err(Errno::ENAMETOOLONG),
357
            }
358
        }
359
    }
360
0
}
361
362
0
pub fn readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString> {
363
0
    inner_readlink(None, path)
364
0
}
365
366
#[cfg(not(target_os = "redox"))]
367
0
pub fn readlinkat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P) -> Result<OsString> {
368
0
    inner_readlink(Some(dirfd), path)
369
0
}
370
371
/// Computes the raw fd consumed by a function of the form `*at`.
372
#[cfg(not(target_os = "redox"))]
373
0
pub(crate) fn at_rawfd(fd: Option<RawFd>) -> raw::c_int {
374
0
    match fd {
375
0
        None => libc::AT_FDCWD,
376
0
        Some(fd) => fd,
377
    }
378
0
}
379
}
380
381
#[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
382
#[cfg(feature = "fs")]
383
libc_bitflags!(
384
    /// Additional flags for file sealing, which allows for limiting operations on a file.
385
    #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
386
    pub struct SealFlag: c_int {
387
        /// Prevents further calls to `fcntl()` with `F_ADD_SEALS`.
388
        F_SEAL_SEAL;
389
        /// The file cannot be reduced in size.
390
        F_SEAL_SHRINK;
391
        /// The size of the file cannot be increased.
392
        F_SEAL_GROW;
393
        /// The file contents cannot be modified.
394
        F_SEAL_WRITE;
395
    }
396
);
397
398
#[cfg(feature = "fs")]
399
libc_bitflags!(
400
    /// Additional configuration flags for `fcntl`'s `F_SETFD`.
401
    #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
402
    pub struct FdFlag: c_int {
403
        /// The file descriptor will automatically be closed during a successful `execve(2)`.
404
        FD_CLOEXEC;
405
    }
406
);
407
408
feature! {
409
#![feature = "fs"]
410
411
#[cfg(not(target_os = "redox"))]
412
#[derive(Debug, Eq, Hash, PartialEq)]
413
#[non_exhaustive]
414
pub enum FcntlArg<'a> {
415
    F_DUPFD(RawFd),
416
    F_DUPFD_CLOEXEC(RawFd),
417
    F_GETFD,
418
    F_SETFD(FdFlag), // FD_FLAGS
419
    F_GETFL,
420
    F_SETFL(OFlag), // O_NONBLOCK
421
    F_SETLK(&'a libc::flock),
422
    F_SETLKW(&'a libc::flock),
423
    F_GETLK(&'a mut libc::flock),
424
    #[cfg(any(target_os = "linux", target_os = "android"))]
425
    F_OFD_SETLK(&'a libc::flock),
426
    #[cfg(any(target_os = "linux", target_os = "android"))]
427
    F_OFD_SETLKW(&'a libc::flock),
428
    #[cfg(any(target_os = "linux", target_os = "android"))]
429
    F_OFD_GETLK(&'a mut libc::flock),
430
    #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
431
    F_ADD_SEALS(SealFlag),
432
    #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
433
    F_GET_SEALS,
434
    #[cfg(any(target_os = "macos", target_os = "ios"))]
435
    F_FULLFSYNC,
436
    #[cfg(any(target_os = "linux", target_os = "android"))]
437
    F_GETPIPE_SZ,
438
    #[cfg(any(target_os = "linux", target_os = "android"))]
439
    F_SETPIPE_SZ(c_int),
440
    // TODO: Rest of flags
441
}
442
443
#[cfg(target_os = "redox")]
444
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
445
#[non_exhaustive]
446
pub enum FcntlArg {
447
    F_DUPFD(RawFd),
448
    F_DUPFD_CLOEXEC(RawFd),
449
    F_GETFD,
450
    F_SETFD(FdFlag), // FD_FLAGS
451
    F_GETFL,
452
    F_SETFL(OFlag), // O_NONBLOCK
453
}
454
pub use self::FcntlArg::*;
455
456
// TODO: Figure out how to handle value fcntl returns
457
0
pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> {
458
0
    let res = unsafe {
459
0
        match arg {
460
0
            F_DUPFD(rawfd) => libc::fcntl(fd, libc::F_DUPFD, rawfd),
461
0
            F_DUPFD_CLOEXEC(rawfd) => libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, rawfd),
462
0
            F_GETFD => libc::fcntl(fd, libc::F_GETFD),
463
0
            F_SETFD(flag) => libc::fcntl(fd, libc::F_SETFD, flag.bits()),
464
0
            F_GETFL => libc::fcntl(fd, libc::F_GETFL),
465
0
            F_SETFL(flag) => libc::fcntl(fd, libc::F_SETFL, flag.bits()),
466
            #[cfg(not(target_os = "redox"))]
467
0
            F_SETLK(flock) => libc::fcntl(fd, libc::F_SETLK, flock),
468
            #[cfg(not(target_os = "redox"))]
469
0
            F_SETLKW(flock) => libc::fcntl(fd, libc::F_SETLKW, flock),
470
            #[cfg(not(target_os = "redox"))]
471
0
            F_GETLK(flock) => libc::fcntl(fd, libc::F_GETLK, flock),
472
            #[cfg(any(target_os = "android", target_os = "linux"))]
473
0
            F_OFD_SETLK(flock) => libc::fcntl(fd, libc::F_OFD_SETLK, flock),
474
            #[cfg(any(target_os = "android", target_os = "linux"))]
475
0
            F_OFD_SETLKW(flock) => libc::fcntl(fd, libc::F_OFD_SETLKW, flock),
476
            #[cfg(any(target_os = "android", target_os = "linux"))]
477
0
            F_OFD_GETLK(flock) => libc::fcntl(fd, libc::F_OFD_GETLK, flock),
478
            #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
479
0
            F_ADD_SEALS(flag) => libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits()),
480
            #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
481
0
            F_GET_SEALS => libc::fcntl(fd, libc::F_GET_SEALS),
482
            #[cfg(any(target_os = "macos", target_os = "ios"))]
483
            F_FULLFSYNC => libc::fcntl(fd, libc::F_FULLFSYNC),
484
            #[cfg(any(target_os = "linux", target_os = "android"))]
485
0
            F_GETPIPE_SZ => libc::fcntl(fd, libc::F_GETPIPE_SZ),
486
            #[cfg(any(target_os = "linux", target_os = "android"))]
487
0
            F_SETPIPE_SZ(size) => libc::fcntl(fd, libc::F_SETPIPE_SZ, size),
488
        }
489
    };
490
491
0
    Errno::result(res)
492
0
}
493
494
// TODO: convert to libc_enum
495
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
496
#[non_exhaustive]
497
pub enum FlockArg {
498
    LockShared,
499
    LockExclusive,
500
    Unlock,
501
    LockSharedNonblock,
502
    LockExclusiveNonblock,
503
    UnlockNonblock,
504
}
505
506
#[cfg(not(target_os = "redox"))]
507
0
pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> {
508
    use self::FlockArg::*;
509
510
0
    let res = unsafe {
511
0
        match arg {
512
0
            LockShared => libc::flock(fd, libc::LOCK_SH),
513
0
            LockExclusive => libc::flock(fd, libc::LOCK_EX),
514
0
            Unlock => libc::flock(fd, libc::LOCK_UN),
515
0
            LockSharedNonblock => libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB),
516
0
            LockExclusiveNonblock => libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB),
517
0
            UnlockNonblock => libc::flock(fd, libc::LOCK_UN | libc::LOCK_NB),
518
        }
519
    };
520
521
0
    Errno::result(res).map(drop)
522
0
}
523
}
524
525
#[cfg(any(target_os = "android", target_os = "linux"))]
526
#[cfg(feature = "zerocopy")]
527
libc_bitflags! {
528
    /// Additional flags to `splice` and friends.
529
    #[cfg_attr(docsrs, doc(cfg(feature = "zerocopy")))]
530
    pub struct SpliceFFlags: c_uint {
531
        /// Request that pages be moved instead of copied.
532
        ///
533
        /// Not applicable to `vmsplice`.
534
        SPLICE_F_MOVE;
535
        /// Do not block on I/O.
536
        SPLICE_F_NONBLOCK;
537
        /// Hint that more data will be coming in a subsequent splice.
538
        ///
539
        /// Not applicable to `vmsplice`.
540
        SPLICE_F_MORE;
541
        /// Gift the user pages to the kernel.
542
        ///
543
        /// Not applicable to `splice`.
544
        SPLICE_F_GIFT;
545
    }
546
}
547
548
feature! {
549
#![feature = "zerocopy"]
550
551
/// Copy a range of data from one file to another
552
///
553
/// The `copy_file_range` system call performs an in-kernel copy between
554
/// file descriptors `fd_in` and `fd_out` without the additional cost of
555
/// transferring data from the kernel to user space and then back into the
556
/// kernel. It copies up to `len` bytes of data from file descriptor `fd_in` to
557
/// file descriptor `fd_out`, overwriting any data that exists within the
558
/// requested range of the target file.
559
///
560
/// If the `off_in` and/or `off_out` arguments are used, the values
561
/// will be mutated to reflect the new position within the file after
562
/// copying. If they are not used, the relevant filedescriptors will be seeked
563
/// to the new position.
564
///
565
/// On successful completion the number of bytes actually copied will be
566
/// returned.
567
#[cfg(any(target_os = "android", target_os = "linux"))]
568
pub fn copy_file_range(
569
    fd_in: RawFd,
570
    off_in: Option<&mut libc::loff_t>,
571
    fd_out: RawFd,
572
    off_out: Option<&mut libc::loff_t>,
573
    len: usize,
574
) -> Result<usize> {
575
    let off_in = off_in
576
        .map(|offset| offset as *mut libc::loff_t)
577
        .unwrap_or(ptr::null_mut());
578
    let off_out = off_out
579
        .map(|offset| offset as *mut libc::loff_t)
580
        .unwrap_or(ptr::null_mut());
581
582
    let ret = unsafe {
583
        libc::syscall(
584
            libc::SYS_copy_file_range,
585
            fd_in,
586
            off_in,
587
            fd_out,
588
            off_out,
589
            len,
590
            0,
591
        )
592
    };
593
    Errno::result(ret).map(|r| r as usize)
594
}
595
596
#[cfg(any(target_os = "linux", target_os = "android"))]
597
pub fn splice(
598
    fd_in: RawFd,
599
    off_in: Option<&mut libc::loff_t>,
600
    fd_out: RawFd,
601
    off_out: Option<&mut libc::loff_t>,
602
    len: usize,
603
    flags: SpliceFFlags,
604
) -> Result<usize> {
605
    let off_in = off_in
606
        .map(|offset| offset as *mut libc::loff_t)
607
        .unwrap_or(ptr::null_mut());
608
    let off_out = off_out
609
        .map(|offset| offset as *mut libc::loff_t)
610
        .unwrap_or(ptr::null_mut());
611
612
    let ret = unsafe { libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits()) };
613
    Errno::result(ret).map(|r| r as usize)
614
}
615
616
#[cfg(any(target_os = "linux", target_os = "android"))]
617
pub fn tee(fd_in: RawFd, fd_out: RawFd, len: usize, flags: SpliceFFlags) -> Result<usize> {
618
    let ret = unsafe { libc::tee(fd_in, fd_out, len, flags.bits()) };
619
    Errno::result(ret).map(|r| r as usize)
620
}
621
622
#[cfg(any(target_os = "linux", target_os = "android"))]
623
pub fn vmsplice(
624
    fd: RawFd,
625
    iov: &[std::io::IoSlice<'_>],
626
    flags: SpliceFFlags
627
    ) -> Result<usize>
628
{
629
    let ret = unsafe {
630
        libc::vmsplice(
631
            fd,
632
            iov.as_ptr() as *const libc::iovec,
633
            iov.len(),
634
            flags.bits(),
635
        )
636
    };
637
    Errno::result(ret).map(|r| r as usize)
638
}
639
}
640
641
#[cfg(target_os = "linux")]
642
#[cfg(feature = "fs")]
643
libc_bitflags!(
644
    /// Mode argument flags for fallocate determining operation performed on a given range.
645
    #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
646
    pub struct FallocateFlags: c_int {
647
        /// File size is not changed.
648
        ///
649
        /// offset + len can be greater than file size.
650
        FALLOC_FL_KEEP_SIZE;
651
        /// Deallocates space by creating a hole.
652
        ///
653
        /// Must be ORed with FALLOC_FL_KEEP_SIZE. Byte range starts at offset and continues for len bytes.
654
        FALLOC_FL_PUNCH_HOLE;
655
        /// Removes byte range from a file without leaving a hole.
656
        ///
657
        /// Byte range to collapse starts at offset and continues for len bytes.
658
        FALLOC_FL_COLLAPSE_RANGE;
659
        /// Zeroes space in specified byte range.
660
        ///
661
        /// Byte range starts at offset and continues for len bytes.
662
        FALLOC_FL_ZERO_RANGE;
663
        /// Increases file space by inserting a hole within the file size.
664
        ///
665
        /// Does not overwrite existing data. Hole starts at offset and continues for len bytes.
666
        FALLOC_FL_INSERT_RANGE;
667
        /// Shared file data extants are made private to the file.
668
        ///
669
        /// Gaurantees that a subsequent write will not fail due to lack of space.
670
        FALLOC_FL_UNSHARE_RANGE;
671
    }
672
);
673
674
feature! {
675
#![feature = "fs"]
676
677
/// Manipulates file space.
678
///
679
/// Allows the caller to directly manipulate the allocated disk space for the
680
/// file referred to by fd.
681
#[cfg(target_os = "linux")]
682
#[cfg(feature = "fs")]
683
0
pub fn fallocate(
684
0
    fd: RawFd,
685
0
    mode: FallocateFlags,
686
0
    offset: libc::off_t,
687
0
    len: libc::off_t,
688
0
) -> Result<()> {
689
0
    let res = unsafe { libc::fallocate(fd, mode.bits(), offset, len) };
690
0
    Errno::result(res).map(drop)
691
0
}
692
693
/// Argument to [`fspacectl`] describing the range to zero.  The first member is
694
/// the file offset, and the second is the length of the region.
695
#[cfg(any(target_os = "freebsd"))]
696
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
697
pub struct SpacectlRange(pub libc::off_t, pub libc::off_t);
698
699
#[cfg(any(target_os = "freebsd"))]
700
impl SpacectlRange {
701
    #[inline]
702
    pub fn is_empty(&self) -> bool {
703
        self.1 == 0
704
    }
705
706
    #[inline]
707
    pub fn len(&self) -> libc::off_t {
708
        self.1
709
    }
710
711
    #[inline]
712
    pub fn offset(&self) -> libc::off_t {
713
        self.0
714
    }
715
}
716
717
/// Punch holes in a file.
718
///
719
/// `fspacectl` instructs the file system to deallocate a portion of a file.
720
/// After a successful operation, this region of the file will return all zeroes
721
/// if read.  If the file system supports deallocation, then it may free the
722
/// underlying storage, too.
723
///
724
/// # Arguments
725
///
726
/// - `fd`      -   File to operate on
727
/// - `range.0` -   File offset at which to begin deallocation
728
/// - `range.1` -   Length of the region to deallocate
729
///
730
/// # Returns
731
///
732
/// The operation may deallocate less than the entire requested region.  On
733
/// success, it returns the region that still remains to be deallocated.  The
734
/// caller should loop until the returned region is empty.
735
///
736
/// # Example
737
///
738
#[cfg_attr(fbsd14, doc = " ```")]
739
#[cfg_attr(not(fbsd14), doc = " ```no_run")]
740
/// # use std::io::Write;
741
/// # use std::os::unix::fs::FileExt;
742
/// # use std::os::unix::io::AsRawFd;
743
/// # use nix::fcntl::*;
744
/// # use tempfile::tempfile;
745
/// const INITIAL: &[u8] = b"0123456789abcdef";
746
/// let mut f = tempfile().unwrap();
747
/// f.write_all(INITIAL).unwrap();
748
/// let mut range = SpacectlRange(3, 6);
749
/// while (!range.is_empty()) {
750
///     range = fspacectl(f.as_raw_fd(), range).unwrap();
751
/// }
752
/// let mut buf = vec![0; INITIAL.len()];
753
/// f.read_exact_at(&mut buf, 0).unwrap();
754
/// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
755
/// ```
756
#[cfg(target_os = "freebsd")]
757
pub fn fspacectl(fd: RawFd, range: SpacectlRange) -> Result<SpacectlRange> {
758
    let mut rqsr = libc::spacectl_range{r_offset: range.0, r_len: range.1};
759
    let res = unsafe { libc::fspacectl(
760
            fd,
761
            libc::SPACECTL_DEALLOC, // Only one command is supported ATM
762
            &rqsr,
763
            0,                      // No flags are currently supported
764
            &mut rqsr
765
    )};
766
    Errno::result(res).map(|_| SpacectlRange(rqsr.r_offset, rqsr.r_len))
767
}
768
769
/// Like [`fspacectl`], but will never return incomplete.
770
///
771
/// # Arguments
772
///
773
/// - `fd`      -   File to operate on
774
/// - `offset`  -   File offset at which to begin deallocation
775
/// - `len`     -   Length of the region to deallocate
776
///
777
/// # Returns
778
///
779
/// Returns `()` on success.  On failure, the region may or may not be partially
780
/// deallocated.
781
///
782
/// # Example
783
///
784
#[cfg_attr(fbsd14, doc = " ```")]
785
#[cfg_attr(not(fbsd14), doc = " ```no_run")]
786
/// # use std::io::Write;
787
/// # use std::os::unix::fs::FileExt;
788
/// # use std::os::unix::io::AsRawFd;
789
/// # use nix::fcntl::*;
790
/// # use tempfile::tempfile;
791
/// const INITIAL: &[u8] = b"0123456789abcdef";
792
/// let mut f = tempfile().unwrap();
793
/// f.write_all(INITIAL).unwrap();
794
/// fspacectl_all(f.as_raw_fd(), 3, 6).unwrap();
795
/// let mut buf = vec![0; INITIAL.len()];
796
/// f.read_exact_at(&mut buf, 0).unwrap();
797
/// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
798
/// ```
799
#[cfg(target_os = "freebsd")]
800
pub fn fspacectl_all(fd: RawFd, offset: libc::off_t, len: libc::off_t)
801
    -> Result<()>
802
{
803
    let mut rqsr = libc::spacectl_range{r_offset: offset, r_len: len};
804
    while rqsr.r_len > 0 {
805
        let res = unsafe { libc::fspacectl(
806
                fd,
807
                libc::SPACECTL_DEALLOC, // Only one command is supported ATM
808
                &rqsr,
809
                0,                      // No flags are currently supported
810
                &mut rqsr
811
        )};
812
        Errno::result(res)?;
813
    }
814
    Ok(())
815
}
816
817
#[cfg(any(
818
    target_os = "linux",
819
    target_os = "android",
820
    target_os = "emscripten",
821
    target_os = "fuchsia",
822
    target_os = "wasi",
823
    target_env = "uclibc",
824
    target_os = "freebsd"
825
))]
826
mod posix_fadvise {
827
    use crate::errno::Errno;
828
    use std::os::unix::io::RawFd;
829
    use crate::Result;
830
831
    #[cfg(feature = "fs")]
832
    libc_enum! {
833
        #[repr(i32)]
834
        #[non_exhaustive]
835
        #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
836
        pub enum PosixFadviseAdvice {
837
            POSIX_FADV_NORMAL,
838
            POSIX_FADV_SEQUENTIAL,
839
            POSIX_FADV_RANDOM,
840
            POSIX_FADV_NOREUSE,
841
            POSIX_FADV_WILLNEED,
842
            POSIX_FADV_DONTNEED,
843
        }
844
    }
845
846
    feature! {
847
    #![feature = "fs"]
848
0
    pub fn posix_fadvise(
849
0
        fd: RawFd,
850
0
        offset: libc::off_t,
851
0
        len: libc::off_t,
852
0
        advice: PosixFadviseAdvice,
853
0
    ) -> Result<()> {
854
0
        let res = unsafe { libc::posix_fadvise(fd, offset, len, advice as libc::c_int) };
855
856
0
        if res == 0 {
857
0
            Ok(())
858
        } else {
859
0
            Err(Errno::from_i32(res))
860
        }
861
0
    }
862
    }
863
}
864
865
#[cfg(any(
866
    target_os = "linux",
867
    target_os = "android",
868
    target_os = "dragonfly",
869
    target_os = "emscripten",
870
    target_os = "fuchsia",
871
    target_os = "wasi",
872
    target_os = "freebsd"
873
))]
874
0
pub fn posix_fallocate(fd: RawFd, offset: libc::off_t, len: libc::off_t) -> Result<()> {
875
0
    let res = unsafe { libc::posix_fallocate(fd, offset, len) };
876
0
    match Errno::result(res) {
877
0
        Err(err) => Err(err),
878
0
        Ok(0) => Ok(()),
879
0
        Ok(errno) => Err(Errno::from_i32(errno)),
880
    }
881
0
}
882
}