2017-09-23 06:22:31 +08:00
|
|
|
use std::cmp;
|
2017-09-23 04:27:39 +08:00
|
|
|
use std::io::prelude::*;
|
2017-09-24 08:10:59 +08:00
|
|
|
use std::io::{SeekFrom, ErrorKind};
|
2017-09-23 04:27:39 +08:00
|
|
|
use std::io;
|
2017-09-23 05:20:06 +08:00
|
|
|
|
2017-10-07 20:56:50 +08:00
|
|
|
use fs::FileSystemRef;
|
2017-11-08 08:23:00 +08:00
|
|
|
use dir::{DirEntryEditor, DateTime, Date};
|
2017-09-23 04:27:39 +08:00
|
|
|
|
2017-10-21 22:25:04 +08:00
|
|
|
/// FAT file used for reading and writing.
|
2017-10-06 22:07:11 +08:00
|
|
|
#[derive(Clone)]
|
2017-10-07 20:56:50 +08:00
|
|
|
pub struct File<'a, 'b: 'a> {
|
2017-10-10 22:05:19 +08:00
|
|
|
// Note first_cluster is None if file is empty
|
2017-10-07 08:37:29 +08:00
|
|
|
first_cluster: Option<u32>,
|
2017-10-10 03:14:28 +08:00
|
|
|
// Note: if offset points between clusters current_cluster is the previous cluster
|
2017-09-24 02:16:32 +08:00
|
|
|
current_cluster: Option<u32>,
|
2017-10-10 22:05:19 +08:00
|
|
|
// current position in this file
|
2017-10-09 20:59:52 +08:00
|
|
|
offset: u32,
|
2017-10-10 22:05:19 +08:00
|
|
|
// file dir entry - None for root dir
|
2017-11-08 08:23:00 +08:00
|
|
|
entry: Option<DirEntryEditor>,
|
|
|
|
// file-system reference
|
2017-10-07 20:56:50 +08:00
|
|
|
fs: FileSystemRef<'a, 'b>,
|
2017-09-23 04:27:39 +08:00
|
|
|
}
|
|
|
|
|
2017-10-07 20:56:50 +08:00
|
|
|
impl <'a, 'b> File<'a, 'b> {
|
2017-11-08 08:23:00 +08:00
|
|
|
pub(crate) fn new(first_cluster: Option<u32>, entry: Option<DirEntryEditor>, fs: FileSystemRef<'a, 'b>) -> Self {
|
2017-10-07 20:56:50 +08:00
|
|
|
File {
|
2017-10-09 20:59:52 +08:00
|
|
|
first_cluster, entry, fs,
|
2017-10-10 03:14:28 +08:00
|
|
|
current_cluster: None, // cluster before first one
|
2017-09-24 02:16:32 +08:00
|
|
|
offset: 0,
|
2017-10-09 20:59:52 +08:00
|
|
|
}
|
|
|
|
}
|
2017-10-25 23:20:27 +08:00
|
|
|
|
2017-10-09 20:59:52 +08:00
|
|
|
fn update_size(&mut self) {
|
2017-10-15 21:56:01 +08:00
|
|
|
let offset = self.offset;
|
2017-10-09 20:59:52 +08:00
|
|
|
match self.entry {
|
|
|
|
Some(ref mut e) => {
|
2017-11-08 08:23:00 +08:00
|
|
|
e.reset_modified();
|
|
|
|
if e.inner().size().map_or(false, |s| offset > s) {
|
|
|
|
e.set_size(offset);
|
2017-10-09 20:59:52 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => {},
|
|
|
|
}
|
|
|
|
}
|
2017-10-25 23:20:27 +08:00
|
|
|
|
2017-10-21 22:25:04 +08:00
|
|
|
/// Truncate file to current position.
|
2017-10-10 03:14:28 +08:00
|
|
|
pub fn truncate(&mut self) -> io::Result<()> {
|
2017-10-09 20:59:52 +08:00
|
|
|
match self.entry {
|
|
|
|
Some(ref mut e) => {
|
2017-11-08 08:23:00 +08:00
|
|
|
e.set_size(self.offset);
|
2017-10-10 03:14:28 +08:00
|
|
|
if self.offset == 0 {
|
2017-11-08 08:23:00 +08:00
|
|
|
e.set_first_cluster(None, self.fs.fat_type);
|
2017-10-10 03:14:28 +08:00
|
|
|
}
|
2017-10-09 20:59:52 +08:00
|
|
|
},
|
|
|
|
_ => {},
|
|
|
|
}
|
2017-10-10 03:14:28 +08:00
|
|
|
if self.offset > 0 {
|
2017-10-10 22:05:19 +08:00
|
|
|
debug_assert!(self.current_cluster.is_some());
|
|
|
|
self.fs.cluster_iter(self.current_cluster.unwrap()).truncate() // safe
|
2017-10-10 03:14:28 +08:00
|
|
|
} else {
|
2017-10-10 22:05:19 +08:00
|
|
|
debug_assert!(self.current_cluster.is_none());
|
|
|
|
match self.first_cluster {
|
|
|
|
Some(n) => self.fs.cluster_iter(n).free()?,
|
|
|
|
_ => {},
|
|
|
|
}
|
2017-10-10 03:14:28 +08:00
|
|
|
self.first_cluster = None;
|
|
|
|
Ok(())
|
|
|
|
}
|
2017-10-09 20:59:52 +08:00
|
|
|
}
|
2017-10-25 23:20:27 +08:00
|
|
|
|
2017-10-21 21:51:19 +08:00
|
|
|
pub(crate) fn abs_pos(&self) -> Option<u64> {
|
2017-10-10 22:05:19 +08:00
|
|
|
// Returns current position relative to filesystem start
|
2017-10-10 03:14:28 +08:00
|
|
|
// Note: when between clusters it returns position after previous cluster
|
2017-10-09 20:59:52 +08:00
|
|
|
match self.current_cluster {
|
|
|
|
Some(n) => {
|
|
|
|
let cluster_size = self.fs.get_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)
|
|
|
|
},
|
|
|
|
None => None,
|
|
|
|
}
|
|
|
|
}
|
2017-10-25 23:20:27 +08:00
|
|
|
|
2017-11-08 08:23:00 +08:00
|
|
|
pub(crate) fn flush_dir_entry(&mut self) -> io::Result<()> {
|
|
|
|
match self.entry {
|
|
|
|
Some(ref mut e) => e.flush(self.fs)?,
|
|
|
|
_ => {},
|
2017-10-09 20:59:52 +08:00
|
|
|
}
|
2017-10-10 22:05:19 +08:00
|
|
|
Ok(())
|
2017-10-09 20:59:52 +08:00
|
|
|
}
|
2017-10-25 23:20:27 +08:00
|
|
|
|
2017-11-08 08:23:00 +08:00
|
|
|
/// 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),
|
|
|
|
_ => {},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-21 22:25:04 +08:00
|
|
|
/// Set date and time of last modification for this file.
|
|
|
|
///
|
2017-11-08 07:18:31 +08:00
|
|
|
/// Note: if chrono feature is enabled (default) library automatically updates all timestamps
|
2017-10-09 20:59:52 +08:00
|
|
|
pub fn set_modified(&mut self, date_time: DateTime) {
|
|
|
|
match self.entry {
|
2017-11-08 08:23:00 +08:00
|
|
|
Some(ref mut e) => e.set_modified(date_time),
|
2017-10-09 20:59:52 +08:00
|
|
|
_ => {},
|
|
|
|
}
|
|
|
|
}
|
2017-10-25 23:20:27 +08:00
|
|
|
|
2017-10-09 20:59:52 +08:00
|
|
|
fn bytes_left_in_file(&self) -> Option<usize> {
|
|
|
|
match self.entry {
|
2017-11-08 08:23:00 +08:00
|
|
|
Some(ref e) => e.inner().size().map(|s| (s - self.offset) as usize),
|
2017-10-09 20:59:52 +08:00
|
|
|
None => None,
|
2017-09-23 04:27:39 +08:00
|
|
|
}
|
|
|
|
}
|
2017-10-25 23:20:27 +08:00
|
|
|
|
2017-10-10 03:14:28 +08:00
|
|
|
fn set_first_cluster(&mut self, cluster: u32) {
|
|
|
|
self.first_cluster = Some(cluster);
|
|
|
|
match self.entry {
|
2017-11-08 08:23:00 +08:00
|
|
|
Some(ref mut e) => e.set_first_cluster(self.first_cluster, self.fs.fat_type),
|
2017-10-10 03:14:28 +08:00
|
|
|
None => {},
|
|
|
|
}
|
|
|
|
}
|
2017-10-27 20:12:53 +08:00
|
|
|
|
|
|
|
pub(crate) fn first_cluster(&self) -> Option<u32> {
|
|
|
|
self.first_cluster
|
|
|
|
}
|
2017-09-24 01:42:09 +08:00
|
|
|
}
|
|
|
|
|
2017-10-09 20:59:52 +08:00
|
|
|
impl<'a, 'b> Drop for File<'a, 'b> {
|
|
|
|
fn drop(&mut self) {
|
2017-11-07 08:13:02 +08:00
|
|
|
match self.flush() {
|
|
|
|
Err(err) => error!("flush failed {}", err),
|
|
|
|
_ => {},
|
|
|
|
}
|
2017-10-09 20:59:52 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, 'b> Read for File<'a, 'b> {
|
2017-09-24 01:42:09 +08:00
|
|
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
2017-10-04 19:56:44 +08:00
|
|
|
let cluster_size = self.fs.get_cluster_size();
|
2017-10-10 22:05:19 +08:00
|
|
|
let current_cluster_opt = if self.offset % cluster_size == 0 {
|
|
|
|
// next cluster
|
|
|
|
match self.current_cluster {
|
|
|
|
None => self.first_cluster,
|
|
|
|
Some(n) => {
|
|
|
|
let r = self.fs.cluster_iter(n).next();
|
|
|
|
match r {
|
|
|
|
Some(Err(err)) => return Err(err),
|
|
|
|
Some(Ok(n)) => Some(n),
|
|
|
|
None => None,
|
|
|
|
}
|
|
|
|
},
|
2017-09-24 02:16:32 +08:00
|
|
|
}
|
2017-10-10 22:05:19 +08:00
|
|
|
} else {
|
|
|
|
self.current_cluster
|
|
|
|
};
|
|
|
|
let current_cluster = match current_cluster_opt {
|
|
|
|
Some(n) => n,
|
|
|
|
None => return Ok(0),
|
|
|
|
};
|
|
|
|
let offset_in_cluster = self.offset % cluster_size;
|
|
|
|
let bytes_left_in_cluster = (cluster_size - offset_in_cluster) as usize;
|
|
|
|
let bytes_left_in_file = self.bytes_left_in_file().unwrap_or(bytes_left_in_cluster);
|
|
|
|
let read_size = cmp::min(cmp::min(buf.len(), bytes_left_in_cluster), bytes_left_in_file);
|
|
|
|
if read_size == 0 {
|
|
|
|
return Ok(0);
|
|
|
|
}
|
2017-10-15 06:55:19 +08:00
|
|
|
trace!("read {} bytes in cluster {}", read_size, current_cluster);
|
2017-10-10 22:05:19 +08:00
|
|
|
let offset_in_fs = self.fs.offset_from_cluster(current_cluster) + (offset_in_cluster as u64);
|
|
|
|
let read_bytes = {
|
|
|
|
let mut disk = self.fs.disk.borrow_mut();
|
|
|
|
disk.seek(SeekFrom::Start(offset_in_fs))?;
|
|
|
|
disk.read(&mut buf[..read_size])?
|
|
|
|
};
|
|
|
|
if read_bytes == 0 {
|
|
|
|
return Ok(0);
|
2017-09-24 02:16:32 +08:00
|
|
|
}
|
2017-10-10 22:05:19 +08:00
|
|
|
self.offset += read_bytes as u32;
|
|
|
|
self.current_cluster = Some(current_cluster);
|
2017-11-08 07:18:31 +08:00
|
|
|
|
|
|
|
match self.entry {
|
2017-11-08 08:23:00 +08:00
|
|
|
Some(ref mut e) if !self.fs.read_only => e.reset_accessed(),
|
2017-11-08 07:18:31 +08:00
|
|
|
_ => {},
|
|
|
|
}
|
2017-10-10 22:05:19 +08:00
|
|
|
Ok(read_bytes)
|
2017-09-23 04:27:39 +08:00
|
|
|
}
|
|
|
|
}
|
2017-09-24 08:10:59 +08:00
|
|
|
|
2017-10-09 20:59:52 +08:00
|
|
|
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();
|
2017-10-10 22:05:19 +08:00
|
|
|
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);
|
|
|
|
// Exit early if we are going to write no data
|
|
|
|
if write_size == 0 {
|
|
|
|
return Ok(0);
|
|
|
|
}
|
|
|
|
// Get cluster for write possibly allocating new one
|
|
|
|
let current_cluster = if self.offset % cluster_size == 0 {
|
|
|
|
// next cluster
|
|
|
|
let next_cluster = match self.current_cluster {
|
|
|
|
None => self.first_cluster,
|
|
|
|
Some(n) => {
|
|
|
|
let r = self.fs.cluster_iter(n).next();
|
|
|
|
match r {
|
|
|
|
Some(Err(err)) => return Err(err),
|
|
|
|
Some(Ok(n)) => Some(n),
|
|
|
|
None => None,
|
|
|
|
}
|
|
|
|
},
|
2017-10-10 03:14:28 +08:00
|
|
|
};
|
2017-10-10 22:05:19 +08:00
|
|
|
match next_cluster {
|
2017-10-10 03:14:28 +08:00
|
|
|
Some(n) => n,
|
2017-10-10 22:05:19 +08:00
|
|
|
None => {
|
|
|
|
// end of chain reached - allocate new cluster
|
|
|
|
let new_cluster = self.fs.alloc_cluster(self.current_cluster)?;
|
2017-10-27 21:03:54 +08:00
|
|
|
trace!("allocated cluser {}", new_cluster);
|
2017-10-10 22:05:19 +08:00
|
|
|
if self.first_cluster.is_none() {
|
|
|
|
self.set_first_cluster(new_cluster);
|
|
|
|
}
|
2017-11-08 08:23:00 +08:00
|
|
|
if self.entry.clone().map_or(true, |e| e.inner().size().is_none()) {
|
2017-10-27 21:03:54 +08:00
|
|
|
// zero new directory cluster
|
|
|
|
trace!("zeroing directory cluser {}", new_cluster);
|
|
|
|
let abs_pos = self.fs.offset_from_cluster(new_cluster);
|
|
|
|
let mut disk = self.fs.disk.borrow_mut();
|
|
|
|
disk.seek(SeekFrom::Start(abs_pos))?;
|
|
|
|
for _ in 0..cluster_size/32 {
|
|
|
|
let zero = [0u8; 32];
|
|
|
|
disk.write(&zero)?;
|
|
|
|
}
|
|
|
|
}
|
2017-10-10 22:05:19 +08:00
|
|
|
new_cluster
|
|
|
|
},
|
2017-10-09 20:59:52 +08:00
|
|
|
}
|
2017-10-10 22:05:19 +08:00
|
|
|
} else {
|
|
|
|
// self.current_cluster should be a valid cluster
|
|
|
|
match self.current_cluster {
|
|
|
|
Some(n) => n,
|
|
|
|
None => panic!("Offset inside cluster but no cluster allocated"),
|
|
|
|
}
|
|
|
|
};
|
2017-10-15 06:55:19 +08:00
|
|
|
trace!("write {} bytes in cluster {}", write_size, current_cluster);
|
2017-10-10 22:05:19 +08:00
|
|
|
let offset_in_fs = self.fs.offset_from_cluster(current_cluster) + (offset_in_cluster as u64);
|
|
|
|
let written_bytes = {
|
|
|
|
let mut disk = self.fs.disk.borrow_mut();
|
|
|
|
disk.seek(SeekFrom::Start(offset_in_fs))?;
|
|
|
|
disk.write(&buf[..write_size])?
|
|
|
|
};
|
|
|
|
if written_bytes == 0 {
|
|
|
|
return Ok(0);
|
2017-10-09 20:59:52 +08:00
|
|
|
}
|
2017-10-10 22:05:19 +08:00
|
|
|
self.offset += written_bytes as u32;
|
|
|
|
self.current_cluster = Some(current_cluster);
|
2017-10-09 20:59:52 +08:00
|
|
|
self.update_size();
|
2017-10-10 22:05:19 +08:00
|
|
|
Ok(written_bytes)
|
2017-10-09 20:59:52 +08:00
|
|
|
}
|
2017-10-25 23:20:27 +08:00
|
|
|
|
2017-10-09 20:59:52 +08:00
|
|
|
fn flush(&mut self) -> io::Result<()> {
|
|
|
|
self.flush_dir_entry()?;
|
|
|
|
let mut disk = self.fs.disk.borrow_mut();
|
|
|
|
disk.flush()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, 'b> Seek for File<'a, 'b> {
|
2017-09-24 08:10:59 +08:00
|
|
|
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
2017-10-10 22:05:19 +08:00
|
|
|
let mut new_pos = match pos {
|
2017-09-24 08:10:59 +08:00
|
|
|
SeekFrom::Current(x) => self.offset as i64 + x,
|
|
|
|
SeekFrom::Start(x) => x as i64,
|
2017-11-08 08:23:00 +08:00
|
|
|
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,
|
2017-09-24 08:10:59 +08:00
|
|
|
};
|
2017-10-10 22:05:19 +08:00
|
|
|
if new_pos < 0 {
|
2017-09-24 08:10:59 +08:00
|
|
|
return Err(io::Error::new(ErrorKind::InvalidInput, "invalid seek"));
|
|
|
|
}
|
2017-10-10 22:05:19 +08:00
|
|
|
new_pos = match self.entry {
|
2017-10-15 21:56:01 +08:00
|
|
|
Some(ref e) => {
|
2017-11-08 08:23:00 +08:00
|
|
|
if e.inner().size().map_or(false, |s| new_pos > s as i64) {
|
2017-10-15 21:56:01 +08:00
|
|
|
info!("seek beyond end of file");
|
2017-11-08 08:23:00 +08:00
|
|
|
e.inner().size().unwrap() as i64 // safe
|
2017-10-15 21:56:01 +08:00
|
|
|
} else {
|
|
|
|
new_pos
|
|
|
|
}
|
|
|
|
},
|
2017-10-10 22:05:19 +08:00
|
|
|
_ => new_pos,
|
|
|
|
};
|
2017-10-15 21:56:01 +08:00
|
|
|
trace!("file seek {} -> {} - entry {:?}", self.offset, new_pos, self.entry);
|
|
|
|
if new_pos == self.offset as i64 {
|
|
|
|
return Ok(self.offset as u64);
|
|
|
|
}
|
2017-10-04 19:56:44 +08:00
|
|
|
let cluster_size = self.fs.get_cluster_size();
|
2017-10-15 22:17:41 +08:00
|
|
|
// 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;
|
2017-10-16 04:42:26 +08:00
|
|
|
let new_cluster = if new_pos == 0 {
|
2017-10-10 03:14:28 +08:00
|
|
|
None
|
2017-10-15 22:17:41 +08:00
|
|
|
} else if cluster_count == old_cluster_count {
|
|
|
|
self.current_cluster
|
2017-10-10 03:14:28 +08:00
|
|
|
} else {
|
2017-10-07 22:07:33 +08:00
|
|
|
match self.first_cluster {
|
|
|
|
Some(n) => {
|
2017-10-10 22:05:19 +08:00
|
|
|
let mut cluster = n;
|
|
|
|
let mut iter = self.fs.cluster_iter(n);
|
|
|
|
for i in 0..cluster_count {
|
|
|
|
cluster = match iter.next() {
|
2017-10-15 22:17:41 +08:00
|
|
|
Some(r) => r?,
|
2017-10-10 22:05:19 +08:00
|
|
|
None => {
|
|
|
|
// chain ends before new position - seek to end of last cluster
|
|
|
|
new_pos = (i + 1) as i64 * cluster_size as i64;
|
|
|
|
break;
|
|
|
|
},
|
|
|
|
};
|
2017-10-07 22:07:33 +08:00
|
|
|
}
|
2017-10-10 22:05:19 +08:00
|
|
|
Some(cluster)
|
|
|
|
},
|
|
|
|
None => {
|
|
|
|
// empty file - always seek to 0
|
|
|
|
new_pos = 0;
|
|
|
|
None
|
2017-10-07 22:07:33 +08:00
|
|
|
},
|
2017-10-07 08:19:19 +08:00
|
|
|
}
|
2017-10-07 08:37:29 +08:00
|
|
|
};
|
2017-10-10 22:05:19 +08:00
|
|
|
self.offset = new_pos as u32;
|
2017-09-24 08:10:59 +08:00
|
|
|
self.current_cluster = new_cluster;
|
|
|
|
Ok(self.offset as u64)
|
|
|
|
}
|
|
|
|
}
|