feat(ext4): mkdir and nested directory support
This commit is contained in:
@@ -179,6 +179,25 @@ pub fn remove_entry<B: BlockDevice>(
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn init_dir_block(buf: &mut [u8], self_inode: u32, parent_inode: u32, block_size: usize) {
|
||||||
|
for b in buf.iter_mut() { *b = 0; }
|
||||||
|
let mut pos = 0;
|
||||||
|
// "." entry
|
||||||
|
write_u32(buf, pos, self_inode);
|
||||||
|
write_u16(buf, pos + 4, 12);
|
||||||
|
buf[pos + 6] = 1;
|
||||||
|
buf[pos + 7] = 2;
|
||||||
|
buf[pos + 8] = b'.';
|
||||||
|
pos += 12;
|
||||||
|
// ".." entry
|
||||||
|
write_u32(buf, pos, parent_inode);
|
||||||
|
write_u16(buf, pos + 4, (block_size - 12) as u16);
|
||||||
|
buf[pos + 6] = 2;
|
||||||
|
buf[pos + 7] = 2;
|
||||||
|
buf[pos + 8] = b'.';
|
||||||
|
buf[pos + 9] = b'.';
|
||||||
|
}
|
||||||
|
|
||||||
pub fn list_entries<B: BlockDevice>(
|
pub fn list_entries<B: BlockDevice>(
|
||||||
device: &mut B,
|
device: &mut B,
|
||||||
sb: &Superblock,
|
sb: &Superblock,
|
||||||
|
|||||||
@@ -586,5 +586,60 @@ impl<B: BlockDevice> FileSystem for Ext4<B> {
|
|||||||
self.open_files[handle.raw() as usize].as_ref().map(|f| f.size)
|
self.open_files[handle.raw() as usize].as_ref().map(|f| f.size)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mkdir(&mut self, _path: &[u8]) -> bool { false }
|
fn mkdir(&mut self, path: &[u8]) -> bool {
|
||||||
|
let (parent_inode_num, dirname) = match self.resolve_parent(path) {
|
||||||
|
Some(v) => v,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
let parent_inode = match self.read_inode(parent_inode_num) {
|
||||||
|
Some(i) => i,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
if !parent_inode.is_dir() { return false; }
|
||||||
|
|
||||||
|
if dir::find_entry(&mut self.device, &self.sb, self.gdt_block, &parent_inode, dirname).is_some() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_inode_num = match self.alloc_inode() {
|
||||||
|
Some(n) => n,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
let new_block = match self.alloc_block() {
|
||||||
|
Some(b) => b,
|
||||||
|
None => { self.free_inode(new_inode_num); return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut new_inode = Self::init_empty_inode(0x4000 | 0o755);
|
||||||
|
new_inode.links_count = 2;
|
||||||
|
new_inode.size = self.sb.block_size as u64;
|
||||||
|
new_inode.blocks_count = (self.sb.block_size / 512) as u32;
|
||||||
|
if !Self::append_extent(&mut new_inode.block_data, 0, new_block as u64, 1) {
|
||||||
|
self.free_block(new_block);
|
||||||
|
self.free_inode(new_inode_num);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self.write_inode(new_inode_num, &new_inode);
|
||||||
|
|
||||||
|
let mut dir_block = [0u8; 4096];
|
||||||
|
dir::init_dir_block(&mut dir_block, new_inode_num, parent_inode_num, self.sb.block_size);
|
||||||
|
io::write_block(&mut self.device, new_block as u64, self.sb.sectors_per_block(), &dir_block);
|
||||||
|
|
||||||
|
let parent_inode = match self.read_inode(parent_inode_num) {
|
||||||
|
Some(i) => i,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
if !dir::add_entry(&mut self.device, &self.sb, self.gdt_block, &parent_inode, dirname, new_inode_num, 2) {
|
||||||
|
self.free_block(new_block);
|
||||||
|
self.free_inode(new_inode_num);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(mut pi) = self.read_inode(parent_inode_num) {
|
||||||
|
pi.links_count += 1;
|
||||||
|
self.write_inode(parent_inode_num, &pi);
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -413,6 +413,48 @@ fn create_with_missing_parent_fails() {
|
|||||||
assert!(fs.create(b"nodir/file.txt").is_none());
|
assert!(fs.create(b"nodir/file.txt").is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mkdir_creates_directory() {
|
||||||
|
let mut fs = Ext4::mount(format_ext4_disk()).unwrap();
|
||||||
|
assert!(fs.mkdir(b"subdir"));
|
||||||
|
let mut entries = [DirEntry::empty(); 8];
|
||||||
|
let count = fs.list_dir(b"/", &mut entries).unwrap();
|
||||||
|
assert!(entries[..count].iter().any(|e| e.name.as_bytes() == b"subdir" && e.is_dir));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn create_file_in_subdirectory() {
|
||||||
|
let mut fs = Ext4::mount(format_ext4_disk()).unwrap();
|
||||||
|
assert!(fs.mkdir(b"docs"));
|
||||||
|
let h = fs.create(b"docs/readme").unwrap();
|
||||||
|
fs.write(h, b"content").unwrap();
|
||||||
|
fs.close(h);
|
||||||
|
let h = fs.open(b"docs/readme").unwrap();
|
||||||
|
let mut buf = [0u8; 32];
|
||||||
|
let n = fs.read(h, &mut buf).unwrap();
|
||||||
|
assert_eq!(&buf[..n], b"content");
|
||||||
|
fs.close(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_subdirectory() {
|
||||||
|
let mut fs = Ext4::mount(format_ext4_disk()).unwrap();
|
||||||
|
assert!(fs.mkdir(b"mydir"));
|
||||||
|
let h1 = fs.create(b"mydir/a.txt").unwrap();
|
||||||
|
fs.close(h1);
|
||||||
|
let h2 = fs.create(b"mydir/b.txt").unwrap();
|
||||||
|
fs.close(h2);
|
||||||
|
let mut entries = [DirEntry::empty(); 8];
|
||||||
|
let count = fs.list_dir(b"mydir", &mut entries).unwrap();
|
||||||
|
assert_eq!(count, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mkdir_nonexistent_parent_fails() {
|
||||||
|
let mut fs = Ext4::mount(format_ext4_disk()).unwrap();
|
||||||
|
assert!(!fs.mkdir(b"noparent/child"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn read_past_eof_returns_zero() {
|
fn read_past_eof_returns_zero() {
|
||||||
let mut fs = Ext4::mount(format_ext4_disk()).unwrap();
|
let mut fs = Ext4::mount(format_ext4_disk()).unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user