Add support for truncate syscall

This commit is contained in:
LI Qing
2024-01-05 14:44:19 +08:00
committed by Tate, Hongliang Tian
parent 31998d1cd4
commit 195c6a0739
19 changed files with 167 additions and 44 deletions

View File

@ -2,7 +2,7 @@
TESTS ?= chmod_test fsync_test getdents_test link_test lseek_test mkdir_test \ TESTS ?= chmod_test fsync_test getdents_test link_test lseek_test mkdir_test \
open_create_test open_test pty_test read_test rename_test stat_test \ open_create_test open_test pty_test read_test rename_test stat_test \
statfs_test symlink_test sync_test uidgid_test unlink_test \ statfs_test symlink_test sync_test truncate_test uidgid_test unlink_test \
vdso_clock_gettime_test write_test vdso_clock_gettime_test write_test
MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST))) MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))

View File

@ -0,0 +1,5 @@
FixtureTruncateTest.Truncate
FixtureTruncateTest.Ftruncate
FixtureTruncateTest.FtruncateShrinkGrow
TruncateTest.TruncateNonWriteable
TruncateTest.FtruncateVirtualTmp_NoRandomSave

View File

@ -137,7 +137,7 @@ impl RootInode {
} }
impl Inode for RootInode { impl Inode for RootInode {
fn len(&self) -> usize { fn size(&self) -> usize {
self.metadata.size self.metadata.size
} }

View File

@ -63,7 +63,7 @@ impl Ptmx {
// Many methods are left to do nothing because every time the ptmx is being opened, // Many methods are left to do nothing because every time the ptmx is being opened,
// it returns the pty master. So the ptmx can not be used at upper layer. // it returns the pty master. So the ptmx can not be used at upper layer.
impl Inode for Ptmx { impl Inode for Ptmx {
fn len(&self) -> usize { fn size(&self) -> usize {
self.metadata.size self.metadata.size
} }

View File

@ -43,7 +43,7 @@ impl Inode for PtySlaveInode {
false false
} }
fn len(&self) -> usize { fn size(&self) -> usize {
self.metadata.size self.metadata.size
} }

View File

@ -12,7 +12,7 @@ use aster_rights::Full;
use core::time::Duration; use core::time::Duration;
impl Inode for Ext2Inode { impl Inode for Ext2Inode {
fn len(&self) -> usize { fn size(&self) -> usize {
self.file_size() as _ self.file_size() as _
} }

View File

@ -630,8 +630,8 @@ impl Inner {
} }
pub fn resize(&mut self, new_size: usize) -> Result<()> { pub fn resize(&mut self, new_size: usize) -> Result<()> {
self.page_cache.pages().resize(new_size)?;
self.inode_impl.resize(new_size)?; self.inode_impl.resize(new_size)?;
self.page_cache.pages().resize(new_size)?;
Ok(()) Ok(())
} }

View File

@ -29,6 +29,10 @@ pub trait FileLike: Send + Sync + Any {
IoEvents::empty() IoEvents::empty()
} }
fn resize(&self, new_size: usize) -> Result<()> {
return_errno_with_message!(Errno::EINVAL, "resize is not supported");
}
fn flush(&self) -> Result<()> { fn flush(&self) -> Result<()> {
Ok(()) Ok(())
} }

View File

@ -93,6 +93,13 @@ impl FileLike for InodeHandle<Rights> {
self.0.ioctl(cmd, arg) self.0.ioctl(cmd, arg)
} }
fn resize(&self, new_size: usize) -> Result<()> {
if !self.1.contains(Rights::WRITE) {
return_errno_with_message!(Errno::EINVAL, "File is not writable");
}
self.0.resize(new_size)
}
fn metadata(&self) -> Metadata { fn metadata(&self) -> Metadata {
self.dentry().inode_metadata() self.dentry().inode_metadata()
} }

View File

@ -57,7 +57,7 @@ impl InodeHandle_ {
} }
if self.status_flags().contains(StatusFlags::O_APPEND) { if self.status_flags().contains(StatusFlags::O_APPEND) {
*offset = self.dentry.inode_len(); *offset = self.dentry.inode_size();
} }
let len = if self.status_flags().contains(StatusFlags::O_DIRECT) { let len = if self.status_flags().contains(StatusFlags::O_DIRECT) {
self.dentry.inode().write_direct_at(*offset, buf)? self.dentry.inode().write_direct_at(*offset, buf)?
@ -92,7 +92,7 @@ impl InodeHandle_ {
off as isize off as isize
} }
SeekFrom::End(off /* as isize */) => { SeekFrom::End(off /* as isize */) => {
let file_size = self.dentry.inode_len() as isize; let file_size = self.dentry.inode_size() as isize;
assert!(file_size >= 0); assert!(file_size >= 0);
file_size file_size
.checked_add(off) .checked_add(off)
@ -116,8 +116,15 @@ impl InodeHandle_ {
*offset *offset
} }
pub fn len(&self) -> usize { pub fn size(&self) -> usize {
self.dentry.inode_len() self.dentry.inode_size()
}
pub fn resize(&self, new_size: usize) -> Result<()> {
if self.status_flags().contains(StatusFlags::O_APPEND) {
return_errno_with_message!(Errno::EPERM, "can not resize append-only file");
}
self.dentry.set_inode_size(new_size)
} }
pub fn access_mode(&self) -> AccessMode { pub fn access_mode(&self) -> AccessMode {

View File

@ -56,7 +56,7 @@ impl<D: DirOps> ProcDir<D> {
} }
impl<D: DirOps + 'static> Inode for ProcDir<D> { impl<D: DirOps + 'static> Inode for ProcDir<D> {
fn len(&self) -> usize { fn size(&self) -> usize {
self.info.size() self.info.size()
} }

View File

@ -28,7 +28,7 @@ impl<F: FileOps> ProcFile<F> {
} }
impl<F: FileOps + 'static> Inode for ProcFile<F> { impl<F: FileOps + 'static> Inode for ProcFile<F> {
fn len(&self) -> usize { fn size(&self) -> usize {
self.info.size() self.info.size()
} }

View File

@ -28,7 +28,7 @@ impl<S: SymOps> ProcSym<S> {
} }
impl<S: SymOps + 'static> Inode for ProcSym<S> { impl<S: SymOps + 'static> Inode for ProcSym<S> {
fn len(&self) -> usize { fn size(&self) -> usize {
self.info.size() self.info.size()
} }

View File

@ -412,8 +412,9 @@ impl RamInode {
} }
impl PageCacheBackend for RamInode { impl PageCacheBackend for RamInode {
fn read_page(&self, _idx: usize, _frame: &VmFrame) -> Result<()> { fn read_page(&self, _idx: usize, frame: &VmFrame) -> Result<()> {
// do nothing // Initially, any block/page in a RamFs inode contains all zeros
frame.zero();
Ok(()) Ok(())
} }
@ -446,9 +447,9 @@ impl Inode for RamInode {
return_errno_with_message!(Errno::EISDIR, "read is not supported"); return_errno_with_message!(Errno::EISDIR, "read is not supported");
}; };
let (offset, read_len) = { let (offset, read_len) = {
let file_len = self_inode.metadata.size; let file_size = self_inode.metadata.size;
let start = file_len.min(offset); let start = file_size.min(offset);
let end = file_len.min(offset + buf.len()); let end = file_size.min(offset + buf.len());
(start, end - start) (start, end - start)
}; };
page_cache page_cache
@ -470,17 +471,17 @@ impl Inode for RamInode {
let Some(page_cache) = self_inode.inner.as_file() else { let Some(page_cache) = self_inode.inner.as_file() else {
return_errno_with_message!(Errno::EISDIR, "write is not supported"); return_errno_with_message!(Errno::EISDIR, "write is not supported");
}; };
let file_len = self_inode.metadata.size; let file_size = self_inode.metadata.size;
let new_len = offset + buf.len(); let new_size = offset + buf.len();
let should_expand_len = new_len > file_len; let should_expand_size = new_size > file_size;
if should_expand_len { if should_expand_size {
page_cache.pages().resize(new_len)?; page_cache.pages().resize(new_size)?;
} }
page_cache.pages().write_bytes(offset, buf)?; page_cache.pages().write_bytes(offset, buf)?;
if should_expand_len { if should_expand_size {
// Turn the read guard into a write guard without releasing the lock. // Turn the read guard into a write guard without releasing the lock.
let mut self_inode = self_inode.upgrade(); let mut self_inode = self_inode.upgrade();
self_inode.resize(new_len); self_inode.resize(new_size);
} }
Ok(buf.len()) Ok(buf.len())
} }
@ -489,12 +490,26 @@ impl Inode for RamInode {
self.write_at(offset, buf) self.write_at(offset, buf)
} }
fn len(&self) -> usize { fn size(&self) -> usize {
self.0.read().metadata.size self.0.read().metadata.size
} }
fn resize(&self, new_size: usize) -> Result<()> { fn resize(&self, new_size: usize) -> Result<()> {
self.0.write().resize(new_size); let self_inode = self.0.upread();
if self_inode.inner.as_file().is_none() {
return_errno!(Errno::EISDIR);
}
let file_size = self_inode.metadata.size;
if file_size == new_size {
return Ok(());
}
let mut self_inode = self_inode.upgrade();
self_inode.resize(new_size);
let page_cache = self_inode.inner.as_file().unwrap();
page_cache.pages().resize(new_size)?;
Ok(()) Ok(())
} }

View File

@ -410,9 +410,14 @@ impl Dentry {
self.inode.set_mode(mode) self.inode.set_mode(mode)
} }
/// Get the inode length /// Gets the size of the inode
pub fn inode_len(&self) -> usize { pub fn inode_size(&self) -> usize {
self.inode.len() self.inode.size()
}
/// Sets the size of the inode
pub fn set_inode_size(&self, new_size: usize) -> Result<()> {
self.inode.resize(new_size)
} }
/// Get the access timestamp /// Get the access timestamp

View File

@ -227,11 +227,7 @@ impl Metadata {
} }
pub trait Inode: Any + Sync + Send { pub trait Inode: Any + Sync + Send {
fn len(&self) -> usize; fn size(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn resize(&self, new_size: usize) -> Result<()>; fn resize(&self, new_size: usize) -> Result<()>;
@ -364,11 +360,11 @@ impl dyn Inode {
return_errno!(Errno::EISDIR); return_errno!(Errno::EISDIR);
} }
let file_len = self.len(); let file_size = self.size();
if buf.len() < file_len { if buf.len() < file_size {
buf.resize(file_len, 0); buf.resize(file_size, 0);
} }
self.read_at(0, &mut buf[..file_len]) self.read_at(0, &mut buf[..file_size])
} }
pub fn read_direct_to_end(&self, buf: &mut Vec<u8>) -> Result<usize> { pub fn read_direct_to_end(&self, buf: &mut Vec<u8>) -> Result<usize> {
@ -376,11 +372,11 @@ impl dyn Inode {
return_errno!(Errno::EISDIR); return_errno!(Errno::EISDIR);
} }
let file_len = self.len(); let file_size = self.size();
if buf.len() < file_len { if buf.len() < file_size {
buf.resize(file_len, 0); buf.resize(file_size, 0);
} }
self.read_direct_at(0, &mut buf[..file_len]) self.read_direct_at(0, &mut buf[..file_size])
} }
pub fn writer(&self, from_offset: usize) -> InodeWriter { pub fn writer(&self, from_offset: usize) -> InodeWriter {

View File

@ -56,6 +56,16 @@ impl PageCache {
} }
} }
impl Drop for PageCache {
fn drop(&mut self) {
// TODO:
// The default destruction procedure exhibits slow performance.
// In contrast, resizing the `VMO` to zero greatly accelerates the process.
// We need to find out the underlying cause of this discrepancy.
let _ = self.pages.resize(0);
}
}
impl Debug for PageCache { impl Debug for PageCache {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("PageCache") f.debug_struct("PageCache")
@ -147,7 +157,9 @@ impl Pager for PageCacheManager {
let mut pages = self.pages.lock(); let mut pages = self.pages.lock();
if let Some(page) = pages.pop(&idx) { if let Some(page) = pages.pop(&idx) {
if let PageState::Dirty = page.state() { if let PageState::Dirty = page.state() {
let backend = self.backend(); let Some(backend) = self.backend.upgrade() else {
return Ok(());
};
if idx < backend.npages() { if idx < backend.npages() {
backend.write_page(idx, page.frame())?; backend.write_page(idx, page.frame())?;
} }

View File

@ -65,6 +65,7 @@ use crate::syscall::symlink::{sys_symlink, sys_symlinkat};
use crate::syscall::sync::sys_sync; use crate::syscall::sync::sys_sync;
use crate::syscall::tgkill::sys_tgkill; use crate::syscall::tgkill::sys_tgkill;
use crate::syscall::time::sys_time; use crate::syscall::time::sys_time;
use crate::syscall::truncate::{sys_ftruncate, sys_truncate};
use crate::syscall::umask::sys_umask; use crate::syscall::umask::sys_umask;
use crate::syscall::uname::sys_uname; use crate::syscall::uname::sys_uname;
use crate::syscall::unlink::{sys_unlink, sys_unlinkat}; use crate::syscall::unlink::{sys_unlink, sys_unlinkat};
@ -200,6 +201,7 @@ mod symlink;
mod sync; mod sync;
mod tgkill; mod tgkill;
mod time; mod time;
mod truncate;
mod umask; mod umask;
mod uname; mod uname;
mod unlink; mod unlink;
@ -290,6 +292,8 @@ define_syscall_nums!(
SYS_UNAME = 63, SYS_UNAME = 63,
SYS_FCNTL = 72, SYS_FCNTL = 72,
SYS_FSYNC = 74, SYS_FSYNC = 74,
SYS_TRUNCATE = 76,
SYS_FTRUNCATE = 77,
SYS_GETCWD = 79, SYS_GETCWD = 79,
SYS_CHDIR = 80, SYS_CHDIR = 80,
SYS_FCHDIR = 81, SYS_FCHDIR = 81,
@ -468,6 +472,8 @@ pub fn syscall_dispatch(
SYS_UNAME => syscall_handler!(1, sys_uname, args), SYS_UNAME => syscall_handler!(1, sys_uname, args),
SYS_FCNTL => syscall_handler!(3, sys_fcntl, args), SYS_FCNTL => syscall_handler!(3, sys_fcntl, args),
SYS_FSYNC => syscall_handler!(1, sys_fsync, args), SYS_FSYNC => syscall_handler!(1, sys_fsync, args),
SYS_TRUNCATE => syscall_handler!(2, sys_truncate, args),
SYS_FTRUNCATE => syscall_handler!(2, sys_ftruncate, args),
SYS_GETCWD => syscall_handler!(2, sys_getcwd, args), SYS_GETCWD => syscall_handler!(2, sys_getcwd, args),
SYS_CHDIR => syscall_handler!(1, sys_chdir, args), SYS_CHDIR => syscall_handler!(1, sys_chdir, args),
SYS_FCHDIR => syscall_handler!(1, sys_fchdir, args), SYS_FCHDIR => syscall_handler!(1, sys_fchdir, args),

View File

@ -0,0 +1,66 @@
// SPDX-License-Identifier: MPL-2.0
use crate::fs::{
file_table::FileDescripter,
fs_resolver::{FsPath, AT_FDCWD},
utils::PATH_MAX,
};
use crate::log_syscall_entry;
use crate::prelude::*;
use crate::process::ResourceType;
use crate::util::read_cstring_from_user;
use super::SyscallReturn;
use super::{SYS_FTRUNCATE, SYS_TRUNCATE};
pub fn sys_ftruncate(fd: FileDescripter, len: isize) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_FTRUNCATE);
debug!("fd = {}, lentgh = {}", fd, len);
check_length(len)?;
let current = current!();
let file_table = current.file_table().lock();
let file = file_table.get_file(fd)?;
file.resize(len as usize)?;
Ok(SyscallReturn::Return(0))
}
pub fn sys_truncate(path_ptr: Vaddr, len: isize) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_TRUNCATE);
let path = read_cstring_from_user(path_ptr, PATH_MAX)?;
debug!("path = {:?}, length = {}", path, len);
check_length(len)?;
let current = current!();
let dentry = {
let path = path.to_string_lossy();
if path.is_empty() {
return_errno_with_message!(Errno::ENOENT, "path is empty");
}
let fs_path = FsPath::new(AT_FDCWD, path.as_ref())?;
current.fs().read().lookup(&fs_path)?
};
dentry.set_inode_size(len as usize)?;
Ok(SyscallReturn::Return(0))
}
#[inline]
fn check_length(len: isize) -> Result<()> {
if len < 0 {
return_errno_with_message!(Errno::EINVAL, "length is negative");
}
let max_file_size = {
let current = current!();
let resource_limits = current.resource_limits().lock();
resource_limits
.get_rlimit(ResourceType::RLIMIT_FSIZE)
.get_cur() as usize
};
if len as usize > max_file_size {
return_errno_with_message!(Errno::EFBIG, "length is larger than the maximum file size");
}
Ok(())
}