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 \
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
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 {
fn len(&self) -> usize {
fn size(&self) -> usize {
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,
// it returns the pty master. So the ptmx can not be used at upper layer.
impl Inode for Ptmx {
fn len(&self) -> usize {
fn size(&self) -> usize {
self.metadata.size
}

View File

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

View File

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

View File

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

View File

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

View File

@ -93,6 +93,13 @@ impl FileLike for InodeHandle<Rights> {
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 {
self.dentry().inode_metadata()
}

View File

@ -57,7 +57,7 @@ impl InodeHandle_ {
}
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) {
self.dentry.inode().write_direct_at(*offset, buf)?
@ -92,7 +92,7 @@ impl InodeHandle_ {
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);
file_size
.checked_add(off)
@ -116,8 +116,15 @@ impl InodeHandle_ {
*offset
}
pub fn len(&self) -> usize {
self.dentry.inode_len()
pub fn size(&self) -> usize {
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 {

View File

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

View File

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

View File

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

View File

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

View File

@ -410,9 +410,14 @@ impl Dentry {
self.inode.set_mode(mode)
}
/// Get the inode length
pub fn inode_len(&self) -> usize {
self.inode.len()
/// Gets the size of the inode
pub fn inode_size(&self) -> usize {
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

View File

@ -227,11 +227,7 @@ impl Metadata {
}
pub trait Inode: Any + Sync + Send {
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn size(&self) -> usize;
fn resize(&self, new_size: usize) -> Result<()>;
@ -364,11 +360,11 @@ impl dyn Inode {
return_errno!(Errno::EISDIR);
}
let file_len = self.len();
if buf.len() < file_len {
buf.resize(file_len, 0);
let file_size = self.size();
if buf.len() < file_size {
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> {
@ -376,11 +372,11 @@ impl dyn Inode {
return_errno!(Errno::EISDIR);
}
let file_len = self.len();
if buf.len() < file_len {
buf.resize(file_len, 0);
let file_size = self.size();
if buf.len() < file_size {
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 {

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 {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("PageCache")
@ -147,7 +157,9 @@ impl Pager for PageCacheManager {
let mut pages = self.pages.lock();
if let Some(page) = pages.pop(&idx) {
if let PageState::Dirty = page.state() {
let backend = self.backend();
let Some(backend) = self.backend.upgrade() else {
return Ok(());
};
if idx < backend.npages() {
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::tgkill::sys_tgkill;
use crate::syscall::time::sys_time;
use crate::syscall::truncate::{sys_ftruncate, sys_truncate};
use crate::syscall::umask::sys_umask;
use crate::syscall::uname::sys_uname;
use crate::syscall::unlink::{sys_unlink, sys_unlinkat};
@ -200,6 +201,7 @@ mod symlink;
mod sync;
mod tgkill;
mod time;
mod truncate;
mod umask;
mod uname;
mod unlink;
@ -290,6 +292,8 @@ define_syscall_nums!(
SYS_UNAME = 63,
SYS_FCNTL = 72,
SYS_FSYNC = 74,
SYS_TRUNCATE = 76,
SYS_FTRUNCATE = 77,
SYS_GETCWD = 79,
SYS_CHDIR = 80,
SYS_FCHDIR = 81,
@ -468,6 +472,8 @@ pub fn syscall_dispatch(
SYS_UNAME => syscall_handler!(1, sys_uname, args),
SYS_FCNTL => syscall_handler!(3, sys_fcntl, 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_CHDIR => syscall_handler!(1, sys_chdir, 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(())
}