Skip to content

Commit 7be2e81

Browse files
committed
ch6: enhance fs & syscalls
multi-level dir (including . and ..) getcwd, mkdirat, chdir, openat
1 parent 493ae3e commit 7be2e81

File tree

15 files changed

+667
-67
lines changed

15 files changed

+667
-67
lines changed

easy-fs-fuse/src/main.rs

Lines changed: 144 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#![feature(assert_matches)]
2-
use easy_fs::{BlockDevice, EasyFileSystem, BLOCK_SZ};
2+
use easy_fs::{BlockDevice, EasyFileSystem, Inode, BLOCK_SZ};
33
use structopt::StructOpt;
44

55
use std::{
@@ -158,3 +158,146 @@ fn efs_test() -> std::io::Result<()> {
158158

159159
Ok(())
160160
}
161+
162+
fn read_string(file: &Arc<Inode>) -> String {
163+
let mut read_buffer = [0u8; 512];
164+
let mut offset = 0usize;
165+
let mut read_str = String::new();
166+
loop {
167+
let len = file.read_at(offset, &mut read_buffer);
168+
if len == 0 {
169+
break;
170+
}
171+
offset += len;
172+
read_str.push_str(core::str::from_utf8(&read_buffer[..len]).unwrap());
173+
}
174+
read_str
175+
}
176+
177+
fn tree(inode: &Arc<Inode>, name: &str, depth: usize) {
178+
if depth > 1 {
179+
for _ in 0..depth - 1 {
180+
print!(" ");
181+
}
182+
}
183+
if depth == 0 {
184+
println!("{}", name);
185+
} else {
186+
println!("+-- {}", name);
187+
}
188+
for f in inode.ls() {
189+
// skip "." and ".."
190+
if matches!(f.as_str(), "." | "..") {
191+
continue;
192+
}
193+
let child = inode
194+
.find(&f)
195+
.expect(&format!("{f} in `ls {name}`(d={depth}) but cannot find"));
196+
tree(&child, &f, depth + 1);
197+
}
198+
}
199+
200+
#[test]
201+
fn efs_dir_test() -> std::io::Result<()> {
202+
let block_file = Arc::new(BlockFile(Mutex::new({
203+
let f = OpenOptions::new()
204+
.read(true)
205+
.write(true)
206+
.create(true)
207+
.open("target/fs.img")?;
208+
f.set_len(8192 * 512).unwrap();
209+
f
210+
})));
211+
EasyFileSystem::create(block_file.clone(), 4096, 1);
212+
let efs = EasyFileSystem::open(block_file.clone());
213+
let root = Arc::new(EasyFileSystem::root_inode(&efs));
214+
root.create("f1");
215+
root.create("f2");
216+
217+
let d1 = root.create_dir("d1").unwrap();
218+
219+
let f3 = d1.create("f3").unwrap();
220+
let d2 = d1.create_dir("d2").unwrap();
221+
222+
let f4 = d2.create("f4").unwrap();
223+
tree(&root, "/", 0);
224+
225+
let f3_content = "3333333";
226+
let f4_content = "4444444444444444444";
227+
f3.write_at(0, f3_content.as_bytes());
228+
f4.write_at(0, f4_content.as_bytes());
229+
230+
assert_eq!(read_string(&d1.find("f3").unwrap()), f3_content);
231+
assert_eq!(read_string(&root.find("/d1/f3").unwrap()), f3_content);
232+
assert_eq!(read_string(&d2.find("f4").unwrap()), f4_content);
233+
assert_eq!(read_string(&d1.find("d2/f4").unwrap()), f4_content);
234+
assert_eq!(read_string(&root.find("/d1/d2/f4").unwrap()), f4_content);
235+
assert!(f3.find("whatever").is_none());
236+
Ok(())
237+
}
238+
239+
#[test]
240+
fn efs_dir_dot_test() -> std::io::Result<()> {
241+
let block_file = Arc::new(BlockFile(Mutex::new({
242+
let f = OpenOptions::new()
243+
.read(true)
244+
.write(true)
245+
.create(true)
246+
.open("target/fs.img")?;
247+
f.set_len(8192 * 512).unwrap();
248+
f
249+
})));
250+
EasyFileSystem::create(block_file.clone(), 4096, 1);
251+
let efs = EasyFileSystem::open(block_file.clone());
252+
let root = Arc::new(EasyFileSystem::root_inode(&efs));
253+
254+
root.create("file0");
255+
root.create("file1");
256+
println!("ls /\n{:?}", root.ls());
257+
258+
if let Some(root_dot) = root.find(".") {
259+
println!("ls /.\n{:?}", root_dot.ls());
260+
}
261+
if let Some(root_ddot) = root.find("..") {
262+
println!("ls /..\n{:?}", root_ddot.ls());
263+
if let Some(ddot_of_root_ddot) = root_ddot.find("..") {
264+
println!("ls /../..\n{:?}", ddot_of_root_ddot.ls());
265+
}
266+
}
267+
if let Some(d) = root.find("./.././..") {
268+
println!("ls*(at /) ./.././..\n{:?}", d.ls());
269+
}
270+
271+
println!("\ncreate dir0");
272+
let dir0 = root.create_dir("dir0").unwrap();
273+
dir0.create("dir0_file0");
274+
println!("ls dir0\n{:?}", dir0.ls());
275+
if let Some(dot) = dir0.find(".") {
276+
println!("ls dir0/.\n{:?}", dot.ls());
277+
}
278+
if let Some(ddot) = dir0.find("..") {
279+
println!("ls dir0/..\n{:?}", ddot.ls());
280+
if let Some(ddot_of_ddot) = ddot.find("..") {
281+
println!("ls dir0/../..\n{:?}", ddot_of_ddot.ls());
282+
}
283+
}
284+
if let Some(d) = dir0.find("./.././..") {
285+
println!("ls*(at dir0) ./.././..\n{:?}", d.ls()); // eqv. ls /
286+
}
287+
Ok(())
288+
}
289+
290+
#[test]
291+
fn check_os_image() -> std::io::Result<()> {
292+
let block_file = Arc::new(BlockFile(Mutex::new({
293+
let f = OpenOptions::new()
294+
.read(true)
295+
.write(true)
296+
.open("target/os.img")?;
297+
f
298+
})));
299+
let efs = EasyFileSystem::open(block_file);
300+
let root = Arc::new(EasyFileSystem::root_inode(&efs));
301+
tree(&root, "/", 0);
302+
Ok(())
303+
}

easy-fs/src/efs.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ impl EasyFileSystem {
8888
get_block_cache(root_inode_block_id as usize, Arc::clone(&block_device))
8989
.lock()
9090
.modify(root_inode_offset, |root_inode: &mut DiskInode| {
91-
root_inode.initialize(DiskInodeType::Directory)
91+
root_inode.initialize(DiskInodeType::Directory);
92+
root_inode.initialize_dir(0, 0, || efs.alloc_data(), &block_device);
9293
});
9394

9495
Arc::new(Mutex::new(efs))

easy-fs/src/layout.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ pub struct DiskInode {
8383
type_: DiskInodeType,
8484
}
8585

86-
#[derive(PartialEq)]
86+
#[derive(Clone, Copy, Debug, PartialEq)]
8787
pub enum DiskInodeType {
8888
File,
8989
Directory,
@@ -98,6 +98,36 @@ impl DiskInode {
9898
self.type_ = type_;
9999
}
100100

101+
pub fn initialize_dir<F: FnMut() -> u32>(
102+
&mut self,
103+
self_inode: u32,
104+
parent_inode: u32,
105+
mut data_alloc: F,
106+
block_device: &Arc<dyn BlockDevice>,
107+
) {
108+
assert_eq!(self.type_, DiskInodeType::Directory);
109+
110+
// increase size
111+
let file_count = (self.size as usize) / DIRENT_SZ; // should be 0 when create
112+
let new_size = ((file_count + 2) * DIRENT_SZ) as u32; // "." and ".."
113+
let blocks_needed = self.blocks_num_needed(new_size);
114+
let mut new_blocks = Vec::new();
115+
for _ in 0..blocks_needed {
116+
new_blocks.push(data_alloc());
117+
}
118+
self.increase_size(new_size, new_blocks, block_device);
119+
// write both dir entry points to self
120+
let buf = {
121+
let mut b = [0u8; 2 * DIRENT_SZ];
122+
let dir = DirEntry::new(".", self_inode);
123+
b[..DIRENT_SZ].copy_from_slice(dir.as_bytes());
124+
let dir = DirEntry::new("..", parent_inode);
125+
b[DIRENT_SZ..].copy_from_slice(dir.as_bytes());
126+
b
127+
};
128+
self.write_at(file_count * DIRENT_SZ, &buf[..], &block_device);
129+
}
130+
101131
pub fn is_dir(&self) -> bool {
102132
self.type_ == DiskInodeType::Directory
103133
}

easy-fs/src/vfs.rs

Lines changed: 102 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -70,28 +70,75 @@ impl Inode {
7070
None
7171
}
7272

73-
/// Find inode under current inode by name (1 level only)
74-
pub fn find(&self, name: &str) -> Option<Arc<Inode>> {
75-
let fs = self.fs.lock();
73+
/// Find direntry under a disk inode by pred
74+
fn get_dirent(
75+
&self,
76+
disk_inode: &DiskInode,
77+
pred: impl Fn(&DirEntry) -> bool,
78+
) -> Option<DirEntry> {
79+
assert!(disk_inode.is_dir());
80+
let file_count = (disk_inode.size as usize) / DIRENT_SZ;
81+
let mut dirent = DirEntry::new_empty();
82+
for i in 0..file_count {
83+
assert_eq!(
84+
disk_inode.read_at(i * DIRENT_SZ, dirent.as_bytes_mut(), &self.block_device),
85+
DIRENT_SZ
86+
);
87+
if pred(&dirent) {
88+
return Some(dirent);
89+
}
90+
}
91+
None
92+
}
93+
94+
/// Read child direnty by inode_id
95+
pub fn read_dirent<V>(&self, inode_id: u32, f: impl FnOnce(DirEntry) -> V) -> Option<V> {
7696
self.read_disk_inode(|disk_inode| {
77-
self.find_inode_id(name, disk_inode).map(|inode_id| {
78-
let (block_id, block_offset) = fs.get_disk_inode_pos(inode_id);
79-
Arc::new(Inode::new(
80-
inode_id,
81-
block_id,
82-
block_offset,
83-
self.fs.clone(),
84-
self.block_device.clone(),
85-
))
86-
})
97+
self.get_dirent(disk_inode, |d| d.inode_number() == inode_id)
8798
})
99+
.map(f)
100+
}
101+
102+
/// Find inode under current inode(recursively) by name
103+
pub fn find(&self, path: &str) -> Option<Arc<Inode>> {
104+
let fs = self.fs.lock();
105+
let mut inode_id = self.inode_id;
106+
let mut block_id = self.block_id as u32;
107+
let mut block_offset = self.block_offset;
108+
109+
for name in path.split('/').filter(|s| !s.is_empty()) {
110+
let opt_inode_id = get_block_cache(block_id as usize, self.block_device.clone())
111+
.lock()
112+
.read(block_offset, |disk_inode: &DiskInode| {
113+
if disk_inode.is_file() {
114+
return None;
115+
}
116+
self.find_inode_id(name, disk_inode)
117+
});
118+
match opt_inode_id {
119+
Some(v) => {
120+
inode_id = v;
121+
(block_id, block_offset) = fs.get_disk_inode_pos(v);
122+
}
123+
_ => return None,
124+
}
125+
}
126+
Some(Arc::new(Self::new(
127+
inode_id,
128+
block_id,
129+
block_offset,
130+
self.fs.clone(),
131+
self.block_device.clone(),
132+
)))
88133
}
89134

90135
/// List inodes under current inode
91136
pub fn ls(&self) -> Vec<String> {
92137
let _fs = self.fs.lock();
93138
self.read_disk_inode(|disk_inode| {
94-
assert!(disk_inode.is_dir());
139+
if disk_inode.is_file() {
140+
return Vec::new();
141+
}
95142
let file_count = (disk_inode.size as usize) / DIRENT_SZ;
96143
let mut v = Vec::with_capacity(file_count);
97144
let mut dirent = DirEntry::new_empty();
@@ -126,7 +173,7 @@ impl Inode {
126173
}
127174

128175
/// Create inode under current inode by name
129-
pub fn create(&self, name: &str) -> Option<Arc<Inode>> {
176+
fn create_inode(&self, name: &str, inode_type: DiskInodeType) -> Option<Arc<Inode>> {
130177
let mut fs = self.fs.lock();
131178
let op = |root_inode: &DiskInode| {
132179
assert!(root_inode.is_dir());
@@ -145,9 +192,9 @@ impl Inode {
145192
get_block_cache(new_inode_block_id as usize, self.block_device.clone())
146193
.lock()
147194
.modify(new_inode_block_offset, |new_inode: &mut DiskInode| {
148-
new_inode.initialize(DiskInodeType::File)
195+
new_inode.initialize(inode_type)
149196
});
150-
// 3. modify current inode(root inode): add one more dirent
197+
// 3. modify current inode: add one more dirent
151198
self.modify_disk_inode(|root_inode| {
152199
// append file in dirent
153200
let file_count = (root_inode.size as usize) / DIRENT_SZ;
@@ -162,22 +209,45 @@ impl Inode {
162209
&self.block_device,
163210
);
164211
});
165-
// 4. return inode
166-
block_cache_sync_all();
167-
Some(Arc::new(Self::new(
212+
let inode = Self::new(
168213
new_inode_id,
169214
new_inode_block_id,
170215
new_inode_block_offset,
171216
self.fs.clone(),
172217
self.block_device.clone(),
173-
)))
218+
);
219+
if inode_type == DiskInodeType::Directory {
220+
let curr_inode_id = self.inode_id;
221+
inode.modify_disk_inode(|curr_inode| {
222+
curr_inode.initialize_dir(
223+
new_inode_id,
224+
curr_inode_id,
225+
|| fs.alloc_data(),
226+
&self.block_device,
227+
);
228+
});
229+
}
230+
// 4. return inode
231+
block_cache_sync_all();
232+
Some(Arc::new(inode))
174233
// release efs lock
175234
}
176235

236+
/// Create regular file under current inode
237+
pub fn create(&self, name: &str) -> Option<Arc<Inode>> {
238+
self.create_inode(name, DiskInodeType::File)
239+
}
240+
241+
/// Create directory under current inode
242+
pub fn create_dir(&self, name: &str) -> Option<Arc<Inode>> {
243+
self.create_inode(name, DiskInodeType::Directory)
244+
}
245+
177246
/// Clear the data in current inode
178247
pub fn clear(&self) {
179248
let mut fs = self.fs.lock();
180249
self.modify_disk_inode(|disk_inode| {
250+
assert!(disk_inode.is_file());
181251
let size = disk_inode.size;
182252
let data_blocks_dealloc = disk_inode.clear_size(&self.block_device);
183253
assert_eq!(
@@ -200,6 +270,7 @@ impl Inode {
200270
pub fn write_at(&self, offset: usize, buf: &[u8]) -> usize {
201271
let mut fs = self.fs.lock();
202272
self.modify_disk_inode(|disk_inode| {
273+
assert!(disk_inode.is_file());
203274
// extend first
204275
self.increase_size((offset + buf.len()) as u32, disk_inode, &mut fs);
205276
disk_inode.write_at(offset, buf, &self.block_device)
@@ -215,4 +286,14 @@ impl Inode {
215286
pub fn get_size(&self) -> usize {
216287
self.read_disk_inode(|disk_inode| disk_inode.size as usize)
217288
}
289+
290+
/// Is dir?
291+
pub fn is_dir(&self) -> bool {
292+
self.read_disk_inode(|disk_inode| disk_inode.is_dir())
293+
}
294+
295+
/// Is file?
296+
pub fn is_file(&self) -> bool {
297+
self.read_disk_inode(|disk_inode| disk_inode.is_file())
298+
}
218299
}

0 commit comments

Comments
 (0)