@@ -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
1516use 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 ) ]
2032pub struct VolumeName {
21- data : [ u8 ; 11 ] ,
33+ pub ( crate ) contents : [ u8 ; Self :: TOTAL_LEN ] ,
2234}
2335
2436impl 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 \xA3 99 " ,
1268+ } ;
1269+ assert_eq ! ( sfn, VolumeName :: create_from_str( "Hello £99" ) . unwrap( ) )
1270+ }
1271+ }
1272+
11621273// ****************************************************************************
11631274//
11641275// End Of File
0 commit comments