1use core::ops::{Div, Sub};
2
3use crate::{
4 DEFAULT_BOUNDARY_ALIGNEMENT, FIRST_USABLE_CLUSTER_INDEX, GB, KB, Label, MB,
5 boot_sector::{FileSystemRevision, UnixEpochDuration, VolumeFlags, VolumeSerialNumber},
6 dir::{RawRoot, entry::DirEntry},
7 disk::{SeekFrom, WriteSeek},
8 error::ExfatError,
9 upcase_table::{DEFAULT_UPCASE_TABLE, UPCASE_TABLE_SIZE_BYTES},
10};
11use boot::{BACKUP_BOOT_OFFSET, MAIN_BOOT_OFFSET, MAX_CLUSTER_COUNT, MAX_CLUSTER_SIZE};
12use bytemuck::cast_slice;
13use checked_num::CheckedU64;
14use derive_builder::Builder;
15
16use crate::{disk, error::ExfatFormatError};
17use alloc::string::String;
18use alloc::string::ToString;
19use alloc::vec;
20mod boot;
22mod fat;
23
24#[derive(Builder, Copy, Clone, Debug)]
26#[builder(no_std, build_fn(validate = "Self::validate"))]
27pub struct FormatVolumeOptions {
28 #[builder(default = true)]
31 pack_bitmap: bool,
32 #[builder(default)]
34 full_format: bool,
35 dev_size: u64,
37 #[builder(default)]
39 label: Label,
40 #[builder(default)]
42 guid: Option<u128>,
43 #[builder(default)]
46 partition_offset: u64,
47 bytes_per_sector: u16,
49 #[builder(default = DEFAULT_BOUNDARY_ALIGNEMENT)]
52 boundary_align: u32,
53}
54
55impl FormatVolumeOptionsBuilder {
56 fn validate(&self) -> Result<(), String> {
57 if let Some(ref bytes_per_sector) = self.bytes_per_sector {
58 if !bytes_per_sector.is_power_of_two() || !(512..=4096).contains(bytes_per_sector) {
59 return Err(
60 "Bytes per sector field must be a power of two and between `512` and `4096`."
61 .to_string(),
62 );
63 }
64 }
65
66 if let Some(ref boundary_align) = self.boundary_align {
67 if !boundary_align.is_power_of_two() {
68 return Err("Boundary alignment field must be a power of two.".to_string());
69 }
70 }
71
72 Ok(())
73 }
74}
75
76#[derive(Copy, Clone, Debug)]
77pub struct Exfat {
78 volume_length: u64,
79 fat_offset: u32,
80 fat_length: u32,
81 cluster_heap_offset: u32,
82 cluster_count: u32,
83 cluster_count_used: u32,
84 first_cluster_of_root_directory: u32,
85 file_system_revision: FileSystemRevision,
86 volume_flags: VolumeFlags,
87 bytes_per_sector_shift: u8,
88 sectors_per_cluster_shift: u8,
89 number_of_fats: u8,
90 uptable_length_bytes: u32,
91 bitmap_length_bytes: u32,
92 bitmap_offset_bytes: u32,
93 bytes_per_cluster: u32,
94 volume_serial_number: VolumeSerialNumber,
95 root_offset_bytes: u32,
96 format_options: FormatVolumeOptions,
97 root_length_bytes: u32,
98 uptable_offset_bytes: u32,
99 uptable_start_cluster: u32,
100}
101
102impl Exfat {
103 pub fn try_from<T: UnixEpochDuration>(
106 format_options: FormatVolumeOptions,
107 ) -> Result<Self, ExfatFormatError<T>> {
108 let size = format_options.dev_size;
109
110 let bytes_per_cluster = default_cluster_size(size);
111
112 let number_of_fats = 1u8;
114 let volume_flags = VolumeFlags::empty();
115
116 let partition_offset =
118 format_options.partition_offset / format_options.bytes_per_sector as u64;
119
120 if !bytes_per_cluster.is_power_of_two()
121 || !(format_options.bytes_per_sector as u32..=MAX_CLUSTER_SIZE)
122 .contains(&bytes_per_cluster)
123 {
124 return Err(ExfatFormatError::InvlaidClusterSize(bytes_per_cluster));
125 }
126 let bytes_per_sector_shift = format_options.bytes_per_sector.ilog2() as u8;
127 let sectors_per_cluster_shift =
128 (bytes_per_cluster / format_options.bytes_per_sector as u32).ilog2() as u8;
129
130 let volume_length = size / format_options.bytes_per_sector as u64;
131
132 if volume_length < (1 << (20 - bytes_per_sector_shift)) {
133 return Err(ExfatFormatError::InvalidSize(size));
134 }
135
136 let fat_offset_bytes: u32 = (CheckedU64::new(format_options.bytes_per_sector as u64) * 24
137 + partition_offset)
138 .ok_or(ExfatFormatError::InvalidPartitionOffset(partition_offset))?
139 .next_multiple_of(format_options.boundary_align as u64)
140 .sub(partition_offset)
141 .try_into()
142 .map_err(|_| {
143 ExfatFormatError::BoundaryAlignemntTooBig(format_options.boundary_align)
144 })?;
145
146 let fat_offset = fat_offset_bytes / format_options.bytes_per_sector as u32;
147
148 let max_clusters: CheckedU64 =
149 ((CheckedU64::new(size) - fat_offset_bytes as u64 - number_of_fats as u64 * 8 - 1)
150 / (bytes_per_cluster as u64 + 4 * number_of_fats as u64)
151 + 1)
152 .ok_or(ExfatFormatError::InvlaidClusterSize(bytes_per_cluster))?
153 .into();
154
155 let fat_length_bytes = ((max_clusters + 2) * 4)
156 .ok_or(ExfatFormatError::InvlaidClusterSize(bytes_per_cluster))?
157 .next_multiple_of(format_options.bytes_per_sector as u64);
158
159 let fat_length: u32 = (fat_length_bytes / format_options.bytes_per_sector as u64)
160 .try_into()
161 .map_err(|_| ExfatFormatError::InvlaidClusterSize(bytes_per_cluster))?;
162
163 let mut cluster_heap_offset_bytes = ((partition_offset
164 + fat_offset_bytes as u64
165 + fat_length_bytes * number_of_fats as u64)
166 .next_multiple_of(format_options.boundary_align as u64)
167 - partition_offset) as u32;
168
169 let mut cluster_heap_offset =
170 cluster_heap_offset_bytes / format_options.bytes_per_sector as u32;
171
172 if cluster_heap_offset_bytes as u64 >= size {
173 return Err(ExfatFormatError::BoundaryAlignemntTooBig(
174 format_options.boundary_align,
175 ));
176 }
177
178 let mut cluster_count: u32 = ((size - cluster_heap_offset_bytes as u64)
179 / bytes_per_cluster as u64)
180 .try_into()
181 .map_err(|_| ExfatFormatError::InvlaidClusterSize(bytes_per_cluster))?;
182
183 if cluster_count
184 > MAX_CLUSTER_COUNT.min(
185 ((volume_length - cluster_heap_offset as u64)
186 / 2u64.pow(sectors_per_cluster_shift as u32)) as u32,
187 )
188 {
189 return Err(ExfatFormatError::InvlaidClusterSize(bytes_per_cluster));
190 }
191
192 let mut bitmap_offset_bytes = cluster_heap_offset_bytes;
194 let mut bitmap_length_bytes = cluster_count.next_multiple_of(8) / 8;
195
196 if format_options.pack_bitmap {
197 let fat_end_bytes = fat_offset_bytes as u64 + fat_length_bytes;
198 let mut bitmap_length_bytes_packed;
199 let mut bitmap_length_clusters_packed =
200 bitmap_length_bytes.next_multiple_of(bytes_per_cluster);
201
202 loop {
203 let bitmap_cluster_count_packed = bitmap_length_clusters_packed / bytes_per_cluster;
204 if ((cluster_heap_offset_bytes - bitmap_length_clusters_packed) as u64)
206 < fat_end_bytes
207 || cluster_count > MAX_CLUSTER_COUNT - bitmap_cluster_count_packed
208 {
209 return Err(ExfatFormatError::CannotPackBitmap);
210 }
211
212 let total_cluster_count = cluster_count + bitmap_cluster_count_packed;
213 bitmap_length_bytes_packed = total_cluster_count.next_multiple_of(8).div(8);
214 let new_bitmap_length_clusters =
215 bitmap_length_bytes_packed.next_multiple_of(bytes_per_cluster);
216
217 if new_bitmap_length_clusters == bitmap_length_clusters_packed {
218 cluster_heap_offset_bytes -= bitmap_length_clusters_packed;
219 cluster_count = total_cluster_count;
220 bitmap_offset_bytes -= bitmap_length_clusters_packed;
221 bitmap_length_bytes = bitmap_length_bytes_packed;
222 break;
223 }
224 bitmap_length_clusters_packed = new_bitmap_length_clusters;
225 }
226
227 cluster_heap_offset =
229 cluster_heap_offset_bytes / format_options.bytes_per_sector as u32;
230 }
231 let cluster_length = bitmap_length_bytes.next_multiple_of(bytes_per_cluster);
232
233 let uptable_offset_bytes = bitmap_offset_bytes + cluster_length;
234 let uptable_start_cluster = FIRST_USABLE_CLUSTER_INDEX + cluster_length / bytes_per_cluster;
235 let uptable_length_bytes = UPCASE_TABLE_SIZE_BYTES;
236
237 let cluster_length = uptable_length_bytes.next_multiple_of(bytes_per_cluster);
238
239 let root_offset_bytes = uptable_offset_bytes + cluster_length;
240 let first_cluster_of_root_directory =
241 uptable_start_cluster + cluster_length / bytes_per_cluster;
242
243 let file_system_revision = FileSystemRevision::default();
244 let volume_serial_number =
245 VolumeSerialNumber::try_new::<T>().map_err(|err| ExfatFormatError::NoSerial(err))?;
246
247 let root_length_bytes = size_of::<DirEntry>() as u32 * 3;
248 let cluster_count_used = 0; Ok(Self {
251 volume_length,
252 bytes_per_sector_shift,
253 fat_offset,
254 number_of_fats,
255 fat_length,
256 cluster_heap_offset,
257 cluster_count,
258 sectors_per_cluster_shift,
259 first_cluster_of_root_directory,
260 volume_flags,
261 volume_serial_number,
262 file_system_revision,
263 bytes_per_cluster,
264 root_offset_bytes,
265 format_options,
266 bitmap_length_bytes,
267 uptable_length_bytes,
268 root_length_bytes,
269 cluster_count_used,
270 bitmap_offset_bytes,
271 uptable_offset_bytes,
272 uptable_start_cluster,
273 })
274 }
275}
276
277impl Exfat {
278 pub fn write<T: UnixEpochDuration, O: WriteSeek>(
281 &mut self,
282 f: &mut O,
283 ) -> Result<(), ExfatError<T, O>>
284 where
285 T::Err: core::fmt::Debug,
286 {
287 let old_pos = f.stream_position().map_err(|err| ExfatError::Io(err))?;
288 let len = f
289 .seek(SeekFrom::End(0))
290 .map_err(|err| ExfatError::Io(err))?;
291
292 if old_pos != len {
293 f.seek(SeekFrom::Start(old_pos))
294 .map_err(|err| ExfatError::Io(err))?;
295 }
296
297 assert_eq!(len, self.format_options.dev_size);
298
299 if len != self.format_options.dev_size {
300 return Err(ExfatError::Format(ExfatFormatError::InvalidFileSize));
301 }
302
303 let size = if self.format_options.full_format {
304 self.format_options.dev_size
305 } else {
306 self.root_offset_bytes as u64 + self.bytes_per_cluster as u64
307 };
308
309 disk::write_zeroes(f, size, 0).map_err(|err| ExfatError::Io(err))?;
311
312 self.write_boot_region(f, MAIN_BOOT_OFFSET)
314 .map_err(|err| ExfatError::Io(err))?;
315
316 self.write_boot_region(f, BACKUP_BOOT_OFFSET)
318 .map_err(|err| ExfatError::Io(err))?;
319
320 self.write_fat(f).map_err(|err| ExfatError::Io(err))?;
322
323 self.write_bitmap(f).map_err(|err| ExfatError::Io(err))?;
325
326 self.write_upcase_table(f)
328 .map_err(|err| ExfatError::Io(err))?;
329
330 self.write_root_dir(f).map_err(|err| ExfatError::Io(err))?;
332 Ok(())
333 }
334}
335
336fn default_cluster_size(size: u64) -> u32 {
338 const FIRST_BOUND: u64 = 256 * MB as u64;
339 const FROM_FIRST_BOUND: u64 = FIRST_BOUND + 1;
340
341 const SECOND_BOUND: u64 = 32 * GB as u64;
342 const FROM_SECOND_BOUND: u64 = SECOND_BOUND + 1;
343
344 match size {
345 ..=FIRST_BOUND => 4 * KB as u32,
346 FROM_FIRST_BOUND..=SECOND_BOUND => 32 * KB as u32,
347 FROM_SECOND_BOUND.. => 128 * KB as u32,
348 }
349}
350
351impl Exfat {
352 fn write_upcase_table<T: WriteSeek>(&self, device: &mut T) -> Result<(), T::Err> {
353 device.seek(SeekFrom::Start(self.uptable_offset_bytes as u64))?;
354 device.write_all(&DEFAULT_UPCASE_TABLE)
355 }
356
357 fn write_bitmap<T: WriteSeek>(&self, device: &mut T) -> Result<(), T::Err> {
358 let mut bitmap = vec![0u8; self.bitmap_length_bytes as usize];
359
360 let full_bytes = self.cluster_count_used / 8;
362 let remaining_bits = self.cluster_count_used % 8;
364
365 let mut zero_offset = full_bytes;
367
368 bitmap[..full_bytes as usize].fill(0xff);
369
370 if remaining_bits != 0 {
372 bitmap[full_bytes as usize] = (1 << remaining_bits) - 1;
373 zero_offset += 1;
374 }
375
376 if zero_offset < self.bitmap_length_bytes {
377 bitmap[(zero_offset as usize)..].fill(0);
378 }
379
380 device.seek(SeekFrom::Start(self.bitmap_offset_bytes as u64))?;
381 device.write_all(cast_slice(&bitmap))
382 }
383
384 fn write_root_dir<T: WriteSeek>(&self, device: &mut T) -> Result<(), T::Err> {
385 let root = RawRoot::new(
386 self.format_options.label,
387 self.format_options.guid,
388 self.bitmap_length_bytes as u64,
389 self.uptable_start_cluster,
390 );
391
392 device.seek(SeekFrom::Start(self.root_offset_bytes as u64))?;
393 device.write_all(&root.bytes())?;
394 Ok(())
395 }
396}
397
398#[cfg(test)]
399#[test]
400fn small_format() {
401 use crate::Label;
402 use crate::format::FormatVolumeOptionsBuilder;
403 use std::io::Read;
404 use std::vec::Vec;
405
406 let size: u64 = 32 * crate::MB as u64;
407 let mut f = std::io::Cursor::new(vec![0u8; size as usize]);
408
409 let label = Label::new("Hello".to_string()).expect("label creation failed");
410
411 let format_options = FormatVolumeOptionsBuilder::default()
412 .label(label)
413 .pack_bitmap(false)
414 .full_format(false)
415 .dev_size(size)
416 .bytes_per_sector(512)
417 .boundary_align(crate::DEFAULT_BOUNDARY_ALIGNEMENT)
418 .build()
419 .expect("building format volume option failed");
420
421 let mut formatter =
422 Exfat::try_from::<std::time::SystemTime>(format_options).expect("formatting failed");
423 formatter
424 .write::<std::time::SystemTime, std::io::Cursor<Vec<u8>>>(&mut f)
425 .expect("writing failed");
426
427 let offset_volume_label_entry_bytes = 0x203000;
428 let mut read_buffer = vec![0u8; 32];
429 f.seek(crate::disk::SeekFrom::Start(
430 offset_volume_label_entry_bytes,
431 ))
432 .unwrap();
433 f.read_exact(&mut read_buffer).unwrap();
434
435 let vol_label_entry_type = read_buffer[0];
437 assert_eq!(
438 vol_label_entry_type, 0x83,
439 "Volume Label Root Directory Entry has invalid type"
440 );
441
442 let vol_label_length = read_buffer[1];
444 assert_eq!(
445 vol_label_length, 5,
446 "Volume Label Root Directory Entry has invalid label length"
447 );
448
449 assert_eq!(
451 &read_buffer[2..2 + vol_label_length as usize],
452 &label.0[..vol_label_length as usize],
453 "Volume Label Root Directory Entry has invalid data"
454 );
455 let offset_upcase_table_entry_bytes = 0x203060;
456
457 f.seek(crate::disk::SeekFrom::Start(
458 offset_upcase_table_entry_bytes,
459 ))
460 .unwrap();
461 f.read_exact(&mut read_buffer).unwrap();
462
463 assert_eq!(
465 read_buffer[0], 0x82,
466 "Upcase Table Root Directory Entry has invalid type"
467 );
468
469 assert_eq!(
471 u32::from_le_bytes(read_buffer[4..8].try_into().unwrap()),
472 0xe619d30d,
473 "Upcase Table Root Directory Entry has invalid checksum"
474 );
475
476 let offset_bitmap_entry_bytes = 0x203040;
477
478 f.seek(crate::disk::SeekFrom::Start(offset_bitmap_entry_bytes))
479 .unwrap();
480 f.read_exact(&mut read_buffer).unwrap();
481
482 assert_eq!(
484 read_buffer[0], 0x81,
485 "Allocation Bitmap Root Directory Entry has invalid type"
486 );
487
488 assert_eq!(
490 &read_buffer[24..],
491 960u64.to_le_bytes(),
492 "Allocation Bitmap Root Directory Entry has invalid size"
493 );
494}