Add and refactor read-write syscalls

This commit is contained in:
Fabing Li 2024-06-27 22:35:07 +08:00 committed by Tate, Hongliang Tian
parent e8a3e00dec
commit 12db7ec55d
15 changed files with 418 additions and 85 deletions

View File

@ -38,8 +38,8 @@ provided by Linux on x86-64 architecture.
| 15 | rt_sigreturn | ✅ |
| 16 | ioctl | ✅ |
| 17 | pread64 | ✅ |
| 18 | pwrite64 | |
| 19 | readv | |
| 18 | pwrite64 | |
| 19 | readv | |
| 20 | writev | ✅ |
| 21 | access | ✅ |
| 22 | pipe | ✅ |
@ -315,8 +315,8 @@ provided by Linux on x86-64 architecture.
| 292 | dup3 | ✅ |
| 293 | pipe2 | ✅ |
| 294 | inotify_init1 | ❌ |
| 295 | preadv | |
| 296 | pwritev | |
| 295 | preadv | |
| 296 | pwritev | |
| 297 | rt_tgsigqueueinfo | ❌ |
| 298 | perf_event_open | ❌ |
| 299 | recvmmsg | ❌ |
@ -336,6 +336,8 @@ provided by Linux on x86-64 architecture.
| 313 | finit_module | ❌ |
| 318 | getrandom | ✅ |
| 322 | execveat | ✅ |
| 327 | preadv2 | ✅ |
| 328 | pwritev2 | ✅ |
| 435 | clone3 | ✅ |
## File Systems

View File

@ -32,7 +32,7 @@ pub trait FileLike: Send + Sync + Any {
///
/// [`read`]: FileLike::read
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
return_errno_with_message!(Errno::EINVAL, "read_at is not supported");
return_errno_with_message!(Errno::ESPIPE, "read_at is not supported");
}
/// Write at the given file offset.
@ -43,7 +43,7 @@ pub trait FileLike: Send + Sync + Any {
///
/// [`write`]: FileLike::write
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
return_errno_with_message!(Errno::EINVAL, "write_at is not supported");
return_errno_with_message!(Errno::ESPIPE, "write_at is not supported");
}
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {

View File

@ -66,7 +66,10 @@ use crate::syscall::{
poll::sys_poll,
prctl::sys_prctl,
pread64::sys_pread64,
preadv::{sys_preadv, sys_preadv2, sys_readv},
prlimit64::sys_prlimit64,
pwrite64::sys_pwrite64,
pwritev::{sys_pwritev, sys_pwritev2, sys_writev},
read::sys_read,
readlink::{sys_readlink, sys_readlinkat},
recvfrom::sys_recvfrom,
@ -121,7 +124,6 @@ use crate::syscall::{
wait4::sys_wait4,
waitid::sys_waitid,
write::sys_write,
writev::sys_writev,
};
impl_syscall_nums_and_dispatch_fn! {
@ -143,6 +145,8 @@ impl_syscall_nums_and_dispatch_fn! {
SYS_RT_SIGRETURN = 15 => sys_rt_sigreturn(args[..0], &mut context);
SYS_IOCTL = 16 => sys_ioctl(args[..3]);
SYS_PREAD64 = 17 => sys_pread64(args[..4]);
SYS_PWRITE64 = 18 => sys_pwrite64(args[..4]);
SYS_READV = 19 => sys_readv(args[..3]);
SYS_WRITEV = 20 => sys_writev(args[..3]);
SYS_ACCESS = 21 => sys_access(args[..2]);
SYS_PIPE = 22 => sys_pipe(args[..1]);
@ -282,8 +286,12 @@ impl_syscall_nums_and_dispatch_fn! {
SYS_EPOLL_CREATE1 = 291 => sys_epoll_create1(args[..1]);
SYS_DUP3 = 292 => sys_dup3(args[..3]);
SYS_PIPE2 = 293 => sys_pipe2(args[..2]);
SYS_PREADV = 295 => sys_preadv(args[..4]);
SYS_PWRITEV = 296 => sys_pwritev(args[..4]);
SYS_PRLIMIT64 = 302 => sys_prlimit64(args[..4]);
SYS_GETRANDOM = 318 => sys_getrandom(args[..3]);
SYS_EXECVEAT = 322 => sys_execveat(args[..5], &mut context);
SYS_PREADV2 = 327 => sys_preadv2(args[..5]);
SYS_PWRITEV2 = 328 => sys_pwritev2(args[..5]);
SYS_CLONE3 = 435 => sys_clone3(args[..2], &context);
}

View File

@ -73,7 +73,10 @@ mod pipe;
mod poll;
mod prctl;
mod pread64;
mod preadv;
mod prlimit64;
mod pwrite64;
mod pwritev;
mod read;
mod readlink;
mod recvfrom;
@ -128,7 +131,6 @@ mod utimens;
mod wait4;
mod waitid;
mod write;
mod writev;
/// This macro is used to define syscall handler.
/// The first param is ths number of parameters,

View File

@ -1,29 +1,39 @@
// SPDX-License-Identifier: MPL-2.0
use super::SyscallReturn;
use crate::{
fs::{file_table::FileDesc, utils::SeekFrom},
prelude::*,
util::write_bytes_to_user,
};
use crate::{fs::file_table::FileDesc, prelude::*, util::write_bytes_to_user};
pub fn sys_pread64(fd: FileDesc, buf_ptr: Vaddr, count: usize, pos: i64) -> Result<SyscallReturn> {
pub fn sys_pread64(
fd: FileDesc,
user_buf_ptr: Vaddr,
user_buf_len: usize,
offset: i64,
) -> Result<SyscallReturn> {
debug!(
"fd = {}, buf = 0x{:x}, count = 0x{:x}, pos = 0x{:x}",
fd, buf_ptr, count, pos
"fd = {}, buf = 0x{:x}, user_buf_len = 0x{:x}, offset = 0x{:x}",
fd, user_buf_ptr, user_buf_len, offset
);
let current = current!();
let file_table = current.file_table().lock();
let file = file_table.get_file(fd)?;
let seek_from = SeekFrom::Start(pos as usize);
file.seek(seek_from)?;
if offset < 0 {
return_errno_with_message!(Errno::EINVAL, "offset cannot be negative");
}
let file = {
let current = current!();
let filetable = current.file_table().lock();
filetable.get_file(fd)?.clone()
};
// TODO: Check (f.file->f_mode & FMODE_PREAD); We don't have f_mode in our FileLike trait
if user_buf_len == 0 {
return Ok(SyscallReturn::Return(0));
}
if offset.checked_add(user_buf_len as i64).is_none() {
return_errno_with_message!(Errno::EINVAL, "offset + user_buf_len overflow");
}
let read_len = {
let mut buffer = vec![0u8; count];
let read_len = file.read(&mut buffer)?;
write_bytes_to_user(buf_ptr, &buffer)?;
let mut buffer = vec![0u8; user_buf_len];
let read_len = file.read_at(offset as usize, &mut buffer)?;
write_bytes_to_user(user_buf_ptr, &buffer)?;
read_len
};

View File

@ -0,0 +1,171 @@
// SPDX-License-Identifier: MPL-2.0
use super::SyscallReturn;
use crate::{
fs::file_table::FileDesc,
prelude::*,
util::{copy_iovs_from_user, IoVec},
};
pub fn sys_readv(fd: FileDesc, io_vec_ptr: Vaddr, io_vec_count: usize) -> Result<SyscallReturn> {
let res = do_sys_readv(fd, io_vec_ptr, io_vec_count)?;
Ok(SyscallReturn::Return(res as _))
}
pub fn sys_preadv(
fd: FileDesc,
io_vec_ptr: Vaddr,
io_vec_count: usize,
offset: i64,
) -> Result<SyscallReturn> {
let res = do_sys_preadv(fd, io_vec_ptr, io_vec_count, offset, RWFFlag::empty())?;
Ok(SyscallReturn::Return(res as _))
}
pub fn sys_preadv2(
fd: FileDesc,
io_vec_ptr: Vaddr,
io_vec_count: usize,
offset: i64,
flags: u32,
) -> Result<SyscallReturn> {
let flags = match RWFFlag::from_bits(flags) {
Some(flags) => flags,
None => return_errno_with_message!(Errno::EINVAL, "invalid flags"),
};
let res = if offset == -1 {
do_sys_readv(fd, io_vec_ptr, io_vec_count)?
} else {
do_sys_preadv(fd, io_vec_ptr, io_vec_count, offset, flags)?
};
Ok(SyscallReturn::Return(res as _))
}
fn do_sys_preadv(
fd: FileDesc,
io_vec_ptr: Vaddr,
io_vec_count: usize,
offset: i64,
_flags: RWFFlag,
) -> Result<usize> {
debug!(
"preadv: fd = {}, io_vec_ptr = 0x{:x}, io_vec_counter = 0x{:x}, offset = 0x{:x}",
fd, io_vec_ptr, io_vec_count, offset
);
if offset < 0 {
return_errno_with_message!(Errno::EINVAL, "offset cannot be negative");
}
let file = {
let current = current!();
let filetable = current.file_table().lock();
filetable.get_file(fd)?.clone()
};
if io_vec_count == 0 {
return Ok(0);
}
// Calculate the total buffer length and check for overflow
let total_len = io_vec_count
.checked_mul(core::mem::size_of::<IoVec>())
.and_then(|val| val.checked_add(offset as usize));
if total_len.is_none() {
return_errno_with_message!(Errno::EINVAL, "offset + io_vec_count overflow");
}
let mut total_len: usize = 0;
let mut cur_offset = offset as usize;
let io_vecs = copy_iovs_from_user(io_vec_ptr, io_vec_count)?;
for io_vec in io_vecs.as_ref() {
if io_vec.is_empty() {
continue;
}
if total_len.checked_add(io_vec.len()).is_none()
|| total_len
.checked_add(io_vec.len())
.and_then(|sum| sum.checked_add(cur_offset))
.is_none()
|| total_len
.checked_add(io_vec.len())
.and_then(|sum| sum.checked_add(cur_offset))
.map(|sum| sum > isize::MAX as usize)
.unwrap_or(false)
{
return_errno_with_message!(Errno::EINVAL, "Total length overflow");
}
let mut buffer = vec![0u8; io_vec.len()];
// TODO: According to the man page
// at <https://man7.org/linux/man-pages/man2/readv.2.html>,
// readv must be atomic,
// but the current implementation does not ensure atomicity.
// A suitable fix would be to add a `readv` method for the `FileLike` trait,
// allowing each subsystem to implement atomicity.
let read_len = file.read_at(cur_offset, &mut buffer)?;
io_vec.write_exact_to_user(&buffer)?;
total_len += read_len;
cur_offset += read_len;
if read_len == 0 || read_len < buffer.len() {
// End of file reached or no more data to read
break;
}
}
Ok(total_len)
}
fn do_sys_readv(fd: FileDesc, io_vec_ptr: Vaddr, io_vec_count: usize) -> Result<usize> {
debug!(
"fd = {}, io_vec_ptr = 0x{:x}, io_vec_counter = 0x{:x}",
fd, io_vec_ptr, io_vec_count
);
let file = {
let current = current!();
let filetable = current.file_table().lock();
filetable.get_file(fd)?.clone()
};
if io_vec_count == 0 {
return Ok(0);
}
let mut total_len = 0;
let io_vecs = copy_iovs_from_user(io_vec_ptr, io_vec_count)?;
for io_vec in io_vecs.as_ref() {
if io_vec.is_empty() {
continue;
}
let mut buffer = vec![0u8; io_vec.len()];
// TODO: According to the man page
// at <https://man7.org/linux/man-pages/man2/readv.2.html>,
// readv must be atomic,
// but the current implementation does not ensure atomicity.
// A suitable fix would be to add a `readv` method for the `FileLike` trait,
// allowing each subsystem to implement atomicity.
let read_len = file.read(&mut buffer)?;
io_vec.write_exact_to_user(&buffer)?;
total_len += read_len;
if read_len == 0 || read_len < buffer.len() {
// End of file reached or no more data to read
break;
}
}
Ok(total_len)
}
bitflags! {
struct RWFFlag: u32 {
const RWF_DSYNC = 0x00000001;
const RWF_HIPRI = 0x00000002;
const RWF_SYNC = 0x00000004;
const RWF_NOWAIT = 0x00000008;
}
}

View File

@ -0,0 +1,36 @@
// SPDX-License-Identifier: MPL-2.0
use super::SyscallReturn;
use crate::{fs::file_table::FileDesc, prelude::*, util::read_bytes_from_user};
pub fn sys_pwrite64(
fd: FileDesc,
user_buf_ptr: Vaddr,
user_buf_len: usize,
offset: i64,
) -> Result<SyscallReturn> {
debug!(
"fd = {}, user_buf_ptr = 0x{:x}, user_buf_len = 0x{:x}, offset = 0x{:x}",
fd, user_buf_ptr, user_buf_len, offset
);
if offset < 0 {
return_errno_with_message!(Errno::EINVAL, "offset cannot be negative");
}
let file = {
let current = current!();
let filetable = current.file_table().lock();
filetable.get_file(fd)?.clone()
};
// TODO: Check (f.file->f_mode & FMODE_PWRITE); We don't have f_mode in our FileLike trait
if user_buf_len == 0 {
return Ok(SyscallReturn::Return(0));
}
if offset.checked_add(user_buf_len as i64).is_none() {
return_errno_with_message!(Errno::EINVAL, "offset + user_buf_len overflow");
}
let mut buffer = vec![0u8; user_buf_len];
read_bytes_from_user(user_buf_ptr, &mut buffer)?;
let write_len = file.write_at(offset as _, &buffer)?;
Ok(SyscallReturn::Return(write_len as _))
}

View File

@ -0,0 +1,149 @@
// SPDX-License-Identifier: MPL-2.0
use super::SyscallReturn;
use crate::{fs::file_table::FileDesc, prelude::*, util::copy_iovs_from_user};
pub fn sys_writev(fd: FileDesc, io_vec_ptr: Vaddr, io_vec_count: usize) -> Result<SyscallReturn> {
let res = do_sys_writev(fd, io_vec_ptr, io_vec_count)?;
Ok(SyscallReturn::Return(res as _))
}
pub fn sys_pwritev(
fd: FileDesc,
io_vec_ptr: Vaddr,
io_vec_count: usize,
offset: i64,
) -> Result<SyscallReturn> {
let res = do_sys_pwritev(fd, io_vec_ptr, io_vec_count, offset, RWFFlag::empty())?;
Ok(SyscallReturn::Return(res as _))
}
pub fn sys_pwritev2(
fd: FileDesc,
io_vec_ptr: Vaddr,
io_vec_count: usize,
offset: i64,
flags: u32,
) -> Result<SyscallReturn> {
let flags = match RWFFlag::from_bits(flags) {
Some(flags) => flags,
None => return_errno_with_message!(Errno::EINVAL, "invalid flags"),
};
let res = if offset == -1 {
do_sys_writev(fd, io_vec_ptr, io_vec_count)?
} else {
do_sys_pwritev(fd, io_vec_ptr, io_vec_count, offset, flags)?
};
Ok(SyscallReturn::Return(res as _))
}
fn do_sys_pwritev(
fd: FileDesc,
io_vec_ptr: Vaddr,
io_vec_count: usize,
offset: i64,
_flags: RWFFlag,
) -> Result<usize> {
// TODO: Implement flags support
debug!(
"fd = {}, io_vec_ptr = 0x{:x}, io_vec_counter = 0x{:x}, offset = 0x{:x}",
fd, io_vec_ptr, io_vec_count, offset
);
if offset < 0 {
return_errno_with_message!(Errno::EINVAL, "offset cannot be negative");
}
let file = {
let current = current!();
let filetable = current.file_table().lock();
filetable.get_file(fd)?.clone()
};
// TODO: Check (f.file->f_mode & FMODE_PREAD); We don't have f_mode in our FileLike trait
if io_vec_count == 0 {
return Ok(0);
}
let mut total_len: usize = 0;
let mut cur_offset = offset as usize;
let io_vecs = copy_iovs_from_user(io_vec_ptr, io_vec_count)?;
for io_vec in io_vecs.as_ref() {
if io_vec.is_empty() {
continue;
}
if total_len.checked_add(io_vec.len()).is_none()
|| total_len
.checked_add(io_vec.len())
.and_then(|sum| sum.checked_add(cur_offset))
.is_none()
|| total_len
.checked_add(io_vec.len())
.and_then(|sum| sum.checked_add(cur_offset))
.map(|sum| sum > isize::MAX as usize)
.unwrap_or(false)
{
return_errno_with_message!(Errno::EINVAL, "Total length overflow");
}
let buffer = {
let mut buffer = vec![0u8; io_vec.len()];
io_vec.read_exact_from_user(&mut buffer)?;
buffer
};
// TODO: According to the man page
// at <https://man7.org/linux/man-pages/man2/readv.2.html>,
// writev must be atomic,
// but the current implementation does not ensure atomicity.
// A suitable fix would be to add a `writev` method for the `FileLike` trait,
// allowing each subsystem to implement atomicity.
let write_len = file.write_at(cur_offset, &buffer)?;
total_len += write_len;
cur_offset += write_len;
}
Ok(total_len)
}
fn do_sys_writev(fd: FileDesc, io_vec_ptr: Vaddr, io_vec_count: usize) -> Result<usize> {
debug!(
"fd = {}, io_vec_ptr = 0x{:x}, io_vec_counter = 0x{:x}",
fd, io_vec_ptr, io_vec_count
);
let file = {
let current = current!();
let filetable = current.file_table().lock();
filetable.get_file(fd)?.clone()
};
let mut total_len = 0;
let io_vecs = copy_iovs_from_user(io_vec_ptr, io_vec_count)?;
for io_vec in io_vecs.as_ref() {
if io_vec.is_empty() {
continue;
}
let buffer = {
let mut buffer = vec![0u8; io_vec.len()];
io_vec.read_exact_from_user(&mut buffer)?;
buffer
};
// TODO: According to the man page
// at <https://man7.org/linux/man-pages/man2/readv.2.html>,
// writev must be atomic,
// but the current implementation does not ensure atomicity.
// A suitable fix would be to add a `writev` method for the `FileLike` trait,
// allowing each subsystem to implement atomicity.
let write_len = file.write(&buffer)?;
total_len += write_len;
}
Ok(total_len)
}
bitflags! {
struct RWFFlag: u32 {
const RWF_DSYNC = 0x00000001;
const RWF_HIPRI = 0x00000002;
const RWF_SYNC = 0x00000004;
const RWF_NOWAIT = 0x00000008;
}
}

View File

@ -1,48 +0,0 @@
// SPDX-License-Identifier: MPL-2.0
#![allow(dead_code)]
use super::SyscallReturn;
use crate::{fs::file_table::FileDesc, prelude::*, util::copy_iovs_from_user};
pub fn sys_writev(fd: FileDesc, io_vec_ptr: Vaddr, io_vec_count: usize) -> Result<SyscallReturn> {
let res = do_sys_writev(fd, io_vec_ptr, io_vec_count)?;
Ok(SyscallReturn::Return(res as _))
}
fn do_sys_writev(fd: FileDesc, io_vec_ptr: Vaddr, io_vec_count: usize) -> Result<usize> {
debug!(
"fd = {}, io_vec_ptr = 0x{:x}, io_vec_counter = 0x{:x}",
fd, io_vec_ptr, io_vec_count
);
let file = {
let current = current!();
let filetable = current.file_table().lock();
filetable.get_file(fd)?.clone()
};
let mut total_len = 0;
let io_vecs = copy_iovs_from_user(io_vec_ptr, io_vec_count)?;
for io_vec in io_vecs.as_ref() {
if io_vec.is_empty() {
continue;
}
let buffer = {
let mut buffer = vec![0u8; io_vec.len()];
io_vec.read_exact_from_user(&mut buffer)?;
buffer
};
// FIXME: According to the man page
// at <https://man7.org/linux/man-pages/man2/readv.2.html>,
// writev must be atomic,
// but the current implementation does not ensure atomicity.
// A suitable fix would be to add a `writev` method for the `FileLike` trait,
// allowing each subsystem to implement atomicity.
let write_len = file.write(&buffer)?;
total_len += write_len;
}
Ok(total_len)
}

View File

@ -24,19 +24,13 @@ TESTS ?= \
mount_test \
open_create_test \
open_test \
pread64_test \
preadv2_test \
pwrite64_test \
pwritev2_test \
pty_test \
read_test \
rename_test \
sendfile_test \
stat_test \
statfs_test \
symlink_test \
sync_test \
timers_test \
truncate_test \
uidgid_test \
unlink_test \
vdso_clock_gettime_test \
readv_test \
write_test \
utimes_test \
# The end of the list

View File

@ -1,3 +1,2 @@
EventfdTest.IllegalPwrite
EventfdTest.SpliceFromPipePartialSucceeds
EventfdTest.NotifyNonZero_NoRandomSave

View File

@ -0,0 +1 @@
Pread64Test.Overflow

View File

@ -0,0 +1,3 @@
Preadv2Test.TestInvalidFlag
Preadv2Test.TestUnreadableFile
Preadv2Test.TestUnseekableFileInvalid

View File

@ -0,0 +1 @@
Pwritev2Test.InvalidFlag

View File

@ -0,0 +1,5 @@
ReadvTest.BadIovecBase_File
ReadvTest.BadIovecBase_Pipe
ReadvTest.NotReadable_Pipe
ReadvTest.IovecOutsideTaskAddressRangeInNonemptyArray
ReadvTestNoFixture.TruncatedAtMax_NoRandomSave