Coverage Report

Created: 2025-06-24 06:17

/rust/registry/src/index.crates.io-6f17d22bba15001f/nix-0.28.0/src/sys/stat.rs
Line
Count
Source (jump to first uncovered line)
1
#[cfg(any(apple_targets, target_os = "openbsd"))]
2
pub use libc::c_uint;
3
#[cfg(any(target_os = "netbsd", freebsdlike))]
4
pub use libc::c_ulong;
5
pub use libc::stat as FileStat;
6
pub use libc::{dev_t, mode_t};
7
8
#[cfg(not(target_os = "redox"))]
9
use crate::fcntl::{at_rawfd, AtFlags};
10
use crate::sys::time::{TimeSpec, TimeVal};
11
use crate::{errno::Errno, NixPath, Result};
12
use std::mem;
13
use std::os::unix::io::RawFd;
14
15
libc_bitflags!(
16
    /// "File type" flags for `mknod` and related functions.
17
    pub struct SFlag: mode_t {
18
        S_IFIFO;
19
        S_IFCHR;
20
        S_IFDIR;
21
        S_IFBLK;
22
        S_IFREG;
23
        S_IFLNK;
24
        S_IFSOCK;
25
        S_IFMT;
26
    }
27
);
28
29
libc_bitflags! {
30
    /// "File mode / permissions" flags.
31
    pub struct Mode: mode_t {
32
        /// Read, write and execute for owner.
33
        S_IRWXU;
34
        /// Read for owner.
35
        S_IRUSR;
36
        /// Write for owner.
37
        S_IWUSR;
38
        /// Execute for owner.
39
        S_IXUSR;
40
        /// Read write and execute for group.
41
        S_IRWXG;
42
        /// Read for group.
43
        S_IRGRP;
44
        /// Write for group.
45
        S_IWGRP;
46
        /// Execute for group.
47
        S_IXGRP;
48
        /// Read, write and execute for other.
49
        S_IRWXO;
50
        /// Read for other.
51
        S_IROTH;
52
        /// Write for other.
53
        S_IWOTH;
54
        /// Execute for other.
55
        S_IXOTH;
56
        /// Set user id on execution.
57
        S_ISUID as mode_t;
58
        /// Set group id on execution.
59
        S_ISGID as mode_t;
60
        S_ISVTX as mode_t;
61
    }
62
}
63
64
#[cfg(any(apple_targets, target_os = "openbsd"))]
65
pub type type_of_file_flag = c_uint;
66
#[cfg(any(freebsdlike, target_os = "netbsd"))]
67
pub type type_of_file_flag = c_ulong;
68
69
#[cfg(bsd)]
70
libc_bitflags! {
71
    /// File flags.
72
    pub struct FileFlag: type_of_file_flag {
73
        /// The file may only be appended to.
74
        SF_APPEND;
75
        /// The file has been archived.
76
        SF_ARCHIVED;
77
        #[cfg(any(target_os = "dragonfly"))]
78
        SF_CACHE;
79
        /// The file may not be changed.
80
        SF_IMMUTABLE;
81
        /// Indicates a WAPBL journal file.
82
        #[cfg(any(target_os = "netbsd"))]
83
        SF_LOG;
84
        /// Do not retain history for file
85
        #[cfg(any(target_os = "dragonfly"))]
86
        SF_NOHISTORY;
87
        /// The file may not be renamed or deleted.
88
        #[cfg(freebsdlike)]
89
        SF_NOUNLINK;
90
        /// Mask of superuser changeable flags
91
        SF_SETTABLE;
92
        /// Snapshot is invalid.
93
        #[cfg(any(target_os = "netbsd"))]
94
        SF_SNAPINVAL;
95
        /// The file is a snapshot file.
96
        #[cfg(any(target_os = "netbsd", target_os = "freebsd"))]
97
        SF_SNAPSHOT;
98
        #[cfg(any(target_os = "dragonfly"))]
99
        SF_XLINK;
100
        /// The file may only be appended to.
101
        UF_APPEND;
102
        /// The file needs to be archived.
103
        #[cfg(any(target_os = "freebsd"))]
104
        UF_ARCHIVE;
105
        #[cfg(any(target_os = "dragonfly"))]
106
        UF_CACHE;
107
        /// File is compressed at the file system level.
108
        #[cfg(apple_targets)]
109
        UF_COMPRESSED;
110
        /// The file may be hidden from directory listings at the application's
111
        /// discretion.
112
        #[cfg(any(
113
            target_os = "freebsd",
114
            apple_targets,
115
        ))]
116
        UF_HIDDEN;
117
        /// The file may not be changed.
118
        UF_IMMUTABLE;
119
        /// Do not dump the file.
120
        UF_NODUMP;
121
        #[cfg(any(target_os = "dragonfly"))]
122
        UF_NOHISTORY;
123
        /// The file may not be renamed or deleted.
124
        #[cfg(freebsdlike)]
125
        UF_NOUNLINK;
126
        /// The file is offline, or has the Windows and CIFS
127
        /// `FILE_ATTRIBUTE_OFFLINE` attribute.
128
        #[cfg(any(target_os = "freebsd"))]
129
        UF_OFFLINE;
130
        /// The directory is opaque when viewed through a union stack.
131
        UF_OPAQUE;
132
        /// The file is read only, and may not be written or appended.
133
        #[cfg(any(target_os = "freebsd"))]
134
        UF_READONLY;
135
        /// The file contains a Windows reparse point.
136
        #[cfg(any(target_os = "freebsd"))]
137
        UF_REPARSE;
138
        /// Mask of owner changeable flags.
139
        UF_SETTABLE;
140
        /// The file has the Windows `FILE_ATTRIBUTE_SPARSE_FILE` attribute.
141
        #[cfg(any(target_os = "freebsd"))]
142
        UF_SPARSE;
143
        /// The file has the DOS, Windows and CIFS `FILE_ATTRIBUTE_SYSTEM`
144
        /// attribute.
145
        #[cfg(any(target_os = "freebsd"))]
146
        UF_SYSTEM;
147
        /// File renames and deletes are tracked.
148
        #[cfg(apple_targets)]
149
        UF_TRACKED;
150
        #[cfg(any(target_os = "dragonfly"))]
151
        UF_XLINK;
152
    }
153
}
154
155
/// Create a special or ordinary file, by pathname.
156
0
pub fn mknod<P: ?Sized + NixPath>(
157
0
    path: &P,
158
0
    kind: SFlag,
159
0
    perm: Mode,
160
0
    dev: dev_t,
161
0
) -> Result<()> {
162
0
    let res = path.with_nix_path(|cstr| unsafe {
163
0
        libc::mknod(cstr.as_ptr(), kind.bits() | perm.bits() as mode_t, dev)
164
0
    })?;
165
166
0
    Errno::result(res).map(drop)
167
0
}
168
169
/// Create a special or ordinary file, relative to a given directory.
170
#[cfg(not(any(apple_targets, target_os = "redox", target_os = "haiku")))]
171
0
pub fn mknodat<P: ?Sized + NixPath>(
172
0
    dirfd: Option<RawFd>,
173
0
    path: &P,
174
0
    kind: SFlag,
175
0
    perm: Mode,
176
0
    dev: dev_t,
177
0
) -> Result<()> {
178
0
    let dirfd = at_rawfd(dirfd);
179
0
    let res = path.with_nix_path(|cstr| unsafe {
180
0
        libc::mknodat(
181
0
            dirfd,
182
0
            cstr.as_ptr(),
183
0
            kind.bits() | perm.bits() as mode_t,
184
0
            dev,
185
0
        )
186
0
    })?;
187
188
0
    Errno::result(res).map(drop)
189
0
}
190
191
#[cfg(target_os = "linux")]
192
0
pub const fn major(dev: dev_t) -> u64 {
193
0
    ((dev >> 32) & 0xffff_f000) | ((dev >> 8) & 0x0000_0fff)
194
0
}
195
196
#[cfg(target_os = "linux")]
197
0
pub const fn minor(dev: dev_t) -> u64 {
198
0
    ((dev >> 12) & 0xffff_ff00) | ((dev) & 0x0000_00ff)
199
0
}
200
201
#[cfg(target_os = "linux")]
202
0
pub const fn makedev(major: u64, minor: u64) -> dev_t {
203
0
    ((major & 0xffff_f000) << 32)
204
0
        | ((major & 0x0000_0fff) << 8)
205
0
        | ((minor & 0xffff_ff00) << 12)
206
0
        | (minor & 0x0000_00ff)
207
0
}
208
209
0
pub fn umask(mode: Mode) -> Mode {
210
0
    let prev = unsafe { libc::umask(mode.bits() as mode_t) };
211
0
    Mode::from_bits(prev).expect("[BUG] umask returned invalid Mode")
212
0
}
213
214
0
pub fn stat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
215
0
    let mut dst = mem::MaybeUninit::uninit();
216
0
    let res = path.with_nix_path(|cstr| unsafe {
217
0
        libc::stat(cstr.as_ptr(), dst.as_mut_ptr())
218
0
    })?;
219
220
0
    Errno::result(res)?;
221
222
0
    Ok(unsafe { dst.assume_init() })
223
0
}
224
225
0
pub fn lstat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
226
0
    let mut dst = mem::MaybeUninit::uninit();
227
0
    let res = path.with_nix_path(|cstr| unsafe {
228
0
        libc::lstat(cstr.as_ptr(), dst.as_mut_ptr())
229
0
    })?;
230
231
0
    Errno::result(res)?;
232
233
0
    Ok(unsafe { dst.assume_init() })
234
0
}
235
236
0
pub fn fstat(fd: RawFd) -> Result<FileStat> {
237
0
    let mut dst = mem::MaybeUninit::uninit();
238
0
    let res = unsafe { libc::fstat(fd, dst.as_mut_ptr()) };
239
0
240
0
    Errno::result(res)?;
241
242
0
    Ok(unsafe { dst.assume_init() })
243
0
}
244
245
#[cfg(not(target_os = "redox"))]
246
0
pub fn fstatat<P: ?Sized + NixPath>(
247
0
    dirfd: Option<RawFd>,
248
0
    pathname: &P,
249
0
    f: AtFlags,
250
0
) -> Result<FileStat> {
251
0
    let dirfd = at_rawfd(dirfd);
252
0
    let mut dst = mem::MaybeUninit::uninit();
253
0
    let res = pathname.with_nix_path(|cstr| unsafe {
254
0
        libc::fstatat(
255
0
            dirfd,
256
0
            cstr.as_ptr(),
257
0
            dst.as_mut_ptr(),
258
0
            f.bits() as libc::c_int,
259
0
        )
260
0
    })?;
261
262
0
    Errno::result(res)?;
263
264
0
    Ok(unsafe { dst.assume_init() })
265
0
}
266
267
/// Change the file permission bits of the file specified by a file descriptor.
268
///
269
/// # References
270
///
271
/// [fchmod(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html).
272
0
pub fn fchmod(fd: RawFd, mode: Mode) -> Result<()> {
273
0
    let res = unsafe { libc::fchmod(fd, mode.bits() as mode_t) };
274
0
275
0
    Errno::result(res).map(drop)
276
0
}
277
278
/// Flags for `fchmodat` function.
279
#[derive(Clone, Copy, Debug)]
280
pub enum FchmodatFlags {
281
    FollowSymlink,
282
    NoFollowSymlink,
283
}
284
285
/// Change the file permission bits.
286
///
287
/// The file to be changed is determined relative to the directory associated
288
/// with the file descriptor `dirfd` or the current working directory
289
/// if `dirfd` is `None`.
290
///
291
/// If `flag` is `FchmodatFlags::NoFollowSymlink` and `path` names a symbolic link,
292
/// then the mode of the symbolic link is changed.
293
///
294
/// `fchmodat(None, path, mode, FchmodatFlags::FollowSymlink)` is identical to
295
/// a call `libc::chmod(path, mode)`. That's why `chmod` is unimplemented
296
/// in the `nix` crate.
297
///
298
/// # References
299
///
300
/// [fchmodat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html).
301
#[cfg(not(target_os = "redox"))]
302
0
pub fn fchmodat<P: ?Sized + NixPath>(
303
0
    dirfd: Option<RawFd>,
304
0
    path: &P,
305
0
    mode: Mode,
306
0
    flag: FchmodatFlags,
307
0
) -> Result<()> {
308
0
    let atflag = match flag {
309
0
        FchmodatFlags::FollowSymlink => AtFlags::empty(),
310
0
        FchmodatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
311
    };
312
0
    let res = path.with_nix_path(|cstr| unsafe {
313
0
        libc::fchmodat(
314
0
            at_rawfd(dirfd),
315
0
            cstr.as_ptr(),
316
0
            mode.bits() as mode_t,
317
0
            atflag.bits() as libc::c_int,
318
0
        )
319
0
    })?;
320
321
0
    Errno::result(res).map(drop)
322
0
}
323
324
/// Change the access and modification times of a file.
325
///
326
/// `utimes(path, times)` is identical to
327
/// `utimensat(None, path, times, UtimensatFlags::FollowSymlink)`. The former
328
/// is a deprecated API so prefer using the latter if the platforms you care
329
/// about support it.
330
///
331
/// # References
332
///
333
/// [utimes(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html).
334
0
pub fn utimes<P: ?Sized + NixPath>(
335
0
    path: &P,
336
0
    atime: &TimeVal,
337
0
    mtime: &TimeVal,
338
0
) -> Result<()> {
339
0
    let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()];
340
0
    let res = path.with_nix_path(|cstr| unsafe {
341
0
        libc::utimes(cstr.as_ptr(), &times[0])
342
0
    })?;
343
344
0
    Errno::result(res).map(drop)
345
0
}
346
347
/// Change the access and modification times of a file without following symlinks.
348
///
349
/// `lutimes(path, times)` is identical to
350
/// `utimensat(None, path, times, UtimensatFlags::NoFollowSymlink)`. The former
351
/// is a deprecated API so prefer using the latter if the platforms you care
352
/// about support it.
353
///
354
/// # References
355
///
356
/// [lutimes(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lutimes.html).
357
#[cfg(any(
358
    target_os = "linux",
359
    target_os = "haiku",
360
    apple_targets,
361
    target_os = "freebsd",
362
    target_os = "netbsd"
363
))]
364
0
pub fn lutimes<P: ?Sized + NixPath>(
365
0
    path: &P,
366
0
    atime: &TimeVal,
367
0
    mtime: &TimeVal,
368
0
) -> Result<()> {
369
0
    let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()];
370
0
    let res = path.with_nix_path(|cstr| unsafe {
371
0
        libc::lutimes(cstr.as_ptr(), &times[0])
372
0
    })?;
373
374
0
    Errno::result(res).map(drop)
375
0
}
376
377
/// Change the access and modification times of the file specified by a file descriptor.
378
///
379
/// If you want to set the timestamp to now, use `TimeSpec::UTIME_NOW`. Use
380
/// `TimeSpec::UTIME_OMIT` if you don't want to change it.
381
///
382
/// # References
383
///
384
/// [futimens(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html).
385
#[inline]
386
0
pub fn futimens(fd: RawFd, atime: &TimeSpec, mtime: &TimeSpec) -> Result<()> {
387
0
    let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
388
0
    let res = unsafe { libc::futimens(fd, &times[0]) };
389
0
390
0
    Errno::result(res).map(drop)
391
0
}
392
393
/// Flags for `utimensat` function.
394
// TODO: replace with fcntl::AtFlags
395
#[derive(Clone, Copy, Debug)]
396
pub enum UtimensatFlags {
397
    FollowSymlink,
398
    NoFollowSymlink,
399
}
400
401
/// Change the access and modification times of a file.
402
///
403
/// The file to be changed is determined relative to the directory associated
404
/// with the file descriptor `dirfd` or the current working directory
405
/// if `dirfd` is `None`.
406
///
407
/// If `flag` is `UtimensatFlags::NoFollowSymlink` and `path` names a symbolic link,
408
/// then the mode of the symbolic link is changed.
409
///
410
/// `utimensat(None, path, times, UtimensatFlags::FollowSymlink)` is identical to
411
/// `utimes(path, times)`. The latter is a deprecated API so prefer using the
412
/// former if the platforms you care about support it.
413
///
414
/// If you want to set the timestamp to now, use `TimeSpec::UTIME_NOW`. Use
415
/// `TimeSpec::UTIME_OMIT` if you don't want to change it.
416
///
417
/// # References
418
///
419
/// [utimensat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimens.html).
420
#[cfg(not(target_os = "redox"))]
421
0
pub fn utimensat<P: ?Sized + NixPath>(
422
0
    dirfd: Option<RawFd>,
423
0
    path: &P,
424
0
    atime: &TimeSpec,
425
0
    mtime: &TimeSpec,
426
0
    flag: UtimensatFlags,
427
0
) -> Result<()> {
428
0
    let atflag = match flag {
429
0
        UtimensatFlags::FollowSymlink => AtFlags::empty(),
430
0
        UtimensatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
431
    };
432
0
    let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
433
0
    let res = path.with_nix_path(|cstr| unsafe {
434
0
        libc::utimensat(
435
0
            at_rawfd(dirfd),
436
0
            cstr.as_ptr(),
437
0
            &times[0],
438
0
            atflag.bits() as libc::c_int,
439
0
        )
440
0
    })?;
441
442
0
    Errno::result(res).map(drop)
443
0
}
444
445
#[cfg(not(target_os = "redox"))]
446
0
pub fn mkdirat<P: ?Sized + NixPath>(
447
0
    fd: Option<RawFd>,
448
0
    path: &P,
449
0
    mode: Mode,
450
0
) -> Result<()> {
451
0
    let fd = at_rawfd(fd);
452
0
    let res = path.with_nix_path(|cstr| unsafe {
453
0
        libc::mkdirat(fd, cstr.as_ptr(), mode.bits() as mode_t)
454
0
    })?;
455
456
0
    Errno::result(res).map(drop)
457
0
}