implement pread* and pwrite* syscalls

This commit is contained in:
ootinnyoo
2025-12-26 21:36:05 -05:00
parent e31d1a05e8
commit bada97e048
12 changed files with 218 additions and 26 deletions

View File

@@ -67,10 +67,10 @@
| 0x40 (64) | write | (unsigned int fd, const char *buf, size_t count) | __arm64_sys_write | true |
| 0x41 (65) | readv | (unsigned long fd, const struct iovec *vec, unsigned long vlen) | __arm64_sys_readv | true |
| 0x42 (66) | writev | (unsigned long fd, const struct iovec *vec, unsigned long vlen) | __arm64_sys_writev | true |
| 0x43 (67) | pread64 | (unsigned int fd, char *buf, size_t count, loff_t pos) | __arm64_sys_pread64 | false |
| 0x44 (68) | pwrite64 | (unsigned int fd, const char *buf, size_t count, loff_t pos) | __arm64_sys_pwrite64 | false |
| 0x45 (69) | preadv | (unsigned long fd, const struct iovec *vec, unsigned long vlen, unsigned long pos_l, unsigned long pos_h) | __arm64_sys_preadv | false |
| 0x46 (70) | pwritev | (unsigned long fd, const struct iovec *vec, unsigned long vlen, unsigned long pos_l, unsigned long pos_h) | __arm64_sys_pwritev | false |
| 0x43 (67) | pread64 | (unsigned int fd, char *buf, size_t count, loff_t pos) | __arm64_sys_pread64 | true |
| 0x44 (68) | pwrite64 | (unsigned int fd, const char *buf, size_t count, loff_t pos) | __arm64_sys_pwrite64 | true |
| 0x45 (69) | preadv | (unsigned long fd, const struct iovec *vec, unsigned long vlen, unsigned long pos_l, unsigned long pos_h) | __arm64_sys_preadv | true |
| 0x46 (70) | pwritev | (unsigned long fd, const struct iovec *vec, unsigned long vlen, unsigned long pos_l, unsigned long pos_h) | __arm64_sys_pwritev | true |
| 0x47 (71) | sendfile64 | (int out_fd, int in_fd, loff_t *offset, size_t count) | __arm64_sys_sendfile64 | true |
| 0x48 (72) | pselect6 | (int n, fd_set *inp, fd_set *outp, fd_set *exp, struct __kernel_timespec *tsp, void *sig) | __arm64_sys_pselect6 | true |
| 0x49 (73) | ppoll | (struct pollfd *ufds, unsigned int nfds, struct __kernel_timespec *tsp, const sigset_t *sigmask, size_t sigsetsize) | __arm64_sys_ppoll | true |
@@ -270,8 +270,8 @@
| 0x11b (283) | membarrier | (int cmd, unsigned int flags, int cpu_id) | __arm64_sys_membarrier | false |
| 0x11c (284) | mlock2 | (unsigned long start, size_t len, int flags) | __arm64_sys_mlock2 | false |
| 0x11d (285) | copy_file_range | (int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags) | __arm64_sys_copy_file_range | false |
| 0x11e (286) | preadv2 | (unsigned long fd, const struct iovec *vec, unsigned long vlen, unsigned long pos_l, unsigned long pos_h, rwf_t flags) | __arm64_sys_preadv2 | false |
| 0x11f (287) | pwritev2 | (unsigned long fd, const struct iovec *vec, unsigned long vlen, unsigned long pos_l, unsigned long pos_h, rwf_t flags) | __arm64_sys_pwritev2 | false |
| 0x11e (286) | preadv2 | (unsigned long fd, const struct iovec *vec, unsigned long vlen, unsigned long pos_l, unsigned long pos_h, rwf_t flags) | __arm64_sys_preadv2 | true |
| 0x11f (287) | pwritev2 | (unsigned long fd, const struct iovec *vec, unsigned long vlen, unsigned long pos_l, unsigned long pos_h, rwf_t flags) | __arm64_sys_pwritev2 | true |
| 0x120 (288) | pkey_mprotect | (unsigned long start, size_t len, unsigned long prot, int pkey) | __arm64_sys_pkey_mprotect | false |
| 0x121 (289) | pkey_alloc | (unsigned long flags, unsigned long init_val) | __arm64_sys_pkey_alloc | false |
| 0x122 (290) | pkey_free | (int pkey) | __arm64_sys_pkey_free | false |

View File

@@ -24,8 +24,8 @@ use crate::{
chown::sys_fchown,
close::sys_close,
ioctl::sys_ioctl,
iov::{sys_readv, sys_writev},
rw::{sys_read, sys_write},
iov::{sys_preadv, sys_preadv2, sys_pwritev, sys_pwritev2, sys_readv, sys_writev},
rw::{sys_pread64, sys_pwrite64, sys_read, sys_write},
seek::sys_lseek,
splice::sys_sendfile,
stat::sys_fstat,
@@ -172,6 +172,42 @@ pub async fn handle_syscall() {
0x40 => sys_write(arg1.into(), TUA::from_value(arg2 as _), arg3 as _).await,
0x41 => sys_readv(arg1.into(), TUA::from_value(arg2 as _), arg3 as _).await,
0x42 => sys_writev(arg1.into(), TUA::from_value(arg2 as _), arg3 as _).await,
0x43 => {
sys_pread64(
arg1.into(),
TUA::from_value(arg2 as _),
arg3 as _,
arg4 as _,
)
.await
}
0x44 => {
sys_pwrite64(
arg1.into(),
TUA::from_value(arg2 as _),
arg3 as _,
arg4 as _,
)
.await
}
0x45 => {
sys_preadv(
arg1.into(),
TUA::from_value(arg2 as _),
arg3 as _,
arg4 as _,
)
.await
}
0x46 => {
sys_pwritev(
arg1.into(),
TUA::from_value(arg2 as _),
arg3 as _,
arg4 as _,
)
.await
}
0x47 => {
sys_sendfile(
arg1.into(),
@@ -364,6 +400,26 @@ pub async fn handle_syscall() {
.await
}
0x116 => sys_getrandom(TUA::from_value(arg1 as _), arg2 as _, arg3 as _).await,
0x11e => {
sys_preadv2(
arg1.into(),
TUA::from_value(arg2 as _),
arg3 as _,
arg4 as _,
arg5 as _,
)
.await
}
0x11f => {
sys_pwritev2(
arg1.into(),
TUA::from_value(arg2 as _),
arg3 as _,
arg4 as _,
arg5 as _,
)
.await
}
0x125 => Err(KernelError::NotSupported),
0x1b7 => {
sys_faccessat2(

View File

@@ -84,6 +84,10 @@ impl Tty {
#[async_trait]
impl FileOps for Tty {
async fn read(&mut self, _ctx: &mut FileCtx, usr_buf: UA, count: usize) -> Result<usize> {
self.readat(usr_buf, count, 0).await
}
async fn readat(&mut self, usr_buf: UA, count: usize, _offset: u64) -> Result<usize> {
let (cooked_pipe, eof_fut) = {
let cooker = self.input_cooker.lock_save_irq();
@@ -129,7 +133,11 @@ impl FileOps for Tty {
})
}
async fn write(&mut self, _ctx: &mut FileCtx, mut ptr: UA, count: usize) -> Result<usize> {
async fn write(&mut self, _ctx: &mut FileCtx, ptr: UA, count: usize) -> Result<usize> {
self.writeat(ptr, count, 0).await
}
async fn writeat(&mut self, mut ptr: UA, count: usize, _offset: u64) -> Result<usize> {
const CHUNK_SZ: usize = 128;
let mut remaining = count;

View File

@@ -3,7 +3,7 @@ use crate::{
CharDriver, DriverManager, OpenableDevice, ReservedMajors, fs::dev::devfs,
init::PlatformBus,
},
fs::{fops::FileOps, open_file::FileCtx, open_file::OpenFile},
fs::{fops::FileOps, open_file::OpenFile},
kernel_driver,
};
use alloc::string::ToString;
@@ -22,12 +22,12 @@ struct NullFileOps;
#[async_trait]
impl FileOps for NullFileOps {
async fn read(&mut self, _ctx: &mut FileCtx, _buf: UA, _count: usize) -> Result<usize> {
async fn readat(&mut self, _buf: UA, _count: usize, _offset: u64) -> Result<usize> {
// EOF
Ok(0)
}
async fn write(&mut self, _ctx: &mut FileCtx, _buf: UA, count: usize) -> Result<usize> {
async fn writeat(&mut self, _buf: UA, count: usize, _offset: u64) -> Result<usize> {
// Pretend we wrote everything successfully.
Ok(count)
}

View File

@@ -25,7 +25,11 @@ struct ZeroFileOps;
#[async_trait]
impl FileOps for ZeroFileOps {
async fn read(&mut self, _ctx: &mut FileCtx, mut buf: UA, mut count: usize) -> Result<usize> {
async fn read(&mut self, _ctx: &mut FileCtx, buf: UA, count: usize) -> Result<usize> {
self.readat(buf, count, 0).await
}
async fn readat(&mut self, mut buf: UA, mut count: usize, _offset: u64) -> Result<usize> {
let requested = count;
while count > 0 {
@@ -39,7 +43,11 @@ impl FileOps for ZeroFileOps {
Ok(requested)
}
async fn write(&mut self, _ctx: &mut FileCtx, _buf: UA, count: usize) -> Result<usize> {
async fn write(&mut self, _ctx: &mut FileCtx, buf: UA, count: usize) -> Result<usize> {
self.writeat(buf, count, 0).await
}
async fn writeat(&mut self, _buf: UA, count: usize, _offset: u64) -> Result<usize> {
Ok(count)
}

View File

@@ -62,10 +62,18 @@ impl FileOps for DirFile {
Err(FsError::IsADirectory.into())
}
async fn readat(&mut self, _buf: UA, _count: usize, _offset: u64) -> Result<usize> {
Err(FsError::IsADirectory.into())
}
async fn write(&mut self, _ctx: &mut FileCtx, _buf: UA, _count: usize) -> Result<usize> {
Err(FsError::IsADirectory.into())
}
async fn writeat(&mut self, _buf: UA, _count: usize, _offset: u64) -> Result<usize> {
Err(FsError::IsADirectory.into())
}
async fn readdir<'a>(&'a mut self, ctx: &'a mut FileCtx) -> Result<OpenFileDirIter<'a>> {
if self.inode.getattr().await?.file_type != FileType::Directory {
return Err(FsError::NotADirectory.into());

View File

@@ -42,16 +42,32 @@ macro_rules! process_iovec {
pub trait FileOps: Send + Sync {
/// Reads data from the current file position into `buf`.
/// The file's cursor is advanced by the number of bytes read.
async fn read(&mut self, ctx: &mut FileCtx, buf: UA, count: usize) -> Result<usize>;
async fn read(&mut self, ctx: &mut FileCtx, buf: UA, count: usize) -> Result<usize> {
let total_bytes_read = self.readat(buf, count, ctx.pos).await?;
ctx.pos += total_bytes_read as u64;
Ok(total_bytes_read)
}
async fn readat(&mut self, buf: UA, count: usize, offset: u64) -> Result<usize>;
/// Writes data from `buf` to the current file position.
/// The file's cursor is advanced by the number of bytes written.
async fn write(&mut self, ctx: &mut FileCtx, buf: UA, count: usize) -> Result<usize>;
async fn write(&mut self, ctx: &mut FileCtx, buf: UA, count: usize) -> Result<usize> {
let total_bytes_written = self.writeat(buf, count, ctx.pos).await?;
ctx.pos += total_bytes_written as u64;
Ok(total_bytes_written)
}
async fn writeat(&mut self, buf: UA, count: usize, offset: u64) -> Result<usize>;
async fn readv(&mut self, ctx: &mut FileCtx, iovecs: &[IoVec]) -> Result<usize> {
process_iovec!(iovecs, |addr, count| self.read(ctx, addr, count)).await
}
async fn readvat(&mut self, iovecs: &[IoVec], offset: u64) -> Result<usize> {
process_iovec!(iovecs, |addr, count| self.readat(addr, count, offset)).await
}
async fn readdir<'a>(&'a mut self, _ctx: &'a mut FileCtx) -> Result<OpenFileDirIter<'a>> {
Err(FsError::NotADirectory.into())
}
@@ -60,6 +76,10 @@ pub trait FileOps: Send + Sync {
process_iovec!(iovecs, |addr, count| self.write(ctx, addr, count)).await
}
async fn writevat(&mut self, iovecs: &[IoVec], offset: u64) -> Result<usize> {
process_iovec!(iovecs, |addr, count| self.writeat(addr, count, offset)).await
}
/// Puts the current task to sleep until a call to `read()` would no longer
/// block.
fn poll_read_ready(&self) -> Pin<Box<dyn Future<Output = Result<()>> + 'static + Send>> {

View File

@@ -61,6 +61,10 @@ impl PipeReader {
#[async_trait]
impl FileOps for PipeReader {
async fn read(&mut self, _ctx: &mut FileCtx, u_buf: UA, count: usize) -> Result<usize> {
self.readat(u_buf, count, 0).await
}
async fn readat(&mut self, u_buf: UA, count: usize, _offset: u64) -> Result<usize> {
if count == 0 {
return Ok(0);
}
@@ -73,6 +77,10 @@ impl FileOps for PipeReader {
Err(KernelError::BadFd)
}
async fn writeat(&mut self, _buf: UA, _count: usize, _offset: u64) -> Result<usize> {
Err(KernelError::BadFd)
}
async fn seek(&mut self, _ctx: &mut FileCtx, _pos: SeekFrom) -> Result<u64> {
Err(KernelError::SeekPipe)
}
@@ -135,7 +143,15 @@ impl FileOps for PipeWriter {
Err(KernelError::BadFd)
}
async fn readat(&mut self, _buf: UA, _count: usize, _offset: u64) -> Result<usize> {
Err(KernelError::BadFd)
}
async fn write(&mut self, _ctx: &mut FileCtx, u_buf: UA, count: usize) -> Result<usize> {
self.writeat(u_buf, count, 0).await
}
async fn writeat(&mut self, u_buf: UA, count: usize, _offset: u64) -> Result<usize> {
if count == 0 {
return Ok(0);
}

View File

@@ -31,11 +31,11 @@ impl RegFile {
impl FileOps for RegFile {
/// Reads data from the current file position into `buf`. The file's cursor
/// is advanced by the number of bytes read.
async fn read(
async fn readat(
&mut self,
ctx: &mut FileCtx,
mut user_buf: UA,
mut count: usize,
mut offset: u64,
) -> Result<usize> {
let mut pg = ClaimedPage::alloc_zeroed()?;
let kbuf = pg.as_slice_mut();
@@ -45,7 +45,7 @@ impl FileOps for RegFile {
let chunk_sz = min(PAGE_SIZE, count);
copy_from_user_slice(user_buf, &mut kbuf[..chunk_sz]).await?;
let bytes_read = self.inode.read_at(ctx.pos, &mut kbuf[..chunk_sz]).await?;
let bytes_read = self.inode.read_at(offset, &mut kbuf[..chunk_sz]).await?;
if bytes_read == 0 {
break;
@@ -53,7 +53,7 @@ impl FileOps for RegFile {
copy_to_user_slice(&kbuf[..bytes_read], user_buf).await?;
ctx.pos += bytes_read as u64;
offset += bytes_read as u64;
total_bytes_read += bytes_read;
user_buf = user_buf.add_bytes(bytes_read);
count -= bytes_read;
@@ -64,7 +64,7 @@ impl FileOps for RegFile {
/// Writes data from `buf` to the current file position.
/// The file's cursor is advanced by the number of bytes written.
async fn write(&mut self, ctx: &mut FileCtx, mut buf: UA, mut count: usize) -> Result<usize> {
async fn writeat(&mut self, mut buf: UA, mut count: usize, mut offset: u64) -> Result<usize> {
let mut pg = ClaimedPage::alloc_zeroed()?;
let kbuf = pg.as_slice_mut();
let mut total_bytes_written = 0;
@@ -74,7 +74,7 @@ impl FileOps for RegFile {
copy_from_user_slice(buf, &mut kbuf[..chunk_sz]).await?;
let bytes_written = self.inode.write_at(ctx.pos, &kbuf[..chunk_sz]).await?;
let bytes_written = self.inode.write_at(offset, &kbuf[..chunk_sz]).await?;
// If we wrote 0 bytes, the disk might be full or the file cannot be
// extended.
@@ -82,7 +82,7 @@ impl FileOps for RegFile {
break;
}
ctx.pos += bytes_written as u64;
offset += bytes_written as u64;
total_bytes_written += bytes_written;
count -= bytes_written;
buf = buf.add_bytes(bytes_written);

View File

@@ -45,3 +45,51 @@ pub async fn sys_readv(fd: Fd, iov_ptr: TUA<IoVec>, no_iov: usize) -> Result<usi
ops.readv(state, &iovs).await
}
pub async fn sys_pwritev(fd: Fd, iov_ptr: TUA<IoVec>, no_iov: usize, offset: u64) -> Result<usize> {
sys_pwritev2(fd, iov_ptr, no_iov, offset, 0).await
}
pub async fn sys_preadv(fd: Fd, iov_ptr: TUA<IoVec>, no_iov: usize, offset: u64) -> Result<usize> {
sys_preadv2(fd, iov_ptr, no_iov, offset, 0).await
}
pub async fn sys_pwritev2(
fd: Fd,
iov_ptr: TUA<IoVec>,
no_iov: usize,
offset: u64,
_flags: u32, // TODO: implement these flags
) -> Result<usize> {
let file = current_task()
.fd_table
.lock_save_irq()
.get(fd)
.ok_or(KernelError::BadFd)?;
let iovs = copy_obj_array_from_user(iov_ptr, no_iov).await?;
let (ops, _state) = &mut *file.lock().await;
ops.writevat(&iovs, offset).await
}
pub async fn sys_preadv2(
fd: Fd,
iov_ptr: TUA<IoVec>,
no_iov: usize,
offset: u64,
_flags: u32,
) -> Result<usize> {
let file = current_task()
.fd_table
.lock_save_irq()
.get(fd)
.ok_or(KernelError::BadFd)?;
let iovs = copy_obj_array_from_user(iov_ptr, no_iov).await?;
let (ops, _state) = &mut *file.lock().await;
ops.readvat(&iovs, offset).await
}

View File

@@ -27,3 +27,27 @@ pub async fn sys_read(fd: Fd, user_buf: UA, count: usize) -> Result<usize> {
ops.read(ctx, user_buf, count).await
}
pub async fn sys_pwrite64(fd: Fd, user_buf: UA, count: usize, offset: u64) -> Result<usize> {
let file = current_task()
.fd_table
.lock_save_irq()
.get(fd)
.ok_or(KernelError::BadFd)?;
let (ops, _ctx) = &mut *file.lock().await;
ops.writeat(user_buf, count, offset).await
}
pub async fn sys_pread64(fd: Fd, user_buf: UA, count: usize, offset: u64) -> Result<usize> {
let file = current_task()
.fd_table
.lock_save_irq()
.get(fd)
.ok_or(KernelError::BadFd)?;
let (ops, _ctx) = &mut *file.lock().await;
ops.readat(user_buf, count, offset).await
}

View File

@@ -482,13 +482,17 @@ fn test_ftruncate() {
let mut buffer = [1u8; 5];
unsafe {
let fd = libc::open(c_file.as_ptr(), libc::O_RDWR | libc::O_CREAT, 0o777);
let ret = libc::write(fd, data.as_ptr() as *const libc::c_void, data.len());
let ret = libc::pwrite64(fd, data.as_ptr() as *const libc::c_void, data.len(), 0);
if ret < 0 || ret as usize != data.len() {
panic!("write failed");
}
libc::ftruncate(fd, 5);
libc::lseek(fd, 0, libc::SEEK_SET);
let ret = libc::read(fd, buffer.as_mut_ptr() as *mut libc::c_void, buffer.len());
let ret = libc::pread64(
fd,
buffer.as_mut_ptr() as *mut libc::c_void,
buffer.len(),
0,
);
if ret < 0 || ret as usize != 5 {
panic!("read failed");
}