Enable some fs system call test cases

This commit is contained in:
LI Qing
2023-11-28 12:17:42 +08:00
committed by Tate, Hongliang Tian
parent 5bc1312a91
commit ddca4fb2fc
25 changed files with 197 additions and 69 deletions

View File

@ -35,3 +35,11 @@ jobs:
- name: Syscall Test (MicroVM)
id: syscall_test_microvm
run: make run AUTO_TEST=syscall ENABLE_KVM=0 BOOT_METHOD=microvm RELEASE_MODE=1
- name: Syscall Test at Ext2 (Linux Boot Protocol)
id: syscall_test_at_ext2_linux
run: make run AUTO_TEST=syscall SYSCALL_TEST_DIR=/ext2 ENABLE_KVM=0 BOOT_PROTOCOL=linux RELEASE_MODE=1
- name: Syscall Test at Ext2 (MicroVM)
id: syscall_test_at_ext2_microvm
run: make run AUTO_TEST=syscall SYSCALL_TEST_DIR=/ext2 ENABLE_KVM=0 BOOT_METHOD=microvm RELEASE_MODE=1

View File

@ -12,6 +12,7 @@ KTEST ?= 0
KTEST_CRATES ?= all
KTEST_WHITELIST ?=
SKIP_GRUB_MENU ?= 1
SYSCALL_TEST_DIR ?= /tmp
RELEASE_MODE ?= 0
# End of setting up Make varaiables
@ -20,6 +21,7 @@ KERNEL_CMDLINE += ktest.whitelist="$(KTEST_WHITELIST)"
INIT_CMDLINE := sh -l
ifeq ($(AUTO_TEST), syscall)
BUILD_SYSCALL_TEST := 1
KERNEL_CMDLINE += SYSCALL_TEST_DIR=$(SYSCALL_TEST_DIR)
INIT_CMDLINE += /opt/syscall_test/run_syscall_test.sh
endif
ifeq ($(AUTO_TEST), regression)

View File

@ -1,4 +1,7 @@
TESTS ?= open_test read_test statfs_test chmod_test pty_test uidgid_test vdso_clock_gettime_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 \
statfs_test symlink_test sync_test uidgid_test unlink_test \
vdso_clock_gettime_test write_test
MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
CUR_DIR := $(patsubst %/,%,$(dir $(MKFILE_PATH)))

View File

@ -0,0 +1,11 @@
GetdentsTest/0.VerifyEntries
GetdentsTest/0.VerifyPadding
GetdentsTest/0.SmallDir
GetdentsTest/0.LargeDir
GetdentsTest/0.PartialBuffer
GetdentsTest/0.ProcSelfFd
GetdentsTest/0.NotDir
GetdentsTest/0.SeekResetsCursor
GetdentsTest/0.Issue128ProcSeekEnd
GetdentsTest/1.PartialBuffer
GetdentsTest/1.ProcSelfFd

View File

@ -0,0 +1 @@
LinkTest.PermissionDenied

View File

@ -0,0 +1,6 @@
LseekTest.Overflow
LseekTest.ProcFile
LseekTest.SysDir
LseekTest.SeekCurrentDir
LseekTest.ProcStatTwice
LseekTest.EtcPasswdDup

View File

@ -0,0 +1 @@
MkdirTest.FailsOnDirWithoutWritePerms

View File

@ -0,0 +1,9 @@
CreateTest.HonorsUmask_NoRandomSave
CreateTest.CreatWithOTrunc
CreateTest.CreatDirWithOTruncAndReadOnly
CreateTest.CreateFailsOnUnpermittedDir
CreateTest.CreateFailsOnDirWithoutWritePerms
CreateTest.ChmodReadToWriteBetweenOpens_NoRandomSave
CreateTest.ChmodWriteToReadBetweenOpens_NoRandomSave
CreateTest.CreateWithReadFlagNotAllowedByMode_NoRandomSave
CreateTest.CreateWithWriteFlagNotAllowedByMode_NoRandomSave

View File

@ -0,0 +1,7 @@
RenameTest.RootToAnything
RenameTest.AnythingToRoot
RenameTest.FailsWithDots
RenameTest.FailsWhenOldParentNotWritable
RenameTest.FailsWhenNewParentNotWritable
RenameTest.OverwriteFailsWhenNewParentNotWritable
RenameTest.FileDoesNotExistWhenNewParentNotExecutable

View File

@ -0,0 +1,4 @@
StatTest.FstatatSymlinkDirWithTrailingSlash
StatTest.FstatatSymlinkDirWithTrailingSlashSameInode
StatTest.LstatSymlinkDir
SimpleStatTest.AnonDeviceAllocatesUniqueInodesAcrossSaveRestore

View File

@ -0,0 +1,7 @@
SymlinkTest.CannotCreateSymlinkInReadOnlyDir
SymlinkTest.PwriteToSymlink
SymlinkTest.SymlinkAtDegradedPermissions_NoRandomSave
SymlinkTest.ReadlinkAtDegradedPermissions_NoRandomSave
SymlinkTest.FollowUpdatesATime
AbsAndRelTarget/ParamSymlinkTest.CreatLinkCreatesTarget/0
AbsAndRelTarget/ParamSymlinkTest.CreatLinkCreatesTarget/1

View File

@ -0,0 +1,3 @@
SyncTest.SyncFileSytem
SyncTest.SyncFromPipe
SyncTest.CannotSyncFileSytemAtBadFd

View File

@ -0,0 +1,4 @@
UnlinkTest.AtDirDegradedPermissions_NoRandomSave
UnlinkTest.ParentDegradedPermissions
UnlinkTest.AtBad
UnlinkTest.TooLongName

View File

@ -0,0 +1,3 @@
WriteTest.WriteNoExceedsRLimit
WriteTest.WriteExceedsRLimit
WriteTest.PwriteNoChangeOffset

View File

@ -1,6 +1,7 @@
#!/bin/sh
SCRIPT_DIR=$(dirname "$0")
TEST_TMP_DIR=${SYSCALL_TEST_DIR:-/tmp}
TEST_BIN_DIR=$SCRIPT_DIR/tests
BLOCKLIST_DIR=$SCRIPT_DIR/blocklists
FAIL_CASES=$SCRIPT_DIR/fail_cases
@ -24,8 +25,11 @@ get_blocklist_subtests(){
run_one_test(){
echo -e "Run Test Case: $1"
# The gvisor test framework utilizes the "TEST_TMPDIR" environment variable to dictate the directory's location.
export TEST_TMPDIR=$TEST_TMP_DIR
ret=0
if [ -f $TEST_BIN_DIR/$1 ]; then
rm -rf $TEST_TMP_DIR/*
get_blocklist_subtests $1
$TEST_BIN_DIR/$1 --gtest_filter=-$BLOCK
ret=$?

View File

@ -59,11 +59,14 @@ impl FsResolver {
let access_mode = AccessMode::from_u32(flags)?;
let inode_mode = InodeMode::from_bits_truncate(mode);
let follow_tail_link = !creation_flags.contains(CreationFlags::O_NOFOLLOW);
let follow_tail_link = !(creation_flags.contains(CreationFlags::O_NOFOLLOW)
|| creation_flags.contains(CreationFlags::O_CREAT)
&& creation_flags.contains(CreationFlags::O_EXCL));
let dentry = match self.lookup_inner(path, follow_tail_link) {
Ok(dentry) => {
let inode = dentry.inode();
if inode.type_() == InodeType::SymLink
&& creation_flags.contains(CreationFlags::O_NOFOLLOW)
&& !status_flags.contains(StatusFlags::O_PATH)
{
return_errno_with_message!(Errno::ELOOP, "file is a symlink");

View File

@ -621,15 +621,12 @@ impl Inode for RamInode {
return_errno_with_message!(Errno::EPERM, "old is a dir");
}
let mut self_inode = self.0.write();
if self_inode.inner.as_direntry().unwrap().contains_entry(name) {
let self_dir = self_inode.inner.as_direntry_mut().unwrap();
if self_dir.contains_entry(name) {
return_errno_with_message!(Errno::EEXIST, "entry exist");
}
self_inode
.inner
.as_direntry_mut()
.unwrap()
.append_entry(name, old.0.read().this.upgrade().unwrap());
self_dir.append_entry(name, old.0.read().this.upgrade().unwrap());
self_inode.inc_size();
drop(self_inode);
old.0.write().inc_nlinks();
@ -660,8 +657,11 @@ impl Inode for RamInode {
if self.0.read().metadata.type_ != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
}
if name == "." || name == ".." {
return_errno_with_message!(Errno::EISDIR, "rmdir on . or ..");
if name == "." {
return_errno_with_message!(Errno::EINVAL, "rmdir on .");
}
if name == ".." {
return_errno_with_message!(Errno::ENOTEMPTY, "rmdir on ..");
}
let mut self_inode = self.0.write();
let self_dir = self_inode.inner.as_direntry_mut().unwrap();
@ -724,65 +724,101 @@ impl Inode for RamInode {
if new_name == "." || new_name == ".." {
return_errno_with_message!(Errno::EISDIR, "new_name is . or ..");
}
let src_inode = self.lookup(old_name)?;
if src_inode.metadata().ino == target.metadata().ino {
return_errno_with_message!(Errno::EINVAL, "target is a descendant of old");
}
if let Ok(dst_inode) = target.lookup(new_name) {
if src_inode.metadata().ino == dst_inode.metadata().ino {
return Ok(());
}
match (src_inode.metadata().type_, dst_inode.metadata().type_) {
(InodeType::Dir, InodeType::Dir) => {
let dst_inode = dst_inode.downcast_ref::<RamInode>().unwrap();
if !dst_inode
.0
.read()
.inner
.as_direntry()
.unwrap()
.is_empty_children()
{
return_errno_with_message!(Errno::ENOTEMPTY, "dir not empty");
// Perform necessary checks to ensure that `dst_inode` can be replaced by `src_inode`.
let check_replace_inode =
|src_inode: &Arc<RamInode>, dst_inode: &Arc<RamInode>| -> Result<()> {
if src_inode.metadata().ino == dst_inode.metadata().ino {
return Ok(());
}
match (src_inode.metadata().type_, dst_inode.metadata().type_) {
(InodeType::Dir, InodeType::Dir) => {
if !dst_inode
.0
.read()
.inner
.as_direntry()
.unwrap()
.is_empty_children()
{
return_errno_with_message!(Errno::ENOTEMPTY, "dir not empty");
}
}
(InodeType::Dir, _) => {
return_errno_with_message!(Errno::ENOTDIR, "old is not dir");
}
(_, InodeType::Dir) => {
return_errno_with_message!(Errno::EISDIR, "new is dir");
}
_ => {}
}
(InodeType::Dir, _) => {
return_errno_with_message!(Errno::ENOTDIR, "old is not dir");
}
(_, InodeType::Dir) => {
return_errno_with_message!(Errno::EISDIR, "new is dir");
}
_ => {}
}
}
Ok(())
};
// Rename in the same directory
if self.metadata().ino == target.metadata().ino {
let mut self_inode = self.0.write();
let self_dir = self_inode.inner.as_direntry_mut().unwrap();
let (idx, inode) = self_dir
let (src_idx, src_inode) = self_dir
.get_entry(old_name)
.ok_or(Error::new(Errno::ENOENT))?;
self_dir.substitute_entry(idx, (CStr256::from(new_name), inode));
} else {
let is_dir = src_inode.0.read().metadata.type_ == InodeType::Dir;
if let Some((dst_idx, dst_inode)) = self_dir.get_entry(new_name) {
check_replace_inode(&src_inode, &dst_inode)?;
self_dir.remove_entry(dst_idx);
self_dir.substitute_entry(src_idx, (CStr256::from(new_name), src_inode.clone()));
self_inode.dec_size();
if is_dir {
self_inode.dec_nlinks();
}
} else {
self_dir.substitute_entry(src_idx, (CStr256::from(new_name), src_inode.clone()));
}
}
// Or rename across different directories
else {
let (mut self_inode, mut target_inode) = write_lock_two_inodes(self, target);
let self_inode_arc = self_inode.this.upgrade().unwrap();
let target_inode_arc = target_inode.this.upgrade().unwrap();
let self_dir = self_inode.inner.as_direntry_mut().unwrap();
let (idx, src_inode) = self_dir
let (src_idx, src_inode) = self_dir
.get_entry(old_name)
.ok_or(Error::new(Errno::ENOENT))?;
self_dir.remove_entry(idx);
target_inode
.inner
.as_direntry_mut()
.unwrap()
.append_entry(new_name, src_inode.clone());
self_inode.dec_size();
target_inode.inc_size();
if src_inode.0.read().metadata.type_ == InodeType::Dir {
self_inode.dec_nlinks();
target_inode.inc_nlinks();
// Avoid renaming a directory to a subdirectory of itself
if Arc::ptr_eq(&src_inode, &target_inode_arc) {
return_errno!(Errno::EINVAL);
}
let is_dir = src_inode.0.read().metadata.type_ == InodeType::Dir;
let target_dir = target_inode.inner.as_direntry_mut().unwrap();
if let Some((dst_idx, dst_inode)) = target_dir.get_entry(new_name) {
// Avoid renaming a subdirectory to a directory.
if Arc::ptr_eq(&self_inode_arc, &dst_inode) {
return_errno!(Errno::ENOTEMPTY);
}
check_replace_inode(&src_inode, &dst_inode)?;
self_dir.remove_entry(src_idx);
target_dir.remove_entry(dst_idx);
target_dir.append_entry(new_name, src_inode.clone());
self_inode.dec_size();
if is_dir {
self_inode.dec_nlinks();
}
} else {
self_dir.remove_entry(src_idx);
target_dir.append_entry(new_name, src_inode.clone());
self_inode.dec_size();
target_inode.inc_size();
if is_dir {
self_inode.dec_nlinks();
target_inode.inc_nlinks();
}
}
drop(self_inode);
drop(target_inode);
if src_inode.0.read().metadata.type_ == InodeType::Dir {
if is_dir {
src_inode
.0
.write()

View File

@ -25,6 +25,6 @@ impl FileCreationMask {
impl Default for FileCreationMask {
fn default() -> Self {
Self(0o777)
Self(0o022)
}
}

View File

@ -1,7 +1,7 @@
//! constants used in syscall
/// LONGEST ALLOWED FILENAME
pub const MAX_FILENAME_LEN: usize = 128;
pub const MAX_FILENAME_LEN: usize = 4096;
pub const MAX_ARGV_NUMBER: usize = 128;
pub const MAX_ENVP_NUMBER: usize = 128;
pub const MAX_ARG_LEN: usize = 2048;

View File

@ -37,11 +37,8 @@ pub fn sys_linkat(
return_errno_with_message!(Errno::ENOENT, "oldpath is empty");
}
let new_pathname = new_pathname.to_string_lossy();
if new_pathname.ends_with('/') {
return_errno_with_message!(Errno::EPERM, "newpath is dir");
}
if new_pathname.is_empty() {
return_errno_with_message!(Errno::ENOENT, "newpath is empty");
if new_pathname.ends_with('/') || new_pathname.is_empty() {
return_errno_with_message!(Errno::ENOENT, "newpath is dir or is empty");
}
let old_fs_path = FsPath::new(old_dirfd, old_pathname.as_ref())?;

View File

@ -32,7 +32,11 @@ pub fn sys_mkdirat(
let fs_path = FsPath::new(dirfd, pathname.as_ref())?;
current.fs().read().lookup_dir_and_base_name(&fs_path)?
};
let inode_mode = InodeMode::from_bits_truncate(mode);
let inode_mode = {
let mask_mode = mode & !current.umask().read().get();
InodeMode::from_bits_truncate(mask_mode)
};
let _ = dir_dentry.create(name.trim_end_matches('/'), InodeType::Dir, inode_mode)?;
Ok(SyscallReturn::Return(0))
}

View File

@ -28,7 +28,8 @@ pub fn sys_openat(
let file_handle = {
let pathname = pathname.to_string_lossy();
let fs_path = FsPath::new(dirfd, pathname.as_ref())?;
let inode_handle = current.fs().read().open(&fs_path, flags, mode)?;
let mask_mode = mode & !current.umask().read().get();
let inode_handle = current.fs().read().open(&fs_path, flags, mask_mode)?;
Arc::new(inode_handle)
};
let mut file_table = current.file_table().lock();

View File

@ -1,4 +1,7 @@
use crate::fs::fs_resolver::FsPath;
use crate::fs::{
file_table::FileDescripter,
fs_resolver::{FsPath, AT_FDCWD},
};
use crate::log_syscall_entry;
use crate::prelude::*;
use crate::syscall::constants::MAX_FILENAME_LEN;
@ -8,14 +11,21 @@ use super::SyscallReturn;
use super::SYS_RMDIR;
pub fn sys_rmdir(pathname_addr: Vaddr) -> Result<SyscallReturn> {
self::sys_rmdirat(AT_FDCWD, pathname_addr)
}
pub(super) fn sys_rmdirat(dirfd: FileDescripter, pathname_addr: Vaddr) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_RMDIR);
let pathname = read_cstring_from_user(pathname_addr, MAX_FILENAME_LEN)?;
debug!("pathname = {:?}", pathname);
debug!("dirfd = {}, pathname = {:?}", dirfd, pathname);
let current = current!();
let (dir_dentry, name) = {
let pathname = pathname.to_string_lossy();
let fs_path = FsPath::try_from(pathname.as_ref())?;
if pathname == "/" {
return_errno_with_message!(Errno::EBUSY, "is root directory");
}
let fs_path = FsPath::new(dirfd, pathname.as_ref())?;
current.fs().read().lookup_dir_and_base_name(&fs_path)?
};
dir_dentry.rmdir(name.trim_end_matches('/'))?;

View File

@ -18,7 +18,7 @@ pub fn sys_unlinkat(
let flags =
UnlinkFlags::from_bits(flags).ok_or(Error::with_message(Errno::EINVAL, "invalid flags"))?;
if flags.contains(UnlinkFlags::AT_REMOVEDIR) {
return super::rmdir::sys_rmdir(pathname_addr);
return super::rmdir::sys_rmdirat(dirfd, pathname_addr);
}
log_syscall_entry!(SYS_UNLINKAT);

View File

@ -23,6 +23,10 @@ pub fn sys_write(
let current = current!();
let file_table = current.file_table().lock();
let file = file_table.get_file(fd)?;
if user_buf_len == 0 {
return Ok(SyscallReturn::Return(0));
}
let mut buffer = vec![0u8; user_buf_len];
read_bytes_from_user(user_buf_ptr, &mut buffer)?;
debug!("write content = {:?}", buffer);