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