diff --git a/Makefile b/Makefile
index b8cf0324..34311466 100644
--- a/Makefile
+++ b/Makefile
@@ -9,7 +9,7 @@ setup:
@cargo install mdbook
build:
- @make --no-print-directory -C regression/ramdisk
+ @make --no-print-directory -C regression
@cargo kbuild
tools:
@@ -33,4 +33,4 @@ check:
clean:
@cargo clean
@cd docs && mdbook clean
- @make --no-print-directory -C regression/ramdisk clean
+ @make --no-print-directory -C regression clean
diff --git a/README.md b/README.md
index eba94c01..d5ff8db6 100644
--- a/README.md
+++ b/README.md
@@ -77,6 +77,23 @@ Then we can use the tool to check access control policy.
cargo component-check
```
+### Syscall Test
+
+If we have already built the project for normal use, please clean the project before running syscall test.
+```bash
+make clean
+```
+
+For running syscall tests, we can run following command to build test binaries and run the OS for testing purpose.
+```bash
+ENABLE_SYSCALL_TEST=1 make run
+```
+
+Then, we can run the following script inside the OS to run all syscall test cases.
+```bash
+/opt/syscall_test/run_syscall_test.sh
+```
+
## Code organization
The codebase of Jinux is organized as below.
diff --git a/boot/limine/scripts/limine-build.sh b/boot/limine/scripts/limine-build.sh
index 69188a64..0c3c1458 100755
--- a/boot/limine/scripts/limine-build.sh
+++ b/boot/limine/scripts/limine-build.sh
@@ -25,7 +25,7 @@ cp target/limine/limine-cd.bin target/iso_root
cp target/limine/limine-cd-efi.bin target/iso_root
# Copy ramdisk
-cp regression/ramdisk/build/ramdisk.cpio target/iso_root
+cp regression/build/ramdisk.cpio target/iso_root
xorriso -as mkisofs \
-b limine-cd.bin \
diff --git a/regression/.gitignore b/regression/.gitignore
new file mode 100644
index 00000000..567609b1
--- /dev/null
+++ b/regression/.gitignore
@@ -0,0 +1 @@
+build/
diff --git a/regression/ramdisk/Makefile b/regression/Makefile
similarity index 53%
rename from regression/ramdisk/Makefile
rename to regression/Makefile
index 1c8f4653..d07c8ab1 100644
--- a/regression/ramdisk/Makefile
+++ b/regression/Makefile
@@ -1,47 +1,45 @@
-MAKEFLAGS += --no-builtin-rules # Prevent the implicit rules from compiling ".c" or ".s" files automatically.
-APPS := ../apps
-BUILD_DIR := ./build
-INITRAMFS := ./initramfs
+MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
+CUR_DIR := $(patsubst %/,%,$(dir $(MKFILE_PATH)))
+BUILD_DIR := $(CUR_DIR)/build
+INITRAMFS := $(BUILD_DIR)/initramfs
RAMDISK := $(BUILD_DIR)/ramdisk.cpio
SHELL := /bin/bash
-ifneq (, $(wildcard $(APPS)/. ))
- APPS_DIRS := $(shell find $(APPS) -type d 2>/dev/null | sed 's/ /\\ /g' | sed 's/:/\\:/g' || true)
- APPS_FILES := $(shell find $(APPS) -type f 2>/dev/null | sed 's/ /\\ /g' | sed 's/:/\\:/g' || true)
-endif
-
ifneq (, $(wildcard $(INITRAMFS)/. ))
INITRAMFS_DIRS := $(shell find $(INITRAMFS) -type d 2>/dev/null | sed 's/ /\\ /g' | sed 's/:/\\:/g' || true)
INITRAMFS_FILES := $(shell find $(INITRAMFS) -type f 2>/dev/null | sed 's/ /\\ /g' | sed 's/:/\\:/g' || true)
endif
+.PHONY: all clean
-.PHONY: all clean prepare_libs copy_special_files
+all: build
-all: $(RAMDISK)
-
-$(INITRAMFS): $(APPS) $(APPS_DIRS) $(APPS_FILES)
+$(INITRAMFS):
@rm -rf $@ && mkdir -p $@
-# Copy Apps
- @cp -a $(APPS)/* $@
- @cd $@ && find . \( -name "*.s" -o -name "*.c" -o -name "Makefile" -o -name "README.md" \) -delete
-# Mkdir folders
+ @# Mkdir necessary folders
@mkdir -p $@/tmp
- @mkdir -p $@/test
+ @mkdir -p $@/opt
@mkdir -p $@/lib64
@mkdir -p $@/lib/x86_64-linux-gnu
-# Copy libs
+ @# Copy necessary libs
@cp -L /lib64/ld-linux-x86-64.so.2 $@/lib64
@cp -L /lib/x86_64-linux-gnu/libc.so.6 $@/lib/x86_64-linux-gnu
@cp -L /lib/x86_64-linux-gnu/libstdc++.so.6 $@/lib/x86_64-linux-gnu
@cp -L /lib/x86_64-linux-gnu/libm.so.6 $@/lib/x86_64-linux-gnu
@cp -L /lib/x86_64-linux-gnu/libgcc_s.so.1 $@/lib/x86_64-linux-gnu
@cp -L /lib/x86_64-linux-gnu/libpthread.so.0 $@/lib/x86_64-linux-gnu
+ @# Copy from apps
+ @make --no-print-directory -C apps
+ifeq ($(ENABLE_SYSCALL_TEST), 1)
+ @# Copy syscall test suite
+ @make --no-print-directory -C syscall_test
+endif
$(RAMDISK): $(INITRAMFS) $(INITRAMFS_DIRS) $(INITRAMFS_FILES)
@echo "Generating the ramdisk image..."
- @rm -rf $(BUILD_DIR) && mkdir -p $(BUILD_DIR)
- @./mkinitramfs $(INITRAMFS) $@
+ @(cd $(INITRAMFS); find . | cpio -o -H newc) > $@
+
+build: $(RAMDISK)
clean:
- @rm -rf $(INITRAMFS) $(BUILD_DIR)
+ @rm -rf $(BUILD_DIR)
diff --git a/regression/apps/Makefile b/regression/apps/Makefile
new file mode 100644
index 00000000..9de37f7f
--- /dev/null
+++ b/regression/apps/Makefile
@@ -0,0 +1,12 @@
+MAKEFLAGS += --no-builtin-rules # Prevent the implicit rules from compiling ".c" or ".s" files automatically.
+MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
+CUR_DIR := $(patsubst %/,%,$(dir $(MKFILE_PATH)))
+INITRAMFS ?= $(CUR_DIR)/../build/initramfs
+
+.PHONY: all
+
+all: build
+
+build:
+ @cp -a $(CUR_DIR)/* $(INITRAMFS)
+ @cd $(INITRAMFS) && find . \( -name "*.s" -o -name "*.c" -o -name "Makefile" -o -name "README.md" \) -delete
diff --git a/regression/ramdisk/mkinitramfs b/regression/ramdisk/mkinitramfs
deleted file mode 100755
index 52b8fc54..00000000
--- a/regression/ramdisk/mkinitramfs
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/bash
-set -e
-
-if [ $# -ne 2 ]; then
- echo "Usage: mkinitramfs
"
- exit 1
-fi
-
-if [ -d "$1" ]; then
- echo "Creating $2 from $1"
- (cd "$1"; find . | cpio -o -H newc) > "$2"
-else
- echo "The first argument must be a directory"
- exit 1
-fi
diff --git a/regression/syscall_test/Makefile b/regression/syscall_test/Makefile
new file mode 100644
index 00000000..4b13e9ca
--- /dev/null
+++ b/regression/syscall_test/Makefile
@@ -0,0 +1,43 @@
+TESTS ?= open_test read_test
+
+MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
+CUR_DIR := $(patsubst %/,%,$(dir $(MKFILE_PATH)))
+BUILD_DIR := $(CUR_DIR)/../build
+SRC_DIR := $(BUILD_DIR)/gvisor_src
+BIN_DIR := $(BUILD_DIR)/syscall_test_bins
+INITRAMFS ?= $(CUR_DIR)/../build/initramfs
+TARGET_DIR := $(INITRAMFS)/opt/syscall_test
+RUN_BASH := $(CUR_DIR)/run_syscall_test.sh
+BLOCK_LIST := $(CUR_DIR)/blocklists
+
+.PHONY: all clean
+
+all: $(TESTS)
+
+$(SRC_DIR):
+ @if ! type bazel > /dev/null; then \
+ echo "bazel is not installed, please run $(CUR_DIR)/install_bazel.sh with sudo permission to install it."; \
+ exit 1 ; \
+ fi
+ @rm -rf $@ && mkdir -p $@
+ @cd $@ && git clone -b 20200921.0 https://github.com/jinzhao-dev/gvisor.git .
+
+$(BIN_DIR): $(SRC_DIR)
+ @rm -rf $@ && mkdir -p $@
+ @cd $(SRC_DIR) && bazel build --test_tag_filters=native //test/syscalls/...
+ @cp $(SRC_DIR)/bazel-bin/test/syscalls/linux/*_test $@
+
+$(TARGET_DIR): $(RUN_BASH) $(BLOCK_LIST) $(BIN_DIR)
+ @rm -rf $@ && mkdir -p $@
+ @# Prepare tests dir for test binaries
+ @mkdir $@/tests
+ @# Copy blocklists
+ @cp -rf $(BLOCK_LIST) $@
+ @# Copy bash script
+ @cp -f $(RUN_BASH) $@
+
+$(TESTS): $(TARGET_DIR)
+ @cp -f $(BIN_DIR)/$@ $(TARGET_DIR)/tests
+
+clean:
+ @rm -rf $(BIN_DIR) $(TARGET_DIR)
diff --git a/regression/syscall_test/blocklists/open_test b/regression/syscall_test/blocklists/open_test
new file mode 100644
index 00000000..108aca4a
--- /dev/null
+++ b/regression/syscall_test/blocklists/open_test
@@ -0,0 +1,15 @@
+OpenTest.AppendConcurrentWrite
+OpenTest.CanTruncateReadOnly
+OpenTest.CanTruncateWithStrangePermissions
+OpenTest.CanTruncateReadOnlyNoWritePermission_NoRandomSave
+OpenTest.CanTruncateWriteOnlyNoReadPermission_NoRandomSave
+OpenTest.FileNotDirectory
+OpenTest.MustCreateExisting
+OpenTest.NameTooLong
+OpenTest.Null
+OpenTest.OTrunc
+OpenTest.OTruncAndReadOnlyDir
+OpenTest.OpenNoFollowStillFollowsLinksInPath
+OpenTest.OpenNonDirectoryWithTrailingSlash
+OpenTest.SymlinkRecurse
+OpenTest.Truncate
\ No newline at end of file
diff --git a/regression/syscall_test/blocklists/read_test b/regression/syscall_test/blocklists/read_test
new file mode 100644
index 00000000..02c450bb
--- /dev/null
+++ b/regression/syscall_test/blocklists/read_test
@@ -0,0 +1,2 @@
+ReadTest.EofAfterRead
+ReadTest.ZeroBuffer
\ No newline at end of file
diff --git a/regression/syscall_test/install_bazel.sh b/regression/syscall_test/install_bazel.sh
new file mode 100755
index 00000000..b64516b4
--- /dev/null
+++ b/regression/syscall_test/install_bazel.sh
@@ -0,0 +1,17 @@
+#! /bin/bash
+
+set -e
+
+if type bazel > /dev/null; then
+ echo "Bazel has been installed already"
+ exit 0
+fi
+
+apt update && apt install curl gnupg -y
+curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor > bazel.gpg
+mv bazel.gpg /etc/apt/trusted.gpg.d/
+
+echo 'deb [arch=amd64] https://storage.googleapis.com/bazel-apt stable jdk1.8' | tee /etc/apt/sources.list.d/bazel.list
+apt update && apt install bazel=5.4.0 -y
+
+echo "Bazel is installed successfully"
diff --git a/regression/syscall_test/run_syscall_test.sh b/regression/syscall_test/run_syscall_test.sh
new file mode 100755
index 00000000..449fb3cf
--- /dev/null
+++ b/regression/syscall_test/run_syscall_test.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+SCRIPT_DIR=$(dirname "$0")
+TEST_BIN_DIR=$SCRIPT_DIR/tests
+BLOCKLIST_DIR=$SCRIPT_DIR/blocklists
+FAIL_CASES=$SCRIPT_DIR/fail_cases
+BLOCK=""
+TESTS=0
+PASSED_TESTS=0
+RESULT=0
+GREEN='\033[0;32m'
+RED='\033[0;31m'
+NC='\033[0m'
+
+get_blocklist_subtests(){
+ if [ -f $BLOCKLIST_DIR/$1 ]; then
+ BLOCK=$(sed ':a;N;$!ba;s/\n/:/g' $BLOCKLIST_DIR/$1)
+ return 0
+ else
+ BLOCK=""
+ return 1
+ fi
+}
+
+run_one_test(){
+ echo -e "Run Test Case: $1"
+ ret=0
+ if [ -f $TEST_BIN_DIR/$1 ]; then
+ get_blocklist_subtests $1
+ $TEST_BIN_DIR/$1 --gtest_filter=-$BLOCK
+ ret=$?
+ else
+ echo -e "Warning: $1 test does not exit"
+ ret=1
+ fi
+ echo ""
+ return $ret
+}
+
+rm -f $FAIL_CASES && touch $FAIL_CASES
+
+for syscall_test in $(find $TEST_BIN_DIR/. -name \*_test) ; do
+ test_name=$(basename "$syscall_test")
+ run_one_test $test_name
+ if [ $? -eq 0 ] && PASSED_TESTS=$((PASSED_TESTS+1));then
+ TESTS=$((TESTS+1))
+ else
+ echo -e "$test_name" >> $FAIL_CASES
+ TESTS=$((TESTS+1))
+ fi
+done
+
+echo -e "$GREEN$PASSED_TESTS$NC of $GREEN$TESTS$NC test cases are passed."
+[ $PASSED_TESTS -ne $TESTS ] && RESULT=1
+if [ $TESTS != $PASSED_TESTS ]; then
+ echo -e "The $RED$(($TESTS-$PASSED_TESTS))$NC failed test cases in this run are as follows:"
+ cat $FAIL_CASES
+fi
+
+exit $RESULT
diff --git a/services/libs/jinux-std/src/fs/file_table.rs b/services/libs/jinux-std/src/fs/file_table.rs
index 28f9fcbb..40d8ee79 100644
--- a/services/libs/jinux-std/src/fs/file_table.rs
+++ b/services/libs/jinux-std/src/fs/file_table.rs
@@ -108,6 +108,23 @@ impl FileTable {
entry.map(|e| e.file)
}
+ pub fn close_all(&mut self) -> Vec> {
+ let mut closed_files = Vec::new();
+ let closed_fds: Vec = self
+ .table
+ .idxes_and_items()
+ .map(|(idx, _)| idx as FileDescripter)
+ .collect();
+ for fd in closed_fds {
+ let entry = self.table.remove(fd as usize).unwrap();
+ let events = FdEvents::Close(fd);
+ self.notify_fd_events(&events);
+ entry.notify_fd_events(&events);
+ closed_files.push(entry.file);
+ }
+ closed_files
+ }
+
pub fn get_file(&self, fd: FileDescripter) -> Result<&Arc> {
self.table
.get(fd as usize)
diff --git a/services/libs/jinux-std/src/fs/pipe.rs b/services/libs/jinux-std/src/fs/pipe.rs
index d829aadb..a813e19c 100644
--- a/services/libs/jinux-std/src/fs/pipe.rs
+++ b/services/libs/jinux-std/src/fs/pipe.rs
@@ -2,7 +2,9 @@ use crate::events::Observer;
use crate::prelude::*;
use super::file_handle::FileLike;
-use super::utils::{AccessMode, Consumer, IoEvents, Poller, Producer, StatusFlags};
+use super::utils::{
+ AccessMode, Consumer, InodeMode, InodeType, IoEvents, Metadata, Poller, Producer, StatusFlags,
+};
pub struct PipeReader {
consumer: Consumer,
@@ -55,6 +57,25 @@ impl FileLike for PipeReader {
AccessMode::O_RDONLY
}
+ fn metadata(&self) -> Metadata {
+ Metadata {
+ dev: 0,
+ ino: 0,
+ size: 0,
+ blk_size: 0,
+ blocks: 0,
+ atime: Default::default(),
+ mtime: Default::default(),
+ ctime: Default::default(),
+ type_: InodeType::NamedPipe,
+ mode: InodeMode::from_bits_truncate(0o400),
+ nlinks: 1,
+ uid: 0,
+ gid: 0,
+ rdev: 0,
+ }
+ }
+
fn register_observer(
&self,
observer: Weak>,
@@ -122,6 +143,25 @@ impl FileLike for PipeWriter {
AccessMode::O_WRONLY
}
+ fn metadata(&self) -> Metadata {
+ Metadata {
+ dev: 0,
+ ino: 0,
+ size: 0,
+ blk_size: 0,
+ blocks: 0,
+ atime: Default::default(),
+ mtime: Default::default(),
+ ctime: Default::default(),
+ type_: InodeType::NamedPipe,
+ mode: InodeMode::from_bits_truncate(0o200),
+ nlinks: 1,
+ uid: 0,
+ gid: 0,
+ rdev: 0,
+ }
+ }
+
fn register_observer(
&self,
observer: Weak>,
diff --git a/services/libs/jinux-std/src/process/mod.rs b/services/libs/jinux-std/src/process/mod.rs
index fd099fe2..b6e2f421 100644
--- a/services/libs/jinux-std/src/process/mod.rs
+++ b/services/libs/jinux-std/src/process/mod.rs
@@ -270,6 +270,11 @@ impl Process {
for thread in &*self.threads.lock() {
thread.exit();
}
+ // close all files then exit the process
+ let files = self.file_table().lock().close_all();
+ for file in files {
+ let _ = file.clean_for_close();
+ }
// move children to the init process
if !self.is_init_process() {
if let Some(init_process) = get_init_process() {
diff --git a/services/libs/jinux-std/src/process/posix_thread/name.rs b/services/libs/jinux-std/src/process/posix_thread/name.rs
index 3231f165..cd689a81 100644
--- a/services/libs/jinux-std/src/process/posix_thread/name.rs
+++ b/services/libs/jinux-std/src/process/posix_thread/name.rs
@@ -1,6 +1,6 @@
use crate::prelude::*;
-pub const MAX_THREAD_NAME_LEN: usize = 256;
+pub const MAX_THREAD_NAME_LEN: usize = 16;
#[derive(Debug)]
pub struct ThreadName {
diff --git a/services/libs/jinux-std/src/syscall/constants.rs b/services/libs/jinux-std/src/syscall/constants.rs
index e175eb88..0c9a4f91 100644
--- a/services/libs/jinux-std/src/syscall/constants.rs
+++ b/services/libs/jinux-std/src/syscall/constants.rs
@@ -4,5 +4,5 @@
pub const MAX_FILENAME_LEN: usize = 128;
pub const MAX_ARGV_NUMBER: usize = 128;
pub const MAX_ENVP_NUMBER: usize = 128;
-pub const MAX_ARG_LEN: usize = 128;
+pub const MAX_ARG_LEN: usize = 2048;
pub const MAX_ENV_LEN: usize = 128;
diff --git a/services/libs/jinux-std/src/syscall/pipe.rs b/services/libs/jinux-std/src/syscall/pipe.rs
index 015f2a1b..973163d7 100644
--- a/services/libs/jinux-std/src/syscall/pipe.rs
+++ b/services/libs/jinux-std/src/syscall/pipe.rs
@@ -28,6 +28,7 @@ pub fn sys_pipe2(fds: Vaddr, flags: u32) -> Result {
let mut file_table = current.file_table().lock();
pipe_fds.reader_fd = file_table.insert(pipe_reader);
pipe_fds.writer_fd = file_table.insert(pipe_writer);
+ debug!("pipe_fds: {:?}", pipe_fds);
write_val_to_user(fds, &pipe_fds)?;
Ok(SyscallReturn::Return(0))