From c6a262d2157a02ca917205d3291a1ec271a3faf3 Mon Sep 17 00:00:00 2001
From: Yifan Wu <shinbokuow@163.com>
Date: Sun, 21 Feb 2021 09:28:50 +0800
Subject: [PATCH 1/5] Close all pipes in pipetest.

---
 user/src/bin/pipetest.rs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/user/src/bin/pipetest.rs b/user/src/bin/pipetest.rs
index 58237847..c151fbdd 100644
--- a/user/src/bin/pipetest.rs
+++ b/user/src/bin/pipetest.rs
@@ -23,6 +23,8 @@ pub fn main() -> i32 {
         close(pipe_fd[1]);
         let mut buffer = [0u8; 32];
         let len_read = read(pipe_fd[0], &mut buffer) as usize;
+        // close read_end
+        close(pipe_fd[0]);
         assert_eq!(core::str::from_utf8(&buffer[..len_read]).unwrap(), STR);
         println!("Read OK, child process exited!");
         0

From e8a0682cf8d2f533711cf2fe99f015ce6462ac4e Mon Sep 17 00:00:00 2001
From: Yifan Wu <shinbokuow@163.com>
Date: Wed, 24 Feb 2021 03:42:45 +0800
Subject: [PATCH 2/5] Refactor easy-fs

---
 .gitignore                                    |   4 +
 easy-fs-fuse/Cargo.toml                       |  12 ++
 {easy-fs/src/bin => easy-fs-fuse/src}/main.rs |  48 +++--
 easy-fs/Cargo.toml                            |   3 +-
 easy-fs/build.rs                              |   6 -
 easy-fs/src/bitmap.rs                         |  54 +++---
 easy-fs/src/block_cache.rs                    | 116 ++++++++----
 easy-fs/src/dirty.rs                          |  44 -----
 easy-fs/src/efs.rs                            |  91 ++++++----
 easy-fs/src/layout.rs                         |  52 +++---
 easy-fs/src/lib.rs                            |   4 +-
 easy-fs/src/vfs.rs                            | 169 ++++++++++--------
 os/Makefile                                   |   2 +-
 user/Makefile                                 |   2 +-
 14 files changed, 329 insertions(+), 278 deletions(-)
 create mode 100644 easy-fs-fuse/Cargo.toml
 rename {easy-fs/src/bin => easy-fs-fuse/src}/main.rs (77%)
 delete mode 100644 easy-fs/build.rs
 delete mode 100644 easy-fs/src/dirty.rs

diff --git a/.gitignore b/.gitignore
index 3327607d..74b65454 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,4 +6,8 @@ os/Cargo.lock
 user/target/*
 user/.idea/*
 user/Cargo.lock
+easy-fs/Cargo.lock
+easy-fs/target/*
+easy-fs-fuse/Cargo.lock
+easy-fs-fuse/target/*
 tools/
diff --git a/easy-fs-fuse/Cargo.toml b/easy-fs-fuse/Cargo.toml
new file mode 100644
index 00000000..ee0ef971
--- /dev/null
+++ b/easy-fs-fuse/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "easy-fs-fuse"
+version = "0.1.0"
+authors = ["Yifan Wu <shinbokuow@163.com>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+clap = "2.33.3"
+easy-fs = { path = "../easy-fs" }
+rand = "0.8.0"
\ No newline at end of file
diff --git a/easy-fs/src/bin/main.rs b/easy-fs-fuse/src/main.rs
similarity index 77%
rename from easy-fs/src/bin/main.rs
rename to easy-fs-fuse/src/main.rs
index af972ddf..04f74f00 100644
--- a/easy-fs/src/bin/main.rs
+++ b/easy-fs-fuse/src/main.rs
@@ -1,6 +1,3 @@
-extern crate easy_fs;
-extern crate alloc;
-
 use easy_fs::{
     BlockDevice,
     EasyFileSystem,
@@ -8,7 +5,8 @@ use easy_fs::{
 use std::fs::{File, OpenOptions, read_dir};
 use std::io::{Read, Write, Seek, SeekFrom};
 use std::sync::Mutex;
-use alloc::sync::Arc;
+use std::sync::Arc;
+use clap::{Arg, App};
 
 const BLOCK_SZ: usize = 512;
 
@@ -34,15 +32,30 @@ fn main() {
     easy_fs_pack().expect("Error when packing easy-fs!");
 }
 
-static TARGET_PATH: &str = "../user/target/riscv64gc-unknown-none-elf/release/";
-
 fn easy_fs_pack() -> std::io::Result<()> {
+    let matches = App::new("EasyFileSystem packer")
+        .arg(Arg::with_name("source")
+            .short("s")
+            .long("source")
+            .takes_value(true)
+            .help("Executable source dir(with backslash)")
+        )
+        .arg(Arg::with_name("target")
+            .short("t")
+            .long("target")
+            .takes_value(true)
+            .help("Executable target dir(with backslash)")    
+        )
+        .get_matches();
+    let src_path = matches.value_of("source").unwrap();
+    let target_path = matches.value_of("target").unwrap();
+    println!("src_path = {}\ntarget_path = {}", src_path, target_path);
     let block_file = Arc::new(BlockFile(Mutex::new({
         let f = OpenOptions::new()
             .read(true)
             .write(true)
             .create(true)
-            .open(format!("{}{}", TARGET_PATH, "fs.img"))?;
+            .open(format!("{}{}", target_path, "fs.img"))?;
         f.set_len(8192 * 512).unwrap();
         f
     })));
@@ -53,7 +66,7 @@ fn easy_fs_pack() -> std::io::Result<()> {
         1,
     );
     let root_inode = Arc::new(EasyFileSystem::root_inode(&efs));
-    let apps: Vec<_> = read_dir("../user/src/bin")
+    let apps: Vec<_> = read_dir(src_path)
         .unwrap()
         .into_iter()
         .map(|dir_entry| {
@@ -64,7 +77,7 @@ fn easy_fs_pack() -> std::io::Result<()> {
         .collect();
     for app in apps {
         // load app data from host file system
-        let mut host_file = File::open(format!("{}{}", TARGET_PATH, app)).unwrap();
+        let mut host_file = File::open(format!("{}{}", target_path, app)).unwrap();
         let mut all_data: Vec<u8> = Vec::new();
         host_file.read_to_end(&mut all_data).unwrap();
         // create a file in easy-fs
@@ -79,22 +92,24 @@ fn easy_fs_pack() -> std::io::Result<()> {
     Ok(())
 }
 
-/*
 #[test]
 fn efs_test() -> std::io::Result<()> {
-    let block_file = Arc::new(BlockFile(Mutex::new(
-        OpenOptions::new()
+    let block_file = Arc::new(BlockFile(Mutex::new({
+        let f = OpenOptions::new()
             .read(true)
             .write(true)
-            .open("target/fs.img")?
-    )));
+            .create(true)
+            .open("target/fs.img")?;
+        f.set_len(8192 * 512).unwrap();
+        f
+    })));
     EasyFileSystem::create(
         block_file.clone(),
         4096,
         1,
     );
     let efs = EasyFileSystem::open(block_file.clone());
-    let mut root_inode = EasyFileSystem::root_inode(&efs);
+    let root_inode = EasyFileSystem::root_inode(&efs);
     root_inode.create("filea");
     root_inode.create("fileb");
     for name in root_inode.ls() {
@@ -146,5 +161,4 @@ fn efs_test() -> std::io::Result<()> {
     random_str_test((12 + 128) * BLOCK_SZ);
 
     Ok(())
-}
- */
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/easy-fs/Cargo.toml b/easy-fs/Cargo.toml
index f32c49f7..6e7cd925 100644
--- a/easy-fs/Cargo.toml
+++ b/easy-fs/Cargo.toml
@@ -8,5 +8,4 @@ edition = "2018"
 
 [dependencies]
 spin = "0.7.0"
-lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
-#rand = "0.8.0"
\ No newline at end of file
+lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
\ No newline at end of file
diff --git a/easy-fs/build.rs b/easy-fs/build.rs
deleted file mode 100644
index 5529b4fe..00000000
--- a/easy-fs/build.rs
+++ /dev/null
@@ -1,6 +0,0 @@
-static TARGET_PATH: &str = "../user/target/riscv64gc-unknown-none-elf/release/";
-
-fn main() {
-    println!("cargo:rerun-if-changed=../user/src/");
-    println!("cargo:rerun-if-changed={}", TARGET_PATH);
-}
diff --git a/easy-fs/src/bitmap.rs b/easy-fs/src/bitmap.rs
index 87f8d297..4feaa9cf 100644
--- a/easy-fs/src/bitmap.rs
+++ b/easy-fs/src/bitmap.rs
@@ -1,7 +1,9 @@
 use alloc::sync::Arc;
-use super::BlockDevice;
-use super::Dirty;
-use super::BLOCK_SZ;
+use super::{
+    BlockDevice,
+    BLOCK_SZ,
+    get_block_cache,
+};
 
 type BitmapBlock = [u64; 64];
 
@@ -26,41 +28,45 @@ impl Bitmap {
             blocks,
         }
     }
+
     pub fn alloc(&self, block_device: &Arc<dyn BlockDevice>) -> Option<usize> {
         for block_id in 0..self.blocks {
-            let mut dirty_bitmap_block: Dirty<BitmapBlock> = Dirty::new(
+            let pos = get_block_cache(
                 block_id + self.start_block_id as usize,
-                0,
-                block_device.clone()
-            );
-            let bitmap_block = dirty_bitmap_block.get_mut();
-            if let Some((bits64_pos, inner_pos)) = bitmap_block
-                .iter()
-                .enumerate()
-                .find(|(_, bits64)| **bits64 != u64::MAX)
-                .map(|(bits64_pos, bits64)| {
-                    (bits64_pos, bits64.trailing_ones() as usize)
-                }) {
-                // modify cache
-                bitmap_block[bits64_pos] |= 1u64 << inner_pos;
-                return Some(block_id * BLOCK_BITS + bits64_pos * 64 + inner_pos as usize);
-                // after dirty is dropped, data will be written back automatically
+                Arc::clone(block_device),
+            ).lock().modify(0, |bitmap_block: &mut BitmapBlock| {
+                if let Some((bits64_pos, inner_pos)) = bitmap_block
+                    .iter()
+                    .enumerate()
+                    .find(|(_, bits64)| **bits64 != u64::MAX)
+                    .map(|(bits64_pos, bits64)| {
+                        (bits64_pos, bits64.trailing_ones() as usize)
+                    }) {
+                    // modify cache
+                    bitmap_block[bits64_pos] |= 1u64 << inner_pos;
+                    Some(block_id * BLOCK_BITS + bits64_pos * 64 + inner_pos as usize)
+                } else {
+                    None
+                }
+            });
+            if pos.is_some() {
+                return pos;
             }
         }
         None
     }
+
     pub fn dealloc(&self, block_device: &Arc<dyn BlockDevice>, bit: usize) {
         let (block_pos, bits64_pos, inner_pos) = decomposition(bit);
-        let mut dirty_bitmap_block: Dirty<BitmapBlock> = Dirty::new(
+        get_block_cache(
             block_pos + self.start_block_id,
-            0,
-            block_device.clone(),
-        );
-        dirty_bitmap_block.modify(|bitmap_block| {
+            Arc::clone(block_device)
+        ).lock().modify(0, |bitmap_block: &mut BitmapBlock| {
             assert!(bitmap_block[bits64_pos] & (1u64 << inner_pos) > 0);
             bitmap_block[bits64_pos] -= 1u64 << inner_pos;
         });
     }
+
     pub fn maximum(&self) -> usize {
         self.blocks * BLOCK_BITS
     }
diff --git a/easy-fs/src/block_cache.rs b/easy-fs/src/block_cache.rs
index 34450f63..57265812 100644
--- a/easy-fs/src/block_cache.rs
+++ b/easy-fs/src/block_cache.rs
@@ -1,9 +1,9 @@
 use super::{
-    BlockDevice,
     BLOCK_SZ,
+    BlockDevice,
 };
-use alloc::sync::{Arc, Weak};
-use alloc::collections::BTreeMap;
+use alloc::collections::VecDeque;
+use alloc::sync::Arc;
 use lazy_static::*;
 use spin::Mutex;
 
@@ -11,76 +11,118 @@ pub struct BlockCache {
     cache: [u8; BLOCK_SZ],
     block_id: usize,
     block_device: Arc<dyn BlockDevice>,
+    modified: bool,
 }
 
 impl BlockCache {
-    pub fn new(block_id: usize, block_device: Arc<dyn BlockDevice>) -> Self {
+    /// Load a new BlockCache from disk.
+    pub fn new(
+        block_id: usize, 
+        block_device: Arc<dyn BlockDevice>
+    ) -> Self {
         let mut cache = [0u8; BLOCK_SZ];
         block_device.read_block(block_id, &mut cache);
         Self {
             cache,
             block_id,
             block_device,
+            modified: false,
         }
     }
-    pub fn start_addr(&self, offset: usize) -> usize {
+
+    fn addr_of_offset(&self, offset: usize) -> usize {
         &self.cache[offset] as *const _ as usize
     }
+
+    pub fn get_ref<T>(&self, offset: usize) -> &T where T: Sized {
+        let type_size = core::mem::size_of::<T>();
+        assert!(offset + type_size <= BLOCK_SZ);
+        let addr = self.addr_of_offset(offset);
+        unsafe { &*(addr as *const T) } 
+    }
+
+    pub fn get_mut<T>(&mut self, offset: usize) -> &mut T where T: Sized {
+        let type_size = core::mem::size_of::<T>();
+        assert!(offset + type_size <= BLOCK_SZ);
+        self.modified = true;
+        let addr = self.addr_of_offset(offset);
+        unsafe { &mut *(addr as *mut T) }
+    }
+
+    pub fn read<T, V>(&self, offset: usize, f: impl FnOnce(&T) -> V) -> V {
+        f(self.get_ref(offset))
+    }
+
+    pub fn modify<T, V>(&mut self, offset:usize, f: impl FnOnce(&mut T) -> V) -> V {
+        f(self.get_mut(offset))
+    }
+
+    pub fn sync(&mut self) {
+        if self.modified {
+            self.modified = false;
+            self.block_device.write_block(self.block_id, &self.cache);
+        }
+    }
 }
 
 impl Drop for BlockCache {
     fn drop(&mut self) {
-        // write back
-        self.block_device.write_block(self.block_id, &self.cache);
-        // invalid in block cache manager
-        BLOCK_CACHE_MANAGER.lock().invalid(self.block_id);
+        self.sync()
     }
 }
 
-pub struct BlockCacheManager {
-    map: BTreeMap<usize, Weak<BlockCache>>,
-}
+const BLOCK_CACHE_SIZE: usize = 16;
 
-lazy_static! {
-    static ref BLOCK_CACHE_MANAGER: Mutex<BlockCacheManager> = Mutex::new(
-        BlockCacheManager::new()
-    );
+pub struct BlockCacheManager {
+    queue: VecDeque<(usize, Arc<Mutex<BlockCache>>)>,
 }
 
 impl BlockCacheManager {
     pub fn new() -> Self {
-        Self { map: BTreeMap::new() }
+        Self { queue: VecDeque::new() }
     }
-    pub fn get(
+
+    pub fn get_block_cache(
         &mut self,
         block_id: usize,
-        block_device: Arc<dyn BlockDevice>
-    ) -> Arc<BlockCache> {
-        if let Some(block_cache) = self.map.get(&block_id) {
-            // return cloned
-            block_cache.upgrade().unwrap().clone()
+        block_device: Arc<dyn BlockDevice>,
+    ) -> Arc<Mutex<BlockCache>> {
+        if let Some(pair) = self.queue
+            .iter()
+            .find(|pair| pair.0 == block_id) {
+                Arc::clone(&pair.1)
         } else {
-            // fetch from disk
-            let block_cache = Arc::new(BlockCache::new(
-                block_id,
-                block_device.clone()
+            // substitute
+            if self.queue.len() == BLOCK_CACHE_SIZE {
+                // from front to tail
+                if let Some((idx, _)) = self.queue
+                    .iter()
+                    .enumerate()
+                    .find(|(_, pair)| Arc::strong_count(&pair.1) == 1) {
+                    self.queue.drain(idx..=idx);
+                } else {
+                    panic!("Run out of BlockCache!");
+                }
+            }
+            // load block into mem and push back
+            let block_cache = Arc::new(Mutex::new(
+                BlockCache::new(block_id, Arc::clone(&block_device))
             ));
-            self.map.insert(
-                block_id,
-                Arc::downgrade(&block_cache),
-            );
-            // return
+            self.queue.push_back((block_id, Arc::clone(&block_cache)));
             block_cache
         }
     }
-    pub fn invalid(&mut self, block_id: usize) {
-        assert!(self.map.remove(&block_id).is_some());
-    }
+}
+
+lazy_static! {
+    pub static ref BLOCK_CACHE_MANAGER: Mutex<BlockCacheManager> = Mutex::new(
+        BlockCacheManager::new()
+    );
 }
 
 pub fn get_block_cache(
     block_id: usize,
     block_device: Arc<dyn BlockDevice>
-) -> Arc<BlockCache> {
-    BLOCK_CACHE_MANAGER.lock().get(block_id, block_device)
+) -> Arc<Mutex<BlockCache>> {
+    BLOCK_CACHE_MANAGER.lock().get_block_cache(block_id, block_device)
 }
\ No newline at end of file
diff --git a/easy-fs/src/dirty.rs b/easy-fs/src/dirty.rs
deleted file mode 100644
index 8aa41e35..00000000
--- a/easy-fs/src/dirty.rs
+++ /dev/null
@@ -1,44 +0,0 @@
-use super::{
-    BlockDevice,
-    BLOCK_SZ,
-    BlockCache,
-    get_block_cache,
-};
-use alloc::sync::Arc;
-use core::marker::PhantomData;
-
-pub struct Dirty<T> {
-    block_cache: Arc<BlockCache>,
-    offset: usize,
-    phantom: PhantomData<T>,
-}
-
-impl<T> Dirty<T> where T: Sized {
-    pub fn new(block_id: usize, offset: usize, block_device: Arc<dyn BlockDevice>) -> Self {
-        Self {
-            block_cache: get_block_cache(block_id, block_device.clone()),
-            offset,
-            phantom: PhantomData,
-        }
-    }
-    pub fn get_mut(&mut self) -> &mut T {
-        let type_size = core::mem::size_of::<T>();
-        // assert that the struct is inside a block
-        assert!(self.offset + type_size <= BLOCK_SZ);
-        let start_addr = self.block_cache.start_addr(self.offset);
-        unsafe { &mut *(start_addr as *mut T) }
-    }
-    pub fn get_ref(&self) -> &T {
-        let type_size = core::mem::size_of::<T>();
-        // assert that the struct is inside a block
-        assert!(self.offset + type_size <= BLOCK_SZ);
-        let start_addr = self.block_cache.start_addr(self.offset);
-        unsafe { &*(start_addr as *const T) }
-    }
-    pub fn read<V>(&self, f: impl FnOnce(&T) -> V) -> V {
-        f(self.get_ref())
-    }
-    pub fn modify<V>(&mut self, f: impl FnOnce(&mut T) -> V) -> V {
-        f(self.get_mut())
-    }
-}
diff --git a/easy-fs/src/efs.rs b/easy-fs/src/efs.rs
index f2103740..caf4d8cb 100644
--- a/easy-fs/src/efs.rs
+++ b/easy-fs/src/efs.rs
@@ -6,8 +6,8 @@ use super::{
     SuperBlock,
     DiskInode,
     DiskInodeType,
-    Dirty,
     Inode,
+    get_block_cache,
 };
 use crate::BLOCK_SZ;
 
@@ -41,7 +41,7 @@ impl EasyFileSystem {
             data_bitmap_blocks as usize,
         );
         let mut efs = Self {
-            block_device,
+            block_device: Arc::clone(&block_device),
             inode_bitmap,
             data_bitmap,
             inode_area_start_block: 1 + inode_bitmap_blocks,
@@ -49,14 +49,19 @@ impl EasyFileSystem {
         };
         // clear all blocks
         for i in 0..total_blocks {
-            efs.get_block(i).modify(|data_block| {
-                for byte in data_block.iter_mut() {
-                    *byte = 0;
-                }
+            get_block_cache(
+                i as usize, 
+                Arc::clone(&block_device)
+            )
+            .lock()
+            .modify(0, |data_block: &mut DataBlock| {
+                for byte in data_block.iter_mut() { *byte = 0; }
             });
         }
         // initialize SuperBlock
-        efs.get_super_block().modify(|super_block| {
+        get_block_cache(0, Arc::clone(&block_device))
+        .lock()
+        .modify(0, |super_block: &mut SuperBlock| {
             super_block.initialize(
                 total_blocks,
                 inode_bitmap_blocks,
@@ -68,10 +73,15 @@ impl EasyFileSystem {
         // write back immediately
         // create a inode for root node "/"
         assert_eq!(efs.alloc_inode(), 0);
-        efs.get_disk_inode(0).modify(|disk_inode| {
+        let (root_inode_block_id, root_inode_offset) = efs.get_disk_inode_pos(0);
+        get_block_cache(
+            root_inode_block_id as usize,
+            Arc::clone(&block_device)
+        )
+        .lock()
+        .modify(root_inode_offset, |disk_inode: &mut DiskInode| {
             disk_inode.initialize(
-                DiskInodeType::Directory,
-                efs.alloc_data(),
+                DiskInodeType::Directory,efs.alloc_data()
             );
         });
         Arc::new(Mutex::new(efs))
@@ -79,54 +89,56 @@ impl EasyFileSystem {
 
     pub fn open(block_device: Arc<dyn BlockDevice>) -> Arc<Mutex<Self>> {
         // read SuperBlock
-        let super_block_dirty: Dirty<SuperBlock> = Dirty::new(0, 0, block_device.clone());
-        let super_block = super_block_dirty.get_ref();
-        assert!(super_block.is_valid(), "Error loading EFS!");
-        let inode_total_blocks =
-            super_block.inode_bitmap_blocks + super_block.inode_area_blocks;
-        let efs = Self {
-            block_device,
-            inode_bitmap: Bitmap::new(
-                1,
-                super_block.inode_bitmap_blocks as usize
-            ),
-            data_bitmap: Bitmap::new(
-                (1 + inode_total_blocks) as usize,
-                super_block.data_bitmap_blocks as usize,
-            ),
-            inode_area_start_block: 1 + super_block.inode_bitmap_blocks,
-            data_area_start_block: 1 + inode_total_blocks + super_block.data_bitmap_blocks,
-        };
-        Arc::new(Mutex::new(efs))
+        get_block_cache(0, Arc::clone(&block_device))
+            .lock()
+            .read(0, |super_block: &SuperBlock| {
+                assert!(super_block.is_valid(), "Error loading EFS!");
+                let inode_total_blocks =
+                    super_block.inode_bitmap_blocks + super_block.inode_area_blocks;
+                let efs = Self {
+                    block_device,
+                    inode_bitmap: Bitmap::new(
+                        1,
+                        super_block.inode_bitmap_blocks as usize
+                    ),
+                    data_bitmap: Bitmap::new(
+                        (1 + inode_total_blocks) as usize,
+                        super_block.data_bitmap_blocks as usize,
+                    ),
+                    inode_area_start_block: 1 + super_block.inode_bitmap_blocks,
+                    data_area_start_block: 1 + inode_total_blocks + super_block.data_bitmap_blocks,
+                };
+                Arc::new(Mutex::new(efs))
+            })        
     }
 
     pub fn root_inode(efs: &Arc<Mutex<Self>>) -> Inode {
+        let block_device = Arc::clone(&efs.lock().block_device);
         Inode::new(
             0,
-            efs.clone(),
-            efs.lock().block_device.clone(),
+            Arc::clone(efs),
+            block_device,
         )
     }
 
+    /*
     fn get_super_block(&self) -> Dirty<SuperBlock> {
         Dirty::new(0, 0, self.block_device.clone())
     }
+    */
 
-    pub fn get_disk_inode(&self, inode_id: u32) -> Dirty<DiskInode> {
+    pub fn get_disk_inode_pos(&self, inode_id: u32) -> (u32, usize) {
         let inode_size = core::mem::size_of::<DiskInode>();
         let inodes_per_block = (BLOCK_SZ / inode_size) as u32;
         let block_id = self.inode_area_start_block + inode_id / inodes_per_block;
-        Dirty::new(
-            block_id as usize,
-            (inode_id % inodes_per_block) as usize * inode_size,
-            self.block_device.clone(),
-        )
+        (block_id, (inode_id % inodes_per_block) as usize * inode_size)
     }
 
-    pub fn get_data_block(&self, data_block_id: u32) -> Dirty<DataBlock> {
-        self.get_block(self.data_area_start_block + data_block_id)
+    pub fn get_data_block_id(&self, data_block_id: u32) -> u32 {
+        self.data_area_start_block + data_block_id
     }
 
+    /*
     fn get_block(&self, block_id: u32) -> Dirty<DataBlock> {
         Dirty::new(
             block_id as usize,
@@ -134,6 +146,7 @@ impl EasyFileSystem {
             self.block_device.clone(),
         )
     }
+    */
 
     pub fn alloc_inode(&mut self) -> u32 {
         self.inode_bitmap.alloc(&self.block_device).unwrap() as u32
diff --git a/easy-fs/src/layout.rs b/easy-fs/src/layout.rs
index 725c1e44..ace1ced8 100644
--- a/easy-fs/src/layout.rs
+++ b/easy-fs/src/layout.rs
@@ -2,7 +2,7 @@ use core::fmt::{Debug, Formatter, Result};
 use super::{
     BLOCK_SZ,
     BlockDevice,
-    Dirty,
+    get_block_cache,
 };
 use alloc::sync::Arc;
 use alloc::vec::Vec;
@@ -87,6 +87,7 @@ impl DiskInode {
     pub fn is_dir(&self) -> bool {
         self.type_ == DiskInodeType::Directory
     }
+    #[allow(unused)]
     pub fn is_file(&self) -> bool {
         self.type_ == DiskInodeType::File
     }
@@ -102,14 +103,11 @@ impl DiskInode {
             self.direct[inner_id]
         } else {
             // only support indirect1 now
-            Dirty::<IndirectBlock>::new(
-                self.indirect1 as usize,
-                0,
-                block_device.clone()
-            ).read(|indirect_block| {
-                // it will panic if file is too large
-                indirect_block[inner_id - INODE_DIRECT_COUNT]
-            })
+            get_block_cache(self.indirect1 as usize, Arc::clone(block_device))
+                .lock()
+                .read(0, |indirect_block: &IndirectBlock| {
+                    indirect_block[inner_id - INODE_DIRECT_COUNT]
+                })
         }
     }
     pub fn blocks_num_needed(&self, new_size: u32) -> u32 {
@@ -126,11 +124,12 @@ impl DiskInode {
         let last_blocks = self.blocks();
         self.size = new_size;
         let current_blocks = self.blocks();
-        Dirty::<IndirectBlock>::new(
+        get_block_cache(
             self.indirect1 as usize,
-            0,
-            block_device.clone()
-        ).modify(|indirect_block| {
+            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];
@@ -152,11 +151,12 @@ impl DiskInode {
             self.direct[i] = 0;
         }
         if blocks > INODE_DIRECT_COUNT {
-            Dirty::<IndirectBlock>::new(
+            get_block_cache(
                 self.indirect1 as usize,
-                0,
-                block_device.clone(),
-            ).modify(|indirect_block| {
+                Arc::clone(block_device),
+            )
+            .lock()
+            .modify(0, |indirect_block: &mut IndirectBlock| {
                 for i in 0..blocks - INODE_DIRECT_COUNT {
                     v.push(indirect_block[i]);
                     indirect_block[i] = 0;
@@ -185,11 +185,12 @@ impl DiskInode {
             // read and update read size
             let block_read_size = end_current_block - start;
             let dst = &mut buf[read_size..read_size + block_read_size];
-            Dirty::<DataBlock>::new(
+            get_block_cache(
                 self.get_block_id(start_block as u32, block_device) as usize,
-                0,
-                block_device.clone()
-            ).read(|data_block| {
+                Arc::clone(block_device),
+            )
+            .lock()
+            .read(0, |data_block: &DataBlock| {
                 let src = &data_block[start % BLOCK_SZ..start % BLOCK_SZ + block_read_size];
                 dst.copy_from_slice(src);
             });
@@ -219,11 +220,12 @@ impl DiskInode {
             end_current_block = end_current_block.min(end);
             // write and update write size
             let block_write_size = end_current_block - start;
-            Dirty::<DataBlock>::new(
+            get_block_cache(
                 self.get_block_id(start_block as u32, block_device) as usize,
-                0,
-                block_device.clone()
-            ).modify(|data_block| {
+                Arc::clone(block_device)
+            )
+            .lock()
+            .modify(0, |data_block: &mut DataBlock| {
                 let src = &buf[write_size..write_size + block_write_size];
                 let dst = &mut data_block[start % BLOCK_SZ..start % BLOCK_SZ + block_write_size];
                 dst.copy_from_slice(src);
diff --git a/easy-fs/src/lib.rs b/easy-fs/src/lib.rs
index 961efa65..10a5af76 100644
--- a/easy-fs/src/lib.rs
+++ b/easy-fs/src/lib.rs
@@ -5,7 +5,6 @@ extern crate alloc;
 mod block_dev;
 mod layout;
 mod efs;
-mod dirty;
 mod bitmap;
 mod vfs;
 mod block_cache;
@@ -15,6 +14,5 @@ pub use block_dev::BlockDevice;
 pub use efs::EasyFileSystem;
 pub use vfs::Inode;
 use layout::*;
-use dirty::Dirty;
 use bitmap::Bitmap;
-use block_cache::{BlockCache, get_block_cache};
\ No newline at end of file
+use block_cache::get_block_cache;
\ No newline at end of file
diff --git a/easy-fs/src/vfs.rs b/easy-fs/src/vfs.rs
index d7631354..a98fd607 100644
--- a/easy-fs/src/vfs.rs
+++ b/easy-fs/src/vfs.rs
@@ -1,12 +1,12 @@
 use super::{
     BlockDevice,
-    Dirty,
     DiskInode,
     DiskInodeType,
     DirEntry,
     DirentBytes,
     EasyFileSystem,
     DIRENT_SZ,
+    get_block_cache,
 };
 use alloc::sync::Arc;
 use alloc::string::String;
@@ -14,7 +14,8 @@ use alloc::vec::Vec;
 use spin::{Mutex, MutexGuard};
 
 pub struct Inode {
-    inode_id: u32,
+    block_id: usize,
+    block_offset: usize,
     fs: Arc<Mutex<EasyFileSystem>>,
     block_device: Arc<dyn BlockDevice>,
 }
@@ -25,37 +26,51 @@ impl Inode {
         fs: Arc<Mutex<EasyFileSystem>>,
         block_device: Arc<dyn BlockDevice>,
     ) -> Self {
+        let (block_id, block_offset) = fs.lock().get_disk_inode_pos(inode_id);
         Self {
-            inode_id,
+            block_id: block_id as usize,
+            block_offset,
             fs,
             block_device,
         }
     }
 
+    fn read_disk_inode<V>(&self, f: impl FnOnce(&DiskInode) -> V) -> V {
+        get_block_cache(
+            self.block_id,
+            Arc::clone(&self.block_device)
+        ).lock().read(self.block_offset, f)
+    }
+
+    fn modify_disk_inode<V>(&self, f: impl FnOnce(&mut DiskInode) -> V) -> V {
+        get_block_cache(
+            self.block_id,
+            Arc::clone(&self.block_device)
+        ).lock().modify(self.block_offset, f)
+    }
+
+    /*
     fn get_disk_inode(&self, fs: &mut MutexGuard<EasyFileSystem>) -> Dirty<DiskInode> {
         fs.get_disk_inode(self.inode_id)
     }
+    */
 
     fn find_inode_id(
         &self,
         name: &str,
-        inode: &Dirty<DiskInode>,
+        disk_inode: &DiskInode,
     ) -> Option<u32> {
         // assert it is a directory
-        assert!(inode.read(|inode| inode.is_dir()));
-        let file_count = inode.read(|inode| {
-            inode.size as usize
-        }) / DIRENT_SZ;
+        assert!(disk_inode.is_dir());
+        let file_count = (disk_inode.size as usize) / DIRENT_SZ;
         let mut dirent_space: DirentBytes = Default::default();
         for i in 0..file_count {
             assert_eq!(
-                inode.read(|inode| {
-                    inode.read_at(
-                        DIRENT_SZ * i,
-                        &mut dirent_space,
-                        &self.block_device,
-                    )
-                }),
+                disk_inode.read_at(
+                    DIRENT_SZ * i,
+                    &mut dirent_space,
+                    &self.block_device,
+                ),
                 DIRENT_SZ,
             );
             let dirent = DirEntry::from_bytes(&dirent_space);
@@ -67,9 +82,9 @@ impl Inode {
     }
 
     pub fn find(&self, name: &str) -> Option<Arc<Inode>> {
-        let mut fs = self.fs.lock();
-        let inode = self.get_disk_inode(&mut fs);
-        self.find_inode_id(name, &inode)
+        let _ = self.fs.lock();
+        self.read_disk_inode(|disk_inode| {
+            self.find_inode_id(name, disk_inode)
             .map(|inode_id| {
                 Arc::new(Self::new(
                     inode_id,
@@ -77,68 +92,69 @@ impl Inode {
                     self.block_device.clone(),
                 ))
             })
+        })
     }
 
     fn increase_size(
         &self,
         new_size: u32,
-        inode: &mut Dirty<DiskInode>,
+        disk_inode: &mut DiskInode,
         fs: &mut MutexGuard<EasyFileSystem>,
     ) {
-        let size = inode.read(|inode| inode.size);
-        if new_size < size {
+        if new_size < disk_inode.size {
             return;
         }
-        let blocks_needed = inode.read(|inode| {
-            inode.blocks_num_needed(new_size)
-        });
+        let blocks_needed = disk_inode.blocks_num_needed(new_size);
         let mut v: Vec<u32> = Vec::new();
         for _ in 0..blocks_needed {
             v.push(fs.alloc_data());
         }
-        inode.modify(|inode| {
-            inode.increase_size(new_size, v, &self.block_device);
-        });
+        disk_inode.increase_size(new_size, v, &self.block_device);
     }
 
     pub fn create(&self, name: &str) -> Option<Arc<Inode>> {
         let mut fs = self.fs.lock();
-        let mut inode = self.get_disk_inode(&mut fs);
-        // assert it is a directory
-        assert!(inode.read(|inode| inode.is_dir()));
-        // has the file been created?
-        if let Some(_) = self.find_inode_id(name, &inode) {
+        if self.modify_disk_inode(|root_inode| {
+            // assert it is a directory
+            assert!(root_inode.is_dir());
+            // has the file been created?
+            self.find_inode_id(name, root_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
-        fs.get_disk_inode(new_inode_id).modify(|inode| {
-            inode.initialize(
-                DiskInodeType::File,
-                indirect1,
-            )
+        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);
         });
-
-        // append file in the dirent
-        let file_count =
-            inode.read(|inode| inode.size as usize) / DIRENT_SZ;
-        let new_size = (file_count + 1) * DIRENT_SZ;
-        // increase size
-        self.increase_size(new_size as u32, &mut inode, &mut fs);
-        // write dirent
-        let dirent = DirEntry::new(name, new_inode_id);
-        inode.modify(|inode| {
-            inode.write_at(
+        //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;
+            let new_size = (file_count + 1) * DIRENT_SZ;
+            // increase size
+            self.increase_size(new_size as u32, root_inode, &mut fs);
+            // write dirent
+            let dirent = DirEntry::new(name, new_inode_id);
+            root_inode.write_at(
                 file_count * DIRENT_SZ,
                 dirent.into_bytes(),
                 &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
         Some(Arc::new(Self::new(
             new_inode_id,
@@ -148,53 +164,48 @@ impl Inode {
     }
 
     pub fn ls(&self) -> Vec<String> {
-        let mut fs = self.fs.lock();
-        let inode = self.get_disk_inode(&mut fs);
-        let file_count = inode.read(|inode| {
-            (inode.size as usize) / DIRENT_SZ
-        });
-        let mut v: Vec<String> = Vec::new();
-        for i in 0..file_count {
-            let mut dirent_bytes: DirentBytes = Default::default();
-            assert_eq!(
-                inode.read(|inode| {
-                    inode.read_at(
+        let _ = self.fs.lock();
+        self.read_disk_inode(|disk_inode| {
+            let file_count = (disk_inode.size as usize) / DIRENT_SZ;
+            let mut v: Vec<String> = Vec::new();
+            for i in 0..file_count {
+                let mut dirent_bytes: DirentBytes = Default::default();
+                assert_eq!(
+                    disk_inode.read_at(
                         i * DIRENT_SZ,
                         &mut dirent_bytes,
                         &self.block_device,
-                    )
-                }),
-                DIRENT_SZ,
-            );
-            v.push(String::from(DirEntry::from_bytes(&dirent_bytes).name()));
-        }
-        v
+                    ),
+                    DIRENT_SZ,
+                );
+                v.push(String::from(DirEntry::from_bytes(&dirent_bytes).name()));
+            }
+            v
+        })
     }
 
     pub fn read_at(&self, offset: usize, buf: &mut [u8]) -> usize {
-        let mut fs = self.fs.lock();
-        self.get_disk_inode(&mut fs).modify(|disk_inode| {
+        let _ = self.fs.lock();
+        self.read_disk_inode(|disk_inode| {
             disk_inode.read_at(offset, buf, &self.block_device)
         })
     }
 
     pub fn write_at(&self, offset: usize, buf: &[u8]) -> usize {
         let mut fs = self.fs.lock();
-        let mut inode = self.get_disk_inode(&mut fs);
-        self.increase_size((offset + buf.len()) as u32, &mut inode, &mut fs);
-        inode.modify(|disk_inode| {
+        self.modify_disk_inode(|disk_inode| {
+            self.increase_size((offset + buf.len()) as u32, disk_inode, &mut fs);
             disk_inode.write_at(offset, buf, &self.block_device)
         })
     }
 
     pub fn clear(&self) {
         let mut fs = self.fs.lock();
-        let mut inode = self.get_disk_inode(&mut fs);
-        let data_blocks_dealloc = inode.modify(|disk_inode| {
-            disk_inode.clear_size(&self.block_device)
+        self.modify_disk_inode(|disk_inode| {
+            let data_blocks_dealloc = disk_inode.clear_size(&self.block_device);
+            for data_block in data_blocks_dealloc.into_iter() {
+                fs.dealloc_data(data_block);
+            }
         });
-        for data_block in data_blocks_dealloc.into_iter() {
-            fs.dealloc_data(data_block);
-        }
     }
 }
diff --git a/os/Makefile b/os/Makefile
index f1813146..568a2779 100644
--- a/os/Makefile
+++ b/os/Makefile
@@ -50,7 +50,7 @@ $(KERNEL_BIN): kernel
 
 $(FS_IMG): $(APPS)
 	@cd ../user && make build
-	@cd ../easy-fs && cargo run --release
+	@cd ../easy-fs-fuse && cargo run --release -- -s ../user/src/bin/ -t ../user/target/riscv64gc-unknown-none-elf/release/
 
 $(APPS):
 
diff --git a/user/Makefile b/user/Makefile
index 5e0f87c3..e2eaf994 100644
--- a/user/Makefile
+++ b/user/Makefile
@@ -20,4 +20,4 @@ build: binary
 clean:
 	@cargo clean
 
-.PHONY: elf binary build clean
\ No newline at end of file
+.PHONY: elf binary build clean

From 35cc3d6e2fb73acd4e7d874ca8227c0bd50eedd0 Mon Sep 17 00:00:00 2001
From: Yifan Wu <shinbokuow@163.com>
Date: Wed, 24 Feb 2021 03:50:59 +0800
Subject: [PATCH 3/5] Fix overflow bug when ceiling va

---
 os/src/mm/address.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/os/src/mm/address.rs b/os/src/mm/address.rs
index 05d04abd..f9e02805 100644
--- a/os/src/mm/address.rs
+++ b/os/src/mm/address.rs
@@ -73,7 +73,7 @@ impl From<VirtPageNum> for usize {
 
 impl VirtAddr {
     pub fn floor(&self) -> VirtPageNum { VirtPageNum(self.0 / PAGE_SIZE) }
-    pub fn ceil(&self) -> VirtPageNum  { VirtPageNum((self.0 + PAGE_SIZE - 1) / PAGE_SIZE) }
+    pub fn ceil(&self) -> VirtPageNum  { VirtPageNum((self.0 - 1 + PAGE_SIZE) / PAGE_SIZE) }
     pub fn page_offset(&self) -> usize { self.0 & (PAGE_SIZE - 1) }
     pub fn aligned(&self) -> bool { self.page_offset() == 0 }
 }
@@ -88,7 +88,7 @@ impl From<VirtPageNum> for VirtAddr {
 }
 impl PhysAddr {
     pub fn floor(&self) -> PhysPageNum { PhysPageNum(self.0 / PAGE_SIZE) }
-    pub fn ceil(&self) -> PhysPageNum { PhysPageNum((self.0 + PAGE_SIZE - 1) / PAGE_SIZE) }
+    pub fn ceil(&self) -> PhysPageNum { PhysPageNum((self.0 - 1 + PAGE_SIZE) / PAGE_SIZE) }
     pub fn page_offset(&self) -> usize { self.0 & (PAGE_SIZE - 1) }
     pub fn aligned(&self) -> bool { self.page_offset() == 0 }
 }

From cfa3819bee0419250c4049c5143b954a7596abde Mon Sep 17 00:00:00 2001
From: Yifan Wu <shinbokuow@163.com>
Date: Sun, 28 Feb 2021 06:34:15 +0800
Subject: [PATCH 4/5] Add Ubuntu18.04 docker

---
 .dockerignore |  1 +
 Dockerfile    | 40 ++++++++++++++++++++++++++++++++++++++++
 Makefile      |  8 ++++++++
 README.md     |  2 +-
 4 files changed, 50 insertions(+), 1 deletion(-)
 create mode 100644 .dockerignore
 create mode 100644 Dockerfile
 create mode 100644 Makefile

diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000..df3359dd
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1 @@
+*/*
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..ac784bc2
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,40 @@
+FROM ubuntu:18.04
+LABEL maintainer="dinghao188" \
+      version="1.1" \
+      description="ubuntu 18.04 with tools for tsinghua's rCore-Tutorial-V3"
+
+#install some deps
+RUN set -x \
+    && apt-get update \
+    && apt-get install -y curl wget autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev \
+              gawk build-essential bison flex texinfo gperf libtool patchutils bc xz-utils \
+              zlib1g-dev libexpat-dev pkg-config  libglib2.0-dev libpixman-1-dev git tmux python3 
+
+#install rust and qemu
+RUN set -x; \
+    RUSTUP='/root/rustup.sh' \
+    && cd $HOME \
+    #install rust
+    && curl https://sh.rustup.rs -sSf > $RUSTUP && chmod +x $RUSTUP \
+    && $RUSTUP -y --default-toolchain nightly --profile minimal \
+
+    #compile qemu
+    && wget https://ftp.osuosl.org/pub/blfs/conglomeration/qemu/qemu-5.0.0.tar.xz \
+    && tar xvJf qemu-5.0.0.tar.xz \
+    && cd qemu-5.0.0 \
+    && ./configure --target-list=riscv64-softmmu,riscv64-linux-user \
+    && make -j$(nproc) install \
+    && cd $HOME && rm -rf qemu-5.0.0 qemu-5.0.0.tar.xz
+
+#for chinese network
+RUN set -x; \
+    APT_CONF='/etc/apt/sources.list'; \
+    CARGO_CONF='/root/.cargo/config'; \
+    BASHRC='/root/.bashrc' \
+    && echo 'export RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static' >> $BASHRC \
+    && echo 'export RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup' >> $BASHRC \
+    && touch $CARGO_CONF \
+    && echo '[source.crates-io]' > $CARGO_CONF \
+    && echo "replace-with = 'ustc'" >> $CARGO_CONF \
+    && echo '[source.ustc]' >> $CARGO_CONF \
+    && echo 'registry = "git://mirrors.ustc.edu.cn/crates.io-index"' >> $CARGO_CONF
\ No newline at end of file
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..2e339762
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,8 @@
+DOCKER_NAME ?= dinghao188/rcore-tutorial
+.PHONY: docker build_docker
+
+docker:
+	docker run --rm -it --mount type=bind,source=$(shell pwd),destination=/mnt ${DOCKER_NAME}
+
+build_docker: 
+	docker build -t ${DOCKER_NAME} .
diff --git a/README.md b/README.md
index 50834482..dd356b5f 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,2 @@
 # rCore-Tutorial-v3
-rCore-Tutorial version 3.
+rCore-Tutorial version 3.
\ No newline at end of file

From 67372ac84d6618c369f402c4c269728102bfc3ae Mon Sep 17 00:00:00 2001
From: Yifan Wu <shinbokuow@163.com>
Date: Sun, 28 Feb 2021 06:38:13 +0800
Subject: [PATCH 5/5] Merge updates from ch7

---
 easy-fs-fuse/src/main.rs      |   6 +-
 easy-fs/src/efs.rs            |  12 +-
 easy-fs/src/layout.rs         | 238 ++++++++++++++++++++++++++++++----
 easy-fs/src/vfs.rs            |   9 +-
 os/Makefile                   |   2 +-
 os/src/mm/address.rs          |   5 +
 os/src/mm/mod.rs              |   1 +
 os/src/mm/page_table.rs       |  11 +-
 os/src/syscall/fs.rs          |  15 +++
 os/src/syscall/mod.rs         |   4 +-
 os/src/syscall/process.rs     |  20 ++-
 os/src/task/task.rs           |  41 +++++-
 os/src/trap/context.rs        |   1 +
 os/src/trap/mod.rs            |   1 +
 user/src/bin/cat.rs           |  34 +++++
 user/src/bin/cmdline_args.rs  |  16 +++
 user/src/bin/initproc.rs      |   2 +-
 user/src/bin/run_pipe_test.rs |   2 +-
 user/src/bin/user_shell.rs    |  82 +++++++++++-
 user/src/bin/usertests.rs     |   2 +-
 user/src/lib.rs               |  26 +++-
 user/src/syscall.rs           |   9 +-
 22 files changed, 477 insertions(+), 62 deletions(-)
 create mode 100644 user/src/bin/cat.rs
 create mode 100644 user/src/bin/cmdline_args.rs

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<dyn BlockDevice>) -> 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<u32>,
         block_device: &Arc<dyn BlockDevice>,
     ) {
-        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<dyn BlockDevice>) -> Vec<u32> {
         let mut v: Vec<u32> = 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<dyn BlockDevice>) -> Vec<u32> {
+        let mut v: Vec<u32> = 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<T>(&self) -> &'static T {
+        unsafe {
+            (self.0 as *const T).as_ref().unwrap()
+        }
+    }
     pub fn get_mut<T>(&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<T>(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<T>(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<String> = 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<String>) {
         // 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::<usize>();
+        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::<usize>()) 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<TaskControlBlock>) -> Arc<TaskControlBlock> {
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<String> = 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::<usize>()) 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 {