Skip to main content

tg_easy_fs/
vfs.rs

1use super::{
2    block_cache_sync_all, get_block_cache, BlockDevice, DirEntry, DiskInode, DiskInodeType,
3    EasyFileSystem, DIRENT_SZ,
4};
5use alloc::string::String;
6use alloc::sync::Arc;
7use alloc::vec::Vec;
8use spin::{Mutex, MutexGuard};
9/// Virtual filesystem layer over easy-fs
10pub struct Inode {
11    block_id: usize,
12    block_offset: usize,
13    fs: Arc<Mutex<EasyFileSystem>>,
14    block_device: Arc<dyn BlockDevice>,
15}
16
17impl Inode {
18    /// Create a vfs inode
19    pub fn new(
20        block_id: u32,
21        block_offset: usize,
22        fs: Arc<Mutex<EasyFileSystem>>,
23        block_device: Arc<dyn BlockDevice>,
24    ) -> Self {
25        Self {
26            block_id: block_id as usize,
27            block_offset,
28            fs,
29            block_device,
30        }
31    }
32
33    /// Call a function over a disk inode to read it
34    fn read_disk_inode<V>(&self, f: impl FnOnce(&DiskInode) -> V) -> V {
35        get_block_cache(self.block_id, Arc::clone(&self.block_device))
36            .lock()
37            .read(self.block_offset, f)
38    }
39
40    /// Call a function over a disk inode to modify it
41    fn modify_disk_inode<V>(&self, f: impl FnOnce(&mut DiskInode) -> V) -> V {
42        get_block_cache(self.block_id, Arc::clone(&self.block_device))
43            .lock()
44            .modify(self.block_offset, f)
45    }
46
47    /// Find inode under a disk inode by name
48    fn find_inode_id(&self, name: &str, disk_inode: &DiskInode) -> Option<u32> {
49        // assert it is a directory
50        assert!(disk_inode.is_dir());
51        let file_count = (disk_inode.size as usize) / DIRENT_SZ;
52        let mut dirent = DirEntry::empty();
53        for i in 0..file_count {
54            assert_eq!(
55                disk_inode.read_at(DIRENT_SZ * i, dirent.as_bytes_mut(), &self.block_device,),
56                DIRENT_SZ,
57            );
58            if dirent.name() == name {
59                return Some(dirent.inode_number());
60            }
61        }
62        None
63    }
64
65    /// Find inode under current inode by name
66    pub fn find(&self, name: &str) -> Option<Arc<Inode>> {
67        // 目录查找流程:目录 inode -> 遍历 dirent -> 定位子 inode 的磁盘位置。
68        let fs = self.fs.lock();
69        self.read_disk_inode(|disk_inode| {
70            self.find_inode_id(name, disk_inode).map(|inode_id| {
71                let (block_id, block_offset) = fs.get_disk_inode_pos(inode_id);
72                Arc::new(Self::new(
73                    block_id,
74                    block_offset,
75                    self.fs.clone(),
76                    self.block_device.clone(),
77                ))
78            })
79        })
80    }
81
82    /// Increase the size of a disk inode
83    fn increase_size(
84        &self,
85        new_size: u32,
86        disk_inode: &mut DiskInode,
87        fs: &mut MutexGuard<EasyFileSystem>,
88    ) {
89        if new_size < disk_inode.size {
90            return;
91        }
92        // 先按“新增块数”批量申请数据块,再一次性扩容 inode。
93        let blocks_needed = disk_inode.blocks_num_needed(new_size);
94        let mut v: Vec<u32> = Vec::new();
95        for _ in 0..blocks_needed {
96            v.push(fs.alloc_data());
97        }
98        disk_inode.increase_size(new_size, v, &self.block_device);
99    }
100
101    /// Create inode under current inode by name.
102    /// Attention: use find previously to ensure the new file not existing.
103    pub fn create(&self, name: &str) -> Option<Arc<Inode>> {
104        let mut fs = self.fs.lock();
105        // 1) 分配新 inode
106        let new_inode_id = fs.alloc_inode();
107        // 2) 初始化 inode 元数据
108        let (new_inode_block_id, new_inode_block_offset) = fs.get_disk_inode_pos(new_inode_id);
109        get_block_cache(new_inode_block_id as usize, Arc::clone(&self.block_device))
110            .lock()
111            .modify(new_inode_block_offset, |new_inode: &mut DiskInode| {
112                new_inode.initialize(DiskInodeType::File);
113            });
114        // 3) 在当前目录追加 dirent 项
115        self.modify_disk_inode(|root_inode| {
116            // append file in the dirent
117            let file_count = (root_inode.size as usize) / DIRENT_SZ;
118            let new_size = (file_count + 1) * DIRENT_SZ;
119            // increase size
120            self.increase_size(new_size as u32, root_inode, &mut fs);
121            // write dirent
122            let dirent = DirEntry::new(name, new_inode_id);
123            root_inode.write_at(
124                file_count * DIRENT_SZ,
125                dirent.as_bytes(),
126                &self.block_device,
127            );
128        });
129
130        let (block_id, block_offset) = fs.get_disk_inode_pos(new_inode_id);
131        block_cache_sync_all();
132        // 4) 返回新文件的 Inode 句柄
133        Some(Arc::new(Self::new(
134            block_id,
135            block_offset,
136            self.fs.clone(),
137            self.block_device.clone(),
138        )))
139        // release efs lock automatically by compiler
140    }
141
142    /// List inodes by id under current inode
143    pub fn readdir(&self) -> Vec<String> {
144        let _fs = self.fs.lock();
145        self.read_disk_inode(|disk_inode| {
146            let file_count = (disk_inode.size as usize) / DIRENT_SZ;
147            let mut v: Vec<String> = Vec::new();
148            for i in 0..file_count {
149                let mut dirent = DirEntry::empty();
150                assert_eq!(
151                    disk_inode.read_at(i * DIRENT_SZ, dirent.as_bytes_mut(), &self.block_device,),
152                    DIRENT_SZ,
153                );
154                v.push(String::from(dirent.name()));
155            }
156            v
157        })
158    }
159
160    /// Read data from current inode
161    pub fn read_at(&self, offset: usize, buf: &mut [u8]) -> usize {
162        let _fs = self.fs.lock();
163        self.read_disk_inode(|disk_inode| disk_inode.read_at(offset, buf, &self.block_device))
164    }
165
166    /// Write data to current inode
167    pub fn write_at(&self, offset: usize, buf: &[u8]) -> usize {
168        let mut fs = self.fs.lock();
169        let size = self.modify_disk_inode(|disk_inode| {
170            self.increase_size((offset + buf.len()) as u32, disk_inode, &mut fs);
171            disk_inode.write_at(offset, buf, &self.block_device)
172        });
173        block_cache_sync_all();
174        size
175    }
176
177    /// Clear the data in current inode
178    pub fn clear(&self) {
179        let mut fs = self.fs.lock();
180        self.modify_disk_inode(|disk_inode| {
181            let size = disk_inode.size;
182            let data_blocks_dealloc = disk_inode.clear_size(&self.block_device);
183            assert!(data_blocks_dealloc.len() == DiskInode::total_blocks(size) as usize);
184            for data_block in data_blocks_dealloc.into_iter() {
185                fs.dealloc_data(data_block);
186            }
187        });
188        block_cache_sync_all();
189    }
190}