basics of the filesystem, funcs are todo
This commit is contained in:
parent
7ced33cfdc
commit
d02674a179
6 changed files with 378 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
/Cargo.lock
|
10
Cargo.toml
Normal file
10
Cargo.toml
Normal 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
31
src/bitmap.rs
Normal 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
182
src/btree.rs
Normal 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
14
src/lib.rs
Normal 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
139
src/structs.rs
Normal 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,
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue