diff --git a/src/backend/libc/c.rs b/src/backend/libc/c.rs index 4fa654457..f788cb120 100644 --- a/src/backend/libc/c.rs +++ b/src/backend/libc/c.rs @@ -224,25 +224,6 @@ pub(super) unsafe fn prlimit( prlimit64(pid, resource, new_limit, old_limit) } -// 64-bit offsets on 32-bit platforms are passed in endianness-specific -// lo/hi pairs. See src/backend/linux_raw/conv.rs for details. -#[cfg(all(linux_kernel, target_endian = "little", target_pointer_width = "32"))] -fn lo(x: i64) -> usize { - (x >> 32) as usize -} -#[cfg(all(linux_kernel, target_endian = "little", target_pointer_width = "32"))] -fn hi(x: i64) -> usize { - x as usize -} -#[cfg(all(linux_kernel, target_endian = "big", target_pointer_width = "32"))] -fn lo(x: i64) -> usize { - x as usize -} -#[cfg(all(linux_kernel, target_endian = "big", target_pointer_width = "32"))] -fn hi(x: i64) -> usize { - (x >> 32) as usize -} - #[cfg(target_os = "android")] mod readwrite_pv64 { use super::*; @@ -263,31 +244,18 @@ mod readwrite_pv64 { if let Some(fun) = preadv64.get() { fun(fd, iov, iovcnt, offset) } else { - #[cfg(target_pointer_width = "32")] - { - syscall! { - fn preadv( - fd: libc::c_int, - iov: *const libc::iovec, - iovcnt: libc::c_int, - offset_hi: usize, - offset_lo: usize - ) via SYS_preadv -> libc::ssize_t - } - preadv(fd, iov, iovcnt, hi(offset), lo(offset)) - } - #[cfg(target_pointer_width = "64")] - { - syscall! { - fn preadv( - fd: libc::c_int, - iov: *const libc::iovec, - iovcnt: libc::c_int, - offset: libc::off_t - ) via SYS_preadv -> libc::ssize_t - } - preadv(fd, iov, iovcnt, offset) + // Unlike the plain "p" functions, the "pv" functions pass their + // offset in an endian-independent way, and always in two registers. + syscall! { + fn preadv( + fd: libc::c_int, + iov: *const libc::iovec, + iovcnt: libc::c_int, + offset_lo: usize, + offset_hi: usize + ) via SYS_preadv -> libc::ssize_t } + preadv(fd, iov, iovcnt, offset as usize, (offset >> 32) as usize) } } pub(in super::super) unsafe fn pwritev64( @@ -303,31 +271,18 @@ mod readwrite_pv64 { if let Some(fun) = pwritev64.get() { fun(fd, iov, iovcnt, offset) } else { - #[cfg(target_pointer_width = "32")] - { - syscall! { - fn pwritev( - fd: libc::c_int, - iov: *const libc::iovec, - iovcnt: libc::c_int, - offset_hi: usize, - offset_lo: usize - ) via SYS_pwritev -> libc::ssize_t - } - pwritev(fd, iov, iovcnt, hi(offset), lo(offset)) - } - #[cfg(target_pointer_width = "64")] - { - syscall! { - fn pwritev( - fd: libc::c_int, - iov: *const libc::iovec, - iovcnt: libc::c_int, - offset: libc::off_t - ) via SYS_pwritev -> libc::ssize_t - } - pwritev(fd, iov, iovcnt, offset) + // Unlike the plain "p" functions, the "pv" functions pass their + // offset in an endian-independent way, and always in two registers. + syscall! { + fn pwritev( + fd: libc::c_int, + iov: *const libc::iovec, + iovcnt: libc::c_int, + offset_lo: usize, + offset_hi: usize + ) via SYS_pwritev -> libc::ssize_t } + pwritev(fd, iov, iovcnt, offset as usize, (offset >> 32) as usize) } } } @@ -378,33 +333,26 @@ mod readwrite_pv64v2 { if let Some(fun) = preadv64v2.get() { fun(fd, iov, iovcnt, offset, flags) } else { - #[cfg(target_pointer_width = "32")] - { - syscall! { - fn preadv2( - fd: libc::c_int, - iov: *const libc::iovec, - iovcnt: libc::c_int, - offset_hi: usize, - offset_lo: usize, - flags: libc::c_int - ) via SYS_preadv2 -> libc::ssize_t - } - preadv2(fd, iov, iovcnt, hi(offset), lo(offset), flags) - } - #[cfg(target_pointer_width = "64")] - { - syscall! { - fn preadv2( - fd: libc::c_int, - iov: *const libc::iovec, - iovcnt: libc::c_int, - offset: libc::off_t, - flags: libc::c_int - ) via SYS_preadv2 -> libc::ssize_t - } - preadv2(fd, iov, iovcnt, offset, flags) + // Unlike the plain "p" functions, the "pv" functions pass their + // offset in an endian-independent way, and always in two registers. + syscall! { + fn preadv2( + fd: libc::c_int, + iov: *const libc::iovec, + iovcnt: libc::c_int, + offset_lo: usize, + offset_hi: usize, + flags: libc::c_int + ) via SYS_preadv2 -> libc::ssize_t } + preadv2( + fd, + iov, + iovcnt, + offset as usize, + (offset >> 32) as usize, + flags, + ) } } pub(in super::super) unsafe fn pwritev64v2( @@ -421,33 +369,26 @@ mod readwrite_pv64v2 { if let Some(fun) = pwritev64v2.get() { fun(fd, iov, iovcnt, offset, flags) } else { - #[cfg(target_pointer_width = "32")] - { - syscall! { - fn pwritev2( - fd: libc::c_int, - iov: *const libc::iovec, - iovec: libc::c_int, - offset_hi: usize, - offset_lo: usize, - flags: libc::c_int - ) via SYS_pwritev2 -> libc::ssize_t - } - pwritev2(fd, iov, iovcnt, hi(offset), lo(offset), flags) - } - #[cfg(target_pointer_width = "64")] - { - syscall! { - fn pwritev2( - fd: libc::c_int, - iov:*const libc::iovec, - iovcnt: libc::c_int, - offset: libc::off_t, - flags: libc::c_int - ) via SYS_pwritev2 -> libc::ssize_t - } - pwritev2(fd, iov, iovcnt, offset, flags) + // Unlike the plain "p" functions, the "pv" functions pass their + // offset in an endian-independent way, and always in two registers. + syscall! { + fn pwritev2( + fd: libc::c_int, + iov: *const libc::iovec, + iovec: libc::c_int, + offset_lo: usize, + offset_hi: usize, + flags: libc::c_int + ) via SYS_pwritev2 -> libc::ssize_t } + pwritev2( + fd, + iov, + iovcnt, + offset as usize, + (offset >> 32) as usize, + flags, + ) } } } @@ -470,33 +411,26 @@ mod readwrite_pv64v2 { offset: libc::off64_t, flags: libc::c_int, ) -> libc::ssize_t { - #[cfg(target_pointer_width = "32")] - { - syscall! { - fn preadv2( - fd: libc::c_int, - iov: *const libc::iovec, - iovcnt: libc::c_int, - offset_hi: usize, - offset_lo: usize, - flags: libc::c_int - ) via SYS_preadv2 -> libc::ssize_t - } - preadv2(fd, iov, iovcnt, hi(offset), lo(offset), flags) - } - #[cfg(target_pointer_width = "64")] - { - syscall! { - fn preadv2( - fd: libc::c_int, - iov: *const libc::iovec, - iovcnt: libc::c_int, - offset: libc::off_t, - flags: libc::c_int - ) via SYS_preadv2 -> libc::ssize_t - } - preadv2(fd, iov, iovcnt, offset, flags) + // Unlike the plain "p" functions, the "pv" functions pass their offset + // in an endian-independent way, and always in two registers. + syscall! { + fn preadv2( + fd: libc::c_int, + iov: *const libc::iovec, + iovcnt: libc::c_int, + offset_lo: usize, + offset_hi: usize, + flags: libc::c_int + ) via SYS_preadv2 -> libc::ssize_t } + preadv2( + fd, + iov, + iovcnt, + offset as usize, + (offset >> 32) as usize, + flags, + ) } pub(in super::super) unsafe fn pwritev64v2( fd: libc::c_int, @@ -505,33 +439,26 @@ mod readwrite_pv64v2 { offset: libc::off64_t, flags: libc::c_int, ) -> libc::ssize_t { - #[cfg(target_pointer_width = "32")] - { - syscall! { - fn pwritev2( - fd: libc::c_int, - iov: *const libc::iovec, - iovcnt: libc::c_int, - offset_hi: usize, - offset_lo: usize, - flags: libc::c_int - ) via SYS_pwritev2 -> libc::ssize_t - } - pwritev2(fd, iov, iovcnt, hi(offset), lo(offset), flags) - } - #[cfg(target_pointer_width = "64")] - { - syscall! { - fn pwritev2( - fd: libc::c_int, - iov:*const libc::iovec, - iovcnt: libc::c_int, - offset: libc::off_t, - flags: libc::c_int - ) via SYS_pwritev2 -> libc::ssize_t - } - pwritev2(fd, iov, iovcnt, offset, flags) + // Unlike the plain "p" functions, the "pv" functions pass their offset + // in an endian-independent way, and always in two registers. + syscall! { + fn pwritev2( + fd: libc::c_int, + iov: *const libc::iovec, + iovcnt: libc::c_int, + offset_lo: usize, + offset_hi: usize, + flags: libc::c_int + ) via SYS_pwritev2 -> libc::ssize_t } + pwritev2( + fd, + iov, + iovcnt, + offset as usize, + (offset >> 32) as usize, + flags, + ) } } #[cfg(any( diff --git a/src/backend/linux_raw/io/syscalls.rs b/src/backend/linux_raw/io/syscalls.rs index d2cbba0c3..62c68a22f 100644 --- a/src/backend/linux_raw/io/syscalls.rs +++ b/src/backend/linux_raw/io/syscalls.rs @@ -14,8 +14,8 @@ use crate::backend::conv::loff_t_from_u64; ))] use crate::backend::conv::zero; use crate::backend::conv::{ - c_uint, raw_fd, ret, ret_c_int, ret_c_uint, ret_discarded_fd, ret_owned_fd, ret_usize, slice, - slice_mut, + c_uint, pass_usize, raw_fd, ret, ret_c_int, ret_c_uint, ret_discarded_fd, ret_owned_fd, + ret_usize, slice, slice_mut, }; #[cfg(target_pointer_width = "32")] use crate::backend::conv::{hi, lo}; @@ -96,25 +96,16 @@ pub(crate) fn preadv( ) -> io::Result { let (bufs_addr, bufs_len) = slice(&bufs[..cmp::min(bufs.len(), MAX_IOV)]); - #[cfg(target_pointer_width = "32")] + // Unlike the plain "p" functions, the "pv" functions pass their offset in + // an endian-independent way, and always in two registers. unsafe { ret_usize(syscall!( __NR_preadv, fd, bufs_addr, bufs_len, - hi(pos), - lo(pos) - )) - } - #[cfg(target_pointer_width = "64")] - unsafe { - ret_usize(syscall!( - __NR_preadv, - fd, - bufs_addr, - bufs_len, - loff_t_from_u64(pos) + pass_usize(pos as usize), + pass_usize((pos >> 32) as usize) )) } } @@ -128,26 +119,16 @@ pub(crate) fn preadv2( ) -> io::Result { let (bufs_addr, bufs_len) = slice(&bufs[..cmp::min(bufs.len(), MAX_IOV)]); - #[cfg(target_pointer_width = "32")] - unsafe { - ret_usize(syscall!( - __NR_preadv2, - fd, - bufs_addr, - bufs_len, - hi(pos), - lo(pos), - flags - )) - } - #[cfg(target_pointer_width = "64")] + // Unlike the plain "p" functions, the "pv" functions pass their offset in + // an endian-independent way, and always in two registers. unsafe { ret_usize(syscall!( __NR_preadv2, fd, bufs_addr, bufs_len, - loff_t_from_u64(pos), + pass_usize(pos as usize), + pass_usize((pos >> 32) as usize), flags )) } @@ -217,25 +198,16 @@ pub(crate) fn writev(fd: BorrowedFd<'_>, bufs: &[IoSlice<'_>]) -> io::Result, bufs: &[IoSlice<'_>], pos: u64) -> io::Result { let (bufs_addr, bufs_len) = slice(&bufs[..cmp::min(bufs.len(), MAX_IOV)]); - #[cfg(target_pointer_width = "32")] + // Unlike the plain "p" functions, the "pv" functions pass their offset in + // an endian-independent way, and always in two registers. unsafe { ret_usize(syscall_readonly!( __NR_pwritev, fd, bufs_addr, bufs_len, - hi(pos), - lo(pos) - )) - } - #[cfg(target_pointer_width = "64")] - unsafe { - ret_usize(syscall_readonly!( - __NR_pwritev, - fd, - bufs_addr, - bufs_len, - loff_t_from_u64(pos) + pass_usize(pos as usize), + pass_usize((pos >> 32) as usize) )) } } @@ -249,26 +221,16 @@ pub(crate) fn pwritev2( ) -> io::Result { let (bufs_addr, bufs_len) = slice(&bufs[..cmp::min(bufs.len(), MAX_IOV)]); - #[cfg(target_pointer_width = "32")] - unsafe { - ret_usize(syscall_readonly!( - __NR_pwritev2, - fd, - bufs_addr, - bufs_len, - hi(pos), - lo(pos), - flags - )) - } - #[cfg(target_pointer_width = "64")] + // Unlike the plain "p" functions, the "pv" functions pass their offset in + // an endian-independent way, and always in two registers. unsafe { ret_usize(syscall_readonly!( __NR_pwritev2, fd, bufs_addr, bufs_len, - loff_t_from_u64(pos), + pass_usize(pos as usize), + pass_usize((pos >> 32) as usize), flags )) } diff --git a/tests/fs/seek.rs b/tests/fs/seek.rs index 638bdaf9c..aa339a0b5 100644 --- a/tests/fs/seek.rs +++ b/tests/fs/seek.rs @@ -58,3 +58,22 @@ fn test_seek_holes() { Ok(hole_size * 2) ); } + +#[test] +fn test_seek_offsets() { + use rustix::fs::{openat, seek, Mode, OFlags, SeekFrom, CWD}; + + let f = openat(CWD, "Cargo.toml", OFlags::RDONLY, Mode::empty()).unwrap(); + + match seek(&f, SeekFrom::Start(0)) { + Ok(_) => {} + Err(e) => panic!("seek failed with an unexpected error: {:?}", e), + } + for invalid_offset in [i32::MIN as u64, !1 as u64, i64::MIN as u64] { + match seek(&f, SeekFrom::Start(invalid_offset)) { + Err(rustix::io::Errno::INVAL) => {} + Ok(_) => panic!("seek unexpectedly succeeded"), + Err(e) => panic!("seek failed with an unexpected error: {:?}", e), + } + } +} diff --git a/tests/io/read_write.rs b/tests/io/read_write.rs index 2fe537613..ee1b457c3 100644 --- a/tests/io/read_write.rs +++ b/tests/io/read_write.rs @@ -244,3 +244,119 @@ fn test_preadv2_nowait() { Err(e) => panic!("preadv2 failed with an unexpected error: {:?}", e), } } + +#[cfg(all(feature = "net", feature = "pipe"))] +#[cfg(not(target_os = "espidf"))] // no preadv/pwritev +#[cfg(not(target_os = "solaris"))] // no preadv/pwritev +#[cfg(not(target_os = "haiku"))] // no preadv/pwritev +#[test] +fn test_p_offsets() { + use rustix::fs::{openat, Mode, OFlags, CWD}; + use rustix::io::{pread, preadv, pwrite, pwritev}; + #[cfg(linux_kernel)] + use rustix::io::{preadv2, pwritev2, ReadWriteFlags}; + + let mut buf = [0_u8; 5]; + + let tmp = tempfile::tempdir().unwrap(); + let f = openat( + CWD, + tmp.path().join("file"), + OFlags::RDWR | OFlags::CREATE | OFlags::TRUNC, + Mode::RUSR | Mode::WUSR, + ) + .unwrap(); + + // Test that offset 0 works. + match pread(&f, &mut buf, 0_u64) { + Err(rustix::io::Errno::OPNOTSUPP | rustix::io::Errno::NOSYS) => {} + Ok(_) => {} + Err(e) => panic!("pread failed with an unexpected error: {:?}", e), + } + match pwrite(&f, &buf, 0_u64) { + Err(rustix::io::Errno::OPNOTSUPP | rustix::io::Errno::NOSYS) => {} + Ok(_) => {} + Err(e) => panic!("pwrite failed with an unexpected error: {:?}", e), + } + match preadv(&f, &mut [IoSliceMut::new(&mut buf)], 0_u64) { + Err(rustix::io::Errno::OPNOTSUPP | rustix::io::Errno::NOSYS) => {} + Ok(_) => {} + Err(e) => panic!("preadv failed with an unexpected error: {:?}", e), + } + match pwritev(&f, &[IoSlice::new(&buf)], 0_u64) { + Err(rustix::io::Errno::OPNOTSUPP | rustix::io::Errno::NOSYS) => {} + Ok(_) => {} + Err(e) => panic!("pwritev failed with an unexpected error: {:?}", e), + } + #[cfg(linux_kernel)] + { + match preadv2( + &f, + &mut [IoSliceMut::new(&mut buf)], + 0_u64, + ReadWriteFlags::empty(), + ) { + Err(rustix::io::Errno::OPNOTSUPP | rustix::io::Errno::NOSYS) => {} + Ok(_) => {} + Err(e) => panic!("preadv2 failed with an unexpected error: {:?}", e), + } + match pwritev2(&f, &[IoSlice::new(&buf)], 0_u64, ReadWriteFlags::empty()) { + Err(rustix::io::Errno::OPNOTSUPP | rustix::io::Errno::NOSYS) => {} + Ok(_) => {} + Err(e) => panic!("pwritev2 failed with an unexpected error: {:?}", e), + } + } + + // Test that negative offsets fail with `INVAL`. + for invalid_offset in [i32::MIN as u64, !1 as u64, i64::MIN as u64] { + match pread(&f, &mut buf, invalid_offset) { + Err(rustix::io::Errno::OPNOTSUPP | rustix::io::Errno::NOSYS) => {} + Err(rustix::io::Errno::INVAL) => {} + Ok(_) => panic!("pread unexpectedly succeeded"), + Err(e) => panic!("pread failed with an unexpected error: {:?}", e), + } + match pwrite(&f, &buf, invalid_offset) { + Err(rustix::io::Errno::OPNOTSUPP | rustix::io::Errno::NOSYS) => {} + Err(rustix::io::Errno::INVAL) => {} + Ok(_) => panic!("pwrite unexpectedly succeeded"), + Err(e) => panic!("pwrite failed with an unexpected error: {:?}", e), + } + match preadv(&f, &mut [IoSliceMut::new(&mut buf)], invalid_offset) { + Err(rustix::io::Errno::OPNOTSUPP | rustix::io::Errno::NOSYS) => {} + Err(rustix::io::Errno::INVAL) => {} + Ok(_) => panic!("preadv unexpectedly succeeded"), + Err(e) => panic!("preadv failed with an unexpected error: {:?}", e), + } + match pwritev(&f, &[IoSlice::new(&buf)], invalid_offset) { + Err(rustix::io::Errno::OPNOTSUPP | rustix::io::Errno::NOSYS) => {} + Err(rustix::io::Errno::INVAL) => {} + Ok(_) => panic!("pwritev unexpectedly succeeded"), + Err(e) => panic!("pwritev failed with an unexpected error: {:?}", e), + } + #[cfg(linux_kernel)] + { + match preadv2( + &f, + &mut [IoSliceMut::new(&mut buf)], + invalid_offset, + ReadWriteFlags::empty(), + ) { + Err(rustix::io::Errno::OPNOTSUPP | rustix::io::Errno::NOSYS) => {} + Err(rustix::io::Errno::INVAL) => {} + Ok(_) => panic!("preadv2 unexpectedly succeeded"), + Err(e) => panic!("preadv2 failed with an unexpected error: {:?}", e), + } + match pwritev2( + &f, + &[IoSlice::new(&buf)], + invalid_offset, + ReadWriteFlags::empty(), + ) { + Err(rustix::io::Errno::OPNOTSUPP | rustix::io::Errno::NOSYS) => {} + Err(rustix::io::Errno::INVAL) => {} + Ok(_) => panic!("pwritev2 unexpectedly succeeded"), + Err(e) => panic!("pwritev2 failed with an unexpected error: {:?}", e), + } + } + } +}