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.
|
FAT filesystem library implemented in Rust.
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
* FAT12, FAT16, FAT32 supported,
|
* read file and directory,
|
||||||
* read directory entries,
|
* write file and directory,
|
||||||
* read file contents,
|
* FAT12, FAT16, FAT32 compatibility,
|
||||||
* LFN (Long File Names),
|
* LFN (Long File Names) extension supported.
|
||||||
* basic write support (write and truncate existing file).
|
|
||||||
|
|
||||||
Missing features (yet):
|
Planned features (Nice to Have):
|
||||||
* create new file/directory,
|
|
||||||
* remove file/directory,
|
|
||||||
|
|
||||||
Other planned features (Nice to Have):
|
|
||||||
* no_std environment support.
|
* no_std environment support.
|
||||||
|
|
||||||
License
|
License
|
||||||
|
32
src/dir.rs
32
src/dir.rs
@ -63,6 +63,7 @@ impl <'a, 'b> Seek for DirRawStream<'a, 'b> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
|
/// FAT file attributes
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct FileAttributes: u8 {
|
pub struct FileAttributes: u8 {
|
||||||
const READ_ONLY = 0x01;
|
const READ_ONLY = 0x01;
|
||||||
@ -266,6 +267,7 @@ impl DirEntryData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// DOS compatible date
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct Date {
|
pub struct Date {
|
||||||
pub year: u16,
|
pub year: u16,
|
||||||
@ -284,6 +286,7 @@ impl Date {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// DOS compatible time
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct Time {
|
pub struct Time {
|
||||||
pub hour: u16,
|
pub hour: u16,
|
||||||
@ -302,6 +305,7 @@ impl Time {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// DOS compatible date and time
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct DateTime {
|
pub struct DateTime {
|
||||||
pub date: Date,
|
pub date: Date,
|
||||||
@ -346,6 +350,9 @@ impl FileEntryInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// FAT directory entry.
|
||||||
|
///
|
||||||
|
/// Returned by DirIter.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct DirEntry<'a, 'b: 'a> {
|
pub struct DirEntry<'a, 'b: 'a> {
|
||||||
data: DirFileEntryData,
|
data: DirFileEntryData,
|
||||||
@ -356,6 +363,7 @@ pub struct DirEntry<'a, 'b: 'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl <'a, 'b> DirEntry<'a, 'b> {
|
impl <'a, 'b> DirEntry<'a, 'b> {
|
||||||
|
/// Returns short file name
|
||||||
pub fn short_file_name(&self) -> String {
|
pub fn short_file_name(&self) -> String {
|
||||||
let name_str = String::from_utf8_lossy(&self.data.name[0..8]);
|
let name_str = String::from_utf8_lossy(&self.data.name[0..8]);
|
||||||
let ext_str = String::from_utf8_lossy(&self.data.name[8..11]);
|
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 {
|
pub fn file_name(&self) -> String {
|
||||||
if self.lfn.len() > 0 {
|
if self.lfn.len() > 0 {
|
||||||
String::from_utf16_lossy(&self.lfn)
|
String::from_utf16_lossy(&self.lfn)
|
||||||
@ -376,14 +385,17 @@ impl <'a, 'b> DirEntry<'a, 'b> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns file attributes
|
||||||
pub fn attributes(&self) -> FileAttributes {
|
pub fn attributes(&self) -> FileAttributes {
|
||||||
self.data.attrs
|
self.data.attrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if entry belongs to directory.
|
||||||
pub fn is_dir(&self) -> bool {
|
pub fn is_dir(&self) -> bool {
|
||||||
self.data.is_dir()
|
self.data.is_dir()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if entry belongs to regular file.
|
||||||
pub fn is_file(&self) -> bool {
|
pub fn is_file(&self) -> bool {
|
||||||
self.data.is_file()
|
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> {
|
pub fn to_file(&self) -> File<'a, 'b> {
|
||||||
assert!(!self.is_dir(), "Not a file entry");
|
assert!(!self.is_dir(), "Not a file entry");
|
||||||
File::new(self.first_cluster(), Some(self.entry_info()), self.fs)
|
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> {
|
pub fn to_dir(&self) -> Dir<'a, 'b> {
|
||||||
assert!(self.is_dir(), "Not a directory entry");
|
assert!(self.is_dir(), "Not a directory entry");
|
||||||
match self.first_cluster() {
|
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 {
|
pub fn len(&self) -> u64 {
|
||||||
self.data.size as u64
|
self.data.size as u64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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)
|
DateTime::from_u16(self.data.create_date, self.data.create_time_1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns file last access date.
|
||||||
pub fn accessed(&self) -> Date {
|
pub fn accessed(&self) -> Date {
|
||||||
Date::from_u16(self.data.access_date)
|
Date::from_u16(self.data.access_date)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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)
|
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)]
|
#[derive(Clone)]
|
||||||
pub struct Dir<'a, 'b: 'a> {
|
pub struct Dir<'a, 'b: 'a> {
|
||||||
stream: DirRawStream<'a, 'b>,
|
stream: DirRawStream<'a, 'b>,
|
||||||
@ -450,6 +473,7 @@ impl <'a, 'b> Dir<'a, 'b> {
|
|||||||
Dir { stream, fs }
|
Dir { stream, fs }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates directory entries iterator
|
||||||
pub fn iter(&self) -> DirIter<'a, 'b> {
|
pub fn iter(&self) -> DirIter<'a, 'b> {
|
||||||
DirIter {
|
DirIter {
|
||||||
stream: self.stream.clone(),
|
stream: self.stream.clone(),
|
||||||
@ -475,6 +499,7 @@ impl <'a, 'b> Dir<'a, 'b> {
|
|||||||
Err(io::Error::new(ErrorKind::NotFound, "file not found"))
|
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>> {
|
pub fn open_dir(&mut self, path: &str) -> io::Result<Dir<'a, 'b>> {
|
||||||
let (name, rest_opt) = Self::split_path(path);
|
let (name, rest_opt) = Self::split_path(path);
|
||||||
let e = self.find_entry(name)?;
|
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>> {
|
pub fn open_file(&mut self, path: &str) -> io::Result<File<'a, 'b>> {
|
||||||
let (name, rest_opt) = Self::split_path(path);
|
let (name, rest_opt) = Self::split_path(path);
|
||||||
let e = self.find_entry(name)?;
|
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>> {
|
pub fn create_file(&mut self, path: &str) -> io::Result<File<'a, 'b>> {
|
||||||
let (name, rest_opt) = Self::split_path(path);
|
let (name, rest_opt) = Self::split_path(path);
|
||||||
let r = self.find_entry(name);
|
let r = self.find_entry(name);
|
||||||
@ -518,6 +545,10 @@ impl <'a, 'b> Dir<'a, 'b> {
|
|||||||
Ok(true)
|
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<()> {
|
pub fn remove(&mut self, path: &str) -> io::Result<()> {
|
||||||
let (name, rest_opt) = Self::split_path(path);
|
let (name, rest_opt) = Self::split_path(path);
|
||||||
let e = self.find_entry(name)?;
|
let e = self.find_entry(name)?;
|
||||||
@ -657,6 +688,7 @@ impl <'a, 'b> Dir<'a, 'b> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Directory entries iterator.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct DirIter<'a, 'b: 'a> {
|
pub struct DirIter<'a, 'b: 'a> {
|
||||||
stream: DirRawStream<'a, 'b>,
|
stream: DirRawStream<'a, 'b>,
|
||||||
|
@ -6,6 +6,7 @@ use std::io;
|
|||||||
use fs::FileSystemRef;
|
use fs::FileSystemRef;
|
||||||
use dir::{FileEntryInfo, DateTime};
|
use dir::{FileEntryInfo, DateTime};
|
||||||
|
|
||||||
|
/// FAT file used for reading and writing.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct File<'a, 'b: 'a> {
|
pub struct File<'a, 'b: 'a> {
|
||||||
// Note first_cluster is None if file is empty
|
// 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<()> {
|
pub fn truncate(&mut self) -> io::Result<()> {
|
||||||
let offset = self.offset;
|
let offset = self.offset;
|
||||||
match self.entry {
|
match self.entry {
|
||||||
@ -98,6 +100,9 @@ impl <'a, 'b> File<'a, 'b> {
|
|||||||
Ok(())
|
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) {
|
pub fn set_modified(&mut self, date_time: DateTime) {
|
||||||
match self.entry {
|
match self.entry {
|
||||||
Some(ref mut e) => {
|
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>;
|
pub(crate) type FileSystemRef<'a, 'b: 'a> = &'a FileSystem<'b>;
|
||||||
|
|
||||||
|
/// FAT filesystem main struct.
|
||||||
pub struct FileSystem<'a> {
|
pub struct FileSystem<'a> {
|
||||||
pub(crate) disk: RefCell<&'a mut ReadWriteSeek>,
|
pub(crate) disk: RefCell<&'a mut ReadWriteSeek>,
|
||||||
pub(crate) fat_type: FatType,
|
pub(crate) fat_type: FatType,
|
||||||
@ -88,7 +89,10 @@ pub struct FileSystem<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl <'a> 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>> {
|
pub fn new<T: ReadWriteSeek>(disk: &'a mut T) -> 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] {
|
||||||
@ -112,18 +116,25 @@ impl <'a> FileSystem<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns type of used File Allocation Table (FAT).
|
||||||
pub fn fat_type(&self) -> FatType {
|
pub fn fat_type(&self) -> FatType {
|
||||||
self.fat_type
|
self.fat_type
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns volume identifier read from BPB in Boot Sector.
|
||||||
pub fn volume_id(&self) -> u32 {
|
pub fn volume_id(&self) -> u32 {
|
||||||
self.boot.bpb.volume_id
|
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 {
|
pub fn volume_label(&self) -> String {
|
||||||
String::from_utf8_lossy(&self.boot.bpb.volume_label).trim_right().to_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> {
|
pub fn root_dir<'b>(&'b self) -> Dir<'b, 'a> {
|
||||||
let root_rdr = {
|
let root_rdr = {
|
||||||
match self.fat_type {
|
match self.fat_type {
|
||||||
|
@ -18,7 +18,11 @@ pub struct BufStream<T: Read+Write+Seek> {
|
|||||||
write: bool,
|
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> {
|
impl<T: Read+Write+Seek> BufStream<T> {
|
||||||
|
/// Creates new BufStream object for given stream.
|
||||||
pub fn new(inner: T) -> Self {
|
pub fn new(inner: T) -> Self {
|
||||||
BufStream::<T> {
|
BufStream::<T> {
|
||||||
inner,
|
inner,
|
||||||
|
Loading…
Reference in New Issue
Block a user