nix/sys/
stat.rs

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