From a49432e7c445f56a890a5dde97243a56bc027cd9 Mon Sep 17 00:00:00 2001 From: Bruno Bousquet <21108660+brunob45@users.noreply.github.com> Date: Sat, 19 Oct 2024 16:21:31 -0400 Subject: [PATCH] add support for `async BlockDevice` github workflow build is_sync fix workflow got renamed run `cargo fmt` --- .github/workflows/rust.yml | 4 +- Cargo.toml | 5 +- src/blockdevice.rs | 33 ++++-- src/fat/volume.rs | 197 ++++++++++++++++++++------------ src/filesystem/directory.rs | 39 ++++--- src/filesystem/files.rs | 43 ++++--- src/sdcard/mod.rs | 10 +- src/volume_mgr.rs | 219 +++++++++++++++++++++++------------- 8 files changed, 350 insertions(+), 200 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 75b29b7a..0921ba4f 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -18,6 +18,6 @@ jobs: steps: - uses: actions/checkout@v1 - name: Build - run: cargo build --no-default-features --features ${{matrix.features}} --verbose + run: cargo build --no-default-features --features ${{matrix.features}},is_sync --verbose - name: Run Tests - run: cargo test --no-default-features --features ${{matrix.features}} --verbose + run: cargo test --no-default-features --features ${{matrix.features}},is_sync --verbose diff --git a/Cargo.toml b/Cargo.toml index 45276c1c..de8574d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,8 +15,10 @@ byteorder = {version = "1", default-features = false} defmt = {version = "0.3", optional = true} embedded-hal = "1.0.0" embedded-io = "0.6.1" +embedded-io-async = "0.6.1" heapless = "^0.8" log = {version = "0.4", default-features = false, optional = true} +maybe-async = {version = "0.2"} [dev-dependencies] chrono = "0.4" @@ -27,6 +29,7 @@ hex-literal = "0.4.1" sha2 = "0.10" [features] -default = ["log"] +default = ["log", "is_sync"] defmt-log = ["dep:defmt"] log = ["dep:log"] +is_sync = ["maybe-async/is_sync"] diff --git a/src/blockdevice.rs b/src/blockdevice.rs index 618eda23..f4a45145 100644 --- a/src/blockdevice.rs +++ b/src/blockdevice.rs @@ -75,13 +75,19 @@ 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. +#[allow(async_fn_in_trait)] +#[maybe_async::maybe_async(AFIT)] 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) -> Result<(), Self::Error>; + async fn read( + &self, + blocks: &mut [Block], + start_block_idx: BlockIdx, + ) -> Result<(), Self::Error>; /// Write one or more blocks, starting at the given block index. - fn write(&self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Self::Error>; + async fn write(&self, blocks: &[Block], start_block_idx: BlockIdx) -> Result<(), Self::Error>; /// Determine how many blocks this device can hold. fn num_blocks(&self) -> Result; } @@ -110,31 +116,36 @@ where } /// Read a block, and return a reference to it. - pub fn read(&mut self, block_idx: BlockIdx) -> Result<&Block, D::Error> { + #[maybe_async::maybe_async] + 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> { + #[maybe_async::maybe_async] + 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"), - ) + #[maybe_async::maybe_async] + 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 } /// Access a blank sector diff --git a/src/fat/volume.rs b/src/fat/volume.rs index fbbedf9e..7f34f6ce 100644 --- a/src/fat/volume.rs +++ b/src/fat/volume.rs @@ -161,7 +161,8 @@ pub struct FatVolume { impl FatVolume { /// Write a new entry in the FAT - pub fn update_info_sector( + #[maybe_async::maybe_async] + pub async fn update_info_sector( &mut self, block_cache: &mut BlockCache, ) -> Result<(), Error> @@ -179,6 +180,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()); @@ -187,7 +189,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(()) @@ -202,7 +204,8 @@ impl FatVolume { } /// Write a new entry in the FAT - fn update_fat( + #[maybe_async::maybe_async] + async fn update_fat( &mut self, block_cache: &mut BlockCache, cluster: ClusterId, @@ -220,6 +223,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 { @@ -242,6 +246,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, @@ -259,12 +264,13 @@ impl FatVolume { } } trace!("Updating FAT"); - 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( + #[maybe_async::maybe_async] + pub(crate) async fn next_cluster( &self, block_cache: &mut BlockCache, cluster: ClusterId, @@ -281,7 +287,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 { @@ -304,7 +310,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; @@ -367,7 +373,8 @@ 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( + #[maybe_async::maybe_async] + pub(crate) async fn write_new_directory_entry( &mut self, block_cache: &mut BlockCache, time_source: &T, @@ -405,6 +412,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() @@ -424,19 +432,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) } @@ -466,6 +475,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. @@ -487,20 +497,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) } @@ -516,7 +526,8 @@ impl FatVolume { /// Calls callback `func` with every valid entry in the given directory. /// Useful for performing directory listings. - pub(crate) fn iterate_dir( + #[maybe_async::maybe_async] + pub(crate) async fn iterate_dir( &self, block_cache: &mut BlockCache, dir_info: &DirectoryInfo, @@ -529,14 +540,17 @@ impl FatVolume { match &self.fat_specific_info { FatSpecificInfo::Fat16(fat16_info) => { self.iterate_fat16(dir_info, fat16_info, block_cache, func) + .await } FatSpecificInfo::Fat32(fat32_info) => { self.iterate_fat32(dir_info, fat32_info, block_cache, func) + .await } } } - fn iterate_fat16( + #[maybe_async::maybe_async] + async fn iterate_fat16( &self, dir_info: &DirectoryInfo, fat16_info: &Fat16Info, @@ -567,7 +581,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() { @@ -582,7 +596,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) @@ -596,7 +610,8 @@ impl FatVolume { Ok(()) } - fn iterate_fat32( + #[maybe_async::maybe_async] + async fn iterate_fat32( &self, dir_info: &DirectoryInfo, fat32_info: &Fat32Info, @@ -617,7 +632,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() { @@ -631,7 +649,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, }; @@ -640,7 +658,8 @@ impl FatVolume { } /// Get an entry from the given directory - pub(crate) fn find_directory_entry( + #[maybe_async::maybe_async] + pub(crate) async fn find_directory_entry( &self, block_cache: &mut BlockCache, dir_info: &DirectoryInfo, @@ -671,18 +690,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) @@ -703,17 +720,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, } @@ -724,7 +739,8 @@ impl FatVolume { } /// Finds an entry in a given block of directory entries. - fn find_entry_in_block( + #[maybe_async::maybe_async] + async fn find_entry_in_block( &self, block_cache: &mut BlockCache, fat_type: FatType, @@ -735,7 +751,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() { @@ -752,7 +771,8 @@ impl FatVolume { } /// Delete an entry from the given directory - pub(crate) fn delete_directory_entry( + #[maybe_async::maybe_async] + pub(crate) async fn delete_directory_entry( &self, block_cache: &mut BlockCache, dir_info: &DirectoryInfo, @@ -785,7 +805,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 } @@ -798,7 +821,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) @@ -825,7 +848,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; @@ -838,7 +864,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, } @@ -855,7 +881,8 @@ 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( + #[maybe_async::maybe_async] + async fn delete_entry_in_block( &self, block_cache: &mut BlockCache, match_name: &ShortFileName, @@ -867,6 +894,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); @@ -878,14 +906,15 @@ 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( + #[maybe_async::maybe_async] + pub(crate) async fn find_next_free_cluster( &self, block_cache: &mut BlockCache, start_cluster: ClusterId, @@ -913,6 +942,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( @@ -943,6 +973,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( @@ -962,7 +993,8 @@ impl FatVolume { } /// Tries to allocate a cluster - pub(crate) fn alloc_cluster( + #[maybe_async::maybe_async] + pub(crate) async fn alloc_cluster( &mut self, block_cache: &mut BlockCache, prev_cluster: Option, @@ -982,7 +1014,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 => { @@ -991,39 +1025,42 @@ 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), }; - self.update_fat(block_cache, new_cluster, ClusterId::END_OF_FILE)?; + self.update_fat(block_cache, new_cluster, ClusterId::END_OF_FILE) + .await?; if let Some(cluster) = prev_cluster { trace!( "Updating old cluster {:?} to {:?} in FAT", 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); if let Some(ref mut number_free_cluster) = self.free_clusters_count { *number_free_cluster -= 1; @@ -1037,6 +1074,7 @@ impl FatVolume { block_cache .block_device() .write(&blocks, block_idx) + .await .map_err(Error::DeviceError)?; } } @@ -1045,7 +1083,8 @@ 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( + #[maybe_async::maybe_async] + pub(crate) async fn truncate_cluster_chain( &mut self, block_cache: &mut BlockCache, cluster: ClusterId, @@ -1058,7 +1097,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), @@ -1071,15 +1110,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), @@ -1092,7 +1132,8 @@ impl FatVolume { } /// Writes a Directory Entry to the disk - pub(crate) fn write_entry_to_disk( + #[maybe_async::maybe_async] + pub(crate) async fn write_entry_to_disk( &self, block_cache: &mut BlockCache, entry: &DirEntry, @@ -1107,20 +1148,22 @@ 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(()) } } /// 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( +#[maybe_async::maybe_async] +pub async fn parse_volume( block_cache: &mut BlockCache, lba_start: BlockIdx, num_blocks: BlockCount, @@ -1130,7 +1173,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)?; match bpb.fat_type { FatType::Fat16 => { @@ -1193,6 +1239,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 5cfc5f07..8e1d52c0 100644 --- a/src/filesystem/directory.rs +++ b/src/filesystem/directory.rs @@ -110,37 +110,41 @@ where /// Open a directory. /// /// You can then read the directory entries with `iterate_dir` and `open_file_in_dir`. - pub fn open_dir( + #[maybe_async::maybe_async] + 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> + #[maybe_async::maybe_async] + 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> + #[maybe_async::maybe_async] + 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. @@ -152,15 +156,17 @@ where /// object is already locked in order to do the iteration. /// /// - pub fn iterate_dir(&self, func: F) -> Result<(), Error> + #[maybe_async::maybe_async] + 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 } /// Open a file with the given full path. A file can only be opened once. - pub fn open_file_in_dir( + #[maybe_async::maybe_async] + pub async fn open_file_in_dir( &self, name: N, mode: crate::Mode, @@ -170,24 +176,31 @@ 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> + #[maybe_async::maybe_async] + 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> + #[maybe_async::maybe_async] + 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 870d85df..fb3a3ca0 100644 --- a/src/filesystem/files.rs +++ b/src/filesystem/files.rs @@ -3,8 +3,13 @@ use crate::{ filesystem::{ClusterId, DirEntry, Handle}, BlockDevice, Error, RawVolume, VolumeManager, }; + +#[cfg(feature = "is_sync")] use embedded_io::{ErrorType, Read, Seek, SeekFrom, Write}; +#[cfg(not(feature = "is_sync"))] +use embedded_io_async::{ErrorType, Read, Seek, SeekFrom, Write}; + /// A handle for an open file on disk. /// /// Do NOT drop this object! It doesn't hold a reference to the Volume Manager @@ -76,13 +81,15 @@ 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) + #[maybe_async::maybe_async] + 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) + #[maybe_async::maybe_async] + 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,18 @@ where } /// Flush any written data by updating the directory entry. - pub fn flush(&self) -> Result<(), Error> { - self.volume_mgr.flush_file(self.raw_file) + #[maybe_async::maybe_async] + 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); + #[maybe_async::maybe_async] + 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 +195,12 @@ impl< const MAX_VOLUMES: usize, > Read for File<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> { - fn read(&mut self, buf: &mut [u8]) -> Result { + #[maybe_async::maybe_async] + async fn read(&mut self, buf: &mut [u8]) -> Result { if buf.is_empty() { Ok(0) } else { - self.read(buf) + Self::read(self, buf).await } } } @@ -203,17 +213,19 @@ impl< const MAX_VOLUMES: usize, > Write for File<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> { - fn write(&mut self, buf: &[u8]) -> Result { + #[maybe_async::maybe_async] + async fn write(&mut self, buf: &[u8]) -> Result { if buf.is_empty() { Ok(0) } else { - self.write(buf)?; + Self::write(self, buf).await?; Ok(buf.len()) } } - fn flush(&mut self) -> Result<(), Self::Error> { - Self::flush(self) + #[maybe_async::maybe_async] + async fn flush(&mut self) -> Result<(), Self::Error> { + Self::flush(self).await } } @@ -225,7 +237,8 @@ impl< const MAX_VOLUMES: usize, > Seek for File<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> { - fn seek(&mut self, pos: SeekFrom) -> Result { + #[maybe_async::maybe_async] + 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/sdcard/mod.rs b/src/sdcard/mod.rs index 5c3c0381..974c6edb 100644 --- a/src/sdcard/mod.rs +++ b/src/sdcard/mod.rs @@ -162,7 +162,12 @@ 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> { + #[maybe_async::maybe_async] + 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 +177,8 @@ 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> { + #[maybe_async::maybe_async] + 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()?; diff --git a/src/volume_mgr.rs b/src/volume_mgr.rs index 75140d07..bf7d9678 100644 --- a/src/volume_mgr.rs +++ b/src/volume_mgr.rs @@ -107,11 +107,12 @@ 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( + #[maybe_async::maybe_async] + 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 +123,11 @@ 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> { + #[maybe_async::maybe_async] + 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 +157,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) @@ -196,7 +202,8 @@ where | PARTITION_ID_FAT32_LBA | PARTITION_ID_FAT16_LBA | PARTITION_ID_FAT16 => { - 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, @@ -242,7 +249,8 @@ 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( + #[maybe_async::maybe_async] + pub async fn open_dir( &self, parent_dir: RawDirectory, name: N, @@ -283,11 +291,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); @@ -355,7 +366,8 @@ where } /// Look in a directory for a named file. - pub fn find_directory_entry( + #[maybe_async::maybe_async] + pub async fn find_directory_entry( &self, directory: RawDirectory, name: N, @@ -376,6 +388,7 @@ where &data.open_dirs[directory_idx], &sfn, ) + .await } } } @@ -389,7 +402,12 @@ where /// object is already locked in order to do the iteration. /// /// - pub fn iterate_dir(&self, directory: RawDirectory, func: F) -> Result<(), Error> + #[maybe_async::maybe_async] + pub async fn iterate_dir( + &self, + directory: RawDirectory, + func: F, + ) -> Result<(), Error> where F: FnMut(&DirEntry), { @@ -401,12 +419,14 @@ where match &data.open_volumes[volume_idx].volume_type { VolumeType::Fat(fat) => { fat.iterate_dir(&mut data.block_cache, &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( + #[maybe_async::maybe_async] + pub async fn open_file_in_dir( &self, directory: RawDirectory, name: N, @@ -430,11 +450,14 @@ where let sfn = name.to_short_filename().map_err(Error::FilenameError)?; let dir_entry = match &volume_info.volume_type { - VolumeType::Fat(fat) => fat.find_directory_entry( - &mut data.block_cache, - &data.open_dirs[directory_idx], - &sfn, - ), + VolumeType::Fat(fat) => { + fat.find_directory_entry( + &mut data.block_cache, + &data.open_dirs[directory_idx], + &sfn, + ) + .await + } }; let dir_entry = match dir_entry { @@ -475,13 +498,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()); @@ -558,16 +584,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?; } }; @@ -587,7 +617,8 @@ where } /// Delete a closed file with the given filename, if it exists. - pub fn delete_file_in_dir( + #[maybe_async::maybe_async] + pub async fn delete_file_in_dir( &self, directory: RawDirectory, name: N, @@ -604,8 +635,11 @@ 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() { return Err(Error::DeleteDirAsFile); @@ -618,7 +652,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? } } @@ -629,7 +664,8 @@ 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( + #[maybe_async::maybe_async] + pub async fn get_root_volume_label( &self, raw_volume: RawVolume, ) -> Result, Error> { @@ -653,13 +689,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", @@ -670,7 +708,8 @@ where } /// Read from an open file. - pub fn read(&self, file: RawFile, buffer: &mut [u8]) -> Result> { + #[maybe_async::maybe_async] + 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(); @@ -684,17 +723,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) @@ -712,7 +754,8 @@ where } /// Write to a open file. - pub fn write(&self, file: RawFile, buffer: &[u8]) -> Result<(), Error> { + #[maybe_async::maybe_async] + pub async fn write(&self, file: RawFile, buffer: &[u8]) -> Result<(), Error> { #[cfg(feature = "defmt-log")] debug!("write(file={:?}, buffer={:x}", file, buffer); @@ -738,7 +781,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!( @@ -768,12 +812,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={}", @@ -791,6 +838,7 @@ where Some(current_cluster.1), false, ) + .await .is_err() { return Err(Error::DiskFull); @@ -803,6 +851,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 @@ -816,6 +865,7 @@ where debug!("Reading for partial block write"); data.block_cache .read_mut(block_idx) + .await .map_err(Error::DeviceError)? } else { data.block_cache.blank_mut(block_idx) @@ -823,7 +873,7 @@ where 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; @@ -844,8 +894,9 @@ 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); + #[maybe_async::maybe_async] + 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); @@ -853,7 +904,8 @@ where } /// Flush (update the entry) for a file with the given raw file handle. - pub fn flush_file(&self, file: RawFile) -> Result<(), Error> { + #[maybe_async::maybe_async] + 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(); @@ -864,16 +916,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?; } }; } @@ -948,7 +998,8 @@ where } /// Create a directory in a given directory. - pub fn make_dir_in_dir( + #[maybe_async::maybe_async] + pub async fn make_dir_in_dir( &self, directory: RawDirectory, name: N, @@ -981,6 +1032,7 @@ where let maybe_dir_entry = match &volume_info.volume_type { VolumeType::Fat(fat) => { fat.find_directory_entry(&mut data.block_cache, parent_directory_info, &sfn) + .await } }; @@ -1006,18 +1058,22 @@ where match &mut data.open_volumes[volume_idx].volume_type { VolumeType::Fat(fat) => { debug!("Making dir entry"); - let mut new_dir_entry_in_parent = fat.write_new_directory_entry( - &mut data.block_cache, - &self.time_source, - parent_directory_info.cluster, - sfn, - att, - )?; + let mut new_dir_entry_in_parent = fat + .write_new_directory_entry( + &mut data.block_cache, + &self.time_source, + parent_directory_info.cluster, + sfn, + att, + ) + .await?; if new_dir_entry_in_parent.cluster == ClusterId::EMPTY { - new_dir_entry_in_parent.cluster = - fat.alloc_cluster(&mut data.block_cache, None, false)?; + new_dir_entry_in_parent.cluster = fat + .alloc_cluster(&mut data.block_cache, None, false) + .await?; // update the parent dir with the cluster of the new dir - fat.write_entry_to_disk(&mut data.block_cache, &new_dir_entry_in_parent)?; + fat.write_entry_to_disk(&mut data.block_cache, &new_dir_entry_in_parent) + .await?; } let new_dir_start_block = fat.cluster_to_block(new_dir_entry_in_parent.cluster); debug!("Made new dir entry {:?}", new_dir_entry_in_parent); @@ -1068,14 +1124,14 @@ where block[offset..offset + fat::OnDiskDirEntry::LEN] .copy_from_slice(&dot_dot_entry_in_child.serialize(fat_type)[..]); - data.block_cache.write_back()?; + data.block_cache.write_back().await?; for block_idx in new_dir_start_block .range(BlockCount(u32::from(fat.blocks_per_cluster))) .skip(1) { let _block = data.block_cache.blank_mut(block_idx); - data.block_cache.write_back()?; + data.block_cache.write_back().await?; } } }; @@ -1169,7 +1225,8 @@ 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( + #[maybe_async::maybe_async] + async fn find_data_on_disk( &mut self, volume_idx: usize, start: &mut (u32, ClusterId), @@ -1195,7 +1252,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; }