Add FileSystem::stats method
It allows to get number of total and free clusters on FAT volume.
This commit is contained in:
parent
6230fe91f9
commit
7709f5e6f6
33
src/fs.rs
33
src/fs.rs
@ -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)]
|
||||
|
83
src/table.rs
83
src/table.rs
@ -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> {
|
||||
|
@ -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)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user