1 | //! linux_raw syscalls supporting `rustix::fs`. |
2 | //! |
3 | //! # Safety |
4 | //! |
5 | //! See the `rustix::backend` module documentation for details. |
6 | #![allow (unsafe_code)] |
7 | #![allow (clippy::undocumented_unsafe_blocks)] |
8 | |
9 | use crate::backend::c; |
10 | use crate::backend::conv::fs::oflags_for_open_how; |
11 | #[cfg (any( |
12 | not(feature = "linux_4_11" ), |
13 | target_arch = "aarch64" , |
14 | target_arch = "riscv64" , |
15 | target_arch = "mips" , |
16 | target_arch = "mips32r6" , |
17 | ))] |
18 | use crate::backend::conv::zero; |
19 | use crate::backend::conv::{ |
20 | by_ref, c_int, c_uint, dev_t, opt_mut, pass_usize, raw_fd, ret, ret_c_int, ret_c_uint, |
21 | ret_infallible, ret_owned_fd, ret_usize, size_of, slice, slice_mut, |
22 | }; |
23 | #[cfg (target_pointer_width = "64" )] |
24 | use crate::backend::conv::{loff_t, loff_t_from_u64, ret_u64}; |
25 | use crate::fd::{BorrowedFd, OwnedFd}; |
26 | use crate::ffi::CStr; |
27 | #[cfg (any(target_arch = "aarch64" , target_arch = "riscv64" ))] |
28 | use crate::fs::CWD; |
29 | use crate::fs::{ |
30 | inotify, Access, Advice, AtFlags, FallocateFlags, FileType, FlockOperation, Fsid, Gid, |
31 | MemfdFlags, Mode, OFlags, RenameFlags, ResolveFlags, SealFlags, SeekFrom, Stat, StatFs, |
32 | StatVfs, StatVfsMountFlags, Statx, StatxFlags, Timestamps, Uid, XattrFlags, |
33 | }; |
34 | use crate::io; |
35 | use core::mem::MaybeUninit; |
36 | use core::num::NonZeroU64; |
37 | #[cfg (any(target_arch = "mips64" , target_arch = "mips64r6" ))] |
38 | use linux_raw_sys::general::stat as linux_stat64; |
39 | use linux_raw_sys::general::{ |
40 | open_how, AT_EACCESS, AT_FDCWD, AT_REMOVEDIR, AT_SYMLINK_NOFOLLOW, F_ADD_SEALS, F_GETFL, |
41 | F_GET_SEALS, F_SETFL, SEEK_CUR, SEEK_DATA, SEEK_END, SEEK_HOLE, SEEK_SET, STATX__RESERVED, |
42 | }; |
43 | #[cfg (target_pointer_width = "32" )] |
44 | use { |
45 | crate::backend::conv::{hi, lo, slice_just_addr}, |
46 | linux_raw_sys::general::stat64 as linux_stat64, |
47 | linux_raw_sys::general::timespec as __kernel_old_timespec, |
48 | }; |
49 | |
50 | #[inline ] |
51 | pub(crate) fn open(path: &CStr, flags: OFlags, mode: Mode) -> io::Result<OwnedFd> { |
52 | // Always enable support for large files. |
53 | let flags: OFlags = flags | OFlags::LARGEFILE; |
54 | |
55 | #[cfg (any(target_arch = "aarch64" , target_arch = "riscv64" ))] |
56 | { |
57 | openat(CWD, path, flags, mode) |
58 | } |
59 | #[cfg (not(any(target_arch = "aarch64" , target_arch = "riscv64" )))] |
60 | unsafe { |
61 | ret_owned_fd(raw:syscall_readonly!(__NR_open, path, flags, mode)) |
62 | } |
63 | } |
64 | |
65 | #[inline ] |
66 | pub(crate) fn openat( |
67 | dirfd: BorrowedFd<'_>, |
68 | path: &CStr, |
69 | flags: OFlags, |
70 | mode: Mode, |
71 | ) -> io::Result<OwnedFd> { |
72 | // Always enable support for large files. |
73 | let flags: OFlags = flags | OFlags::LARGEFILE; |
74 | |
75 | unsafe { ret_owned_fd(raw:syscall_readonly!(__NR_openat, dirfd, path, flags, mode)) } |
76 | } |
77 | |
78 | #[inline ] |
79 | pub(crate) fn openat2( |
80 | dirfd: BorrowedFd<'_>, |
81 | path: &CStr, |
82 | mut flags: OFlags, |
83 | mode: Mode, |
84 | resolve: ResolveFlags, |
85 | ) -> io::Result<OwnedFd> { |
86 | // Enable support for large files, but not with `O_PATH` because |
87 | // `openat2` doesn't like those flags together. |
88 | if !flags.contains(OFlags::PATH) { |
89 | flags |= OFlags::from_bits_retain(bits:c::O_LARGEFILE); |
90 | } |
91 | |
92 | unsafe { |
93 | ret_owned_fd(raw:syscall_readonly!( |
94 | __NR_openat2, |
95 | dirfd, |
96 | path, |
97 | by_ref(&open_how { |
98 | flags: oflags_for_open_how(flags), |
99 | mode: u64::from(mode.bits()), |
100 | resolve: resolve.bits(), |
101 | }), |
102 | size_of::<open_how, _>() |
103 | )) |
104 | } |
105 | } |
106 | |
107 | #[inline ] |
108 | pub(crate) fn chmod(path: &CStr, mode: Mode) -> io::Result<()> { |
109 | unsafe { |
110 | ret(raw:syscall_readonly!( |
111 | __NR_fchmodat, |
112 | raw_fd(AT_FDCWD), |
113 | path, |
114 | mode |
115 | )) |
116 | } |
117 | } |
118 | |
119 | #[inline ] |
120 | pub(crate) fn chmodat( |
121 | dirfd: BorrowedFd<'_>, |
122 | path: &CStr, |
123 | mode: Mode, |
124 | flags: AtFlags, |
125 | ) -> io::Result<()> { |
126 | if flags == AtFlags::SYMLINK_NOFOLLOW { |
127 | return Err(io::Errno::OPNOTSUPP); |
128 | } |
129 | if !flags.is_empty() { |
130 | return Err(io::Errno::INVAL); |
131 | } |
132 | unsafe { ret(raw:syscall_readonly!(__NR_fchmodat, dirfd, path, mode)) } |
133 | } |
134 | |
135 | #[inline ] |
136 | pub(crate) fn fchmod(fd: BorrowedFd<'_>, mode: Mode) -> io::Result<()> { |
137 | unsafe { ret(raw:syscall_readonly!(__NR_fchmod, fd, mode)) } |
138 | } |
139 | |
140 | #[inline ] |
141 | pub(crate) fn chownat( |
142 | dirfd: BorrowedFd<'_>, |
143 | path: &CStr, |
144 | owner: Option<Uid>, |
145 | group: Option<Gid>, |
146 | flags: AtFlags, |
147 | ) -> io::Result<()> { |
148 | unsafe { |
149 | let (ow: u32, gr: u32) = crate::ugid::translate_fchown_args(owner, group); |
150 | ret(raw:syscall_readonly!( |
151 | __NR_fchownat, |
152 | dirfd, |
153 | path, |
154 | c_uint(ow), |
155 | c_uint(gr), |
156 | flags |
157 | )) |
158 | } |
159 | } |
160 | |
161 | #[inline ] |
162 | pub(crate) fn chown(path: &CStr, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()> { |
163 | // Most architectures have a `chown` syscall. |
164 | #[cfg (not(any(target_arch = "aarch64" , target_arch = "riscv64" )))] |
165 | unsafe { |
166 | let (ow: u32, gr: u32) = crate::ugid::translate_fchown_args(owner, group); |
167 | ret(raw:syscall_readonly!(__NR_chown, path, c_uint(ow), c_uint(gr))) |
168 | } |
169 | |
170 | // Aarch64 and RISC-V don't, so use `fchownat`. |
171 | #[cfg (any(target_arch = "aarch64" , target_arch = "riscv64" ))] |
172 | unsafe { |
173 | let (ow, gr) = crate::ugid::translate_fchown_args(owner, group); |
174 | ret(syscall_readonly!( |
175 | __NR_fchownat, |
176 | raw_fd(AT_FDCWD), |
177 | path, |
178 | c_uint(ow), |
179 | c_uint(gr), |
180 | zero() |
181 | )) |
182 | } |
183 | } |
184 | |
185 | #[inline ] |
186 | pub(crate) fn fchown(fd: BorrowedFd<'_>, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()> { |
187 | unsafe { |
188 | let (ow: u32, gr: u32) = crate::ugid::translate_fchown_args(owner, group); |
189 | ret(raw:syscall_readonly!(__NR_fchown, fd, c_uint(ow), c_uint(gr))) |
190 | } |
191 | } |
192 | |
193 | #[inline ] |
194 | pub(crate) fn mknodat( |
195 | dirfd: BorrowedFd<'_>, |
196 | path: &CStr, |
197 | file_type: FileType, |
198 | mode: Mode, |
199 | dev: u64, |
200 | ) -> io::Result<()> { |
201 | #[cfg (target_pointer_width = "32" )] |
202 | unsafe { |
203 | ret(syscall_readonly!( |
204 | __NR_mknodat, |
205 | dirfd, |
206 | path, |
207 | (mode, file_type), |
208 | dev_t(dev)? |
209 | )) |
210 | } |
211 | #[cfg (target_pointer_width = "64" )] |
212 | unsafe { |
213 | ret(raw:syscall_readonly!( |
214 | __NR_mknodat, |
215 | dirfd, |
216 | path, |
217 | (mode, file_type), |
218 | dev_t(dev) |
219 | )) |
220 | } |
221 | } |
222 | |
223 | #[inline ] |
224 | pub(crate) fn seek(fd: BorrowedFd<'_>, pos: SeekFrom) -> io::Result<u64> { |
225 | let (whence: u32, offset: i64) = match pos { |
226 | SeekFrom::Start(pos: u64) => { |
227 | let pos: u64 = pos; |
228 | // Silently cast; we'll get `EINVAL` if the value is negative. |
229 | (SEEK_SET, pos as i64) |
230 | } |
231 | SeekFrom::End(offset: i64) => (SEEK_END, offset), |
232 | SeekFrom::Current(offset: i64) => (SEEK_CUR, offset), |
233 | SeekFrom::Data(pos: u64) => { |
234 | let pos: u64 = pos; |
235 | // Silently cast; we'll get `EINVAL` if the value is negative. |
236 | (SEEK_DATA, pos as i64) |
237 | } |
238 | SeekFrom::Hole(pos: u64) => { |
239 | let pos: u64 = pos; |
240 | // Silently cast; we'll get `EINVAL` if the value is negative. |
241 | (SEEK_HOLE, pos as i64) |
242 | } |
243 | }; |
244 | _seek(fd, offset, whence) |
245 | } |
246 | |
247 | #[inline ] |
248 | pub(crate) fn _seek(fd: BorrowedFd<'_>, offset: i64, whence: c::c_uint) -> io::Result<u64> { |
249 | #[cfg (target_pointer_width = "32" )] |
250 | unsafe { |
251 | let mut result = MaybeUninit::<u64>::uninit(); |
252 | ret(syscall!( |
253 | __NR__llseek, |
254 | fd, |
255 | // Don't use the hi/lo functions here because Linux's llseek |
256 | // takes its 64-bit argument differently from everything else. |
257 | pass_usize((offset >> 32) as usize), |
258 | pass_usize(offset as usize), |
259 | &mut result, |
260 | c_uint(whence) |
261 | ))?; |
262 | Ok(result.assume_init()) |
263 | } |
264 | #[cfg (target_pointer_width = "64" )] |
265 | unsafe { |
266 | ret_u64(syscall_readonly!( |
267 | __NR_lseek, |
268 | fd, |
269 | loff_t(offset), |
270 | c_uint(whence) |
271 | )) |
272 | } |
273 | } |
274 | |
275 | #[inline ] |
276 | pub(crate) fn tell(fd: BorrowedFd<'_>) -> io::Result<u64> { |
277 | _seek(fd, 0, SEEK_CUR).map(|x: u64| x as u64) |
278 | } |
279 | |
280 | #[inline ] |
281 | pub(crate) fn ftruncate(fd: BorrowedFd<'_>, length: u64) -> io::Result<()> { |
282 | // <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm64/kernel/sys32.c?h=v6.13#n89> |
283 | #[cfg (all( |
284 | target_pointer_width = "32" , |
285 | any( |
286 | target_arch = "arm" , |
287 | target_arch = "mips" , |
288 | target_arch = "mips32r6" , |
289 | target_arch = "powerpc" |
290 | ), |
291 | ))] |
292 | unsafe { |
293 | ret(syscall_readonly!( |
294 | __NR_ftruncate64, |
295 | fd, |
296 | zero(), |
297 | hi(length), |
298 | lo(length) |
299 | )) |
300 | } |
301 | #[cfg (all( |
302 | target_pointer_width = "32" , |
303 | not(any( |
304 | target_arch = "arm" , |
305 | target_arch = "mips" , |
306 | target_arch = "mips32r6" , |
307 | target_arch = "powerpc" |
308 | )), |
309 | ))] |
310 | unsafe { |
311 | ret(syscall_readonly!( |
312 | __NR_ftruncate64, |
313 | fd, |
314 | hi(length), |
315 | lo(length) |
316 | )) |
317 | } |
318 | #[cfg (target_pointer_width = "64" )] |
319 | unsafe { |
320 | ret(syscall_readonly!( |
321 | __NR_ftruncate, |
322 | fd, |
323 | loff_t_from_u64(length) |
324 | )) |
325 | } |
326 | } |
327 | |
328 | #[inline ] |
329 | pub(crate) fn fallocate( |
330 | fd: BorrowedFd<'_>, |
331 | mode: FallocateFlags, |
332 | offset: u64, |
333 | len: u64, |
334 | ) -> io::Result<()> { |
335 | #[cfg (target_pointer_width = "32" )] |
336 | unsafe { |
337 | ret(syscall_readonly!( |
338 | __NR_fallocate, |
339 | fd, |
340 | mode, |
341 | hi(offset), |
342 | lo(offset), |
343 | hi(len), |
344 | lo(len) |
345 | )) |
346 | } |
347 | #[cfg (target_pointer_width = "64" )] |
348 | unsafe { |
349 | ret(raw:syscall_readonly!( |
350 | __NR_fallocate, |
351 | fd, |
352 | mode, |
353 | loff_t_from_u64(offset), |
354 | loff_t_from_u64(len) |
355 | )) |
356 | } |
357 | } |
358 | |
359 | #[inline ] |
360 | pub(crate) fn fadvise( |
361 | fd: BorrowedFd<'_>, |
362 | pos: u64, |
363 | len: Option<NonZeroU64>, |
364 | advice: Advice, |
365 | ) -> io::Result<()> { |
366 | let len = match len { |
367 | None => 0, |
368 | Some(len) => len.get(), |
369 | }; |
370 | |
371 | // On ARM, the arguments are reordered so that the `len` and `pos` argument |
372 | // pairs are aligned. And ARM has a custom syscall code for this. |
373 | #[cfg (target_arch = "arm" )] |
374 | unsafe { |
375 | ret(syscall_readonly!( |
376 | __NR_arm_fadvise64_64, |
377 | fd, |
378 | advice, |
379 | hi(pos), |
380 | lo(pos), |
381 | hi(len), |
382 | lo(len) |
383 | )) |
384 | } |
385 | |
386 | // On powerpc, the arguments are reordered as on ARM. |
387 | #[cfg (target_arch = "powerpc" )] |
388 | unsafe { |
389 | ret(syscall_readonly!( |
390 | __NR_fadvise64_64, |
391 | fd, |
392 | advice, |
393 | hi(pos), |
394 | lo(pos), |
395 | hi(len), |
396 | lo(len) |
397 | )) |
398 | } |
399 | |
400 | // On mips, the arguments are not reordered, and padding is inserted |
401 | // instead to ensure alignment. |
402 | #[cfg (any(target_arch = "mips" , target_arch = "mips32r6" ))] |
403 | unsafe { |
404 | ret(syscall_readonly!( |
405 | __NR_fadvise64, |
406 | fd, |
407 | zero(), |
408 | hi(pos), |
409 | lo(pos), |
410 | hi(len), |
411 | lo(len), |
412 | advice |
413 | )) |
414 | } |
415 | |
416 | // For all other 32-bit architectures, use `fadvise64_64` so that we get a |
417 | // 64-bit length. |
418 | #[cfg (all( |
419 | target_pointer_width = "32" , |
420 | not(any( |
421 | target_arch = "arm" , |
422 | target_arch = "mips" , |
423 | target_arch = "mips32r6" , |
424 | target_arch = "powerpc" |
425 | )), |
426 | ))] |
427 | unsafe { |
428 | ret(syscall_readonly!( |
429 | __NR_fadvise64_64, |
430 | fd, |
431 | hi(pos), |
432 | lo(pos), |
433 | hi(len), |
434 | lo(len), |
435 | advice |
436 | )) |
437 | } |
438 | |
439 | // On 64-bit architectures, use `fadvise64` which is sufficient. |
440 | #[cfg (target_pointer_width = "64" )] |
441 | unsafe { |
442 | ret(syscall_readonly!( |
443 | __NR_fadvise64, |
444 | fd, |
445 | loff_t_from_u64(pos), |
446 | loff_t_from_u64(len), |
447 | advice |
448 | )) |
449 | } |
450 | } |
451 | |
452 | #[inline ] |
453 | pub(crate) fn fsync(fd: BorrowedFd<'_>) -> io::Result<()> { |
454 | unsafe { ret(raw:syscall_readonly!(__NR_fsync, fd)) } |
455 | } |
456 | |
457 | #[inline ] |
458 | pub(crate) fn fdatasync(fd: BorrowedFd<'_>) -> io::Result<()> { |
459 | unsafe { ret(raw:syscall_readonly!(__NR_fdatasync, fd)) } |
460 | } |
461 | |
462 | #[inline ] |
463 | pub(crate) fn flock(fd: BorrowedFd<'_>, operation: FlockOperation) -> io::Result<()> { |
464 | unsafe { |
465 | ret(raw:syscall_readonly!( |
466 | __NR_flock, |
467 | fd, |
468 | c_uint(operation as c::c_uint) |
469 | )) |
470 | } |
471 | } |
472 | |
473 | #[inline ] |
474 | pub(crate) fn syncfs(fd: BorrowedFd<'_>) -> io::Result<()> { |
475 | unsafe { ret(raw:syscall_readonly!(__NR_syncfs, fd)) } |
476 | } |
477 | |
478 | #[inline ] |
479 | pub(crate) fn sync() { |
480 | unsafe { ret_infallible(raw:syscall_readonly!(__NR_sync)) } |
481 | } |
482 | |
483 | #[inline ] |
484 | pub(crate) fn fstat(fd: BorrowedFd<'_>) -> io::Result<Stat> { |
485 | // 32-bit and mips64 Linux: `struct stat64` is not y2038 compatible; use |
486 | // `statx`. |
487 | // |
488 | // And, some old platforms don't support `statx`, and some fail with a |
489 | // confusing error code, so we call `crate::fs::statx` to handle that. If |
490 | // `statx` isn't available, fall back to the buggy system call. |
491 | #[cfg (any( |
492 | target_pointer_width = "32" , |
493 | target_arch = "mips64" , |
494 | target_arch = "mips64r6" |
495 | ))] |
496 | { |
497 | match crate::fs::statx(fd, cstr!("" ), AtFlags::EMPTY_PATH, StatxFlags::BASIC_STATS) { |
498 | Ok(x) => statx_to_stat(x), |
499 | Err(io::Errno::NOSYS) => fstat_old(fd), |
500 | Err(err) => Err(err), |
501 | } |
502 | } |
503 | |
504 | #[cfg (all( |
505 | target_pointer_width = "64" , |
506 | not(target_arch = "mips64" ), |
507 | not(target_arch = "mips64r6" ) |
508 | ))] |
509 | unsafe { |
510 | let mut result = MaybeUninit::<Stat>::uninit(); |
511 | ret(syscall!(__NR_fstat, fd, &mut result))?; |
512 | Ok(result.assume_init()) |
513 | } |
514 | } |
515 | |
516 | #[cfg (any( |
517 | target_pointer_width = "32" , |
518 | target_arch = "mips64" , |
519 | target_arch = "mips64r6" , |
520 | ))] |
521 | fn fstat_old(fd: BorrowedFd<'_>) -> io::Result<Stat> { |
522 | let mut result = MaybeUninit::<linux_stat64>::uninit(); |
523 | |
524 | #[cfg (any(target_arch = "mips64" , target_arch = "mips64r6" ))] |
525 | unsafe { |
526 | ret(syscall!(__NR_fstat, fd, &mut result))?; |
527 | stat_to_stat(result.assume_init()) |
528 | } |
529 | |
530 | #[cfg (target_pointer_width = "32" )] |
531 | unsafe { |
532 | ret(syscall!(__NR_fstat64, fd, &mut result))?; |
533 | stat_to_stat(result.assume_init()) |
534 | } |
535 | } |
536 | |
537 | #[inline ] |
538 | pub(crate) fn stat(path: &CStr) -> io::Result<Stat> { |
539 | // See the comments in `fstat` about using `crate::fs::statx` here. |
540 | #[cfg (any( |
541 | target_pointer_width = "32" , |
542 | target_arch = "mips64" , |
543 | target_arch = "mips64r6" |
544 | ))] |
545 | { |
546 | match crate::fs::statx( |
547 | crate::fs::CWD, |
548 | path, |
549 | AtFlags::empty(), |
550 | StatxFlags::BASIC_STATS, |
551 | ) { |
552 | Ok(x) => statx_to_stat(x), |
553 | Err(io::Errno::NOSYS) => stat_old(path), |
554 | Err(err) => Err(err), |
555 | } |
556 | } |
557 | |
558 | #[cfg (all( |
559 | target_pointer_width = "64" , |
560 | not(target_arch = "mips64" ), |
561 | not(target_arch = "mips64r6" ), |
562 | ))] |
563 | unsafe { |
564 | let mut result = MaybeUninit::<Stat>::uninit(); |
565 | ret(syscall!( |
566 | __NR_newfstatat, |
567 | raw_fd(AT_FDCWD), |
568 | path, |
569 | &mut result, |
570 | c_uint(0) |
571 | ))?; |
572 | Ok(result.assume_init()) |
573 | } |
574 | } |
575 | |
576 | #[cfg (any( |
577 | target_pointer_width = "32" , |
578 | target_arch = "mips64" , |
579 | target_arch = "mips64r6" |
580 | ))] |
581 | fn stat_old(path: &CStr) -> io::Result<Stat> { |
582 | let mut result = MaybeUninit::<linux_stat64>::uninit(); |
583 | |
584 | #[cfg (any(target_arch = "mips64" , target_arch = "mips64r6" ))] |
585 | unsafe { |
586 | ret(syscall!( |
587 | __NR_newfstatat, |
588 | raw_fd(AT_FDCWD), |
589 | path, |
590 | &mut result, |
591 | c_uint(0) |
592 | ))?; |
593 | stat_to_stat(result.assume_init()) |
594 | } |
595 | |
596 | #[cfg (target_pointer_width = "32" )] |
597 | unsafe { |
598 | ret(syscall!( |
599 | __NR_fstatat64, |
600 | raw_fd(AT_FDCWD), |
601 | path, |
602 | &mut result, |
603 | c_uint(0) |
604 | ))?; |
605 | stat_to_stat(result.assume_init()) |
606 | } |
607 | } |
608 | |
609 | #[inline ] |
610 | pub(crate) fn statat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<Stat> { |
611 | // See the comments in `fstat` about using `crate::fs::statx` here. |
612 | #[cfg (any( |
613 | target_pointer_width = "32" , |
614 | target_arch = "mips64" , |
615 | target_arch = "mips64r6" |
616 | ))] |
617 | { |
618 | match crate::fs::statx(dirfd, path, flags, StatxFlags::BASIC_STATS) { |
619 | Ok(x) => statx_to_stat(x), |
620 | Err(io::Errno::NOSYS) => statat_old(dirfd, path, flags), |
621 | Err(err) => Err(err), |
622 | } |
623 | } |
624 | |
625 | #[cfg (all( |
626 | target_pointer_width = "64" , |
627 | not(target_arch = "mips64" ), |
628 | not(target_arch = "mips64r6" ), |
629 | ))] |
630 | unsafe { |
631 | let mut result = MaybeUninit::<Stat>::uninit(); |
632 | ret(syscall!(__NR_newfstatat, dirfd, path, &mut result, flags))?; |
633 | Ok(result.assume_init()) |
634 | } |
635 | } |
636 | |
637 | #[cfg (any( |
638 | target_pointer_width = "32" , |
639 | target_arch = "mips64" , |
640 | target_arch = "mips64r6" |
641 | ))] |
642 | fn statat_old(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<Stat> { |
643 | let mut result = MaybeUninit::<linux_stat64>::uninit(); |
644 | |
645 | #[cfg (any(target_arch = "mips64" , target_arch = "mips64r6" ))] |
646 | unsafe { |
647 | ret(syscall!(__NR_newfstatat, dirfd, path, &mut result, flags))?; |
648 | stat_to_stat(result.assume_init()) |
649 | } |
650 | |
651 | #[cfg (target_pointer_width = "32" )] |
652 | unsafe { |
653 | ret(syscall!(__NR_fstatat64, dirfd, path, &mut result, flags))?; |
654 | stat_to_stat(result.assume_init()) |
655 | } |
656 | } |
657 | |
658 | #[inline ] |
659 | pub(crate) fn lstat(path: &CStr) -> io::Result<Stat> { |
660 | // See the comments in `fstat` about using `crate::fs::statx` here. |
661 | #[cfg (any(target_pointer_width = "32" , target_arch = "mips64" ))] |
662 | { |
663 | match crate::fs::statx( |
664 | crate::fs::CWD, |
665 | path, |
666 | AtFlags::SYMLINK_NOFOLLOW, |
667 | StatxFlags::BASIC_STATS, |
668 | ) { |
669 | Ok(x) => statx_to_stat(x), |
670 | Err(io::Errno::NOSYS) => lstat_old(path), |
671 | Err(err) => Err(err), |
672 | } |
673 | } |
674 | |
675 | #[cfg (all(target_pointer_width = "64" , not(target_arch = "mips64" )))] |
676 | unsafe { |
677 | let mut result = MaybeUninit::<Stat>::uninit(); |
678 | ret(syscall!( |
679 | __NR_newfstatat, |
680 | raw_fd(AT_FDCWD), |
681 | path, |
682 | &mut result, |
683 | c_uint(AT_SYMLINK_NOFOLLOW) |
684 | ))?; |
685 | Ok(result.assume_init()) |
686 | } |
687 | } |
688 | |
689 | #[cfg (any(target_pointer_width = "32" , target_arch = "mips64" ))] |
690 | fn lstat_old(path: &CStr) -> io::Result<Stat> { |
691 | let mut result = MaybeUninit::<linux_stat64>::uninit(); |
692 | |
693 | #[cfg (any(target_arch = "mips64" , target_arch = "mips64r6" ))] |
694 | unsafe { |
695 | ret(syscall!( |
696 | __NR_newfstatat, |
697 | raw_fd(AT_FDCWD), |
698 | path, |
699 | &mut result, |
700 | c_uint(AT_SYMLINK_NOFOLLOW) |
701 | ))?; |
702 | stat_to_stat(result.assume_init()) |
703 | } |
704 | |
705 | #[cfg (target_pointer_width = "32" )] |
706 | unsafe { |
707 | ret(syscall!( |
708 | __NR_fstatat64, |
709 | raw_fd(AT_FDCWD), |
710 | path, |
711 | &mut result, |
712 | c_uint(AT_SYMLINK_NOFOLLOW) |
713 | ))?; |
714 | stat_to_stat(result.assume_init()) |
715 | } |
716 | } |
717 | |
718 | /// Convert from a Linux `statx` value to rustix's `Stat`. |
719 | #[cfg (any( |
720 | target_pointer_width = "32" , |
721 | target_arch = "mips64" , |
722 | target_arch = "mips64r6" |
723 | ))] |
724 | fn statx_to_stat(x: crate::fs::Statx) -> io::Result<Stat> { |
725 | Ok(Stat { |
726 | st_dev: crate::fs::makedev(x.stx_dev_major, x.stx_dev_minor), |
727 | st_mode: x.stx_mode.into(), |
728 | st_nlink: x.stx_nlink.into(), |
729 | st_uid: x.stx_uid.into(), |
730 | st_gid: x.stx_gid.into(), |
731 | st_rdev: crate::fs::makedev(x.stx_rdev_major, x.stx_rdev_minor), |
732 | st_size: x.stx_size.try_into().map_err(|_| io::Errno::OVERFLOW)?, |
733 | st_blksize: x.stx_blksize.into(), |
734 | st_blocks: x.stx_blocks.into(), |
735 | st_atime: i64::from(x.stx_atime.tv_sec), |
736 | st_atime_nsec: x.stx_atime.tv_nsec.into(), |
737 | st_mtime: i64::from(x.stx_mtime.tv_sec), |
738 | st_mtime_nsec: x.stx_mtime.tv_nsec.into(), |
739 | st_ctime: i64::from(x.stx_ctime.tv_sec), |
740 | st_ctime_nsec: x.stx_ctime.tv_nsec.into(), |
741 | st_ino: x.stx_ino.into(), |
742 | }) |
743 | } |
744 | |
745 | /// Convert from a Linux `stat64` value to rustix's `Stat`. |
746 | #[cfg (target_pointer_width = "32" )] |
747 | fn stat_to_stat(s64: linux_raw_sys::general::stat64) -> io::Result<Stat> { |
748 | Ok(Stat { |
749 | st_dev: s64.st_dev.try_into().map_err(|_| io::Errno::OVERFLOW)?, |
750 | st_mode: s64.st_mode.try_into().map_err(|_| io::Errno::OVERFLOW)?, |
751 | st_nlink: s64.st_nlink.try_into().map_err(|_| io::Errno::OVERFLOW)?, |
752 | st_uid: s64.st_uid.try_into().map_err(|_| io::Errno::OVERFLOW)?, |
753 | st_gid: s64.st_gid.try_into().map_err(|_| io::Errno::OVERFLOW)?, |
754 | st_rdev: s64.st_rdev.try_into().map_err(|_| io::Errno::OVERFLOW)?, |
755 | st_size: s64.st_size.try_into().map_err(|_| io::Errno::OVERFLOW)?, |
756 | st_blksize: s64.st_blksize.try_into().map_err(|_| io::Errno::OVERFLOW)?, |
757 | st_blocks: s64.st_blocks.try_into().map_err(|_| io::Errno::OVERFLOW)?, |
758 | st_atime: i64::from(s64.st_atime.to_signed()), |
759 | st_atime_nsec: s64 |
760 | .st_atime_nsec |
761 | .try_into() |
762 | .map_err(|_| io::Errno::OVERFLOW)?, |
763 | st_mtime: i64::from(s64.st_mtime.to_signed()), |
764 | st_mtime_nsec: s64 |
765 | .st_mtime_nsec |
766 | .try_into() |
767 | .map_err(|_| io::Errno::OVERFLOW)?, |
768 | st_ctime: i64::from(s64.st_ctime.to_signed()), |
769 | st_ctime_nsec: s64 |
770 | .st_ctime_nsec |
771 | .try_into() |
772 | .map_err(|_| io::Errno::OVERFLOW)?, |
773 | st_ino: s64.st_ino.try_into().map_err(|_| io::Errno::OVERFLOW)?, |
774 | }) |
775 | } |
776 | |
777 | /// Convert from a Linux `stat` value to rustix's `Stat`. |
778 | #[cfg (any(target_arch = "mips64" , target_arch = "mips64r6" ))] |
779 | fn stat_to_stat(s: linux_raw_sys::general::stat) -> io::Result<Stat> { |
780 | Ok(Stat { |
781 | st_dev: s.st_dev.try_into().map_err(|_| io::Errno::OVERFLOW)?, |
782 | st_mode: s.st_mode.try_into().map_err(|_| io::Errno::OVERFLOW)?, |
783 | st_nlink: s.st_nlink.try_into().map_err(|_| io::Errno::OVERFLOW)?, |
784 | st_uid: s.st_uid.try_into().map_err(|_| io::Errno::OVERFLOW)?, |
785 | st_gid: s.st_gid.try_into().map_err(|_| io::Errno::OVERFLOW)?, |
786 | st_rdev: s.st_rdev.try_into().map_err(|_| io::Errno::OVERFLOW)?, |
787 | st_size: s.st_size.try_into().map_err(|_| io::Errno::OVERFLOW)?, |
788 | st_blksize: s.st_blksize.try_into().map_err(|_| io::Errno::OVERFLOW)?, |
789 | st_blocks: s.st_blocks.try_into().map_err(|_| io::Errno::OVERFLOW)?, |
790 | st_atime: i64::from(s.st_atime.to_signed()), |
791 | st_atime_nsec: s |
792 | .st_atime_nsec |
793 | .try_into() |
794 | .map_err(|_| io::Errno::OVERFLOW)?, |
795 | st_mtime: i64::from(s.st_mtime.to_signed()), |
796 | st_mtime_nsec: s |
797 | .st_mtime_nsec |
798 | .try_into() |
799 | .map_err(|_| io::Errno::OVERFLOW)?, |
800 | st_ctime: i64::from(s.st_ctime.to_signed()), |
801 | st_ctime_nsec: s |
802 | .st_ctime_nsec |
803 | .try_into() |
804 | .map_err(|_| io::Errno::OVERFLOW)?, |
805 | st_ino: s.st_ino.try_into().map_err(|_| io::Errno::OVERFLOW)?, |
806 | }) |
807 | } |
808 | |
809 | #[inline ] |
810 | pub(crate) fn statx( |
811 | dirfd: BorrowedFd<'_>, |
812 | path: &CStr, |
813 | flags: AtFlags, |
814 | mask: StatxFlags, |
815 | ) -> io::Result<Statx> { |
816 | // If a future Linux kernel adds more fields to `struct statx` and users |
817 | // passing flags unknown to rustix in `StatxFlags`, we could end up |
818 | // writing outside of the buffer. To prevent this possibility, we mask off |
819 | // any flags that we don't know about. |
820 | // |
821 | // This includes `STATX__RESERVED`, which has a value that we know, but |
822 | // which could take on arbitrary new meaning in the future. Linux currently |
823 | // rejects this flag with `EINVAL`, so we do the same. |
824 | // |
825 | // This doesn't rely on `STATX_ALL` because [it's deprecated] and already |
826 | // doesn't represent all the known flags. |
827 | // |
828 | // [it's deprecated]: https://patchwork.kernel.org/project/linux-fsdevel/patch/20200505095915.11275-7-mszeredi@redhat.com/ |
829 | if (mask.bits() & STATX__RESERVED) == STATX__RESERVED { |
830 | return Err(io::Errno::INVAL); |
831 | } |
832 | let mask = mask & StatxFlags::all(); |
833 | |
834 | unsafe { |
835 | let mut statx_buf = MaybeUninit::<Statx>::uninit(); |
836 | ret(syscall!( |
837 | __NR_statx, |
838 | dirfd, |
839 | path, |
840 | flags, |
841 | mask, |
842 | &mut statx_buf |
843 | ))?; |
844 | Ok(statx_buf.assume_init()) |
845 | } |
846 | } |
847 | |
848 | #[cfg (not(feature = "linux_4_11" ))] |
849 | #[inline ] |
850 | pub(crate) fn is_statx_available() -> bool { |
851 | unsafe { |
852 | // Call `statx` with null pointers so that if it fails for any reason |
853 | // other than `EFAULT`, we know it's not supported. This can use |
854 | // "readonly" because we don't pass it a buffer to mutate. |
855 | matches!( |
856 | ret(syscall_readonly!( |
857 | __NR_statx, |
858 | raw_fd(AT_FDCWD), |
859 | zero(), |
860 | zero(), |
861 | zero(), |
862 | zero() |
863 | )), |
864 | Err(io::Errno::FAULT) |
865 | ) |
866 | } |
867 | } |
868 | |
869 | #[inline ] |
870 | pub(crate) fn fstatfs(fd: BorrowedFd<'_>) -> io::Result<StatFs> { |
871 | #[cfg (target_pointer_width = "32" )] |
872 | unsafe { |
873 | let mut result = MaybeUninit::<StatFs>::uninit(); |
874 | ret(syscall!( |
875 | __NR_fstatfs64, |
876 | fd, |
877 | size_of::<StatFs, _>(), |
878 | &mut result |
879 | ))?; |
880 | Ok(result.assume_init()) |
881 | } |
882 | |
883 | #[cfg (target_pointer_width = "64" )] |
884 | unsafe { |
885 | let mut result: MaybeUninit = MaybeUninit::<StatFs>::uninit(); |
886 | ret(raw:syscall!(__NR_fstatfs, fd, &mut result))?; |
887 | Ok(result.assume_init()) |
888 | } |
889 | } |
890 | |
891 | #[inline ] |
892 | pub(crate) fn fstatvfs(fd: BorrowedFd<'_>) -> io::Result<StatVfs> { |
893 | // Linux doesn't have an `fstatvfs` syscall; we have to do `fstatfs` and |
894 | // translate the fields as best we can. |
895 | let statfs: StatFs = fstatfs(fd)?; |
896 | |
897 | Ok(statfs_to_statvfs(statfs)) |
898 | } |
899 | |
900 | #[inline ] |
901 | pub(crate) fn statfs(path: &CStr) -> io::Result<StatFs> { |
902 | #[cfg (target_pointer_width = "32" )] |
903 | unsafe { |
904 | let mut result = MaybeUninit::<StatFs>::uninit(); |
905 | ret(syscall!( |
906 | __NR_statfs64, |
907 | path, |
908 | size_of::<StatFs, _>(), |
909 | &mut result |
910 | ))?; |
911 | Ok(result.assume_init()) |
912 | } |
913 | #[cfg (target_pointer_width = "64" )] |
914 | unsafe { |
915 | let mut result: MaybeUninit = MaybeUninit::<StatFs>::uninit(); |
916 | ret(raw:syscall!(__NR_statfs, path, &mut result))?; |
917 | Ok(result.assume_init()) |
918 | } |
919 | } |
920 | |
921 | #[inline ] |
922 | pub(crate) fn statvfs(path: &CStr) -> io::Result<StatVfs> { |
923 | // Linux doesn't have a `statvfs` syscall; we have to do `statfs` and |
924 | // translate the fields as best we can. |
925 | let statfs: StatFs = statfs(path)?; |
926 | |
927 | Ok(statfs_to_statvfs(statfs)) |
928 | } |
929 | |
930 | fn statfs_to_statvfs(statfs: StatFs) -> StatVfs { |
931 | let Fsid { val: [{unknown}; 2] } = Fsid { |
932 | val: statfs.f_fsid.val, |
933 | }; |
934 | let [f_fsid_val0: i32, f_fsid_val1: i32]: [i32; 2] = val; |
935 | |
936 | StatVfs { |
937 | f_bsize: statfs.f_bsize as u64, |
938 | f_frsize: if statfs.f_frsize != 0 { |
939 | statfs.f_frsize |
940 | } else { |
941 | statfs.f_bsize |
942 | } as u64, |
943 | f_blocks: statfs.f_blocks as u64, |
944 | f_bfree: statfs.f_bfree as u64, |
945 | f_bavail: statfs.f_bavail as u64, |
946 | f_files: statfs.f_files as u64, |
947 | f_ffree: statfs.f_ffree as u64, |
948 | f_favail: statfs.f_ffree as u64, |
949 | f_fsid: u64::from(f_fsid_val0 as u32) | (u64::from(f_fsid_val1 as u32) << 32), |
950 | f_flag: StatVfsMountFlags::from_bits_retain(bits:statfs.f_flags as u64), |
951 | f_namemax: statfs.f_namelen as u64, |
952 | } |
953 | } |
954 | |
955 | #[cfg (feature = "alloc" )] |
956 | #[inline ] |
957 | pub(crate) fn readlink(path: &CStr, buf: &mut [u8]) -> io::Result<usize> { |
958 | let (buf_addr_mut: ArgReg<'_, A2>, buf_len: ArgReg<'_, A3>) = slice_mut(buf); |
959 | unsafe { |
960 | ret_usize(raw:syscall!( |
961 | __NR_readlinkat, |
962 | raw_fd(AT_FDCWD), |
963 | path, |
964 | buf_addr_mut, |
965 | buf_len |
966 | )) |
967 | } |
968 | } |
969 | |
970 | #[inline ] |
971 | pub(crate) unsafe fn readlinkat( |
972 | dirfd: BorrowedFd<'_>, |
973 | path: &CStr, |
974 | buf: (*mut u8, usize), |
975 | ) -> io::Result<usize> { |
976 | ret_usize(raw:syscall!( |
977 | __NR_readlinkat, |
978 | dirfd, |
979 | path, |
980 | buf.0, |
981 | pass_usize(buf.1) |
982 | )) |
983 | } |
984 | |
985 | #[inline ] |
986 | pub(crate) fn fcntl_getfl(fd: BorrowedFd<'_>) -> io::Result<OFlags> { |
987 | #[cfg (target_pointer_width = "32" )] |
988 | unsafe { |
989 | ret_c_uint(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GETFL))) |
990 | .map(OFlags::from_bits_retain) |
991 | } |
992 | #[cfg (target_pointer_width = "64" )] |
993 | unsafe { |
994 | ret_c_uint(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GETFL))).map(op:OFlags::from_bits_retain) |
995 | } |
996 | } |
997 | |
998 | #[inline ] |
999 | pub(crate) fn fcntl_setfl(fd: BorrowedFd<'_>, flags: OFlags) -> io::Result<()> { |
1000 | // Always enable support for large files. |
1001 | let flags: OFlags = flags | OFlags::from_bits_retain(bits:c::O_LARGEFILE); |
1002 | |
1003 | #[cfg (target_pointer_width = "32" )] |
1004 | unsafe { |
1005 | ret(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_SETFL), flags)) |
1006 | } |
1007 | #[cfg (target_pointer_width = "64" )] |
1008 | unsafe { |
1009 | ret(raw:syscall_readonly!(__NR_fcntl, fd, c_uint(F_SETFL), flags)) |
1010 | } |
1011 | } |
1012 | |
1013 | #[inline ] |
1014 | pub(crate) fn fcntl_get_seals(fd: BorrowedFd<'_>) -> io::Result<SealFlags> { |
1015 | #[cfg (target_pointer_width = "32" )] |
1016 | unsafe { |
1017 | ret_c_int(syscall_readonly!(__NR_fcntl64, fd, c_uint(F_GET_SEALS))) |
1018 | .map(|seals| SealFlags::from_bits_retain(seals as u32)) |
1019 | } |
1020 | #[cfg (target_pointer_width = "64" )] |
1021 | unsafe { |
1022 | ret_c_int(syscall_readonly!(__NR_fcntl, fd, c_uint(F_GET_SEALS))) |
1023 | .map(|seals: i32| SealFlags::from_bits_retain(bits:seals as u32)) |
1024 | } |
1025 | } |
1026 | |
1027 | #[inline ] |
1028 | pub(crate) fn fcntl_add_seals(fd: BorrowedFd<'_>, seals: SealFlags) -> io::Result<()> { |
1029 | #[cfg (target_pointer_width = "32" )] |
1030 | unsafe { |
1031 | ret(syscall_readonly!( |
1032 | __NR_fcntl64, |
1033 | fd, |
1034 | c_uint(F_ADD_SEALS), |
1035 | seals |
1036 | )) |
1037 | } |
1038 | #[cfg (target_pointer_width = "64" )] |
1039 | unsafe { |
1040 | ret(raw:syscall_readonly!( |
1041 | __NR_fcntl, |
1042 | fd, |
1043 | c_uint(F_ADD_SEALS), |
1044 | seals |
1045 | )) |
1046 | } |
1047 | } |
1048 | |
1049 | #[inline ] |
1050 | pub(crate) fn fcntl_lock(fd: BorrowedFd<'_>, operation: FlockOperation) -> io::Result<()> { |
1051 | #[cfg (target_pointer_width = "64" )] |
1052 | use linux_raw_sys::general::{flock, F_SETLK, F_SETLKW}; |
1053 | #[cfg (target_pointer_width = "32" )] |
1054 | use linux_raw_sys::general::{flock64 as flock, F_SETLK64 as F_SETLK, F_SETLKW64 as F_SETLKW}; |
1055 | use linux_raw_sys::general::{F_RDLCK, F_UNLCK, F_WRLCK}; |
1056 | |
1057 | let (cmd, l_type) = match operation { |
1058 | FlockOperation::LockShared => (F_SETLKW, F_RDLCK), |
1059 | FlockOperation::LockExclusive => (F_SETLKW, F_WRLCK), |
1060 | FlockOperation::Unlock => (F_SETLKW, F_UNLCK), |
1061 | FlockOperation::NonBlockingLockShared => (F_SETLK, F_RDLCK), |
1062 | FlockOperation::NonBlockingLockExclusive => (F_SETLK, F_WRLCK), |
1063 | FlockOperation::NonBlockingUnlock => (F_SETLK, F_UNLCK), |
1064 | }; |
1065 | |
1066 | let lock = flock { |
1067 | l_type: l_type as _, |
1068 | |
1069 | // When `l_len` is zero, this locks all the bytes from |
1070 | // `l_whence`/`l_start` to the end of the file, even as the |
1071 | // file grows dynamically. |
1072 | l_whence: SEEK_SET as _, |
1073 | l_start: 0, |
1074 | l_len: 0, |
1075 | |
1076 | // Unused. |
1077 | l_pid: 0, |
1078 | }; |
1079 | |
1080 | #[cfg (target_pointer_width = "32" )] |
1081 | unsafe { |
1082 | ret(syscall_readonly!( |
1083 | __NR_fcntl64, |
1084 | fd, |
1085 | c_uint(cmd), |
1086 | by_ref(&lock) |
1087 | )) |
1088 | } |
1089 | #[cfg (target_pointer_width = "64" )] |
1090 | unsafe { |
1091 | ret(syscall_readonly!( |
1092 | __NR_fcntl, |
1093 | fd, |
1094 | c_uint(cmd), |
1095 | by_ref(&lock) |
1096 | )) |
1097 | } |
1098 | } |
1099 | |
1100 | #[inline ] |
1101 | pub(crate) fn rename(old_path: &CStr, new_path: &CStr) -> io::Result<()> { |
1102 | #[cfg (target_arch = "riscv64" )] |
1103 | unsafe { |
1104 | ret(syscall_readonly!( |
1105 | __NR_renameat2, |
1106 | raw_fd(AT_FDCWD), |
1107 | old_path, |
1108 | raw_fd(AT_FDCWD), |
1109 | new_path, |
1110 | c_uint(0) |
1111 | )) |
1112 | } |
1113 | #[cfg (not(target_arch = "riscv64" ))] |
1114 | unsafe { |
1115 | ret(raw:syscall_readonly!( |
1116 | __NR_renameat, |
1117 | raw_fd(AT_FDCWD), |
1118 | old_path, |
1119 | raw_fd(AT_FDCWD), |
1120 | new_path |
1121 | )) |
1122 | } |
1123 | } |
1124 | |
1125 | #[inline ] |
1126 | pub(crate) fn renameat( |
1127 | old_dirfd: BorrowedFd<'_>, |
1128 | old_path: &CStr, |
1129 | new_dirfd: BorrowedFd<'_>, |
1130 | new_path: &CStr, |
1131 | ) -> io::Result<()> { |
1132 | #[cfg (target_arch = "riscv64" )] |
1133 | unsafe { |
1134 | ret(syscall_readonly!( |
1135 | __NR_renameat2, |
1136 | old_dirfd, |
1137 | old_path, |
1138 | new_dirfd, |
1139 | new_path, |
1140 | c_uint(0) |
1141 | )) |
1142 | } |
1143 | #[cfg (not(target_arch = "riscv64" ))] |
1144 | unsafe { |
1145 | ret(raw:syscall_readonly!( |
1146 | __NR_renameat, |
1147 | old_dirfd, |
1148 | old_path, |
1149 | new_dirfd, |
1150 | new_path |
1151 | )) |
1152 | } |
1153 | } |
1154 | |
1155 | #[inline ] |
1156 | pub(crate) fn renameat2( |
1157 | old_dirfd: BorrowedFd<'_>, |
1158 | old_path: &CStr, |
1159 | new_dirfd: BorrowedFd<'_>, |
1160 | new_path: &CStr, |
1161 | flags: RenameFlags, |
1162 | ) -> io::Result<()> { |
1163 | unsafe { |
1164 | ret(raw:syscall_readonly!( |
1165 | __NR_renameat2, |
1166 | old_dirfd, |
1167 | old_path, |
1168 | new_dirfd, |
1169 | new_path, |
1170 | flags |
1171 | )) |
1172 | } |
1173 | } |
1174 | |
1175 | #[inline ] |
1176 | pub(crate) fn unlink(path: &CStr) -> io::Result<()> { |
1177 | unsafe { |
1178 | ret(raw:syscall_readonly!( |
1179 | __NR_unlinkat, |
1180 | raw_fd(AT_FDCWD), |
1181 | path, |
1182 | c_uint(0) |
1183 | )) |
1184 | } |
1185 | } |
1186 | |
1187 | #[inline ] |
1188 | pub(crate) fn unlinkat(dirfd: BorrowedFd<'_>, path: &CStr, flags: AtFlags) -> io::Result<()> { |
1189 | unsafe { ret(raw:syscall_readonly!(__NR_unlinkat, dirfd, path, flags)) } |
1190 | } |
1191 | |
1192 | #[inline ] |
1193 | pub(crate) fn rmdir(path: &CStr) -> io::Result<()> { |
1194 | unsafe { |
1195 | ret(raw:syscall_readonly!( |
1196 | __NR_unlinkat, |
1197 | raw_fd(AT_FDCWD), |
1198 | path, |
1199 | c_uint(AT_REMOVEDIR) |
1200 | )) |
1201 | } |
1202 | } |
1203 | |
1204 | #[inline ] |
1205 | pub(crate) fn link(old_path: &CStr, new_path: &CStr) -> io::Result<()> { |
1206 | unsafe { |
1207 | ret(raw:syscall_readonly!( |
1208 | __NR_linkat, |
1209 | raw_fd(AT_FDCWD), |
1210 | old_path, |
1211 | raw_fd(AT_FDCWD), |
1212 | new_path, |
1213 | c_uint(0) |
1214 | )) |
1215 | } |
1216 | } |
1217 | |
1218 | #[inline ] |
1219 | pub(crate) fn linkat( |
1220 | old_dirfd: BorrowedFd<'_>, |
1221 | old_path: &CStr, |
1222 | new_dirfd: BorrowedFd<'_>, |
1223 | new_path: &CStr, |
1224 | flags: AtFlags, |
1225 | ) -> io::Result<()> { |
1226 | unsafe { |
1227 | ret(raw:syscall_readonly!( |
1228 | __NR_linkat, |
1229 | old_dirfd, |
1230 | old_path, |
1231 | new_dirfd, |
1232 | new_path, |
1233 | flags |
1234 | )) |
1235 | } |
1236 | } |
1237 | |
1238 | #[inline ] |
1239 | pub(crate) fn symlink(old_path: &CStr, new_path: &CStr) -> io::Result<()> { |
1240 | unsafe { |
1241 | ret(raw:syscall_readonly!( |
1242 | __NR_symlinkat, |
1243 | old_path, |
1244 | raw_fd(AT_FDCWD), |
1245 | new_path |
1246 | )) |
1247 | } |
1248 | } |
1249 | |
1250 | #[inline ] |
1251 | pub(crate) fn symlinkat(old_path: &CStr, dirfd: BorrowedFd<'_>, new_path: &CStr) -> io::Result<()> { |
1252 | unsafe { ret(raw:syscall_readonly!(__NR_symlinkat, old_path, dirfd, new_path)) } |
1253 | } |
1254 | |
1255 | #[inline ] |
1256 | pub(crate) fn mkdir(path: &CStr, mode: Mode) -> io::Result<()> { |
1257 | unsafe { |
1258 | ret(raw:syscall_readonly!( |
1259 | __NR_mkdirat, |
1260 | raw_fd(AT_FDCWD), |
1261 | path, |
1262 | mode |
1263 | )) |
1264 | } |
1265 | } |
1266 | |
1267 | #[inline ] |
1268 | pub(crate) fn mkdirat(dirfd: BorrowedFd<'_>, path: &CStr, mode: Mode) -> io::Result<()> { |
1269 | unsafe { ret(raw:syscall_readonly!(__NR_mkdirat, dirfd, path, mode)) } |
1270 | } |
1271 | |
1272 | #[cfg (feature = "alloc" )] |
1273 | #[inline ] |
1274 | pub(crate) fn getdents(fd: BorrowedFd<'_>, dirent: &mut [u8]) -> io::Result<usize> { |
1275 | let (dirent_addr_mut: ArgReg<'_, A1>, dirent_len: ArgReg<'_, A2>) = slice_mut(dirent); |
1276 | |
1277 | unsafe { ret_usize(raw:syscall!(__NR_getdents64, fd, dirent_addr_mut, dirent_len)) } |
1278 | } |
1279 | |
1280 | #[inline ] |
1281 | pub(crate) fn getdents_uninit( |
1282 | fd: BorrowedFd<'_>, |
1283 | dirent: &mut [MaybeUninit<u8>], |
1284 | ) -> io::Result<usize> { |
1285 | let (dirent_addr_mut: ArgReg<'_, A1>, dirent_len: ArgReg<'_, A2>) = slice_mut(dirent); |
1286 | |
1287 | unsafe { ret_usize(raw:syscall!(__NR_getdents64, fd, dirent_addr_mut, dirent_len)) } |
1288 | } |
1289 | |
1290 | #[inline ] |
1291 | pub(crate) fn utimensat( |
1292 | dirfd: BorrowedFd<'_>, |
1293 | path: &CStr, |
1294 | times: &Timestamps, |
1295 | flags: AtFlags, |
1296 | ) -> io::Result<()> { |
1297 | _utimensat(dirfd, path:Some(path), times, flags) |
1298 | } |
1299 | |
1300 | #[inline ] |
1301 | fn _utimensat( |
1302 | dirfd: BorrowedFd<'_>, |
1303 | path: Option<&CStr>, |
1304 | times: &Timestamps, |
1305 | flags: AtFlags, |
1306 | ) -> io::Result<()> { |
1307 | // `utimensat_time64` was introduced in Linux 5.1. The old `utimensat` |
1308 | // syscall is not y2038-compatible on 32-bit architectures. |
1309 | #[cfg (target_pointer_width = "32" )] |
1310 | unsafe { |
1311 | match ret(syscall_readonly!( |
1312 | __NR_utimensat_time64, |
1313 | dirfd, |
1314 | path, |
1315 | by_ref(times), |
1316 | flags |
1317 | )) { |
1318 | Err(io::Errno::NOSYS) => _utimensat_old(dirfd, path, times, flags), |
1319 | otherwise => otherwise, |
1320 | } |
1321 | } |
1322 | #[cfg (target_pointer_width = "64" )] |
1323 | unsafe { |
1324 | ret(syscall_readonly!( |
1325 | __NR_utimensat, |
1326 | dirfd, |
1327 | path, |
1328 | by_ref(times), |
1329 | flags |
1330 | )) |
1331 | } |
1332 | } |
1333 | |
1334 | #[cfg (target_pointer_width = "32" )] |
1335 | unsafe fn _utimensat_old( |
1336 | dirfd: BorrowedFd<'_>, |
1337 | path: Option<&CStr>, |
1338 | times: &Timestamps, |
1339 | flags: AtFlags, |
1340 | ) -> io::Result<()> { |
1341 | // See the comments in `clock_gettime_via_syscall` about emulation. |
1342 | let old_times = [ |
1343 | __kernel_old_timespec { |
1344 | tv_sec: times |
1345 | .last_access |
1346 | .tv_sec |
1347 | .try_into() |
1348 | .map_err(|_| io::Errno::OVERFLOW)?, |
1349 | tv_nsec: times |
1350 | .last_access |
1351 | .tv_nsec |
1352 | .try_into() |
1353 | .map_err(|_| io::Errno::INVAL)?, |
1354 | }, |
1355 | __kernel_old_timespec { |
1356 | tv_sec: times |
1357 | .last_modification |
1358 | .tv_sec |
1359 | .try_into() |
1360 | .map_err(|_| io::Errno::OVERFLOW)?, |
1361 | tv_nsec: times |
1362 | .last_modification |
1363 | .tv_nsec |
1364 | .try_into() |
1365 | .map_err(|_| io::Errno::INVAL)?, |
1366 | }, |
1367 | ]; |
1368 | // The length of the array is fixed and not passed into the syscall. |
1369 | let old_times_addr = slice_just_addr(&old_times); |
1370 | ret(syscall_readonly!( |
1371 | __NR_utimensat, |
1372 | dirfd, |
1373 | path, |
1374 | old_times_addr, |
1375 | flags |
1376 | )) |
1377 | } |
1378 | |
1379 | #[inline ] |
1380 | pub(crate) fn futimens(fd: BorrowedFd<'_>, times: &Timestamps) -> io::Result<()> { |
1381 | _utimensat(dirfd:fd, path:None, times, flags:AtFlags::empty()) |
1382 | } |
1383 | |
1384 | #[inline ] |
1385 | pub(crate) fn access(path: &CStr, access: Access) -> io::Result<()> { |
1386 | #[cfg (any(target_arch = "aarch64" , target_arch = "riscv64" ))] |
1387 | { |
1388 | accessat_noflags(CWD, path, access) |
1389 | } |
1390 | |
1391 | #[cfg (not(any(target_arch = "aarch64" , target_arch = "riscv64" )))] |
1392 | unsafe { |
1393 | ret(raw:syscall_readonly!(__NR_access, path, access)) |
1394 | } |
1395 | } |
1396 | |
1397 | pub(crate) fn accessat( |
1398 | dirfd: BorrowedFd<'_>, |
1399 | path: &CStr, |
1400 | access: Access, |
1401 | flags: AtFlags, |
1402 | ) -> io::Result<()> { |
1403 | if !flags |
1404 | .difference(AtFlags::EACCESS | AtFlags::SYMLINK_NOFOLLOW) |
1405 | .is_empty() |
1406 | { |
1407 | return Err(io::Errno::INVAL); |
1408 | } |
1409 | |
1410 | // Linux's `faccessat` syscall doesn't have a flags argument, so if we have |
1411 | // any flags, use the newer `faccessat2` introduced in Linux 5.8 which |
1412 | // does. Unless we're on Android where using newer system calls can cause |
1413 | // seccomp to abort the process. |
1414 | #[cfg (not(target_os = "android" ))] |
1415 | if !flags.is_empty() { |
1416 | unsafe { |
1417 | match ret(syscall_readonly!( |
1418 | __NR_faccessat2, |
1419 | dirfd, |
1420 | path, |
1421 | access, |
1422 | flags |
1423 | )) { |
1424 | Ok(()) => return Ok(()), |
1425 | Err(io::Errno::NOSYS) => {} |
1426 | Err(other) => return Err(other), |
1427 | } |
1428 | } |
1429 | } |
1430 | |
1431 | // Linux's `faccessat` doesn't have a flags parameter. If we have |
1432 | // `AT_EACCESS` and we're not setuid or setgid, we can emulate it. |
1433 | if flags.is_empty() |
1434 | || (flags.bits() == AT_EACCESS |
1435 | && crate::backend::ugid::syscalls::getuid() |
1436 | == crate::backend::ugid::syscalls::geteuid() |
1437 | && crate::backend::ugid::syscalls::getgid() |
1438 | == crate::backend::ugid::syscalls::getegid()) |
1439 | { |
1440 | return accessat_noflags(dirfd, path, access); |
1441 | } |
1442 | |
1443 | Err(io::Errno::NOSYS) |
1444 | } |
1445 | |
1446 | #[inline ] |
1447 | fn accessat_noflags(dirfd: BorrowedFd<'_>, path: &CStr, access: Access) -> io::Result<()> { |
1448 | unsafe { ret(raw:syscall_readonly!(__NR_faccessat, dirfd, path, access)) } |
1449 | } |
1450 | |
1451 | #[inline ] |
1452 | pub(crate) fn copy_file_range( |
1453 | fd_in: BorrowedFd<'_>, |
1454 | off_in: Option<&mut u64>, |
1455 | fd_out: BorrowedFd<'_>, |
1456 | off_out: Option<&mut u64>, |
1457 | len: usize, |
1458 | ) -> io::Result<usize> { |
1459 | unsafe { |
1460 | ret_usize(raw:syscall!( |
1461 | __NR_copy_file_range, |
1462 | fd_in, |
1463 | opt_mut(off_in), |
1464 | fd_out, |
1465 | opt_mut(off_out), |
1466 | pass_usize(len), |
1467 | c_uint(0) |
1468 | )) |
1469 | } |
1470 | } |
1471 | |
1472 | #[inline ] |
1473 | pub(crate) fn memfd_create(name: &CStr, flags: MemfdFlags) -> io::Result<OwnedFd> { |
1474 | unsafe { ret_owned_fd(raw:syscall_readonly!(__NR_memfd_create, name, flags)) } |
1475 | } |
1476 | |
1477 | #[inline ] |
1478 | pub(crate) fn sendfile( |
1479 | out_fd: BorrowedFd<'_>, |
1480 | in_fd: BorrowedFd<'_>, |
1481 | offset: Option<&mut u64>, |
1482 | count: usize, |
1483 | ) -> io::Result<usize> { |
1484 | #[cfg (target_pointer_width = "32" )] |
1485 | unsafe { |
1486 | ret_usize(syscall!( |
1487 | __NR_sendfile64, |
1488 | out_fd, |
1489 | in_fd, |
1490 | opt_mut(offset), |
1491 | pass_usize(count) |
1492 | )) |
1493 | } |
1494 | #[cfg (target_pointer_width = "64" )] |
1495 | unsafe { |
1496 | ret_usize(raw:syscall!( |
1497 | __NR_sendfile, |
1498 | out_fd, |
1499 | in_fd, |
1500 | opt_mut(offset), |
1501 | pass_usize(count) |
1502 | )) |
1503 | } |
1504 | } |
1505 | |
1506 | #[inline ] |
1507 | pub(crate) fn inotify_init1(flags: inotify::CreateFlags) -> io::Result<OwnedFd> { |
1508 | unsafe { ret_owned_fd(raw:syscall_readonly!(__NR_inotify_init1, flags)) } |
1509 | } |
1510 | |
1511 | #[inline ] |
1512 | pub(crate) fn inotify_add_watch( |
1513 | infd: BorrowedFd<'_>, |
1514 | path: &CStr, |
1515 | flags: inotify::WatchFlags, |
1516 | ) -> io::Result<i32> { |
1517 | unsafe { ret_c_int(raw:syscall_readonly!(__NR_inotify_add_watch, infd, path, flags)) } |
1518 | } |
1519 | |
1520 | #[inline ] |
1521 | pub(crate) fn inotify_rm_watch(infd: BorrowedFd<'_>, wfd: i32) -> io::Result<()> { |
1522 | unsafe { ret(raw:syscall_readonly!(__NR_inotify_rm_watch, infd, c_int(wfd))) } |
1523 | } |
1524 | |
1525 | #[inline ] |
1526 | pub(crate) unsafe fn getxattr( |
1527 | path: &CStr, |
1528 | name: &CStr, |
1529 | value: (*mut u8, usize), |
1530 | ) -> io::Result<usize> { |
1531 | ret_usize(raw:syscall!( |
1532 | __NR_getxattr, |
1533 | path, |
1534 | name, |
1535 | value.0, |
1536 | pass_usize(value.1) |
1537 | )) |
1538 | } |
1539 | |
1540 | #[inline ] |
1541 | pub(crate) unsafe fn lgetxattr( |
1542 | path: &CStr, |
1543 | name: &CStr, |
1544 | value: (*mut u8, usize), |
1545 | ) -> io::Result<usize> { |
1546 | ret_usize(raw:syscall!( |
1547 | __NR_lgetxattr, |
1548 | path, |
1549 | name, |
1550 | value.0, |
1551 | pass_usize(value.1) |
1552 | )) |
1553 | } |
1554 | |
1555 | #[inline ] |
1556 | pub(crate) unsafe fn fgetxattr( |
1557 | fd: BorrowedFd<'_>, |
1558 | name: &CStr, |
1559 | value: (*mut u8, usize), |
1560 | ) -> io::Result<usize> { |
1561 | ret_usize(raw:syscall!( |
1562 | __NR_fgetxattr, |
1563 | fd, |
1564 | name, |
1565 | value.0, |
1566 | pass_usize(value.1) |
1567 | )) |
1568 | } |
1569 | |
1570 | #[inline ] |
1571 | pub(crate) fn setxattr( |
1572 | path: &CStr, |
1573 | name: &CStr, |
1574 | value: &[u8], |
1575 | flags: XattrFlags, |
1576 | ) -> io::Result<()> { |
1577 | let (value_addr: ArgReg<'_, A2>, value_len: ArgReg<'_, A3>) = slice(value); |
1578 | unsafe { |
1579 | ret(raw:syscall_readonly!( |
1580 | __NR_setxattr, |
1581 | path, |
1582 | name, |
1583 | value_addr, |
1584 | value_len, |
1585 | flags |
1586 | )) |
1587 | } |
1588 | } |
1589 | |
1590 | #[inline ] |
1591 | pub(crate) fn lsetxattr( |
1592 | path: &CStr, |
1593 | name: &CStr, |
1594 | value: &[u8], |
1595 | flags: XattrFlags, |
1596 | ) -> io::Result<()> { |
1597 | let (value_addr: ArgReg<'_, A2>, value_len: ArgReg<'_, A3>) = slice(value); |
1598 | unsafe { |
1599 | ret(raw:syscall_readonly!( |
1600 | __NR_lsetxattr, |
1601 | path, |
1602 | name, |
1603 | value_addr, |
1604 | value_len, |
1605 | flags |
1606 | )) |
1607 | } |
1608 | } |
1609 | |
1610 | #[inline ] |
1611 | pub(crate) fn fsetxattr( |
1612 | fd: BorrowedFd<'_>, |
1613 | name: &CStr, |
1614 | value: &[u8], |
1615 | flags: XattrFlags, |
1616 | ) -> io::Result<()> { |
1617 | let (value_addr: ArgReg<'_, A2>, value_len: ArgReg<'_, A3>) = slice(value); |
1618 | unsafe { |
1619 | ret(raw:syscall_readonly!( |
1620 | __NR_fsetxattr, |
1621 | fd, |
1622 | name, |
1623 | value_addr, |
1624 | value_len, |
1625 | flags |
1626 | )) |
1627 | } |
1628 | } |
1629 | |
1630 | #[inline ] |
1631 | pub(crate) unsafe fn listxattr(path: &CStr, list: (*mut u8, usize)) -> io::Result<usize> { |
1632 | ret_usize(raw:syscall!(__NR_listxattr, path, list.0, pass_usize(list.1))) |
1633 | } |
1634 | |
1635 | #[inline ] |
1636 | pub(crate) unsafe fn llistxattr(path: &CStr, list: (*mut u8, usize)) -> io::Result<usize> { |
1637 | ret_usize(raw:syscall!(__NR_llistxattr, path, list.0, pass_usize(list.1))) |
1638 | } |
1639 | |
1640 | #[inline ] |
1641 | pub(crate) unsafe fn flistxattr(fd: BorrowedFd<'_>, list: (*mut u8, usize)) -> io::Result<usize> { |
1642 | ret_usize(raw:syscall!(__NR_flistxattr, fd, list.0, pass_usize(list.1))) |
1643 | } |
1644 | |
1645 | #[inline ] |
1646 | pub(crate) fn removexattr(path: &CStr, name: &CStr) -> io::Result<()> { |
1647 | unsafe { ret(raw:syscall_readonly!(__NR_removexattr, path, name)) } |
1648 | } |
1649 | |
1650 | #[inline ] |
1651 | pub(crate) fn lremovexattr(path: &CStr, name: &CStr) -> io::Result<()> { |
1652 | unsafe { ret(raw:syscall_readonly!(__NR_lremovexattr, path, name)) } |
1653 | } |
1654 | |
1655 | #[inline ] |
1656 | pub(crate) fn fremovexattr(fd: BorrowedFd<'_>, name: &CStr) -> io::Result<()> { |
1657 | unsafe { ret(raw:syscall_readonly!(__NR_fremovexattr, fd, name)) } |
1658 | } |
1659 | |
1660 | // Some linux_raw_sys structs have unsigned types for values which are |
1661 | // interpreted as signed. This defines a utility or casting to the |
1662 | // same-sized signed type. |
1663 | #[cfg (any( |
1664 | target_pointer_width = "32" , |
1665 | target_arch = "mips64" , |
1666 | target_arch = "mips64r6" |
1667 | ))] |
1668 | mod to_signed { |
1669 | pub(super) trait ToSigned { |
1670 | type Signed; |
1671 | fn to_signed(self) -> Self::Signed; |
1672 | } |
1673 | impl ToSigned for u32 { |
1674 | type Signed = i32; |
1675 | |
1676 | fn to_signed(self) -> Self::Signed { |
1677 | self as _ |
1678 | } |
1679 | } |
1680 | impl ToSigned for i32 { |
1681 | type Signed = i32; |
1682 | |
1683 | fn to_signed(self) -> Self::Signed { |
1684 | self |
1685 | } |
1686 | } |
1687 | impl ToSigned for u64 { |
1688 | type Signed = i64; |
1689 | |
1690 | fn to_signed(self) -> Self::Signed { |
1691 | self as _ |
1692 | } |
1693 | } |
1694 | impl ToSigned for i64 { |
1695 | type Signed = i64; |
1696 | |
1697 | fn to_signed(self) -> Self::Signed { |
1698 | self |
1699 | } |
1700 | } |
1701 | } |
1702 | #[cfg (any( |
1703 | target_pointer_width = "32" , |
1704 | target_arch = "mips64" , |
1705 | target_arch = "mips64r6" |
1706 | ))] |
1707 | use to_signed::*; |
1708 | |
1709 | #[cfg (test)] |
1710 | mod tests { |
1711 | use super::*; |
1712 | |
1713 | #[test ] |
1714 | fn test_sizes() { |
1715 | assert_eq_size!(linux_raw_sys::general::__kernel_loff_t, u64); |
1716 | assert_eq_align!(linux_raw_sys::general::__kernel_loff_t, u64); |
1717 | |
1718 | // Assert that `Timestamps` has the expected layout. |
1719 | assert_eq_size!([linux_raw_sys::general::__kernel_timespec; 2], Timestamps); |
1720 | assert_eq_align!([linux_raw_sys::general::__kernel_timespec; 2], Timestamps); |
1721 | } |
1722 | } |
1723 | |