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