nix/sys/
stat.rs

1pub use libc::{dev_t, mode_t};
2pub use libc::stat as FileStat;
3
4use crate::{Result, NixPath, errno::Errno};
5#[cfg(not(target_os = "redox"))]
6use crate::fcntl::{AtFlags, at_rawfd};
7use std::mem;
8use std::os::unix::io::RawFd;
9use crate::sys::time::{TimeSpec, TimeVal};
10
11libc_bitflags!(
12    /// "File type" flags for `mknod` and related functions.
13    pub struct SFlag: mode_t {
14        S_IFIFO;
15        S_IFCHR;
16        S_IFDIR;
17        S_IFBLK;
18        S_IFREG;
19        S_IFLNK;
20        S_IFSOCK;
21        S_IFMT;
22    }
23);
24
25libc_bitflags! {
26    /// "File mode / permissions" flags.
27    pub struct Mode: mode_t {
28        S_IRWXU;
29        S_IRUSR;
30        S_IWUSR;
31        S_IXUSR;
32        S_IRWXG;
33        S_IRGRP;
34        S_IWGRP;
35        S_IXGRP;
36        S_IRWXO;
37        S_IROTH;
38        S_IWOTH;
39        S_IXOTH;
40        S_ISUID as mode_t;
41        S_ISGID as mode_t;
42        S_ISVTX as mode_t;
43    }
44}
45
46/// Create a special or ordinary file, by pathname.
47pub fn mknod<P: ?Sized + NixPath>(path: &P, kind: SFlag, perm: Mode, dev: dev_t) -> Result<()> {
48    let res = path.with_nix_path(|cstr| unsafe {
49        libc::mknod(cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev)
50    })?;
51
52    Errno::result(res).map(drop)
53}
54
55/// Create a special or ordinary file, relative to a given directory.
56#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
57#[cfg_attr(docsrs, doc(cfg(all())))]
58pub fn mknodat<P: ?Sized + NixPath>(
59    dirfd: RawFd,
60    path: &P,
61    kind: SFlag,
62    perm: Mode,
63    dev: dev_t,
64) -> Result<()> {
65    let res = path.with_nix_path(|cstr| unsafe {
66        libc::mknodat(dirfd, cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev)
67    })?;
68
69    Errno::result(res).map(drop)
70}
71
72#[cfg(target_os = "linux")]
73#[cfg_attr(docsrs, doc(cfg(all())))]
74pub const fn major(dev: dev_t) -> u64 {
75    ((dev >> 32) & 0xffff_f000) |
76    ((dev >>  8) & 0x0000_0fff)
77}
78
79#[cfg(target_os = "linux")]
80#[cfg_attr(docsrs, doc(cfg(all())))]
81pub const fn minor(dev: dev_t) -> u64 {
82    ((dev >> 12) & 0xffff_ff00) |
83    ((dev      ) & 0x0000_00ff)
84}
85
86#[cfg(target_os = "linux")]
87#[cfg_attr(docsrs, doc(cfg(all())))]
88pub const fn makedev(major: u64, minor: u64) -> dev_t {
89    ((major & 0xffff_f000) << 32) |
90    ((major & 0x0000_0fff) <<  8) |
91    ((minor & 0xffff_ff00) << 12) |
92     (minor & 0x0000_00ff)
93}
94
95pub fn umask(mode: Mode) -> Mode {
96    let prev = unsafe { libc::umask(mode.bits() as mode_t) };
97    Mode::from_bits(prev).expect("[BUG] umask returned invalid Mode")
98}
99
100pub fn stat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
101    let mut dst = mem::MaybeUninit::uninit();
102    let res = path.with_nix_path(|cstr| {
103        unsafe {
104            libc::stat(cstr.as_ptr(), dst.as_mut_ptr())
105        }
106    })?;
107
108    Errno::result(res)?;
109
110    Ok(unsafe{dst.assume_init()})
111}
112
113pub fn lstat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
114    let mut dst = mem::MaybeUninit::uninit();
115    let res = path.with_nix_path(|cstr| {
116        unsafe {
117            libc::lstat(cstr.as_ptr(), dst.as_mut_ptr())
118        }
119    })?;
120
121    Errno::result(res)?;
122
123    Ok(unsafe{dst.assume_init()})
124}
125
126pub fn fstat(fd: RawFd) -> Result<FileStat> {
127    let mut dst = mem::MaybeUninit::uninit();
128    let res = unsafe { libc::fstat(fd, dst.as_mut_ptr()) };
129
130    Errno::result(res)?;
131
132    Ok(unsafe{dst.assume_init()})
133}
134
135#[cfg(not(target_os = "redox"))]
136#[cfg_attr(docsrs, doc(cfg(all())))]
137pub fn fstatat<P: ?Sized + NixPath>(dirfd: RawFd, pathname: &P, f: AtFlags) -> Result<FileStat> {
138    let mut dst = mem::MaybeUninit::uninit();
139    let res = pathname.with_nix_path(|cstr| {
140        unsafe { libc::fstatat(dirfd, cstr.as_ptr(), dst.as_mut_ptr(), f.bits() as libc::c_int) }
141    })?;
142
143    Errno::result(res)?;
144
145    Ok(unsafe{dst.assume_init()})
146}
147
148/// Change the file permission bits of the file specified by a file descriptor.
149///
150/// # References
151///
152/// [fchmod(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html).
153pub fn fchmod(fd: RawFd, mode: Mode) -> Result<()> {
154    let res = unsafe { libc::fchmod(fd, mode.bits() as mode_t) };
155
156    Errno::result(res).map(drop)
157}
158
159/// Flags for `fchmodat` function.
160#[derive(Clone, Copy, Debug)]
161pub enum FchmodatFlags {
162    FollowSymlink,
163    NoFollowSymlink,
164}
165
166/// Change the file permission bits.
167///
168/// The file to be changed is determined relative to the directory associated
169/// with the file descriptor `dirfd` or the current working directory
170/// if `dirfd` is `None`.
171///
172/// If `flag` is `FchmodatFlags::NoFollowSymlink` and `path` names a symbolic link,
173/// then the mode of the symbolic link is changed.
174///
175/// `fchmodat(None, path, mode, FchmodatFlags::FollowSymlink)` is identical to
176/// a call `libc::chmod(path, mode)`. That's why `chmod` is unimplemented
177/// in the `nix` crate.
178///
179/// # References
180///
181/// [fchmodat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html).
182#[cfg(not(target_os = "redox"))]
183#[cfg_attr(docsrs, doc(cfg(all())))]
184pub fn fchmodat<P: ?Sized + NixPath>(
185    dirfd: Option<RawFd>,
186    path: &P,
187    mode: Mode,
188    flag: FchmodatFlags,
189) -> Result<()> {
190    let atflag =
191        match flag {
192            FchmodatFlags::FollowSymlink => AtFlags::empty(),
193            FchmodatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
194        };
195    let res = path.with_nix_path(|cstr| unsafe {
196        libc::fchmodat(
197            at_rawfd(dirfd),
198            cstr.as_ptr(),
199            mode.bits() as mode_t,
200            atflag.bits() as libc::c_int,
201        )
202    })?;
203
204    Errno::result(res).map(drop)
205}
206
207/// Change the access and modification times of a file.
208///
209/// `utimes(path, times)` is identical to
210/// `utimensat(None, path, times, UtimensatFlags::FollowSymlink)`. The former
211/// is a deprecated API so prefer using the latter if the platforms you care
212/// about support it.
213///
214/// # References
215///
216/// [utimes(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html).
217pub fn utimes<P: ?Sized + NixPath>(path: &P, atime: &TimeVal, mtime: &TimeVal) -> Result<()> {
218    let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()];
219    let res = path.with_nix_path(|cstr| unsafe {
220        libc::utimes(cstr.as_ptr(), &times[0])
221    })?;
222
223    Errno::result(res).map(drop)
224}
225
226/// Change the access and modification times of a file without following symlinks.
227///
228/// `lutimes(path, times)` is identical to
229/// `utimensat(None, path, times, UtimensatFlags::NoFollowSymlink)`. The former
230/// is a deprecated API so prefer using the latter if the platforms you care
231/// about support it.
232///
233/// # References
234///
235/// [lutimes(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lutimes.html).
236#[cfg(any(target_os = "linux",
237          target_os = "haiku",
238          target_os = "ios",
239          target_os = "macos",
240          target_os = "freebsd",
241          target_os = "netbsd"))]
242#[cfg_attr(docsrs, doc(cfg(all())))]
243pub fn lutimes<P: ?Sized + NixPath>(path: &P, atime: &TimeVal, mtime: &TimeVal) -> Result<()> {
244    let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()];
245    let res = path.with_nix_path(|cstr| unsafe {
246        libc::lutimes(cstr.as_ptr(), &times[0])
247    })?;
248
249    Errno::result(res).map(drop)
250}
251
252/// Change the access and modification times of the file specified by a file descriptor.
253///
254/// # References
255///
256/// [futimens(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html).
257#[inline]
258pub fn futimens(fd: RawFd, atime: &TimeSpec, mtime: &TimeSpec) -> Result<()> {
259    let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
260    let res = unsafe { libc::futimens(fd, &times[0]) };
261
262    Errno::result(res).map(drop)
263}
264
265/// Flags for `utimensat` function.
266// TODO: replace with fcntl::AtFlags
267#[derive(Clone, Copy, Debug)]
268pub enum UtimensatFlags {
269    FollowSymlink,
270    NoFollowSymlink,
271}
272
273/// Change the access and modification times of a file.
274///
275/// The file to be changed is determined relative to the directory associated
276/// with the file descriptor `dirfd` or the current working directory
277/// if `dirfd` is `None`.
278///
279/// If `flag` is `UtimensatFlags::NoFollowSymlink` and `path` names a symbolic link,
280/// then the mode of the symbolic link is changed.
281///
282/// `utimensat(None, path, times, UtimensatFlags::FollowSymlink)` is identical to
283/// `utimes(path, times)`. The latter is a deprecated API so prefer using the
284/// former if the platforms you care about support it.
285///
286/// # References
287///
288/// [utimensat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimens.html).
289#[cfg(not(target_os = "redox"))]
290#[cfg_attr(docsrs, doc(cfg(all())))]
291pub fn utimensat<P: ?Sized + NixPath>(
292    dirfd: Option<RawFd>,
293    path: &P,
294    atime: &TimeSpec,
295    mtime: &TimeSpec,
296    flag: UtimensatFlags
297) -> Result<()> {
298    let atflag =
299        match flag {
300            UtimensatFlags::FollowSymlink => AtFlags::empty(),
301            UtimensatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
302        };
303    let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
304    let res = path.with_nix_path(|cstr| unsafe {
305        libc::utimensat(
306            at_rawfd(dirfd),
307            cstr.as_ptr(),
308            &times[0],
309            atflag.bits() as libc::c_int,
310        )
311    })?;
312
313    Errno::result(res).map(drop)
314}
315
316#[cfg(not(target_os = "redox"))]
317#[cfg_attr(docsrs, doc(cfg(all())))]
318pub fn mkdirat<P: ?Sized + NixPath>(fd: RawFd, path: &P, mode: Mode) -> Result<()> {
319    let res = path.with_nix_path(|cstr| {
320        unsafe { libc::mkdirat(fd, cstr.as_ptr(), mode.bits() as mode_t) }
321    })?;
322
323    Errno::result(res).map(drop)
324}