basics of the filesystem, funcs are todo

This commit is contained in:
husky 2023-07-25 07:03:06 -07:00
parent 7ced33cfdc
commit d02674a179
No known key found for this signature in database
GPG key ID: 6B3D8CB511646891
6 changed files with 378 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
/target
/Cargo.lock

10
Cargo.toml Normal file
View file

@ -0,0 +1,10 @@
[package]
name = "vapfs_ufs"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
vapfs = { path = "../vapfs" }
embedded-crc32c = "0.1.0"

31
src/bitmap.rs Normal file
View file

@ -0,0 +1,31 @@
pub const SET: bool = true;
pub const UNSET: bool = false;
/// returns the bit index of the first bit that is, if value is true, set, or if value is false, unset
pub fn find_first_bit_equal_to(bitmap: &[u8], value: bool) -> Option<usize> {
for (byte_index, byte) in bitmap.iter().enumerate() {
for bit_index in 0..8 {
let bit = (byte >> bit_index) & 1;
if bit == (value as u8) {
return Some(byte_index * 8 + bit_index);
}
}
}
None
}
/// sets the bit at the given index to the given value
pub fn set_bit(bitmap: &mut [u8], index: usize, value: bool) {
let byte_index = index / 8;
let bit_index = index % 8;
let bit = (value as u8) << bit_index;
bitmap[byte_index] |= bit;
}
/// returns the bit at the given index
pub fn get_bit(bitmap: &[u8], index: usize) -> bool {
let byte_index = index / 8;
let bit_index = index % 8;
let bit = (bitmap[byte_index] >> bit_index) & 1;
bit == 1
}

182
src/btree.rs Normal file
View file

@ -0,0 +1,182 @@
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use vapfs::{BlockDevice, Index};
/// # BTd
/// min-degree of all BTrees
pub const BTd: u32 = 3;
/// # BTKeyType
/// key used to index a BTree
pub type BTKeyType = u32;
/// # BTKey
/// whole value of a key in a BTree
#[repr(C)]
#[derive(Copy, Clone)]
pub struct BTKey {
pub key: BTKeyType,
/// length of this entry in bytes
pub length: u32,
/// amount of files represented by this key, used in the case of hash collisions
pub count: u32,
/// if count is 1, this is the inode number of the file represented by this key
/// if count is >1, offset from first BTNode in bytes to an HCList struct
pub value: Index,
/// length of filename
/// zero if count is >1
pub filename_length: u32,
// rest of the struct is the filename
}
/// # HCList
/// list of files represented by a single key in a BTree, used in the case of hash collisions
#[repr(C)]
#[derive(Copy, Clone)]
pub struct HCList {
/// offset from first HCList in bytes to the next HCList
pub next: Index,
/// inode number of the file represented by this HCList
pub inode: Index,
/// length of filename
pub filename_length: u32,
// rest of the struct is the filename
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct BTNode {
/// offset from first BTNode in bytes, each entry is a DirectoryKey
pub key_array: Index,
pub key_count: u32,
/// offset from first BTNode in bytes, each entry is another BTNode
pub child_array: Index,
pub child_count: u32,
}
impl BTKey {
/// in-place conversion from the storage representation (big endian) to the native representation
pub fn convert_big_endian_to_native(&mut self) {
#[cfg(target_endian = "little")]
{
self.key = u32::from_be(self.key);
self.length = u32::from_be(self.length);
self.count = u32::from_be(self.count);
self.value = u64::from_be(self.value);
self.filename_length = u32::from_be(self.filename_length);
}
}
/// in-place conversion from the native representation to the storage representation (big endian)
pub fn convert_native_to_big_endian(&mut self) {
#[cfg(target_endian = "little")]
{
self.key = u32::to_be(self.key);
self.length = u32::to_be(self.length);
self.count = u32::to_be(self.count);
self.value = u64::to_be(self.value);
self.filename_length = u32::to_be(self.filename_length);
}
}
/// offset is from 0x0 in the block device to the beginning of the struct
pub fn read(offset: Index, bd: &mut dyn BlockDevice) -> Option<(Self, String)> {
let mut buf: [u8; core::mem::size_of::<Self>()] = [0; core::mem::size_of::<Self>()];
bd.seek(offset);
let read_count = bd.read_blocks(&mut buf);
if read_count < core::mem::size_of::<Self>() {
return None;
}
let mut key = unsafe { core::ptr::read(buf.as_ptr() as *const Self) };
key.convert_big_endian_to_native();
let filename = key.read_filename(offset, bd);
Some((key, filename))
}
/// offset is from 0x0 in the block device to the beginning of the struct
pub fn read_filename(&self, offset: Index, bd: &mut dyn BlockDevice) -> String {
let mut filename_buf: Vec<u8> = Vec::new();
filename_buf.resize(self.filename_length as usize, 0);
bd.seek(offset);
bd.read_blocks(&mut filename_buf);
String::from_utf8_lossy(unsafe { core::slice::from_raw_parts(filename_buf.as_ptr(), filename_buf.len()) }).to_string()
}
}
impl BTNode {
pub const fn new(key_array: Index, key_count: u32, child_array: Index, child_count: u32) -> Self {
Self {
key_array,
key_count,
child_array,
child_count,
}
}
/// in-place conversion from the storage representation (big endian) to the native representation
pub fn convert_big_endian_to_native(&mut self) {
#[cfg(target_endian = "little")]
{
self.key_array = u64::from_be(self.key_array);
self.key_count = u32::from_be(self.key_count);
self.child_array = u64::from_be(self.child_array);
self.child_count = u32::from_be(self.child_count);
}
}
/// in-place conversion from the native representation to the storage representation (big endian)
pub fn convert_native_to_big_endian(&mut self) {
#[cfg(target_endian = "little")]
{
self.key_array = u64::to_be(self.key_array);
self.key_count = u32::to_be(self.key_count);
self.child_array = u64::to_be(self.child_array);
self.child_count = u32::to_be(self.child_count);
}
}
/// offset is from root in bytes
pub fn read(root: Index, offset: Index, bd: &mut dyn BlockDevice) -> Option<Self> {
let mut buf: [u8; core::mem::size_of::<Self>()] = [0; core::mem::size_of::<Self>()];
bd.seek(root + offset);
let read_count = bd.read_blocks(&mut buf);
if read_count < core::mem::size_of::<Self>() {
return None;
}
let mut node = unsafe { core::ptr::read(buf.as_ptr() as *const Self) };
node.convert_big_endian_to_native();
Some(node)
}
pub fn search(self, root: Index, key_to_find: BTKeyType, bd: &mut dyn BlockDevice) -> Option<(BTKey, String)> {
let mut node = self;
loop {
let mut i = 0;
let mut last_key: Option<BTKey> = None;
while i < node.key_count {
let key_offset = if i == 0 {
node.key_array
} else {
last_key.as_ref().unwrap().length as Index + last_key.as_ref().unwrap().value
};
let key = BTKey::read(key_offset, bd)?;
if key.0.key == key_to_find {
return Some(key);
} else if key.0.key > key_to_find {
break;
}
i += 1;
last_key = Some(key.0);
}
if node.child_count == 0 {
return None;
}
let child_offset = if i == 0 {
node.child_array
} else {
last_key.unwrap().value
};
node = BTNode::read(root, child_offset, bd)?;
}
}
}

14
src/lib.rs Normal file
View file

@ -0,0 +1,14 @@
#![no_std]
extern crate alloc;
use vapfs::BlockDevice;
use crate::structs::Superblock;
pub mod btree;
pub mod structs;
pub mod bitmap;
pub fn get_superblock(bd: &mut dyn BlockDevice) -> Option<Superblock> {
todo!()
}

139
src/structs.rs Normal file
View file

@ -0,0 +1,139 @@
use vapfs::{Index, Timestamp};
/// # Magic
/// Constant value that identifies the Superblock as a valid VapUFS filesystem
pub const MAGIC: u64 = 0x766170554653;
/// # Superblock
/// The primary struct of a VapUFS filesystem, contains metadata about the filesystem.
/// Located at byte offset 1024 of the block device.
/// All values are big-endian unless otherwise specified.
/// Directly after the Superblock are the free data blocks bitmap and the free inodes bitmap.
/// Free data blocks bitmap is data_block_count / 8 bytes long (rounded up).
/// Free inodes bitmap is inode_count / 8 bytes long (rounded up).
#[repr(C)]
pub struct Superblock {
/// magic number that identifies the Superblock as a valid VapUFS filesystem
pub magic: u64,
/// size of each block in bytes
pub block_size: u32,
/// location of first data block in blocks
pub first_data_block: Index,
/// location of first inode block in blocks
pub first_inode_block: Index,
/// location of first journal block in blocks
pub first_journal_block: Index,
/// total count of inodes, including unused inodes
pub inode_count: Index,
/// total count of data blocks, including unused blocks
pub data_block_count: Index,
/// total count of blocks dedicated to journal
pub journal_block_count: Index,
/// total count of inodes in use
pub allocated_inode_count: Index,
/// total count of data blocks in use
pub allocated_data_block_count: Index,
/// timestamp of creation
pub creation_time: Timestamp,
/// timestamp of last modification
pub last_modification_time: Timestamp,
/// reserved values for expansion
pub reserved: [u64; 8],
// 156 bytes used so far
// rest of the block is used by the free data blocks bitmap and the free inodes bitmap
}
/// # UNIXPermissions
/// UNIX permissions
#[repr(u16)]
pub enum UNIXMode {
/// Read, Other
Read = 1 << 0,
/// Write, Other
Write = 1 << 1,
/// Execute, Other
Execute = 1 << 2,
/// Read, Group
ReadGroup = 1 << 3,
/// Write, Group
WriteGroup = 1 << 4,
/// Execute, Group
ExecuteGroup = 1 << 5,
/// Read, Owner
ReadOwner = 1 << 6,
/// Write, Owner
WriteOwner = 1 << 7,
/// Execute, Owner
ExecuteOwner = 1 << 8,
/// Sticky bit
Sticky = 1 << 9,
/// Set GID
SetGID = 1 << 10,
/// Set UID
SetUID = 1 << 11,
}
/// # Filetype
/// Represents a file's type in the last 4 bits of the mode field of an inode.
#[repr(u16)]
pub enum Filetype {
/// Named FIFO pipe
FIFO = 0b0001 << 12,
/// Character device
CharDevice = 0b0010 << 12,
/// Directory
Directory = 0b0011 << 12,
/// Block device
BlockDevice = 0b0100 << 12,
/// Regular file
RegularFile = 0b0101 << 12,
/// Symbolic link
SymbolicLink = 0b0110 << 12,
/// Socket
Socket = 0b0111 << 12,
}
/// # Inode
/// Usually represents a file or directory, used to store metadata and locations of data blocks.
#[repr(C)]
pub struct Inode {
/// UNIX permissions / mode and filetype
pub mode: u16,
/// Number of links to this inode
pub link_count: u16,
/// User ID of owner
pub uid: u32,
/// Group ID of owner
pub gid: u32,
/// Size in bytes
pub size: Index,
/// Size in blocks
pub block_count: Index,
/// Timestamp of creation
pub creation_time: Timestamp,
/// Timestamp of last access
pub last_access_time: Timestamp,
/// Timestamp of last modification
pub last_modification_time: Timestamp,
/// Timestamp of last Inode modification
pub last_inode_modification_time: Timestamp,
/// Timestamp of deletion
pub deletion_time: Timestamp,
/// Flags field, see `InodeFlags`
pub flags: u32,
/// Direct-Block-Addresses, last three are indirect if `InodeFlags::INDIRECT` is set
/// Indirect blocks are Indexes to other blocks, their contents are a u64 count "N" followed by N u64 indexes
pub direct_block_addresses: [Index; 12],
/// CRC32 checksum of this inode
pub checksum: u32,
}
/// # InodeFlags
/// Flags field of an inode
#[repr(u32)]
pub enum InodeFlags {
/// File is corrupted
CORRUPT = 1 << 0,
/// File uses indirect blocks
INDIRECT = 1 << 1,
}