Add more sanity checks and fix size formatting in ls example (#11)
* Fix copy/paste errors in ls example so file sizes are correctly output. * BPB updates. Fix bug in bpb.active_fat(), because active_fat is only valid when mirroring is disabled. Add additional santiy checks to BPB deserialization: 1. bytes per sector must be a power of 2 2. 512 <= bytes per sector <= 4096 3. sectors per cluster must be a power of 2 4. 1 <= sectors per cluster <= 128 Also added some comments relating to conditions that may be useful to WARN about for BPB deserialization: Z. bpb.reserved_sectors should be 1 Y. bpb.fats should be either 1 or 2 X. bpb.fs_version should be validated as zero Add syntactic sugar for: A. bpb.is_fat32() B. bpb.sectors_per_fat() C. bpb.total_sectors()
This commit is contained in:
parent
8c9d476c2f
commit
2d689a668d
@ -14,14 +14,14 @@ fn format_file_size(size: u64) -> String {
|
||||
const KB: u64 = 1024;
|
||||
const MB: u64 = 1024 * KB;
|
||||
const GB: u64 = 1024 * MB;
|
||||
if size < 1024 {
|
||||
if size < KB {
|
||||
format!("{}B", size)
|
||||
} else if size < MB {
|
||||
format!("{}KB", size / KB)
|
||||
} else if size < GB {
|
||||
format!("{}KB", size / MB)
|
||||
format!("{}MB", size / MB)
|
||||
} else {
|
||||
format!("{}KB", size / GB)
|
||||
format!("{}GB", size / GB)
|
||||
}
|
||||
}
|
||||
|
||||
|
104
src/fs.rs
104
src/fs.rs
@ -143,23 +143,52 @@ impl BiosParameterBlock {
|
||||
bpb.total_sectors_32 = rdr.read_u32::<LittleEndian>()?;
|
||||
|
||||
// sanity checks
|
||||
if bpb.bytes_per_sector < 512 {
|
||||
return Err(Error::new(ErrorKind::Other, "invalid bytes_per_sector value in BPB"));
|
||||
}
|
||||
if bpb.sectors_per_cluster < 1 {
|
||||
return Err(Error::new(ErrorKind::Other, "invalid sectors_per_cluster value in BPB"));
|
||||
}
|
||||
if bpb.reserved_sectors < 1 {
|
||||
return Err(Error::new(ErrorKind::Other, "invalid reserved_sectors value in BPB"));
|
||||
}
|
||||
if bpb.fats == 0 {
|
||||
return Err(Error::new(ErrorKind::Other, "invalid fats value in BPB"));
|
||||
if bpb.bytes_per_sector.count_ones() != 1 {
|
||||
return Err(Error::new(ErrorKind::Other, "invalid bytes_per_sector value in BPB (not power of two)"));
|
||||
} else if bpb.bytes_per_sector < 512 {
|
||||
return Err(Error::new(ErrorKind::Other, "invalid bytes_per_sector value in BPB (value < 512)"));
|
||||
} else if bpb.bytes_per_sector > 4096 {
|
||||
return Err(Error::new(ErrorKind::Other, "invalid bytes_per_sector value in BPB (value > 4096)"));
|
||||
}
|
||||
|
||||
if bpb.sectors_per_fat_16 == 0 {
|
||||
if bpb.sectors_per_cluster.count_ones() != 1 {
|
||||
return Err(Error::new(ErrorKind::Other, "invalid sectors_per_cluster value in BPB (not power of two)"));
|
||||
} else if bpb.sectors_per_cluster < 1 {
|
||||
return Err(Error::new(ErrorKind::Other, "invalid sectors_per_cluster value in BPB (value < 1)"));
|
||||
} else if bpb.sectors_per_cluster > 128 {
|
||||
return Err(Error::new(ErrorKind::Other, "invalid sectors_per_cluster value in BPB (value > 128)"));
|
||||
}
|
||||
|
||||
// bytes per sector is u16, sectors per cluster is u8, so guaranteed no overflow in multiplication
|
||||
let bytes_per_cluster = bpb.bytes_per_sector as u32 * bpb.sectors_per_cluster as u32;
|
||||
let maximum_compatibility_bytes_per_cluster : u32 = 32 * 1024;
|
||||
|
||||
if bytes_per_cluster > maximum_compatibility_bytes_per_cluster {
|
||||
// 32k is the largest value to maintain greatest compatibility
|
||||
// Many implementations appear to support 64k per cluster, and some may support 128k or larger
|
||||
// However, >32k is not as thoroughly tested...
|
||||
warn!("fs compatibility: bytes_per_cluster value '{}' in BPB exceeds 32k, and thus may be incompatible with some implementations", bytes_per_cluster);
|
||||
}
|
||||
|
||||
if bpb.reserved_sectors < 1 {
|
||||
return Err(Error::new(ErrorKind::Other, "invalid reserved_sectors value in BPB"));
|
||||
} else if bpb.reserved_sectors != 1 {
|
||||
// Microsoft document indicates fat12 and fat16 code exists that presume this value is 1
|
||||
warn!("fs compatibility: reserved_sectors value '{}' in BPB is not '1', and thus is incompatible with some implementations", bpb.reserved_sectors);
|
||||
}
|
||||
|
||||
if bpb.fats == 0 {
|
||||
return Err(Error::new(ErrorKind::Other, "invalid fats value in BPB"));
|
||||
} else if bpb.fats > 2 {
|
||||
// Microsoft document indicates that few implementations support any values other than 1 or 2
|
||||
warn!("fs compatibility: numbers of FATs '{}' in BPB is greater than '2', and thus is incompatible with some implementations", bpb.fats);
|
||||
}
|
||||
|
||||
if bpb.is_fat32() {
|
||||
bpb.sectors_per_fat_32 = rdr.read_u32::<LittleEndian>()?;
|
||||
bpb.extended_flags = rdr.read_u16::<LittleEndian>()?;
|
||||
bpb.fs_version = rdr.read_u16::<LittleEndian>()?;
|
||||
// TODO: Validate the only valid fs_version value is still zero, then check that here
|
||||
bpb.root_dir_first_cluster = rdr.read_u32::<LittleEndian>()?;
|
||||
bpb.fs_info_sector = rdr.read_u16::<LittleEndian>()?;
|
||||
bpb.backup_boot_sector = rdr.read_u16::<LittleEndian>()?;
|
||||
@ -178,12 +207,15 @@ impl BiosParameterBlock {
|
||||
rdr.read_exact(&mut bpb.volume_label)?;
|
||||
rdr.read_exact(&mut bpb.fs_type_label)?;
|
||||
}
|
||||
|
||||
// when the extended boot signature is anything other than 0x29, the fields are invalid
|
||||
if bpb.ext_sig != 0x29 {
|
||||
// fields after ext_sig are not used - clean them
|
||||
bpb.volume_id = 0;
|
||||
bpb.volume_label = [0; 11];
|
||||
bpb.fs_type_label = [0; 8];
|
||||
}
|
||||
|
||||
Ok(bpb)
|
||||
}
|
||||
|
||||
@ -192,12 +224,40 @@ impl BiosParameterBlock {
|
||||
}
|
||||
|
||||
fn active_fat(&self) -> u16 {
|
||||
// The zero-based number of the active FAT is only valid if mirroring is disabled.
|
||||
if self.mirroring_enabled() {
|
||||
0
|
||||
} else {
|
||||
self.extended_flags & 0x0F
|
||||
}
|
||||
}
|
||||
|
||||
fn status_flags(&self) -> FsStatusFlags {
|
||||
FsStatusFlags::decode(self.reserved_1)
|
||||
}
|
||||
|
||||
fn is_fat32(&self) -> bool {
|
||||
// because this field must be zero on FAT32, and
|
||||
// because it must be non-zero on FAT12/FAT16,
|
||||
// this provides a simple way to detect FAT32
|
||||
self.sectors_per_fat_16 == 0
|
||||
}
|
||||
|
||||
fn sectors_per_fat(&self) -> u32 {
|
||||
if self.is_fat32() {
|
||||
self.sectors_per_fat_32
|
||||
} else {
|
||||
self.sectors_per_fat_16 as u32
|
||||
}
|
||||
}
|
||||
|
||||
fn total_sectors(&self) -> u32 {
|
||||
if self.total_sectors_16 == 0 {
|
||||
self.total_sectors_32
|
||||
} else {
|
||||
self.total_sectors_16 as u32
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
@ -216,7 +276,7 @@ impl BootRecord {
|
||||
rdr.read_exact(&mut boot.oem_name)?;
|
||||
boot.bpb = BiosParameterBlock::deserialize(rdr)?;
|
||||
|
||||
if boot.bpb.sectors_per_fat_16 == 0 {
|
||||
if boot.bpb.is_fat32() {
|
||||
rdr.read_exact(&mut boot.boot_code[0..420])?;
|
||||
} else {
|
||||
rdr.read_exact(&mut boot.boot_code[0..448])?;
|
||||
@ -418,16 +478,8 @@ impl<T: ReadWriteSeek> FileSystem<T> {
|
||||
return Err(Error::new(ErrorKind::Other, "Unknown FS version"));
|
||||
}
|
||||
|
||||
let total_sectors = if bpb.total_sectors_16 == 0 {
|
||||
bpb.total_sectors_32
|
||||
} else {
|
||||
bpb.total_sectors_16 as u32
|
||||
};
|
||||
let sectors_per_fat = if bpb.sectors_per_fat_16 == 0 {
|
||||
bpb.sectors_per_fat_32
|
||||
} else {
|
||||
bpb.sectors_per_fat_16 as u32
|
||||
};
|
||||
let total_sectors = bpb.total_sectors();
|
||||
let sectors_per_fat = bpb.sectors_per_fat();
|
||||
let root_dir_bytes = bpb.root_entries as u32 * DIR_ENTRY_SIZE as u32;
|
||||
let root_dir_sectors = (root_dir_bytes + (bpb.bytes_per_sector as u32 - 1)) / bpb.bytes_per_sector as u32;
|
||||
let first_data_sector = bpb.reserved_sectors as u32 + (bpb.fats as u32 * sectors_per_fat) + root_dir_sectors;
|
||||
@ -565,11 +617,7 @@ impl<T: ReadWriteSeek> FileSystem<T> {
|
||||
}
|
||||
|
||||
fn fat_slice<'b>(&'b self) -> DiskSlice<'b, T> {
|
||||
let sectors_per_fat = if self.bpb.sectors_per_fat_16 == 0 {
|
||||
self.bpb.sectors_per_fat_32
|
||||
} else {
|
||||
self.bpb.sectors_per_fat_16 as u32
|
||||
};
|
||||
let sectors_per_fat = self.bpb.sectors_per_fat();
|
||||
let mirroring_enabled = self.bpb.mirroring_enabled();
|
||||
let (fat_first_sector, mirrors) = if mirroring_enabled {
|
||||
(self.bpb.reserved_sectors as u32, self.bpb.fats)
|
||||
|
Loading…
Reference in New Issue
Block a user