diff --git a/crates/servers/storage-server/src/fs/ext4/dir.rs b/crates/servers/storage-server/src/fs/ext4/dir.rs index c8f7ca0..254f08c 100644 --- a/crates/servers/storage-server/src/fs/ext4/dir.rs +++ b/crates/servers/storage-server/src/fs/ext4/dir.rs @@ -179,6 +179,25 @@ pub fn remove_entry( 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( device: &mut B, sb: &Superblock, diff --git a/crates/servers/storage-server/src/fs/ext4/mod.rs b/crates/servers/storage-server/src/fs/ext4/mod.rs index 5c28a8b..338eeb3 100644 --- a/crates/servers/storage-server/src/fs/ext4/mod.rs +++ b/crates/servers/storage-server/src/fs/ext4/mod.rs @@ -586,5 +586,60 @@ impl FileSystem for Ext4 { 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 + } } diff --git a/crates/servers/storage-server/src/tests/ext4.rs b/crates/servers/storage-server/src/tests/ext4.rs index 2469be5..564a2aa 100644 --- a/crates/servers/storage-server/src/tests/ext4.rs +++ b/crates/servers/storage-server/src/tests/ext4.rs @@ -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();