nix/sys/
stat.rs

1#[cfg(any(apple_targets, target_os = "openbsd"))]
2pub use libc::c_uint;
3#[cfg(any(target_os = "netbsd", freebsdlike))]
4pub use libc::c_ulong;
5pub use libc::stat as FileStat;
6pub use libc::{dev_t, mode_t};
7
8#[cfg(not(target_os = "redox"))]
9use crate::fcntl::{at_rawfd, AtFlags};
10use crate::sys::time::{TimeSpec, TimeVal};
11use crate::{errno::Errno, NixPath, Result};
12use std::mem;
13use std::os::unix::io::RawFd;
14
15libc_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
29libc_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"))]
65pub type type_of_file_flag = c_uint;
66#[cfg(any(freebsdlike, target_os = "netbsd"))]
67pub type type_of_file_flag = c_ulong;
68
69#[cfg(bsd)]
70libc_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.
156pub fn mknod<P: ?Sized + NixPath>(
157    path: &P,
158    kind: SFlag,
159    perm: Mode,
160    dev: dev_t,
161) -> Result<()> {
162    let res = path.with_nix_path(|cstr| unsafe {
163        libc::mknod(cstr.as_ptr(), kind.bits() | perm.bits() as mode_t, dev)
164    })?;
165
166    Errno::result(res).map(drop)
167}
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")))]
171pub fn mknodat<P: ?Sized + NixPath>(
172    dirfd: Option<RawFd>,
173    path: &P,
174    kind: SFlag,
175    perm: Mode,
176    dev: dev_t,
177) -> Result<()> {
178    let dirfd = at_rawfd(dirfd);
179    let res = path.with_nix_path(|cstr| unsafe {
180        libc::mknodat(
181            dirfd,
182            cstr.as_ptr(),
183            kind.bits() | perm.bits() as mode_t,
184            dev,
185        )
186    })?;
187
188    Errno::result(res).map(drop)
189}
190
191#[cfg(target_os = "linux")]
192pub const fn major(dev: dev_t) -> u64 {
193    ((dev >> 32) & 0xffff_f000) | ((dev >> 8) & 0x0000_0fff)
194}
195
196#[cfg(target_os = "linux")]
197pub const fn minor(dev: dev_t) -> u64 {
198    ((dev >> 12) & 0xffff_ff00) | ((dev) & 0x0000_00ff)
199}
200
201#[cfg(target_os = "linux")]
202pub const fn makedev(major: u64, minor: u64) -> dev_t {
203    ((major & 0xffff_f000) << 32)
204        | ((major & 0x0000_0fff) << 8)
205        | ((minor & 0xffff_ff00) << 12)
206        | (minor & 0x0000_00ff)
207}
208
209pub fn umask(mode: Mode) -> Mode {
210    let prev = unsafe { libc::umask(mode.bits() as mode_t) };
211    Mode::from_bits(prev).expect("[BUG] umask returned invalid Mode")
212}
213
214pub fn stat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
215    let mut dst = mem::MaybeUninit::uninit();
216    let res = path.with_nix_path(|cstr| unsafe {
217        libc::stat(cstr.as_ptr(), dst.as_mut_ptr())
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| unsafe {
228        libc::lstat(cstr.as_ptr(), dst.as_mut_ptr())
229    })?;
230
231    Errno::result(res)?;
232
233    Ok(unsafe { dst.assume_init() })
234}
235
236pub fn fstat(fd: RawFd) -> Result<FileStat> {
237    let mut dst = mem::MaybeUninit::uninit();
238    let res = unsafe { libc::fstat(fd, dst.as_mut_ptr()) };
239
240    Errno::result(res)?;
241
242    Ok(unsafe { dst.assume_init() })
243}
244
245#[cfg(not(target_os = "redox"))]
246pub fn fstatat<P: ?Sized + NixPath>(
247    dirfd: Option<RawFd>,
248    pathname: &P,
249    f: AtFlags,
250) -> Result<FileStat> {
251    let dirfd = at_rawfd(dirfd);
252    let mut dst = mem::MaybeUninit::uninit();
253    let res = pathname.with_nix_path(|cstr| unsafe {
254        libc::fstatat(
255            dirfd,
256            cstr.as_ptr(),
257            dst.as_mut_ptr(),
258            f.bits() as libc::c_int,
259        )
260    })?;
261
262    Errno::result(res)?;
263
264    Ok(unsafe { dst.assume_init() })
265}
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).
272pub fn fchmod(fd: RawFd, mode: Mode) -> Result<()> {
273    let res = unsafe { libc::fchmod(fd, mode.bits() as mode_t) };
274
275    Errno::result(res).map(drop)
276}
277
278/// Flags for `fchmodat` function.
279#[derive(Clone, Copy, Debug)]
280pub 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"))]
302pub fn fchmodat<P: ?Sized + NixPath>(
303    dirfd: Option<RawFd>,
304    path: &P,
305    mode: Mode,
306    flag: FchmodatFlags,
307) -> Result<()> {
308    let atflag = match flag {
309        FchmodatFlags::FollowSymlink => AtFlags::empty(),
310        FchmodatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
311    };
312    let res = path.with_nix_path(|cstr| unsafe {
313        libc::fchmodat(
314            at_rawfd(dirfd),
315            cstr.as_ptr(),
316            mode.bits() as mode_t,
317            atflag.bits() as libc::c_int,
318        )
319    })?;
320
321    Errno::result(res).map(drop)
322}
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).
334pub fn utimes<P: ?Sized + NixPath>(
335    path: &P,
336    atime: &TimeVal,
337    mtime: &TimeVal,
338) -> Result<()> {
339    let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()];
340    let res = path.with_nix_path(|cstr| unsafe {
341        libc::utimes(cstr.as_ptr(), &times[0])
342    })?;
343
344    Errno::result(res).map(drop)
345}
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))]
364pub fn lutimes<P: ?Sized + NixPath>(
365    path: &P,
366    atime: &TimeVal,
367    mtime: &TimeVal,
368) -> Result<()> {
369    let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()];
370    let res = path.with_nix_path(|cstr| unsafe {
371        libc::lutimes(cstr.as_ptr(), &times[0])
372    })?;
373
374    Errno::result(res).map(drop)
375}
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]
386pub fn futimens(fd: RawFd, atime: &TimeSpec, mtime: &TimeSpec) -> Result<()> {
387    let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
388    let res = unsafe { libc::futimens(fd, &times[0]) };
389
390    Errno::result(res).map(drop)
391}
392
393/// Flags for `utimensat` function.
394// TODO: replace with fcntl::AtFlags
395#[derive(Clone, Copy, Debug)]
396pub 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"))]
421pub fn utimensat<P: ?Sized + NixPath>(
422    dirfd: Option<RawFd>,
423    path: &P,
424    atime: &TimeSpec,
425    mtime: &TimeSpec,
426    flag: UtimensatFlags,
427) -> Result<()> {
428    let atflag = match flag {
429        UtimensatFlags::FollowSymlink => AtFlags::empty(),
430        UtimensatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
431    };
432    let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
433    let res = path.with_nix_path(|cstr| unsafe {
434        libc::utimensat(
435            at_rawfd(dirfd),
436            cstr.as_ptr(),
437            &times[0],
438            atflag.bits() as libc::c_int,
439        )
440    })?;
441
442    Errno::result(res).map(drop)
443}
444
445#[cfg(not(target_os = "redox"))]
446pub fn mkdirat<P: ?Sized + NixPath>(
447    fd: Option<RawFd>,
448    path: &P,
449    mode: Mode,
450) -> Result<()> {
451    let fd = at_rawfd(fd);
452    let res = path.with_nix_path(|cstr| unsafe {
453        libc::mkdirat(fd, cstr.as_ptr(), mode.bits() as mode_t)
454    })?;
455
456    Errno::result(res).map(drop)
457}