feat(ext4): mkdir and nested directory support
This commit is contained in:
@@ -179,6 +179,25 @@ pub fn remove_entry<B: BlockDevice>(
|
||||
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>(
|
||||
device: &mut B,
|
||||
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)
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
#[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]
|
||||
fn read_past_eof_returns_zero() {
|
||||
let mut fs = Ext4::mount(format_ext4_disk()).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user