diff --git a/easy-fs-fuse/src/main.rs b/easy-fs-fuse/src/main.rs index 04f74f00..498b61ec 100644 --- a/easy-fs-fuse/src/main.rs +++ b/easy-fs-fuse/src/main.rs @@ -118,7 +118,8 @@ fn efs_test() -> std::io::Result<()> { let filea = root_inode.find("filea").unwrap(); let greet_str = "Hello, world!"; filea.write_at(0, greet_str.as_bytes()); - let mut buffer = [0u8; 512]; + //let mut buffer = [0u8; 512]; + let mut buffer = [0u8; 233]; let len = filea.read_at(0, &mut buffer); assert_eq!( greet_str, @@ -159,6 +160,9 @@ fn efs_test() -> std::io::Result<()> { random_str_test(100 * BLOCK_SZ); random_str_test(70 * BLOCK_SZ + BLOCK_SZ / 7); random_str_test((12 + 128) * BLOCK_SZ); + random_str_test(400 * BLOCK_SZ); + random_str_test(1000 * BLOCK_SZ); + random_str_test(2000 * BLOCK_SZ); Ok(()) } \ No newline at end of file diff --git a/easy-fs/src/efs.rs b/easy-fs/src/efs.rs index caf4d8cb..9e4445df 100644 --- a/easy-fs/src/efs.rs +++ b/easy-fs/src/efs.rs @@ -80,9 +80,7 @@ impl EasyFileSystem { ) .lock() .modify(root_inode_offset, |disk_inode: &mut DiskInode| { - disk_inode.initialize( - DiskInodeType::Directory,efs.alloc_data() - ); + disk_inode.initialize(DiskInodeType::Directory); }); Arc::new(Mutex::new(efs)) } @@ -158,6 +156,14 @@ impl EasyFileSystem { } pub fn dealloc_data(&mut self, block_id: u32) { + get_block_cache( + block_id as usize, + Arc::clone(&self.block_device) + ) + .lock() + .modify(0, |data_block: &mut DataBlock| { + data_block.iter_mut().for_each(|p| { *p = 0; }) + }); self.data_bitmap.dealloc( &self.block_device, (block_id - self.data_area_start_block) as usize diff --git a/easy-fs/src/layout.rs b/easy-fs/src/layout.rs index ace1ced8..7d14de46 100644 --- a/easy-fs/src/layout.rs +++ b/easy-fs/src/layout.rs @@ -8,8 +8,14 @@ use alloc::sync::Arc; use alloc::vec::Vec; const EFS_MAGIC: u32 = 0x3b800001; -const INODE_DIRECT_COUNT: usize = 60; +const INODE_DIRECT_COUNT: usize = 28; const NAME_LENGTH_LIMIT: usize = 27; +const INODE_INDIRECT1_COUNT: usize = BLOCK_SZ / 4; +const INODE_INDIRECT2_COUNT: usize = INODE_INDIRECT1_COUNT * INODE_INDIRECT1_COUNT; +const DIRECT_BOUND: usize = INODE_DIRECT_COUNT; +const INDIRECT1_BOUND: usize = DIRECT_BOUND + INODE_INDIRECT1_COUNT; +#[allow(unused)] +const INDIRECT2_BOUND: usize = INDIRECT1_BOUND + INODE_INDIRECT2_COUNT; #[repr(C)] pub struct SuperBlock { @@ -76,11 +82,11 @@ pub struct DiskInode { } impl DiskInode { - /// indirect1 block is allocated when the file is created. - pub fn initialize(&mut self, type_: DiskInodeType, indirect1: u32) { + /// indirect1 and indirect2 block are allocated only when they are needed. + pub fn initialize(&mut self, type_: DiskInodeType) { self.size = 0; self.direct.iter_mut().for_each(|v| *v = 0); - self.indirect1 = indirect1; + self.indirect1 = 0; self.indirect2 = 0; self.type_ = type_; } @@ -91,57 +97,146 @@ impl DiskInode { pub fn is_file(&self) -> bool { self.type_ == DiskInodeType::File } - pub fn blocks(&self) -> u32 { - Self::_blocks(self.size) + /// Return block number correspond to size. + pub fn data_blocks(&self) -> u32 { + Self::_data_blocks(self.size) } - fn _blocks(size: u32) -> u32 { + fn _data_blocks(size: u32) -> u32 { (size + BLOCK_SZ as u32 - 1) / BLOCK_SZ as u32 } + /// Return number of blocks needed include indirect1/2. + pub fn total_blocks(size: u32) -> u32 { + let data_blocks = Self::_data_blocks(size) as usize; + let mut total = data_blocks as usize; + // indirect1 + if data_blocks > INODE_DIRECT_COUNT { + total += 1; + } + // indirect2 + if data_blocks > INDIRECT1_BOUND { + total += 1; + // sub indirect1 + total += (data_blocks - INDIRECT1_BOUND + INODE_INDIRECT1_COUNT - 1) / INODE_INDIRECT1_COUNT; + } + total as u32 + } + pub fn blocks_num_needed(&self, new_size: u32) -> u32 { + assert!(new_size >= self.size); + Self::total_blocks(new_size) - Self::total_blocks(self.size) + } pub fn get_block_id(&self, inner_id: u32, block_device: &Arc) -> u32 { let inner_id = inner_id as usize; if inner_id < INODE_DIRECT_COUNT { self.direct[inner_id] - } else { - // only support indirect1 now + } else if inner_id < INDIRECT1_BOUND { get_block_cache(self.indirect1 as usize, Arc::clone(block_device)) .lock() .read(0, |indirect_block: &IndirectBlock| { indirect_block[inner_id - INODE_DIRECT_COUNT] }) + } else { + let last = inner_id - INDIRECT1_BOUND; + let indirect1 = get_block_cache( + self.indirect2 as usize, + Arc::clone(block_device) + ) + .lock() + .read(0, |indirect2: &IndirectBlock| { + indirect2[last / INODE_INDIRECT1_COUNT] + }); + get_block_cache( + indirect1 as usize, + Arc::clone(block_device) + ) + .lock() + .read(0, |indirect1: &IndirectBlock| { + indirect1[last % INODE_INDIRECT1_COUNT] + }) } } - pub fn blocks_num_needed(&self, new_size: u32) -> u32 { - assert!(new_size >= self.size); - Self::_blocks(new_size) - self.blocks() - } pub fn increase_size( &mut self, new_size: u32, new_blocks: Vec, block_device: &Arc, ) { - assert_eq!(new_blocks.len() as u32, self.blocks_num_needed(new_size)); - let last_blocks = self.blocks(); + let mut current_blocks = self.data_blocks(); self.size = new_size; - let current_blocks = self.blocks(); + let mut total_blocks = self.data_blocks(); + let mut new_blocks = new_blocks.into_iter(); + // fill direct + while current_blocks < total_blocks.min(INODE_DIRECT_COUNT as u32) { + self.direct[current_blocks as usize] = new_blocks.next().unwrap(); + current_blocks += 1; + } + // alloc indirect1 + if total_blocks > INODE_DIRECT_COUNT as u32{ + if current_blocks == INODE_DIRECT_COUNT as u32 { + self.indirect1 = new_blocks.next().unwrap(); + } + current_blocks -= INODE_DIRECT_COUNT as u32; + total_blocks -= INODE_DIRECT_COUNT as u32; + } else { + return; + } + // fill indirect1 get_block_cache( self.indirect1 as usize, Arc::clone(block_device) ) .lock() - .modify(0, |indirect_block: &mut IndirectBlock| { - for i in 0..current_blocks - last_blocks { - let inner_id = (last_blocks + i) as usize; - let new_block = new_blocks[i as usize]; - if inner_id < INODE_DIRECT_COUNT { - self.direct[inner_id] = new_block; - } else { - indirect_block[inner_id - INODE_DIRECT_COUNT] = new_block; - } + .modify(0, |indirect1: &mut IndirectBlock| { + while current_blocks < total_blocks.min(INODE_INDIRECT1_COUNT as u32) { + indirect1[current_blocks as usize] = new_blocks.next().unwrap(); + current_blocks += 1; } }); + // alloc indirect2 + if total_blocks > INODE_INDIRECT1_COUNT as u32 { + if current_blocks == INODE_INDIRECT1_COUNT as u32 { + self.indirect2 = new_blocks.next().unwrap(); + } + current_blocks -= INODE_INDIRECT1_COUNT as u32; + total_blocks -= INODE_INDIRECT1_COUNT as u32; + } else { + return; + } + // fill indirect2 from (a0, b0) -> (a1, b1) + let mut a0 = current_blocks as usize / INODE_INDIRECT1_COUNT; + let mut b0 = current_blocks as usize % INODE_INDIRECT1_COUNT; + let a1 = total_blocks as usize / INODE_INDIRECT1_COUNT; + let b1 = total_blocks as usize % INODE_INDIRECT1_COUNT; + // alloc low-level indirect1 + get_block_cache( + self.indirect2 as usize, + Arc::clone(block_device) + ) + .lock() + .modify(0, |indirect2: &mut IndirectBlock| { + while (a0 < a1) || (a0 == a1 && b0 < b1) { + if b0 == 0 { + indirect2[a0] = new_blocks.next().unwrap(); + } + // fill current + get_block_cache( + indirect2[a0] as usize, + Arc::clone(block_device) + ) + .lock() + .modify(0, |indirect1: &mut IndirectBlock| { + indirect1[b0] = new_blocks.next().unwrap(); + }); + // move to next + b0 += 1; + if b0 == INODE_INDIRECT1_COUNT { + b0 = 0; + a0 += 1; + } + } + }); } - /// Clear size to zero and return blocks that should be deallocated. + + /* pub fn clear_size(&mut self, block_device: &Arc) -> Vec { let mut v: Vec = Vec::new(); let blocks = self.blocks() as usize; @@ -165,6 +260,97 @@ impl DiskInode { } v } + */ + + /// Clear size to zero and return blocks that should be deallocated. + /// + /// We will clear the block contents to zero later. + pub fn clear_size(&mut self, block_device: &Arc) -> Vec { + let mut v: Vec = Vec::new(); + let mut data_blocks = self.data_blocks() as usize; + self.size = 0; + let mut current_blocks = 0usize; + // direct + while current_blocks < data_blocks.min(INODE_DIRECT_COUNT) { + v.push(self.direct[current_blocks]); + self.direct[current_blocks] = 0; + current_blocks += 1; + } + // indirect1 block + if data_blocks > INODE_DIRECT_COUNT { + v.push(self.indirect1); + data_blocks -= INODE_DIRECT_COUNT; + current_blocks = 0; + } else { + return v; + } + // indirect1 + get_block_cache( + self.indirect1 as usize, + Arc::clone(block_device), + ) + .lock() + .modify(0, |indirect1: &mut IndirectBlock| { + while current_blocks < data_blocks.min(INODE_INDIRECT1_COUNT) { + v.push(indirect1[current_blocks]); + //indirect1[current_blocks] = 0; + current_blocks += 1; + } + }); + self.indirect1 = 0; + // indirect2 block + if data_blocks > INODE_INDIRECT1_COUNT { + v.push(self.indirect2); + data_blocks -= INODE_INDIRECT1_COUNT; + } else { + return v; + } + // indirect2 + assert!(data_blocks <= INODE_INDIRECT2_COUNT); + let a1 = data_blocks / INODE_INDIRECT1_COUNT; + let b1 = data_blocks % INODE_INDIRECT1_COUNT; + get_block_cache( + self.indirect2 as usize, + Arc::clone(block_device), + ) + .lock() + .modify(0, |indirect2: &mut IndirectBlock| { + // full indirect1 blocks + for i in 0..a1 { + v.push(indirect2[i]); + get_block_cache( + indirect2[i] as usize, + Arc::clone(block_device), + ) + .lock() + .modify(0, |indirect1: &mut IndirectBlock| { + for j in 0..INODE_INDIRECT1_COUNT { + v.push(indirect1[j]); + //indirect1[j] = 0; + } + }); + //indirect2[i] = 0; + } + // last indirect1 block + if b1 > 0 { + v.push(indirect2[a1]); + get_block_cache( + indirect2[a1] as usize, + Arc::clone(block_device), + ) + .lock() + .modify(0, |indirect1: &mut IndirectBlock| { + for j in 0..b1 { + v.push(indirect1[j]); + //indirect1[j] = 0; + } + }); + //indirect2[a1] = 0; + } + }); + self.indirect2 = 0; + v + } pub fn read_at( &self, offset: usize, diff --git a/easy-fs/src/vfs.rs b/easy-fs/src/vfs.rs index a98fd607..f10a8c8a 100644 --- a/easy-fs/src/vfs.rs +++ b/easy-fs/src/vfs.rs @@ -122,22 +122,18 @@ impl Inode { }).is_some() { return None; } - //println!("same file does not exist in Inode::create."); // create a new file // alloc a inode with an indirect block let new_inode_id = fs.alloc_inode(); - let indirect1 = fs.alloc_data(); // initialize inode let (new_inode_block_id, new_inode_block_offset) = fs.get_disk_inode_pos(new_inode_id); - //println!("new_inode_id={} ({},{})", new_inode_id, new_inode_block_id, new_inode_block_offset); get_block_cache( new_inode_block_id as usize, Arc::clone(&self.block_device) ).lock().modify(new_inode_block_offset, |new_inode: &mut DiskInode| { - new_inode.initialize(DiskInodeType::File, indirect1); + new_inode.initialize(DiskInodeType::File); }); - //println!("new inode has been initialized."); self.modify_disk_inode(|root_inode| { // append file in the dirent let file_count = (root_inode.size as usize) / DIRENT_SZ; @@ -152,7 +148,6 @@ impl Inode { &self.block_device, ); }); - //println!("new file has been inserted into root inode."); // release efs lock manually because we will acquire it again in Inode::new drop(fs); // return inode @@ -202,7 +197,9 @@ impl Inode { pub fn clear(&self) { let mut fs = self.fs.lock(); self.modify_disk_inode(|disk_inode| { + let size = disk_inode.size; let data_blocks_dealloc = disk_inode.clear_size(&self.block_device); + assert!(data_blocks_dealloc.len() == DiskInode::total_blocks(size) as usize); for data_block in data_blocks_dealloc.into_iter() { fs.dealloc_data(data_block); } diff --git a/os/Makefile b/os/Makefile index 568a2779..4d86c124 100644 --- a/os/Makefile +++ b/os/Makefile @@ -6,7 +6,7 @@ KERNEL_BIN := $(KERNEL_ELF).bin DISASM_TMP := target/$(TARGET)/$(MODE)/asm FS_IMG := ../user/target/$(TARGET)/$(MODE)/fs.img SDCARD := /dev/sdb -APPS := ../user/src/bin +APPS := ../user/src/bin/* # BOARD BOARD ?= qemu diff --git a/os/src/mm/address.rs b/os/src/mm/address.rs index f9e02805..d5828ed2 100644 --- a/os/src/mm/address.rs +++ b/os/src/mm/address.rs @@ -115,6 +115,11 @@ impl VirtPageNum { } impl PhysAddr { + pub fn get_ref(&self) -> &'static T { + unsafe { + (self.0 as *const T).as_ref().unwrap() + } + } pub fn get_mut(&self) -> &'static mut T { unsafe { (self.0 as *mut T).as_mut().unwrap() diff --git a/os/src/mm/mod.rs b/os/src/mm/mod.rs index 2242eecb..dbd47fe7 100644 --- a/os/src/mm/mod.rs +++ b/os/src/mm/mod.rs @@ -13,6 +13,7 @@ pub use page_table::{ PageTableEntry, translated_byte_buffer, translated_str, + translated_ref, translated_refmut, UserBuffer, UserBufferIterator, diff --git a/os/src/mm/page_table.rs b/os/src/mm/page_table.rs index 47f39a76..b1a227fa 100644 --- a/os/src/mm/page_table.rs +++ b/os/src/mm/page_table.rs @@ -174,6 +174,7 @@ pub fn translated_byte_buffer(token: usize, ptr: *const u8, len: usize) -> Vec<& v } +/// Load a string from other address spaces into kernel space without an end `\0`. pub fn translated_str(token: usize, ptr: *const u8) -> String { let page_table = PageTable::from_token(token); let mut string = String::new(); @@ -182,14 +183,18 @@ pub fn translated_str(token: usize, ptr: *const u8) -> String { let ch: u8 = *(page_table.translate_va(VirtAddr::from(va)).unwrap().get_mut()); if ch == 0 { break; - } else { - string.push(ch as char); - va += 1; } + string.push(ch as char); + va += 1; } string } +pub fn translated_ref(token: usize, ptr: *const T) -> &'static T { + let page_table = PageTable::from_token(token); + page_table.translate_va(VirtAddr::from(ptr as usize)).unwrap().get_ref() +} + pub fn translated_refmut(token: usize, ptr: *mut T) -> &'static mut T { let page_table = PageTable::from_token(token); let va = ptr as usize; diff --git a/os/src/syscall/fs.rs b/os/src/syscall/fs.rs index 577e4a0a..79eac32a 100644 --- a/os/src/syscall/fs.rs +++ b/os/src/syscall/fs.rs @@ -6,6 +6,7 @@ use crate::mm::{ }; use crate::task::{current_user_token, current_task}; use crate::fs::{make_pipe, OpenFlags, open_file}; +use alloc::sync::Arc; pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize { let token = current_user_token(); @@ -93,4 +94,18 @@ pub fn sys_pipe(pipe: *mut usize) -> isize { *translated_refmut(token, pipe) = read_fd; *translated_refmut(token, unsafe { pipe.add(1) }) = write_fd; 0 +} + +pub fn sys_dup(fd: usize) -> isize { + let task = current_task().unwrap(); + let mut inner = task.acquire_inner_lock(); + if fd >= inner.fd_table.len() { + return -1; + } + if inner.fd_table[fd].is_none() { + return -1; + } + let new_fd = inner.alloc_fd(); + inner.fd_table[new_fd] = Some(Arc::clone(inner.fd_table[fd].as_ref().unwrap())); + new_fd as isize } \ No newline at end of file diff --git a/os/src/syscall/mod.rs b/os/src/syscall/mod.rs index f4a67538..4683d055 100644 --- a/os/src/syscall/mod.rs +++ b/os/src/syscall/mod.rs @@ -1,3 +1,4 @@ +const SYSCALL_DUP: usize = 24; const SYSCALL_OPEN: usize = 56; const SYSCALL_CLOSE: usize = 57; const SYSCALL_PIPE: usize = 59; @@ -19,6 +20,7 @@ use process::*; pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize { match syscall_id { + SYSCALL_DUP=> sys_dup(args[0]), SYSCALL_OPEN => sys_open(args[0] as *const u8, args[1] as u32), SYSCALL_CLOSE => sys_close(args[0]), SYSCALL_PIPE => sys_pipe(args[0] as *mut usize), @@ -29,7 +31,7 @@ pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize { SYSCALL_GET_TIME => sys_get_time(), SYSCALL_GETPID => sys_getpid(), SYSCALL_FORK => sys_fork(), - SYSCALL_EXEC => sys_exec(args[0] as *const u8), + SYSCALL_EXEC => sys_exec(args[0] as *const u8, args[1] as *const usize), SYSCALL_WAITPID => sys_waitpid(args[0] as isize, args[1] as *mut i32), _ => panic!("Unsupported syscall_id: {}", syscall_id), } diff --git a/os/src/syscall/process.rs b/os/src/syscall/process.rs index 370ae9cb..bfd592d6 100644 --- a/os/src/syscall/process.rs +++ b/os/src/syscall/process.rs @@ -9,12 +9,15 @@ use crate::timer::get_time_ms; use crate::mm::{ translated_str, translated_refmut, + translated_ref, }; use crate::fs::{ open_file, OpenFlags, }; use alloc::sync::Arc; +use alloc::vec::Vec; +use alloc::string::String; pub fn sys_exit(exit_code: i32) -> ! { exit_current_and_run_next(exit_code); @@ -48,14 +51,25 @@ pub fn sys_fork() -> isize { new_pid as isize } -pub fn sys_exec(path: *const u8) -> isize { +pub fn sys_exec(path: *const u8, mut args: *const usize) -> isize { let token = current_user_token(); let path = translated_str(token, path); + let mut args_vec: Vec = Vec::new(); + loop { + let arg_str_ptr = *translated_ref(token, args); + if arg_str_ptr == 0 { + break; + } + args_vec.push(translated_str(token, arg_str_ptr as *const u8)); + unsafe { args = args.add(1); } + } if let Some(app_inode) = open_file(path.as_str(), OpenFlags::RDONLY) { let all_data = app_inode.read_all(); let task = current_task().unwrap(); - task.exec(all_data.as_slice()); - 0 + let argc = args_vec.len(); + task.exec(all_data.as_slice(), args_vec); + // return argc because cx.x[10] will be covered with it later + argc as isize } else { -1 } diff --git a/os/src/task/task.rs b/os/src/task/task.rs index 1c733da1..4edd5cef 100644 --- a/os/src/task/task.rs +++ b/os/src/task/task.rs @@ -1,4 +1,10 @@ -use crate::mm::{MemorySet, PhysPageNum, KERNEL_SPACE, VirtAddr}; +use crate::mm::{ + MemorySet, + PhysPageNum, + KERNEL_SPACE, + VirtAddr, + translated_refmut, +}; use crate::trap::{TrapContext, trap_handler}; use crate::config::{TRAP_CONTEXT}; use super::TaskContext; @@ -6,6 +12,7 @@ use super::{PidHandle, pid_alloc, KernelStack}; use alloc::sync::{Weak, Arc}; use alloc::vec; use alloc::vec::Vec; +use alloc::string::String; use spin::{Mutex, MutexGuard}; use crate::fs::{File, Stdin, Stdout}; @@ -106,13 +113,35 @@ impl TaskControlBlock { ); task_control_block } - pub fn exec(&self, elf_data: &[u8]) { + pub fn exec(&self, elf_data: &[u8], args: Vec) { // memory_set with elf program headers/trampoline/trap context/user stack - let (memory_set, user_sp, entry_point) = MemorySet::from_elf(elf_data); + let (memory_set, mut user_sp, entry_point) = MemorySet::from_elf(elf_data); let trap_cx_ppn = memory_set .translate(VirtAddr::from(TRAP_CONTEXT).into()) .unwrap() .ppn(); + // push arguments on user stack + user_sp -= (args.len() + 1) * core::mem::size_of::(); + let argv_base = user_sp; + let mut argv: Vec<_> = (0..=args.len()) + .map(|arg| { + translated_refmut( + memory_set.token(), + (argv_base + arg * core::mem::size_of::()) as *mut usize + ) + }) + .collect(); + *argv[args.len()] = 0; + for i in 0..args.len() { + user_sp -= args[i].len() + 1; + *argv[i] = user_sp; + let mut p = user_sp; + for c in args[i].as_bytes() { + *translated_refmut(memory_set.token(), p as *mut u8) = *c; + p += 1; + } + *translated_refmut(memory_set.token(), p as *mut u8) = 0; + } // **** hold current PCB lock let mut inner = self.acquire_inner_lock(); @@ -121,14 +150,16 @@ impl TaskControlBlock { // update trap_cx ppn inner.trap_cx_ppn = trap_cx_ppn; // initialize trap_cx - let trap_cx = inner.get_trap_cx(); - *trap_cx = TrapContext::app_init_context( + let mut trap_cx = TrapContext::app_init_context( entry_point, user_sp, KERNEL_SPACE.lock().token(), self.kernel_stack.get_top(), trap_handler as usize, ); + trap_cx.x[10] = args.len(); + trap_cx.x[11] = argv_base; + *inner.get_trap_cx() = trap_cx; // **** release current PCB lock } pub fn fork(self: &Arc) -> Arc { diff --git a/os/src/trap/context.rs b/os/src/trap/context.rs index 8c5175f7..16f81415 100644 --- a/os/src/trap/context.rs +++ b/os/src/trap/context.rs @@ -1,6 +1,7 @@ use riscv::register::sstatus::{Sstatus, self, SPP}; #[repr(C)] +#[derive(Debug)] pub struct TrapContext { pub x: [usize; 32], pub sstatus: Sstatus, diff --git a/os/src/trap/mod.rs b/os/src/trap/mod.rs index 4f5126b2..f83560b2 100644 --- a/os/src/trap/mod.rs +++ b/os/src/trap/mod.rs @@ -88,6 +88,7 @@ pub fn trap_handler() -> ! { panic!("Unsupported trap {:?}, stval = {:#x}!", scause.cause(), stval); } } + //println!("before trap_return"); trap_return(); } diff --git a/user/src/bin/cat.rs b/user/src/bin/cat.rs new file mode 100644 index 00000000..988164d3 --- /dev/null +++ b/user/src/bin/cat.rs @@ -0,0 +1,34 @@ +#![no_std] +#![no_main] + +#[macro_use] +extern crate user_lib; +extern crate alloc; + +use user_lib::{ + open, + OpenFlags, + close, + read, +}; +use alloc::string::String; + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + assert!(argc == 2); + let fd = open(argv[1], OpenFlags::RDONLY); + if fd == -1 { + panic!("Error occured when opening file"); + } + let fd = fd as usize; + let mut buf = [0u8; 16]; + let mut s = String::new(); + loop { + let size = read(fd, &mut buf) as usize; + if size == 0 { break; } + s.push_str(core::str::from_utf8(&buf[..size]).unwrap()); + } + println!("{}", s); + close(fd); + 0 +} \ No newline at end of file diff --git a/user/src/bin/cmdline_args.rs b/user/src/bin/cmdline_args.rs new file mode 100644 index 00000000..b49ec332 --- /dev/null +++ b/user/src/bin/cmdline_args.rs @@ -0,0 +1,16 @@ +#![no_std] +#![no_main] + +extern crate alloc; + +#[macro_use] +extern crate user_lib; + +#[no_mangle] +pub fn main(argc: usize, argv: &[&str]) -> i32 { + println!("argc = {}", argc); + for i in 0..argc { + println!("argv[{}] = {}", i, argv[i]); + } + 0 +} \ No newline at end of file diff --git a/user/src/bin/initproc.rs b/user/src/bin/initproc.rs index 6d30fdb6..99563f47 100644 --- a/user/src/bin/initproc.rs +++ b/user/src/bin/initproc.rs @@ -14,7 +14,7 @@ use user_lib::{ #[no_mangle] fn main() -> i32 { if fork() == 0 { - exec("user_shell\0"); + exec("user_shell\0", &[0 as *const u8]); } else { loop { let mut exit_code: i32 = 0; diff --git a/user/src/bin/run_pipe_test.rs b/user/src/bin/run_pipe_test.rs index ca2afd15..000b82d5 100644 --- a/user/src/bin/run_pipe_test.rs +++ b/user/src/bin/run_pipe_test.rs @@ -10,7 +10,7 @@ use user_lib::{fork, exec, wait}; pub fn main() -> i32 { for i in 0..1000 { if fork() == 0 { - exec("pipe_large_test\0"); + exec("pipe_large_test\0", &[0 as *const u8]); } else { let mut _unused: i32 = 0; wait(&mut _unused); diff --git a/user/src/bin/user_shell.rs b/user/src/bin/user_shell.rs index 796bddb5..b2c33f2d 100644 --- a/user/src/bin/user_shell.rs +++ b/user/src/bin/user_shell.rs @@ -12,7 +12,16 @@ const DL: u8 = 0x7fu8; const BS: u8 = 0x08u8; use alloc::string::String; -use user_lib::{fork, exec, waitpid}; +use alloc::vec::Vec; +use user_lib::{ + fork, + exec, + waitpid, + open, + OpenFlags, + close, + dup, +}; use user_lib::console::getchar; #[no_mangle] @@ -26,11 +35,78 @@ pub fn main() -> i32 { LF | CR => { println!(""); if !line.is_empty() { - line.push('\0'); + let args: Vec<_> = line.as_str().split(' ').collect(); + let mut args_copy: Vec = args + .iter() + .map(|&arg| { + let mut string = String::new(); + string.push_str(arg); + string + }) + .collect(); + + args_copy + .iter_mut() + .for_each(|string| { + string.push('\0'); + }); + + // redirect input + let mut input = String::new(); + if let Some((idx, _)) = args_copy + .iter() + .enumerate() + .find(|(_, arg)| arg.as_str() == "<\0") { + input = args_copy[idx + 1].clone(); + args_copy.drain(idx..=idx + 1); + } + + // redirect output + let mut output = String::new(); + if let Some((idx, _)) = args_copy + .iter() + .enumerate() + .find(|(_, arg)| arg.as_str() == ">\0") { + output = args_copy[idx + 1].clone(); + args_copy.drain(idx..=idx + 1); + } + + let mut args_addr: Vec<*const u8> = args_copy + .iter() + .map(|arg| arg.as_ptr()) + .collect(); + args_addr.push(0 as *const u8); let pid = fork(); if pid == 0 { + // input redirection + if !input.is_empty() { + let input_fd = open(input.as_str(), OpenFlags::RDONLY); + if input_fd == -1 { + println!("Error when opening file {}", input); + return -4; + } + let input_fd = input_fd as usize; + close(0); + assert_eq!(dup(input_fd), 0); + close(input_fd); + } + // output redirection + if !output.is_empty() { + let output_fd = open( + output.as_str(), + OpenFlags::CREATE | OpenFlags::WRONLY + ); + if output_fd == -1 { + println!("Error when opening file {}", output); + return -4; + } + let output_fd = output_fd as usize; + close(1); + assert_eq!(dup(output_fd), 1); + close(output_fd); + } // child process - if exec(line.as_str()) == -1 { + if exec(args_copy[0].as_str(), args_addr.as_slice()) == -1 { println!("Error when executing!"); return -4; } diff --git a/user/src/bin/usertests.rs b/user/src/bin/usertests.rs index 7a4a6d7b..e8be6c45 100644 --- a/user/src/bin/usertests.rs +++ b/user/src/bin/usertests.rs @@ -26,7 +26,7 @@ pub fn main() -> i32 { println!("Usertests: Running {}", test); let pid = fork(); if pid == 0 { - exec(*test); + exec(*test, &[0 as *const u8]); panic!("unreachable!"); } else { let mut exit_code: i32 = Default::default(); diff --git a/user/src/lib.rs b/user/src/lib.rs index 244baaea..0ef03bfb 100644 --- a/user/src/lib.rs +++ b/user/src/lib.rs @@ -15,8 +15,9 @@ extern crate bitflags; use syscall::*; use buddy_system_allocator::LockedHeap; +use alloc::vec::Vec; -const USER_HEAP_SIZE: usize = 16384; +const USER_HEAP_SIZE: usize = 32768; static mut HEAP_SPACE: [u8; USER_HEAP_SIZE] = [0; USER_HEAP_SIZE]; @@ -30,17 +31,31 @@ pub fn handle_alloc_error(layout: core::alloc::Layout) -> ! { #[no_mangle] #[link_section = ".text.entry"] -pub extern "C" fn _start() -> ! { +pub extern "C" fn _start(argc: usize, argv: usize) -> ! { unsafe { HEAP.lock() .init(HEAP_SPACE.as_ptr() as usize, USER_HEAP_SIZE); } - exit(main()); + let mut v: Vec<&'static str> = Vec::new(); + for i in 0..argc { + let str_start = unsafe { + ((argv + i * core::mem::size_of::()) as *const usize).read_volatile() + }; + let len = (0usize..).find(|i| unsafe { + ((str_start + *i) as *const u8).read_volatile() == 0 + }).unwrap(); + v.push( + core::str::from_utf8(unsafe { + core::slice::from_raw_parts(str_start as *const u8, len) + }).unwrap() + ); + } + exit(main(argc, v.as_slice())); } #[linkage = "weak"] #[no_mangle] -fn main() -> i32 { +fn main(_argc: usize, _argv: &[&str]) -> i32 { panic!("Cannot find main!"); } @@ -54,6 +69,7 @@ bitflags! { } } +pub fn dup(fd: usize) -> isize { sys_dup(fd) } pub fn open(path: &str, flags: OpenFlags) -> isize { sys_open(path, flags.bits) } pub fn close(fd: usize) -> isize { sys_close(fd) } pub fn pipe(pipe_fd: &mut [usize]) -> isize { sys_pipe(pipe_fd) } @@ -64,7 +80,7 @@ pub fn yield_() -> isize { sys_yield() } pub fn get_time() -> isize { sys_get_time() } pub fn getpid() -> isize { sys_getpid() } pub fn fork() -> isize { sys_fork() } -pub fn exec(path: &str) -> isize { sys_exec(path) } +pub fn exec(path: &str, args: &[*const u8]) -> isize { sys_exec(path, args) } pub fn wait(exit_code: &mut i32) -> isize { loop { match sys_waitpid(-1, exit_code as *mut _) { diff --git a/user/src/syscall.rs b/user/src/syscall.rs index a1101e1b..1cd30f84 100644 --- a/user/src/syscall.rs +++ b/user/src/syscall.rs @@ -1,3 +1,4 @@ +const SYSCALL_DUP: usize = 24; const SYSCALL_OPEN: usize = 56; const SYSCALL_CLOSE: usize = 57; const SYSCALL_PIPE: usize = 59; @@ -24,6 +25,10 @@ fn syscall(id: usize, args: [usize; 3]) -> isize { ret } +pub fn sys_dup(fd: usize) -> isize { + syscall(SYSCALL_DUP, [fd, 0, 0]) +} + pub fn sys_open(path: &str, flags: u32) -> isize { syscall(SYSCALL_OPEN, [path.as_ptr() as usize, flags as usize, 0]) } @@ -65,8 +70,8 @@ pub fn sys_fork() -> isize { syscall(SYSCALL_FORK, [0, 0, 0]) } -pub fn sys_exec(path: &str) -> isize { - syscall(SYSCALL_EXEC, [path.as_ptr() as usize, 0, 0]) +pub fn sys_exec(path: &str, args: &[*const u8]) -> isize { + syscall(SYSCALL_EXEC, [path.as_ptr() as usize, args.as_ptr() as usize, 0]) } pub fn sys_waitpid(pid: isize, exit_code: *mut i32) -> isize {