Skip to content

Commit 11f6f41

Browse files
committed
Better support for volume labels.
They can contain spaces! But not .
1 parent a8f1d85 commit 11f6f41

File tree

2 files changed

+81
-82
lines changed

2 files changed

+81
-82
lines changed

src/fat/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ mod test {
139139
"#;
140140
let results = [
141141
Expected::Short(DirEntry {
142-
name: ShortFileName::create_from_str_mixed_case("boot").unwrap(),
142+
name: ShortFileName::create_volume_label_from_str("boot").unwrap(),
143143
mtime: Timestamp::from_calendar(2015, 11, 21, 19, 35, 18).unwrap(),
144144
ctime: Timestamp::from_calendar(2015, 11, 21, 19, 35, 18).unwrap(),
145145
attributes: Attributes::create_from_fat(Attributes::VOLUME),

src/filesystem/filename.rs

Lines changed: 80 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,15 @@ impl ToShortFileName for &str {
4040
}
4141
}
4242

43+
/// How filenames should be handled
44+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
45+
enum FilenameKind {
46+
/// Stored mixed case
47+
VolumeLabel,
48+
/// Converted to upper case
49+
DirectoryEntry,
50+
}
51+
4352
/// An MS-DOS 8.3 filename. 7-bit ASCII only. All lower-case is converted to
4453
/// upper-case by default.
4554
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
@@ -82,6 +91,26 @@ impl ShortFileName {
8291

8392
/// Create a new MS-DOS 8.3 space-padded file name as stored in the directory entry.
8493
pub fn create_from_str(name: &str) -> Result<ShortFileName, FilenameError> {
94+
Self::create_from_str_inner(name, FilenameKind::DirectoryEntry)
95+
}
96+
97+
/// Create a new Volume Label.
98+
pub fn create_volume_label_from_str(name: &str) -> Result<ShortFileName, FilenameError> {
99+
Self::create_from_str_inner(name, FilenameKind::VolumeLabel)
100+
}
101+
102+
/// Create a new MS-DOS 8.3 space-padded file name as stored in the
103+
/// directory entry.
104+
///
105+
/// The Unicode string must contain only code points from ISO-8859-1 (i.e.
106+
/// Code Points \U+0000 to \U+00FF).
107+
///
108+
/// Convert the filename to upper case if specified by the `case_handling`
109+
/// argument.
110+
fn create_from_str_inner(
111+
name: &str,
112+
case_handling: FilenameKind,
113+
) -> Result<ShortFileName, FilenameError> {
85114
let mut sfn = ShortFileName {
86115
contents: [b' '; Self::FILENAME_MAX_LEN],
87116
};
@@ -98,92 +127,42 @@ impl ShortFileName {
98127

99128
let mut idx = 0;
100129
let mut seen_dot = false;
101-
for ch in name.bytes() {
130+
for ch in name.chars() {
102131
match ch {
103132
// Microsoft say these are the invalid characters
104-
0x00..=0x1F
105-
| 0x20
106-
| 0x22
107-
| 0x2A
108-
| 0x2B
109-
| 0x2C
110-
| 0x2F
111-
| 0x3A
112-
| 0x3B
113-
| 0x3C
114-
| 0x3D
115-
| 0x3E
116-
| 0x3F
117-
| 0x5B
118-
| 0x5C
119-
| 0x5D
120-
| 0x7C => {
133+
'\u{0000}'..='\u{001F}'
134+
| '"'
135+
| '*'
136+
| '+'
137+
| ','
138+
| '/'
139+
| ':'
140+
| ';'
141+
| '<'
142+
| '='
143+
| '>'
144+
| '?'
145+
| '['
146+
| '\\'
147+
| ']'
148+
| '|' => {
121149
return Err(FilenameError::InvalidCharacter);
122150
}
123-
// Denotes the start of the file extension
124-
b'.' => {
125-
if (1..=Self::FILENAME_BASE_MAX_LEN).contains(&idx) {
126-
idx = Self::FILENAME_BASE_MAX_LEN;
127-
seen_dot = true;
128-
} else {
129-
return Err(FilenameError::MisplacedPeriod);
130-
}
131-
}
132-
_ => {
133-
let ch = ch.to_ascii_uppercase();
134-
if seen_dot {
135-
if (Self::FILENAME_BASE_MAX_LEN..Self::FILENAME_MAX_LEN).contains(&idx) {
136-
sfn.contents[idx] = ch;
137-
} else {
138-
return Err(FilenameError::NameTooLong);
139-
}
140-
} else if idx < Self::FILENAME_BASE_MAX_LEN {
141-
sfn.contents[idx] = ch;
142-
} else {
143-
return Err(FilenameError::NameTooLong);
144-
}
145-
idx += 1;
151+
' ' if case_handling == FilenameKind::DirectoryEntry => {
152+
// can't have spaces in regular file names
153+
return Err(FilenameError::InvalidCharacter);
146154
}
147-
}
148-
}
149-
if idx == 0 {
150-
return Err(FilenameError::FilenameEmpty);
151-
}
152-
Ok(sfn)
153-
}
154-
155-
/// Create a new MS-DOS 8.3 space-padded file name as stored in the directory entry.
156-
/// Use this for volume labels with mixed case.
157-
pub fn create_from_str_mixed_case(name: &str) -> Result<ShortFileName, FilenameError> {
158-
let mut sfn = ShortFileName {
159-
contents: [b' '; Self::FILENAME_MAX_LEN],
160-
};
161-
let mut idx = 0;
162-
let mut seen_dot = false;
163-
for ch in name.bytes() {
164-
match ch {
165-
// Microsoft say these are the invalid characters
166-
0x00..=0x1F
167-
| 0x20
168-
| 0x22
169-
| 0x2A
170-
| 0x2B
171-
| 0x2C
172-
| 0x2F
173-
| 0x3A
174-
| 0x3B
175-
| 0x3C
176-
| 0x3D
177-
| 0x3E
178-
| 0x3F
179-
| 0x5B
180-
| 0x5C
181-
| 0x5D
182-
| 0x7C => {
155+
x if x > '\u{00FF}' => {
156+
// We only handle ISO-8859-1 which is Unicode Code Points
157+
// \U+0000 to \U+00FF. This is above that.
183158
return Err(FilenameError::InvalidCharacter);
184159
}
185-
// Denotes the start of the file extension
186-
b'.' => {
160+
'.' if case_handling == FilenameKind::VolumeLabel => {
161+
// not allowed
162+
return Err(FilenameError::MisplacedPeriod);
163+
}
164+
'.' => {
165+
// Denotes the start of the file extension
187166
if (1..=Self::FILENAME_BASE_MAX_LEN).contains(&idx) {
188167
idx = Self::FILENAME_BASE_MAX_LEN;
189168
seen_dot = true;
@@ -192,14 +171,23 @@ impl ShortFileName {
192171
}
193172
}
194173
_ => {
174+
let b = if case_handling == FilenameKind::DirectoryEntry {
175+
ch.to_ascii_uppercase() as u8
176+
} else {
177+
ch as u8
178+
};
195179
if seen_dot {
196180
if (Self::FILENAME_BASE_MAX_LEN..Self::FILENAME_MAX_LEN).contains(&idx) {
197-
sfn.contents[idx] = ch;
181+
sfn.contents[idx] = b;
198182
} else {
199183
return Err(FilenameError::NameTooLong);
200184
}
201185
} else if idx < Self::FILENAME_BASE_MAX_LEN {
202-
sfn.contents[idx] = ch;
186+
sfn.contents[idx] = b;
187+
} else if idx < Self::FILENAME_MAX_LEN
188+
&& case_handling == FilenameKind::VolumeLabel
189+
{
190+
sfn.contents[idx] = b;
203191
} else {
204192
return Err(FilenameError::NameTooLong);
205193
}
@@ -337,6 +325,17 @@ mod test {
337325
assert!(ShortFileName::create_from_str("123456789").is_err());
338326
assert!(ShortFileName::create_from_str("12345678.ABCD").is_err());
339327
}
328+
329+
#[test]
330+
fn volume_name() {
331+
let sfn = ShortFileName {
332+
contents: *b"Hello \xA399 ",
333+
};
334+
assert_eq!(
335+
sfn,
336+
ShortFileName::create_volume_label_from_str("Hello £99").unwrap()
337+
)
338+
}
340339
}
341340

342341
// ****************************************************************************

0 commit comments

Comments
 (0)