blob: df81a2cb329d6e958d0d8546cf3fbf7dc68a3043 [file] [log] [blame]
Andrew Walbran12f61402020-10-14 11:10:53 +01001pub use libc::{dev_t, mode_t};
2pub use libc::stat as FileStat;
3
4use crate::{Result, NixPath, errno::Errno};
5#[cfg(not(target_os = "redox"))]
6use crate::fcntl::{AtFlags, at_rawfd};
7use std::mem;
8use std::os::unix::io::RawFd;
9use crate::sys::time::{TimeSpec, TimeVal};
10
11libc_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
24libc_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
44pub 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")]
55pub fn major(dev: dev_t) -> u64 {
56 ((dev >> 32) & 0xffff_f000) |
57 ((dev >> 8) & 0x0000_0fff)
58}
59
60#[cfg(target_os = "linux")]
61pub fn minor(dev: dev_t) -> u64 {
62 ((dev >> 12) & 0xffff_ff00) |
63 ((dev ) & 0x0000_00ff)
64}
65
66#[cfg(target_os = "linux")]
67pub 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
74pub 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
79pub 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
92pub 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
105pub 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"))]
115pub 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).
131pub 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)]
139pub 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"))]
161pub 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).
194pub 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"))]
219pub 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]
234pub 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)]
243pub 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"))]
265pub 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"))]
291pub 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}