Skip to content

Commit 62546fc

Browse files
authored
Merge branch 'develop' into chunks-exact
2 parents 5e718c2 + 504b440 commit 62546fc

File tree

12 files changed

+341
-204
lines changed

12 files changed

+341
-204
lines changed

examples/shell.rs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -230,10 +230,17 @@ impl Context {
230230
let dir = self.resolve_existing_directory(path)?;
231231
let mut dir = dir.to_directory(&mut self.volume_mgr);
232232
dir.iterate_dir(|entry| {
233-
println!(
234-
"{:12} {:9} {} {} {:08X?} {:?}",
235-
entry.name, entry.size, entry.ctime, entry.mtime, entry.cluster, entry.attributes
236-
);
233+
if !entry.attributes.is_volume() && !entry.attributes.is_lfn() {
234+
println!(
235+
"{:12} {:9} {} {} {:08X?} {:?}",
236+
entry.name,
237+
entry.size,
238+
entry.ctime,
239+
entry.mtime,
240+
entry.cluster,
241+
entry.attributes
242+
);
243+
}
237244
})?;
238245
Ok(())
239246
}
@@ -310,6 +317,8 @@ impl Context {
310317
for fragment in full_path.iterate_components().filter(|s| !s.is_empty()) {
311318
if fragment == ".." {
312319
s.path.pop();
320+
} else if fragment == "." {
321+
// do nothing
313322
} else {
314323
s.path.push(fragment.to_owned());
315324
}
@@ -533,7 +542,11 @@ fn main() -> Result<(), Error> {
533542
for volume_no in 0..4 {
534543
match ctx.volume_mgr.open_raw_volume(VolumeIdx(volume_no)) {
535544
Ok(volume) => {
536-
println!("Volume # {}: found", Context::volume_to_letter(volume_no));
545+
println!(
546+
"Volume # {}: found, label: {:?}",
547+
Context::volume_to_letter(volume_no),
548+
ctx.volume_mgr.get_root_volume_label(volume)?
549+
);
537550
match ctx.volume_mgr.open_root_dir(volume) {
538551
Ok(root_dir) => {
539552
ctx.volumes[volume_no] = Some(VolumeState {

src/fat/bpb.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -85,23 +85,23 @@ impl<'a> Bpb<'a> {
8585
// FAT16/FAT32 functions
8686

8787
/// Get the Volume Label string for this volume
88-
pub fn volume_label(&self) -> &[u8] {
89-
if self.fat_type != FatType::Fat32 {
90-
&self.data[43..=53]
91-
} else {
92-
&self.data[71..=81]
88+
pub fn volume_label(&self) -> [u8; 11] {
89+
let mut result = [0u8; 11];
90+
match self.fat_type {
91+
FatType::Fat16 => result.copy_from_slice(&self.data[43..=53]),
92+
FatType::Fat32 => result.copy_from_slice(&self.data[71..=81]),
9393
}
94+
result
9495
}
9596

9697
// FAT32 only functions
9798

9899
/// On a FAT32 volume, return the free block count from the Info Block. On
99100
/// a FAT16 volume, returns None.
100101
pub fn fs_info_block(&self) -> Option<BlockCount> {
101-
if self.fat_type != FatType::Fat32 {
102-
None
103-
} else {
104-
Some(BlockCount(u32::from(self.fs_info())))
102+
match self.fat_type {
103+
FatType::Fat16 => None,
104+
FatType::Fat32 => Some(BlockCount(u32::from(self.fs_info()))),
105105
}
106106
}
107107

src/fat/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,11 @@ mod test {
138138
"#;
139139
let results = [
140140
Expected::Short(DirEntry {
141-
name: ShortFileName::create_from_str_mixed_case("boot").unwrap(),
141+
name: unsafe {
142+
VolumeName::create_from_str("boot")
143+
.unwrap()
144+
.to_short_filename()
145+
},
142146
mtime: Timestamp::from_calendar(2015, 11, 21, 19, 35, 18).unwrap(),
143147
ctime: Timestamp::from_calendar(2015, 11, 21, 19, 35, 18).unwrap(),
144148
attributes: Attributes::create_from_fat(Attributes::VOLUME),
@@ -348,7 +352,7 @@ mod test {
348352
assert_eq!(bpb.fat_size16(), 32);
349353
assert_eq!(bpb.total_blocks32(), 122_880);
350354
assert_eq!(bpb.footer(), 0xAA55);
351-
assert_eq!(bpb.volume_label(), b"boot ");
355+
assert_eq!(bpb.volume_label(), *b"boot ");
352356
assert_eq!(bpb.fat_size(), 32);
353357
assert_eq!(bpb.total_blocks(), 122_880);
354358
assert_eq!(bpb.fat_type, FatType::Fat16);

src/fat/volume.rs

Lines changed: 131 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::{
66
Bpb, Fat16Info, Fat32Info, FatSpecificInfo, FatType, InfoSector, OnDiskDirEntry,
77
RESERVED_ENTRIES,
88
},
9+
filesystem::FilenameError,
910
trace, warn, Attributes, Block, BlockCount, BlockDevice, BlockIdx, ClusterId, DirEntry,
1011
DirectoryInfo, Error, ShortFileName, TimeSource, VolumeType,
1112
};
@@ -14,26 +15,121 @@ use core::convert::TryFrom;
1415

1516
use super::BlockCache;
1617

17-
/// The name given to a particular FAT formatted volume.
18+
/// An MS-DOS 11 character volume label.
19+
///
20+
/// ISO-8859-1 encoding is assumed. Trailing spaces are trimmed. Reserved
21+
/// characters are not allowed. There is no file extension, unlike with a
22+
/// filename.
23+
///
24+
/// Volume labels can be found in the BIOS Parameter Block, and in a root
25+
/// directory entry with the 'Volume Label' bit set. Both places should have the
26+
/// same contents, but they can get out of sync.
27+
///
28+
/// MS-DOS FDISK would show you the one in the BPB, but DIR would show you the
29+
/// one in the root directory.
1830
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
19-
#[derive(Clone, PartialEq, Eq)]
31+
#[derive(PartialEq, Eq, Clone)]
2032
pub struct VolumeName {
21-
data: [u8; 11],
33+
pub(crate) contents: [u8; Self::TOTAL_LEN],
2234
}
2335

2436
impl VolumeName {
25-
/// Create a new VolumeName
26-
pub fn new(data: [u8; 11]) -> VolumeName {
27-
VolumeName { data }
37+
const TOTAL_LEN: usize = 11;
38+
39+
/// Get name
40+
pub fn name(&self) -> &[u8] {
41+
self.contents.trim_ascii_end()
42+
}
43+
44+
/// Create a new MS-DOS volume label.
45+
pub fn create_from_str(name: &str) -> Result<VolumeName, FilenameError> {
46+
let mut sfn = VolumeName {
47+
contents: [b' '; Self::TOTAL_LEN],
48+
};
49+
50+
let mut idx = 0;
51+
for ch in name.chars() {
52+
match ch {
53+
// Microsoft say these are the invalid characters
54+
'\u{0000}'..='\u{001F}'
55+
| '"'
56+
| '*'
57+
| '+'
58+
| ','
59+
| '/'
60+
| ':'
61+
| ';'
62+
| '<'
63+
| '='
64+
| '>'
65+
| '?'
66+
| '['
67+
| '\\'
68+
| ']'
69+
| '.'
70+
| '|' => {
71+
return Err(FilenameError::InvalidCharacter);
72+
}
73+
x if x > '\u{00FF}' => {
74+
// We only handle ISO-8859-1 which is Unicode Code Points
75+
// \U+0000 to \U+00FF. This is above that.
76+
return Err(FilenameError::InvalidCharacter);
77+
}
78+
_ => {
79+
let b = ch as u8;
80+
if idx < Self::TOTAL_LEN {
81+
sfn.contents[idx] = b;
82+
} else {
83+
return Err(FilenameError::NameTooLong);
84+
}
85+
idx += 1;
86+
}
87+
}
88+
}
89+
if idx == 0 {
90+
return Err(FilenameError::FilenameEmpty);
91+
}
92+
Ok(sfn)
93+
}
94+
95+
/// Convert to a Short File Name
96+
///
97+
/// # Safety
98+
///
99+
/// Volume Labels can contain things that Short File Names cannot, so only
100+
/// do this conversion if you are creating the name of a directory entry
101+
/// with the 'Volume Label' attribute.
102+
pub unsafe fn to_short_filename(self) -> ShortFileName {
103+
ShortFileName {
104+
contents: self.contents,
105+
}
28106
}
29107
}
30108

31-
impl core::fmt::Debug for VolumeName {
32-
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
33-
match core::str::from_utf8(&self.data) {
34-
Ok(s) => write!(fmt, "{:?}", s),
35-
Err(_e) => write!(fmt, "{:?}", &self.data),
109+
impl core::fmt::Display for VolumeName {
110+
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
111+
let mut printed = 0;
112+
for &c in self.name().iter() {
113+
// converting a byte to a codepoint means you are assuming
114+
// ISO-8859-1 encoding, because that's how Unicode was designed.
115+
write!(f, "{}", c as char)?;
116+
printed += 1;
117+
}
118+
if let Some(mut width) = f.width() {
119+
if width > printed {
120+
width -= printed;
121+
for _ in 0..width {
122+
write!(f, "{}", f.fill())?;
123+
}
124+
}
36125
}
126+
Ok(())
127+
}
128+
}
129+
130+
impl core::fmt::Debug for VolumeName {
131+
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
132+
write!(f, "VolumeName(\"{}\")", self)
37133
}
38134
}
39135

@@ -498,7 +594,7 @@ impl FatVolume {
498594
// Can quit early
499595
return Ok(());
500596
} else if dir_entry.is_valid() && !dir_entry.is_lfn() {
501-
// Safe, since Block::LEN always fits on a u32
597+
// Block::LEN always fits on a u32
502598
let start = (i * OnDiskDirEntry::LEN) as u32;
503599
let entry = dir_entry.get_entry(FatType::Fat16, block_idx, start);
504600
func(&entry);
@@ -554,7 +650,7 @@ impl FatVolume {
554650
// Can quit early
555651
return Ok(());
556652
} else if dir_entry.is_valid() && !dir_entry.is_lfn() {
557-
// Safe, since Block::LEN always fits on a u32
653+
// Block::LEN always fits on a u32
558654
let start = (i * OnDiskDirEntry::LEN) as u32;
559655
let entry = dir_entry.get_entry(FatType::Fat32, block, start);
560656
func(&entry);
@@ -680,7 +776,7 @@ impl FatVolume {
680776
break;
681777
} else if dir_entry.matches(match_name) {
682778
// Found it
683-
// Safe, since Block::LEN always fits on a u32
779+
// Block::LEN always fits on a u32
684780
let start = (i * OnDiskDirEntry::LEN) as u32;
685781
return Ok(dir_entry.get_entry(fat_type, block, start));
686782
}
@@ -1104,10 +1200,12 @@ where
11041200
let first_root_dir_block =
11051201
fat_start + BlockCount(u32::from(bpb.num_fats()) * bpb.fat_size());
11061202
let first_data_block = first_root_dir_block + BlockCount(root_dir_blocks);
1107-
let mut volume = FatVolume {
1203+
let volume = FatVolume {
11081204
lba_start,
11091205
num_blocks,
1110-
name: VolumeName { data: [0u8; 11] },
1206+
name: VolumeName {
1207+
contents: bpb.volume_label(),
1208+
},
11111209
blocks_per_cluster: bpb.blocks_per_cluster(),
11121210
first_data_block: (first_data_block),
11131211
fat_start: BlockCount(u32::from(bpb.reserved_block_count())),
@@ -1119,7 +1217,6 @@ where
11191217
first_root_dir_block,
11201218
}),
11211219
};
1122-
volume.name.data[..].copy_from_slice(bpb.volume_label());
11231220
Ok(VolumeType::Fat(volume))
11241221
}
11251222
FatType::Fat32 => {
@@ -1138,10 +1235,12 @@ where
11381235
let info_sector =
11391236
InfoSector::create_from_bytes(info_block).map_err(Error::FormatError)?;
11401237

1141-
let mut volume = FatVolume {
1238+
let volume = FatVolume {
11421239
lba_start,
11431240
num_blocks,
1144-
name: VolumeName { data: [0u8; 11] },
1241+
name: VolumeName {
1242+
contents: bpb.volume_label(),
1243+
},
11451244
blocks_per_cluster: bpb.blocks_per_cluster(),
11461245
first_data_block: BlockCount(first_data_block),
11471246
fat_start: BlockCount(u32::from(bpb.reserved_block_count())),
@@ -1153,12 +1252,24 @@ where
11531252
first_root_dir_cluster: ClusterId(bpb.first_root_dir_cluster()),
11541253
}),
11551254
};
1156-
volume.name.data[..].copy_from_slice(bpb.volume_label());
11571255
Ok(VolumeType::Fat(volume))
11581256
}
11591257
}
11601258
}
11611259

1260+
#[cfg(test)]
1261+
mod tests {
1262+
use super::*;
1263+
1264+
#[test]
1265+
fn volume_name() {
1266+
let sfn = VolumeName {
1267+
contents: *b"Hello \xA399 ",
1268+
};
1269+
assert_eq!(sfn, VolumeName::create_from_str("Hello £99").unwrap())
1270+
}
1271+
}
1272+
11621273
// ****************************************************************************
11631274
//
11641275
// End Of File

src/filesystem/directory.rs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
use core::convert::TryFrom;
2-
31
use crate::blockdevice::BlockIdx;
42
use crate::fat::{FatType, OnDiskDirEntry};
5-
use crate::filesystem::{Attributes, ClusterId, SearchId, ShortFileName, Timestamp};
3+
use crate::filesystem::{Attributes, ClusterId, Handle, ShortFileName, Timestamp};
64
use crate::{Error, RawVolume, VolumeManager};
75

86
use super::ToShortFileName;
@@ -47,7 +45,7 @@ pub struct DirEntry {
4745
/// and there's a reason we did it this way.
4846
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
4947
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
50-
pub struct RawDirectory(pub(crate) SearchId);
48+
pub struct RawDirectory(pub(crate) Handle);
5149

5250
impl RawDirectory {
5351
/// Convert a raw directory into a droppable [`Directory`]
@@ -240,9 +238,9 @@ where
240238
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
241239
#[derive(Debug, Clone)]
242240
pub(crate) struct DirectoryInfo {
243-
/// Unique ID for this directory.
241+
/// The handle for this directory.
244242
pub(crate) directory_id: RawDirectory,
245-
/// The unique ID for the volume this directory is on
243+
/// The handle for the volume this directory is on
246244
pub(crate) volume_id: RawVolume,
247245
/// The starting point of the directory listing.
248246
pub(crate) cluster: ClusterId,
@@ -262,16 +260,12 @@ impl DirEntry {
262260
[0u8; 2]
263261
} else {
264262
// Safe due to the AND operation
265-
u16::try_from((cluster_number >> 16) & 0x0000_FFFF)
266-
.unwrap()
267-
.to_le_bytes()
263+
(((cluster_number >> 16) & 0x0000_FFFF) as u16).to_le_bytes()
268264
};
269265
data[20..22].copy_from_slice(&cluster_hi[..]);
270266
data[22..26].copy_from_slice(&self.mtime.serialize_to_fat()[..]);
271267
// Safe due to the AND operation
272-
let cluster_lo = u16::try_from(cluster_number & 0x0000_FFFF)
273-
.unwrap()
274-
.to_le_bytes();
268+
let cluster_lo = ((cluster_number & 0x0000_FFFF) as u16).to_le_bytes();
275269
data[26..28].copy_from_slice(&cluster_lo[..]);
276270
data[28..32].copy_from_slice(&self.size.to_le_bytes()[..]);
277271
data

0 commit comments

Comments
 (0)