Add automatic update of timestamps (created, accessed, modified).
Works only if chrono feature is enabled (default).
This commit is contained in:
parent
cdde3ca468
commit
b38ea78f3e
@ -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![];
|
||||
|
@ -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,
|
||||
|
107
src/dir.rs
107
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<DateTime> for chrono::DateTime<Local> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "chrono")]
|
||||
impl From<chrono::Date<Local>> for Date {
|
||||
fn from(date: chrono::Date<Local>) -> Self {
|
||||
Date {
|
||||
year: date.year() as u16,
|
||||
month: date.month() as u16,
|
||||
day: date.day() as u16,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "chrono")]
|
||||
impl From<chrono::DateTime<Local>> for DateTime {
|
||||
fn from(date_time: chrono::DateTime<Local>) -> 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);
|
||||
|
10
src/file.rs
10
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)
|
||||
}
|
||||
}
|
||||
|
@ -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<T: ReadWriteSeek>(disk: &'a mut T) -> io::Result<FileSystem<'a>> {
|
||||
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"));
|
||||
@ -113,6 +117,7 @@ impl <'a> FileSystem<'a> {
|
||||
boot,
|
||||
first_data_sector,
|
||||
root_dir_sectors,
|
||||
read_only,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user