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 file::File;
|
||||||
use dir::{DirRawStream, Dir};
|
use dir::{DirRawStream, Dir};
|
||||||
use dir_entry::DIR_ENTRY_SIZE;
|
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"))]
|
#[cfg(all(not(feature = "std"), feature = "alloc"))]
|
||||||
use alloc::{String, string::ToString};
|
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>;
|
pub(crate) type FileSystemRef<'a, 'b> = &'a FileSystem<'b>;
|
||||||
|
|
||||||
/// FAT filesystem main struct.
|
/// FAT filesystem main struct.
|
||||||
@ -430,6 +441,7 @@ impl <'a> FileSystem<'a> {
|
|||||||
Ok(cluster)
|
Ok(cluster)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns status flags for this volume.
|
||||||
pub fn read_status_flags(&self) -> io::Result<FsStatusFlags> {
|
pub fn read_status_flags(&self) -> io::Result<FsStatusFlags> {
|
||||||
let bpb_status = self.bpb.status_flags();
|
let bpb_status = self.bpb.status_flags();
|
||||||
let fat_status = read_fat_flags(&mut self.fat_slice(), self.fat_type)?;
|
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,
|
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)]
|
#[derive(Clone)]
|
||||||
|
83
src/table.rs
83
src/table.rs
@ -24,10 +24,11 @@ enum FatValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
trait FatTrait {
|
trait FatTrait {
|
||||||
|
fn get_raw(fat: &mut ReadSeek, cluster: u32) -> io::Result<u32>;
|
||||||
fn get(fat: &mut ReadSeek, cluster: u32) -> io::Result<FatValue>;
|
fn get(fat: &mut ReadSeek, cluster: u32) -> io::Result<FatValue>;
|
||||||
fn set(fat: &mut DiskSlice, cluster: u32, value: FatValue) -> io::Result<()>;
|
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 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> {
|
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)
|
Ok(new_cluster)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub(crate) fn read_fat_flags(fat: &mut DiskSlice, fat_type: FatType) -> io::Result<FsStatusFlags> {
|
pub(crate) fn read_fat_flags(fat: &mut DiskSlice, fat_type: FatType) -> io::Result<FsStatusFlags> {
|
||||||
// check MSB (except in FAT12)
|
// check MSB (except in FAT12)
|
||||||
let val = match fat_type {
|
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 {
|
impl FatTrait for Fat12 {
|
||||||
fn get_raw(fat: &mut ReadSeek, cluster: u32) -> io::Result<u32> {
|
fn get_raw(fat: &mut ReadSeek, cluster: u32) -> io::Result<u32> {
|
||||||
let fat_offset = cluster + (cluster / 2);
|
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 {
|
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 {
|
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> {
|
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)
|
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