1 #[cfg(any(target_os = "macos", target_os = "ios", target_os = "openbsd"))]
2 pub use libc::c_uint;
3 #[cfg(any(
4 target_os = "netbsd",
5 target_os = "freebsd",
6 target_os = "dragonfly"
7 ))]
8 pub use libc::c_ulong;
9 pub use libc::stat as FileStat;
10 pub use libc::{dev_t, mode_t};
11
12 #[cfg(not(target_os = "redox"))]
13 use crate::fcntl::{at_rawfd, AtFlags};
14 use crate::sys::time::{TimeSpec, TimeVal};
15 use crate::{errno::Errno, NixPath, Result};
16 use std::mem;
17 use std::os::unix::io::RawFd;
18
19 libc_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
33 libc_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"))]
69 pub type type_of_file_flag = c_uint;
70 #[cfg(any(
71 target_os = "netbsd",
72 target_os = "freebsd",
73 target_os = "dragonfly"
74 ))]
75 pub 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 ))]
85 libc_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.
mknod<P: ?Sized + NixPath>( path: &P, kind: SFlag, perm: Mode, dev: dev_t, ) -> Result<()>173 pub fn mknod<P: ?Sized + NixPath>(
174 path: &P,
175 kind: SFlag,
176 perm: Mode,
177 dev: dev_t,
178 ) -> Result<()> {
179 let res = path.with_nix_path(|cstr| unsafe {
180 libc::mknod(cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev)
181 })?;
182
183 Errno::result(res).map(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())))]
mknodat<P: ?Sized + NixPath>( dirfd: RawFd, path: &P, kind: SFlag, perm: Mode, dev: dev_t, ) -> Result<()>194 pub 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 = path.with_nix_path(|cstr| unsafe {
202 libc::mknodat(
203 dirfd,
204 cstr.as_ptr(),
205 kind.bits | perm.bits() as mode_t,
206 dev,
207 )
208 })?;
209
210 Errno::result(res).map(drop)
211 }
212
213 #[cfg(target_os = "linux")]
214 #[cfg_attr(docsrs, doc(cfg(all())))]
major(dev: dev_t) -> u64215 pub 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())))]
minor(dev: dev_t) -> u64221 pub 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())))]
makedev(major: u64, minor: u64) -> dev_t227 pub 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
umask(mode: Mode) -> Mode234 pub fn umask(mode: Mode) -> Mode {
235 let prev = unsafe { libc::umask(mode.bits() as mode_t) };
236 Mode::from_bits(prev).expect("[BUG] umask returned invalid Mode")
237 }
238
stat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat>239 pub fn stat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
240 let mut dst = mem::MaybeUninit::uninit();
241 let res = path.with_nix_path(|cstr| unsafe {
242 libc::stat(cstr.as_ptr(), dst.as_mut_ptr())
243 })?;
244
245 Errno::result(res)?;
246
247 Ok(unsafe { dst.assume_init() })
248 }
249
lstat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat>250 pub fn lstat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
251 let mut dst = mem::MaybeUninit::uninit();
252 let res = path.with_nix_path(|cstr| unsafe {
253 libc::lstat(cstr.as_ptr(), dst.as_mut_ptr())
254 })?;
255
256 Errno::result(res)?;
257
258 Ok(unsafe { dst.assume_init() })
259 }
260
fstat(fd: RawFd) -> Result<FileStat>261 pub fn fstat(fd: RawFd) -> Result<FileStat> {
262 let mut dst = mem::MaybeUninit::uninit();
263 let res = unsafe { libc::fstat(fd, 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())))]
fstatat<P: ?Sized + NixPath>( dirfd: RawFd, pathname: &P, f: AtFlags, ) -> Result<FileStat>272 pub fn fstatat<P: ?Sized + NixPath>(
273 dirfd: RawFd,
274 pathname: &P,
275 f: AtFlags,
276 ) -> Result<FileStat> {
277 let mut dst = mem::MaybeUninit::uninit();
278 let res = pathname.with_nix_path(|cstr| unsafe {
279 libc::fstatat(
280 dirfd,
281 cstr.as_ptr(),
282 dst.as_mut_ptr(),
283 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).
fchmod(fd: RawFd, mode: Mode) -> Result<()>297 pub fn fchmod(fd: RawFd, mode: Mode) -> Result<()> {
298 let res = unsafe { libc::fchmod(fd, mode.bits() as mode_t) };
299
300 Errno::result(res).map(drop)
301 }
302
303 /// Flags for `fchmodat` function.
304 #[derive(Clone, Copy, Debug)]
305 pub 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())))]
fchmodat<P: ?Sized + NixPath>( dirfd: Option<RawFd>, path: &P, mode: Mode, flag: FchmodatFlags, ) -> Result<()>328 pub fn fchmodat<P: ?Sized + NixPath>(
329 dirfd: Option<RawFd>,
330 path: &P,
331 mode: Mode,
332 flag: FchmodatFlags,
333 ) -> Result<()> {
334 let atflag = match flag {
335 FchmodatFlags::FollowSymlink => AtFlags::empty(),
336 FchmodatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
337 };
338 let res = path.with_nix_path(|cstr| unsafe {
339 libc::fchmodat(
340 at_rawfd(dirfd),
341 cstr.as_ptr(),
342 mode.bits() as mode_t,
343 atflag.bits() as libc::c_int,
344 )
345 })?;
346
347 Errno::result(res).map(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).
utimes<P: ?Sized + NixPath>( path: &P, atime: &TimeVal, mtime: &TimeVal, ) -> Result<()>360 pub 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 = path.with_nix_path(|cstr| unsafe {
367 libc::utimes(cstr.as_ptr(), ×[0])
368 })?;
369
370 Errno::result(res).map(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())))]
lutimes<P: ?Sized + NixPath>( path: &P, atime: &TimeVal, mtime: &TimeVal, ) -> Result<()>392 pub 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 = path.with_nix_path(|cstr| unsafe {
399 libc::lutimes(cstr.as_ptr(), ×[0])
400 })?;
401
402 Errno::result(res).map(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]
futimens(fd: RawFd, atime: &TimeSpec, mtime: &TimeSpec) -> Result<()>411 pub fn futimens(fd: RawFd, atime: &TimeSpec, mtime: &TimeSpec) -> Result<()> {
412 let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
413 let res = unsafe { libc::futimens(fd, ×[0]) };
414
415 Errno::result(res).map(drop)
416 }
417
418 /// Flags for `utimensat` function.
419 // TODO: replace with fcntl::AtFlags
420 #[derive(Clone, Copy, Debug)]
421 pub 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())))]
utimensat<P: ?Sized + NixPath>( dirfd: Option<RawFd>, path: &P, atime: &TimeSpec, mtime: &TimeSpec, flag: UtimensatFlags, ) -> Result<()>444 pub 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 = 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 = path.with_nix_path(|cstr| unsafe {
457 libc::utimensat(
458 at_rawfd(dirfd),
459 cstr.as_ptr(),
460 ×[0],
461 atflag.bits() as libc::c_int,
462 )
463 })?;
464
465 Errno::result(res).map(drop)
466 }
467
468 #[cfg(not(target_os = "redox"))]
469 #[cfg_attr(docsrs, doc(cfg(all())))]
mkdirat<P: ?Sized + NixPath>( fd: RawFd, path: &P, mode: Mode, ) -> Result<()>470 pub fn mkdirat<P: ?Sized + NixPath>(
471 fd: RawFd,
472 path: &P,
473 mode: Mode,
474 ) -> Result<()> {
475 let res = path.with_nix_path(|cstr| unsafe {
476 libc::mkdirat(fd, cstr.as_ptr(), mode.bits() as mode_t)
477 })?;
478
479 Errno::result(res).map(drop)
480 }
481