@@ -6,6 +6,7 @@ use crate::{
6
6
Bpb , Fat16Info , Fat32Info , FatSpecificInfo , FatType , InfoSector , OnDiskDirEntry ,
7
7
RESERVED_ENTRIES ,
8
8
} ,
9
+ filesystem:: FilenameError ,
9
10
trace, warn, Attributes , Block , BlockCount , BlockDevice , BlockIdx , ClusterId , DirEntry ,
10
11
DirectoryInfo , Error , ShortFileName , TimeSource , VolumeType ,
11
12
} ;
@@ -14,26 +15,121 @@ use core::convert::TryFrom;
14
15
15
16
use super :: BlockCache ;
16
17
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.
18
30
#[ cfg_attr( feature = "defmt-log" , derive( defmt:: Format ) ) ]
19
- #[ derive( Clone , PartialEq , Eq ) ]
31
+ #[ derive( PartialEq , Eq , Clone ) ]
20
32
pub struct VolumeName {
21
- data : [ u8 ; 11 ] ,
33
+ pub ( crate ) contents : [ u8 ; Self :: TOTAL_LEN ] ,
22
34
}
23
35
24
36
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
+ }
28
106
}
29
107
}
30
108
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
+ }
36
125
}
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 )
37
133
}
38
134
}
39
135
@@ -1091,10 +1187,12 @@ where
1091
1187
let first_root_dir_block =
1092
1188
fat_start + BlockCount ( u32:: from ( bpb. num_fats ( ) ) * bpb. fat_size ( ) ) ;
1093
1189
let first_data_block = first_root_dir_block + BlockCount ( root_dir_blocks) ;
1094
- let mut volume = FatVolume {
1190
+ let volume = FatVolume {
1095
1191
lba_start,
1096
1192
num_blocks,
1097
- name : VolumeName { data : [ 0u8 ; 11 ] } ,
1193
+ name : VolumeName {
1194
+ contents : bpb. volume_label ( ) ,
1195
+ } ,
1098
1196
blocks_per_cluster : bpb. blocks_per_cluster ( ) ,
1099
1197
first_data_block : ( first_data_block) ,
1100
1198
fat_start : BlockCount ( u32:: from ( bpb. reserved_block_count ( ) ) ) ,
@@ -1106,7 +1204,6 @@ where
1106
1204
first_root_dir_block,
1107
1205
} ) ,
1108
1206
} ;
1109
- volume. name . data [ ..] . copy_from_slice ( bpb. volume_label ( ) ) ;
1110
1207
Ok ( VolumeType :: Fat ( volume) )
1111
1208
}
1112
1209
FatType :: Fat32 => {
@@ -1128,10 +1225,12 @@ where
1128
1225
let info_sector =
1129
1226
InfoSector :: create_from_bytes ( info_block) . map_err ( Error :: FormatError ) ?;
1130
1227
1131
- let mut volume = FatVolume {
1228
+ let volume = FatVolume {
1132
1229
lba_start,
1133
1230
num_blocks,
1134
- name : VolumeName { data : [ 0u8 ; 11 ] } ,
1231
+ name : VolumeName {
1232
+ contents : bpb. volume_label ( ) ,
1233
+ } ,
1135
1234
blocks_per_cluster : bpb. blocks_per_cluster ( ) ,
1136
1235
first_data_block : BlockCount ( first_data_block) ,
1137
1236
fat_start : BlockCount ( u32:: from ( bpb. reserved_block_count ( ) ) ) ,
@@ -1143,12 +1242,24 @@ where
1143
1242
first_root_dir_cluster : ClusterId ( bpb. first_root_dir_cluster ( ) ) ,
1144
1243
} ) ,
1145
1244
} ;
1146
- volume. name . data [ ..] . copy_from_slice ( bpb. volume_label ( ) ) ;
1147
1245
Ok ( VolumeType :: Fat ( volume) )
1148
1246
}
1149
1247
}
1150
1248
}
1151
1249
1250
+ #[ cfg( test) ]
1251
+ mod tests {
1252
+ use super :: * ;
1253
+
1254
+ #[ test]
1255
+ fn volume_name ( ) {
1256
+ let sfn = VolumeName {
1257
+ contents : * b"Hello \xA3 99 " ,
1258
+ } ;
1259
+ assert_eq ! ( sfn, VolumeName :: create_from_str( "Hello £99" ) . unwrap( ) )
1260
+ }
1261
+ }
1262
+
1152
1263
// ****************************************************************************
1153
1264
//
1154
1265
// End Of File
0 commit comments