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