From 5c0ad0ce180666929b1a2e4e8d949784ae0446ee Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Rafa=C5=82=20Harabie=C5=84?= <rafalh1992@o2.pl>
Date: Wed, 8 Nov 2017 19:59:03 +0100
Subject: [PATCH] More code refactoring.

---
 src/dir.rs  |   3 +-
 src/file.rs |   8 +-
 src/fs.rs   | 232 ++++++++++++++++++++++++++++------------------------
 3 files changed, 130 insertions(+), 113 deletions(-)

diff --git a/src/dir.rs b/src/dir.rs
index f82ac65..b19fc97 100644
--- a/src/dir.rs
+++ b/src/dir.rs
@@ -7,7 +7,8 @@ use std::iter;
 
 use fs::{FileSystemRef, DiskSlice};
 use file::File;
-use dir_entry::*;
+use dir_entry::{DirEntry, DirEntryData, DirFileEntryData, DirLfnEntryData, FileAttributes,
+    DIR_ENTRY_SIZE, LFN_PART_LEN, LFN_ENTRY_LAST_FLAG};
 
 #[derive(Clone)]
 pub(crate) enum DirRawStream<'a, 'b: 'a> {
diff --git a/src/file.rs b/src/file.rs
index 8500a33..41b8956 100644
--- a/src/file.rs
+++ b/src/file.rs
@@ -74,7 +74,7 @@ impl <'a, 'b> File<'a, 'b> {
         // Note: when between clusters it returns position after previous cluster
         match self.current_cluster {
             Some(n) => {
-                let cluster_size = self.fs.get_cluster_size();
+                let cluster_size = self.fs.cluster_size();
                 let offset_in_cluster = self.offset % cluster_size;
                 let offset_in_fs = self.fs.offset_from_cluster(n) + (offset_in_cluster as u64);
                 Some(offset_in_fs)
@@ -152,7 +152,7 @@ impl<'a, 'b> Drop for File<'a, 'b> {
 
 impl<'a, 'b> Read for File<'a, 'b> {
     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
-        let cluster_size = self.fs.get_cluster_size();
+        let cluster_size = self.fs.cluster_size();
         let current_cluster_opt = if self.offset % cluster_size == 0 {
             // next cluster
             match self.current_cluster {
@@ -203,7 +203,7 @@ impl<'a, 'b> Read for File<'a, 'b> {
 
 impl<'a, 'b> Write for File<'a, 'b> {
     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
-        let cluster_size = self.fs.get_cluster_size();
+        let cluster_size = self.fs.cluster_size();
         let offset_in_cluster = self.offset % cluster_size;
         let bytes_left_in_cluster = (cluster_size - offset_in_cluster) as usize;
         let write_size = cmp::min(buf.len(), bytes_left_in_cluster);
@@ -304,7 +304,7 @@ impl<'a, 'b> Seek for File<'a, 'b> {
         if new_pos == self.offset as i64 {
             return Ok(self.offset as u64);
         }
-        let cluster_size = self.fs.get_cluster_size();
+        let cluster_size = self.fs.cluster_size();
         // get number of clusters to seek (favoring previous cluster in corner case)
         let cluster_count = ((new_pos + cluster_size as i64 - 1) / cluster_size as i64 - 1) as isize;
         let old_cluster_count = ((self.offset as i64 + cluster_size as i64 - 1) / cluster_size as i64 - 1) as isize;
diff --git a/src/fs.rs b/src/fs.rs
index 032b694..156f988 100644
--- a/src/fs.rs
+++ b/src/fs.rs
@@ -7,6 +7,7 @@ use byteorder::{LittleEndian, ReadBytesExt};
 
 use file::File;
 use dir::{DirRawStream, Dir};
+use dir_entry::DIR_ENTRY_SIZE;
 use table::{ClusterIterator, alloc_cluster};
 
 // FAT implementation based on:
@@ -18,6 +19,18 @@ pub enum FatType {
     Fat12, Fat16, Fat32,
 }
 
+impl FatType {
+    fn from_clusters(total_clusters: u32) -> FatType {
+        if total_clusters < 4085 {
+            FatType::Fat12
+        } else if total_clusters < 65525 {
+            FatType::Fat16
+        } else {
+            FatType::Fat32
+        }
+    }
+}
+
 pub trait ReadSeek: Read + Seek {}
 impl<T> ReadSeek for T where T: Read + Seek {}
 
@@ -56,102 +69,8 @@ struct BiosParameterBlock {
     fs_type_label: [u8; 8],
 }
 
-#[allow(dead_code)]
-struct BootRecord {
-    bootjmp: [u8; 3],
-    oem_name: [u8; 8],
-    bpb: BiosParameterBlock,
-    boot_code: [u8; 448],
-    boot_sig: [u8; 2],
-}
-
-impl Default for BootRecord {
-    fn default() -> BootRecord {
-        BootRecord {
-            bootjmp: Default::default(),
-            oem_name: Default::default(),
-            bpb: Default::default(),
-            boot_code: [0; 448],
-            boot_sig: Default::default(),
-        }
-    }
-}
-
-pub(crate) type FileSystemRef<'a, 'b: 'a> = &'a FileSystem<'b>;
-
-/// FAT filesystem main struct.
-pub struct FileSystem<'a> {
-    pub(crate) disk: RefCell<&'a mut ReadWriteSeek>,
-    pub(crate) read_only: bool,
-    fat_type: FatType,
-    bpb: BiosParameterBlock,
-    first_data_sector: u32,
-    root_dir_sectors: u32,
-}
-
-impl <'a> FileSystem<'a> {
-    /// Creates new filesystem object instance.
-    ///
-    /// read_only argument is a hint for library. It doesnt prevent user from writing to file.
-    /// For now it prevents accessed date field automatic update in dir entry.
-    ///
-    /// Note: creating multiple filesystem objects with one underlying device/disk image can
-    /// cause filesystem corruption.
-    pub fn new<T: ReadWriteSeek>(disk: &'a mut T, read_only: bool) -> io::Result<FileSystem<'a>> {
-        let boot = Self::read_boot_record(disk)?;
-        if boot.boot_sig != [0x55, 0xAA] {
-            return Err(Error::new(ErrorKind::Other, "invalid signature"));
-        }
-
-        let total_sectors = if boot.bpb.total_sectors_16 == 0 { boot.bpb.total_sectors_32 } else { boot.bpb.total_sectors_16 as u32 };
-        let sectors_per_fat = if boot.bpb.sectors_per_fat_16 == 0 { boot.bpb.sectors_per_fat_32 } else { boot.bpb.sectors_per_fat_16 as u32 };
-        let root_dir_sectors = (((boot.bpb.root_entries * 32) + (boot.bpb.bytes_per_sector - 1)) / boot.bpb.bytes_per_sector) as u32;
-        let first_data_sector = boot.bpb.reserved_sectors as u32 + (boot.bpb.fats as u32 * sectors_per_fat) + root_dir_sectors;
-        let data_sectors = total_sectors - (boot.bpb.reserved_sectors as u32 + (boot.bpb.fats as u32 * sectors_per_fat) + root_dir_sectors as u32);
-        let total_clusters = data_sectors / boot.bpb.sectors_per_cluster as u32;
-        let fat_type = Self::fat_type_from_clusters(total_clusters);
-
-        Ok(FileSystem {
-            disk: RefCell::new(disk),
-            read_only,
-            fat_type,
-            bpb: boot.bpb,
-            first_data_sector,
-            root_dir_sectors,
-        })
-    }
-
-    /// Returns type of used File Allocation Table (FAT).
-    pub fn fat_type(&self) -> FatType {
-        self.fat_type
-    }
-
-    /// Returns volume identifier read from BPB in Boot Sector.
-    pub fn volume_id(&self) -> u32 {
-        self.bpb.volume_id
-    }
-
-    /// Returns volume label from BPB in Boot Sector.
-    ///
-    /// Note: File with VOLUME_ID attribute in root directory is ignored by this library.
-    /// Only label from BPB is used.
-    pub fn volume_label(&self) -> String {
-        String::from_utf8_lossy(&self.bpb.volume_label).trim_right().to_string()
-    }
-
-    /// Returns root directory object allowing futher penetration of filesystem structure.
-    pub fn root_dir<'b>(&'b self) -> Dir<'b, 'a> {
-        let root_rdr = {
-            match self.fat_type {
-                FatType::Fat12 | FatType::Fat16 => DirRawStream::Root(DiskSlice::from_sectors(
-                   self.first_data_sector - self.root_dir_sectors, self.root_dir_sectors, 1, self)),
-                _ => DirRawStream::File(File::new(Some(self.bpb.root_dir_first_cluster), None, self)),
-            }
-        };
-        Dir::new(root_rdr, self)
-    }
-
-    fn read_bpb(rdr: &mut Read) -> io::Result<BiosParameterBlock> {
+impl BiosParameterBlock {
+    fn deserialize(rdr: &mut Read) -> io::Result<BiosParameterBlock> {
         let mut bpb: BiosParameterBlock = Default::default();
         bpb.bytes_per_sector = rdr.read_u16::<LittleEndian>()?;
         bpb.sectors_per_cluster = rdr.read_u8()?;
@@ -204,22 +123,23 @@ impl <'a> FileSystem<'a> {
         }
         Ok(bpb)
     }
+}
 
-    fn fat_type_from_clusters(total_clusters: u32) -> FatType {
-        if total_clusters < 4085 {
-            FatType::Fat12
-        } else if total_clusters < 65525 {
-            FatType::Fat16
-        } else {
-            FatType::Fat32
-        }
-    }
+#[allow(dead_code)]
+struct BootRecord {
+    bootjmp: [u8; 3],
+    oem_name: [u8; 8],
+    bpb: BiosParameterBlock,
+    boot_code: [u8; 448],
+    boot_sig: [u8; 2],
+}
 
-    fn read_boot_record(rdr: &mut Read) -> io::Result<BootRecord> {
+impl BootRecord {
+    fn deserialize(rdr: &mut Read) -> io::Result<BootRecord> {
         let mut boot: BootRecord = Default::default();
         rdr.read_exact(&mut boot.bootjmp)?;
         rdr.read_exact(&mut boot.oem_name)?;
-        boot.bpb = Self::read_bpb(rdr)?;
+        boot.bpb = BiosParameterBlock::deserialize(rdr)?;
 
         if boot.bpb.sectors_per_fat_16 == 0 {
             rdr.read_exact(&mut boot.boot_code[0..420])?;
@@ -229,6 +149,102 @@ impl <'a> FileSystem<'a> {
         rdr.read_exact(&mut boot.boot_sig)?;
         Ok(boot)
     }
+}
+
+impl Default for BootRecord {
+    fn default() -> BootRecord {
+        BootRecord {
+            bootjmp: Default::default(),
+            oem_name: Default::default(),
+            bpb: Default::default(),
+            boot_code: [0; 448],
+            boot_sig: Default::default(),
+        }
+    }
+}
+
+pub(crate) type FileSystemRef<'a, 'b: 'a> = &'a FileSystem<'b>;
+
+/// FAT filesystem main struct.
+pub struct FileSystem<'a> {
+    pub(crate) disk: RefCell<&'a mut ReadWriteSeek>,
+    pub(crate) read_only: bool,
+    fat_type: FatType,
+    bpb: BiosParameterBlock,
+    first_data_sector: u32,
+    root_dir_sectors: u32,
+}
+
+impl <'a> FileSystem<'a> {
+    /// Creates new filesystem object instance.
+    ///
+    /// read_only argument is a hint for library. It doesnt prevent user from writing to file.
+    /// For now it prevents accessed date field automatic update in dir entry.
+    ///
+    /// Note: creating multiple filesystem objects with one underlying device/disk image can
+    /// cause filesystem corruption.
+    pub fn new<T: ReadWriteSeek>(disk: &'a mut T, read_only: bool) -> io::Result<FileSystem<'a>> {
+        let bpb = {
+            let boot = BootRecord::deserialize(disk)?;
+            if boot.boot_sig != [0x55, 0xAA] {
+                return Err(Error::new(ErrorKind::Other, "invalid signature"));
+            }
+            boot.bpb
+        };
+
+        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 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;
+        let fat_sectors = bpb.fats as u32 * sectors_per_fat;
+        let data_sectors = total_sectors - (bpb.reserved_sectors as u32 + fat_sectors + root_dir_sectors as u32);
+        let total_clusters = data_sectors / bpb.sectors_per_cluster as u32;
+        let fat_type = FatType::from_clusters(total_clusters);
+
+        Ok(FileSystem {
+            disk: RefCell::new(disk),
+            read_only,
+            fat_type,
+            bpb: bpb,
+            first_data_sector,
+            root_dir_sectors,
+        })
+    }
+
+    /// Returns type of used File Allocation Table (FAT).
+    pub fn fat_type(&self) -> FatType {
+        self.fat_type
+    }
+
+    /// Returns volume identifier read from BPB in Boot Sector.
+    pub fn volume_id(&self) -> u32 {
+        self.bpb.volume_id
+    }
+
+    /// Returns volume label from BPB in Boot Sector.
+    ///
+    /// Note: File with VOLUME_ID attribute in root directory is ignored by this library.
+    /// Only label from BPB is used.
+    pub fn volume_label(&self) -> String {
+        String::from_utf8_lossy(&self.bpb.volume_label).trim_right().to_string()
+    }
+
+    /// Returns root directory object allowing futher penetration of filesystem structure.
+    pub fn root_dir<'b>(&'b self) -> Dir<'b, 'a> {
+        let root_rdr = {
+            match self.fat_type {
+                FatType::Fat12 | FatType::Fat16 => DirRawStream::Root(DiskSlice::from_sectors(
+                   self.first_data_sector - self.root_dir_sectors, self.root_dir_sectors, 1, self)),
+                _ => DirRawStream::File(File::new(Some(self.bpb.root_dir_first_cluster), None, self)),
+            }
+        };
+        Dir::new(root_rdr, self)
+    }
 
     pub(crate) fn offset_from_sector(&self, sector: u32) -> u64 {
         (sector as u64) * self.bpb.bytes_per_sector as u64
@@ -238,7 +254,7 @@ impl <'a> FileSystem<'a> {
         ((cluster - 2) * self.bpb.sectors_per_cluster as u32) + self.first_data_sector
     }
 
-    pub(crate) fn get_cluster_size(&self) -> u32 {
+    pub(crate) fn cluster_size(&self) -> u32 {
         self.bpb.sectors_per_cluster as u32 * self.bpb.bytes_per_sector as u32
     }