Add pipe test

This commit is contained in:
Tate, Hongliang Tian 2019-01-07 19:20:01 +08:00
parent a2b62891cc
commit a712bfe70a
11 changed files with 178 additions and 25 deletions

@ -26,8 +26,8 @@ extern int occlum_dup2(int old_fd, int new_fd);
extern int occlum_dup3(int old_fd, int new_fd, int flags);
extern int occlum_spawn(int* child_pid, const char* path,
const char** argv,
const char** envp);
const char** argv, const char** envp,
void* file_actions);
extern int occlum_wait4(int child_pid, int* status, int options/*, struct rusage* rusage*/);
extern void occlum_exit(int status);
extern unsigned int occlum_getpid(void);

@ -78,8 +78,9 @@ fn do_boot(path_str: &str) -> Result<(), Error> {
let argv = std::vec::Vec::new();
let envp = std::vec::Vec::new();
let file_actions = Vec::new();
let parent = &process::IDLE_PROCESS;
process::do_spawn(&path_str, &argv, &envp, parent)?;
process::do_spawn(&path_str, &argv, &envp, &file_actions, parent)?;
Ok(())
}

@ -3,7 +3,7 @@ pub use self::task::{get_current, run_task};
pub mod table {
pub use super::process_table::{get};
}
pub use self::spawn::{do_spawn};
pub use self::spawn::{do_spawn, FileAction};
pub use self::exit::{do_exit, do_wait4, ChildProcessFilter};
pub use self::wait::{Waiter, WaitQueue};

@ -1,5 +1,5 @@
use super::*;
use fs::{File, StdinFile, StdoutFile/*, StderrFile*/, FileTable};
use fs::{File, FileDesc, StdinFile, StdoutFile/*, StderrFile*/, FileTable};
use std::path::Path;
use std::ffi::{CStr, CString};
use std::sgxfs::SgxFile;
@ -14,8 +14,16 @@ mod init_vm;
mod elf_helper;
mod segment;
#[derive(Debug)]
pub enum FileAction {
// TODO: Add open action
// Open(...)
Dup2(FileDesc, FileDesc),
Close(FileDesc),
}
pub fn do_spawn<P: AsRef<Path>>(elf_path: &P, argv: &[CString], envp: &[CString],
parent_ref: &ProcessRef)
file_actions: &[FileAction], parent_ref: &ProcessRef)
-> Result<u32, Error>
{
let mut elf_buf = {
@ -55,7 +63,7 @@ pub fn do_spawn<P: AsRef<Path>>(elf_path: &P, argv: &[CString], envp: &[CString]
let stack_top = vm.get_stack_top();
init_task(program_entry, stack_top, argv, envp)?
};
let files = init_files(parent_ref)?;
let files = init_files(parent_ref, file_actions)?;
let exec_path = elf_path.as_ref().to_str().unwrap();
Process::new(exec_path, task, vm, files)?
};
@ -65,12 +73,29 @@ pub fn do_spawn<P: AsRef<Path>>(elf_path: &P, argv: &[CString], envp: &[CString]
Ok(new_pid)
}
fn init_files(parent_ref: &ProcessRef) -> Result<FileTable, Error> {
fn init_files(parent_ref: &ProcessRef, file_actions: &[FileAction])
-> Result<FileTable, Error>
{
// Usually, we just inherit the file table from the parent
let parent = parent_ref.lock().unwrap();
let should_inherit_file_table = parent.get_pid() > 0;
if should_inherit_file_table {
return Ok(parent.get_files().clone());
let mut cloned_file_table = parent.get_files().clone();
// Perform file actions to modify the cloned file table
for file_action in file_actions {
match file_action {
FileAction::Dup2(old_fd, new_fd) => {
let file = cloned_file_table.get(*old_fd)?;
if old_fd != new_fd {
cloned_file_table.put_at(*new_fd, file, false);
}
},
FileAction::Close(fd) => {
cloned_file_table.del(*fd)?;
}
}
}
return Ok(cloned_file_table);
}
drop(parent);

@ -1,10 +1,11 @@
use super::*;
use prelude::*;
use std::{ptr};
use {std, fs, process, vm};
use std::ffi::{CStr, CString};
use fs::{off_t, FileDesc};
use vm::{VMAreaFlags, VMResizeOptions};
use process::{pid_t, ChildProcessFilter};
use process::{pid_t, ChildProcessFilter, FileAction};
// Use the internal syscall wrappers from sgx_tstd
//use std::libc_fs as fs;
//use std::libc_io as io;
@ -413,19 +414,77 @@ pub extern "C" fn occlum_unknown(num: u32)
println!("[WARNING] Unknown syscall (num = {})", num);
}
/*
* This Rust-version of fdop correspond to the C-version one in Occlum.
* See <path_to_musl_libc>/src/process/fdop.h.
*/
const FDOP_CLOSE : u32 = 1;
const FDOP_DUP2 : u32 = 2;
const FDOP_OPEN : u32 = 3;
#[repr(C)]
#[derive(Debug)]
pub struct FdOp {
// We actually switch the prev and next fields in the libc definition.
prev: *const FdOp,
next: *const FdOp,
cmd: u32,
fd: u32,
srcfd: u32,
oflag: u32,
mode: u32,
path: *const u8,
}
fn clone_file_actions_from_user_safely(fdop_ptr: *const FdOp)
-> Result<Vec<FileAction>, Error>
{
let mut file_actions = Vec::new();
let mut fdop_ptr = fdop_ptr;
while fdop_ptr != ptr::null() {
check_ptr_from_user(fdop_ptr)?;
let fdop = unsafe { &*fdop_ptr };
let file_action = match fdop.cmd {
FDOP_CLOSE => {
FileAction::Close(fdop.fd)
},
FDOP_DUP2 => {
FileAction::Dup2(fdop.srcfd, fdop.fd)
},
FDOP_OPEN => {
return errno!(EINVAL, "Not implemented");
},
_ => {
return errno!(EINVAL, "Unknown file action command");
},
};
file_actions.push(file_action);
fdop_ptr = fdop.next;
}
Ok(file_actions)
}
fn do_spawn(child_pid_ptr: *mut c_uint,
path: *const c_char,
argv: *const *const c_char,
envp: *const *const c_char)
envp: *const *const c_char,
fdop_list: *const FdOp,
)
-> Result<(), Error>
{
check_mut_ptr_from_user(child_pid_ptr)?;
let path = clone_cstring_from_user_safely(path)?;
let argv = clone_cstrings_from_user_safely(argv)?;
let envp = clone_cstrings_from_user_safely(envp)?;
let file_actions = clone_file_actions_from_user_safely(fdop_list)?;
let parent = process::get_current();
let child_pid = process::do_spawn(&path, &argv, &envp, &parent)?;
let child_pid = process::do_spawn(&path, &argv, &envp, &file_actions, &parent)?;
unsafe { *child_pid_ptr = child_pid };
Ok(())
@ -434,9 +493,10 @@ fn do_spawn(child_pid_ptr: *mut c_uint,
#[no_mangle]
pub extern "C" fn occlum_spawn(
child_pid: *mut c_uint, path: *const c_char,
argv: *const *const c_char, envp: *const *const c_char) -> c_int
argv: *const *const c_char, envp: *const *const c_char,
fdop_list: *const FdOp) -> c_int
{
match do_spawn(child_pid, path, argv, envp) {
match do_spawn(child_pid, path, argv, envp, fdop_list) {
Ok(()) => 0,
Err(e) => { e.errno.as_retval() }
}

@ -65,7 +65,8 @@ long dispatch_syscall(int num, long arg0, long arg1, long arg2, long arg3, long
DECL_SYSCALL_ARG(const char*, path, arg1);
DECL_SYSCALL_ARG(const char**, argv, arg2);
DECL_SYSCALL_ARG(const char**, envp, arg3);
ret = occlum_spawn(child_pid, path, argv, envp);
DECL_SYSCALL_ARG(void*, file_actions, arg4);
ret = occlum_spawn(child_pid, path, argv, envp, file_actions);
break;
}
case SYS_wait4: {

@ -31,6 +31,7 @@ pub struct ProcessVM {
heap_vma: VMArea,
stack_vma: VMArea,
mmap_vmas: Vec<Box<VMArea>>,
brk: usize,
}
impl ProcessVM {
@ -46,6 +47,8 @@ impl ProcessVM {
let (code_vma, data_vma, heap_vma, stack_vma) =
ProcessVM::alloc_vmas(&mut data_domain, code_size, data_size,
heap_size, stack_size)?;
// Initial value of the program break
let brk = heap_vma.get_start();
// No mmapped vmas initially
let mmap_vmas = Vec::new();
@ -56,6 +59,7 @@ impl ProcessVM {
heap_vma,
stack_vma,
mmap_vmas,
brk,
})
}
@ -128,7 +132,7 @@ impl ProcessVM {
}
pub fn get_brk(&self) -> usize {
self.get_heap_vma().get_end()
self.brk
}
pub fn get_mmap_start(&self) -> usize {
@ -196,17 +200,17 @@ impl ProcessVM {
if new_brk == 0 {
return Ok(self.get_brk());
}
else if new_brk < self.heap_vma.get_start() {
return errno!(EINVAL, "New brk address is too low");
}
else if new_brk <= self.heap_vma.get_end() {
self.brk = new_brk;
return Ok(new_brk);
}
let resize_options = {
let brk_start = self.get_brk_start();
let new_heap_size = {
if new_brk < brk_start {
return Err(Error::new(Errno::EINVAL, "Invalid brk"));
}
new_brk - brk_start
};
let new_heap_size = align_up(new_brk, 4096) - brk_start;
let mut options = VMResizeOptions::new(new_heap_size)?;
options.addr(VMAddrOption::Fixed(brk_start));
options

@ -1,7 +1,7 @@
CUR_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
PROJECT_DIR := $(realpath $(CUR_DIR)/../)
TEST_SUITES := empty hello_world malloc file getpid spawn
TEST_SUITES := empty hello_world malloc file getpid spawn pipe
BUILD_TEST_SUITES := $(TEST_SUITES:%=%)
RUN_TEST_SUITES := $(TEST_SUITES:%=test-%)
CLEAN_TEST_SUITES := $(TEST_SUITES:%=clean-%)

@ -9,6 +9,11 @@ int main(void) {
for (size_t buf_size = MIN_SIZE; buf_size <= MAX_SIZE; buf_size *= 4) {
printf("buf_size = %lu\n", buf_size);
void* buf = malloc(buf_size);
/* FIXME: why the first call to malloc always fail?
if (buf == NULL) {
printf("ERROR: failed to malloc for a buffer of %lu size\n", buf_size);
}
*/
free(buf);
}
printf("Done.\n");

4
test/pipe/Makefile Normal file

@ -0,0 +1,4 @@
include ../test_common.mk
EXTRA_C_FLAGS :=
EXTRA_LINK_FLAGS :=

53
test/pipe/main.c Normal file

@ -0,0 +1,53 @@
#include <sys/syscall.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <spawn.h>
#include <string.h>
int main(void) {
// XXX: this is a hack! remove this in the future
void* ptr = malloc(64);
free(ptr);
int pipe_fds[2];
if (pipe(pipe_fds) < 0) {
printf("ERROR: failed to create a pipe\n");
return -1;
}
int pipe_rd_fd = pipe_fds[0];
int pipe_wr_fd = pipe_fds[1];
posix_spawn_file_actions_t file_actions;
posix_spawn_file_actions_init(&file_actions);
posix_spawn_file_actions_adddup2(&file_actions, pipe_wr_fd, STDOUT_FILENO);
posix_spawn_file_actions_addclose(&file_actions, pipe_rd_fd);
int child_pid;
if (posix_spawn(&child_pid, "hello_world/bin.encrypted", &file_actions,
NULL, NULL, NULL) < 0) {
printf("ERROR: failed to spawn a child process\n");
return -1;
}
close(pipe_wr_fd);
const char* expected_str = "Hello World\n";
size_t expected_len = strlen(expected_str);
char actual_str[32] = {0};
ssize_t actual_len;
do {
actual_len = read(pipe_rd_fd, actual_str, sizeof(actual_str) - 1);
} while (actual_len == 0);
if (strncmp(expected_str, actual_str, expected_len) != 0) {
printf("ERROR: received string is not as expected\n");
return -1;
}
int status = 0;
if (wait4(child_pid, &status, 0, NULL) < 0) {
printf("ERROR: failed to wait4 the child process\n");
return -1;
}
return 0;
}