Add FileSystem::stats method

It allows to get number of total and free clusters on FAT volume.
This commit is contained in:
Rafał Harabień 2018-05-26 17:09:13 +02:00
parent 6230fe91f9
commit 7709f5e6f6
3 changed files with 141 additions and 4 deletions

View File

@ -9,7 +9,7 @@ use byteorder_ext::{ReadBytesExt, WriteBytesExt};
use file::File;
use dir::{DirRawStream, Dir};
use dir_entry::DIR_ENTRY_SIZE;
use table::{ClusterIterator, alloc_cluster, read_fat_flags};
use table::{ClusterIterator, alloc_cluster, read_fat_flags, count_free_clusters};
#[cfg(all(not(feature = "std"), feature = "alloc"))]
use alloc::{String, string::ToString};
@ -283,6 +283,17 @@ impl FsOptions {
}
}
/// FAT filesystem statistics
#[derive(Debug, Clone, Copy)]
pub struct FileSystemStats {
/// Cluster size in bytes
pub cluster_size: u32,
/// Number of total clusters in filesystem usable for file allocation
pub total_clusters: u32,
/// Number of free clusters
pub free_clusters: u32,
}
pub(crate) type FileSystemRef<'a, 'b> = &'a FileSystem<'b>;
/// FAT filesystem main struct.
@ -430,6 +441,7 @@ impl <'a> FileSystem<'a> {
Ok(cluster)
}
/// Returns status flags for this volume.
pub fn read_status_flags(&self) -> io::Result<FsStatusFlags> {
let bpb_status = self.bpb.status_flags();
let fat_status = read_fat_flags(&mut self.fat_slice(), self.fat_type)?;
@ -438,6 +450,25 @@ impl <'a> FileSystem<'a> {
io_error: bpb_status.io_error || fat_status.io_error,
})
}
/// Returns filesystem statistics like number of total and free clusters.
///
/// For FAT32 volumes number of free clusters from FSInfo sector is returned (may be incorrect).
/// For other filesystems number is computed on each call (scans entire FAT so it takes more time).
pub fn stats(&self) -> io::Result<FileSystemStats> {
let free_clusters = match self.fs_info.borrow().free_cluster_count {
Some(n) => n,
_ => {
let mut fat = self.fat_slice();
count_free_clusters(&mut fat, self.fat_type, self.total_clusters)?
}
};
Ok(FileSystemStats {
cluster_size: self.cluster_size(),
total_clusters: self.total_clusters,
free_clusters,
})
}
}
#[derive(Clone)]

View File

@ -24,10 +24,11 @@ enum FatValue {
}
trait FatTrait {
fn get_raw(fat: &mut ReadSeek, cluster: u32) -> io::Result<u32>;
fn get(fat: &mut ReadSeek, cluster: u32) -> io::Result<FatValue>;
fn set(fat: &mut DiskSlice, cluster: u32, value: FatValue) -> io::Result<()>;
fn find_free(fat: &mut ReadSeek, start_cluster: u32, end_cluster: u32) -> io::Result<u32>;
fn get_raw(fat: &mut ReadSeek, cluster: u32) -> io::Result<u32>;
fn count_free(fat: &mut ReadSeek, end_cluster: u32) -> io::Result<u32>;
}
fn read_fat(fat: &mut ReadSeek, fat_type: FatType, cluster: u32) -> io::Result<FatValue> {
@ -83,8 +84,6 @@ pub(crate) fn alloc_cluster(fat: &mut DiskSlice, fat_type: FatType, prev_cluster
Ok(new_cluster)
}
pub(crate) fn read_fat_flags(fat: &mut DiskSlice, fat_type: FatType) -> io::Result<FsStatusFlags> {
// check MSB (except in FAT12)
let val = match fat_type {
@ -107,6 +106,15 @@ pub(crate) fn read_fat_flags(fat: &mut DiskSlice, fat_type: FatType) -> io::Resu
})
}
pub(crate) fn count_free_clusters(fat: &mut ReadSeek, fat_type: FatType, total_clusters: u32) -> io::Result<u32> {
let end_cluster = total_clusters + RESERVED_FAT_ENTRIES;
match fat_type {
FatType::Fat12 => Fat12::count_free(fat, end_cluster),
FatType::Fat16 => Fat16::count_free(fat, end_cluster),
FatType::Fat32 => Fat32::count_free(fat, end_cluster),
}
}
impl FatTrait for Fat12 {
fn get_raw(fat: &mut ReadSeek, cluster: u32) -> io::Result<u32> {
let fat_offset = cluster + (cluster / 2);
@ -173,6 +181,37 @@ impl FatTrait for Fat12 {
};
}
}
fn count_free(fat: &mut ReadSeek, end_cluster: u32) -> io::Result<u32> {
let mut count = 0;
let mut cluster = RESERVED_FAT_ENTRIES;
fat.seek(io::SeekFrom::Start((cluster*3/2) as u64))?;
let mut prev_packed_val = 0u16;
loop {
let res = match cluster & 1 {
0 => fat.read_u16::<LittleEndian>(),
_ => fat.read_u8().map(|n| n as u16),
};
let packed_val = match res {
Err(ref err) if err.kind() == io::ErrorKind::UnexpectedEof => break,
Err(err) => return Err(err),
Ok(n) => n,
};
let val = match cluster & 1 {
0 => packed_val & 0x0FFF,
_ => (packed_val << 8) | (prev_packed_val >> 12),
};
prev_packed_val = packed_val;
if val == 0 {
count += 1;
}
cluster += 1;
if cluster >= end_cluster {
break;
}
}
Ok(count)
}
}
impl FatTrait for Fat16 {
@ -217,6 +256,25 @@ impl FatTrait for Fat16 {
}
}
}
fn count_free(fat: &mut ReadSeek, end_cluster: u32) -> io::Result<u32> {
let mut count = 0;
let mut cluster = RESERVED_FAT_ENTRIES;
fat.seek(io::SeekFrom::Start((cluster*2) as u64))?;
loop {
match fat.read_u16::<LittleEndian>() {
Err(ref err) if err.kind() == io::ErrorKind::UnexpectedEof => break,
Err(err) => return Err(err),
Ok(0) => count += 1,
_ => {},
}
cluster += 1;
if cluster >= end_cluster {
break;
}
}
Ok(count)
}
}
impl FatTrait for Fat32 {
@ -261,6 +319,25 @@ impl FatTrait for Fat32 {
}
}
}
fn count_free(fat: &mut ReadSeek, end_cluster: u32) -> io::Result<u32> {
let mut count = 0;
let mut cluster = RESERVED_FAT_ENTRIES;
fat.seek(io::SeekFrom::Start((cluster*4) as u64))?;
loop {
match fat.read_u32::<LittleEndian>() {
Err(ref err) if err.kind() == io::ErrorKind::UnexpectedEof => break,
Err(err) => return Err(err),
Ok(0) => count += 1,
_ => {},
}
cluster += 1;
if cluster >= end_cluster {
break;
}
}
Ok(count)
}
}
pub(crate) struct ClusterIterator<'a, 'b: 'a> {

View File

@ -210,3 +210,32 @@ fn test_status_flags_fat32() {
call_with_fs(&|fs| test_status_flags(fs), FAT32_IMG)
}
#[test]
fn test_stats_fat12() {
call_with_fs(&|fs| {
let stats = fs.stats().unwrap();
assert_eq!(stats.cluster_size, 512);
assert_eq!(stats.total_clusters, 1955); // 1000 * 1024 / 512 = 2000
assert_eq!(stats.free_clusters, 1920);
}, FAT12_IMG)
}
#[test]
fn test_stats_fat16() {
call_with_fs(&|fs| {
let stats = fs.stats().unwrap();
assert_eq!(stats.cluster_size, 512);
assert_eq!(stats.total_clusters, 4927); // 2500 * 1024 / 512 = 5000
assert_eq!(stats.free_clusters, 4892);
}, FAT16_IMG)
}
#[test]
fn test_stats_fat32() {
call_with_fs(&|fs| {
let stats = fs.stats().unwrap();
assert_eq!(stats.cluster_size, 512);
assert_eq!(stats.total_clusters, 66922); // 34000 * 1024 / 512 = 68000
assert_eq!(stats.free_clusters, 66886);
}, FAT32_IMG)
}