Add set_created and set_accessed public functions in File.

Small code refactoring included.
This commit is contained in:
Rafał Harabień 2017-11-08 01:23:00 +01:00
parent b38ea78f3e
commit c4e6d59492
2 changed files with 166 additions and 76 deletions

View File

@ -107,13 +107,20 @@ pub(crate) struct DirFileEntryData {
}
impl DirFileEntryData {
fn new(name: [u8; 11], attrs: FileAttributes) -> Self {
DirFileEntryData {
name, attrs,
..Default::default()
}
}
pub(crate) fn first_cluster(&self, fat_type: FatType) -> Option<u32> {
let first_cluster_hi = if fat_type == FatType::Fat32 { self.first_cluster_hi } else { 0 };
let n = ((first_cluster_hi as u32) << 16) | self.first_cluster_lo as u32;
if n == 0 { None } else { Some(n) }
}
pub(crate) fn set_first_cluster(&mut self, cluster: Option<u32>, fat_type: FatType) {
fn set_first_cluster(&mut self, cluster: Option<u32>, fat_type: FatType) {
let n = cluster.unwrap_or(0);
if fat_type == FatType::Fat32 {
self.first_cluster_hi = (n >> 16) as u16;
@ -129,7 +136,7 @@ impl DirFileEntryData {
}
}
pub(crate) fn set_size(&mut self, size: u32) {
fn set_size(&mut self, size: u32) {
self.size = size;
}
@ -162,19 +169,19 @@ impl DirFileEntryData {
self.access_date = date.to_u16();
}
pub(crate) fn set_modified(&mut self, date_time: DateTime) {
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) {
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 {
fn reset_accessed(&mut self) -> bool {
let now = Date::from(chrono::Local::now().date());
if now == self.accessed() {
false
@ -185,30 +192,30 @@ impl DirFileEntryData {
}
#[cfg(feature = "chrono")]
pub(crate) fn reset_modified(&mut self) {
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) {
fn reset_created(&mut self) {
// nop - user controls timestamps manually
false
}
#[cfg(not(feature = "chrono"))]
pub(crate) fn reset_accessed(&mut self) -> bool {
fn reset_accessed(&mut self) -> bool {
// nop - user controls timestamps manually
false
}
#[cfg(not(feature = "chrono"))]
pub(crate) fn reset_modified(&mut self) {
fn reset_modified(&mut self) {
// nop - user controls timestamps manually
false
}
pub(crate) fn serialize(&self, wrt: &mut Write) -> io::Result<()> {
fn serialize(&self, wrt: &mut Write) -> io::Result<()> {
wrt.write_all(&self.name)?;
wrt.write_u8(self.attrs.bits())?;
wrt.write_u8(self.reserved_0)?;
@ -228,6 +235,10 @@ impl DirFileEntryData {
self.name[0] == DIR_ENTRY_FREE_FLAG
}
fn set_free(&mut self) {
self.name[0] = DIR_ENTRY_FREE_FLAG;
}
fn is_end(&self) -> bool {
self.name[0] == 0
}
@ -247,6 +258,20 @@ struct DirLfnEntryData {
}
impl DirLfnEntryData {
fn new(order: u8, checksum: u8) -> Self {
DirLfnEntryData {
order, checksum,
attrs: FileAttributes::LFN,
..Default::default()
}
}
fn copy_name_from_slice(&mut self, lfn_part: &[u16; LFN_PART_LEN]) {
self.name_0.copy_from_slice(&lfn_part[0..5]);
self.name_1.copy_from_slice(&lfn_part[5..5+6]);
self.name_2.copy_from_slice(&lfn_part[11..11+2]);
}
fn serialize(&self, wrt: &mut Write) -> io::Result<()> {
wrt.write_u8(self.order)?;
for ch in self.name_0.iter() {
@ -269,6 +294,10 @@ impl DirLfnEntryData {
self.order == DIR_ENTRY_FREE_FLAG
}
fn set_free(&mut self) {
self.order = DIR_ENTRY_FREE_FLAG;
}
fn is_end(&self) -> bool {
self.order == 0
}
@ -339,6 +368,13 @@ impl DirEntryData {
}
}
fn set_free(&mut self) {
match self {
&mut DirEntryData::File(ref mut file) => file.set_free(),
&mut DirEntryData::Lfn(ref mut lfn) => lfn.set_free(),
}
}
fn is_end(&self) -> bool {
match self {
&DirEntryData::File(ref file) => file.is_end(),
@ -443,13 +479,80 @@ impl From<chrono::DateTime<Local>> for DateTime {
}
#[derive(Clone, Debug)]
pub(crate) struct FileEntryInfo {
pub(crate) data: DirFileEntryData,
pub(crate) struct DirEntryEditor {
data: DirFileEntryData,
pos: u64,
dirty: bool,
}
impl FileEntryInfo {
pub(crate) fn write(&self, fs: FileSystemRef) -> io::Result<()> {
impl DirEntryEditor {
fn new(data: DirFileEntryData, pos: u64) -> DirEntryEditor {
DirEntryEditor {
data, pos,
dirty: false,
}
}
pub(crate) fn inner(&self) -> &DirFileEntryData {
&self.data
}
pub(crate) fn set_first_cluster(&mut self, first_cluster: Option<u32>, fat_type: FatType) {
if first_cluster != self.data.first_cluster(fat_type) {
self.data.set_first_cluster(first_cluster, fat_type);
self.dirty = true;
}
}
pub(crate) fn set_size(&mut self, size: u32) {
match self.data.size() {
Some(n) if size != n => {
self.data.set_size(size);
self.dirty = true;
},
_ => {},
}
}
pub(crate) fn set_created(&mut self, date_time: DateTime) {
if date_time != self.data.created() {
self.data.set_created(date_time);
self.dirty = true;
}
}
pub(crate) fn set_accessed(&mut self, date: Date) {
if date != self.data.accessed() {
self.data.set_accessed(date);
self.dirty = true;
}
}
pub(crate) fn set_modified(&mut self, date_time: DateTime) {
if date_time != self.data.modified() {
self.data.set_modified(date_time);
self.dirty = true;
}
}
pub(crate) fn reset_accessed(&mut self) {
self.dirty |= self.data.reset_accessed();
}
pub(crate) fn reset_modified(&mut self) {
self.data.reset_modified();
self.dirty = true;
}
pub(crate) fn flush(&mut self, fs: FileSystemRef) -> io::Result<()> {
if self.dirty {
self.write(fs)?;
self.dirty = false;
}
Ok(())
}
fn write(&self, fs: FileSystemRef) -> io::Result<()> {
let mut disk = fs.disk.borrow_mut();
disk.seek(io::SeekFrom::Start(self.pos))?;
self.data.serialize(&mut *disk)
@ -510,11 +613,8 @@ impl <'a, 'b> DirEntry<'a, 'b> {
self.data.first_cluster(self.fs.fat_type)
}
fn entry_info(&self) -> FileEntryInfo {
FileEntryInfo {
data: self.data.clone(),
pos: self.entry_pos,
}
fn entry_info(&self) -> DirEntryEditor {
DirEntryEditor::new(self.data.clone(), self.entry_pos)
}
/// Returns File struct for this entry.
@ -700,11 +800,7 @@ impl <'a, 'b> Dir<'a, 'b> {
for _ in 0..num {
let mut data = DirEntryData::deserialize(&mut stream)?;
trace!("removing dir entry {:?}", data);
match data {
DirEntryData::File(ref mut data) =>
data.name[0] = DIR_ENTRY_FREE_FLAG,
DirEntryData::Lfn(ref mut data) => data.order = DIR_ENTRY_FREE_FLAG,
};
data.set_free();
stream.seek(SeekFrom::Current(-(DIR_ENTRY_SIZE as i64)))?;
data.serialize(&mut stream)?;
}
@ -821,24 +917,15 @@ impl <'a, 'b> Dir<'a, 'b> {
if lfn_part_len < LFN_PART_LEN {
lfn_part[lfn_part_len] = 0;
}
let mut lfn_entry = DirLfnEntryData {
order,
attrs: FileAttributes::LFN,
checksum: lfn_chsum,
..Default::default()
};
lfn_entry.name_0.copy_from_slice(&lfn_part[0..5]);
lfn_entry.name_1.copy_from_slice(&lfn_part[5..5+6]);
lfn_entry.name_2.copy_from_slice(&lfn_part[11..11+2]);
let mut lfn_entry = DirLfnEntryData::new(order, lfn_chsum);
lfn_entry.copy_name_from_slice(&lfn_part);
lfn_entry.serialize(&mut stream)?;
}
let mut raw_entry = DirFileEntryData {
name: short_name,
attrs,
..Default::default()
};
let mut raw_entry = DirFileEntryData::new(short_name, attrs);
raw_entry.set_first_cluster(first_cluster, self.fs.fat_type);
raw_entry.reset_created();
raw_entry.reset_accessed();
raw_entry.reset_modified();
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);

View File

@ -4,7 +4,7 @@ use std::io::{SeekFrom, ErrorKind};
use std::io;
use fs::FileSystemRef;
use dir::{FileEntryInfo, DateTime};
use dir::{DirEntryEditor, DateTime, Date};
/// FAT file used for reading and writing.
#[derive(Clone)]
@ -16,19 +16,17 @@ pub struct File<'a, 'b: 'a> {
// current position in this file
offset: u32,
// file dir entry - None for root dir
entry: Option<FileEntryInfo>,
// should file dir entry be flushed?
entry_dirty: bool,
entry: Option<DirEntryEditor>,
// file-system reference
fs: FileSystemRef<'a, 'b>,
}
impl <'a, 'b> File<'a, 'b> {
pub(crate) fn new(first_cluster: Option<u32>, entry: Option<FileEntryInfo>, fs: FileSystemRef<'a, 'b>) -> Self {
pub(crate) fn new(first_cluster: Option<u32>, entry: Option<DirEntryEditor>, fs: FileSystemRef<'a, 'b>) -> Self {
File {
first_cluster, entry, fs,
current_cluster: None, // cluster before first one
offset: 0,
entry_dirty: false,
}
}
@ -36,11 +34,10 @@ 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);
e.reset_modified();
if e.inner().size().map_or(false, |s| offset > s) {
e.set_size(offset);
}
self.entry_dirty = true;
},
_ => {},
}
@ -48,18 +45,12 @@ impl <'a, 'b> File<'a, 'b> {
/// Truncate file to current position.
pub fn truncate(&mut self) -> io::Result<()> {
let offset = self.offset;
match self.entry {
Some(ref mut e) => {
if e.data.size().map_or(false, |s| offset == s) {
return Ok(());
}
e.data.set_size(self.offset);
e.set_size(self.offset);
if self.offset == 0 {
e.data.set_first_cluster(None, self.fs.fat_type);
e.set_first_cluster(None, self.fs.fat_type);
}
self.entry_dirty = true;
},
_ => {},
}
@ -91,32 +82,47 @@ impl <'a, 'b> File<'a, 'b> {
}
}
pub(crate) fn flush_dir_entry(&self) -> io::Result<()> {
if self.entry_dirty {
match self.entry {
Some(ref e) => e.write(self.fs)?,
_ => {},
}
pub(crate) fn flush_dir_entry(&mut self) -> io::Result<()> {
match self.entry {
Some(ref mut e) => e.flush(self.fs)?,
_ => {},
}
Ok(())
}
/// Set date and time of creation for this file.
///
/// Note: if chrono feature is enabled (default) library automatically updates all timestamps
pub fn set_created(&mut self, date_time: DateTime) {
match self.entry {
Some(ref mut e) => e.set_created(date_time),
_ => {},
}
}
/// Set date of last access for this file.
///
/// Note: if chrono feature is enabled (default) library automatically updates all timestamps
pub fn set_accessed(&mut self, date: Date) {
match self.entry {
Some(ref mut e) => e.set_accessed(date),
_ => {},
}
}
/// Set date and time of last modification for this file.
///
/// 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) => {
e.data.set_modified(date_time);
self.entry_dirty = true;
},
Some(ref mut e) => e.set_modified(date_time),
_ => {},
}
}
fn bytes_left_in_file(&self) -> Option<usize> {
match self.entry {
Some(ref e) => e.data.size().map(|s| (s - self.offset) as usize),
Some(ref e) => e.inner().size().map(|s| (s - self.offset) as usize),
None => None,
}
}
@ -124,12 +130,9 @@ impl <'a, 'b> File<'a, 'b> {
fn set_first_cluster(&mut self, cluster: u32) {
self.first_cluster = Some(cluster);
match self.entry {
Some(ref mut e) => {
e.data.set_first_cluster(self.first_cluster, self.fs.fat_type);
},
Some(ref mut e) => e.set_first_cluster(self.first_cluster, self.fs.fat_type),
None => {},
}
self.entry_dirty = true;
}
pub(crate) fn first_cluster(&self) -> Option<u32> {
@ -190,7 +193,7 @@ impl<'a, 'b> Read for File<'a, 'b> {
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(),
Some(ref mut e) if !self.fs.read_only => e.reset_accessed(),
_ => {},
}
Ok(read_bytes)
@ -230,7 +233,7 @@ impl<'a, 'b> Write for File<'a, 'b> {
if self.first_cluster.is_none() {
self.set_first_cluster(new_cluster);
}
if self.entry.clone().map_or(true, |e| e.data.size().is_none()) {
if self.entry.clone().map_or(true, |e| e.inner().size().is_none()) {
// zero new directory cluster
trace!("zeroing directory cluser {}", new_cluster);
let abs_pos = self.fs.offset_from_cluster(new_cluster);
@ -279,16 +282,16 @@ impl<'a, 'b> Seek for File<'a, 'b> {
let mut new_pos = match pos {
SeekFrom::Current(x) => self.offset as i64 + x,
SeekFrom::Start(x) => x as i64,
SeekFrom::End(x) => self.entry.iter().next().map_or(None, |e| e.data.size()).expect("cannot seek from end if size is unknown") as i64 + x,
SeekFrom::End(x) => self.entry.iter().next().map_or(None, |e| e.inner().size()).expect("cannot seek from end if size is unknown") as i64 + x,
};
if new_pos < 0 {
return Err(io::Error::new(ErrorKind::InvalidInput, "invalid seek"));
}
new_pos = match self.entry {
Some(ref e) => {
if e.data.size().map_or(false, |s| new_pos > s as i64) {
if e.inner().size().map_or(false, |s| new_pos > s as i64) {
info!("seek beyond end of file");
e.data.size().unwrap() as i64 // safe
e.inner().size().unwrap() as i64 // safe
} else {
new_pos
}