working squashfs driver, minus directory traversing, compression, and a bunch of other stuff ;~;

This commit is contained in:
Zoe
2023-11-28 23:50:57 -06:00
parent c58b89299d
commit 69ed6e56df
5 changed files with 339 additions and 107 deletions

View File

@@ -46,6 +46,7 @@ copy-initramfs-files:
# Stub for now ;)
touch ${INITRAMFS_PATH}/example.txt
echo "Hello World from Initramfs" > ${INITRAMFS_PATH}/example.txt
echo "Second file for testing" > ${INITRAMFS_PATH}/example2.txt
compile-initramfs: copy-initramfs-files
# Make squashfs without compression temporaily so I can get it working before I have to write a gzip driver

View File

@@ -9,6 +9,7 @@ CappuccinOS is a small x86-64 operating system written from scratch in rust. Thi
- [X] Hardware interrupts
- [X] PS/2 Keyboard support
- [X] ANSI color codes in console
- [X] Heap allocation
- [ ] Externalized kernel modules
- [ ] Initramfs
- [ ] SMP
@@ -36,7 +37,6 @@ CappuccinOS is a small x86-64 operating system written from scratch in rust. Thi
- [ ] User authentication
- [ ] Power management
- [ ] Paging
- [ ] Heap allocation
- [ ] Hardware abstraction layer
- [ ] RTC Clock
@@ -105,6 +105,7 @@ Inspiration was mainly from [JDH's Tetris OS](https://www.youtube.com/watch?v=Fa
Some Resources I used over the creation of CappuccinOS:
- [OSDev wiki](https://wiki.osdev.org)
- Wikipedia on various random things
- [Squashfs Binary Format](https://dr-emann.github.io/squashfs/squashfs.html)
And mostly for examples of how people did stuff I used these (projects made by people who actually have a clue what they're doing).
- [MOROS](https://github.com/vinc/moros)

View File

@@ -1,5 +1,10 @@
pub mod compressors;
use core::{
fmt::{self, write},
ops::{Index, Range},
};
use alloc::vec::Vec;
use limine::ModuleRequest;
@@ -64,17 +69,18 @@ pub fn init() {
squashfs.test();
}
#[repr(C)]
#[derive(Debug)]
struct Squashfs<'a> {
ptr: *mut u8,
superblock: SquashfsSuperblock,
data_table: &'a [u8],
inode_table: &'a [u8],
directory_table: &'a [u8],
fragment_table: Option<&'a [u8]>,
export_table: Option<&'a [u8]>,
id_table: &'a [u8],
xattr_table: Option<&'a [u8]>,
inode_table: MetadataArray<'a>,
directory_table: MetadataArray<'a>,
fragment_table: Option<MetadataArray<'a>>,
export_table: Option<MetadataArray<'a>>,
id_table: MetadataArray<'a>,
xattr_table: Option<MetadataArray<'a>>,
}
impl Squashfs<'_> {
@@ -91,35 +97,41 @@ impl Squashfs<'_> {
let data_table = &squashfs_data
[core::mem::size_of::<SquashfsSuperblock>()..superblock.inode_table as usize];
let inode_table =
&squashfs_data[superblock.inode_table as usize..superblock.dir_table as usize];
let inode_table = MetadataArray::from(
&squashfs_data[superblock.inode_table as usize..superblock.dir_table as usize],
);
let directory_table =
&squashfs_data[superblock.dir_table as usize..superblock.frag_table as usize];
let directory_table = MetadataArray::from(
&squashfs_data[superblock.dir_table as usize..superblock.frag_table as usize],
);
let mut fragment_table: Option<&[u8]> = None;
let mut fragment_table: Option<MetadataArray> = None;
if superblock.frag_table != u64::MAX {
fragment_table = Some(
fragment_table = Some(MetadataArray::from(
&squashfs_data[superblock.frag_table as usize..superblock.export_table as usize],
);
));
}
let mut export_table: Option<&[u8]> = None;
let mut export_table: Option<MetadataArray> = None;
if superblock.export_table != u64::MAX {
export_table = Some(
export_table = Some(MetadataArray::from(
&squashfs_data[superblock.export_table as usize..superblock.id_table as usize],
);
));
}
let mut id_table: &[u8] = &squashfs_data[superblock.id_table as usize..];
let mut xattr_table: Option<&[u8]> = None;
let mut id_table: MetadataArray =
MetadataArray::from(&squashfs_data[superblock.id_table as usize..]);
let mut xattr_table: Option<MetadataArray> = None;
if superblock.xattr_table != u64::MAX {
id_table =
&squashfs_data[superblock.id_table as usize..superblock.xattr_table as usize];
xattr_table = Some(&squashfs_data[superblock.xattr_table as usize..]);
id_table = MetadataArray::from(
&squashfs_data[superblock.id_table as usize..superblock.xattr_table as usize],
);
xattr_table = Some(MetadataArray::from(
&squashfs_data[superblock.xattr_table as usize..],
));
}
return Ok(Squashfs {
@@ -138,7 +150,7 @@ impl Squashfs<'_> {
// big function that does stuff hard coded-ly before I rip it all out
pub fn test(&self) {
// the bottom 15 bits, I think the last bit indicates whether the data is uncompressed
let inode_table_header = u16::from_le_bytes(self.inode_table[0..2].try_into().unwrap());
let inode_table_header = u16::from_le_bytes(*self.inode_table.get_header());
let inode_is_compressed = inode_table_header & 0x80 != 0;
let inode_table_size = inode_table_header & 0x7FFF;
@@ -146,98 +158,119 @@ impl Squashfs<'_> {
panic!("Inode block is not less than 8KiB!");
}
let mut buffer: Vec<u8> = Vec::with_capacity(8192);
let mut inode_table_buffer: Vec<u8> = Vec::with_capacity(8192);
if inode_is_compressed {
todo!("uncompress zlib data")
} else {
unsafe {
core::ptr::copy_nonoverlapping(
self.inode_table.as_ptr().add(2),
buffer.as_mut_ptr(),
self.inode_table.as_ptr(),
inode_table_buffer.as_mut_ptr(),
inode_table_size as usize,
);
buffer.set_len(inode_table_size as usize);
inode_table_buffer.set_len(inode_table_size as usize);
}
}
let root_inode_block = self.superblock.root_inode_block as usize;
let root_inode_offset = self.superblock.root_inode_offset as usize;
let root_inode_header = self.read_inode(root_inode_offset as u32);
let root_inode_header = InodeHeader {
file_type: u16::from_le_bytes(
buffer[root_inode_offset..root_inode_offset + 2]
.try_into()
.unwrap(),
)
.into(),
_reserved: [
u16::from_le_bytes(
buffer[root_inode_offset + 2..root_inode_offset + 4]
.try_into()
.unwrap(),
),
u16::from_le_bytes(
buffer[root_inode_offset + 4..root_inode_offset + 6]
.try_into()
.unwrap(),
),
u16::from_le_bytes(
buffer[root_inode_offset + 6..root_inode_offset + 8]
.try_into()
.unwrap(),
),
],
mtime: u32::from_le_bytes(
buffer[root_inode_offset + 8..root_inode_offset + 12]
.try_into()
.unwrap(),
),
inode_num: u32::from_le_bytes(
buffer[root_inode_offset + 12..root_inode_offset + 16]
.try_into()
.unwrap(),
),
};
let root_inode_header = self
.read_inode(root_inode_offset as u32)
.expect("Failed to read root directory header!");
crate::println!("{:X?}", root_inode_header);
let root_inode = DirectoryInode {
block_index: u32::from_le_bytes(
buffer[root_inode_offset + 16..root_inode_offset + 20]
.try_into()
.unwrap(),
),
link_count: u32::from_le_bytes(
buffer[root_inode_offset + 20..root_inode_offset + 24]
.try_into()
.unwrap(),
),
file_size: u16::from_le_bytes(
buffer[root_inode_offset + 24..root_inode_offset + 26]
.try_into()
.unwrap(),
),
block_offset: u16::from_le_bytes(
buffer[root_inode_offset + 26..root_inode_offset + 28]
.try_into()
.unwrap(),
),
parent_inode: u32::from_le_bytes(
buffer[root_inode_offset + 28..root_inode_offset + 32]
.try_into()
.unwrap(),
),
};
let root_inode = DirectoryInode::from_bytes(&inode_table_buffer[root_inode_offset + 16..]);
crate::println!("{:?}", root_inode);
crate::println!("root_inode: {:X?}", root_inode);
let directory_table_header = u16::from_le_bytes(*self.directory_table.get_header());
let directory_is_compressed = directory_table_header & 0x80 != 0;
let directory_table_size = directory_table_header & 0x7FFF;
if directory_table_size >= 8192 {
panic!("Directory block is not less than 8KiB!");
}
let mut directory_table_buffer: Vec<u8> = Vec::with_capacity(8192);
if directory_is_compressed {
todo!("uncompress zlib data")
} else {
unsafe {
core::ptr::copy_nonoverlapping(
self.directory_table.as_ptr(),
directory_table_buffer.as_mut_ptr(),
directory_table_size as usize,
);
directory_table_buffer.set_len(directory_table_size as usize);
}
}
let root_directory = DirectoryTableHeader::from_bytes(
&directory_table_buffer[root_inode.block_offset as usize..],
);
crate::println!("{:?}", root_directory);
// TODO: cheap hack, fix it when I have more hours of sleep.
let mut offset =
root_inode.block_offset as usize + core::mem::size_of::<DirectoryTableHeader>();
for i in 0..root_directory.count as usize {
let root_dir_entry = DirectoryTableEntry::from_bytes(&directory_table_buffer[offset..]);
offset += 8 + root_dir_entry.name.len();
crate::println!("{:?}", root_dir_entry);
let file_inode_header = self
.read_inode(root_dir_entry.offset as u32)
.expect("Directory has files with missing inodes");
crate::println!("{:?}", file_inode_header);
let file_inode =
FileInode::from_bytes(&inode_table_buffer[root_dir_entry.offset as usize + 16..]);
crate::println!("{:?}", file_inode);
// TODO: get a real ceil function instead of this horse shit
// TODO: handle tail end packing (somehow?)
let block_count =
((file_inode.file_size as f32 / self.superblock.block_size as f32) + 1.0) as usize;
// TODO: is this really how you're supposed to do this?
let mut block_data: Vec<u8> = Vec::with_capacity(8192 * block_count);
unsafe {
core::ptr::copy_nonoverlapping(
self.data_table
.as_ptr()
.add(file_inode.block_offset as usize),
block_data.as_mut_ptr(),
file_inode.file_size as usize,
);
block_data.set_len(file_inode.file_size as usize);
}
crate::println!("{:?}", block_count);
crate::println!(
"{:X?} \033[93m{:?}\033[0m",
block_data,
core::str::from_utf8(&block_data)
);
}
}
// TODO: decompress stuff
fn read_inode(&self, inode_num: u32) -> Option<InodeHeader> {
let inode_offset = inode_num as usize + 2;
let inode_offset = inode_num as usize;
if inode_offset + core::mem::size_of::<InodeHeader>() > self.inode_table.len() {
return None;
@@ -252,6 +285,7 @@ impl Squashfs<'_> {
}
}
#[repr(C, packed)]
#[derive(Debug)]
struct InodeHeader {
file_type: InodeFileType,
@@ -261,36 +295,123 @@ struct InodeHeader {
}
impl InodeHeader {
fn from_bytes(bytes: &[u8]) -> Option<InodeHeader> {
fn from_bytes(bytes: &[u8]) -> Option<Self> {
let file_type = u16::from_le_bytes(bytes[0..2].try_into().unwrap()).into();
let mtime = u32::from_le_bytes(bytes[8..12].try_into().unwrap());
let inode_num = u32::from_le_bytes(bytes[12..16].try_into().unwrap());
Some(InodeHeader {
return Some(Self {
file_type,
_reserved: [0; 3],
mtime,
inode_num,
})
});
}
}
#[repr(C, packed)]
#[derive(Debug)]
struct DirectoryInode {
block_index: u32,
link_count: u32,
file_size: u16,
block_offset: u16,
parent_inode: u32,
block_index: u32, // 4
link_count: u32, // 8
file_size: u16, // 10
block_offset: u16, // 12
parent_inode: u32, // 16
}
impl DirectoryInode {
fn from_bytes(bytes: &[u8]) -> Self {
let block_index = u32::from_le_bytes(bytes[0..4].try_into().unwrap());
let link_count = u32::from_le_bytes(bytes[4..8].try_into().unwrap());
let file_size = u16::from_le_bytes(bytes[8..10].try_into().unwrap());
let block_offset = u16::from_le_bytes(bytes[10..12].try_into().unwrap());
let parent_inode = u32::from_le_bytes(bytes[12..16].try_into().unwrap());
return Self {
block_index,
link_count,
file_size,
block_offset,
parent_inode,
};
}
}
#[repr(C, packed)]
#[derive(Debug)]
struct FileInode {
block_start: u32,
frag_idx: u32,
block_offset: u32,
file_size: u32,
block_size: [u32; 0],
block_start: u32, // 4
frag_idx: u32, // 8
block_offset: u32, // 12
file_size: u32, // 16
}
impl FileInode {
fn from_bytes(bytes: &[u8]) -> Self {
let block_start = u32::from_le_bytes(bytes[0..4].try_into().unwrap());
let frag_idx = u32::from_le_bytes(bytes[4..8].try_into().unwrap());
let block_offset = u32::from_le_bytes(bytes[8..12].try_into().unwrap());
let file_size = u32::from_le_bytes(bytes[12..16].try_into().unwrap());
return Self {
block_start,
frag_idx,
block_offset,
file_size,
};
}
}
#[repr(C, packed)]
#[derive(Debug)]
struct DirectoryTableHeader {
count: u32,
start: u32,
inode_num: u32,
}
impl DirectoryTableHeader {
fn from_bytes(bytes: &[u8]) -> Self {
// count is off by 2 entry
let count = u32::from_le_bytes(bytes[0..4].try_into().unwrap()) + 1;
let start = u32::from_le_bytes(bytes[4..8].try_into().unwrap());
let inode_num = u32::from_le_bytes(bytes[8..12].try_into().unwrap());
return Self {
count,
start,
inode_num,
};
}
}
#[repr(C, packed)]
#[derive(Debug)]
struct DirectoryTableEntry<'a> {
offset: u16,
inode_offset: i16,
inode_type: InodeFileType,
name_size: u16,
name: &'a str, // the file name length is name_size + 1 bytes
}
impl<'a> DirectoryTableEntry<'a> {
fn from_bytes(bytes: &'a [u8]) -> Self {
let offset = u16::from_le_bytes(bytes[0..2].try_into().unwrap());
let inode_offset = i16::from_le_bytes(bytes[2..4].try_into().unwrap());
let inode_type = u16::from_le_bytes(bytes[4..6].try_into().unwrap()).into();
let name_size = u16::from_le_bytes(bytes[6..8].try_into().unwrap());
let name = core::str::from_utf8(&bytes[8..((name_size as usize) + 1) + 8])
.expect("Failed to make DirectoryHeader name");
return Self {
offset,
inode_offset,
inode_type,
name_size,
name,
};
}
}
#[repr(u16)]
@@ -389,7 +510,7 @@ impl Into<SquashfsCompressionType> for u16 {
}
}
#[repr(C)]
#[repr(C, packed)]
#[derive(Clone, Copy, Debug)]
struct SquashfsSuperblock {
magic: u32, // 0x73717368
@@ -506,3 +627,64 @@ impl SquashfsSuperblock {
};
}
}
// Helper functions to deal with metadata arrays
struct MetadataArray<'a> {
header: &'a [u8; 2],
data: &'a [u8],
}
impl<'a> MetadataArray<'a> {
// Function to access the header
pub fn get_header(&self) -> &'a [u8; 2] {
&self.header
}
pub fn as_ptr(&self) -> *const u8 {
return self.data.as_ptr();
}
pub fn len(&self) -> usize {
return self.data.len();
}
}
impl<'a> From<&'a [u8]> for MetadataArray<'a> {
fn from(value: &'a [u8]) -> Self {
let (header, data) = value.split_at(2);
MetadataArray {
header: header.try_into().unwrap(),
data,
}
}
}
impl<'a> Index<usize> for MetadataArray<'a> {
type Output = u8;
fn index(&self, index: usize) -> &Self::Output {
if index > self.data.len() {
panic!("Index out of bounds");
}
return &self.data[index];
}
}
impl<'a> Index<Range<usize>> for MetadataArray<'a> {
type Output = [u8];
fn index(&self, index: Range<usize>) -> &Self::Output {
if index.end > self.data.len() || index.start > self.data.len() {
panic!("Index out of bounds");
}
return &self.data[index];
}
}
impl<'a> fmt::Debug for MetadataArray<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self.data)
}
}

View File

@@ -113,6 +113,11 @@ fn parse_kernel_cmdline() -> KernelFeatures {
#[panic_handler]
fn panic(info: &core::panic::PanicInfo) -> ! {
crate::log_error!("{}", info);
let rbp: u64;
unsafe {
core::arch::asm!("mov {0:r}, rbp", out(reg) rbp);
};
crate::arch::stack_trace::print_stack_trace(6, rbp);
hcf();
}

43
test.log Normal file
View File

@@ -0,0 +1,43 @@
[ * ] Serial Driver successfully initialized
[ ? ] Initialized heap with 4MiB of memory at 0x4E8000
[ ? ] Initialized framebuffer mirroring with 3MiB at 0x100000
====== MEMORY MAP ======
[ 0x0000000000001000..0x0000000000052000 ] Type: BootloaderReclaimable Size: 324KiB
[ 0x0000000000053000..0x000000000009F000 ] Type: Usable Size: 304KiB
[ 0x000000000009FC00..0x00000000000A0000 ] Type: Reserved Size: 1KiB
[ 0x00000000000F0000..0x0000000000100000 ] Type: Reserved Size: 64KiB
[ 0x0000000000100000..0x0000000007AC3000 ] Type: Usable Size: 121MiB
[ 0x0000000007AC3000..0x0000000007AC4000 ] Type: KernelAndModules Size: 4KiB
[ 0x0000000007AC4000..0x0000000007AC5000 ] Type: BootloaderReclaimable Size: 4KiB
[ 0x0000000007AC5000..0x0000000007AC8000 ] Type: Usable Size: 12KiB
[ 0x0000000007AC8000..0x0000000007AD0000 ] Type: BootloaderReclaimable Size: 32KiB
[ 0x0000000007AD0000..0x0000000007AFE000 ] Type: KernelAndModules Size: 184KiB
[ 0x0000000007AFE000..0x0000000007B3C000 ] Type: BootloaderReclaimable Size: 248KiB
[ 0x0000000007B3C000..0x0000000007F26000 ] Type: Usable Size: 3MiB
[ 0x0000000007F26000..0x0000000007FE0000 ] Type: BootloaderReclaimable Size: 744KiB
[ 0x0000000007FE0000..0x0000000008000000 ] Type: Reserved Size: 128KiB
[ 0x00000000FD000000..0x00000000FD3E8000 ] Type: Framebuffer Size: 3MiB
[ 0x00000000FFFC0000..0x0000000100000000 ] Type: Reserved Size: 256KiB
[ 0x000000FD00000000..0x0000010000000000 ] Type: Reserved Size: 12GiB
Initramfs is located at: 0xffff800007ac3000..0xffff800007ac4000
Parsing initramfs fs at 0xffff800007ac3000
Squashfs { ptr: 0xffff800007ac3060, superblock: SquashfsSuperblock { magic: 73717368, inode_count: 2, mod_time: 6566B6A1, block_size: 20000, frag_count: 1, compressor: GZIP, block_log: 11, flags: 1CB, id_count: 1, ver_major: 4, ver_minor: 0, root_inode_offset: 20, root_inode_block: 0, _reserved: 0, bytes_used: 120, id_table: 118, xattr_table: FFFFFFFFFFFFFFFF, inode_table: 7B, dir_table: BD, frag_table: F0, export_table: 10A }, data_table: [48, 65, 6C, 6C, 6F, 20, 57, 6F, 72, 6C, 64, 20, 66, 72, 6F, 6D, 20, 49, 6E, 69, 74, 72, 61, 6D, 66, 73, A], inode_table: [40, 80, 2, 0, A4, 1, 0, 0, 0, 0, A1, B6, 66, 65, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1B, 0, 0, 0, 1, 0, ED, 1, 0, 0, 0, 0, A1, B6, 66, 65, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 22, 0, 0, 0, 3, 0, 0, 0], directory_table: [1F, 80, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, A, 0, 65, 78, 61, 6D, 70, 6C, 65, 2E, 74, 78, 74, 10, 80, 60, 0, 0, 0, 0, 0, 0, 0, 1B, 0, 0, 1, 0, 0, 0, 0], fragment_table: Some([DE, 0, 0, 0, 0, 0, 0, 0, 10, 80, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0]), export_table: Some([F8, 0, 0, 0, 0, 0, 0, 0, 4, 80, E8, 3, 0, 0]), id_table: [12, 1, 0, 0, 0, 0, 0, 0], xattr_table: None }
SquashfsFeatures { uncompressed_inodes: true, uncompressed_data_blocks: true, _reserved: false, uncompressed_fragments: true, unused_fragments: false, fragments_always_present: false, deduplicated_data: true, nfs_table_present: true, uncompressed_xattrs: true, no_xattrs: false, compressor_options_present: false, uncompressed_id_table: false }
[1, 0, ED, 1, 0, 0, 0, 0, A1, B6, 66, 65, 2, 0, 0, 0]
InodeHeader { file_type: BasicDirectory, _reserved: [0, 0, 0], mtime: 6566B6A1, inode_num: 2 }
root_inode: DirectoryInode { block_index: 0, link_count: 2, file_size: 22, block_offset: 0, parent_inode: 3 }
====== PCI DEVICES ======
Entry 0: Bus: 0 Device: 0 Function: 0 VendorID: 0x8086 DeviceID: 0x1237 ClassCode: 0x06 SubclassCode: 0x00 ProgIF: 0x00
Entry 1: Bus: 0 Device: 1 Function: 0 VendorID: 0x8086 DeviceID: 0x7000 ClassCode: 0x06 SubclassCode: 0x01 ProgIF: 0x00
Entry 2: Bus: 0 Device: 1 Function: 1 VendorID: 0x8086 DeviceID: 0x7010 ClassCode: 0x01 SubclassCode: 0x01 ProgIF: 0x80
Entry 3: Bus: 0 Device: 1 Function: 3 VendorID: 0x8086 DeviceID: 0x7113 ClassCode: 0x06 SubclassCode: 0x80 ProgIF: 0x00
Entry 4: Bus: 0 Device: 2 Function: 0 VendorID: 0x1234 DeviceID: 0x1111 ClassCode: 0x03 SubclassCode: 0x00 ProgIF: 0x00
Entry 5: Bus: 0 Device: 3 Function: 0 VendorID: 0x8086 DeviceID: 0x100E ClassCode: 0x02 SubclassCode: 0x00 ProgIF: 0x00
[ ? ] ATA: Detected 1 drive
[ ? ] ATA: Drive 0 has 131072 sectors (64 MB)
11, 44, 128
[ ? ] WARNING: FAT is not being stored in memory, this feature is experimental and file reads are expected to be slower.
Found Fat32 FS
[GPTPartitionEntry { partition_type_guid: [40, 115, 42, 193, 31, 248, 210, 17, 186, 75, 0, 160, 201, 62, 201, 59], start_sector: 2048, end_sector: 34815 }, GPTPartitionEntry { partition_type_guid: [175, 61, 198, 15, 131, 132, 114, 71, 142, 121, 61, 105, 216, 71, 125, 228], start_sector: 34816, end_sector: 131038 }]
Total memory: 125MiB
>