1#[cfg(any(apple_targets, target_os = "openbsd"))]
2pub use libc::c_uint;
3#[cfg(any(target_os = "netbsd", freebsdlike))]
4pub use libc::c_ulong;
5pub use libc::stat as FileStat;
6pub use libc::{dev_t, mode_t};
7
8#[cfg(not(target_os = "redox"))]
9use crate::fcntl::{at_rawfd, AtFlags};
10use crate::sys::time::{TimeSpec, TimeVal};
11use crate::{errno::Errno, NixPath, Result};
12use std::mem;
13use std::os::unix::io::RawFd;
14
15libc_bitflags!(
16 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
29libc_bitflags! {
30 pub struct Mode: mode_t {
32 S_IRWXU;
34 S_IRUSR;
36 S_IWUSR;
38 S_IXUSR;
40 S_IRWXG;
42 S_IRGRP;
44 S_IWGRP;
46 S_IXGRP;
48 S_IRWXO;
50 S_IROTH;
52 S_IWOTH;
54 S_IXOTH;
56 S_ISUID as mode_t;
58 S_ISGID as mode_t;
60 S_ISVTX as mode_t;
61 }
62}
63
64#[cfg(any(apple_targets, target_os = "openbsd"))]
65pub type type_of_file_flag = c_uint;
66#[cfg(any(freebsdlike, target_os = "netbsd"))]
67pub type type_of_file_flag = c_ulong;
68
69#[cfg(bsd)]
70libc_bitflags! {
71 pub struct FileFlag: type_of_file_flag {
73 SF_APPEND;
75 SF_ARCHIVED;
77 #[cfg(any(target_os = "dragonfly"))]
78 SF_CACHE;
79 SF_IMMUTABLE;
81 #[cfg(any(target_os = "netbsd"))]
83 SF_LOG;
84 #[cfg(any(target_os = "dragonfly"))]
86 SF_NOHISTORY;
87 #[cfg(freebsdlike)]
89 SF_NOUNLINK;
90 SF_SETTABLE;
92 #[cfg(any(target_os = "netbsd"))]
94 SF_SNAPINVAL;
95 #[cfg(any(target_os = "netbsd", target_os = "freebsd"))]
97 SF_SNAPSHOT;
98 #[cfg(any(target_os = "dragonfly"))]
99 SF_XLINK;
100 UF_APPEND;
102 #[cfg(any(target_os = "freebsd"))]
104 UF_ARCHIVE;
105 #[cfg(any(target_os = "dragonfly"))]
106 UF_CACHE;
107 #[cfg(apple_targets)]
109 UF_COMPRESSED;
110 #[cfg(any(
113 target_os = "freebsd",
114 apple_targets,
115 ))]
116 UF_HIDDEN;
117 UF_IMMUTABLE;
119 UF_NODUMP;
121 #[cfg(any(target_os = "dragonfly"))]
122 UF_NOHISTORY;
123 #[cfg(freebsdlike)]
125 UF_NOUNLINK;
126 #[cfg(any(target_os = "freebsd"))]
129 UF_OFFLINE;
130 UF_OPAQUE;
132 #[cfg(any(target_os = "freebsd"))]
134 UF_READONLY;
135 #[cfg(any(target_os = "freebsd"))]
137 UF_REPARSE;
138 UF_SETTABLE;
140 #[cfg(any(target_os = "freebsd"))]
142 UF_SPARSE;
143 #[cfg(any(target_os = "freebsd"))]
146 UF_SYSTEM;
147 #[cfg(apple_targets)]
149 UF_TRACKED;
150 #[cfg(any(target_os = "dragonfly"))]
151 UF_XLINK;
152 }
153}
154
155pub 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#[cfg(not(any(apple_targets, target_os = "redox", target_os = "haiku")))]
171pub 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")]
192pub const fn major(dev: dev_t) -> u64 {
193 ((dev >> 32) & 0xffff_f000) | ((dev >> 8) & 0x0000_0fff)
194}
195
196#[cfg(target_os = "linux")]
197pub const fn minor(dev: dev_t) -> u64 {
198 ((dev >> 12) & 0xffff_ff00) | ((dev) & 0x0000_00ff)
199}
200
201#[cfg(target_os = "linux")]
202pub 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
209pub 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
214pub 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
225pub 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
236pub 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"))]
246pub 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
267pub 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#[derive(Clone, Copy, Debug)]
280pub enum FchmodatFlags {
281 FollowSymlink,
282 NoFollowSymlink,
283}
284
285#[cfg(not(target_os = "redox"))]
302pub 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
324pub 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(), ×[0])
342 })?;
343
344 Errno::result(res).map(drop)
345}
346
347#[cfg(any(
358 target_os = "linux",
359 target_os = "haiku",
360 apple_targets,
361 target_os = "freebsd",
362 target_os = "netbsd"
363))]
364pub 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(), ×[0])
372 })?;
373
374 Errno::result(res).map(drop)
375}
376
377#[inline]
386pub 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, ×[0]) };
389
390 Errno::result(res).map(drop)
391}
392
393#[derive(Clone, Copy, Debug)]
396pub enum UtimensatFlags {
397 FollowSymlink,
398 NoFollowSymlink,
399}
400
401#[cfg(not(target_os = "redox"))]
421pub 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 ×[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"))]
446pub 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}