nix/sys/
stat.rs

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