From e9590edf40ba44c8cb348fa4d6aa86e20008fd3d Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Thu, 26 May 2022 05:45:34 +0200 Subject: [PATCH 01/13] Allow clearing FAT dirty flag. --- src/fs.rs | 11 ++++++++++- src/table.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/src/fs.rs b/src/fs.rs index 996a68d..08841d7 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -16,7 +16,7 @@ use crate::error::Error; use crate::file::File; use crate::io::{self, IoBase, Read, ReadLeExt, Seek, SeekFrom, Write, WriteLeExt}; use crate::table::{ - alloc_cluster, count_free_clusters, format_fat, read_fat_flags, ClusterIterator, RESERVED_FAT_ENTRIES, + alloc_cluster, count_free_clusters, format_fat, read_fat_flags, write_fat_flags, ClusterIterator, RESERVED_FAT_ENTRIES, }; use crate::time::{DefaultTimeProvider, TimeProvider}; @@ -605,6 +605,15 @@ impl FileSystem { Ok(()) } + pub fn set_fat_dirty_flag(&self, dirty: bool) -> Result<(), Error> { + let mut fat_status = read_fat_flags(&mut self.fat_slice(), self.fat_type)?; + fat_status.dirty = dirty; + + write_fat_flags(&mut self.fat_slice(), self.fat_type, fat_status)?; + + Ok(()) + } + /// Returns a root directory object allowing for futher penetration of a filesystem structure. pub fn root_dir(&self) -> Dir { trace!("root_dir"); diff --git a/src/table.rs b/src/table.rs index 3afbb63..878f4c9 100644 --- a/src/table.rs +++ b/src/table.rs @@ -152,6 +152,12 @@ where Ok(new_cluster) } +const FAT16_IO_ERROR_BIT: u32 = 1 << 14; +const FAT16_DIRTY_BIT: u32 = 1 << 15; + +const FAT32_IO_ERROR_BIT: u32 = 1 << 26; +const FAT32_DIRTY_BIT: u32 = 1 << 27; + pub(crate) fn read_fat_flags(fat: &mut S, fat_type: FatType) -> Result> where S: Read + Seek, @@ -166,17 +172,57 @@ where }; let dirty = match fat_type { FatType::Fat12 => false, - FatType::Fat16 => val & (1 << 15) == 0, - FatType::Fat32 => val & (1 << 27) == 0, + FatType::Fat16 => val & FAT16_DIRTY_BIT == 0, + FatType::Fat32 => val & FAT32_DIRTY_BIT == 0, }; let io_error = match fat_type { FatType::Fat12 => false, - FatType::Fat16 => val & (1 << 14) == 0, - FatType::Fat32 => val & (1 << 26) == 0, + FatType::Fat16 => val & FAT16_IO_ERROR_BIT == 0, + FatType::Fat32 => val & FAT32_IO_ERROR_BIT == 0, }; Ok(FsStatusFlags { dirty, io_error }) } +pub(crate) fn write_fat_flags(fat: &mut S, fat_type: FatType, fat_status: FsStatusFlags) -> Result<(), Error> +where + S: Read + Write + Seek, + E: IoError, + Error: From, +{ + match fat_type { + FatType::Fat12 => { + Ok(()) + }, + FatType::Fat16 => { + let mut val = 0; + + if fat_status.dirty { + val |= FAT16_DIRTY_BIT; + } + + if fat_status.io_error { + val |= FAT16_IO_ERROR_BIT; + } + + Fat16::set(fat, 1, FatValue::Data(!val)) + }, + FatType::Fat32 => { + + let mut val = 0; + + if fat_status.dirty { + val |= FAT32_DIRTY_BIT; + } + + if fat_status.io_error { + val |= FAT32_IO_ERROR_BIT; + } + + Fat32::set(fat, 1, FatValue::Data(!val)) + }, + } +} + pub(crate) fn count_free_clusters(fat: &mut S, fat_type: FatType, total_clusters: u32) -> Result> where S: Read + Seek, From 215aac21ed164d6a18352e37d9940d2969ce28fe Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sat, 11 Jun 2022 11:44:56 +0200 Subject: [PATCH 02/13] Add option for clearing dirty flag. --- src/error.rs | 5 +- src/fs.rs | 178 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 121 insertions(+), 62 deletions(-) diff --git a/src/error.rs b/src/error.rs index 7a8bd9a..ecf1156 100644 --- a/src/error.rs +++ b/src/error.rs @@ -20,6 +20,8 @@ pub enum Error { DirectoryIsNotEmpty, /// File system internal structures are corrupted/invalid. CorruptedFileSystem, + /// File system is marked dirty. + DirtyFileSystem, /// There is not enough free space on the storage to finish the requested operation. NotEnoughSpace, /// The provided file name is either too long or empty. @@ -47,7 +49,7 @@ impl From> for std::io::Error { | Error::DirectoryIsNotEmpty => Self::new(std::io::ErrorKind::InvalidInput, error), Error::NotFound => Self::new(std::io::ErrorKind::NotFound, error), Error::AlreadyExists => Self::new(std::io::ErrorKind::AlreadyExists, error), - Error::CorruptedFileSystem => Self::new(std::io::ErrorKind::InvalidData, error), + Error::CorruptedFileSystem | Error::DirtyFileSystem => Self::new(std::io::ErrorKind::InvalidData, error), } } } @@ -66,6 +68,7 @@ impl core::fmt::Display for Error { Error::NotFound => write!(f, "No such file or directory"), Error::AlreadyExists => write!(f, "File or directory already exists"), Error::CorruptedFileSystem => write!(f, "Corrupted file system"), + Error::DirtyFileSystem => write!(f, "Dirty file system"), } } } diff --git a/src/fs.rs b/src/fs.rs index 08841d7..9855cc0 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -1,6 +1,6 @@ #[cfg(all(not(feature = "std"), feature = "alloc"))] use alloc::string::String; -use core::borrow::BorrowMut; +use core::borrow::{Borrow, BorrowMut}; use core::cell::{Cell, RefCell}; use core::char; use core::cmp; @@ -10,15 +10,17 @@ use core::marker::PhantomData; use core::u32; use crate::boot_sector::{format_boot_sector, BiosParameterBlock, BootSector}; -use crate::dir::{Dir, DirRawStream}; +use crate::dir::{self, Dir, DirRawStream}; use crate::dir_entry::{DirFileEntryData, FileAttributes, SFN_PADDING, SFN_SIZE}; use crate::error::Error; use crate::file::File; use crate::io::{self, IoBase, Read, ReadLeExt, Seek, SeekFrom, Write, WriteLeExt}; use crate::table::{ - alloc_cluster, count_free_clusters, format_fat, read_fat_flags, write_fat_flags, ClusterIterator, RESERVED_FAT_ENTRIES, + alloc_cluster, count_free_clusters, format_fat, read_fat_flags, write_fat_flags, ClusterIterator, + RESERVED_FAT_ENTRIES, }; use crate::time::{DefaultTimeProvider, TimeProvider}; +use crate::IoError; // FAT implementation based on: // http://wiki.osdev.org/FAT @@ -89,17 +91,17 @@ impl FsStatusFlags { /// /// Dirty flag means volume has been suddenly ejected from filesystem without unmounting. #[must_use] - pub fn dirty(&self) -> bool { + pub const fn dirty(&self) -> bool { self.dirty } /// Checks if the volume has the IO Error flag active. #[must_use] - pub fn io_error(&self) -> bool { + pub const fn io_error(&self) -> bool { self.io_error } - fn encode(self) -> u8 { + const fn encode(self) -> u8 { let mut res = 0_u8; if self.dirty { res |= 1; @@ -110,7 +112,7 @@ impl FsStatusFlags { res } - pub(crate) fn decode(flags: u8) -> Self { + pub(crate) const fn decode(flags: u8) -> Self { Self { dirty: flags & 1 != 0, io_error: flags & 2 != 0, @@ -238,6 +240,7 @@ impl FsInfoSector { #[derive(Copy, Clone, Debug, Default)] pub struct FsOptions { pub(crate) update_accessed_date: bool, + pub(crate) ignore_dirty_flag: bool, pub(crate) oem_cp_converter: OCC, pub(crate) time_provider: TP, } @@ -248,6 +251,7 @@ impl FsOptions { pub fn new() -> Self { Self { update_accessed_date: false, + ignore_dirty_flag: false, oem_cp_converter: LossyOemCpConverter::new(), time_provider: DefaultTimeProvider::new(), } @@ -262,10 +266,17 @@ impl FsOptions { self } + /// Ignore a dirty file system and clear the dirty flag when mounting it. + pub fn ignore_dirty_flag(mut self, enabled: bool) -> Self { + self.ignore_dirty_flag = enabled; + self + } + /// Changes default OEM code page encoder-decoder. pub fn oem_cp_converter(self, oem_cp_converter: OCC2) -> FsOptions { FsOptions:: { update_accessed_date: self.update_accessed_date, + ignore_dirty_flag: self.ignore_dirty_flag, oem_cp_converter, time_provider: self.time_provider, } @@ -275,6 +286,7 @@ impl FsOptions { pub fn time_provider(self, time_provider: TP2) -> FsOptions { FsOptions:: { update_accessed_date: self.update_accessed_date, + ignore_dirty_flag: self.ignore_dirty_flag, oem_cp_converter: self.oem_cp_converter, time_provider, } @@ -388,18 +400,7 @@ impl FileSystem { FsInfoSector::default() }; - // if dirty flag is set completly ignore free_cluster_count in FSInfo - if bpb.status_flags().dirty { - fs_info.free_cluster_count = None; - } - - // Validate the numbers stored in the free_cluster_count and next_free_cluster are within bounds for volume - fs_info.validate_and_fix(total_clusters); - - // return FileSystem struct - let status_flags = bpb.status_flags(); - trace!("FileSystem::new end"); - Ok(Self { + let mut fs = Self { disk: RefCell::new(disk), options, fat_type, @@ -408,8 +409,34 @@ impl FileSystem { root_dir_sectors, total_clusters, fs_info: RefCell::new(fs_info), - current_status_flags: Cell::new(status_flags), - }) + current_status_flags: Cell::new(FsStatusFlags { + dirty: false, + io_error: false, + }), + }; + + { + let status_flags = fs.read_status_flags()?; + let mut fs_info = fs.fs_info.borrow_mut(); + + if status_flags.dirty() { + // if dirty flag is set completly ignore free_cluster_count in FSInfo + fs_info.free_cluster_count = None; + + if fs.options.ignore_dirty_flag { + warn!("File system is dirty, clearing dirty flag."); + fs.set_dirty_flag(false)?; + } else { + return Err(Error::DirtyFileSystem); + } + } + + // Validate the numbers stored in the free_cluster_count and next_free_cluster are within bounds for volume + fs_info.validate_and_fix(total_clusters); + } + + trace!("FileSystem::new end"); + Ok(fs) } /// Returns a type of File Allocation Table (FAT) used by this filesystem. @@ -460,7 +487,7 @@ impl FileSystem { self.bpb.clusters_from_bytes(bytes) } - fn fat_slice(&self) -> impl ReadWriteSeek> + '_ { + fn fat_slice(&self) -> DiskSlice, IO::Error> { let io = FsIoAdapter { fs: self }; fat_slice(io, &self.bpb) } @@ -468,7 +495,7 @@ impl FileSystem { pub(crate) fn cluster_iter( &self, cluster: u32, - ) -> ClusterIterator> + '_, IO::Error> { + ) -> ClusterIterator, IO::Error>, IO::Error> { let disk_slice = self.fat_slice(); ClusterIterator::new(disk_slice, self.fat_type, cluster) } @@ -580,17 +607,28 @@ impl FileSystem { Ok(()) } - pub(crate) fn set_dirty_flag(&self, dirty: bool) -> Result<(), IO::Error> { - // Do not overwrite flags read from BPB on mount - let mut flags = self.bpb.status_flags(); - flags.dirty |= dirty; - // Check if flags has changed - let current_flags = self.current_status_flags.get(); - if flags == current_flags { - // Nothing to do + pub(crate) fn set_dirty_flag(&self, dirty: bool) -> Result<(), Error> { + let mut status_flags = self.current_status_flags.get(); + + if status_flags.dirty == dirty { + // Dirty flag did not change. return Ok(()); } - let encoded = flags.encode(); + + status_flags.dirty = dirty; + + self.set_bpb_status_flags(status_flags)?; + self.set_fat_status_flags(status_flags)?; + + self.current_status_flags.set(status_flags); + + Ok(()) + } + + #[inline] + fn set_bpb_status_flags(&self, status_flags: FsStatusFlags) -> Result<(), Error> { + let encoded = status_flags.encode(); + // Note: only one field is written to avoid rewriting entire boot-sector which could be dangerous // Compute reserver_1 field offset and write new flags let offset = if self.fat_type() == FatType::Fat32 { @@ -598,20 +636,17 @@ impl FileSystem { } else { 0x025 }; + let mut disk = self.disk.borrow_mut(); disk.seek(io::SeekFrom::Start(offset))?; disk.write_u8(encoded)?; - self.current_status_flags.set(flags); + Ok(()) } - pub fn set_fat_dirty_flag(&self, dirty: bool) -> Result<(), Error> { - let mut fat_status = read_fat_flags(&mut self.fat_slice(), self.fat_type)?; - fat_status.dirty = dirty; - - write_fat_flags(&mut self.fat_slice(), self.fat_type, fat_status)?; - - Ok(()) + #[inline] + fn set_fat_status_flags(&self, status_flags: FsStatusFlags) -> Result<(), Error> { + write_fat_flags(&mut self.fat_slice(), self.fat_type, status_flags) } /// Returns a root directory object allowing for futher penetration of a filesystem structure. @@ -705,12 +740,12 @@ pub(crate) struct FsIoAdapter<'a, IO: ReadWriteSeek, TP, OCC> { } impl IoBase for FsIoAdapter<'_, IO, TP, OCC> { - type Error = IO::Error; + type Error = Error; } impl Read for FsIoAdapter<'_, IO, TP, OCC> { fn read(&mut self, buf: &mut [u8]) -> Result { - self.fs.disk.borrow_mut().read(buf) + Ok(self.fs.disk.borrow_mut().read(buf)?) } } @@ -724,13 +759,13 @@ impl Write for FsIoAdapter<'_, IO, TP, OCC> { } fn flush(&mut self) -> Result<(), Self::Error> { - self.fs.disk.borrow_mut().flush() + Ok(self.fs.disk.borrow_mut().flush()?) } } impl Seek for FsIoAdapter<'_, IO, TP, OCC> { fn seek(&mut self, pos: SeekFrom) -> Result { - self.fs.disk.borrow_mut().seek(pos) + Ok(self.fs.disk.borrow_mut().seek(pos)?) } } @@ -741,10 +776,7 @@ impl Clone for FsIoAdapter<'_, IO, TP, OCC> { } } -fn fat_slice>( - io: B, - bpb: &BiosParameterBlock, -) -> impl ReadWriteSeek> { +fn fat_slice, E, S: ReadWriteSeek>(io: B, bpb: &BiosParameterBlock) -> DiskSlice { let sectors_per_fat = bpb.sectors_per_fat(); let mirroring_enabled = bpb.mirroring_enabled(); let (fat_first_sector, mirrors) = if mirroring_enabled { @@ -757,16 +789,17 @@ fn fat_slice>( DiskSlice::from_sectors(fat_first_sector, sectors_per_fat, mirrors, bpb, io) } -pub(crate) struct DiskSlice { +pub(crate) struct DiskSlice { begin: u64, size: u64, offset: u64, mirrors: u8, inner: B, - phantom: PhantomData, + phantom_s: PhantomData, + phantom_e: PhantomData, } -impl, S: ReadWriteSeek> DiskSlice { +impl, E, S: ReadWriteSeek> DiskSlice { pub(crate) fn new(begin: u64, size: u64, mirrors: u8, inner: B) -> Self { Self { begin, @@ -774,7 +807,8 @@ impl, S: ReadWriteSeek> DiskSlice { mirrors, inner, offset: 0, - phantom: PhantomData, + phantom_s: PhantomData, + phantom_e: PhantomData, } } @@ -793,7 +827,7 @@ impl, S: ReadWriteSeek> DiskSlice { } // Note: derive cannot be used because of invalid bounds. See: https://github.com/rust-lang/rust/issues/26925 -impl Clone for DiskSlice { +impl Clone for DiskSlice { fn clone(&self) -> Self { Self { begin: self.begin, @@ -802,16 +836,26 @@ impl Clone for DiskSlice { mirrors: self.mirrors, inner: self.inner.clone(), // phantom is needed to add type bounds on the storage type - phantom: PhantomData, + phantom_s: PhantomData, + phantom_e: PhantomData, } } } -impl IoBase for DiskSlice { - type Error = Error; +impl IoBase for DiskSlice +where + E: IoError, +{ + type Error = Error; } -impl, S: Read + Seek> Read for DiskSlice { +impl Read for DiskSlice +where + S: Read + Seek, + B: BorrowMut, + E: IoError, + Error: From, +{ fn read(&mut self, buf: &mut [u8]) -> Result { let offset = self.begin + self.offset; let read_size = cmp::min(self.size - self.offset, buf.len() as u64) as usize; @@ -822,7 +866,13 @@ impl, S: Read + Seek> Read for DiskSlice { } } -impl, S: Write + Seek> Write for DiskSlice { +impl, E, S> Write for DiskSlice +where + S: Write + Seek, + B: BorrowMut, + E: IoError, + Error: From, +{ fn write(&mut self, buf: &[u8]) -> Result { let offset = self.begin + self.offset; let write_size = cmp::min(self.size - self.offset, buf.len() as u64) as usize; @@ -844,7 +894,13 @@ impl, S: Write + Seek> Write for DiskSlice { } } -impl Seek for DiskSlice { +impl Seek for DiskSlice +where + S: IoBase, + B: BorrowMut, + E: IoError, + Error: From, +{ fn seek(&mut self, pos: SeekFrom) -> Result { let new_offset_opt: Option = match pos { SeekFrom::Current(x) => i64::try_from(self.offset) @@ -1176,7 +1232,7 @@ pub fn format_volume(storage: &mut S, options: FormatVolumeOpt storage.seek(SeekFrom::Start(fat_pos))?; write_zeros(storage, bpb.bytes_from_sectors(sectors_per_all_fats))?; { - let mut fat_slice = fat_slice::(storage, bpb); + let mut fat_slice = fat_slice::<&mut S, S::Error, S>(storage, bpb); let sectors_per_fat = bpb.sectors_per_fat(); let bytes_per_fat = bpb.bytes_from_sectors(sectors_per_fat); format_fat(&mut fat_slice, fat_type, bpb.media, bytes_per_fat, bpb.total_clusters())?; @@ -1190,7 +1246,7 @@ pub fn format_volume(storage: &mut S, options: FormatVolumeOpt write_zeros(storage, bpb.bytes_from_sectors(root_dir_sectors))?; if fat_type == FatType::Fat32 { let root_dir_first_cluster = { - let mut fat_slice = fat_slice::(storage, bpb); + let mut fat_slice = fat_slice::<&mut S, S::Error, S>(storage, bpb); alloc_cluster(&mut fat_slice, fat_type, None, None, 1)? }; assert!(root_dir_first_cluster == bpb.root_dir_first_cluster); From 3b927161903df1e38c98b3cfe648288d0fc3cd4e Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sat, 11 Jun 2022 11:47:02 +0200 Subject: [PATCH 03/13] Format code. --- src/table.rs | 61 +++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/src/table.rs b/src/table.rs index 878f4c9..5c66d94 100644 --- a/src/table.rs +++ b/src/table.rs @@ -189,38 +189,35 @@ where E: IoError, Error: From, { - match fat_type { - FatType::Fat12 => { - Ok(()) - }, - FatType::Fat16 => { - let mut val = 0; - - if fat_status.dirty { - val |= FAT16_DIRTY_BIT; - } - - if fat_status.io_error { - val |= FAT16_IO_ERROR_BIT; - } - - Fat16::set(fat, 1, FatValue::Data(!val)) - }, - FatType::Fat32 => { - - let mut val = 0; - - if fat_status.dirty { - val |= FAT32_DIRTY_BIT; - } - - if fat_status.io_error { - val |= FAT32_IO_ERROR_BIT; - } - - Fat32::set(fat, 1, FatValue::Data(!val)) - }, - } + match fat_type { + FatType::Fat12 => Ok(()), + FatType::Fat16 => { + let mut val = 0; + + if fat_status.dirty { + val |= FAT16_DIRTY_BIT; + } + + if fat_status.io_error { + val |= FAT16_IO_ERROR_BIT; + } + + Fat16::set(fat, 1, FatValue::Data(!val)) + } + FatType::Fat32 => { + let mut val = 0; + + if fat_status.dirty { + val |= FAT32_DIRTY_BIT; + } + + if fat_status.io_error { + val |= FAT32_IO_ERROR_BIT; + } + + Fat32::set(fat, 1, FatValue::Data(!val)) + } + } } pub(crate) fn count_free_clusters(fat: &mut S, fat_type: FatType, total_clusters: u32) -> Result> From f6976a9feeea70ebed72262ad56cfacd4b28ece3 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sat, 11 Jun 2022 11:50:47 +0200 Subject: [PATCH 04/13] Remove unused import. --- src/fs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fs.rs b/src/fs.rs index 9855cc0..e71e59a 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -1,6 +1,6 @@ #[cfg(all(not(feature = "std"), feature = "alloc"))] use alloc::string::String; -use core::borrow::{Borrow, BorrowMut}; +use core::borrow::BorrowMut; use core::cell::{Cell, RefCell}; use core::char; use core::cmp; @@ -10,7 +10,7 @@ use core::marker::PhantomData; use core::u32; use crate::boot_sector::{format_boot_sector, BiosParameterBlock, BootSector}; -use crate::dir::{self, Dir, DirRawStream}; +use crate::dir::{Dir, DirRawStream}; use crate::dir_entry::{DirFileEntryData, FileAttributes, SFN_PADDING, SFN_SIZE}; use crate::error::Error; use crate::file::File; From 4a6f78da31325c2f73af5253eedac7740d911e45 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sat, 11 Jun 2022 11:58:50 +0200 Subject: [PATCH 05/13] Reorder types. --- src/fs.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/fs.rs b/src/fs.rs index e71e59a..6465e1b 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -795,8 +795,8 @@ pub(crate) struct DiskSlice { offset: u64, mirrors: u8, inner: B, - phantom_s: PhantomData, phantom_e: PhantomData, + phantom_s: PhantomData, } impl, E, S: ReadWriteSeek> DiskSlice { @@ -807,8 +807,8 @@ impl, E, S: ReadWriteSeek> DiskSlice { mirrors, inner, offset: 0, - phantom_s: PhantomData, phantom_e: PhantomData, + phantom_s: PhantomData, } } @@ -836,8 +836,8 @@ impl Clone for DiskSlice { mirrors: self.mirrors, inner: self.inner.clone(), // phantom is needed to add type bounds on the storage type - phantom_s: PhantomData, phantom_e: PhantomData, + phantom_s: PhantomData, } } } @@ -851,9 +851,9 @@ where impl Read for DiskSlice where - S: Read + Seek, B: BorrowMut, E: IoError, + S: Read + Seek, Error: From, { fn read(&mut self, buf: &mut [u8]) -> Result { @@ -868,9 +868,9 @@ where impl, E, S> Write for DiskSlice where - S: Write + Seek, B: BorrowMut, E: IoError, + S: Write + Seek, Error: From, { fn write(&mut self, buf: &[u8]) -> Result { @@ -896,9 +896,9 @@ where impl Seek for DiskSlice where - S: IoBase, B: BorrowMut, E: IoError, + S: IoBase, Error: From, { fn seek(&mut self, pos: SeekFrom) -> Result { From 9a28fc3e77e8b925fcf52f48cc0f2c5dc7c454f5 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sat, 11 Jun 2022 12:51:19 +0200 Subject: [PATCH 06/13] Fix warnings. --- src/fs.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/fs.rs b/src/fs.rs index 6465e1b..b51e900 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -267,12 +267,14 @@ impl FsOptions { } /// Ignore a dirty file system and clear the dirty flag when mounting it. + #[must_use] pub fn ignore_dirty_flag(mut self, enabled: bool) -> Self { self.ignore_dirty_flag = enabled; self } /// Changes default OEM code page encoder-decoder. + #[must_use] pub fn oem_cp_converter(self, oem_cp_converter: OCC2) -> FsOptions { FsOptions:: { update_accessed_date: self.update_accessed_date, @@ -283,6 +285,7 @@ impl FsOptions { } /// Changes default time provider. + #[must_use] pub fn time_provider(self, time_provider: TP2) -> FsOptions { FsOptions:: { update_accessed_date: self.update_accessed_date, @@ -353,6 +356,8 @@ impl IntoStorage = DiskSlice, E>; + impl FileSystem { /// Creates a new filesystem object instance. /// @@ -393,14 +398,14 @@ impl FileSystem { let fat_type = FatType::from_clusters(total_clusters); // read FSInfo sector if this is FAT32 - let mut fs_info = if fat_type == FatType::Fat32 { + let fs_info = if fat_type == FatType::Fat32 { disk.seek(SeekFrom::Start(bpb.bytes_from_sectors(bpb.fs_info_sector())))?; FsInfoSector::deserialize(&mut disk)? } else { FsInfoSector::default() }; - let mut fs = Self { + let fs = Self { disk: RefCell::new(disk), options, fat_type, @@ -487,7 +492,7 @@ impl FileSystem { self.bpb.clusters_from_bytes(bytes) } - fn fat_slice(&self) -> DiskSlice, IO::Error> { + fn fat_slice(&self) -> FatSlice<'_, IO, TP, OCC, IO::Error> { let io = FsIoAdapter { fs: self }; fat_slice(io, &self.bpb) } @@ -495,7 +500,7 @@ impl FileSystem { pub(crate) fn cluster_iter( &self, cluster: u32, - ) -> ClusterIterator, IO::Error>, IO::Error> { + ) -> ClusterIterator, IO::Error> { let disk_slice = self.fat_slice(); ClusterIterator::new(disk_slice, self.fat_type, cluster) } @@ -866,7 +871,7 @@ where } } -impl, E, S> Write for DiskSlice +impl Write for DiskSlice where B: BorrowMut, E: IoError, From a4b389fbee0b77750cb6f61874c6527cb6b5ad69 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sat, 11 Jun 2022 13:20:23 +0200 Subject: [PATCH 07/13] Fix type. --- src/dir.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dir.rs b/src/dir.rs index 4f48583..767c73e 100644 --- a/src/dir.rs +++ b/src/dir.rs @@ -23,7 +23,7 @@ const LFN_PADDING: u16 = 0xFFFF; pub(crate) enum DirRawStream<'a, IO: ReadWriteSeek, TP, OCC> { File(File<'a, IO, TP, OCC>), - Root(DiskSlice, FsIoAdapter<'a, IO, TP, OCC>>), + Root(DiskSlice, IO::Error>), } impl DirRawStream<'_, IO, TP, OCC> { From 2ce03f476aad7e161d50927fce703b3f9455d2a2 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sat, 11 Jun 2022 15:27:44 +0200 Subject: [PATCH 08/13] Try fixing `BorrowMutError`. --- src/fs.rs | 61 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/src/fs.rs b/src/fs.rs index b51e900..c475a8d 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -398,14 +398,24 @@ impl FileSystem { let fat_type = FatType::from_clusters(total_clusters); // read FSInfo sector if this is FAT32 - let fs_info = if fat_type == FatType::Fat32 { + let mut fs_info = if fat_type == FatType::Fat32 { disk.seek(SeekFrom::Start(bpb.bytes_from_sectors(bpb.fs_info_sector())))?; FsInfoSector::deserialize(&mut disk)? } else { FsInfoSector::default() }; - let fs = Self { + let mut bpb_status_flags = bpb.status_flags(); + + // if dirty flag is set completly ignore free_cluster_count in FSInfo + if bpb_status_flags.dirty() { + fs_info.free_cluster_count = None; + } + + // Validate the numbers stored in the free_cluster_count and next_free_cluster are within bounds for volume + fs_info.validate_and_fix(total_clusters); + + let mut fs = Self { disk: RefCell::new(disk), options, fat_type, @@ -414,30 +424,29 @@ impl FileSystem { root_dir_sectors, total_clusters, fs_info: RefCell::new(fs_info), - current_status_flags: Cell::new(FsStatusFlags { - dirty: false, - io_error: false, - }), + current_status_flags: Cell::new(bpb_status_flags), }; - { - let status_flags = fs.read_status_flags()?; - let mut fs_info = fs.fs_info.borrow_mut(); - - if status_flags.dirty() { - // if dirty flag is set completly ignore free_cluster_count in FSInfo - fs_info.free_cluster_count = None; - - if fs.options.ignore_dirty_flag { - warn!("File system is dirty, clearing dirty flag."); - fs.set_dirty_flag(false)?; - } else { - return Err(Error::DirtyFileSystem); - } + if bpb_status_flags.dirty() { + if fs.options.ignore_dirty_flag { + warn!("BPB is dirty, clearing dirty flag."); + bpb_status_flags.dirty = false; + fs.set_bpb_status_flags(bpb_status_flags)?; + fs.current_status_flags.get_mut().dirty = false; + } else { + return Err(Error::DirtyFileSystem) } + } - // Validate the numbers stored in the free_cluster_count and next_free_cluster are within bounds for volume - fs_info.validate_and_fix(total_clusters); + let mut fat_status_flags = fs.read_fat_status_flags()?; + if fat_status_flags.dirty() { + if fs.options.ignore_dirty_flag { + warn!("FAT is dirty, clearing dirty flag."); + fat_status_flags.dirty = false; + fs.set_fat_status_flags(fat_status_flags)?; + } else { + return Err(Error::DirtyFileSystem); + } } trace!("FileSystem::new end"); @@ -546,7 +555,7 @@ impl FileSystem { /// `Error::Io` will be returned if the underlying storage object returned an I/O error. pub fn read_status_flags(&self) -> Result> { let bpb_status = self.bpb.status_flags(); - let fat_status = read_fat_flags(&mut self.fat_slice(), self.fat_type)?; + let fat_status = self.read_fat_status_flags()?; Ok(FsStatusFlags { dirty: bpb_status.dirty || fat_status.dirty, io_error: bpb_status.io_error || fat_status.io_error, @@ -623,7 +632,6 @@ impl FileSystem { status_flags.dirty = dirty; self.set_bpb_status_flags(status_flags)?; - self.set_fat_status_flags(status_flags)?; self.current_status_flags.set(status_flags); @@ -649,6 +657,11 @@ impl FileSystem { Ok(()) } + #[inline] + fn read_fat_status_flags(&self) -> Result> { + read_fat_flags(&mut self.fat_slice(), self.fat_type) + } + #[inline] fn set_fat_status_flags(&self, status_flags: FsStatusFlags) -> Result<(), Error> { write_fat_flags(&mut self.fat_slice(), self.fat_type, status_flags) From 0dd360a9d4b495f8212f5e41e81686bd94830cbd Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Thu, 26 May 2022 05:42:32 +0200 Subject: [PATCH 09/13] Clear BPB dirty flags before creating `FileSystem`. --- src/boot_sector.rs | 4 +++ src/fs.rs | 66 ++++++++++++++++++++++------------------------ tests/write.rs | 29 +++++++++++--------- 3 files changed, 51 insertions(+), 48 deletions(-) diff --git a/src/boot_sector.rs b/src/boot_sector.rs index 812222d..0636ad2 100644 --- a/src/boot_sector.rs +++ b/src/boot_sector.rs @@ -334,6 +334,10 @@ impl BiosParameterBlock { FsStatusFlags::decode(self.reserved_1) } + pub(crate) fn set_status_flags(&mut self, status_flags: FsStatusFlags) { + self.reserved_1 = status_flags.encode(); + } + pub(crate) fn is_fat32(&self) -> bool { // because this field must be zero on FAT32, and // because it must be non-zero on FAT12/FAT16, diff --git a/src/fs.rs b/src/fs.rs index c475a8d..86c72e4 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -101,7 +101,7 @@ impl FsStatusFlags { self.io_error } - const fn encode(self) -> u8 { + pub(crate) const fn encode(self) -> u8 { let mut res = 0_u8; if self.dirty { res |= 1; @@ -386,7 +386,7 @@ impl FileSystem { debug_assert!(disk.seek(SeekFrom::Current(0))? == 0); // read boot sector - let bpb = { + let mut bpb = { let boot = BootSector::deserialize(&mut disk)?; boot.validate()?; boot.bpb @@ -409,6 +409,15 @@ impl FileSystem { // if dirty flag is set completly ignore free_cluster_count in FSInfo if bpb_status_flags.dirty() { + if options.ignore_dirty_flag { + warn!("BPB is dirty, clearing dirty flag."); + bpb_status_flags.dirty = false; + write_bpb_status_flags(&mut disk, fat_type, bpb_status_flags)?; + bpb.set_status_flags(bpb_status_flags); + } else { + return Err(Error::DirtyFileSystem); + } + fs_info.free_cluster_count = None; } @@ -427,17 +436,6 @@ impl FileSystem { current_status_flags: Cell::new(bpb_status_flags), }; - if bpb_status_flags.dirty() { - if fs.options.ignore_dirty_flag { - warn!("BPB is dirty, clearing dirty flag."); - bpb_status_flags.dirty = false; - fs.set_bpb_status_flags(bpb_status_flags)?; - fs.current_status_flags.get_mut().dirty = false; - } else { - return Err(Error::DirtyFileSystem) - } - } - let mut fat_status_flags = fs.read_fat_status_flags()?; if fat_status_flags.dirty() { if fs.options.ignore_dirty_flag { @@ -554,7 +552,7 @@ impl FileSystem { /// /// `Error::Io` will be returned if the underlying storage object returned an I/O error. pub fn read_status_flags(&self) -> Result> { - let bpb_status = self.bpb.status_flags(); + let bpb_status = self.current_status_flags.get(); let fat_status = self.read_fat_status_flags()?; Ok(FsStatusFlags { dirty: bpb_status.dirty || fat_status.dirty, @@ -631,28 +629,9 @@ impl FileSystem { status_flags.dirty = dirty; - self.set_bpb_status_flags(status_flags)?; - - self.current_status_flags.set(status_flags); - - Ok(()) - } - - #[inline] - fn set_bpb_status_flags(&self, status_flags: FsStatusFlags) -> Result<(), Error> { - let encoded = status_flags.encode(); - - // Note: only one field is written to avoid rewriting entire boot-sector which could be dangerous - // Compute reserver_1 field offset and write new flags - let offset = if self.fat_type() == FatType::Fat32 { - 0x041 - } else { - 0x025 - }; - let mut disk = self.disk.borrow_mut(); - disk.seek(io::SeekFrom::Start(offset))?; - disk.write_u8(encoded)?; + write_bpb_status_flags(&mut *disk, self.fat_type(), status_flags)?; + self.current_status_flags.set(status_flags); Ok(()) } @@ -794,6 +773,23 @@ impl Clone for FsIoAdapter<'_, IO, TP, OCC> { } } +fn write_bpb_status_flags( + disk: &mut IO, + fat_type: FatType, + status_flags: FsStatusFlags, +) -> Result<(), Error> { + let encoded = status_flags.encode(); + + // Note: only one field is written to avoid rewriting entire boot-sector which could be dangerous + // Compute reserver_1 field offset and write new flags + let offset = if fat_type == FatType::Fat32 { 0x041 } else { 0x025 }; + + disk.seek(io::SeekFrom::Start(offset))?; + disk.write_u8(encoded)?; + + Ok(()) +} + fn fat_slice, E, S: ReadWriteSeek>(io: B, bpb: &BiosParameterBlock) -> DiskSlice { let sectors_per_fat = bpb.sectors_per_fat(); let mirroring_enabled = bpb.mirroring_enabled(); diff --git a/tests/write.rs b/tests/write.rs index 8fb55b9..41aae6b 100644 --- a/tests/write.rs +++ b/tests/write.rs @@ -4,7 +4,7 @@ use std::io::prelude::*; use std::mem; use std::str; -use fatfs::{DefaultTimeProvider, FsOptions, LossyOemCpConverter, StdIoWrapper}; +use fatfs::{DefaultTimeProvider, Error, FsOptions, LossyOemCpConverter, StdIoWrapper}; use fscommon::BufStream; const FAT12_IMG: &str = "fat12.img"; @@ -27,16 +27,18 @@ fn call_with_tmp_img ()>(f: F, filename: &str, test_seq: u32) { fs::remove_file(tmp_path).unwrap(); } -fn open_filesystem_rw(tmp_path: &str) -> FileSystem { +fn open_filesystem_rw(tmp_path: &str, ignore_dirty_flag: bool) -> Result> { let file = fs::OpenOptions::new().read(true).write(true).open(&tmp_path).unwrap(); let buf_file = BufStream::new(file); - let options = FsOptions::new().update_accessed_date(true); - FileSystem::new(buf_file, options).unwrap() + let options = FsOptions::new() + .update_accessed_date(true) + .ignore_dirty_flag(ignore_dirty_flag); + FileSystem::new(buf_file, options) } fn call_with_fs ()>(f: F, filename: &str, test_seq: u32) { let callback = |tmp_path: &str| { - let fs = open_filesystem_rw(tmp_path); + let fs = open_filesystem_rw(tmp_path, false).unwrap(); f(fs); }; call_with_tmp_img(&callback, filename, test_seq); @@ -338,22 +340,23 @@ fn test_rename_file_fat32() { fn test_dirty_flag(tmp_path: &str) { // Open filesystem, make change, and forget it - should become dirty - let fs = open_filesystem_rw(tmp_path); + let fs = open_filesystem_rw(tmp_path, false).unwrap(); let status_flags = fs.read_status_flags().unwrap(); assert_eq!(status_flags.dirty(), false); assert_eq!(status_flags.io_error(), false); fs.root_dir().create_file("abc.txt").unwrap(); mem::forget(fs); // Check if volume is dirty now - let fs = open_filesystem_rw(tmp_path); - let status_flags = fs.read_status_flags().unwrap(); - assert_eq!(status_flags.dirty(), true); - assert_eq!(status_flags.io_error(), false); - fs.unmount().unwrap(); + let fs = open_filesystem_rw(tmp_path, false); + assert!(matches!(fs, Err(Error::DirtyFileSystem))); // Make sure remounting does not clear the dirty flag - let fs = open_filesystem_rw(tmp_path); + // Check if volume is dirty now + let fs = open_filesystem_rw(tmp_path, false); + assert!(matches!(fs, Err(Error::DirtyFileSystem))); + // Make sure clearing the dirty flag allows mounting to succeed + let fs = open_filesystem_rw(tmp_path, true).unwrap(); let status_flags = fs.read_status_flags().unwrap(); - assert_eq!(status_flags.dirty(), true); + assert_eq!(status_flags.dirty(), false); assert_eq!(status_flags.io_error(), false); } From 95a1b05616273f7c3d5eb016a81b7eaa19c607b6 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Mon, 13 Jun 2022 12:59:22 +0200 Subject: [PATCH 10/13] Clear FAT dirty flag before creating `FileSystem`. --- src/fs.rs | 82 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/src/fs.rs b/src/fs.rs index 86c72e4..3f0ee4c 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -424,31 +424,33 @@ impl FileSystem { // Validate the numbers stored in the free_cluster_count and next_free_cluster are within bounds for volume fs_info.validate_and_fix(total_clusters); - let mut fs = Self { - disk: RefCell::new(disk), - options, - fat_type, - bpb, - first_data_sector, - root_dir_sectors, - total_clusters, - fs_info: RefCell::new(fs_info), - current_status_flags: Cell::new(bpb_status_flags), - }; - - let mut fat_status_flags = fs.read_fat_status_flags()?; + let mut fat_status_flags = read_fat_flags(&mut fat_slice::<&mut IO, IO::Error, IO>(&mut disk, &bpb), fat_type)?; if fat_status_flags.dirty() { - if fs.options.ignore_dirty_flag { + if options.ignore_dirty_flag { warn!("FAT is dirty, clearing dirty flag."); fat_status_flags.dirty = false; - fs.set_fat_status_flags(fat_status_flags)?; + write_fat_flags( + &mut fat_slice::<&mut IO, IO::Error, IO>(&mut disk, &bpb), + fat_type, + fat_status_flags, + )?; } else { return Err(Error::DirtyFileSystem); } } trace!("FileSystem::new end"); - Ok(fs) + Ok(Self { + disk: RefCell::new(disk), + options, + fat_type, + bpb, + first_data_sector, + root_dir_sectors, + total_clusters, + fs_info: RefCell::new(fs_info), + current_status_flags: Cell::new(bpb_status_flags), + }) } /// Returns a type of File Allocation Table (FAT) used by this filesystem. @@ -553,7 +555,7 @@ impl FileSystem { /// `Error::Io` will be returned if the underlying storage object returned an I/O error. pub fn read_status_flags(&self) -> Result> { let bpb_status = self.current_status_flags.get(); - let fat_status = self.read_fat_status_flags()?; + let fat_status = read_fat_flags(&mut self.fat_slice(), self.fat_type)?; Ok(FsStatusFlags { dirty: bpb_status.dirty || fat_status.dirty, io_error: bpb_status.io_error || fat_status.io_error, @@ -619,31 +621,10 @@ impl FileSystem { Ok(()) } + #[inline] pub(crate) fn set_dirty_flag(&self, dirty: bool) -> Result<(), Error> { - let mut status_flags = self.current_status_flags.get(); - - if status_flags.dirty == dirty { - // Dirty flag did not change. - return Ok(()); - } - - status_flags.dirty = dirty; - let mut disk = self.disk.borrow_mut(); - write_bpb_status_flags(&mut *disk, self.fat_type(), status_flags)?; - self.current_status_flags.set(status_flags); - - Ok(()) - } - - #[inline] - fn read_fat_status_flags(&self) -> Result> { - read_fat_flags(&mut self.fat_slice(), self.fat_type) - } - - #[inline] - fn set_fat_status_flags(&self, status_flags: FsStatusFlags) -> Result<(), Error> { - write_fat_flags(&mut self.fat_slice(), self.fat_type, status_flags) + set_dirty_flag(&mut *disk, self.fat_type(), &self.current_status_flags, dirty) } /// Returns a root directory object allowing for futher penetration of a filesystem structure. @@ -773,6 +754,27 @@ impl Clone for FsIoAdapter<'_, IO, TP, OCC> { } } +fn set_dirty_flag( + disk: &mut IO, + fat_type: FatType, + current_status_flags: &Cell, + dirty: bool, +) -> Result<(), Error> { + let mut status_flags = current_status_flags.get(); + + if status_flags.dirty == dirty { + // Dirty flag did not change. + return Ok(()); + } + + status_flags.dirty = dirty; + + write_bpb_status_flags(disk, fat_type, status_flags)?; + current_status_flags.set(status_flags); + + Ok(()) +} + fn write_bpb_status_flags( disk: &mut IO, fat_type: FatType, From 7f4aa0c580a4b8e97ea6341ac74d9a3f0b48fb12 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Mon, 13 Jun 2022 13:37:24 +0200 Subject: [PATCH 11/13] Revert unneeded changes. --- src/fs.rs | 6 ++---- tests/write.rs | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/fs.rs b/src/fs.rs index 3f0ee4c..81c7256 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -356,8 +356,6 @@ impl IntoStorage = DiskSlice, E>; - impl FileSystem { /// Creates a new filesystem object instance. /// @@ -501,7 +499,7 @@ impl FileSystem { self.bpb.clusters_from_bytes(bytes) } - fn fat_slice(&self) -> FatSlice<'_, IO, TP, OCC, IO::Error> { + fn fat_slice(&self) -> impl ReadWriteSeek> + '_ { let io = FsIoAdapter { fs: self }; fat_slice(io, &self.bpb) } @@ -509,7 +507,7 @@ impl FileSystem { pub(crate) fn cluster_iter( &self, cluster: u32, - ) -> ClusterIterator, IO::Error> { + ) -> ClusterIterator> + '_, IO::Error> { let disk_slice = self.fat_slice(); ClusterIterator::new(disk_slice, self.fat_type, cluster) } diff --git a/tests/write.rs b/tests/write.rs index 41aae6b..21961fa 100644 --- a/tests/write.rs +++ b/tests/write.rs @@ -350,7 +350,6 @@ fn test_dirty_flag(tmp_path: &str) { let fs = open_filesystem_rw(tmp_path, false); assert!(matches!(fs, Err(Error::DirtyFileSystem))); // Make sure remounting does not clear the dirty flag - // Check if volume is dirty now let fs = open_filesystem_rw(tmp_path, false); assert!(matches!(fs, Err(Error::DirtyFileSystem))); // Make sure clearing the dirty flag allows mounting to succeed From 6ec1aa47e1e6c80a9ae117cb888c6d4f86fea517 Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sat, 5 Nov 2022 16:02:18 +0100 Subject: [PATCH 12/13] Move functions into `impl` blocks. --- src/fs.rs | 72 +++++++++++++++++++++++++------------------------------ 1 file changed, 32 insertions(+), 40 deletions(-) diff --git a/src/fs.rs b/src/fs.rs index 81c7256..2efea04 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -410,7 +410,7 @@ impl FileSystem { if options.ignore_dirty_flag { warn!("BPB is dirty, clearing dirty flag."); bpb_status_flags.dirty = false; - write_bpb_status_flags(&mut disk, fat_type, bpb_status_flags)?; + Self::write_bpb_status_flags(&mut disk, fat_type, bpb_status_flags)?; bpb.set_status_flags(bpb_status_flags); } else { return Err(Error::DirtyFileSystem); @@ -619,10 +619,40 @@ impl FileSystem { Ok(()) } + fn write_bpb_status_flags( + disk: &mut IO, + fat_type: FatType, + status_flags: FsStatusFlags, + ) -> Result<(), Error> { + let encoded = status_flags.encode(); + + // Note: only one field is written to avoid rewriting entire boot-sector which could be dangerous + // Compute reserver_1 field offset and write new flags + let offset = if fat_type == FatType::Fat32 { 0x041 } else { 0x025 }; + + disk.seek(io::SeekFrom::Start(offset))?; + disk.write_u8(encoded)?; + + Ok(()) + } + #[inline] pub(crate) fn set_dirty_flag(&self, dirty: bool) -> Result<(), Error> { let mut disk = self.disk.borrow_mut(); - set_dirty_flag(&mut *disk, self.fat_type(), &self.current_status_flags, dirty) + + let mut status_flags = self.current_status_flags.get(); + + if status_flags.dirty == dirty { + // Dirty flag did not change. + return Ok(()); + } + + status_flags.dirty = dirty; + + Self::write_bpb_status_flags(&mut *disk, self.fat_type(), status_flags)?; + self.current_status_flags.set(status_flags); + + Ok(()) } /// Returns a root directory object allowing for futher penetration of a filesystem structure. @@ -752,44 +782,6 @@ impl Clone for FsIoAdapter<'_, IO, TP, OCC> { } } -fn set_dirty_flag( - disk: &mut IO, - fat_type: FatType, - current_status_flags: &Cell, - dirty: bool, -) -> Result<(), Error> { - let mut status_flags = current_status_flags.get(); - - if status_flags.dirty == dirty { - // Dirty flag did not change. - return Ok(()); - } - - status_flags.dirty = dirty; - - write_bpb_status_flags(disk, fat_type, status_flags)?; - current_status_flags.set(status_flags); - - Ok(()) -} - -fn write_bpb_status_flags( - disk: &mut IO, - fat_type: FatType, - status_flags: FsStatusFlags, -) -> Result<(), Error> { - let encoded = status_flags.encode(); - - // Note: only one field is written to avoid rewriting entire boot-sector which could be dangerous - // Compute reserver_1 field offset and write new flags - let offset = if fat_type == FatType::Fat32 { 0x041 } else { 0x025 }; - - disk.seek(io::SeekFrom::Start(offset))?; - disk.write_u8(encoded)?; - - Ok(()) -} - fn fat_slice, E, S: ReadWriteSeek>(io: B, bpb: &BiosParameterBlock) -> DiskSlice { let sectors_per_fat = bpb.sectors_per_fat(); let mirroring_enabled = bpb.mirroring_enabled(); From a23e9cddd0e7c7053200e551ddcf20c88bdc946b Mon Sep 17 00:00:00 2001 From: Markus Reiter Date: Sat, 5 Nov 2022 17:19:37 +0100 Subject: [PATCH 13/13] Extend test. --- tests/write.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/write.rs b/tests/write.rs index 21961fa..bed8467 100644 --- a/tests/write.rs +++ b/tests/write.rs @@ -357,6 +357,9 @@ fn test_dirty_flag(tmp_path: &str) { let status_flags = fs.read_status_flags().unwrap(); assert_eq!(status_flags.dirty(), false); assert_eq!(status_flags.io_error(), false); + // Make sure the dirty flag is cleared after remounting. + drop(fs); + open_filesystem_rw(tmp_path, false).unwrap(); } #[test]