1 pub use libc::{dev_t, mode_t};
2 pub use libc::stat as FileStat;
3 
4 use crate::{Result, NixPath, errno::Errno};
5 #[cfg(not(target_os = "redox"))]
6 use crate::fcntl::{AtFlags, at_rawfd};
7 use std::mem;
8 use std::os::unix::io::RawFd;
9 use crate::sys::time::{TimeSpec, TimeVal};
10 
11 libc_bitflags!(
12     /// "File type" flags for `mknod` and related functions.
13     pub struct SFlag: mode_t {
14         S_IFIFO;
15         S_IFCHR;
16         S_IFDIR;
17         S_IFBLK;
18         S_IFREG;
19         S_IFLNK;
20         S_IFSOCK;
21         S_IFMT;
22     }
23 );
24 
25 libc_bitflags! {
26     /// "File mode / permissions" flags.
27     pub struct Mode: mode_t {
28         S_IRWXU;
29         S_IRUSR;
30         S_IWUSR;
31         S_IXUSR;
32         S_IRWXG;
33         S_IRGRP;
34         S_IWGRP;
35         S_IXGRP;
36         S_IRWXO;
37         S_IROTH;
38         S_IWOTH;
39         S_IXOTH;
40         S_ISUID as mode_t;
41         S_ISGID as mode_t;
42         S_ISVTX as mode_t;
43     }
44 }
45 
46 /// Create a special or ordinary file, by pathname.
mknod<P: ?Sized + NixPath>(path: &P, kind: SFlag, perm: Mode, dev: dev_t) -> Result<()>47 pub fn mknod<P: ?Sized + NixPath>(path: &P, kind: SFlag, perm: Mode, dev: dev_t) -> Result<()> {
48     let res = path.with_nix_path(|cstr| unsafe {
49         libc::mknod(cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev)
50     })?;
51 
52     Errno::result(res).map(drop)
53 }
54 
55 /// Create a special or ordinary file, relative to a given directory.
56 #[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox")))]
mknodat<P: ?Sized + NixPath>( dirfd: RawFd, path: &P, kind: SFlag, perm: Mode, dev: dev_t, ) -> Result<()>57 pub fn mknodat<P: ?Sized + NixPath>(
58     dirfd: RawFd,
59     path: &P,
60     kind: SFlag,
61     perm: Mode,
62     dev: dev_t,
63 ) -> Result<()> {
64     let res = path.with_nix_path(|cstr| unsafe {
65         libc::mknodat(dirfd, cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev)
66     })?;
67 
68     Errno::result(res).map(drop)
69 }
70 
71 #[cfg(target_os = "linux")]
major(dev: dev_t) -> u6472 pub const fn major(dev: dev_t) -> u64 {
73     ((dev >> 32) & 0xffff_f000) |
74     ((dev >>  8) & 0x0000_0fff)
75 }
76 
77 #[cfg(target_os = "linux")]
minor(dev: dev_t) -> u6478 pub const fn minor(dev: dev_t) -> u64 {
79     ((dev >> 12) & 0xffff_ff00) |
80     ((dev      ) & 0x0000_00ff)
81 }
82 
83 #[cfg(target_os = "linux")]
makedev(major: u64, minor: u64) -> dev_t84 pub const fn makedev(major: u64, minor: u64) -> dev_t {
85     ((major & 0xffff_f000) << 32) |
86     ((major & 0x0000_0fff) <<  8) |
87     ((minor & 0xffff_ff00) << 12) |
88      (minor & 0x0000_00ff)
89 }
90 
umask(mode: Mode) -> Mode91 pub fn umask(mode: Mode) -> Mode {
92     let prev = unsafe { libc::umask(mode.bits() as mode_t) };
93     Mode::from_bits(prev).expect("[BUG] umask returned invalid Mode")
94 }
95 
stat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat>96 pub fn stat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
97     let mut dst = mem::MaybeUninit::uninit();
98     let res = path.with_nix_path(|cstr| {
99         unsafe {
100             libc::stat(cstr.as_ptr(), dst.as_mut_ptr())
101         }
102     })?;
103 
104     Errno::result(res)?;
105 
106     Ok(unsafe{dst.assume_init()})
107 }
108 
lstat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat>109 pub fn lstat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
110     let mut dst = mem::MaybeUninit::uninit();
111     let res = path.with_nix_path(|cstr| {
112         unsafe {
113             libc::lstat(cstr.as_ptr(), dst.as_mut_ptr())
114         }
115     })?;
116 
117     Errno::result(res)?;
118 
119     Ok(unsafe{dst.assume_init()})
120 }
121 
fstat(fd: RawFd) -> Result<FileStat>122 pub fn fstat(fd: RawFd) -> Result<FileStat> {
123     let mut dst = mem::MaybeUninit::uninit();
124     let res = unsafe { libc::fstat(fd, dst.as_mut_ptr()) };
125 
126     Errno::result(res)?;
127 
128     Ok(unsafe{dst.assume_init()})
129 }
130 
131 #[cfg(not(target_os = "redox"))]
fstatat<P: ?Sized + NixPath>(dirfd: RawFd, pathname: &P, f: AtFlags) -> Result<FileStat>132 pub fn fstatat<P: ?Sized + NixPath>(dirfd: RawFd, pathname: &P, f: AtFlags) -> Result<FileStat> {
133     let mut dst = mem::MaybeUninit::uninit();
134     let res = pathname.with_nix_path(|cstr| {
135         unsafe { libc::fstatat(dirfd, cstr.as_ptr(), dst.as_mut_ptr(), f.bits() as libc::c_int) }
136     })?;
137 
138     Errno::result(res)?;
139 
140     Ok(unsafe{dst.assume_init()})
141 }
142 
143 /// Change the file permission bits of the file specified by a file descriptor.
144 ///
145 /// # References
146 ///
147 /// [fchmod(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html).
fchmod(fd: RawFd, mode: Mode) -> Result<()>148 pub fn fchmod(fd: RawFd, mode: Mode) -> Result<()> {
149     let res = unsafe { libc::fchmod(fd, mode.bits() as mode_t) };
150 
151     Errno::result(res).map(drop)
152 }
153 
154 /// Flags for `fchmodat` function.
155 #[derive(Clone, Copy, Debug)]
156 pub enum FchmodatFlags {
157     FollowSymlink,
158     NoFollowSymlink,
159 }
160 
161 /// Change the file permission bits.
162 ///
163 /// The file to be changed is determined relative to the directory associated
164 /// with the file descriptor `dirfd` or the current working directory
165 /// if `dirfd` is `None`.
166 ///
167 /// If `flag` is `FchmodatFlags::NoFollowSymlink` and `path` names a symbolic link,
168 /// then the mode of the symbolic link is changed.
169 ///
170 /// `fchmodat(None, path, mode, FchmodatFlags::FollowSymlink)` is identical to
171 /// a call `libc::chmod(path, mode)`. That's why `chmod` is unimplemented
172 /// in the `nix` crate.
173 ///
174 /// # References
175 ///
176 /// [fchmodat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html).
177 #[cfg(not(target_os = "redox"))]
fchmodat<P: ?Sized + NixPath>( dirfd: Option<RawFd>, path: &P, mode: Mode, flag: FchmodatFlags, ) -> Result<()>178 pub fn fchmodat<P: ?Sized + NixPath>(
179     dirfd: Option<RawFd>,
180     path: &P,
181     mode: Mode,
182     flag: FchmodatFlags,
183 ) -> Result<()> {
184     let atflag =
185         match flag {
186             FchmodatFlags::FollowSymlink => AtFlags::empty(),
187             FchmodatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
188         };
189     let res = path.with_nix_path(|cstr| unsafe {
190         libc::fchmodat(
191             at_rawfd(dirfd),
192             cstr.as_ptr(),
193             mode.bits() as mode_t,
194             atflag.bits() as libc::c_int,
195         )
196     })?;
197 
198     Errno::result(res).map(drop)
199 }
200 
201 /// Change the access and modification times of a file.
202 ///
203 /// `utimes(path, times)` is identical to
204 /// `utimensat(None, path, times, UtimensatFlags::FollowSymlink)`. The former
205 /// is a deprecated API so prefer using the latter if the platforms you care
206 /// about support it.
207 ///
208 /// # References
209 ///
210 /// [utimes(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html).
utimes<P: ?Sized + NixPath>(path: &P, atime: &TimeVal, mtime: &TimeVal) -> Result<()>211 pub fn utimes<P: ?Sized + NixPath>(path: &P, atime: &TimeVal, mtime: &TimeVal) -> Result<()> {
212     let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()];
213     let res = path.with_nix_path(|cstr| unsafe {
214         libc::utimes(cstr.as_ptr(), &times[0])
215     })?;
216 
217     Errno::result(res).map(drop)
218 }
219 
220 /// Change the access and modification times of a file without following symlinks.
221 ///
222 /// `lutimes(path, times)` is identical to
223 /// `utimensat(None, path, times, UtimensatFlags::NoFollowSymlink)`. The former
224 /// is a deprecated API so prefer using the latter if the platforms you care
225 /// about support it.
226 ///
227 /// # References
228 ///
229 /// [lutimes(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lutimes.html).
230 #[cfg(any(target_os = "linux",
231           target_os = "haiku",
232           target_os = "ios",
233           target_os = "macos",
234           target_os = "freebsd",
235           target_os = "netbsd"))]
lutimes<P: ?Sized + NixPath>(path: &P, atime: &TimeVal, mtime: &TimeVal) -> Result<()>236 pub fn lutimes<P: ?Sized + NixPath>(path: &P, atime: &TimeVal, mtime: &TimeVal) -> Result<()> {
237     let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()];
238     let res = path.with_nix_path(|cstr| unsafe {
239         libc::lutimes(cstr.as_ptr(), &times[0])
240     })?;
241 
242     Errno::result(res).map(drop)
243 }
244 
245 /// Change the access and modification times of the file specified by a file descriptor.
246 ///
247 /// # References
248 ///
249 /// [futimens(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html).
250 #[inline]
futimens(fd: RawFd, atime: &TimeSpec, mtime: &TimeSpec) -> Result<()>251 pub fn futimens(fd: RawFd, atime: &TimeSpec, mtime: &TimeSpec) -> Result<()> {
252     let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
253     let res = unsafe { libc::futimens(fd, &times[0]) };
254 
255     Errno::result(res).map(drop)
256 }
257 
258 /// Flags for `utimensat` function.
259 // TODO: replace with fcntl::AtFlags
260 #[derive(Clone, Copy, Debug)]
261 pub enum UtimensatFlags {
262     FollowSymlink,
263     NoFollowSymlink,
264 }
265 
266 /// Change the access and modification times of a file.
267 ///
268 /// The file to be changed is determined relative to the directory associated
269 /// with the file descriptor `dirfd` or the current working directory
270 /// if `dirfd` is `None`.
271 ///
272 /// If `flag` is `UtimensatFlags::NoFollowSymlink` and `path` names a symbolic link,
273 /// then the mode of the symbolic link is changed.
274 ///
275 /// `utimensat(None, path, times, UtimensatFlags::FollowSymlink)` is identical to
276 /// `utimes(path, times)`. The latter is a deprecated API so prefer using the
277 /// former if the platforms you care about support it.
278 ///
279 /// # References
280 ///
281 /// [utimensat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimens.html).
282 #[cfg(not(target_os = "redox"))]
utimensat<P: ?Sized + NixPath>( dirfd: Option<RawFd>, path: &P, atime: &TimeSpec, mtime: &TimeSpec, flag: UtimensatFlags ) -> Result<()>283 pub fn utimensat<P: ?Sized + NixPath>(
284     dirfd: Option<RawFd>,
285     path: &P,
286     atime: &TimeSpec,
287     mtime: &TimeSpec,
288     flag: UtimensatFlags
289 ) -> Result<()> {
290     let atflag =
291         match flag {
292             UtimensatFlags::FollowSymlink => AtFlags::empty(),
293             UtimensatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
294         };
295     let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
296     let res = path.with_nix_path(|cstr| unsafe {
297         libc::utimensat(
298             at_rawfd(dirfd),
299             cstr.as_ptr(),
300             &times[0],
301             atflag.bits() as libc::c_int,
302         )
303     })?;
304 
305     Errno::result(res).map(drop)
306 }
307 
308 #[cfg(not(target_os = "redox"))]
mkdirat<P: ?Sized + NixPath>(fd: RawFd, path: &P, mode: Mode) -> Result<()>309 pub fn mkdirat<P: ?Sized + NixPath>(fd: RawFd, path: &P, mode: Mode) -> Result<()> {
310     let res = path.with_nix_path(|cstr| {
311         unsafe { libc::mkdirat(fd, cstr.as_ptr(), mode.bits() as mode_t) }
312     })?;
313 
314     Errno::result(res).map(drop)
315 }
316