Skip to content

Commit 96d2bdb

Browse files
committed
Read root dir entries from FAT partition
1 parent abfdba5 commit 96d2bdb

File tree

1 file changed

+135
-6
lines changed

1 file changed

+135
-6
lines changed

bios/second_stage/src/fat.rs

Lines changed: 135 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
// based on https://crates.io/crates/mini_fat
22

3-
use crate::disk::Read;
3+
use crate::{
4+
disk::{Read, Seek, SeekFrom},
5+
screen,
6+
};
7+
use core::{char::DecodeUtf16Error, fmt::Write as _};
48

59
const DIRECTORY_ENTRY_BYTES: usize = 32;
610
const UNUSED_ENTRY_PREFIX: u8 = 0xE5;
711
const END_OF_DIRECTORY_PREFIX: u8 = 0;
812

13+
static mut BUFFER: [u8; 0x4000] = [0; 0x4000];
14+
915
pub struct File {
1016
first_cluster: u32,
1117
file_size: u32,
@@ -31,7 +37,7 @@ struct Bpb {
3137
}
3238

3339
impl Bpb {
34-
fn parse<D: Read>(disk: &mut D) -> Self {
40+
fn parse<D: Read + Seek>(disk: &mut D) -> Self {
3541
let mut raw = [0u8; 512];
3642
disk.read_exact(&mut raw);
3743

@@ -128,7 +134,7 @@ pub struct FileSystem<D> {
128134
bpb: Bpb,
129135
}
130136

131-
impl<D: Read> FileSystem<D> {
137+
impl<D: Read + Seek> FileSystem<D> {
132138
pub fn parse(mut disk: D) -> Self {
133139
Self {
134140
bpb: Bpb::parse(&mut disk),
@@ -137,17 +143,48 @@ impl<D: Read> FileSystem<D> {
137143
}
138144

139145
pub fn lookup_file(&mut self, path: &str) -> Option<File> {
146+
let root = self.read_root_dir();
147+
for entry in root {
148+
write!(screen::Writer, "entry: ").unwrap();
149+
match entry {
150+
Ok(RawDirectoryEntry::Normal(entry)) => {
151+
writeln!(screen::Writer, "{}", entry.short_filename_main).unwrap();
152+
}
153+
Ok(RawDirectoryEntry::LongName(entry)) => {
154+
for c in entry.name() {
155+
match c {
156+
Ok(c) => write!(screen::Writer, "{c}").unwrap(),
157+
Err(_) => write!(screen::Writer, "X").unwrap(),
158+
}
159+
}
160+
writeln!(screen::Writer).unwrap();
161+
}
162+
Err(()) => writeln!(screen::Writer, "<failed to read>").unwrap(),
163+
}
164+
}
140165
todo!();
141166
}
142167

143-
fn read_root_dir(&mut self) {
168+
fn read_root_dir<'a>(&'a mut self) -> impl Iterator<Item = Result<RawDirectoryEntry, ()>> + 'a {
144169
match self.bpb.fat_type() {
145170
FatType::Fat32 => {
146171
self.bpb.root_cluster;
172+
unimplemented!();
147173
}
148174
FatType::Fat12 | FatType::Fat16 => {
149-
self.bpb.root_directory_offset();
150-
self.bpb.root_directory_size();
175+
let root_directory_size = self.bpb.root_directory_size();
176+
let buffer = unsafe { &mut BUFFER[..] };
177+
assert!(root_directory_size <= buffer.len());
178+
let raw = &mut buffer[..root_directory_size];
179+
180+
self.disk
181+
.seek(SeekFrom::Start(self.bpb.root_directory_offset()));
182+
self.disk.read_exact(raw);
183+
184+
raw.chunks(DIRECTORY_ENTRY_BYTES)
185+
.take_while(|raw_entry| raw_entry[0] != END_OF_DIRECTORY_PREFIX)
186+
.filter(|raw_entry| raw_entry[0] != UNUSED_ENTRY_PREFIX)
187+
.map(RawDirectoryEntry::parse)
151188
}
152189
}
153190
}
@@ -159,3 +196,95 @@ enum FatType {
159196
Fat16,
160197
Fat32,
161198
}
199+
200+
#[derive(Debug)]
201+
struct RawDirectoryEntryNormal<'a> {
202+
short_filename_main: &'a str,
203+
short_filename_extension: &'a str,
204+
attributes: u8,
205+
first_cluster: u32,
206+
file_size: u32,
207+
}
208+
209+
#[derive(Debug)]
210+
struct RawDirectoryEntryLongName<'a> {
211+
order: u8,
212+
name_1: &'a [u8],
213+
name_2: &'a [u8],
214+
name_3: &'a [u8],
215+
attributes: u8,
216+
checksum: u8,
217+
}
218+
219+
impl<'a> RawDirectoryEntryLongName<'a> {
220+
pub fn name(&self) -> impl Iterator<Item = Result<char, DecodeUtf16Error>> + 'a {
221+
let iter = self
222+
.name_1
223+
.chunks(2)
224+
.chain(self.name_2.chunks(2))
225+
.chain(self.name_3.chunks(2))
226+
.map(|c| u16::from_le_bytes(c.try_into().unwrap()))
227+
.take_while(|&c| c != 0);
228+
char::decode_utf16(iter)
229+
}
230+
}
231+
232+
#[derive(Debug)]
233+
enum RawDirectoryEntry<'a> {
234+
Normal(RawDirectoryEntryNormal<'a>),
235+
LongName(RawDirectoryEntryLongName<'a>),
236+
}
237+
238+
impl<'a> RawDirectoryEntry<'a> {
239+
fn parse(raw: &'a [u8]) -> Result<Self, ()> {
240+
let attributes = raw[11];
241+
if attributes == directory_attributes::LONG_NAME {
242+
let order = raw[0];
243+
let name_1 = &raw[1..11];
244+
let checksum = raw[13];
245+
let name_2 = &raw[14..26];
246+
let name_3 = &raw[28..32];
247+
248+
Ok(Self::LongName(RawDirectoryEntryLongName {
249+
order,
250+
name_1,
251+
name_2,
252+
name_3,
253+
attributes,
254+
checksum,
255+
}))
256+
} else {
257+
fn slice_to_string(slice: &[u8]) -> Result<&str, ()> {
258+
const SKIP_SPACE: u8 = 0x20;
259+
let mut iter = slice.into_iter().copied();
260+
let start_idx = iter.position(|c| c != SKIP_SPACE).ok_or(())?;
261+
let end_idx = start_idx + iter.position(|c| c == SKIP_SPACE).unwrap_or(slice.len());
262+
263+
core::str::from_utf8(&slice[start_idx..end_idx]).map_err(|_| ())
264+
}
265+
let short_filename_main = slice_to_string(&raw[0..8])?;
266+
let short_filename_extension = slice_to_string(&raw[8..11])?;
267+
let first_cluster_hi = u16::from_le_bytes(raw[20..22].try_into().unwrap());
268+
let first_cluster_lo = u16::from_le_bytes(raw[26..28].try_into().unwrap());
269+
let first_cluster = ((first_cluster_hi as u32) << 16) | (first_cluster_lo as u32);
270+
let file_size = u32::from_le_bytes(raw[28..32].try_into().unwrap());
271+
Ok(Self::Normal(RawDirectoryEntryNormal {
272+
short_filename_main,
273+
short_filename_extension,
274+
attributes,
275+
first_cluster,
276+
file_size,
277+
}))
278+
}
279+
}
280+
}
281+
282+
mod directory_attributes {
283+
pub const READ_ONLY: u8 = 0x01;
284+
pub const HIDDEN: u8 = 0x02;
285+
pub const SYSTEM: u8 = 0x04;
286+
pub const VOLUME_ID: u8 = 0x08;
287+
pub const DIRECTORY: u8 = 0x10;
288+
289+
pub const LONG_NAME: u8 = READ_ONLY | HIDDEN | SYSTEM | VOLUME_ID;
290+
}

0 commit comments

Comments
 (0)