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