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