Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slightly refactor FatVolume::iterate_dir #127

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 108 additions & 83 deletions src/fat/volume.rs
Original file line number Diff line number Diff line change
Expand Up @@ -435,105 +435,130 @@ impl FatVolume {
&self,
block_device: &D,
dir: &DirectoryInfo,
mut func: F,
func: F,
) -> Result<(), Error<D::Error>>
where
F: FnMut(&DirEntry),
D: BlockDevice,
{
match &self.fat_specific_info {
FatSpecificInfo::Fat16(fat16_info) => {
// Root directories on FAT16 have a fixed size, because they use
// a specially reserved space on disk (see
// `first_root_dir_block`). Other directories can have any size
// as they are made of regular clusters.
let mut current_cluster = Some(dir.cluster);
let mut first_dir_block_num = match dir.cluster {
ClusterId::ROOT_DIR => self.lba_start + fat16_info.first_root_dir_block,
_ => self.cluster_to_block(dir.cluster),
};
let dir_size = match dir.cluster {
ClusterId::ROOT_DIR => {
let len_bytes =
u32::from(fat16_info.root_entries_count) * OnDiskDirEntry::LEN_U32;
BlockCount::from_bytes(len_bytes)
}
_ => BlockCount(u32::from(self.blocks_per_cluster)),
};
self.iterate_fat16(dir, fat16_info, block_device, func)
}
FatSpecificInfo::Fat32(fat32_info) => {
self.iterate_fat32(dir, fat32_info, block_device, func)
}
}
}

let mut block_cache = BlockCache::empty();
while let Some(cluster) = current_cluster {
for block_idx in first_dir_block_num.range(dir_size) {
let block = block_cache.read(block_device, block_idx, "read_dir")?;
for entry in 0..Block::LEN / OnDiskDirEntry::LEN {
let start = entry * OnDiskDirEntry::LEN;
let end = (entry + 1) * OnDiskDirEntry::LEN;
let dir_entry = OnDiskDirEntry::new(&block[start..end]);
if dir_entry.is_end() {
// Can quit early
return Ok(());
} else if dir_entry.is_valid() && !dir_entry.is_lfn() {
// Safe, since Block::LEN always fits on a u32
let start = u32::try_from(start).unwrap();
let entry = dir_entry.get_entry(FatType::Fat16, block_idx, start);
func(&entry);
}
}
}
if cluster != ClusterId::ROOT_DIR {
current_cluster =
match self.next_cluster(block_device, cluster, &mut block_cache) {
Ok(n) => {
first_dir_block_num = self.cluster_to_block(n);
Some(n)
}
_ => None,
};
} else {
current_cluster = None;
fn iterate_fat16<D, F>(
&self,
dir: &DirectoryInfo,
fat16_info: &Fat16Info,
block_device: &D,
mut func: F,
) -> Result<(), Error<D::Error>>
where
F: FnMut(&DirEntry),
D: BlockDevice,
{
// Root directories on FAT16 have a fixed size, because they use
// a specially reserved space on disk (see
// `first_root_dir_block`). Other directories can have any size
// as they are made of regular clusters.
let mut current_cluster = Some(dir.cluster);
let mut first_dir_block_num = match dir.cluster {
ClusterId::ROOT_DIR => self.lba_start + fat16_info.first_root_dir_block,
_ => self.cluster_to_block(dir.cluster),
};
let dir_size = match dir.cluster {
ClusterId::ROOT_DIR => {
let len_bytes = u32::from(fat16_info.root_entries_count) * OnDiskDirEntry::LEN_U32;
BlockCount::from_bytes(len_bytes)
}
_ => BlockCount(u32::from(self.blocks_per_cluster)),
};

let mut block_cache = BlockCache::empty();
while let Some(cluster) = current_cluster {
for block_idx in first_dir_block_num.range(dir_size) {
let block = block_cache.read(block_device, block_idx, "read_dir")?;
for entry in 0..Block::LEN / OnDiskDirEntry::LEN {
let start = entry * OnDiskDirEntry::LEN;
let end = (entry + 1) * OnDiskDirEntry::LEN;
let dir_entry = OnDiskDirEntry::new(&block[start..end]);
if dir_entry.is_end() {
// Can quit early
return Ok(());
} else if dir_entry.is_valid() && !dir_entry.is_lfn() {
// Safe, since Block::LEN always fits on a u32
let start = u32::try_from(start).unwrap();
let entry = dir_entry.get_entry(FatType::Fat16, block_idx, start);
func(&entry);
}
}
Ok(())
}
FatSpecificInfo::Fat32(fat32_info) => {
// All directories on FAT32 have a cluster chain but the root
// dir starts in a specified cluster.
let mut current_cluster = match dir.cluster {
ClusterId::ROOT_DIR => Some(fat32_info.first_root_dir_cluster),
_ => Some(dir.cluster),
if cluster != ClusterId::ROOT_DIR {
current_cluster = match self.next_cluster(block_device, cluster, &mut block_cache) {
Ok(n) => {
first_dir_block_num = self.cluster_to_block(n);
Some(n)
}
_ => None,
};
let mut blocks = [Block::new()];
let mut block_cache = BlockCache::empty();
while let Some(cluster) = current_cluster {
let block_idx = self.cluster_to_block(cluster);
for block in block_idx.range(BlockCount(u32::from(self.blocks_per_cluster))) {
block_device
.read(&mut blocks, block, "read_dir")
.map_err(Error::DeviceError)?;
for entry in 0..Block::LEN / OnDiskDirEntry::LEN {
let start = entry * OnDiskDirEntry::LEN;
let end = (entry + 1) * OnDiskDirEntry::LEN;
let dir_entry = OnDiskDirEntry::new(&blocks[0][start..end]);
if dir_entry.is_end() {
// Can quit early
return Ok(());
} else if dir_entry.is_valid() && !dir_entry.is_lfn() {
// Safe, since Block::LEN always fits on a u32
let start = u32::try_from(start).unwrap();
let entry = dir_entry.get_entry(FatType::Fat32, block, start);
func(&entry);
}
}
} else {
current_cluster = None;
}
}
Ok(())
}

fn iterate_fat32<D, F>(
&self,
dir: &DirectoryInfo,
fat32_info: &Fat32Info,
block_device: &D,
mut func: F,
) -> Result<(), Error<D::Error>>
where
F: FnMut(&DirEntry),
D: BlockDevice,
{
// All directories on FAT32 have a cluster chain but the root
// dir starts in a specified cluster.
let mut current_cluster = match dir.cluster {
ClusterId::ROOT_DIR => Some(fat32_info.first_root_dir_cluster),
_ => Some(dir.cluster),
};
let mut blocks = [Block::new()];
let mut block_cache = BlockCache::empty();
while let Some(cluster) = current_cluster {
let block_idx = self.cluster_to_block(cluster);
for block in block_idx.range(BlockCount(u32::from(self.blocks_per_cluster))) {
block_device
.read(&mut blocks, block, "read_dir")
.map_err(Error::DeviceError)?;
for entry in 0..Block::LEN / OnDiskDirEntry::LEN {
let start = entry * OnDiskDirEntry::LEN;
let end = (entry + 1) * OnDiskDirEntry::LEN;
let dir_entry = OnDiskDirEntry::new(&blocks[0][start..end]);
if dir_entry.is_end() {
// Can quit early
return Ok(());
} else if dir_entry.is_valid() && !dir_entry.is_lfn() {
// Safe, since Block::LEN always fits on a u32
let start = u32::try_from(start).unwrap();
let entry = dir_entry.get_entry(FatType::Fat32, block, start);
func(&entry);
}
current_cluster =
match self.next_cluster(block_device, cluster, &mut block_cache) {
Ok(n) => Some(n),
_ => None,
};
}
Ok(())
}
current_cluster = match self.next_cluster(block_device, cluster, &mut block_cache) {
Ok(n) => Some(n),
_ => None,
};
}
Ok(())
}

/// Get an entry from the given directory
Expand Down
6 changes: 3 additions & 3 deletions src/filesystem/filename.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,19 +59,19 @@ impl ShortFileName {
}
}

/// Get a short file name containing "..", which means "this directory".
/// Get a short file name containing ".", which means "this directory".
pub const fn this_dir() -> Self {
Self {
contents: *b". ",
}
}

/// Get base name (name without extension) of file name
/// Get base name (without extension) of the file.
pub fn base_name(&self) -> &[u8] {
Self::bytes_before_space(&self.contents[..Self::FILENAME_BASE_MAX_LEN])
}

/// Get base name (name without extension) of file name
/// Get extension of the file (without base name).
pub fn extension(&self) -> &[u8] {
Self::bytes_before_space(&self.contents[Self::FILENAME_BASE_MAX_LEN..])
}
Expand Down