1 #[cfg(any(apple_targets, target_os = "openbsd"))]
2 pub use libc::c_uint;
3 #[cfg(any(target_os = "netbsd", freebsdlike))]
4 pub use libc::c_ulong;
5 pub use libc::stat as FileStat;
6 pub use libc::{dev_t, mode_t};
7 
8 #[cfg(not(target_os = "redox"))]
9 use crate::fcntl::{at_rawfd, AtFlags};
10 use crate::sys::time::{TimeSpec, TimeVal};
11 use crate::{errno::Errno, NixPath, Result};
12 use std::mem;
13 use std::os::unix::io::RawFd;
14 
15 libc_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 
29 libc_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"))]
65 pub type type_of_file_flag = c_uint;
66 #[cfg(any(freebsdlike, target_os = "netbsd"))]
67 pub type type_of_file_flag = c_ulong;
68 
69 #[cfg(bsd)]
70 libc_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.
mknod<P: ?Sized + NixPath>( path: &P, kind: SFlag, perm: Mode, dev: dev_t, ) -> Result<()>156 pub 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")))]
mknodat<P: ?Sized + NixPath>( dirfd: Option<RawFd>, path: &P, kind: SFlag, perm: Mode, dev: dev_t, ) -> Result<()>171 pub 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")]
major(dev: dev_t) -> u64192 pub const fn major(dev: dev_t) -> u64 {
193     ((dev >> 32) & 0xffff_f000) | ((dev >> 8) & 0x0000_0fff)
194 }
195 
196 #[cfg(target_os = "linux")]
minor(dev: dev_t) -> u64197 pub const fn minor(dev: dev_t) -> u64 {
198     ((dev >> 12) & 0xffff_ff00) | ((dev) & 0x0000_00ff)
199 }
200 
201 #[cfg(target_os = "linux")]
makedev(major: u64, minor: u64) -> dev_t202 pub 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 
umask(mode: Mode) -> Mode209 pub 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 
stat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat>214 pub 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 
lstat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat>225 pub 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 
fstat(fd: RawFd) -> Result<FileStat>236 pub 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"))]
fstatat<P: ?Sized + NixPath>( dirfd: Option<RawFd>, pathname: &P, f: AtFlags, ) -> Result<FileStat>246 pub 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).
fchmod(fd: RawFd, mode: Mode) -> Result<()>272 pub 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)]
280 pub 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"))]
fchmodat<P: ?Sized + NixPath>( dirfd: Option<RawFd>, path: &P, mode: Mode, flag: FchmodatFlags, ) -> Result<()>302 pub 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).
utimes<P: ?Sized + NixPath>( path: &P, atime: &TimeVal, mtime: &TimeVal, ) -> Result<()>334 pub 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 ))]
lutimes<P: ?Sized + NixPath>( path: &P, atime: &TimeVal, mtime: &TimeVal, ) -> Result<()>364 pub 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]
futimens(fd: RawFd, atime: &TimeSpec, mtime: &TimeSpec) -> Result<()>386 pub 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)]
396 pub 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"))]
utimensat<P: ?Sized + NixPath>( dirfd: Option<RawFd>, path: &P, atime: &TimeSpec, mtime: &TimeSpec, flag: UtimensatFlags, ) -> Result<()>421 pub 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"))]
mkdirat<P: ?Sized + NixPath>( fd: Option<RawFd>, path: &P, mode: Mode, ) -> Result<()>446 pub 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 }
458