Skip to content

Commit 34c59d1

Browse files
committed
Clear BPB dirty flags before creating FileSystem.
1 parent 73a74e1 commit 34c59d1

File tree

3 files changed

+51
-48
lines changed

3 files changed

+51
-48
lines changed

src/boot_sector.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,10 @@ impl BiosParameterBlock {
334334
FsStatusFlags::decode(self.reserved_1)
335335
}
336336

337+
pub(crate) fn set_status_flags(&mut self, status_flags: FsStatusFlags) {
338+
self.reserved_1 = status_flags.encode();
339+
}
340+
337341
pub(crate) fn is_fat32(&self) -> bool {
338342
// because this field must be zero on FAT32, and
339343
// because it must be non-zero on FAT12/FAT16,

src/fs.rs

Lines changed: 31 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ impl FsStatusFlags {
101101
self.io_error
102102
}
103103

104-
const fn encode(self) -> u8 {
104+
pub(crate) const fn encode(self) -> u8 {
105105
let mut res = 0_u8;
106106
if self.dirty {
107107
res |= 1;
@@ -386,7 +386,7 @@ impl<IO: Read + Write + Seek, TP, OCC> FileSystem<IO, TP, OCC> {
386386
debug_assert!(disk.seek(SeekFrom::Current(0))? == 0);
387387

388388
// read boot sector
389-
let bpb = {
389+
let mut bpb = {
390390
let boot = BootSector::deserialize(&mut disk)?;
391391
boot.validate()?;
392392
boot.bpb
@@ -409,6 +409,15 @@ impl<IO: Read + Write + Seek, TP, OCC> FileSystem<IO, TP, OCC> {
409409

410410
// if dirty flag is set completly ignore free_cluster_count in FSInfo
411411
if bpb_status_flags.dirty() {
412+
if options.ignore_dirty_flag {
413+
warn!("BPB is dirty, clearing dirty flag.");
414+
bpb_status_flags.dirty = false;
415+
write_bpb_status_flags(&mut disk, fat_type, bpb_status_flags)?;
416+
bpb.set_status_flags(bpb_status_flags);
417+
} else {
418+
return Err(Error::DirtyFileSystem);
419+
}
420+
412421
fs_info.free_cluster_count = None;
413422
}
414423

@@ -427,17 +436,6 @@ impl<IO: Read + Write + Seek, TP, OCC> FileSystem<IO, TP, OCC> {
427436
current_status_flags: Cell::new(bpb_status_flags),
428437
};
429438

430-
if bpb_status_flags.dirty() {
431-
if fs.options.ignore_dirty_flag {
432-
warn!("BPB is dirty, clearing dirty flag.");
433-
bpb_status_flags.dirty = false;
434-
fs.set_bpb_status_flags(bpb_status_flags)?;
435-
fs.current_status_flags.get_mut().dirty = false;
436-
} else {
437-
return Err(Error::DirtyFileSystem)
438-
}
439-
}
440-
441439
let mut fat_status_flags = fs.read_fat_status_flags()?;
442440
if fat_status_flags.dirty() {
443441
if fs.options.ignore_dirty_flag {
@@ -554,7 +552,7 @@ impl<IO: Read + Write + Seek, TP, OCC> FileSystem<IO, TP, OCC> {
554552
///
555553
/// `Error::Io` will be returned if the underlying storage object returned an I/O error.
556554
pub fn read_status_flags(&self) -> Result<FsStatusFlags, Error<IO::Error>> {
557-
let bpb_status = self.bpb.status_flags();
555+
let bpb_status = self.current_status_flags.get();
558556
let fat_status = self.read_fat_status_flags()?;
559557
Ok(FsStatusFlags {
560558
dirty: bpb_status.dirty || fat_status.dirty,
@@ -631,28 +629,9 @@ impl<IO: Read + Write + Seek, TP, OCC> FileSystem<IO, TP, OCC> {
631629

632630
status_flags.dirty = dirty;
633631

634-
self.set_bpb_status_flags(status_flags)?;
635-
636-
self.current_status_flags.set(status_flags);
637-
638-
Ok(())
639-
}
640-
641-
#[inline]
642-
fn set_bpb_status_flags(&self, status_flags: FsStatusFlags) -> Result<(), Error<IO::Error>> {
643-
let encoded = status_flags.encode();
644-
645-
// Note: only one field is written to avoid rewriting entire boot-sector which could be dangerous
646-
// Compute reserver_1 field offset and write new flags
647-
let offset = if self.fat_type() == FatType::Fat32 {
648-
0x041
649-
} else {
650-
0x025
651-
};
652-
653632
let mut disk = self.disk.borrow_mut();
654-
disk.seek(io::SeekFrom::Start(offset))?;
655-
disk.write_u8(encoded)?;
633+
write_bpb_status_flags(&mut *disk, self.fat_type(), status_flags)?;
634+
self.current_status_flags.set(status_flags);
656635

657636
Ok(())
658637
}
@@ -794,6 +773,23 @@ impl<IO: ReadWriteSeek, TP, OCC> Clone for FsIoAdapter<'_, IO, TP, OCC> {
794773
}
795774
}
796775

776+
fn write_bpb_status_flags<IO: Seek + Write>(
777+
disk: &mut IO,
778+
fat_type: FatType,
779+
status_flags: FsStatusFlags,
780+
) -> Result<(), Error<IO::Error>> {
781+
let encoded = status_flags.encode();
782+
783+
// Note: only one field is written to avoid rewriting entire boot-sector which could be dangerous
784+
// Compute reserver_1 field offset and write new flags
785+
let offset = if fat_type == FatType::Fat32 { 0x041 } else { 0x025 };
786+
787+
disk.seek(io::SeekFrom::Start(offset))?;
788+
disk.write_u8(encoded)?;
789+
790+
Ok(())
791+
}
792+
797793
fn fat_slice<B: BorrowMut<S>, E, S: ReadWriteSeek>(io: B, bpb: &BiosParameterBlock) -> DiskSlice<B, E, S> {
798794
let sectors_per_fat = bpb.sectors_per_fat();
799795
let mirroring_enabled = bpb.mirroring_enabled();

tests/write.rs

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::io::prelude::*;
44
use std::mem;
55
use std::str;
66

7-
use fatfs::{DefaultTimeProvider, FsOptions, LossyOemCpConverter, StdIoWrapper};
7+
use fatfs::{DefaultTimeProvider, Error, FsOptions, LossyOemCpConverter, StdIoWrapper};
88
use fscommon::BufStream;
99

1010
const FAT12_IMG: &str = "fat12.img";
@@ -27,16 +27,18 @@ fn call_with_tmp_img<F: Fn(&str) -> ()>(f: F, filename: &str, test_seq: u32) {
2727
fs::remove_file(tmp_path).unwrap();
2828
}
2929

30-
fn open_filesystem_rw(tmp_path: &str) -> FileSystem {
30+
fn open_filesystem_rw(tmp_path: &str, ignore_dirty_flag: bool) -> Result<FileSystem, Error<io::Error>> {
3131
let file = fs::OpenOptions::new().read(true).write(true).open(&tmp_path).unwrap();
3232
let buf_file = BufStream::new(file);
33-
let options = FsOptions::new().update_accessed_date(true);
34-
FileSystem::new(buf_file, options).unwrap()
33+
let options = FsOptions::new()
34+
.update_accessed_date(true)
35+
.ignore_dirty_flag(ignore_dirty_flag);
36+
FileSystem::new(buf_file, options)
3537
}
3638

3739
fn call_with_fs<F: Fn(FileSystem) -> ()>(f: F, filename: &str, test_seq: u32) {
3840
let callback = |tmp_path: &str| {
39-
let fs = open_filesystem_rw(tmp_path);
41+
let fs = open_filesystem_rw(tmp_path, false).unwrap();
4042
f(fs);
4143
};
4244
call_with_tmp_img(&callback, filename, test_seq);
@@ -338,22 +340,23 @@ fn test_rename_file_fat32() {
338340

339341
fn test_dirty_flag(tmp_path: &str) {
340342
// Open filesystem, make change, and forget it - should become dirty
341-
let fs = open_filesystem_rw(tmp_path);
343+
let fs = open_filesystem_rw(tmp_path, false).unwrap();
342344
let status_flags = fs.read_status_flags().unwrap();
343345
assert_eq!(status_flags.dirty(), false);
344346
assert_eq!(status_flags.io_error(), false);
345347
fs.root_dir().create_file("abc.txt").unwrap();
346348
mem::forget(fs);
347349
// Check if volume is dirty now
348-
let fs = open_filesystem_rw(tmp_path);
349-
let status_flags = fs.read_status_flags().unwrap();
350-
assert_eq!(status_flags.dirty(), true);
351-
assert_eq!(status_flags.io_error(), false);
352-
fs.unmount().unwrap();
350+
let fs = open_filesystem_rw(tmp_path, false);
351+
assert!(matches!(fs, Err(Error::DirtyFileSystem)));
353352
// Make sure remounting does not clear the dirty flag
354-
let fs = open_filesystem_rw(tmp_path);
353+
// Check if volume is dirty now
354+
let fs = open_filesystem_rw(tmp_path, false);
355+
assert!(matches!(fs, Err(Error::DirtyFileSystem)));
356+
// Make sure clearing the dirty flag allows mounting to succeed
357+
let fs = open_filesystem_rw(tmp_path, true).unwrap();
355358
let status_flags = fs.read_status_flags().unwrap();
356-
assert_eq!(status_flags.dirty(), true);
359+
assert_eq!(status_flags.dirty(), false);
357360
assert_eq!(status_flags.io_error(), false);
358361
}
359362

0 commit comments

Comments
 (0)