1 | #[cfg (any(target_os = "macos" , target_os = "ios" , target_os = "openbsd" ))] |
2 | pub use libc::c_uint; |
3 | #[cfg (any( |
4 | target_os = "netbsd" , |
5 | target_os = "freebsd" , |
6 | target_os = "dragonfly" |
7 | ))] |
8 | pub use libc::c_ulong; |
9 | pub use libc::stat as FileStat; |
10 | pub use libc::{dev_t, mode_t}; |
11 | |
12 | #[cfg (not(target_os = "redox" ))] |
13 | use crate::fcntl::{at_rawfd, AtFlags}; |
14 | use crate::sys::time::{TimeSpec, TimeVal}; |
15 | use crate::{errno::Errno, NixPath, Result}; |
16 | use std::mem; |
17 | use std::os::unix::io::RawFd; |
18 | |
19 | libc_bitflags!( |
20 | /// "File type" flags for `mknod` and related functions. |
21 | pub struct SFlag: mode_t { |
22 | S_IFIFO; |
23 | S_IFCHR; |
24 | S_IFDIR; |
25 | S_IFBLK; |
26 | S_IFREG; |
27 | S_IFLNK; |
28 | S_IFSOCK; |
29 | S_IFMT; |
30 | } |
31 | ); |
32 | |
33 | libc_bitflags! { |
34 | /// "File mode / permissions" flags. |
35 | pub struct Mode: mode_t { |
36 | /// Read, write and execute for owner. |
37 | S_IRWXU; |
38 | /// Read for owner. |
39 | S_IRUSR; |
40 | /// Write for owner. |
41 | S_IWUSR; |
42 | /// Execute for owner. |
43 | S_IXUSR; |
44 | /// Read write and execute for group. |
45 | S_IRWXG; |
46 | /// Read fr group. |
47 | S_IRGRP; |
48 | /// Write for group. |
49 | S_IWGRP; |
50 | /// Execute for group. |
51 | S_IXGRP; |
52 | /// Read, write and execute for other. |
53 | S_IRWXO; |
54 | /// Read for other. |
55 | S_IROTH; |
56 | /// Write for other. |
57 | S_IWOTH; |
58 | /// Execute for other. |
59 | S_IXOTH; |
60 | /// Set user id on execution. |
61 | S_ISUID as mode_t; |
62 | /// Set group id on execution. |
63 | S_ISGID as mode_t; |
64 | S_ISVTX as mode_t; |
65 | } |
66 | } |
67 | |
68 | #[cfg (any(target_os = "macos" , target_os = "ios" , target_os = "openbsd" ))] |
69 | pub type type_of_file_flag = c_uint; |
70 | #[cfg (any( |
71 | target_os = "netbsd" , |
72 | target_os = "freebsd" , |
73 | target_os = "dragonfly" |
74 | ))] |
75 | pub type type_of_file_flag = c_ulong; |
76 | |
77 | #[cfg (any( |
78 | target_os = "openbsd" , |
79 | target_os = "netbsd" , |
80 | target_os = "freebsd" , |
81 | target_os = "dragonfly" , |
82 | target_os = "macos" , |
83 | target_os = "ios" |
84 | ))] |
85 | libc_bitflags! { |
86 | /// File flags. |
87 | #[cfg_attr (docsrs, doc(cfg(all())))] |
88 | pub struct FileFlag: type_of_file_flag { |
89 | /// The file may only be appended to. |
90 | SF_APPEND; |
91 | /// The file has been archived. |
92 | SF_ARCHIVED; |
93 | #[cfg(any(target_os = "dragonfly" ))] |
94 | SF_CACHE; |
95 | /// The file may not be changed. |
96 | SF_IMMUTABLE; |
97 | /// Indicates a WAPBL journal file. |
98 | #[cfg(any(target_os = "netbsd" ))] |
99 | SF_LOG; |
100 | /// Do not retain history for file |
101 | #[cfg(any(target_os = "dragonfly" ))] |
102 | SF_NOHISTORY; |
103 | /// The file may not be renamed or deleted. |
104 | #[cfg(any(target_os = "freebsd" , target_os = "dragonfly" ))] |
105 | SF_NOUNLINK; |
106 | /// Mask of superuser changeable flags |
107 | SF_SETTABLE; |
108 | /// Snapshot is invalid. |
109 | #[cfg(any(target_os = "netbsd" ))] |
110 | SF_SNAPINVAL; |
111 | /// The file is a snapshot file. |
112 | #[cfg(any(target_os = "netbsd" , target_os = "freebsd" ))] |
113 | SF_SNAPSHOT; |
114 | #[cfg(any(target_os = "dragonfly" ))] |
115 | SF_XLINK; |
116 | /// The file may only be appended to. |
117 | UF_APPEND; |
118 | /// The file needs to be archived. |
119 | #[cfg(any(target_os = "freebsd" ))] |
120 | UF_ARCHIVE; |
121 | #[cfg(any(target_os = "dragonfly" ))] |
122 | UF_CACHE; |
123 | /// File is compressed at the file system level. |
124 | #[cfg(any(target_os = "macos" , target_os = "ios" ))] |
125 | UF_COMPRESSED; |
126 | /// The file may be hidden from directory listings at the application's |
127 | /// discretion. |
128 | #[cfg(any( |
129 | target_os = "freebsd" , |
130 | target_os = "macos" , |
131 | target_os = "ios" , |
132 | ))] |
133 | UF_HIDDEN; |
134 | /// The file may not be changed. |
135 | UF_IMMUTABLE; |
136 | /// Do not dump the file. |
137 | UF_NODUMP; |
138 | #[cfg(any(target_os = "dragonfly" ))] |
139 | UF_NOHISTORY; |
140 | /// The file may not be renamed or deleted. |
141 | #[cfg(any(target_os = "freebsd" , target_os = "dragonfly" ))] |
142 | UF_NOUNLINK; |
143 | /// The file is offline, or has the Windows and CIFS |
144 | /// `FILE_ATTRIBUTE_OFFLINE` attribute. |
145 | #[cfg(any(target_os = "freebsd" ))] |
146 | UF_OFFLINE; |
147 | /// The directory is opaque when viewed through a union stack. |
148 | UF_OPAQUE; |
149 | /// The file is read only, and may not be written or appended. |
150 | #[cfg(any(target_os = "freebsd" ))] |
151 | UF_READONLY; |
152 | /// The file contains a Windows reparse point. |
153 | #[cfg(any(target_os = "freebsd" ))] |
154 | UF_REPARSE; |
155 | /// Mask of owner changeable flags. |
156 | UF_SETTABLE; |
157 | /// The file has the Windows `FILE_ATTRIBUTE_SPARSE_FILE` attribute. |
158 | #[cfg(any(target_os = "freebsd" ))] |
159 | UF_SPARSE; |
160 | /// The file has the DOS, Windows and CIFS `FILE_ATTRIBUTE_SYSTEM` |
161 | /// attribute. |
162 | #[cfg(any(target_os = "freebsd" ))] |
163 | UF_SYSTEM; |
164 | /// File renames and deletes are tracked. |
165 | #[cfg(any(target_os = "macos" , target_os = "ios" ))] |
166 | UF_TRACKED; |
167 | #[cfg(any(target_os = "dragonfly" ))] |
168 | UF_XLINK; |
169 | } |
170 | } |
171 | |
172 | /// Create a special or ordinary file, by pathname. |
173 | pub fn mknod<P: ?Sized + NixPath>( |
174 | path: &P, |
175 | kind: SFlag, |
176 | perm: Mode, |
177 | dev: dev_t, |
178 | ) -> Result<()> { |
179 | let res: i32 = path.with_nix_path(|cstr: &CStr| unsafe { |
180 | libc::mknod(pathname:cstr.as_ptr(), mode:kind.bits | perm.bits() as mode_t, dev) |
181 | })?; |
182 | |
183 | Errno::result(res).map(op:drop) |
184 | } |
185 | |
186 | /// Create a special or ordinary file, relative to a given directory. |
187 | #[cfg (not(any( |
188 | target_os = "ios" , |
189 | target_os = "macos" , |
190 | target_os = "redox" , |
191 | target_os = "haiku" |
192 | )))] |
193 | #[cfg_attr (docsrs, doc(cfg(all())))] |
194 | pub fn mknodat<P: ?Sized + NixPath>( |
195 | dirfd: RawFd, |
196 | path: &P, |
197 | kind: SFlag, |
198 | perm: Mode, |
199 | dev: dev_t, |
200 | ) -> Result<()> { |
201 | let res: i32 = path.with_nix_path(|cstr: &CStr| unsafe { |
202 | libc::mknodat( |
203 | dirfd, |
204 | pathname:cstr.as_ptr(), |
205 | mode:kind.bits | perm.bits() as mode_t, |
206 | dev, |
207 | ) |
208 | })?; |
209 | |
210 | Errno::result(res).map(op:drop) |
211 | } |
212 | |
213 | #[cfg (target_os = "linux" )] |
214 | #[cfg_attr (docsrs, doc(cfg(all())))] |
215 | pub const fn major(dev: dev_t) -> u64 { |
216 | ((dev >> 32) & 0xffff_f000) | ((dev >> 8) & 0x0000_0fff) |
217 | } |
218 | |
219 | #[cfg (target_os = "linux" )] |
220 | #[cfg_attr (docsrs, doc(cfg(all())))] |
221 | pub const fn minor(dev: dev_t) -> u64 { |
222 | ((dev >> 12) & 0xffff_ff00) | ((dev) & 0x0000_00ff) |
223 | } |
224 | |
225 | #[cfg (target_os = "linux" )] |
226 | #[cfg_attr (docsrs, doc(cfg(all())))] |
227 | pub const fn makedev(major: u64, minor: u64) -> dev_t { |
228 | ((major & 0xffff_f000) << 32) |
229 | | ((major & 0x0000_0fff) << 8) |
230 | | ((minor & 0xffff_ff00) << 12) |
231 | | (minor & 0x0000_00ff) |
232 | } |
233 | |
234 | pub fn umask(mode: Mode) -> Mode { |
235 | let prev: u32 = unsafe { libc::umask(mask:mode.bits() as mode_t) }; |
236 | Mode::from_bits(prev).expect(msg:"[BUG] umask returned invalid Mode" ) |
237 | } |
238 | |
239 | pub fn stat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> { |
240 | let mut dst: MaybeUninit = mem::MaybeUninit::uninit(); |
241 | let res: i32 = path.with_nix_path(|cstr: &CStr| unsafe { |
242 | libc::stat(path:cstr.as_ptr(), buf:dst.as_mut_ptr()) |
243 | })?; |
244 | |
245 | Errno::result(res)?; |
246 | |
247 | Ok(unsafe { dst.assume_init() }) |
248 | } |
249 | |
250 | pub fn lstat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> { |
251 | let mut dst: MaybeUninit = mem::MaybeUninit::uninit(); |
252 | let res: i32 = path.with_nix_path(|cstr: &CStr| unsafe { |
253 | libc::lstat(path:cstr.as_ptr(), buf:dst.as_mut_ptr()) |
254 | })?; |
255 | |
256 | Errno::result(res)?; |
257 | |
258 | Ok(unsafe { dst.assume_init() }) |
259 | } |
260 | |
261 | pub fn fstat(fd: RawFd) -> Result<FileStat> { |
262 | let mut dst: MaybeUninit = mem::MaybeUninit::uninit(); |
263 | let res: i32 = unsafe { libc::fstat(fildes:fd, buf:dst.as_mut_ptr()) }; |
264 | |
265 | Errno::result(res)?; |
266 | |
267 | Ok(unsafe { dst.assume_init() }) |
268 | } |
269 | |
270 | #[cfg (not(target_os = "redox" ))] |
271 | #[cfg_attr (docsrs, doc(cfg(all())))] |
272 | pub fn fstatat<P: ?Sized + NixPath>( |
273 | dirfd: RawFd, |
274 | pathname: &P, |
275 | f: AtFlags, |
276 | ) -> Result<FileStat> { |
277 | let mut dst: MaybeUninit = mem::MaybeUninit::uninit(); |
278 | let res: i32 = pathname.with_nix_path(|cstr: &CStr| unsafe { |
279 | libc::fstatat( |
280 | dirfd, |
281 | pathname:cstr.as_ptr(), |
282 | buf:dst.as_mut_ptr(), |
283 | flags:f.bits() as libc::c_int, |
284 | ) |
285 | })?; |
286 | |
287 | Errno::result(res)?; |
288 | |
289 | Ok(unsafe { dst.assume_init() }) |
290 | } |
291 | |
292 | /// Change the file permission bits of the file specified by a file descriptor. |
293 | /// |
294 | /// # References |
295 | /// |
296 | /// [fchmod(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html). |
297 | pub fn fchmod(fd: RawFd, mode: Mode) -> Result<()> { |
298 | let res: i32 = unsafe { libc::fchmod(fd, mode:mode.bits() as mode_t) }; |
299 | |
300 | Errno::result(res).map(op:drop) |
301 | } |
302 | |
303 | /// Flags for `fchmodat` function. |
304 | #[derive (Clone, Copy, Debug)] |
305 | pub enum FchmodatFlags { |
306 | FollowSymlink, |
307 | NoFollowSymlink, |
308 | } |
309 | |
310 | /// Change the file permission bits. |
311 | /// |
312 | /// The file to be changed is determined relative to the directory associated |
313 | /// with the file descriptor `dirfd` or the current working directory |
314 | /// if `dirfd` is `None`. |
315 | /// |
316 | /// If `flag` is `FchmodatFlags::NoFollowSymlink` and `path` names a symbolic link, |
317 | /// then the mode of the symbolic link is changed. |
318 | /// |
319 | /// `fchmodat(None, path, mode, FchmodatFlags::FollowSymlink)` is identical to |
320 | /// a call `libc::chmod(path, mode)`. That's why `chmod` is unimplemented |
321 | /// in the `nix` crate. |
322 | /// |
323 | /// # References |
324 | /// |
325 | /// [fchmodat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html). |
326 | #[cfg (not(target_os = "redox" ))] |
327 | #[cfg_attr (docsrs, doc(cfg(all())))] |
328 | pub fn fchmodat<P: ?Sized + NixPath>( |
329 | dirfd: Option<RawFd>, |
330 | path: &P, |
331 | mode: Mode, |
332 | flag: FchmodatFlags, |
333 | ) -> Result<()> { |
334 | let atflag: AtFlags = match flag { |
335 | FchmodatFlags::FollowSymlink => AtFlags::empty(), |
336 | FchmodatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW, |
337 | }; |
338 | let res: i32 = path.with_nix_path(|cstr: &CStr| unsafe { |
339 | libc::fchmodat( |
340 | dirfd:at_rawfd(dirfd), |
341 | pathname:cstr.as_ptr(), |
342 | mode:mode.bits() as mode_t, |
343 | flags:atflag.bits() as libc::c_int, |
344 | ) |
345 | })?; |
346 | |
347 | Errno::result(res).map(op:drop) |
348 | } |
349 | |
350 | /// Change the access and modification times of a file. |
351 | /// |
352 | /// `utimes(path, times)` is identical to |
353 | /// `utimensat(None, path, times, UtimensatFlags::FollowSymlink)`. The former |
354 | /// is a deprecated API so prefer using the latter if the platforms you care |
355 | /// about support it. |
356 | /// |
357 | /// # References |
358 | /// |
359 | /// [utimes(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html). |
360 | pub fn utimes<P: ?Sized + NixPath>( |
361 | path: &P, |
362 | atime: &TimeVal, |
363 | mtime: &TimeVal, |
364 | ) -> Result<()> { |
365 | let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()]; |
366 | let res: i32 = path.with_nix_path(|cstr: &CStr| unsafe { |
367 | libc::utimes(filename:cstr.as_ptr(), ×[0]) |
368 | })?; |
369 | |
370 | Errno::result(res).map(op:drop) |
371 | } |
372 | |
373 | /// Change the access and modification times of a file without following symlinks. |
374 | /// |
375 | /// `lutimes(path, times)` is identical to |
376 | /// `utimensat(None, path, times, UtimensatFlags::NoFollowSymlink)`. The former |
377 | /// is a deprecated API so prefer using the latter if the platforms you care |
378 | /// about support it. |
379 | /// |
380 | /// # References |
381 | /// |
382 | /// [lutimes(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lutimes.html). |
383 | #[cfg (any( |
384 | target_os = "linux" , |
385 | target_os = "haiku" , |
386 | target_os = "ios" , |
387 | target_os = "macos" , |
388 | target_os = "freebsd" , |
389 | target_os = "netbsd" |
390 | ))] |
391 | #[cfg_attr (docsrs, doc(cfg(all())))] |
392 | pub fn lutimes<P: ?Sized + NixPath>( |
393 | path: &P, |
394 | atime: &TimeVal, |
395 | mtime: &TimeVal, |
396 | ) -> Result<()> { |
397 | let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()]; |
398 | let res: i32 = path.with_nix_path(|cstr: &CStr| unsafe { |
399 | libc::lutimes(file:cstr.as_ptr(), ×[0]) |
400 | })?; |
401 | |
402 | Errno::result(res).map(op:drop) |
403 | } |
404 | |
405 | /// Change the access and modification times of the file specified by a file descriptor. |
406 | /// |
407 | /// # References |
408 | /// |
409 | /// [futimens(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html). |
410 | #[inline ] |
411 | pub fn futimens(fd: RawFd, atime: &TimeSpec, mtime: &TimeSpec) -> Result<()> { |
412 | let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()]; |
413 | let res: i32 = unsafe { libc::futimens(fd, ×[0]) }; |
414 | |
415 | Errno::result(res).map(op:drop) |
416 | } |
417 | |
418 | /// Flags for `utimensat` function. |
419 | // TODO: replace with fcntl::AtFlags |
420 | #[derive (Clone, Copy, Debug)] |
421 | pub enum UtimensatFlags { |
422 | FollowSymlink, |
423 | NoFollowSymlink, |
424 | } |
425 | |
426 | /// Change the access and modification times of a file. |
427 | /// |
428 | /// The file to be changed is determined relative to the directory associated |
429 | /// with the file descriptor `dirfd` or the current working directory |
430 | /// if `dirfd` is `None`. |
431 | /// |
432 | /// If `flag` is `UtimensatFlags::NoFollowSymlink` and `path` names a symbolic link, |
433 | /// then the mode of the symbolic link is changed. |
434 | /// |
435 | /// `utimensat(None, path, times, UtimensatFlags::FollowSymlink)` is identical to |
436 | /// `utimes(path, times)`. The latter is a deprecated API so prefer using the |
437 | /// former if the platforms you care about support it. |
438 | /// |
439 | /// # References |
440 | /// |
441 | /// [utimensat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimens.html). |
442 | #[cfg (not(target_os = "redox" ))] |
443 | #[cfg_attr (docsrs, doc(cfg(all())))] |
444 | pub fn utimensat<P: ?Sized + NixPath>( |
445 | dirfd: Option<RawFd>, |
446 | path: &P, |
447 | atime: &TimeSpec, |
448 | mtime: &TimeSpec, |
449 | flag: UtimensatFlags, |
450 | ) -> Result<()> { |
451 | let atflag: AtFlags = match flag { |
452 | UtimensatFlags::FollowSymlink => AtFlags::empty(), |
453 | UtimensatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW, |
454 | }; |
455 | let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()]; |
456 | let res: i32 = path.with_nix_path(|cstr: &CStr| unsafe { |
457 | libc::utimensat( |
458 | dirfd:at_rawfd(dirfd), |
459 | path:cstr.as_ptr(), |
460 | ×[0], |
461 | flag:atflag.bits() as libc::c_int, |
462 | ) |
463 | })?; |
464 | |
465 | Errno::result(res).map(op:drop) |
466 | } |
467 | |
468 | #[cfg (not(target_os = "redox" ))] |
469 | #[cfg_attr (docsrs, doc(cfg(all())))] |
470 | pub fn mkdirat<P: ?Sized + NixPath>( |
471 | fd: RawFd, |
472 | path: &P, |
473 | mode: Mode, |
474 | ) -> Result<()> { |
475 | let res: i32 = path.with_nix_path(|cstr: &CStr| unsafe { |
476 | libc::mkdirat(dirfd:fd, pathname:cstr.as_ptr(), mode:mode.bits() as mode_t) |
477 | })?; |
478 | |
479 | Errno::result(res).map(op:drop) |
480 | } |
481 | |