Skip to content

Commit bc77404

Browse files
committed
make sure that entry names are unique when adding them to a directory
When adding entries to a directory, we now make sure that their name is unique within that directory.
1 parent 5218541 commit bc77404

File tree

6 files changed

+80
-9
lines changed

6 files changed

+80
-9
lines changed

ext2/src/dir.rs

+20-4
Original file line numberDiff line numberDiff line change
@@ -49,17 +49,28 @@ where
4949
Ok(entries)
5050
}
5151

52-
pub fn lookup_dir_entry<P>(
52+
pub fn find_entry<P>(
5353
&self,
5454
dir: &Directory,
5555
p: P,
56-
) -> Result<Option<(InodeAddress, Inode)>, Error>
56+
) -> Result<Option<DirEntry>, Error>
5757
where
5858
P: FnMut(&DirEntry) -> bool,
5959
{
60-
self.list_dir(dir)?
60+
Ok(self.list_dir(dir)?
6161
.into_iter()
62-
.find(p)
62+
.find(p))
63+
}
64+
65+
pub fn find_and_resolve_entry<P>(
66+
&self,
67+
dir: &Directory,
68+
p: P,
69+
) -> Result<Option<(InodeAddress, Inode)>, Error>
70+
where
71+
P: FnMut(&DirEntry) -> bool,
72+
{
73+
self.find_entry(dir, p)?
6374
.map(|e| self.resolve_dir_entry(e))
6475
.transpose()
6576
}
@@ -75,6 +86,11 @@ where
7586
inode_address: InodeAddress,
7687
typ: DirType,
7788
) -> Result<(), Error> {
89+
// TODO: make this more efficient once we have indexed lookups implemented
90+
if self.find_entry(dir, |e| e.name() == Some(name))?.is_some() {
91+
return Err(Error::EntryExists);
92+
}
93+
7894
let block_size = self.superblock.block_size() as usize;
7995
let dir_entries_have_type = self
8096
.superblock

ext2/src/error.rs

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub enum Error {
1414
InvalidInodeAddress(u32),
1515
NoSpace,
1616
NotSupported,
17+
EntryExists,
1718
}
1819

1920
impl Display for Error {

ext2/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ mod write;
2828

2929
const ROOT_DIR_INODE_ADDRESS: InodeAddress = InodeAddress::new(2).unwrap();
3030

31+
/// An ext2 filesystem over a block device.
3132
pub struct Ext2Fs<T> {
3233
block_device: T,
3334
superblock: Superblock,

ext2/tests/common.rs

+11
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,17 @@ use std::path::{Path, PathBuf};
55
use rand::distributions::Alphanumeric;
66
use rand::Rng;
77

8+
#[macro_export]
9+
macro_rules! cow_fs {
10+
($path:literal, $device_sector_size:expr) => {
11+
{
12+
let image_data = common::load_copy_of_image($path);
13+
let device = MemoryBlockDevice::try_new($device_sector_size, image_data).unwrap();
14+
Ext2Fs::try_new(device).unwrap()
15+
}
16+
};
17+
}
18+
819
#[macro_export]
920
macro_rules! generate_tests {
1021
($test_fn:ident : $($size:literal - $name:ident),*,) => {

ext2/tests/read.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ fn do_test_read_file(sector_size: usize) {
109109
let root = fs.read_root_inode().unwrap().try_into().unwrap();
110110

111111
let hello_txt: RegularFile = fs
112-
.lookup_dir_entry(&root, |e| e.name().is_some_and(|n| n == "hello.txt"))
112+
.find_and_resolve_entry(&root, |e| e.name().is_some_and(|n| n == "hello.txt"))
113113
.expect("lookup_dir_entry failed")
114114
.expect("hello.txt not found")
115115
.try_into()

ext2/tests/write.rs

+46-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use ext2::Ext2Fs;
1+
use ext2::{Error, Ext2Fs};
22
use filesystem::MemoryBlockDevice;
33

44
mod common;
@@ -13,9 +13,7 @@ generate_tests!(
1313
);
1414

1515
fn test_create_and_write_file(sector_size: usize) {
16-
let image_data = common::load_copy_of_image("tests/filesystems/empty.img");
17-
let device = MemoryBlockDevice::try_new(sector_size, image_data).unwrap();
18-
let mut fs = Ext2Fs::try_new(device).unwrap();
16+
let mut fs = cow_fs!("tests/filesystems/empty.img", sector_size);
1917

2018
let file_name = "my_file.txt";
2119
let mut root = fs.read_root_inode().unwrap();
@@ -28,4 +26,48 @@ fn test_create_and_write_file(sector_size: usize) {
2826
for i in 0..((1024 * 12) / data.len()) {
2927
assert_eq!(fs.write_to_file(&mut file, i * data.len(), data).unwrap(), data.len());
3028
}
29+
}
30+
31+
generate_tests!(
32+
test_create_files:
33+
512 - test_create_files_standard,
34+
1 - test_create_files_tiny,
35+
32 - test_create_files_small,
36+
32768 - test_create_files_large,
37+
1048576 - test_create_files_huge,
38+
);
39+
40+
fn test_create_files(sector_size: usize) {
41+
let mut fs = cow_fs!("tests/filesystems/empty.img", sector_size);
42+
43+
let mut root = fs.read_root_inode().unwrap();
44+
for i in 0..25 {
45+
let file_name = format!("file_{}.txt", i);
46+
let file = fs.create_regular_file(&mut root, &file_name).unwrap();
47+
assert!(fs.list_dir(&root).unwrap().iter().find(|e| e.name() == Some(&file_name)).is_some());
48+
assert_eq!(file.len(), 0);
49+
}
50+
}
51+
52+
generate_tests!(
53+
test_create_file_collision:
54+
512 - test_create_file_collision_standard,
55+
1 - test_create_file_collision_tiny,
56+
32 - test_create_file_collision_small,
57+
32768 - test_create_file_collision_large,
58+
1048576 - test_create_file_collision_huge,
59+
);
60+
61+
fn test_create_file_collision(sector_size: usize) {
62+
let mut fs = cow_fs!("tests/filesystems/empty.img", sector_size);
63+
64+
let mut root = fs.read_root_inode().unwrap();
65+
let file_name = "file.txt";
66+
let file = fs.create_regular_file(&mut root, file_name).unwrap();
67+
assert!(fs.list_dir(&root).unwrap().iter().find(|e| e.name() == Some(file_name)).is_some());
68+
assert_eq!(file.len(), 0);
69+
70+
let mut root = fs.read_root_inode().unwrap();
71+
let result = fs.create_regular_file(&mut root, file_name);
72+
assert_eq!(result.unwrap_err(), Error::EntryExists);
3173
}

0 commit comments

Comments
 (0)