diff --git a/CHANGELOG.md b/CHANGELOG.md index e558816..b4eabaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,8 @@ New features: * Undeprecate `set_created`, `set_accessed`, `set_modified` methods on `File` - there are scenarios where those methods are needed * Relax BPB validation by allowing non-zero values in both `total_sectors_32` and `total_sectors_16` * Add `strict` field in `FsOptions`, which allows disabling validation of boot signature to improve compatibility with old FAT images +* Improve `open_dir` +* Expose bytes_per_sector and first_cluster Bug fixes: * Fix formatting volumes with size in range 4096-4199 KB @@ -48,6 +50,7 @@ Bug fixes: * Fill FAT32 root directory clusters with zeros after allocation to avoid interpreting old data as directory entries * Put '.' and '..' in the first two directory entries. (fixes "Expected a valid '.' entry in this slot." fsck error) * Set the cluster number to 0 in the ".." directory entry if it points to the root dir +* Flush on unmount 0.3.4 (2020-07-20) ------------------ diff --git a/src/boot_sector.rs b/src/boot_sector.rs index 3472535..a5fbf7a 100644 --- a/src/boot_sector.rs +++ b/src/boot_sector.rs @@ -245,7 +245,7 @@ impl BiosParameterBlock { } if self.total_sectors_16 != 0 && self.total_sectors_32 != 0 - && self.total_sectors_16 as u32 != self.total_sectors_32 + && u32::from(self.total_sectors_16) != self.total_sectors_32 { error!("Invalid BPB: total_sectors_16 and total_sectors_32 are non-zero and have conflicting values"); return Err(Error::CorruptedFileSystem); @@ -395,6 +395,10 @@ impl BiosParameterBlock { u32::from(self.sectors_per_cluster) * u32::from(self.bytes_per_sector) } + pub(crate) fn bytes_per_sector(&self) -> u16 { + self.bytes_per_sector + } + pub(crate) fn clusters_from_bytes(&self, bytes: u64) -> u32 { let cluster_size = u64::from(self.cluster_size()); ((bytes + cluster_size - 1) / cluster_size) as u32 diff --git a/src/dir.rs b/src/dir.rs index e2038e8..a4e9182 100644 --- a/src/dir.rs +++ b/src/dir.rs @@ -185,7 +185,7 @@ impl<'a, IO: ReadWriteSeek, TP: TimeProvider, OCC: OemCpConverter> Dir<'a, IO, T Err(err) => return Err(err), // directory already exists - return it Ok(e) => return Ok(DirEntryOrShortName::DirEntry(e)), - }; + } // try to generate short name if let Ok(name) = short_name_gen.generate() { return Ok(DirEntryOrShortName::ShortName(name)); @@ -200,6 +200,10 @@ impl<'a, IO: ReadWriteSeek, TP: TimeProvider, OCC: OemCpConverter> Dir<'a, IO, T /// /// `path` is a '/' separated directory path relative to self directory. /// + /// An empty path returns the current directory. + /// A trailing slash is ignored. + /// (But the path '/' will not be found.) + /// /// # Errors /// /// Errors that can be returned: @@ -207,13 +211,21 @@ impl<'a, IO: ReadWriteSeek, TP: TimeProvider, OCC: OemCpConverter> Dir<'a, IO, T /// * `Error::NotFound` will be returned if `path` does not point to any existing directory entry. /// * `Error::InvalidInput` will be returned if `path` points to a file that is not a directory. /// * `Error::Io` will be returned if the underlying storage object returned an I/O error. - pub fn open_dir(&self, path: &str) -> Result> { + pub fn open_dir(&self, mut path: &str) -> Result> { trace!("Dir::open_dir {}", path); - let (name, rest_opt) = split_path(path); - let e = self.find_entry(name, Some(true), None)?; - match rest_opt { - Some(rest) => e.to_dir().open_dir(rest), - None => Ok(e.to_dir()), + let mut dir = self.clone(); + loop { + if path.is_empty() { + return Ok(dir); + } + let (name, rest_opt) = split_path(path); + dir = dir.find_entry(name, Some(true), None)?.to_dir(); + match rest_opt { + Some(rest) => { + path = rest; + } + None => return Ok(dir), + } } } @@ -541,6 +553,7 @@ impl<'a, IO: ReadWriteSeek, TP: TimeProvider, OCC: OemCpConverter> Dir<'a, IO, T Ok((stream, start_pos)) } + #[allow(clippy::type_complexity)] fn alloc_sfn_entry(&self) -> Result<(DirRawStream<'a, IO, TP, OCC>, u64), Error> { let mut stream = self.find_free_entries(1)?; let start_pos = stream.seek(io::SeekFrom::Current(0))?; diff --git a/src/dir_entry.rs b/src/dir_entry.rs index a9dc398..88f08da 100644 --- a/src/dir_entry.rs +++ b/src/dir_entry.rs @@ -606,7 +606,9 @@ impl<'a, IO: ReadWriteSeek, TP, OCC: OemCpConverter> DirEntry<'a, IO, TP, OCC> { self.data.is_file() } - pub(crate) fn first_cluster(&self) -> Option { + /// Return first cluster of a entry. + #[must_use] + pub fn first_cluster(&self) -> Option { self.data.first_cluster(self.fs.fat_type()) } diff --git a/src/fs.rs b/src/fs.rs index e08b7fa..7f1b8ad 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -282,6 +282,7 @@ impl FsOptions { } /// If enabled more validations are performed to check if file-system is conforming to specification. + #[must_use] pub fn strict(self, strict: bool) -> Self { Self { update_accessed_date: self.update_accessed_date, @@ -459,6 +460,12 @@ impl FileSystem { self.bpb.cluster_size() } + /// Get sector size in bytes + #[must_use] + pub fn bytes_per_sector(&self) -> u16 { + self.bpb.bytes_per_sector() + } + pub(crate) fn offset_from_cluster(&self, cluster: u32) -> u64 { self.offset_from_sector(self.sector_from_cluster(cluster)) } @@ -576,6 +583,7 @@ impl FileSystem { fn unmount_internal(&self) -> Result<(), Error> { self.flush_fs_info()?; self.set_dirty_flag(false)?; + self.disk.borrow_mut().flush()?; Ok(()) } @@ -617,7 +625,7 @@ impl FileSystem { } /// Returns a root directory object allowing for futher penetration of a filesystem structure. - pub fn root_dir(&self) -> Dir { + pub fn root_dir(&self) -> Dir<'_, IO, TP, OCC> { trace!("root_dir"); let root_rdr = { match self.fat_type { diff --git a/src/table.rs b/src/table.rs index a36cf96..b27321a 100644 --- a/src/table.rs +++ b/src/table.rs @@ -217,7 +217,7 @@ where fat.write_u32_le(u32::from(media) | 0xFFF_FF00)?; fat.write_u32_le(0xFFFF_FFFF)?; } - }; + } // mark entries at the end of FAT as used (after FAT but before sector end) let start_cluster = total_clusters + RESERVED_FAT_ENTRIES; let end_cluster = (bytes_per_fat * BITS_PER_BYTE / u64::from(fat_type.bits_per_fat_entry())) as u32; @@ -531,7 +531,7 @@ impl FatTrait for Fat32 { "cluster number {} is a special value in FAT to indicate {}; it should never be set as free", cluster, tmp ); - }; + } let raw_val = match value { FatValue::Free => 0, FatValue::Bad => 0x0FFF_FFF7,