Add pipe test
This commit is contained in:
		
							parent
							
								
									a2b62891cc
								
							
						
					
					
						commit
						a712bfe70a
					
				| @ -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
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										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
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										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; | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user