From e2314b7050a42920f1c83f5f8e43edf13b9161a5 Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 23 Oct 2023 16:51:03 -0700 Subject: [PATCH] Fix `renameat_with` with flags on Android. (#892) When `renameat_with` is passed non-zero flags, fall back to the `syscall!` macro on ABIs such as Android where libc has a `renameat` but not a `renameat2`. Fixes Stebalien/tempfile#259. --- src/backend/libc/fs/syscalls.rs | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/src/backend/libc/fs/syscalls.rs b/src/backend/libc/fs/syscalls.rs index 72034ae55..5e0b62f8e 100644 --- a/src/backend/libc/fs/syscalls.rs +++ b/src/backend/libc/fs/syscalls.rs @@ -521,8 +521,6 @@ pub(crate) fn renameat2( } } -/// At present, `libc` only has `renameat2` defined for glibc. On other -/// ABIs, `RenameFlags` has no flags defined, and we use plain `renameat`. #[cfg(any( target_os = "android", all(target_os = "linux", not(target_env = "gnu")), @@ -535,8 +533,32 @@ pub(crate) fn renameat2( new_path: &CStr, flags: RenameFlags, ) -> io::Result<()> { - assert!(flags.is_empty()); - renameat(old_dirfd, old_path, new_dirfd, new_path) + // At present, `libc` only has `renameat2` defined for glibc. If we have + // no flags, we can use plain `renameat`, but otherwise we use `syscall!`. + // to call `renameat2` ourselves. + if flags.is_empty() { + renameat(old_dirfd, old_path, new_dirfd, new_path) + } else { + syscall! { + fn renameat2( + olddirfd: c::c_int, + oldpath: *const c::c_char, + newdirfd: c::c_int, + newpath: *const c::c_char, + flags: c::c_uint + ) via SYS_renameat2 -> c::c_int + } + + unsafe { + ret(renameat2( + borrowed_fd(old_dirfd), + c_str(old_path), + borrowed_fd(new_dirfd), + c_str(new_path), + flags.bits(), + )) + } + } } pub(crate) fn symlink(old_path: &CStr, new_path: &CStr) -> io::Result<()> {