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() {
|
fn main() {
|
||||||
let file = File::open("resources/fat32.img").unwrap();
|
let file = File::open("resources/fat32.img").unwrap();
|
||||||
let mut buf_rdr = BufStream::new(file);
|
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 root_dir = fs.root_dir();
|
||||||
let mut file = root_dir.open_file(&env::args().nth(1).unwrap()).unwrap();
|
let mut file = root_dir.open_file(&env::args().nth(1).unwrap()).unwrap();
|
||||||
let mut buf = vec![];
|
let mut buf = vec![];
|
||||||
|
@ -25,7 +25,7 @@ fn format_file_size(size: u64) -> String {
|
|||||||
fn main() {
|
fn main() {
|
||||||
let file = File::open("resources/fat32.img").unwrap();
|
let file = File::open("resources/fat32.img").unwrap();
|
||||||
let mut buf_rdr = BufStream::new(file);
|
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 root_dir = fs.root_dir();
|
||||||
let dir = match env::args().nth(1) {
|
let dir = match env::args().nth(1) {
|
||||||
None => root_dir,
|
None => root_dir,
|
||||||
|
107
src/dir.rs
107
src/dir.rs
@ -7,7 +7,7 @@ use std::cmp;
|
|||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
use chrono::{TimeZone, Local};
|
use chrono::{TimeZone, Local, Datelike, Timelike};
|
||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
use chrono;
|
use chrono;
|
||||||
|
|
||||||
@ -133,19 +133,81 @@ impl DirFileEntryData {
|
|||||||
self.size = size;
|
self.size = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_dir(&self) -> bool {
|
pub(crate) fn is_dir(&self) -> bool {
|
||||||
self.attrs.contains(FileAttributes::DIRECTORY)
|
self.attrs.contains(FileAttributes::DIRECTORY)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_file(&self) -> bool {
|
pub(crate) fn is_file(&self) -> bool {
|
||||||
!self.is_dir()
|
!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) {
|
pub(crate) fn set_modified(&mut self, date_time: DateTime) {
|
||||||
self.modify_date = date_time.date.to_u16();
|
self.modify_date = date_time.date.to_u16();
|
||||||
self.modify_time = date_time.time.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<()> {
|
pub(crate) fn serialize(&self, wrt: &mut Write) -> io::Result<()> {
|
||||||
wrt.write_all(&self.name)?;
|
wrt.write_all(&self.name)?;
|
||||||
wrt.write_u8(self.attrs.bits())?;
|
wrt.write_u8(self.attrs.bits())?;
|
||||||
@ -286,7 +348,7 @@ impl DirEntryData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// DOS compatible date
|
/// DOS compatible date
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct Date {
|
pub struct Date {
|
||||||
pub year: u16,
|
pub year: u16,
|
||||||
pub month: u16,
|
pub month: u16,
|
||||||
@ -294,6 +356,7 @@ pub struct Date {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Date {
|
impl Date {
|
||||||
|
|
||||||
pub(crate) fn from_u16(dos_date: u16) -> Self {
|
pub(crate) fn from_u16(dos_date: u16) -> Self {
|
||||||
let (year, month, day) = ((dos_date >> 9) + 1980, (dos_date >> 5) & 0xF, dos_date & 0x1F);
|
let (year, month, day) = ((dos_date >> 9) + 1980, (dos_date >> 5) & 0xF, dos_date & 0x1F);
|
||||||
Date { year, month, day }
|
Date { year, month, day }
|
||||||
@ -305,7 +368,7 @@ impl Date {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// DOS compatible time
|
/// DOS compatible time
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct Time {
|
pub struct Time {
|
||||||
pub hour: u16,
|
pub hour: u16,
|
||||||
pub min: u16,
|
pub min: u16,
|
||||||
@ -324,7 +387,7 @@ impl Time {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// DOS compatible date and time
|
/// DOS compatible date and time
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct DateTime {
|
pub struct DateTime {
|
||||||
pub date: Date,
|
pub date: Date,
|
||||||
pub time: Time,
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct FileEntryInfo {
|
pub(crate) struct FileEntryInfo {
|
||||||
pub(crate) data: DirFileEntryData,
|
pub(crate) data: DirFileEntryData,
|
||||||
@ -458,17 +546,17 @@ impl <'a, 'b> DirEntry<'a, 'b> {
|
|||||||
|
|
||||||
/// Returns file creation date and time.
|
/// Returns file creation date and time.
|
||||||
pub fn created(&self) -> DateTime {
|
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.
|
/// Returns file last access date.
|
||||||
pub fn accessed(&self) -> Date {
|
pub fn accessed(&self) -> Date {
|
||||||
Date::from_u16(self.data.access_date)
|
self.data.accessed()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns file last modification date and time.
|
/// Returns file last modification date and time.
|
||||||
pub fn modified(&self) -> DateTime {
|
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()
|
..Default::default()
|
||||||
};
|
};
|
||||||
raw_entry.set_first_cluster(first_cluster, self.fs.fat_type);
|
raw_entry.set_first_cluster(first_cluster, self.fs.fat_type);
|
||||||
|
raw_entry.reset_created();
|
||||||
raw_entry.serialize(&mut stream)?;
|
raw_entry.serialize(&mut stream)?;
|
||||||
let end_pos = stream.seek(io::SeekFrom::Current(0))?;
|
let end_pos = stream.seek(io::SeekFrom::Current(0))?;
|
||||||
let abs_pos = stream.abs_pos().map(|p| p - DIR_ENTRY_SIZE);
|
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;
|
let offset = self.offset;
|
||||||
match self.entry {
|
match self.entry {
|
||||||
Some(ref mut e) => {
|
Some(ref mut e) => {
|
||||||
|
e.data.reset_modified();
|
||||||
if e.data.size().map_or(false, |s| offset > s) {
|
if e.data.size().map_or(false, |s| offset > s) {
|
||||||
e.data.set_size(offset);
|
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.
|
/// 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) {
|
pub fn set_modified(&mut self, date_time: DateTime) {
|
||||||
match self.entry {
|
match self.entry {
|
||||||
Some(ref mut e) => {
|
Some(ref mut e) => {
|
||||||
@ -187,6 +188,11 @@ impl<'a, 'b> Read for File<'a, 'b> {
|
|||||||
}
|
}
|
||||||
self.offset += read_bytes as u32;
|
self.offset += read_bytes as u32;
|
||||||
self.current_cluster = Some(current_cluster);
|
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)
|
Ok(read_bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,14 +86,18 @@ pub struct FileSystem<'a> {
|
|||||||
pub(crate) boot: BootRecord,
|
pub(crate) boot: BootRecord,
|
||||||
pub(crate) first_data_sector: u32,
|
pub(crate) first_data_sector: u32,
|
||||||
pub(crate) root_dir_sectors: u32,
|
pub(crate) root_dir_sectors: u32,
|
||||||
|
pub(crate) read_only: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a> FileSystem<'a> {
|
impl <'a> FileSystem<'a> {
|
||||||
/// Creates new filesystem object instance.
|
/// 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
|
/// Note: creating multiple filesystem objects with one underlying device/disk image can
|
||||||
/// cause filesystem corruption.
|
/// 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)?;
|
let boot = Self::read_boot_record(disk)?;
|
||||||
if boot.boot_sig != [0x55, 0xAA] {
|
if boot.boot_sig != [0x55, 0xAA] {
|
||||||
return Err(Error::new(ErrorKind::Other, "invalid signature"));
|
return Err(Error::new(ErrorKind::Other, "invalid signature"));
|
||||||
@ -113,6 +117,7 @@ impl <'a> FileSystem<'a> {
|
|||||||
boot,
|
boot,
|
||||||
first_data_sector,
|
first_data_sector,
|
||||||
root_dir_sectors,
|
root_dir_sectors,
|
||||||
|
read_only,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ fn call_with_fs(f: &Fn(FileSystem) -> (), filename: &str) {
|
|||||||
let _ = env_logger::init();
|
let _ = env_logger::init();
|
||||||
let file = fs::File::open(filename).unwrap();
|
let file = fs::File::open(filename).unwrap();
|
||||||
let mut buf_file = BufStream::new(file);
|
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);
|
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 file = fs::OpenOptions::new().read(true).write(true).open(&tmp_path).unwrap();
|
||||||
let mut buf_file = BufStream::new(file);
|
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);
|
f(fs);
|
||||||
}
|
}
|
||||||
fs::remove_file(tmp_path).unwrap();
|
fs::remove_file(tmp_path).unwrap();
|
||||||
|
Loading…
Reference in New Issue
Block a user