Coverage Report

Created: 2025-09-04 06:37

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