From b38ea78f3e7d3331fe2d882bcaed0893f4bed131 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Harabie=C5=84?= Date: Wed, 8 Nov 2017 00:18:31 +0100 Subject: [PATCH] Add automatic update of timestamps (created, accessed, modified). Works only if chrono feature is enabled (default). --- examples/cat.rs | 2 +- examples/ls.rs | 2 +- src/dir.rs | 107 ++++++++++++++++++++++++++++++++++++++++++++---- src/file.rs | 10 ++++- src/fs.rs | 7 +++- tests/read.rs | 2 +- tests/write.rs | 2 +- 7 files changed, 116 insertions(+), 16 deletions(-) diff --git a/examples/cat.rs b/examples/cat.rs index 2419f95..d89adf7 100644 --- a/examples/cat.rs +++ b/examples/cat.rs @@ -10,7 +10,7 @@ use fatfs::{FileSystem, BufStream}; fn main() { let file = File::open("resources/fat32.img").unwrap(); let mut buf_rdr = BufStream::new(file); - let fs = FileSystem::new(&mut buf_rdr).unwrap(); + let fs = FileSystem::new(&mut buf_rdr, true).unwrap(); let mut root_dir = fs.root_dir(); let mut file = root_dir.open_file(&env::args().nth(1).unwrap()).unwrap(); let mut buf = vec![]; diff --git a/examples/ls.rs b/examples/ls.rs index 1add4d5..b23e3c6 100644 --- a/examples/ls.rs +++ b/examples/ls.rs @@ -25,7 +25,7 @@ fn format_file_size(size: u64) -> String { fn main() { let file = File::open("resources/fat32.img").unwrap(); let mut buf_rdr = BufStream::new(file); - let fs = FileSystem::new(&mut buf_rdr).unwrap(); + let fs = FileSystem::new(&mut buf_rdr, true).unwrap(); let mut root_dir = fs.root_dir(); let dir = match env::args().nth(1) { None => root_dir, diff --git a/src/dir.rs b/src/dir.rs index 1e1cbe4..48c0dbf 100644 --- a/src/dir.rs +++ b/src/dir.rs @@ -7,7 +7,7 @@ use std::cmp; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; #[cfg(feature = "chrono")] -use chrono::{TimeZone, Local}; +use chrono::{TimeZone, Local, Datelike, Timelike}; #[cfg(feature = "chrono")] use chrono; @@ -133,19 +133,81 @@ impl DirFileEntryData { self.size = size; } - pub fn is_dir(&self) -> bool { + pub(crate) fn is_dir(&self) -> bool { self.attrs.contains(FileAttributes::DIRECTORY) } - pub fn is_file(&self) -> bool { + pub(crate) fn is_file(&self) -> bool { !self.is_dir() } + fn created(&self) -> DateTime { + DateTime::from_u16(self.create_date, self.create_time_1) + } + + fn accessed(&self) -> Date { + Date::from_u16(self.access_date) + } + + fn modified(&self) -> DateTime { + DateTime::from_u16(self.modify_date, self.modify_time) + } + + fn set_created(&mut self, date_time: DateTime) { + self.create_date = date_time.date.to_u16(); + self.create_time_1 = date_time.time.to_u16(); + } + + fn set_accessed(&mut self, date: Date) { + self.access_date = date.to_u16(); + } + pub(crate) fn set_modified(&mut self, date_time: DateTime) { self.modify_date = date_time.date.to_u16(); self.modify_time = date_time.time.to_u16(); } + #[cfg(feature = "chrono")] + pub(crate) fn reset_created(&mut self) { + let now = DateTime::from(chrono::Local::now()); + self.set_created(now); + } + + #[cfg(feature = "chrono")] + pub(crate) fn reset_accessed(&mut self) -> bool { + let now = Date::from(chrono::Local::now().date()); + if now == self.accessed() { + false + } else { + self.set_accessed(now); + true + } + } + + #[cfg(feature = "chrono")] + pub(crate) fn reset_modified(&mut self) { + let now = DateTime::from(chrono::Local::now()); + self.set_modified(now); + } + + #[cfg(not(feature = "chrono"))] + pub(crate) fn reset_created(&mut self) { + // nop - user controls timestamps manually + false + } + + #[cfg(not(feature = "chrono"))] + pub(crate) fn reset_accessed(&mut self) -> bool { + // nop - user controls timestamps manually + false + } + + #[cfg(not(feature = "chrono"))] + pub(crate) fn reset_modified(&mut self) { + // nop - user controls timestamps manually + false + } + pub(crate) fn serialize(&self, wrt: &mut Write) -> io::Result<()> { wrt.write_all(&self.name)?; wrt.write_u8(self.attrs.bits())?; @@ -286,7 +348,7 @@ impl DirEntryData { } /// DOS compatible date -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct Date { pub year: u16, pub month: u16, @@ -294,6 +356,7 @@ pub struct Date { } impl Date { + pub(crate) fn from_u16(dos_date: u16) -> Self { let (year, month, day) = ((dos_date >> 9) + 1980, (dos_date >> 5) & 0xF, dos_date & 0x1F); Date { year, month, day } @@ -305,7 +368,7 @@ impl Date { } /// DOS compatible time -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct Time { pub hour: u16, pub min: u16, @@ -324,7 +387,7 @@ impl Time { } /// DOS compatible date and time -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq)] pub struct DateTime { pub date: Date, pub time: Time, @@ -354,6 +417,31 @@ impl From for chrono::DateTime { } } +#[cfg(feature = "chrono")] +impl From> for Date { + fn from(date: chrono::Date) -> Self { + Date { + year: date.year() as u16, + month: date.month() as u16, + day: date.day() as u16, + } + } +} + +#[cfg(feature = "chrono")] +impl From> for DateTime { + fn from(date_time: chrono::DateTime) -> Self { + DateTime { + date: Date::from(date_time.date()), + time: Time { + hour: date_time.hour() as u16, + min: date_time.minute() as u16, + sec: date_time.second() as u16, + }, + } + } +} + #[derive(Clone, Debug)] pub(crate) struct FileEntryInfo { pub(crate) data: DirFileEntryData, @@ -458,17 +546,17 @@ impl <'a, 'b> DirEntry<'a, 'b> { /// Returns file creation date and time. pub fn created(&self) -> DateTime { - DateTime::from_u16(self.data.create_date, self.data.create_time_1) + self.data.created() } /// Returns file last access date. pub fn accessed(&self) -> Date { - Date::from_u16(self.data.access_date) + self.data.accessed() } /// Returns file last modification date and time. pub fn modified(&self) -> DateTime { - DateTime::from_u16(self.data.modify_date, self.data.modify_time) + self.data.modified() } } @@ -750,6 +838,7 @@ impl <'a, 'b> Dir<'a, 'b> { ..Default::default() }; raw_entry.set_first_cluster(first_cluster, self.fs.fat_type); + raw_entry.reset_created(); raw_entry.serialize(&mut stream)?; let end_pos = stream.seek(io::SeekFrom::Current(0))?; let abs_pos = stream.abs_pos().map(|p| p - DIR_ENTRY_SIZE); diff --git a/src/file.rs b/src/file.rs index e31642a..e76df70 100644 --- a/src/file.rs +++ b/src/file.rs @@ -36,10 +36,11 @@ impl <'a, 'b> File<'a, 'b> { let offset = self.offset; match self.entry { Some(ref mut e) => { + e.data.reset_modified(); if e.data.size().map_or(false, |s| offset > s) { e.data.set_size(offset); - self.entry_dirty = true; } + self.entry_dirty = true; }, _ => {}, } @@ -102,7 +103,7 @@ impl <'a, 'b> File<'a, 'b> { /// Set date and time of last modification for this file. /// - /// Note: this library doesn't know current time so changing timestamp must be done manually. + /// Note: if chrono feature is enabled (default) library automatically updates all timestamps pub fn set_modified(&mut self, date_time: DateTime) { match self.entry { Some(ref mut e) => { @@ -187,6 +188,11 @@ impl<'a, 'b> Read for File<'a, 'b> { } self.offset += read_bytes as u32; self.current_cluster = Some(current_cluster); + + match self.entry { + Some(ref mut e) if !self.fs.read_only => self.entry_dirty |= e.data.reset_accessed(), + _ => {}, + } Ok(read_bytes) } } diff --git a/src/fs.rs b/src/fs.rs index 5740ba8..5ff23e7 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -86,14 +86,18 @@ pub struct FileSystem<'a> { pub(crate) boot: BootRecord, pub(crate) first_data_sector: u32, pub(crate) root_dir_sectors: u32, + pub(crate) read_only: bool, } 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(disk: &'a mut T) -> io::Result> { + pub fn new(disk: &'a mut T, read_only: bool) -> io::Result> { let boot = Self::read_boot_record(disk)?; if boot.boot_sig != [0x55, 0xAA] { return Err(Error::new(ErrorKind::Other, "invalid signature")); @@ -113,6 +117,7 @@ impl <'a> FileSystem<'a> { boot, first_data_sector, root_dir_sectors, + read_only, }) } diff --git a/tests/read.rs b/tests/read.rs index 2a3d2f0..1661676 100644 --- a/tests/read.rs +++ b/tests/read.rs @@ -17,7 +17,7 @@ fn call_with_fs(f: &Fn(FileSystem) -> (), filename: &str) { let _ = env_logger::init(); let file = fs::File::open(filename).unwrap(); let mut buf_file = BufStream::new(file); - let fs = FileSystem::new(&mut buf_file).unwrap(); + let fs = FileSystem::new(&mut buf_file, true).unwrap(); f(fs); } diff --git a/tests/write.rs b/tests/write.rs index daf06b5..b23e43d 100644 --- a/tests/write.rs +++ b/tests/write.rs @@ -25,7 +25,7 @@ fn call_with_fs(f: &Fn(FileSystem) -> (), filename: &str, test_seq: u32) { { let file = fs::OpenOptions::new().read(true).write(true).open(&tmp_path).unwrap(); let mut buf_file = BufStream::new(file); - let fs = FileSystem::new(&mut buf_file).unwrap(); + let fs = FileSystem::new(&mut buf_file, false).unwrap(); f(fs); } fs::remove_file(tmp_path).unwrap();