exfat_fs/lib.rs
1//! # exFAT-fs
2//!
3//! exFAT filesystem implementation in Rust.
4//!
5//! ## Features
6//! - exFAT formatting
7//! - `no-std` support
8//! - reading
9//!
10//! ## Usage
11//!
12//! ### Formatting
13//! ```rust
14//! use exfat_fs::{
15//! MB,
16//! Label,
17//! format::{Exfat, FormatVolumeOptionsBuilder},
18//! };
19//!
20//! use std::{io::Cursor, time::SystemTime};
21//!
22//! let size: u64 = 32 * MB as u64;
23//! let hello_label = Label::new("Hello".to_string()).unwrap();
24//!
25//! let format_options = FormatVolumeOptionsBuilder::default()
26//! .pack_bitmap(false)
27//! .full_format(false)
28//! .dev_size(size)
29//! .label(hello_label)
30//! .bytes_per_sector(512)
31//! .build()
32//! .unwrap();
33//!
34//! let mut formatter = Exfat::try_from::<SystemTime>(format_options).unwrap();
35//!
36//!
37//! let mut file = Cursor::new(vec![0u8; size as usize]);
38//!
39//!
40//! formatter.write::<SystemTime, Cursor<Vec<u8>>>(&mut file).unwrap();
41//! ```
42//!
43//! ### Reading
44//! ```no_run
45//! use exfat_fs::dir::{Root, entry::fs::FsElement};
46//! use std::{fs::OpenOptions, io::Read};
47//!
48//! # let file = OpenOptions::new().read(true).open("exfat_vol").unwrap();
49//!
50//! // Load root directory
51//! let mut root = Root::open(file).unwrap();
52//!
53//! // Get contents of first element (file)
54//! if let FsElement::F(ref mut file) = root.items()[0] {
55//! let mut buffer = String::default();
56//! file.read_to_string(&mut buffer).unwrap();
57//! println!("Contents of file: {buffer}");
58//! }
59//! ```
60//!
61//! ## Limitations
62//! Writing is currently not supported (WIP).
63#![cfg_attr(not(any(feature = "std", test)), no_std)]
64
65#[cfg(any(feature = "std", test))]
66extern crate std;
67
68extern crate alloc;
69
70use alloc::{string::String, vec::Vec};
71pub(crate) mod boot_sector;
72/// Directory abstractions
73pub mod dir;
74/// Disk utility functions
75pub mod disk;
76pub mod error;
77pub(crate) mod fat;
78/// Filesystem formatting capabilities
79pub mod format;
80pub mod timestamp;
81pub(crate) mod upcase_table;
82
83pub const GB: u32 = 1024 * 1024 * 1024;
84pub const MB: u32 = 1024 * 1024;
85pub const KB: u16 = 1024;
86
87pub const DEFAULT_BOUNDARY_ALIGNEMENT: u32 = 1024 * 1024;
88/// First usable cluster index of the cluster heap
89pub(crate) const FIRST_USABLE_CLUSTER_INDEX: u32 = 2;
90
91/// A UTF16 encoded volume label. The length must not exceed 11 characters.
92#[derive(Copy, Clone, Debug, Default)]
93pub struct Label(pub(crate) [u8; 22], pub(crate) u8);
94
95impl Label {
96 pub fn new(label: String) -> Option<Label> {
97 let len = label.len();
98 if len > 11 {
99 None
100 } else {
101 let mut utf16_bytes = [0u8; 22];
102
103 let encoded: Vec<u8> = label.encode_utf16().flat_map(|x| x.to_le_bytes()).collect();
104
105 let copy_len = encoded.len();
106 assert!(copy_len <= 22);
107 utf16_bytes[..copy_len].copy_from_slice(&encoded[..copy_len]);
108
109 Some(Label(utf16_bytes, len as u8))
110 }
111 }
112}
113impl core::fmt::Display for Label {
114 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
115 let mut converted = [0u16; 11];
116
117 for (i, chunk) in self.0[..self.1 as usize * 2].chunks_exact(2).enumerate() {
118 converted[i] = u16::from_ne_bytes([chunk[0], chunk[1]]);
119 }
120
121 match String::from_utf16(&converted) {
122 Ok(s) => write!(f, "{}", s),
123 Err(_) => write!(f, "<invalid utf16>"),
124 }
125 }
126}