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