Support no_std without alloc
This commit is contained in:
parent
11b2a3b956
commit
35d03daae9
@ -18,15 +18,19 @@ exclude = [
|
|||||||
travis-ci = { repository = "rafalh/rust-fatfs" }
|
travis-ci = { repository = "rafalh/rust-fatfs" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
# Use Rust std library
|
||||||
std = []
|
std = []
|
||||||
default = ["chrono", "std"]
|
# Use dynamic allocation - required for LFN support
|
||||||
|
alloc = ["core_io/collections"]
|
||||||
|
# Default features
|
||||||
|
default = ["chrono", "std", "alloc"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1"
|
byteorder = "1"
|
||||||
bitflags = "1.0"
|
bitflags = "1.0"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
chrono = { version = "0.4", optional = true }
|
chrono = { version = "0.4", optional = true }
|
||||||
core_io = { version = "0.1", features = ["collections"], optional = true }
|
core_io = { version = "0.1", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
env_logger = "0.5"
|
env_logger = "0.5"
|
||||||
|
@ -47,7 +47,11 @@ Put this in your `Cargo.toml`:
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
fatfs = { version = "0.2", features = ["core_io"], default-features = false }
|
fatfs = { version = "0.2", features = ["core_io"], default-features = false }
|
||||||
|
|
||||||
Note: alloc crate is required at the moment so when std is disabled, custom allocator has to be provided.
|
Note: LFN support requires `alloc` feature and makes use of `alloc` crate.
|
||||||
|
You may have to provide a memory allocator implementation.
|
||||||
|
|
||||||
|
For building in `no_std` mode nightly Rust version compatible with current `core_io` crate is required.
|
||||||
|
See date string in `core_io` dependency version.
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
cargo build --no-default-features --features core_io
|
cargo build --no-default-features --features core_io
|
||||||
|
cargo build --no-default-features --features core_io,alloc
|
||||||
|
58
src/dir.rs
58
src/dir.rs
@ -1,5 +1,5 @@
|
|||||||
use core::slice;
|
#[cfg(feature = "alloc")]
|
||||||
use core::iter;
|
use core::{slice, iter};
|
||||||
|
|
||||||
use io::prelude::*;
|
use io::prelude::*;
|
||||||
use io;
|
use io;
|
||||||
@ -7,10 +7,12 @@ use io::{ErrorKind, SeekFrom};
|
|||||||
|
|
||||||
use fs::{FileSystemRef, DiskSlice};
|
use fs::{FileSystemRef, DiskSlice};
|
||||||
use file::File;
|
use file::File;
|
||||||
use dir_entry::{DirEntry, DirEntryData, DirFileEntryData, DirLfnEntryData, FileAttributes,
|
use dir_entry::{DirEntry, DirEntryData, DirFileEntryData, DirLfnEntryData, FileAttributes, ShortName, DIR_ENTRY_SIZE};
|
||||||
DIR_ENTRY_SIZE, LFN_PART_LEN, LFN_ENTRY_LAST_FLAG};
|
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(feature = "alloc")]
|
||||||
|
use dir_entry::{LFN_PART_LEN, LFN_ENTRY_LAST_FLAG};
|
||||||
|
|
||||||
|
#[cfg(all(not(feature = "std"), feature = "alloc"))]
|
||||||
use alloc::Vec;
|
use alloc::Vec;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -254,13 +256,11 @@ impl <'a, 'b> Dir<'a, 'b> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_entry(&mut self, name: &str, attrs: FileAttributes, first_cluster: Option<u32>) -> io::Result<DirEntry<'a, 'b>> {
|
#[cfg(feature = "alloc")]
|
||||||
trace!("create_entry {}", name);
|
fn create_lfn_entries(&mut self, name: &str, short_name: &[u8]) -> io::Result<(DirRawStream<'a, 'b>, u64)> {
|
||||||
// check if name doesn't contain unsupported characters
|
// get short name checksum
|
||||||
validate_long_name(name)?;
|
|
||||||
// generate short name and long entries
|
|
||||||
let short_name = generate_short_name(name);
|
|
||||||
let lfn_chsum = lfn_checksum(&short_name);
|
let lfn_chsum = lfn_checksum(&short_name);
|
||||||
|
// convert long name to UTF-16
|
||||||
let lfn_utf16 = name.encode_utf16().collect::<Vec<u16>>();
|
let lfn_utf16 = name.encode_utf16().collect::<Vec<u16>>();
|
||||||
let lfn_iter = LfnEntriesGenerator::new(&lfn_utf16, lfn_chsum);
|
let lfn_iter = LfnEntriesGenerator::new(&lfn_utf16, lfn_chsum);
|
||||||
// find space for new entries
|
// find space for new entries
|
||||||
@ -271,6 +271,23 @@ impl <'a, 'b> Dir<'a, 'b> {
|
|||||||
for lfn_entry in lfn_iter {
|
for lfn_entry in lfn_iter {
|
||||||
lfn_entry.serialize(&mut stream)?;
|
lfn_entry.serialize(&mut stream)?;
|
||||||
}
|
}
|
||||||
|
Ok((stream, start_pos))
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "alloc"))]
|
||||||
|
fn create_lfn_entries(&mut self, _name: &str, _short_name: &[u8]) -> io::Result<(DirRawStream<'a, 'b>, u64)> {
|
||||||
|
let mut stream = self.find_free_entries(1)?;
|
||||||
|
let start_pos = stream.seek(io::SeekFrom::Current(0))?;
|
||||||
|
Ok((stream, start_pos))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_entry(&mut self, name: &str, attrs: FileAttributes, first_cluster: Option<u32>) -> io::Result<DirEntry<'a, 'b>> {
|
||||||
|
trace!("create_entry {}", name);
|
||||||
|
// check if name doesn't contain unsupported characters
|
||||||
|
validate_long_name(name)?;
|
||||||
|
// generate short name
|
||||||
|
let short_name = generate_short_name(name);
|
||||||
|
// generate long entries
|
||||||
|
let (mut stream, start_pos) = self.create_lfn_entries(&name, &short_name)?;
|
||||||
// create and write short name entry
|
// create and write short name entry
|
||||||
let mut raw_entry = DirFileEntryData::new(short_name, attrs);
|
let mut raw_entry = DirFileEntryData::new(short_name, attrs);
|
||||||
raw_entry.set_first_cluster(first_cluster, self.fs.fat_type());
|
raw_entry.set_first_cluster(first_cluster, self.fs.fat_type());
|
||||||
@ -281,8 +298,11 @@ impl <'a, 'b> Dir<'a, 'b> {
|
|||||||
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);
|
||||||
// return new logical entry descriptor
|
// return new logical entry descriptor
|
||||||
|
let short_name = ShortName::new(raw_entry.name());
|
||||||
return Ok(DirEntry {
|
return Ok(DirEntry {
|
||||||
data: raw_entry,
|
data: raw_entry,
|
||||||
|
short_name,
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
lfn: Vec::new(),
|
lfn: Vec::new(),
|
||||||
fs: self.fs,
|
fs: self.fs,
|
||||||
entry_pos: abs_pos.unwrap(), // SAFE: abs_pos is absent only for empty file
|
entry_pos: abs_pos.unwrap(), // SAFE: abs_pos is absent only for empty file
|
||||||
@ -301,6 +321,7 @@ pub struct DirIter<'a, 'b: 'a> {
|
|||||||
|
|
||||||
impl <'a, 'b> DirIter<'a, 'b> {
|
impl <'a, 'b> DirIter<'a, 'b> {
|
||||||
fn read_dir_entry(&mut self) -> io::Result<Option<DirEntry<'a, 'b>>> {
|
fn read_dir_entry(&mut self) -> io::Result<Option<DirEntry<'a, 'b>>> {
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
let mut lfn_buf = LongNameBuilder::new();
|
let mut lfn_buf = LongNameBuilder::new();
|
||||||
let mut offset = self.stream.seek(SeekFrom::Current(0))?;
|
let mut offset = self.stream.seek(SeekFrom::Current(0))?;
|
||||||
let mut begin_offset = offset;
|
let mut begin_offset = offset;
|
||||||
@ -315,6 +336,7 @@ impl <'a, 'b> DirIter<'a, 'b> {
|
|||||||
}
|
}
|
||||||
// Check if this is deleted or volume ID entry
|
// Check if this is deleted or volume ID entry
|
||||||
if data.is_free() || data.is_volume() {
|
if data.is_free() || data.is_volume() {
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
lfn_buf.clear();
|
lfn_buf.clear();
|
||||||
begin_offset = offset;
|
begin_offset = offset;
|
||||||
continue;
|
continue;
|
||||||
@ -322,9 +344,14 @@ impl <'a, 'b> DirIter<'a, 'b> {
|
|||||||
// Get entry position on volume
|
// Get entry position on volume
|
||||||
let abs_pos = self.stream.abs_pos().map(|p| p - DIR_ENTRY_SIZE);
|
let abs_pos = self.stream.abs_pos().map(|p| p - DIR_ENTRY_SIZE);
|
||||||
// Check if LFN checksum is valid
|
// Check if LFN checksum is valid
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
lfn_buf.validate_chksum(data.name());
|
lfn_buf.validate_chksum(data.name());
|
||||||
|
// Return directory entry
|
||||||
|
let short_name = ShortName::new(data.name());
|
||||||
return Ok(Some(DirEntry {
|
return Ok(Some(DirEntry {
|
||||||
data,
|
data,
|
||||||
|
short_name,
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
lfn: lfn_buf.to_vec(),
|
lfn: lfn_buf.to_vec(),
|
||||||
fs: self.fs,
|
fs: self.fs,
|
||||||
entry_pos: abs_pos.unwrap(), // SAFE: abs_pos is empty only for empty file
|
entry_pos: abs_pos.unwrap(), // SAFE: abs_pos is empty only for empty file
|
||||||
@ -334,11 +361,13 @@ impl <'a, 'b> DirIter<'a, 'b> {
|
|||||||
DirEntryData::Lfn(data) => {
|
DirEntryData::Lfn(data) => {
|
||||||
// Check if this is deleted entry
|
// Check if this is deleted entry
|
||||||
if data.is_free() {
|
if data.is_free() {
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
lfn_buf.clear();
|
lfn_buf.clear();
|
||||||
begin_offset = offset;
|
begin_offset = offset;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// Append to LFN buffer
|
// Append to LFN buffer
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
lfn_buf.process(&data);
|
lfn_buf.process(&data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -420,6 +449,7 @@ fn validate_long_name(name: &str) -> io::Result<()> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
fn lfn_checksum(short_name: &[u8]) -> u8 {
|
fn lfn_checksum(short_name: &[u8]) -> u8 {
|
||||||
let mut chksum = 0u8;
|
let mut chksum = 0u8;
|
||||||
for i in 0..11 {
|
for i in 0..11 {
|
||||||
@ -428,12 +458,14 @@ fn lfn_checksum(short_name: &[u8]) -> u8 {
|
|||||||
chksum
|
chksum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
struct LongNameBuilder {
|
struct LongNameBuilder {
|
||||||
buf: Vec<u16>,
|
buf: Vec<u16>,
|
||||||
chksum: u8,
|
chksum: u8,
|
||||||
index: u8,
|
index: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
impl LongNameBuilder {
|
impl LongNameBuilder {
|
||||||
fn new() -> LongNameBuilder {
|
fn new() -> LongNameBuilder {
|
||||||
LongNameBuilder {
|
LongNameBuilder {
|
||||||
@ -510,6 +542,7 @@ impl LongNameBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
struct LfnEntriesGenerator<'a> {
|
struct LfnEntriesGenerator<'a> {
|
||||||
name_parts_iter: iter::Rev<slice::Chunks<'a, u16>>,
|
name_parts_iter: iter::Rev<slice::Chunks<'a, u16>>,
|
||||||
checksum: u8,
|
checksum: u8,
|
||||||
@ -518,6 +551,7 @@ struct LfnEntriesGenerator<'a> {
|
|||||||
ended: bool,
|
ended: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
impl<'a> LfnEntriesGenerator<'a> {
|
impl<'a> LfnEntriesGenerator<'a> {
|
||||||
fn new(name_utf16: &'a [u16], checksum: u8) -> Self {
|
fn new(name_utf16: &'a [u16], checksum: u8) -> Self {
|
||||||
let num_entries = (name_utf16.len() + LFN_PART_LEN - 1) / LFN_PART_LEN;
|
let num_entries = (name_utf16.len() + LFN_PART_LEN - 1) / LFN_PART_LEN;
|
||||||
@ -532,6 +566,7 @@ impl<'a> LfnEntriesGenerator<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
impl<'a> Iterator for LfnEntriesGenerator<'a> {
|
impl<'a> Iterator for LfnEntriesGenerator<'a> {
|
||||||
type Item = DirLfnEntryData;
|
type Item = DirLfnEntryData;
|
||||||
|
|
||||||
@ -577,4 +612,5 @@ impl<'a> Iterator for LfnEntriesGenerator<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// name_parts_iter is ExactSizeIterator so size_hint returns one limit
|
// name_parts_iter is ExactSizeIterator so size_hint returns one limit
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
impl<'a> ExactSizeIterator for LfnEntriesGenerator<'a> {}
|
impl<'a> ExactSizeIterator for LfnEntriesGenerator<'a> {}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use core::fmt;
|
use core::{fmt, str};
|
||||||
use io::prelude::*;
|
use io::prelude::*;
|
||||||
use io;
|
use io;
|
||||||
use io::Cursor;
|
use io::Cursor;
|
||||||
@ -10,7 +10,7 @@ use chrono::{TimeZone, Local, Datelike, Timelike};
|
|||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
use chrono;
|
use chrono;
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(all(not(feature = "std"), feature = "alloc"))]
|
||||||
use alloc::{Vec, String, string::ToString};
|
use alloc::{Vec, String, string::ToString};
|
||||||
|
|
||||||
use fs::{FileSystemRef, FatType};
|
use fs::{FileSystemRef, FatType};
|
||||||
@ -37,6 +37,45 @@ pub(crate) const DIR_ENTRY_SIZE: u64 = 32;
|
|||||||
pub(crate) const DIR_ENTRY_FREE_FLAG: u8 = 0xE5;
|
pub(crate) const DIR_ENTRY_FREE_FLAG: u8 = 0xE5;
|
||||||
pub(crate) const LFN_ENTRY_LAST_FLAG: u8 = 0x40;
|
pub(crate) const LFN_ENTRY_LAST_FLAG: u8 = 0x40;
|
||||||
|
|
||||||
|
/// Decoded file short name
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub(crate) struct ShortName {
|
||||||
|
name: [u8; 12],
|
||||||
|
len: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShortName {
|
||||||
|
pub(crate) fn new(raw_name: &[u8; 11]) -> Self {
|
||||||
|
// get name components length by looking for space character
|
||||||
|
const SPACE: u8 = ' ' as u8;
|
||||||
|
let name_len = raw_name[0..8].iter().position(|x| *x == SPACE).unwrap_or(8);
|
||||||
|
let ext_len = raw_name[8..11].iter().position(|x| *x == SPACE).unwrap_or(3);
|
||||||
|
let mut name = [SPACE; 12];
|
||||||
|
name[..name_len].copy_from_slice(&raw_name[..name_len]);
|
||||||
|
let total_len = if ext_len > 0 {
|
||||||
|
name[name_len] = '.' as u8;
|
||||||
|
name[name_len+1..name_len+1+ext_len].copy_from_slice(&raw_name[8..8+ext_len]);
|
||||||
|
// Return total name length
|
||||||
|
name_len+1+ext_len
|
||||||
|
} else {
|
||||||
|
// No extension - return length of name part
|
||||||
|
name_len
|
||||||
|
};
|
||||||
|
// Short names in FAT filesystem are encoded in OEM code-page. Rust operates on UTF-8 strings
|
||||||
|
// and there is no built-in conversion so strip non-ascii characters in the name.
|
||||||
|
use strip_non_ascii;
|
||||||
|
strip_non_ascii(&mut name);
|
||||||
|
ShortName {
|
||||||
|
name,
|
||||||
|
len: total_len as u8,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_str(&self) -> &str {
|
||||||
|
str::from_utf8(&self.name[..self.len as usize]).unwrap() // SAFE: all characters outside of ASCII table has been removed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub(crate) struct DirFileEntryData {
|
pub(crate) struct DirFileEntryData {
|
||||||
@ -251,10 +290,12 @@ impl DirLfnEntryData {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
pub(crate) fn order(&self) -> u8 {
|
pub(crate) fn order(&self) -> u8 {
|
||||||
self.order
|
self.order
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
pub(crate) fn checksum(&self) -> u8 {
|
pub(crate) fn checksum(&self) -> u8 {
|
||||||
self.checksum
|
self.checksum
|
||||||
}
|
}
|
||||||
@ -539,6 +580,8 @@ impl DirEntryEditor {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct DirEntry<'a, 'b: 'a> {
|
pub struct DirEntry<'a, 'b: 'a> {
|
||||||
pub(crate) data: DirFileEntryData,
|
pub(crate) data: DirFileEntryData,
|
||||||
|
pub(crate) short_name: ShortName,
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
pub(crate) lfn: Vec<u16>,
|
pub(crate) lfn: Vec<u16>,
|
||||||
pub(crate) entry_pos: u64,
|
pub(crate) entry_pos: u64,
|
||||||
pub(crate) offset_range: (u64, u64),
|
pub(crate) offset_range: (u64, u64),
|
||||||
@ -547,19 +590,17 @@ pub struct DirEntry<'a, 'b: 'a> {
|
|||||||
|
|
||||||
impl <'a, 'b> DirEntry<'a, 'b> {
|
impl <'a, 'b> DirEntry<'a, 'b> {
|
||||||
/// Returns short file name
|
/// Returns short file name
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
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]);
|
self.short_name.to_str().to_string()
|
||||||
let ext_str = String::from_utf8_lossy(&self.data.name[8..11]);
|
|
||||||
let name_trimmed = name_str.trim_right();
|
|
||||||
let ext_trimmed = ext_str.trim_right();
|
|
||||||
if ext_trimmed.is_empty() {
|
|
||||||
name_trimmed.to_string()
|
|
||||||
} else {
|
|
||||||
format!("{}.{}", name_trimmed, ext_trimmed)
|
|
||||||
}
|
}
|
||||||
|
#[cfg(not(feature = "alloc"))]
|
||||||
|
pub fn short_file_name(&self) -> &str {
|
||||||
|
self.short_name.to_str()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns long file name or if it doesn't exist fallbacks to short file name.
|
/// Returns long file name or if it doesn't exist fallbacks to short file name.
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
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)
|
||||||
@ -567,6 +608,10 @@ impl <'a, 'b> DirEntry<'a, 'b> {
|
|||||||
self.short_file_name()
|
self.short_file_name()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(not(feature = "alloc"))]
|
||||||
|
pub fn file_name(&self) -> &str {
|
||||||
|
self.short_file_name()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns file attributes
|
/// Returns file attributes
|
||||||
pub fn attributes(&self) -> FileAttributes {
|
pub fn attributes(&self) -> FileAttributes {
|
||||||
|
19
src/fs.rs
19
src/fs.rs
@ -11,8 +11,10 @@ use dir::{DirRawStream, Dir};
|
|||||||
use dir_entry::DIR_ENTRY_SIZE;
|
use dir_entry::DIR_ENTRY_SIZE;
|
||||||
use table::{ClusterIterator, alloc_cluster, read_fat_flags};
|
use table::{ClusterIterator, alloc_cluster, read_fat_flags};
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(all(not(feature = "std"), feature = "alloc"))]
|
||||||
use alloc::{String, string::ToString};
|
use alloc::{String, string::ToString};
|
||||||
|
#[cfg(all(not(feature = "std"), not(feature = "alloc")))]
|
||||||
|
use core::str;
|
||||||
|
|
||||||
// FAT implementation based on:
|
// FAT implementation based on:
|
||||||
// http://wiki.osdev.org/FAT
|
// http://wiki.osdev.org/FAT
|
||||||
@ -46,6 +48,14 @@ impl<T> ReadSeek for T where T: Read + Seek {}
|
|||||||
pub trait ReadWriteSeek: Read + Write + Seek {}
|
pub trait ReadWriteSeek: Read + Write + Seek {}
|
||||||
impl<T> ReadWriteSeek for T where T: Read + Write + Seek {}
|
impl<T> ReadWriteSeek for T where T: Read + Write + Seek {}
|
||||||
|
|
||||||
|
pub(crate) fn strip_non_ascii(slice: &mut [u8]) {
|
||||||
|
for c in slice {
|
||||||
|
if *c < 0x20 || *c >= 0x80 {
|
||||||
|
*c = '_' as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Default, Debug, Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
struct BiosParameterBlock {
|
struct BiosParameterBlock {
|
||||||
@ -130,6 +140,8 @@ impl BiosParameterBlock {
|
|||||||
rdr.read_exact(&mut bpb.volume_label)?;
|
rdr.read_exact(&mut bpb.volume_label)?;
|
||||||
rdr.read_exact(&mut bpb.fs_type_label)?;
|
rdr.read_exact(&mut bpb.fs_type_label)?;
|
||||||
}
|
}
|
||||||
|
// Strip non-ascii characters from volume label
|
||||||
|
strip_non_ascii(&mut bpb.volume_label);
|
||||||
if bpb.ext_sig != 0x29 {
|
if bpb.ext_sig != 0x29 {
|
||||||
// fields after ext_sig are not used - clean them
|
// fields after ext_sig are not used - clean them
|
||||||
bpb.volume_id = 0;
|
bpb.volume_id = 0;
|
||||||
@ -284,9 +296,14 @@ impl <'a> FileSystem<'a> {
|
|||||||
///
|
///
|
||||||
/// Note: File with VOLUME_ID attribute in root directory is ignored by this library.
|
/// Note: File with VOLUME_ID attribute in root directory is ignored by this library.
|
||||||
/// Only label from BPB is used.
|
/// Only label from BPB is used.
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
pub fn volume_label(&self) -> String {
|
pub fn volume_label(&self) -> String {
|
||||||
String::from_utf8_lossy(&self.bpb.volume_label).trim_right().to_string()
|
String::from_utf8_lossy(&self.bpb.volume_label).trim_right().to_string()
|
||||||
}
|
}
|
||||||
|
#[cfg(not(feature = "alloc"))]
|
||||||
|
pub fn volume_label(&self) -> &str {
|
||||||
|
str::from_utf8(&self.bpb.volume_label).unwrap_or("").trim_right()
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns root directory object allowing futher penetration of filesystem structure.
|
/// 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> {
|
||||||
|
@ -4,6 +4,9 @@
|
|||||||
#![cfg_attr(not(feature="std"), no_std)]
|
#![cfg_attr(not(feature="std"), no_std)]
|
||||||
#![cfg_attr(not(feature="std"), feature(alloc))]
|
#![cfg_attr(not(feature="std"), feature(alloc))]
|
||||||
|
|
||||||
|
// Disable warnings to not clutter code with cfg too much
|
||||||
|
#![cfg_attr(not(feature="alloc"), allow(dead_code, unused_imports))]
|
||||||
|
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
@ -18,7 +21,7 @@ extern crate chrono;
|
|||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
extern crate core_io;
|
extern crate core_io;
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(all(not(feature = "std"), feature = "alloc"))]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
@ -27,6 +30,8 @@ mod dir;
|
|||||||
mod dir_entry;
|
mod dir_entry;
|
||||||
mod file;
|
mod file;
|
||||||
mod table;
|
mod table;
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
@ -48,4 +53,6 @@ pub use fs::*;
|
|||||||
pub use dir::*;
|
pub use dir::*;
|
||||||
pub use dir_entry::*;
|
pub use dir_entry::*;
|
||||||
pub use file::*;
|
pub use file::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
pub use utils::*;
|
pub use utils::*;
|
||||||
|
Loading…
Reference in New Issue
Block a user