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_dup3(int old_fd, int new_fd, int flags); | ||||||
| 
 | 
 | ||||||
| extern int occlum_spawn(int* child_pid, const char* path, | extern int occlum_spawn(int* child_pid, const char* path, | ||||||
|                         const char** argv, |                         const char** argv, const char** envp, | ||||||
|                         const char** envp); |                         void* file_actions); | ||||||
| extern int occlum_wait4(int child_pid, int* status, int options/*, struct rusage* rusage*/); | extern int occlum_wait4(int child_pid, int* status, int options/*, struct rusage* rusage*/); | ||||||
| extern void occlum_exit(int status); | extern void occlum_exit(int status); | ||||||
| extern unsigned int occlum_getpid(void); | 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 argv = std::vec::Vec::new(); | ||||||
|     let envp = std::vec::Vec::new(); |     let envp = std::vec::Vec::new(); | ||||||
|  |     let file_actions = Vec::new(); | ||||||
|     let parent = &process::IDLE_PROCESS; |     let parent = &process::IDLE_PROCESS; | ||||||
|     process::do_spawn(&path_str, &argv, &envp, parent)?; |     process::do_spawn(&path_str, &argv, &envp, &file_actions, parent)?; | ||||||
| 
 | 
 | ||||||
|     Ok(()) |     Ok(()) | ||||||
| } | } | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ pub use self::task::{get_current, run_task}; | |||||||
| pub mod table { | pub mod table { | ||||||
|     pub use super::process_table::{get}; |     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::exit::{do_exit, do_wait4, ChildProcessFilter}; | ||||||
| pub use self::wait::{Waiter, WaitQueue}; | pub use self::wait::{Waiter, WaitQueue}; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| use super::*; | use super::*; | ||||||
| use fs::{File, StdinFile, StdoutFile/*, StderrFile*/, FileTable}; | use fs::{File, FileDesc, StdinFile, StdoutFile/*, StderrFile*/, FileTable}; | ||||||
| use std::path::Path; | use std::path::Path; | ||||||
| use std::ffi::{CStr, CString}; | use std::ffi::{CStr, CString}; | ||||||
| use std::sgxfs::SgxFile; | use std::sgxfs::SgxFile; | ||||||
| @ -14,8 +14,16 @@ mod init_vm; | |||||||
| mod elf_helper; | mod elf_helper; | ||||||
| mod segment; | 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], | 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> |     -> Result<u32, Error> | ||||||
| { | { | ||||||
|     let mut elf_buf = { |     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(); |             let stack_top = vm.get_stack_top(); | ||||||
|             init_task(program_entry, stack_top, argv, envp)? |             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(); |         let exec_path = elf_path.as_ref().to_str().unwrap(); | ||||||
|         Process::new(exec_path, task, vm, files)? |         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) |     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
 |     // Usually, we just inherit the file table from the parent
 | ||||||
|     let parent = parent_ref.lock().unwrap(); |     let parent = parent_ref.lock().unwrap(); | ||||||
|     let should_inherit_file_table = parent.get_pid() > 0; |     let should_inherit_file_table = parent.get_pid() > 0; | ||||||
|     if should_inherit_file_table { |     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); |     drop(parent); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,10 +1,11 @@ | |||||||
| use super::*; | use super::*; | ||||||
| use prelude::*; | use prelude::*; | ||||||
|  | use std::{ptr}; | ||||||
| use {std, fs, process, vm}; | use {std, fs, process, vm}; | ||||||
| use std::ffi::{CStr, CString}; | use std::ffi::{CStr, CString}; | ||||||
| use fs::{off_t, FileDesc}; | use fs::{off_t, FileDesc}; | ||||||
| use vm::{VMAreaFlags, VMResizeOptions}; | use vm::{VMAreaFlags, VMResizeOptions}; | ||||||
| use process::{pid_t, ChildProcessFilter}; | use process::{pid_t, ChildProcessFilter, FileAction}; | ||||||
| // Use the internal syscall wrappers from sgx_tstd
 | // Use the internal syscall wrappers from sgx_tstd
 | ||||||
| //use std::libc_fs as fs;
 | //use std::libc_fs as fs;
 | ||||||
| //use std::libc_io as io;
 | //use std::libc_io as io;
 | ||||||
| @ -413,19 +414,77 @@ pub extern "C" fn occlum_unknown(num: u32) | |||||||
|     println!("[WARNING] Unknown syscall (num = {})", num); |     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, | fn do_spawn(child_pid_ptr: *mut c_uint, | ||||||
|             path: *const c_char, |             path: *const c_char, | ||||||
|             argv: *const *const c_char, |             argv: *const *const c_char, | ||||||
|             envp: *const *const c_char) |             envp: *const *const c_char, | ||||||
|  |             fdop_list: *const FdOp, | ||||||
|  |             ) | ||||||
|     -> Result<(), Error> |     -> Result<(), Error> | ||||||
| { | { | ||||||
|     check_mut_ptr_from_user(child_pid_ptr)?; |     check_mut_ptr_from_user(child_pid_ptr)?; | ||||||
|     let path = clone_cstring_from_user_safely(path)?; |     let path = clone_cstring_from_user_safely(path)?; | ||||||
|     let argv = clone_cstrings_from_user_safely(argv)?; |     let argv = clone_cstrings_from_user_safely(argv)?; | ||||||
|     let envp = clone_cstrings_from_user_safely(envp)?; |     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 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 }; |     unsafe { *child_pid_ptr = child_pid }; | ||||||
|     Ok(()) |     Ok(()) | ||||||
| @ -434,9 +493,10 @@ fn do_spawn(child_pid_ptr: *mut c_uint, | |||||||
| #[no_mangle] | #[no_mangle] | ||||||
| pub extern "C" fn occlum_spawn( | pub extern "C" fn occlum_spawn( | ||||||
|     child_pid: *mut c_uint, path: *const c_char, |     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, |         Ok(()) => 0, | ||||||
|         Err(e) => { e.errno.as_retval() } |         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*, path, arg1); | ||||||
|         DECL_SYSCALL_ARG(const char**, argv, arg2); |         DECL_SYSCALL_ARG(const char**, argv, arg2); | ||||||
|         DECL_SYSCALL_ARG(const char**, envp, arg3); |         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; |         break; | ||||||
|     } |     } | ||||||
|     case SYS_wait4: { |     case SYS_wait4: { | ||||||
|  | |||||||
| @ -31,6 +31,7 @@ pub struct ProcessVM { | |||||||
|     heap_vma: VMArea, |     heap_vma: VMArea, | ||||||
|     stack_vma: VMArea, |     stack_vma: VMArea, | ||||||
|     mmap_vmas: Vec<Box<VMArea>>, |     mmap_vmas: Vec<Box<VMArea>>, | ||||||
|  |     brk: usize, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl ProcessVM { | impl ProcessVM { | ||||||
| @ -46,6 +47,8 @@ impl ProcessVM { | |||||||
|         let (code_vma, data_vma, heap_vma, stack_vma) = |         let (code_vma, data_vma, heap_vma, stack_vma) = | ||||||
|             ProcessVM::alloc_vmas(&mut data_domain, code_size, data_size, |             ProcessVM::alloc_vmas(&mut data_domain, code_size, data_size, | ||||||
|                                  heap_size, stack_size)?; |                                  heap_size, stack_size)?; | ||||||
|  |         // Initial value of the program break
 | ||||||
|  |         let brk = heap_vma.get_start(); | ||||||
|         // No mmapped vmas initially
 |         // No mmapped vmas initially
 | ||||||
|         let mmap_vmas = Vec::new(); |         let mmap_vmas = Vec::new(); | ||||||
| 
 | 
 | ||||||
| @ -56,6 +59,7 @@ impl ProcessVM { | |||||||
|             heap_vma, |             heap_vma, | ||||||
|             stack_vma, |             stack_vma, | ||||||
|             mmap_vmas, |             mmap_vmas, | ||||||
|  |             brk, | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -128,7 +132,7 @@ impl ProcessVM { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn get_brk(&self) -> usize { |     pub fn get_brk(&self) -> usize { | ||||||
|         self.get_heap_vma().get_end() |         self.brk | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn get_mmap_start(&self) -> usize { |     pub fn get_mmap_start(&self) -> usize { | ||||||
| @ -196,17 +200,17 @@ impl ProcessVM { | |||||||
|         if new_brk == 0 { |         if new_brk == 0 { | ||||||
|             return Ok(self.get_brk()); |             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 resize_options = { | ||||||
|             let brk_start = self.get_brk_start(); |             let brk_start = self.get_brk_start(); | ||||||
| 
 |             let new_heap_size = align_up(new_brk, 4096) - brk_start; | ||||||
|             let new_heap_size = { |  | ||||||
|                 if new_brk < brk_start { |  | ||||||
|                     return Err(Error::new(Errno::EINVAL, "Invalid brk")); |  | ||||||
|                 } |  | ||||||
|                 new_brk - brk_start |  | ||||||
|             }; |  | ||||||
| 
 |  | ||||||
|             let mut options = VMResizeOptions::new(new_heap_size)?; |             let mut options = VMResizeOptions::new(new_heap_size)?; | ||||||
|             options.addr(VMAddrOption::Fixed(brk_start)); |             options.addr(VMAddrOption::Fixed(brk_start)); | ||||||
|             options |             options | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| CUR_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) | CUR_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) | ||||||
| PROJECT_DIR := $(realpath $(CUR_DIR)/../) | 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:%=%) | BUILD_TEST_SUITES := $(TEST_SUITES:%=%) | ||||||
| RUN_TEST_SUITES := $(TEST_SUITES:%=test-%) | RUN_TEST_SUITES := $(TEST_SUITES:%=test-%) | ||||||
| CLEAN_TEST_SUITES := $(TEST_SUITES:%=clean-%) | 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) { |     for (size_t buf_size = MIN_SIZE; buf_size <= MAX_SIZE; buf_size *= 4) { | ||||||
|         printf("buf_size = %lu\n", buf_size); |         printf("buf_size = %lu\n", buf_size); | ||||||
|         void* buf = malloc(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); |         free(buf); | ||||||
|     } |     } | ||||||
|     printf("Done.\n"); |     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