From f454fb76eafd85a14bb1b6261695e66728defc89 Mon Sep 17 00:00:00 2001 From: BL-CZY Date: Sat, 8 Nov 2025 02:07:55 +0000 Subject: [PATCH 1/2] Refactor filesystem and volume manager to support async operations - Updated methods in `directory.rs`, `files.rs`, `lib.rs`, `mod.rs`, and `volume_mgr.rs` to be async. - Added `maybe_async` attributes to conditionally compile sync/async versions based on feature flags. - Modified method implementations to await asynchronous calls where necessary. - Ensured compatibility with both async and sync contexts throughout the filesystem and volume management code. --- Cargo.toml | 16 ++- src/blockdevice.rs | 67 +++++++--- src/fat/volume.rs | 242 +++++++++++++++++++++++------------- src/filesystem/directory.rs | 51 +++++--- src/filesystem/files.rs | 49 +++++--- src/lib.rs | 10 +- src/sdcard/mod.rs | 17 ++- src/volume_mgr.rs | 226 ++++++++++++++++++++++----------- 8 files changed, 466 insertions(+), 212 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6bb514b..1d09950 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,8 @@ [package] -authors = ["Jonathan 'theJPster' Pallant ", "Rust Embedded Community Developers"] +authors = [ + "Jonathan 'theJPster' Pallant ", + "Rust Embedded Community Developers", +] categories = ["embedded", "no-std"] description = "A basic SD/MMC driver for Embedded Rust." edition = "2021" @@ -14,12 +17,14 @@ version = "0.9.0" rust-version = "1.86" [dependencies] -byteorder = {version = "1", default-features = false} -defmt = {version = "1.0.1", optional = true} +byteorder = { version = "1", default-features = false } +defmt = { version = "1.0.1", optional = true } embedded-hal = "1.0.0" embedded-io = "0.6.1" +embedded-io-async = "0.6.1" heapless = "0.9.1" -log = {version = "0.4", default-features = false, optional = true} +log = { version = "0.4", default-features = false, optional = true } +maybe-async = "0.2.10" [dev-dependencies] chrono = "0.4" @@ -30,7 +35,8 @@ hex-literal = "1.0.0" sha2 = "0.10" [features] -default = ["log"] +default = ["log", "async"] defmt-log = ["dep:defmt"] log = ["dep:log"] +async = [] core-error = [] diff --git a/src/blockdevice.rs b/src/blockdevice.rs index 674ae55..4d5b6a5 100644 --- a/src/blockdevice.rs +++ b/src/blockdevice.rs @@ -75,6 +75,7 @@ impl Default for Block { /// A block device - a device which can read and write blocks (or /// sectors). Only supports devices which are <= 2 TiB in size. +#[cfg(not(feature = "async"))] pub trait BlockDevice { /// The errors that the `BlockDevice` can return. Must be debug formattable. type Error: core::fmt::Debug; @@ -86,6 +87,30 @@ pub trait BlockDevice { fn num_blocks(&self) -> Result; } +/// A block device - a device which can read and write blocks (or +/// sectors). Only supports devices which are <= 2 TiB in size. +#[cfg(feature = "async")] +pub trait BlockDevice { + /// The errors that the `BlockDevice` can return. Must be debug formattable. + type Error: core::fmt::Debug; + /// Read one or more blocks, starting at the given block index. + fn read( + &self, + blocks: &mut [Block], + start_block_idx: BlockIdx, + ) -> impl core::future::Future> + Send; + /// Write one or more blocks, starting at the given block index. + fn write( + &self, + blocks: &[Block], + start_block_idx: BlockIdx, + ) -> impl core::future::Future> + Send; + /// Determine how many blocks this device can hold. + fn num_blocks( + &self, + ) -> impl core::future::Future> + Send; +} + /// A caching layer for block devices /// /// Caches a single block. @@ -110,42 +135,54 @@ where } /// Read a block, and return a reference to it. - pub fn read(&mut self, block_idx: BlockIdx) -> Result<&Block, D::Error> { + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn read(&mut self, block_idx: BlockIdx) -> Result<&Block, D::Error> { if self.block_idx != Some(block_idx) { self.block_idx = None; - self.block_device.read(&mut self.block, block_idx)?; + self.block_device.read(&mut self.block, block_idx).await?; self.block_idx = Some(block_idx); } Ok(&self.block[0]) } /// Read a block, and return a reference to it. - pub fn read_mut(&mut self, block_idx: BlockIdx) -> Result<&mut Block, D::Error> { + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn read_mut(&mut self, block_idx: BlockIdx) -> Result<&mut Block, D::Error> { if self.block_idx != Some(block_idx) { self.block_idx = None; - self.block_device.read(&mut self.block, block_idx)?; + self.block_device.read(&mut self.block, block_idx).await?; self.block_idx = Some(block_idx); } Ok(&mut self.block[0]) } /// Write back a block you read with [`Self::read_mut`] and then modified. - pub fn write_back(&mut self) -> Result<(), D::Error> { - self.block_device.write( - &self.block, - self.block_idx.expect("write_back with no read"), - ) + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn write_back(&mut self) -> Result<(), D::Error> { + self.block_device + .write( + &self.block, + self.block_idx.expect("write_back with no read"), + ) + .await } /// Write back a block you read with [`Self::read_mut`] and then modified, but to two locations. /// /// This is useful for updating two File Allocation Tables. - pub fn write_back_with_duplicate(&mut self, duplicate: BlockIdx) -> Result<(), D::Error> { - self.block_device.write( - &self.block, - self.block_idx.expect("write_back with no read"), - )?; - self.block_device.write(&self.block, duplicate)?; + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn write_back_with_duplicate(&mut self, duplicate: BlockIdx) -> Result<(), D::Error> { + self.block_device + .write( + &self.block, + self.block_idx.expect("write_back with no read"), + ) + .await?; + self.block_device.write(&self.block, duplicate).await?; Ok(()) } diff --git a/src/fat/volume.rs b/src/fat/volume.rs index ed39b6a..79cb791 100644 --- a/src/fat/volume.rs +++ b/src/fat/volume.rs @@ -172,7 +172,9 @@ pub struct FatVolume { impl FatVolume { /// Write a new entry in the FAT - pub fn update_info_sector( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn update_info_sector( &mut self, block_cache: &mut BlockCache, ) -> Result<(), Error> @@ -190,6 +192,7 @@ impl FatVolume { trace!("Reading info sector"); let block = block_cache .read_mut(fat32_info.info_location) + .await .map_err(Error::DeviceError)?; if let Some(count) = self.free_clusters_count { block[488..492].copy_from_slice(&count.to_le_bytes()); @@ -198,7 +201,7 @@ impl FatVolume { block[492..496].copy_from_slice(&next_free_cluster.0.to_le_bytes()); } trace!("Writing info sector"); - block_cache.write_back()?; + block_cache.write_back().await?; } } Ok(()) @@ -213,7 +216,9 @@ impl FatVolume { } /// Write a new entry in the FAT - fn update_fat( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + async fn update_fat( &mut self, block_cache: &mut BlockCache, cluster: ClusterId, @@ -235,6 +240,7 @@ impl FatVolume { trace!("Reading FAT for update"); let block = block_cache .read_mut(this_fat_block_num) + .await .map_err(Error::DeviceError)?; // See let entry = match new_value { @@ -261,6 +267,7 @@ impl FatVolume { trace!("Reading FAT for update"); let block = block_cache .read_mut(this_fat_block_num) + .await .map_err(Error::DeviceError)?; let entry = match new_value { ClusterId::INVALID => 0x0FFF_FFF6, @@ -279,15 +286,17 @@ impl FatVolume { } trace!("Updating FAT"); if let Some(duplicate) = second_fat_block_num { - block_cache.write_back_with_duplicate(duplicate)?; + block_cache.write_back_with_duplicate(duplicate).await?; } else { - block_cache.write_back()?; + block_cache.write_back().await?; } Ok(()) } /// Look in the FAT to see which cluster comes next. - pub(crate) fn next_cluster( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub(crate) async fn next_cluster( &self, block_cache: &mut BlockCache, cluster: ClusterId, @@ -304,7 +313,7 @@ impl FatVolume { let this_fat_block_num = self.lba_start + self.fat_start.offset_bytes(fat_offset); let this_fat_ent_offset = (fat_offset % Block::LEN_U32) as usize; trace!("Walking FAT"); - let block = block_cache.read(this_fat_block_num)?; + let block = block_cache.read(this_fat_block_num).await?; let fat_entry = LittleEndian::read_u16(&block[this_fat_ent_offset..=this_fat_ent_offset + 1]); match fat_entry { @@ -327,7 +336,7 @@ impl FatVolume { let this_fat_block_num = self.lba_start + self.fat_start.offset_bytes(fat_offset); let this_fat_ent_offset = (fat_offset % Block::LEN_U32) as usize; trace!("Walking FAT"); - let block = block_cache.read(this_fat_block_num)?; + let block = block_cache.read(this_fat_block_num).await?; let fat_entry = LittleEndian::read_u32(&block[this_fat_ent_offset..=this_fat_ent_offset + 3]) & 0x0FFF_FFFF; @@ -390,7 +399,9 @@ impl FatVolume { /// Finds a empty entry space and writes the new entry to it, allocates a new cluster if it's /// needed - pub(crate) fn write_new_directory_entry( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub(crate) async fn write_new_directory_entry( &mut self, block_cache: &mut BlockCache, time_source: &T, @@ -428,6 +439,7 @@ impl FatVolume { trace!("Reading directory"); let block = block_cache .read_mut(block_idx) + .await .map_err(Error::DeviceError)?; for (i, dir_entry_bytes) in block.chunks_exact_mut(OnDiskDirEntry::LEN).enumerate() @@ -447,19 +459,20 @@ impl FatVolume { dir_entry_bytes .copy_from_slice(&entry.serialize(FatType::Fat16)[..]); trace!("Updating directory"); - block_cache.write_back()?; + block_cache.write_back().await?; return Ok(entry); } } } if cluster != ClusterId::ROOT_DIR { - current_cluster = match self.next_cluster(block_cache, cluster) { + current_cluster = match self.next_cluster(block_cache, cluster).await { Ok(n) => { first_dir_block_num = self.cluster_to_block(n); Some(n) } Err(Error::EndOfFile) => { - let c = self.alloc_cluster(block_cache, Some(cluster), true)?; + let c = + self.alloc_cluster(block_cache, Some(cluster), true).await?; first_dir_block_num = self.cluster_to_block(c); Some(c) } @@ -489,6 +502,7 @@ impl FatVolume { trace!("Reading directory"); let block = block_cache .read_mut(block_idx) + .await .map_err(Error::DeviceError)?; // Are any entries in the block we just loaded blank? If so // we can use them. @@ -510,20 +524,20 @@ impl FatVolume { dir_entry_bytes .copy_from_slice(&entry.serialize(FatType::Fat32)[..]); trace!("Updating directory"); - block_cache.write_back()?; + block_cache.write_back().await?; return Ok(entry); } } } // Well none of the blocks in that cluster had any space in // them, let's fetch another one. - current_cluster = match self.next_cluster(block_cache, cluster) { + current_cluster = match self.next_cluster(block_cache, cluster).await { Ok(n) => { first_dir_block_num = self.cluster_to_block(n); Some(n) } Err(Error::EndOfFile) => { - let c = self.alloc_cluster(block_cache, Some(cluster), true)?; + let c = self.alloc_cluster(block_cache, Some(cluster), true).await?; first_dir_block_num = self.cluster_to_block(c); Some(c) } @@ -539,7 +553,9 @@ impl FatVolume { /// Calls callback `func` with every valid entry in the given directory. /// Useful for performing directory listings. - pub(crate) fn iterate_dir( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub(crate) async fn iterate_dir( &self, block_cache: &mut BlockCache, dir_info: &DirectoryInfo, @@ -552,9 +568,11 @@ impl FatVolume { match &self.fat_specific_info { FatSpecificInfo::Fat16(fat16_info) => { self.iterate_fat16(dir_info, fat16_info, block_cache, |de, _| func(de)) + .await } FatSpecificInfo::Fat32(fat32_info) => { self.iterate_fat32(dir_info, fat32_info, block_cache, |de, _| func(de)) + .await } } } @@ -563,7 +581,9 @@ impl FatVolume { /// including the Long File Name. /// /// Useful for performing directory listings. - pub(crate) fn iterate_dir_lfn( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub(crate) async fn iterate_dir_lfn( &self, block_cache: &mut BlockCache, lfn_buffer: &mut LfnBuffer<'_>, @@ -651,6 +671,7 @@ impl FatVolume { func(de, None) } }) + .await } FatSpecificInfo::Fat32(fat32_info) => { self.iterate_fat32(dir_info, fat32_info, block_cache, |de, odde| { @@ -668,11 +689,14 @@ impl FatVolume { func(de, None) } }) + .await } } } - fn iterate_fat16( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + async fn iterate_fat16( &self, dir_info: &DirectoryInfo, fat16_info: &Fat16Info, @@ -703,7 +727,7 @@ impl FatVolume { while let Some(cluster) = current_cluster { for block_idx in first_dir_block_num.range(dir_size) { trace!("Reading FAT"); - let block = block_cache.read(block_idx)?; + let block = block_cache.read(block_idx).await?; for (i, dir_entry_bytes) in block.chunks_exact(OnDiskDirEntry::LEN).enumerate() { let dir_entry = OnDiskDirEntry::new(dir_entry_bytes); if dir_entry.is_end() { @@ -718,7 +742,7 @@ impl FatVolume { } } if cluster != ClusterId::ROOT_DIR { - current_cluster = match self.next_cluster(block_cache, cluster) { + current_cluster = match self.next_cluster(block_cache, cluster).await { Ok(n) => { first_dir_block_num = self.cluster_to_block(n); Some(n) @@ -732,7 +756,9 @@ impl FatVolume { Ok(()) } - fn iterate_fat32( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + async fn iterate_fat32( &self, dir_info: &DirectoryInfo, fat32_info: &Fat32Info, @@ -753,7 +779,10 @@ impl FatVolume { let start_block_idx = self.cluster_to_block(cluster); for block_idx in start_block_idx.range(BlockCount(u32::from(self.blocks_per_cluster))) { trace!("Reading FAT"); - let block = block_cache.read(block_idx).map_err(Error::DeviceError)?; + let block = block_cache + .read(block_idx) + .await + .map_err(Error::DeviceError)?; for (i, dir_entry_bytes) in block.chunks_exact(OnDiskDirEntry::LEN).enumerate() { let dir_entry = OnDiskDirEntry::new(dir_entry_bytes); if dir_entry.is_end() { @@ -767,7 +796,7 @@ impl FatVolume { } } } - current_cluster = match self.next_cluster(block_cache, cluster) { + current_cluster = match self.next_cluster(block_cache, cluster).await { Ok(n) => Some(n), _ => None, }; @@ -776,7 +805,9 @@ impl FatVolume { } /// Get an entry from the given directory - pub(crate) fn find_directory_entry( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub(crate) async fn find_directory_entry( &self, block_cache: &mut BlockCache, dir_info: &DirectoryInfo, @@ -807,18 +838,16 @@ impl FatVolume { while let Some(cluster) = current_cluster { for block in first_dir_block_num.range(dir_size) { - match self.find_entry_in_block( - block_cache, - FatType::Fat16, - match_name, - block, - ) { + match self + .find_entry_in_block(block_cache, FatType::Fat16, match_name, block) + .await + { Err(Error::NotFound) => continue, x => return x, } } if cluster != ClusterId::ROOT_DIR { - current_cluster = match self.next_cluster(block_cache, cluster) { + current_cluster = match self.next_cluster(block_cache, cluster).await { Ok(n) => { first_dir_block_num = self.cluster_to_block(n); Some(n) @@ -839,17 +868,15 @@ impl FatVolume { 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))) { - match self.find_entry_in_block( - block_cache, - FatType::Fat32, - match_name, - block, - ) { + match self + .find_entry_in_block(block_cache, FatType::Fat32, match_name, block) + .await + { Err(Error::NotFound) => continue, x => return x, } } - current_cluster = match self.next_cluster(block_cache, cluster) { + current_cluster = match self.next_cluster(block_cache, cluster).await { Ok(n) => Some(n), _ => None, } @@ -860,7 +887,9 @@ impl FatVolume { } /// Finds an entry in a given block of directory entries. - fn find_entry_in_block( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + async fn find_entry_in_block( &self, block_cache: &mut BlockCache, fat_type: FatType, @@ -871,7 +900,10 @@ impl FatVolume { D: BlockDevice, { trace!("Reading directory"); - let block = block_cache.read(block_idx).map_err(Error::DeviceError)?; + let block = block_cache + .read(block_idx) + .await + .map_err(Error::DeviceError)?; for (i, dir_entry_bytes) in block.chunks_exact(OnDiskDirEntry::LEN).enumerate() { let dir_entry = OnDiskDirEntry::new(dir_entry_bytes); if dir_entry.is_end() { @@ -888,7 +920,9 @@ impl FatVolume { } /// Delete an entry from the given directory - pub(crate) fn delete_directory_entry( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub(crate) async fn delete_directory_entry( &self, block_cache: &mut BlockCache, dir_info: &DirectoryInfo, @@ -921,7 +955,10 @@ impl FatVolume { while let Some(cluster) = current_cluster { // Scan the cluster / root dir a block at a time for block_idx in first_dir_block_num.range(dir_size) { - match self.delete_entry_in_block(block_cache, match_name, block_idx) { + match self + .delete_entry_in_block(block_cache, match_name, block_idx) + .await + { Err(Error::NotFound) => { // Carry on } @@ -934,7 +971,7 @@ impl FatVolume { } // if it's not the root dir, find the next cluster so we can keep looking if cluster != ClusterId::ROOT_DIR { - current_cluster = match self.next_cluster(block_cache, cluster) { + current_cluster = match self.next_cluster(block_cache, cluster).await { Ok(n) => { first_dir_block_num = self.cluster_to_block(n); Some(n) @@ -961,7 +998,10 @@ impl FatVolume { for block_idx in start_block_idx.range(BlockCount(u32::from(self.blocks_per_cluster))) { - match self.delete_entry_in_block(block_cache, match_name, block_idx) { + match self + .delete_entry_in_block(block_cache, match_name, block_idx) + .await + { Err(Error::NotFound) => { // Carry on continue; @@ -974,7 +1014,7 @@ impl FatVolume { } } // Find the next cluster - current_cluster = match self.next_cluster(block_cache, cluster) { + current_cluster = match self.next_cluster(block_cache, cluster).await { Ok(n) => Some(n), _ => None, } @@ -991,7 +1031,9 @@ impl FatVolume { /// /// Entries are marked as deleted by setting the first byte of the file name /// to a special value. - fn delete_entry_in_block( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + async fn delete_entry_in_block( &self, block_cache: &mut BlockCache, match_name: &ShortFileName, @@ -1003,6 +1045,7 @@ impl FatVolume { trace!("Reading directory"); let block = block_cache .read_mut(block_idx) + .await .map_err(Error::DeviceError)?; for (i, dir_entry_bytes) in block.chunks_exact_mut(OnDiskDirEntry::LEN).enumerate() { let dir_entry = OnDiskDirEntry::new(dir_entry_bytes); @@ -1014,14 +1057,16 @@ impl FatVolume { // set first byte to the 'unused' marker block[start] = 0xE5; trace!("Updating directory"); - return block_cache.write_back().map_err(Error::DeviceError); + return block_cache.write_back().await.map_err(Error::DeviceError); } } Err(Error::NotFound) } /// Finds the next free cluster after the start_cluster and before end_cluster - pub(crate) fn find_next_free_cluster( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub(crate) async fn find_next_free_cluster( &self, block_cache: &mut BlockCache, start_cluster: ClusterId, @@ -1049,6 +1094,7 @@ impl FatVolume { trace!("Reading block {:?}", this_fat_block_num); let block = block_cache .read(this_fat_block_num) + .await .map_err(Error::DeviceError)?; while this_fat_ent_offset <= Block::LEN - 2 { let fat_entry = LittleEndian::read_u16( @@ -1079,6 +1125,7 @@ impl FatVolume { trace!("Reading block {:?}", this_fat_block_num); let block = block_cache .read(this_fat_block_num) + .await .map_err(Error::DeviceError)?; while this_fat_ent_offset <= Block::LEN - 4 { let fat_entry = LittleEndian::read_u32( @@ -1098,7 +1145,9 @@ impl FatVolume { } /// Tries to allocate a cluster - pub(crate) fn alloc_cluster( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub(crate) async fn alloc_cluster( &mut self, block_cache: &mut BlockCache, prev_cluster: Option, @@ -1118,7 +1167,9 @@ impl FatVolume { start_cluster, end_cluster ); - let new_cluster = match self.find_next_free_cluster(block_cache, start_cluster, end_cluster) + let new_cluster = match self + .find_next_free_cluster(block_cache, start_cluster, end_cluster) + .await { Ok(cluster) => cluster, Err(_) if start_cluster.0 > RESERVED_ENTRIES => { @@ -1127,12 +1178,15 @@ impl FatVolume { ClusterId(RESERVED_ENTRIES), end_cluster ); - self.find_next_free_cluster(block_cache, ClusterId(RESERVED_ENTRIES), end_cluster)? + self.find_next_free_cluster(block_cache, ClusterId(RESERVED_ENTRIES), end_cluster) + .await? } Err(e) => return Err(e), }; + // This new cluster is the end of the file's chain - self.update_fat(block_cache, new_cluster, ClusterId::END_OF_FILE)?; + self.update_fat(block_cache, new_cluster, ClusterId::END_OF_FILE) + .await?; // If there's something before this new one, update the FAT to point it at us if let Some(cluster) = prev_cluster { trace!( @@ -1140,28 +1194,30 @@ impl FatVolume { cluster, new_cluster ); - self.update_fat(block_cache, cluster, new_cluster)?; + self.update_fat(block_cache, cluster, new_cluster).await?; } + trace!( "Finding next free between {:?}..={:?}", new_cluster, end_cluster ); - self.next_free_cluster = - match self.find_next_free_cluster(block_cache, new_cluster, end_cluster) { - Ok(cluster) => Some(cluster), - Err(_) if new_cluster.0 > RESERVED_ENTRIES => { - match self.find_next_free_cluster( - block_cache, - ClusterId(RESERVED_ENTRIES), - end_cluster, - ) { - Ok(cluster) => Some(cluster), - Err(e) => return Err(e), - } + self.next_free_cluster = match self + .find_next_free_cluster(block_cache, new_cluster, end_cluster) + .await + { + Ok(cluster) => Some(cluster), + Err(_) if new_cluster.0 > RESERVED_ENTRIES => { + match self + .find_next_free_cluster(block_cache, ClusterId(RESERVED_ENTRIES), end_cluster) + .await + { + Ok(cluster) => Some(cluster), + Err(e) => return Err(e), } - Err(e) => return Err(e), - }; + } + Err(e) => return Err(e), + }; debug!("Next free cluster is {:?}", self.next_free_cluster); // Record that we've allocated a cluster if let Some(ref mut number_free_cluster) = self.free_clusters_count { @@ -1173,7 +1229,7 @@ impl FatVolume { for block_idx in start_block_idx.range(num_blocks) { trace!("Zeroing cluster {:?}", block_idx); let _block = block_cache.blank_mut(block_idx); - block_cache.write_back()?; + block_cache.write_back().await?; } } debug!("All done, returning {:?}", new_cluster); @@ -1181,7 +1237,9 @@ impl FatVolume { } /// Marks the input cluster as an EOF and all the subsequent clusters in the chain as free - pub(crate) fn truncate_cluster_chain( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub(crate) async fn truncate_cluster_chain( &mut self, block_cache: &mut BlockCache, cluster: ClusterId, @@ -1194,7 +1252,7 @@ impl FatVolume { return Ok(()); } let mut next = { - match self.next_cluster(block_cache, cluster) { + match self.next_cluster(block_cache, cluster).await { Ok(n) => n, Err(Error::EndOfFile) => return Ok(()), Err(e) => return Err(e), @@ -1207,15 +1265,16 @@ impl FatVolume { } else { self.next_free_cluster = Some(next); } - self.update_fat(block_cache, cluster, ClusterId::END_OF_FILE)?; + self.update_fat(block_cache, cluster, ClusterId::END_OF_FILE) + .await?; loop { - match self.next_cluster(block_cache, next) { + match self.next_cluster(block_cache, next).await { Ok(n) => { - self.update_fat(block_cache, next, ClusterId::EMPTY)?; + self.update_fat(block_cache, next, ClusterId::EMPTY).await?; next = n; } Err(Error::EndOfFile) => { - self.update_fat(block_cache, next, ClusterId::EMPTY)?; + self.update_fat(block_cache, next, ClusterId::EMPTY).await?; break; } Err(e) => return Err(e), @@ -1228,7 +1287,9 @@ impl FatVolume { } /// Writes a Directory Entry to the disk - pub(crate) fn write_entry_to_disk( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub(crate) async fn write_entry_to_disk( &self, block_cache: &mut BlockCache, entry: &DirEntry, @@ -1243,13 +1304,14 @@ impl FatVolume { trace!("Reading directory for update"); let block = block_cache .read_mut(entry.entry_block) + .await .map_err(Error::DeviceError)?; let start = usize::try_from(entry.entry_offset).map_err(|_| Error::ConversionError)?; block[start..start + 32].copy_from_slice(&entry.serialize(fat_type)[..]); trace!("Updating directory"); - block_cache.write_back().map_err(Error::DeviceError)?; + block_cache.write_back().await.map_err(Error::DeviceError)?; Ok(()) } @@ -1258,7 +1320,9 @@ impl FatVolume { /// 1) Creates the directory entry in the parent /// 2) Allocates a new cluster to hold the new directory /// 3) Writes out the `.` and `..` entries in the new directory - pub(crate) fn make_dir( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub(crate) async fn make_dir( &mut self, block_cache: &mut BlockCache, time_source: &T, @@ -1270,12 +1334,14 @@ impl FatVolume { D: BlockDevice, T: TimeSource, { - let mut new_dir_entry_in_parent = - self.write_new_directory_entry(block_cache, time_source, parent, sfn, att)?; + let mut new_dir_entry_in_parent = self + .write_new_directory_entry(block_cache, time_source, parent, sfn, att) + .await?; if new_dir_entry_in_parent.cluster == ClusterId::EMPTY { - new_dir_entry_in_parent.cluster = self.alloc_cluster(block_cache, None, false)?; + new_dir_entry_in_parent.cluster = self.alloc_cluster(block_cache, None, false).await?; // update the parent dir with the cluster of the new dir - self.write_entry_to_disk(block_cache, &new_dir_entry_in_parent)?; + self.write_entry_to_disk(block_cache, &new_dir_entry_in_parent) + .await?; } let new_dir_start_block = self.cluster_to_block(new_dir_entry_in_parent.cluster); debug!("Made new dir entry {:?}", new_dir_entry_in_parent); @@ -1321,14 +1387,14 @@ impl FatVolume { block[offset..offset + OnDiskDirEntry::LEN] .copy_from_slice(&dot_dot_entry_in_child.serialize(fat_type)[..]); - block_cache.write_back()?; + block_cache.write_back().await?; for block_idx in new_dir_start_block .range(BlockCount(u32::from(self.blocks_per_cluster))) .skip(1) { let _block = block_cache.blank_mut(block_idx); - block_cache.write_back()?; + block_cache.write_back().await?; } Ok(()) @@ -1337,7 +1403,9 @@ impl FatVolume { /// Load the boot parameter block from the start of the given partition and /// determine if the partition contains a valid FAT16 or FAT32 file system. -pub fn parse_volume( +#[cfg_attr(feature = "async", maybe_async::must_be_async)] +#[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] +pub async fn parse_volume( block_cache: &mut BlockCache, lba_start: BlockIdx, num_blocks: BlockCount, @@ -1347,7 +1415,10 @@ where D::Error: core::fmt::Debug, { trace!("Reading BPB"); - let block = block_cache.read(lba_start).map_err(Error::DeviceError)?; + let block = block_cache + .read(lba_start) + .await + .map_err(Error::DeviceError)?; let bpb = Bpb::create_from_bytes(block).map_err(Error::FormatError)?; let fat_start = BlockCount(u32::from(bpb.reserved_block_count())); let second_fat_start = if bpb.num_fats() == 2 { @@ -1416,6 +1487,7 @@ where trace!("Reading info block"); let info_block = block_cache .read(lba_start + info_location) + .await .map_err(Error::DeviceError)?; let info_sector = InfoSector::create_from_bytes(info_block).map_err(Error::FormatError)?; diff --git a/src/filesystem/directory.rs b/src/filesystem/directory.rs index ccb6d50..5dfc75a 100644 --- a/src/filesystem/directory.rs +++ b/src/filesystem/directory.rs @@ -110,37 +110,44 @@ where /// Open a directory. /// /// You can then read the directory entries with `iterate_dir` and `open_file_in_dir`. - pub fn open_dir( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn open_dir( &self, name: N, ) -> Result, Error> where N: ToShortFileName, { - let d = self.volume_mgr.open_dir(self.raw_directory, name)?; + let d = self.volume_mgr.open_dir(self.raw_directory, name).await?; Ok(d.to_directory(self.volume_mgr)) } /// Change to a directory, mutating this object. /// /// You can then read the directory entries with `iterate_dir` and `open_file_in_dir`. - pub fn change_dir(&mut self, name: N) -> Result<(), Error> + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn change_dir(&mut self, name: N) -> Result<(), Error> where N: ToShortFileName, { - let d = self.volume_mgr.open_dir(self.raw_directory, name)?; + let d = self.volume_mgr.open_dir(self.raw_directory, name).await?; self.volume_mgr.close_dir(self.raw_directory).unwrap(); self.raw_directory = d; Ok(()) } /// Look in a directory for a named file. - pub fn find_directory_entry(&self, name: N) -> Result> + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn find_directory_entry(&self, name: N) -> Result> where N: ToShortFileName, { self.volume_mgr .find_directory_entry(self.raw_directory, name) + .await } /// Call a callback function for each directory entry in a directory. @@ -154,11 +161,13 @@ where /// object is already locked in order to do the iteration. /// /// - pub fn iterate_dir(&self, func: F) -> Result<(), Error> + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn iterate_dir(&self, func: F) -> Result<(), Error> where F: FnMut(&DirEntry), { - self.volume_mgr.iterate_dir(self.raw_directory, func) + self.volume_mgr.iterate_dir(self.raw_directory, func).await } /// Call a callback function for each directory entry in a directory, and @@ -176,7 +185,9 @@ where /// object is already locked in order to do the iteration. /// /// - pub fn iterate_dir_lfn( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn iterate_dir_lfn( &self, lfn_buffer: &mut LfnBuffer<'_>, func: F, @@ -186,10 +197,13 @@ where { self.volume_mgr .iterate_dir_lfn(self.raw_directory, lfn_buffer, func) + .await } /// Open a file with the given full path. A file can only be opened once. - pub fn open_file_in_dir( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn open_file_in_dir( &self, name: N, mode: crate::Mode, @@ -199,24 +213,33 @@ where { let f = self .volume_mgr - .open_file_in_dir(self.raw_directory, name, mode)?; + .open_file_in_dir(self.raw_directory, name, mode) + .await?; Ok(f.to_file(self.volume_mgr)) } /// Delete a closed file with the given filename, if it exists. - pub fn delete_file_in_dir(&self, name: N) -> Result<(), Error> + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn delete_file_in_dir(&self, name: N) -> Result<(), Error> where N: ToShortFileName, { - self.volume_mgr.delete_file_in_dir(self.raw_directory, name) + self.volume_mgr + .delete_file_in_dir(self.raw_directory, name) + .await } /// Make a directory inside this directory - pub fn make_dir_in_dir(&self, name: N) -> Result<(), Error> + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn make_dir_in_dir(&self, name: N) -> Result<(), Error> where N: ToShortFileName, { - self.volume_mgr.make_dir_in_dir(self.raw_directory, name) + self.volume_mgr + .make_dir_in_dir(self.raw_directory, name) + .await } /// Convert back to a raw directory diff --git a/src/filesystem/files.rs b/src/filesystem/files.rs index 15673c7..2e3eebb 100644 --- a/src/filesystem/files.rs +++ b/src/filesystem/files.rs @@ -3,7 +3,10 @@ use crate::{ filesystem::{ClusterId, DirEntry, Handle}, BlockDevice, Error, RawVolume, VolumeManager, }; +#[cfg(not(feature = "async"))] use embedded_io::{ErrorType, Read, Seek, SeekFrom, Write}; +#[cfg(feature = "async")] +use embedded_io_async::{ErrorType, Read, Seek, SeekFrom, Write}; /// A handle for an open file on disk. /// @@ -76,13 +79,17 @@ where /// Read from the file /// /// Returns how many bytes were read, or an error. - pub fn read(&self, buffer: &mut [u8]) -> Result> { - self.volume_mgr.read(self.raw_file, buffer) + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn read(&self, buffer: &mut [u8]) -> Result> { + self.volume_mgr.read(self.raw_file, buffer).await } /// Write to the file - pub fn write(&self, buffer: &[u8]) -> Result<(), crate::Error> { - self.volume_mgr.write(self.raw_file, buffer) + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn write(&self, buffer: &[u8]) -> Result<(), crate::Error> { + self.volume_mgr.write(self.raw_file, buffer).await } /// Check if a file is at End Of File. @@ -130,16 +137,20 @@ where } /// Flush any written data by updating the directory entry. - pub fn flush(&self) -> Result<(), Error> { - self.volume_mgr.flush_file(self.raw_file) + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn flush(&self) -> Result<(), Error> { + self.volume_mgr.flush_file(self.raw_file).await } /// Consume the `File` handle and close it. The behavior of this is similar /// to using [`core::mem::drop`] or letting the `File` go out of scope, /// except this lets the user handle any errors that may occur in the process, /// whereas when using drop, any errors will be discarded silently. - pub fn close(self) -> Result<(), Error> { - let result = self.volume_mgr.close_file(self.raw_file); + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn close(self) -> Result<(), Error> { + let result = self.volume_mgr.close_file(self.raw_file).await; core::mem::forget(self); result } @@ -186,11 +197,13 @@ impl< const MAX_VOLUMES: usize, > Read for File<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> { - fn read(&mut self, buf: &mut [u8]) -> Result { + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + async fn read(&mut self, buf: &mut [u8]) -> Result { if buf.is_empty() { Ok(0) } else { - File::read(self, buf) + File::read(self, buf).await } } } @@ -203,17 +216,21 @@ impl< const MAX_VOLUMES: usize, > Write for File<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> { - fn write(&mut self, buf: &[u8]) -> Result { + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + async fn write(&mut self, buf: &[u8]) -> Result { if buf.is_empty() { Ok(0) } else { - File::write(self, buf)?; + File::write(self, buf).await?; Ok(buf.len()) } } - fn flush(&mut self) -> Result<(), Self::Error> { - Self::flush(self) + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + async fn flush(&mut self) -> Result<(), Self::Error> { + Self::flush(self).await } } @@ -225,7 +242,9 @@ impl< const MAX_VOLUMES: usize, > Seek for File<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> { - fn seek(&mut self, pos: SeekFrom) -> Result { + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + async fn seek(&mut self, pos: SeekFrom) -> Result { match pos { SeekFrom::Start(offset) => { self.seek_from_start(offset.try_into().map_err(|_| Error::InvalidOffset)?)? diff --git a/src/lib.rs b/src/lib.rs index e8ebbab..174543c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -391,7 +391,9 @@ where /// /// You can then read the directory entries with `iterate_dir`, or you can /// use `open_file_in_dir`. - pub fn open_root_dir( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn open_root_dir( &self, ) -> Result, Error> { let d = self.volume_mgr.open_root_dir(self.raw_volume)?; @@ -409,8 +411,10 @@ where /// to using [`core::mem::drop`] or letting the `Volume` go out of scope, /// except this lets the user handle any errors that may occur in the process, /// whereas when using drop, any errors will be discarded silently. - pub fn close(self) -> Result<(), Error> { - let result = self.volume_mgr.close_volume(self.raw_volume); + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn close(self) -> Result<(), Error> { + let result = self.volume_mgr.close_volume(self.raw_volume).await; core::mem::forget(self); result } diff --git a/src/sdcard/mod.rs b/src/sdcard/mod.rs index c27fa0e..d5eb9d7 100644 --- a/src/sdcard/mod.rs +++ b/src/sdcard/mod.rs @@ -152,6 +152,7 @@ where } } +#[cfg(not(feature = "async"))] impl BlockDevice for SdCard where SPI: embedded_hal::spi::SpiDevice, @@ -162,7 +163,13 @@ where /// Read one or more blocks, starting at the given block index. /// /// This will trigger card (re-)initialisation. - fn read(&self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), Self::Error> { + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + async fn read( + &self, + blocks: &mut [Block], + start_block_idx: BlockIdx, + ) -> Result<(), Self::Error> { let mut inner = self.inner.borrow_mut(); debug!("Read {} blocks @ {}", blocks.len(), start_block_idx.0,); inner.check_init()?; @@ -172,7 +179,9 @@ where /// Write one or more blocks, starting at the given block index. /// /// This will trigger card (re-)initialisation. - fn write(&self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Self::Error> { + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + async fn write(&self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Self::Error> { let mut inner = self.inner.borrow_mut(); debug!("Writing {} blocks @ {}", blocks.len(), start_block_idx.0); inner.check_init()?; @@ -182,7 +191,9 @@ where /// Determine how many blocks this device can hold. /// /// This will trigger card (re-)initialisation. - fn num_blocks(&self) -> Result { + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + async fn num_blocks(&self) -> Result { let mut inner = self.inner.borrow_mut(); inner.check_init()?; inner.num_blocks() diff --git a/src/volume_mgr.rs b/src/volume_mgr.rs index 9dcb7ed..9f18074 100644 --- a/src/volume_mgr.rs +++ b/src/volume_mgr.rs @@ -107,11 +107,13 @@ where /// /// We do not support GUID Partition Table disks. Nor do we support any /// concept of drive letters - that is for a higher layer to handle. - pub fn open_volume( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn open_volume( &self, volume_idx: VolumeIdx, ) -> Result, Error> { - let v = self.open_raw_volume(volume_idx)?; + let v = self.open_raw_volume(volume_idx).await?; Ok(v.to_volume(self)) } @@ -122,7 +124,12 @@ where /// /// This function gives you a `RawVolume` and you must close the volume by /// calling `VolumeManager::close_volume`. - pub fn open_raw_volume(&self, volume_idx: VolumeIdx) -> Result> { + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn open_raw_volume( + &self, + volume_idx: VolumeIdx, + ) -> Result> { const PARTITION1_START: usize = 446; const PARTITION2_START: usize = PARTITION1_START + PARTITION_INFO_LENGTH; const PARTITION3_START: usize = PARTITION2_START + PARTITION_INFO_LENGTH; @@ -152,6 +159,7 @@ where let block = data .block_cache .read(BlockIdx(0)) + .await .map_err(Error::DeviceError)?; // We only support Master Boot Record (MBR) partitioned cards, not // GUID Partition Table (GPT) @@ -197,7 +205,8 @@ where | PARTITION_ID_FAT16_LBA | PARTITION_ID_FAT16 | PARTITION_ID_FAT16_SMALL => { - let volume = fat::parse_volume(&mut data.block_cache, lba_start, num_blocks)?; + let volume = + fat::parse_volume(&mut data.block_cache, lba_start, num_blocks).await?; let id = RawVolume(data.id_generator.generate()); let info = VolumeInfo { raw_volume: id, @@ -243,7 +252,9 @@ where /// You can then read the directory entries with `iterate_dir` and `open_file_in_dir`. /// /// Passing "." as the name results in opening the `parent_dir` a second time. - pub fn open_dir( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn open_dir( &self, parent_dir: RawDirectory, name: N, @@ -284,11 +295,14 @@ where // ok we'll actually look for the directory then let dir_entry = match &data.open_volumes[volume_idx].volume_type { - VolumeType::Fat(fat) => fat.find_directory_entry( - &mut data.block_cache, - &data.open_dirs[parent_dir_idx], - &short_file_name, - )?, + VolumeType::Fat(fat) => { + fat.find_directory_entry( + &mut data.block_cache, + &data.open_dirs[parent_dir_idx], + &short_file_name, + ) + .await? + } }; debug!("Found dir entry: {:?}", dir_entry); @@ -333,7 +347,9 @@ where /// Close a volume /// /// You can't close it if there are any files or directories open on it. - pub fn close_volume(&self, volume: RawVolume) -> Result<(), Error> { + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn close_volume(&self, volume: RawVolume) -> Result<(), Error> { let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?; let data = data.deref_mut(); @@ -353,7 +369,7 @@ where match &mut data.open_volumes[volume_idx].volume_type { VolumeType::Fat(fat) => { - fat.update_info_sector(&mut data.block_cache)?; + fat.update_info_sector(&mut data.block_cache).await?; } } @@ -363,7 +379,9 @@ where } /// Look in a directory for a named file. - pub fn find_directory_entry( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn find_directory_entry( &self, directory: RawDirectory, name: N, @@ -384,6 +402,7 @@ where &data.open_dirs[directory_idx], &sfn, ) + .await } } } @@ -399,7 +418,9 @@ where /// object is already locked in order to do the iteration. /// /// - pub fn iterate_dir( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn iterate_dir( &self, directory: RawDirectory, mut func: F, @@ -424,6 +445,7 @@ where } }, ) + .await } } } @@ -443,7 +465,9 @@ where /// object is already locked in order to do the iteration. /// /// - pub fn iterate_dir_lfn( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn iterate_dir_lfn( &self, directory: RawDirectory, lfn_buffer: &mut LfnBuffer<'_>, @@ -467,12 +491,15 @@ where &data.open_dirs[directory_idx], func, ) + .await } } } /// Open a file with the given full path. A file can only be opened once. - pub fn open_file_in_dir( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn open_file_in_dir( &self, directory: RawDirectory, name: N, @@ -501,7 +528,8 @@ where &data.open_dirs[directory_idx], &sfn, ), - }; + } + .await; let dir_entry = match dir_entry { Ok(entry) => { @@ -541,13 +569,16 @@ where let att = Attributes::create_from_fat(0); let volume_idx = data.get_volume_by_id(volume_id)?; let entry = match &mut data.open_volumes[volume_idx].volume_type { - VolumeType::Fat(fat) => fat.write_new_directory_entry( - &mut data.block_cache, - &self.time_source, - cluster, - sfn, - att, - )?, + VolumeType::Fat(fat) => { + fat.write_new_directory_entry( + &mut data.block_cache, + &self.time_source, + cluster, + sfn, + att, + ) + .await? + } }; let file_id = RawFile(data.id_generator.generate()); @@ -624,16 +655,20 @@ where dirty: false, }; match &mut data.open_volumes[volume_idx].volume_type { - VolumeType::Fat(fat) => fat.truncate_cluster_chain( - &mut data.block_cache, - file.entry.cluster, - )?, + VolumeType::Fat(fat) => { + fat.truncate_cluster_chain( + &mut data.block_cache, + file.entry.cluster, + ) + .await? + } }; file.update_length(0); match &data.open_volumes[volume_idx].volume_type { VolumeType::Fat(fat) => { file.entry.mtime = self.time_source.get_timestamp(); - fat.write_entry_to_disk(&mut data.block_cache, &file.entry)?; + fat.write_entry_to_disk(&mut data.block_cache, &file.entry) + .await?; } }; @@ -653,7 +688,9 @@ where } /// Delete a closed file with the given filename, if it exists. - pub fn delete_file_in_dir( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn delete_file_in_dir( &self, directory: RawDirectory, name: N, @@ -670,7 +707,10 @@ where let sfn = name.to_short_filename().map_err(Error::FilenameError)?; let dir_entry = match &data.open_volumes[volume_idx].volume_type { - VolumeType::Fat(fat) => fat.find_directory_entry(&mut data.block_cache, dir_info, &sfn), + VolumeType::Fat(fat) => { + fat.find_directory_entry(&mut data.block_cache, dir_info, &sfn) + .await + } }?; if dir_entry.attributes.is_directory() { @@ -684,7 +724,8 @@ where let volume_idx = data.get_volume_by_id(dir_info.raw_volume)?; match &data.open_volumes[volume_idx].volume_type { VolumeType::Fat(fat) => { - fat.delete_directory_entry(&mut data.block_cache, dir_info, &sfn)? + fat.delete_directory_entry(&mut data.block_cache, dir_info, &sfn) + .await? } } @@ -695,7 +736,9 @@ where /// /// Will look in the BPB for a volume label, and if nothing is found, will /// search the root directory for a volume label. - pub fn get_root_volume_label( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn get_root_volume_label( &self, raw_volume: RawVolume, ) -> Result, Error> { @@ -719,13 +762,15 @@ where // Nothing in the BPB, let's do it the slow way let root_dir = self.open_root_dir(raw_volume)?.to_directory(self); let mut maybe_volume_name = None; - root_dir.iterate_dir(|de| { - if maybe_volume_name.is_none() - && de.attributes == Attributes::create_from_fat(Attributes::VOLUME) - { - maybe_volume_name = Some(unsafe { de.name.clone().to_volume_label() }) - } - })?; + root_dir + .iterate_dir(|de| { + if maybe_volume_name.is_none() + && de.attributes == Attributes::create_from_fat(Attributes::VOLUME) + { + maybe_volume_name = Some(unsafe { de.name.clone().to_volume_label() }) + } + }) + .await?; debug!( "Got volume label {:?} for {:?} from root", @@ -736,7 +781,9 @@ where } /// Read from an open file. - pub fn read(&self, file: RawFile, buffer: &mut [u8]) -> Result> { + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn read(&self, file: RawFile, buffer: &mut [u8]) -> Result> { let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?; let data = data.deref_mut(); @@ -750,17 +797,20 @@ where let mut read = 0; while space > 0 && !data.open_files[file_idx].eof() { let mut current_cluster = data.open_files[file_idx].current_cluster; - let (block_idx, block_offset, block_avail) = data.find_data_on_disk( - volume_idx, - &mut current_cluster, - data.open_files[file_idx].entry.cluster, - data.open_files[file_idx].current_offset, - )?; + let (block_idx, block_offset, block_avail) = data + .find_data_on_disk( + volume_idx, + &mut current_cluster, + data.open_files[file_idx].entry.cluster, + data.open_files[file_idx].current_offset, + ) + .await?; data.open_files[file_idx].current_cluster = current_cluster; trace!("Reading file ID {:?}", file); let block = data .block_cache .read(block_idx) + .await .map_err(Error::DeviceError)?; let to_copy = block_avail .min(space) @@ -778,7 +828,9 @@ where } /// Write to a open file. - pub fn write(&self, file: RawFile, buffer: &[u8]) -> Result<(), Error> { + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn write(&self, file: RawFile, buffer: &[u8]) -> Result<(), Error> { #[cfg(feature = "defmt-log")] debug!("write(file={:?}, buffer={:x}", file, buffer); @@ -804,7 +856,8 @@ where data.open_files[file_idx].entry.cluster = match data.open_volumes[volume_idx].volume_type { VolumeType::Fat(ref mut fat) => { - fat.alloc_cluster(&mut data.block_cache, None, false)? + fat.alloc_cluster(&mut data.block_cache, None, false) + .await? } }; debug!( @@ -834,12 +887,15 @@ where written, bytes_to_write, current_cluster ); let current_offset = data.open_files[file_idx].current_offset; - let (block_idx, block_offset, block_avail) = match data.find_data_on_disk( - volume_idx, - &mut current_cluster, - data.open_files[file_idx].entry.cluster, - current_offset, - ) { + let (block_idx, block_offset, block_avail) = match data + .find_data_on_disk( + volume_idx, + &mut current_cluster, + data.open_files[file_idx].entry.cluster, + current_offset, + ) + .await + { Ok(vars) => { debug!( "Found block_idx={:?}, block_offset={:?}, block_avail={}", @@ -857,6 +913,7 @@ where Some(current_cluster.1), false, ) + .await .is_err() { return Err(Error::DiskFull); @@ -869,6 +926,7 @@ where data.open_files[file_idx].entry.cluster, data.open_files[file_idx].current_offset, ) + .await .map_err(|_| Error::AllocationError)?; debug!("New offset {:?}", new_offset); new_offset @@ -886,12 +944,13 @@ where debug!("Reading for partial block write"); data.block_cache .read_mut(block_idx) + .await .map_err(Error::DeviceError)? }; block[block_offset..block_offset + to_copy] .copy_from_slice(&buffer[written..written + to_copy]); debug!("Writing block {:?}", block_idx); - data.block_cache.write_back()?; + data.block_cache.write_back().await?; written += to_copy; data.open_files[file_idx].current_cluster = current_cluster; @@ -912,8 +971,10 @@ where } /// Close a file with the given raw file handle. - pub fn close_file(&self, file: RawFile) -> Result<(), Error> { - let flush_result = self.flush_file(file); + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn close_file(&self, file: RawFile) -> Result<(), Error> { + let flush_result = self.flush_file(file).await; let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?; let file_idx = data.get_file_by_id(file)?; data.open_files.swap_remove(file_idx); @@ -921,7 +982,9 @@ where } /// Flush (update the entry) for a file with the given raw file handle. - pub fn flush_file(&self, file: RawFile) -> Result<(), Error> { + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn flush_file(&self, file: RawFile) -> Result<(), Error> { let mut data = self.data.try_borrow_mut().map_err(|_| Error::LockError)?; let data = data.deref_mut(); @@ -932,16 +995,14 @@ where match &mut data.open_volumes[volume_idx].volume_type { VolumeType::Fat(fat) => { debug!("Updating FAT info sector"); - fat.update_info_sector(&mut data.block_cache)?; + fat.update_info_sector(&mut data.block_cache).await?; debug!("Updating dir entry {:?}", data.open_files[file_id].entry); if data.open_files[file_id].entry.size != 0 { // If you have a length, you must have a cluster assert!(data.open_files[file_id].entry.cluster.0 != 0); } - fat.write_entry_to_disk( - &mut data.block_cache, - &data.open_files[file_id].entry, - )?; + fat.write_entry_to_disk(&mut data.block_cache, &data.open_files[file_id].entry) + .await?; } }; } @@ -1016,7 +1077,9 @@ where } /// Create a directory in a given directory. - pub fn make_dir_in_dir( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + pub async fn make_dir_in_dir( &self, directory: RawDirectory, name: N, @@ -1048,7 +1111,8 @@ where // Does an entry exist with this name? let maybe_dir_entry = match &volume_info.volume_type { VolumeType::Fat(fat) => { - fat.find_directory_entry(&mut data.block_cache, parent_directory_info, &sfn) + { fat.find_directory_entry(&mut data.block_cache, parent_directory_info, &sfn) } + .await } }; @@ -1080,7 +1144,8 @@ where parent_directory_info.cluster, sfn, att, - )?; + ) + .await?; } }; @@ -1173,7 +1238,9 @@ where /// * the index for the block on the disk that contains the data we want, /// * the byte offset into that block for the data we want, and /// * how many bytes remain in that block. - fn find_data_on_disk( + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + async fn find_data_on_disk( &mut self, volume_idx: usize, start: &mut (u32, ClusterId), @@ -1199,7 +1266,7 @@ where let num_clusters = offset_from_cluster / bytes_per_cluster; for _ in 0..num_clusters { start.1 = match &self.open_volumes[volume_idx].volume_type { - VolumeType::Fat(fat) => fat.next_cluster(&mut self.block_cache, start.1)?, + VolumeType::Fat(fat) => fat.next_cluster(&mut self.block_cache, start.1).await?, }; start.0 += bytes_per_cluster; } @@ -1275,7 +1342,13 @@ mod tests { type Error = Error; /// Read one or more blocks, starting at the given block index. - fn read(&self, blocks: &mut [Block], start_block_idx: BlockIdx) -> Result<(), Self::Error> { + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + async fn read( + &self, + blocks: &mut [Block], + start_block_idx: BlockIdx, + ) -> Result<(), Self::Error> { // Actual blocks taken from an SD card, except I've changed the start and length of partition 0. static BLOCKS: [Block; 3] = [ Block { @@ -1468,17 +1541,26 @@ mod tests { } /// Write one or more blocks, starting at the given block index. - fn write(&self, _blocks: &[Block], _start_block_idx: BlockIdx) -> Result<(), Self::Error> { + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + async fn write( + &self, + _blocks: &[Block], + _start_block_idx: BlockIdx, + ) -> Result<(), Self::Error> { unimplemented!(); } /// Determine how many blocks this device can hold. - fn num_blocks(&self) -> Result { + #[cfg_attr(feature = "async", maybe_async::must_be_async)] + #[cfg_attr(not(feature = "async"), maybe_async::must_be_sync)] + async fn num_blocks(&self) -> Result { Ok(BlockCount(2)) } } #[test] + #[cfg(not(feature = "async"))] fn partition0() { let c: VolumeManager = VolumeManager::new_with_limits(DummyBlockDevice, Clock, 0xAA00_0000); From c2d29db2800f5d600ca6245468c364d9cca6c0ee Mon Sep 17 00:00:00 2001 From: BL-CZY Date: Sat, 8 Nov 2025 02:10:36 +0000 Subject: [PATCH 2/2] clippy auto fix but some more should be polished --- src/fat/volume.rs | 23 ++++++----------------- src/volume_mgr.rs | 2 +- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/src/fat/volume.rs b/src/fat/volume.rs index 79cb791..519247f 100644 --- a/src/fat/volume.rs +++ b/src/fat/volume.rs @@ -623,7 +623,7 @@ impl FatVolume { lfn_buffer.push(&buffer); SeqState::Complete { csum } } - (true, sequence, _) if sequence >= 0x02 && sequence < 0x14 => { + (true, sequence, _) if (0x02..0x14).contains(&sequence) => { lfn_buffer.clear(); lfn_buffer.push(&buffer); SeqState::Remaining { @@ -636,7 +636,7 @@ impl FatVolume { SeqState::Complete { csum } } (false, sequence, SeqState::Remaining { csum, next }) - if sequence >= 0x01 && sequence < 0x13 && next == sequence => + if (0x01..0x13).contains(&sequence) && next == sequence => { lfn_buffer.push(&buffer); SeqState::Remaining { @@ -796,10 +796,7 @@ impl FatVolume { } } } - current_cluster = match self.next_cluster(block_cache, cluster).await { - Ok(n) => Some(n), - _ => None, - }; + current_cluster = (self.next_cluster(block_cache, cluster).await).ok(); } Ok(()) } @@ -876,10 +873,7 @@ impl FatVolume { x => return x, } } - current_cluster = match self.next_cluster(block_cache, cluster).await { - Ok(n) => Some(n), - _ => None, - } + current_cluster = (self.next_cluster(block_cache, cluster).await).ok() } Err(Error::NotFound) } @@ -1014,10 +1008,7 @@ impl FatVolume { } } // Find the next cluster - current_cluster = match self.next_cluster(block_cache, cluster).await { - Ok(n) => Some(n), - _ => None, - } + current_cluster = (self.next_cluster(block_cache, cluster).await).ok() } // Ok, give up } @@ -1432,9 +1423,7 @@ where return Err(Error::BadBlockSize(bpb.bytes_per_block())); } // FirstDataSector = BPB_ResvdSecCnt + (BPB_NumFATs * FATSz) + RootDirSectors; - let root_dir_blocks = ((u32::from(bpb.root_entries_count()) * OnDiskDirEntry::LEN_U32) - + (Block::LEN_U32 - 1)) - / Block::LEN_U32; + let root_dir_blocks = (u32::from(bpb.root_entries_count()) * OnDiskDirEntry::LEN_U32).div_ceil(Block::LEN_U32); let first_root_dir_block = fat_start + BlockCount(u32::from(bpb.num_fats()) * bpb.fat_size()); let first_data_block = first_root_dir_block + BlockCount(root_dir_blocks); diff --git a/src/volume_mgr.rs b/src/volume_mgr.rs index 9f18074..c7f3d48 100644 --- a/src/volume_mgr.rs +++ b/src/volume_mgr.rs @@ -767,7 +767,7 @@ where if maybe_volume_name.is_none() && de.attributes == Attributes::create_from_fat(Attributes::VOLUME) { - maybe_volume_name = Some(unsafe { de.name.clone().to_volume_label() }) + maybe_volume_name = Some(unsafe { de.name.to_volume_label() }) } }) .await?;