From 0b7d90e2f994b83aa4a4584a87841d66dd33d636 Mon Sep 17 00:00:00 2001 From: William Manley Date: Fri, 26 Mar 2021 01:41:10 +0000 Subject: [PATCH] File: preadv2: Call the syscall directly rather than via glibc This way we don't need to worry about compatiblity with old glibc versions and it will work on Android and musl too. The downside is that you can't use `LD_PRELOAD` tricks any more to intercept these calls. --- tokio/src/fs/file.rs | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/tokio/src/fs/file.rs b/tokio/src/fs/file.rs index 8eeee68eb99..e4a43d687a1 100644 --- a/tokio/src/fs/file.rs +++ b/tokio/src/fs/file.rs @@ -764,7 +764,7 @@ impl Inner { #[cfg(all(target_os = "linux", not(test)))] mod read_nowait { use crate::io::ReadBuf; - use libc::{c_int, c_void, iovec, off_t, preadv2}; + use libc::{c_int, c_long, c_void, iovec, off_t, ssize_t}; use std::{ os::unix::prelude::AsRawFd, sync::atomic::{AtomicBool, Ordering}, @@ -779,7 +779,7 @@ mod read_nowait { if !NONBLOCKING_READ_SUPPORTED.load(Ordering::Relaxed) { return None; } - let out = preadv2_safe(file, dst, -1, libc::RWF_NOWAIT); + let out = preadv2_safe(file, dst, -1, RWF_NOWAIT); if let Err(err) = &out { match err.raw_os_error() { Some(libc::ENOSYS) => { @@ -822,6 +822,32 @@ mod read_nowait { } } } + + fn pos_to_lohi(offset: off_t) -> (c_long, c_long) { + // 64-bit offset is split over high and low 32-bits on 32-bit architectures. + // 64-bit architectures still have high and low arguments, but only the low + // one is inspected. See pos_from_hilo in linux/fs/read_write.c. + const HALF_LONG_BITS: usize = core::mem::size_of::() * 8 / 2; + ( + offset as c_long, + // We want to shift this off_t value by size_of::(). We can't do + // it in one shift because if they're both 64-bits we'd be doing u64 >> 64 + // which is implementation defined. Instead do it in two halves: + ((offset >> HALF_LONG_BITS) >> HALF_LONG_BITS) as c_long, + ) + } + + const RWF_NOWAIT: c_int = 0x00000008; + unsafe fn preadv2( + fd: c_int, + iov: *const iovec, + iovcnt: c_int, + offset: off_t, + flags: c_int, + ) -> ssize_t { + let (lo, hi) = pos_to_lohi(offset); + libc::syscall(libc::SYS_preadv2, fd, iov, iovcnt, lo, hi, flags) as ssize_t + } } #[cfg(any(not(target_os = "linux"), test))]