Update README and add more doc comments.
This commit is contained in:
parent
2a61b0d175
commit
9d3780f859
15
README.md
15
README.md
@ -12,17 +12,12 @@ Introduction
|
||||
FAT filesystem library implemented in Rust.
|
||||
|
||||
Features:
|
||||
* FAT12, FAT16, FAT32 supported,
|
||||
* read directory entries,
|
||||
* read file contents,
|
||||
* LFN (Long File Names),
|
||||
* basic write support (write and truncate existing file).
|
||||
* read file and directory,
|
||||
* write file and directory,
|
||||
* FAT12, FAT16, FAT32 compatibility,
|
||||
* LFN (Long File Names) extension supported.
|
||||
|
||||
Missing features (yet):
|
||||
* create new file/directory,
|
||||
* remove file/directory,
|
||||
|
||||
Other planned features (Nice to Have):
|
||||
Planned features (Nice to Have):
|
||||
* no_std environment support.
|
||||
|
||||
License
|
||||
|
32
src/dir.rs
32
src/dir.rs
@ -63,6 +63,7 @@ impl <'a, 'b> Seek for DirRawStream<'a, 'b> {
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// FAT file attributes
|
||||
#[derive(Default)]
|
||||
pub struct FileAttributes: u8 {
|
||||
const READ_ONLY = 0x01;
|
||||
@ -266,6 +267,7 @@ impl DirEntryData {
|
||||
}
|
||||
}
|
||||
|
||||
/// DOS compatible date
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Date {
|
||||
pub year: u16,
|
||||
@ -284,6 +286,7 @@ impl Date {
|
||||
}
|
||||
}
|
||||
|
||||
/// DOS compatible time
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Time {
|
||||
pub hour: u16,
|
||||
@ -302,6 +305,7 @@ impl Time {
|
||||
}
|
||||
}
|
||||
|
||||
/// DOS compatible date and time
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct DateTime {
|
||||
pub date: Date,
|
||||
@ -346,6 +350,9 @@ impl FileEntryInfo {
|
||||
}
|
||||
}
|
||||
|
||||
/// FAT directory entry.
|
||||
///
|
||||
/// Returned by DirIter.
|
||||
#[derive(Clone)]
|
||||
pub struct DirEntry<'a, 'b: 'a> {
|
||||
data: DirFileEntryData,
|
||||
@ -356,6 +363,7 @@ pub struct DirEntry<'a, 'b: 'a> {
|
||||
}
|
||||
|
||||
impl <'a, 'b> DirEntry<'a, 'b> {
|
||||
/// Returns short file name
|
||||
pub fn short_file_name(&self) -> String {
|
||||
let name_str = String::from_utf8_lossy(&self.data.name[0..8]);
|
||||
let ext_str = String::from_utf8_lossy(&self.data.name[8..11]);
|
||||
@ -368,6 +376,7 @@ impl <'a, 'b> DirEntry<'a, 'b> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns long file name or if it doesn't exist fallbacks to short file name.
|
||||
pub fn file_name(&self) -> String {
|
||||
if self.lfn.len() > 0 {
|
||||
String::from_utf16_lossy(&self.lfn)
|
||||
@ -376,14 +385,17 @@ impl <'a, 'b> DirEntry<'a, 'b> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns file attributes
|
||||
pub fn attributes(&self) -> FileAttributes {
|
||||
self.data.attrs
|
||||
}
|
||||
|
||||
/// Checks if entry belongs to directory.
|
||||
pub fn is_dir(&self) -> bool {
|
||||
self.data.is_dir()
|
||||
}
|
||||
|
||||
/// Checks if entry belongs to regular file.
|
||||
pub fn is_file(&self) -> bool {
|
||||
self.data.is_file()
|
||||
}
|
||||
@ -399,11 +411,17 @@ impl <'a, 'b> DirEntry<'a, 'b> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns File struct for this entry.
|
||||
///
|
||||
/// Panics if this is not a file.
|
||||
pub fn to_file(&self) -> File<'a, 'b> {
|
||||
assert!(!self.is_dir(), "Not a file entry");
|
||||
File::new(self.first_cluster(), Some(self.entry_info()), self.fs)
|
||||
}
|
||||
|
||||
/// Returns Dir struct for this entry.
|
||||
///
|
||||
/// Panics if this is not a directory.
|
||||
pub fn to_dir(&self) -> Dir<'a, 'b> {
|
||||
assert!(self.is_dir(), "Not a directory entry");
|
||||
match self.first_cluster() {
|
||||
@ -415,18 +433,22 @@ impl <'a, 'b> DirEntry<'a, 'b> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns file size or 0 for directory.
|
||||
pub fn len(&self) -> u64 {
|
||||
self.data.size as u64
|
||||
}
|
||||
|
||||
/// Returns file creation date and time.
|
||||
pub fn created(&self) -> DateTime {
|
||||
DateTime::from_u16(self.data.create_date, self.data.create_time_1)
|
||||
}
|
||||
|
||||
/// Returns file last access date.
|
||||
pub fn accessed(&self) -> Date {
|
||||
Date::from_u16(self.data.access_date)
|
||||
}
|
||||
|
||||
/// Returns file last modification date and time.
|
||||
pub fn modified(&self) -> DateTime {
|
||||
DateTime::from_u16(self.data.modify_date, self.data.modify_time)
|
||||
}
|
||||
@ -438,6 +460,7 @@ impl <'a, 'b> fmt::Debug for DirEntry<'a, 'b> {
|
||||
}
|
||||
}
|
||||
|
||||
/// FAT directory
|
||||
#[derive(Clone)]
|
||||
pub struct Dir<'a, 'b: 'a> {
|
||||
stream: DirRawStream<'a, 'b>,
|
||||
@ -450,6 +473,7 @@ impl <'a, 'b> Dir<'a, 'b> {
|
||||
Dir { stream, fs }
|
||||
}
|
||||
|
||||
/// Creates directory entries iterator
|
||||
pub fn iter(&self) -> DirIter<'a, 'b> {
|
||||
DirIter {
|
||||
stream: self.stream.clone(),
|
||||
@ -475,6 +499,7 @@ impl <'a, 'b> Dir<'a, 'b> {
|
||||
Err(io::Error::new(ErrorKind::NotFound, "file not found"))
|
||||
}
|
||||
|
||||
/// Opens existing directory
|
||||
pub fn open_dir(&mut self, path: &str) -> io::Result<Dir<'a, 'b>> {
|
||||
let (name, rest_opt) = Self::split_path(path);
|
||||
let e = self.find_entry(name)?;
|
||||
@ -484,6 +509,7 @@ impl <'a, 'b> Dir<'a, 'b> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Opens existing file.
|
||||
pub fn open_file(&mut self, path: &str) -> io::Result<File<'a, 'b>> {
|
||||
let (name, rest_opt) = Self::split_path(path);
|
||||
let e = self.find_entry(name)?;
|
||||
@ -493,6 +519,7 @@ impl <'a, 'b> Dir<'a, 'b> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates new file or opens existing.
|
||||
pub fn create_file(&mut self, path: &str) -> io::Result<File<'a, 'b>> {
|
||||
let (name, rest_opt) = Self::split_path(path);
|
||||
let r = self.find_entry(name);
|
||||
@ -518,6 +545,10 @@ impl <'a, 'b> Dir<'a, 'b> {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
/// Removes existing file or directory.
|
||||
///
|
||||
/// Make sure there is no reference to this file (no File instance) or filesystem corruption
|
||||
/// can happen.
|
||||
pub fn remove(&mut self, path: &str) -> io::Result<()> {
|
||||
let (name, rest_opt) = Self::split_path(path);
|
||||
let e = self.find_entry(name)?;
|
||||
@ -657,6 +688,7 @@ impl <'a, 'b> Dir<'a, 'b> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Directory entries iterator.
|
||||
#[derive(Clone)]
|
||||
pub struct DirIter<'a, 'b: 'a> {
|
||||
stream: DirRawStream<'a, 'b>,
|
||||
|
@ -6,6 +6,7 @@ use std::io;
|
||||
use fs::FileSystemRef;
|
||||
use dir::{FileEntryInfo, DateTime};
|
||||
|
||||
/// FAT file used for reading and writing.
|
||||
#[derive(Clone)]
|
||||
pub struct File<'a, 'b: 'a> {
|
||||
// Note first_cluster is None if file is empty
|
||||
@ -44,6 +45,7 @@ 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 {
|
||||
@ -98,6 +100,9 @@ impl <'a, 'b> File<'a, 'b> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn set_modified(&mut self, date_time: DateTime) {
|
||||
match self.entry {
|
||||
Some(ref mut e) => {
|
||||
|
13
src/fs.rs
13
src/fs.rs
@ -79,6 +79,7 @@ impl Default for BootRecord {
|
||||
|
||||
pub(crate) type FileSystemRef<'a, 'b: 'a> = &'a FileSystem<'b>;
|
||||
|
||||
/// FAT filesystem main struct.
|
||||
pub struct FileSystem<'a> {
|
||||
pub(crate) disk: RefCell<&'a mut ReadWriteSeek>,
|
||||
pub(crate) fat_type: FatType,
|
||||
@ -88,7 +89,10 @@ pub struct FileSystem<'a> {
|
||||
}
|
||||
|
||||
impl <'a> FileSystem<'a> {
|
||||
|
||||
/// Creates new filesystem object instance.
|
||||
///
|
||||
/// 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>> {
|
||||
let boot = Self::read_boot_record(disk)?;
|
||||
if boot.boot_sig != [0x55, 0xAA] {
|
||||
@ -112,18 +116,25 @@ impl <'a> FileSystem<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns type of used File Allocation Table (FAT).
|
||||
pub fn fat_type(&self) -> FatType {
|
||||
self.fat_type
|
||||
}
|
||||
|
||||
/// Returns volume identifier read from BPB in Boot Sector.
|
||||
pub fn volume_id(&self) -> u32 {
|
||||
self.boot.bpb.volume_id
|
||||
}
|
||||
|
||||
/// Returns volume label from BPB in Boot Sector.
|
||||
///
|
||||
/// Note: File with VOLUME_ID attribute in root directory is ignored by this library.
|
||||
/// Only label from BPB is used.
|
||||
pub fn volume_label(&self) -> String {
|
||||
String::from_utf8_lossy(&self.boot.bpb.volume_label).trim_right().to_string()
|
||||
}
|
||||
|
||||
/// Returns root directory object allowing futher penetration of filesystem structure.
|
||||
pub fn root_dir<'b>(&'b self) -> Dir<'b, 'a> {
|
||||
let root_rdr = {
|
||||
match self.fat_type {
|
||||
|
@ -18,7 +18,11 @@ pub struct BufStream<T: Read+Write+Seek> {
|
||||
write: bool,
|
||||
}
|
||||
|
||||
/// The BufStream struct adds buffering to underlying file or device.
|
||||
///
|
||||
/// It's basically composition of BufReader and BufWritter.
|
||||
impl<T: Read+Write+Seek> BufStream<T> {
|
||||
/// Creates new BufStream object for given stream.
|
||||
pub fn new(inner: T) -> Self {
|
||||
BufStream::<T> {
|
||||
inner,
|
||||
|
Loading…
Reference in New Issue
Block a user