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}