[VFS] Move victoria falls to rust. (Breaks voyageurs)
Move victoria falls to rust, which allows us to remove both the denali and victoria falls C++ code. This disk driver appears to work properly but has highlighted some instability in the voyageus xhci implementation which now breaks.
This commit is contained in:
parent
f918966727
commit
dc801786b1
|
@ -54,6 +54,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"denali_client",
|
||||
"mammoth",
|
||||
"yellowstone-yunq",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -1,31 +1,50 @@
|
|||
use mammoth::{cap::Capability, zion::ZError};
|
||||
|
||||
use crate::{DenaliClient, DiskBlock, ReadRequest};
|
||||
use crate::{DenaliClient, DiskBlock, ReadManyRequest, ReadRequest};
|
||||
|
||||
pub struct DiskReader {
|
||||
client: DenaliClient,
|
||||
disk_id: u64,
|
||||
lba_offset: u64,
|
||||
block_multiplier: u64,
|
||||
}
|
||||
|
||||
impl DiskReader {
|
||||
pub fn new(client: DenaliClient, disk_id: u64, lba_offset: u64) -> Self {
|
||||
pub fn new(client: DenaliClient, disk_id: u64, lba_offset: u64, block_multiplier: u64) -> Self {
|
||||
Self {
|
||||
client,
|
||||
disk_id,
|
||||
lba_offset,
|
||||
block_multiplier,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Make yunq clients callable from a non-mutable reference so this can be called from
|
||||
// shared ownership.
|
||||
pub fn read(&mut self, lba: u64, cnt: u64) -> Result<Capability, ZError> {
|
||||
let read_resp = self.client.read(&ReadRequest {
|
||||
device_id: self.disk_id,
|
||||
block: DiskBlock {
|
||||
lba: self.lba_offset + lba,
|
||||
size: cnt,
|
||||
lba: self.lba_offset + (lba * self.block_multiplier),
|
||||
size: cnt * self.block_multiplier,
|
||||
},
|
||||
})?;
|
||||
|
||||
Ok(Capability::take(read_resp.memory))
|
||||
}
|
||||
|
||||
pub fn read_many(&mut self, blocks: &[DiskBlock]) -> Result<Capability, ZError> {
|
||||
let read_resp = self.client.read_many(&ReadManyRequest {
|
||||
device_id: self.disk_id,
|
||||
blocks: blocks
|
||||
.iter()
|
||||
.map(|b| DiskBlock {
|
||||
lba: self.lba_offset + (b.lba * self.block_multiplier),
|
||||
size: b.size * self.block_multiplier,
|
||||
})
|
||||
.collect(),
|
||||
})?;
|
||||
|
||||
Ok(Capability::take(read_resp.memory))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,3 +6,4 @@ edition = "2024"
|
|||
[dependencies]
|
||||
denali_client = { path = "../../client/denali_client" }
|
||||
mammoth = { path = "../../mammoth" }
|
||||
yellowstone-yunq = { path = "../../yellowstone/" }
|
||||
|
|
|
@ -1,19 +1,284 @@
|
|||
use denali_client::DiskReader;
|
||||
use mammoth::mem::MemoryRegion;
|
||||
use core::cmp::min;
|
||||
|
||||
use crate::types::Superblock;
|
||||
use alloc::{collections::BTreeMap, string::String, vec::Vec};
|
||||
use denali_client::{DenaliClient, DiskBlock, DiskReader, ReadRequest};
|
||||
use mammoth::{cap::Capability, debug, mem::MemoryRegion, zion::ZError};
|
||||
use yellowstone_yunq::DenaliInfo;
|
||||
|
||||
use crate::types::{BlockGroupDescriptor, DirEntry, Inode, Superblock};
|
||||
|
||||
pub struct FileInfo {
|
||||
pub inode: u32,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
/// Ext2 Driver with the ability to read files and directories from the given disk.
|
||||
///
|
||||
/// Implementation based on the information available at
|
||||
/// https://www.nongnu.org/ext2-doc/ext2.html
|
||||
pub struct Ext2Driver {
|
||||
reader: DiskReader,
|
||||
superblock_region: MemoryRegion,
|
||||
bgdt_region: MemoryRegion,
|
||||
|
||||
/// Cache of the memory regions for the inode tables available indexed by
|
||||
/// the block_group number.
|
||||
inode_table_map: Vec<Option<MemoryRegion>>,
|
||||
|
||||
/// Cache of inode_num to memory capability.
|
||||
/// This is particularly important for directories so we
|
||||
/// don't iterate over the disk each time.
|
||||
inode_cache: BTreeMap<u32, Capability>,
|
||||
}
|
||||
|
||||
impl Ext2Driver {
|
||||
pub fn new(mut reader: DiskReader) -> Self {
|
||||
let super_block_mem = MemoryRegion::from_cap(reader.read(2, 2).unwrap()).unwrap();
|
||||
let super_block: &Superblock = super_block_mem.as_ref();
|
||||
let inodes = super_block.inodes_count;
|
||||
let magic = super_block.magic;
|
||||
mammoth::debug!("Superblock ({:#x}): inodes: {:#x}", magic, inodes);
|
||||
Self { reader }
|
||||
pub fn new(denali_info: DenaliInfo) -> Self {
|
||||
let mut client = DenaliClient::new(Capability::take(denali_info.denali_endpoint));
|
||||
|
||||
// Calculate the absolute offset and size of the superblock. It is located at
|
||||
// offset 1024 of the partition and is 1024 bytes long. (Mostly extra
|
||||
// reserved space).
|
||||
// Ref: https://www.nongnu.org/ext2-doc/ext2.html#def-superblock
|
||||
let abs_superblock_start = denali_info.lba_offset + 2;
|
||||
let abs_superblock_size = 2; // TODO: This assumes 512 bytes sectors.
|
||||
let superblock_region = MemoryRegion::from_cap(Capability::take(
|
||||
client
|
||||
.read(&ReadRequest {
|
||||
device_id: denali_info.device_id,
|
||||
block: DiskBlock {
|
||||
lba: abs_superblock_start,
|
||||
size: abs_superblock_size,
|
||||
},
|
||||
})
|
||||
.unwrap()
|
||||
.memory,
|
||||
))
|
||||
.unwrap();
|
||||
let superblock: &Superblock = superblock_region.as_ref();
|
||||
assert!(superblock.is_valid());
|
||||
|
||||
let mut reader = DiskReader::new(
|
||||
client,
|
||||
denali_info.device_id,
|
||||
denali_info.lba_offset,
|
||||
superblock.sectors_per_block(),
|
||||
);
|
||||
|
||||
let bgdt_region = MemoryRegion::from_cap(
|
||||
reader
|
||||
.read(superblock.bgdt_block_num(), superblock.bgdt_block_size())
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut inode_table_map = Vec::new();
|
||||
inode_table_map.resize_with(superblock.num_block_groups() as usize, || None);
|
||||
|
||||
Self {
|
||||
reader,
|
||||
superblock_region,
|
||||
bgdt_region,
|
||||
inode_table_map,
|
||||
inode_cache: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn superblock(&self) -> &Superblock {
|
||||
self.superblock_region.as_ref()
|
||||
}
|
||||
|
||||
fn bgdt(&self) -> &[BlockGroupDescriptor] {
|
||||
self.bgdt_region.slice()
|
||||
}
|
||||
|
||||
/// Updates the cached inode tables to contain the inode table for
|
||||
/// a specific group.
|
||||
fn populate_inode_table_if_none(&mut self, block_group_num: usize) {
|
||||
if let None = self.inode_table_map[block_group_num] {
|
||||
debug!(
|
||||
"Cache MISS on inode table for block_group {}",
|
||||
block_group_num
|
||||
);
|
||||
let inode_table = self.bgdt()[block_group_num].inode_table;
|
||||
self.inode_table_map[block_group_num] = Some(
|
||||
MemoryRegion::from_cap(
|
||||
self.reader
|
||||
.read(
|
||||
inode_table as u64,
|
||||
self.superblock().inode_table_block_size(),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
} else {
|
||||
debug!(
|
||||
"Cache HIT on inode table for block_group {}",
|
||||
block_group_num
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_inode(&mut self, inode_num: u32) -> Inode {
|
||||
// See the following for a description of finding an inode.
|
||||
// https://www.nongnu.org/ext2-doc/ext2.html#idm140660447281728
|
||||
let block_group_num = (inode_num - 1) / self.superblock().inodes_per_group;
|
||||
self.populate_inode_table_if_none(block_group_num as usize);
|
||||
let region = self.inode_table_map[block_group_num as usize]
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
|
||||
let local_index = (inode_num - 1) % self.superblock().inodes_per_group;
|
||||
let offset = self.superblock().inode_size() * local_index as u64;
|
||||
unsafe { region.raw_ptr_at_offset::<Inode>(offset).read().clone() }
|
||||
}
|
||||
|
||||
fn get_blocks_from_single_indirect(&mut self, block_num: u64, num_blocks: usize) -> Vec<u32> {
|
||||
assert!(num_blocks <= 256);
|
||||
let single_indr_block_mem =
|
||||
MemoryRegion::from_cap(self.reader.read(block_num, 1).unwrap()).unwrap();
|
||||
|
||||
single_indr_block_mem.slice()[..num_blocks].to_vec()
|
||||
}
|
||||
|
||||
fn get_blocks_from_double_indirect(&mut self, block_num: u64, num_blocks: usize) -> Vec<u32> {
|
||||
assert!(num_blocks > 0 && num_blocks <= (256 * 256));
|
||||
let num_dbl_indr = ((num_blocks - 1) / 256) + 1;
|
||||
|
||||
let dbl_indr_block_mem =
|
||||
MemoryRegion::from_cap(self.reader.read(block_num, 1).unwrap()).unwrap();
|
||||
|
||||
let dbl_indr_blocks: &[u32] = dbl_indr_block_mem.slice();
|
||||
|
||||
let mut blocks_to_read = Vec::new();
|
||||
|
||||
for i in 0..num_dbl_indr {
|
||||
let num_blocks_in_single = min(num_blocks - (256 * i), 256);
|
||||
blocks_to_read.append(
|
||||
&mut self.get_blocks_from_single_indirect(
|
||||
dbl_indr_blocks[i] as u64,
|
||||
num_blocks_in_single,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
blocks_to_read
|
||||
}
|
||||
|
||||
fn run_len_compress_blocks(&self, blocks: Vec<u32>) -> Vec<DiskBlock> {
|
||||
let mut curr_block = DiskBlock {
|
||||
lba: blocks[0] as u64,
|
||||
size: 1,
|
||||
};
|
||||
|
||||
let mut iter = blocks.into_iter();
|
||||
iter.next();
|
||||
|
||||
let mut blocks = Vec::new();
|
||||
|
||||
while let Some(block) = iter.next() {
|
||||
if block as u64 == (curr_block.lba + curr_block.size) {
|
||||
curr_block.size += 1;
|
||||
} else {
|
||||
blocks.push(curr_block.clone());
|
||||
|
||||
curr_block.lba = block as u64;
|
||||
curr_block.size = 1;
|
||||
}
|
||||
}
|
||||
|
||||
blocks.push(curr_block);
|
||||
|
||||
blocks
|
||||
}
|
||||
|
||||
fn read_inode(&mut self, _inode_num: u32, inode: Inode) -> Result<Capability, ZError> {
|
||||
// TODO: Cache this method using _inode_num
|
||||
// TODO: This assumes 512 byte sectors.
|
||||
let real_block_cnt = (inode.blocks as u64 - 1) / (self.superblock().block_size() / 512) + 1;
|
||||
if inode.block[14] != 0 {
|
||||
debug!("Can't handle triply indirect inodes yet.");
|
||||
return Err(ZError::UNIMPLEMENTED);
|
||||
}
|
||||
|
||||
let mut blocks_to_read = Vec::new();
|
||||
|
||||
for i in 0..min(12, real_block_cnt) {
|
||||
blocks_to_read.push(inode.block[i as usize])
|
||||
}
|
||||
|
||||
// Singly indirect block.
|
||||
if inode.block[12] != 0 {
|
||||
let num_blocks = min(256, real_block_cnt - 12) as usize;
|
||||
blocks_to_read.append(
|
||||
&mut self.get_blocks_from_single_indirect(inode.block[12] as u64, num_blocks),
|
||||
);
|
||||
}
|
||||
|
||||
// Doubly indirect block.
|
||||
if inode.block[13] != 0 {
|
||||
let num_blocks = min(256 * 256, real_block_cnt - 268) as usize;
|
||||
blocks_to_read.append(
|
||||
&mut self.get_blocks_from_double_indirect(inode.block[13] as u64, num_blocks),
|
||||
);
|
||||
};
|
||||
|
||||
self.reader
|
||||
.read_many(&self.run_len_compress_blocks(blocks_to_read))
|
||||
}
|
||||
|
||||
fn read_inode_into_mem(
|
||||
&mut self,
|
||||
inode_num: u32,
|
||||
inode: Inode,
|
||||
) -> Result<MemoryRegion, ZError> {
|
||||
if !self.inode_cache.contains_key(&inode_num) {
|
||||
debug!("Cache MISS for inode_num: {}", inode_num);
|
||||
let inode_cap = self.read_inode(inode_num, inode)?;
|
||||
self.inode_cache.insert(inode_num, inode_cap);
|
||||
} else {
|
||||
debug!("Cache HIT for inode_num: {}", inode_num);
|
||||
}
|
||||
|
||||
MemoryRegion::from_cap(self.inode_cache[&inode_num].duplicate(Capability::PERMS_ALL)?)
|
||||
}
|
||||
|
||||
pub fn read_file(&mut self, inode_num: u32) -> Result<Capability, ZError> {
|
||||
let inode = self.get_inode(inode_num);
|
||||
if (inode.mode & 0x8000) == 0 {
|
||||
debug!("Reading non file.");
|
||||
return Err(ZError::INVALID_ARGUMENT);
|
||||
}
|
||||
self.read_inode(inode_num, inode)
|
||||
}
|
||||
|
||||
pub fn read_directory(&mut self, inode_num: u32) -> Result<Vec<FileInfo>, ZError> {
|
||||
let inode = self.get_inode(inode_num);
|
||||
if (inode.mode & 0x4000) == 0 {
|
||||
let mode = inode.mode;
|
||||
debug!("Reading non directory. Inode {:?}, Mode {}", inode, mode);
|
||||
return Err(ZError::INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
let dir = self.read_inode_into_mem(inode_num, inode)?;
|
||||
|
||||
let mut file_names = Vec::new();
|
||||
|
||||
let mut offset = 0;
|
||||
while offset < dir.size() {
|
||||
let dir_ptr: DirEntry = unsafe { dir.raw_ptr_at_offset::<DirEntry>(offset).read() };
|
||||
|
||||
let name = dir_ptr.name;
|
||||
let file_name: String =
|
||||
String::from_utf8(name[..dir_ptr.name_len as usize].to_vec()).unwrap();
|
||||
file_names.push(FileInfo {
|
||||
inode: dir_ptr.inode,
|
||||
name: file_name,
|
||||
});
|
||||
|
||||
offset += dir_ptr.record_length as u64;
|
||||
}
|
||||
|
||||
Ok(file_names)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
/// Superblock structure.
|
||||
/// https://www.nongnu.org/ext2-doc/ext2.html#superblock
|
||||
#[repr(C, packed)]
|
||||
pub struct Superblock {
|
||||
pub inodes_count: u32,
|
||||
|
@ -28,3 +30,95 @@ pub struct Superblock {
|
|||
pub first_ino: u32,
|
||||
pub inode_size: u16,
|
||||
}
|
||||
|
||||
impl Superblock {
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.magic == 0xEF53
|
||||
}
|
||||
|
||||
pub fn sectors_per_block(&self) -> u64 {
|
||||
1 << (self.log_block_size + 1)
|
||||
}
|
||||
|
||||
pub fn block_size(&self) -> u64 {
|
||||
1024 << self.log_block_size
|
||||
}
|
||||
|
||||
pub fn bgdt_block_num(&self) -> u64 {
|
||||
if self.block_size() == 1024 { 2 } else { 1 }
|
||||
}
|
||||
|
||||
pub fn bgdt_block_size(&self) -> u64 {
|
||||
(self.num_block_groups() * (size_of::<BlockGroupDescriptor>() as u64) - 1)
|
||||
/ self.block_size()
|
||||
+ 1
|
||||
}
|
||||
|
||||
pub fn num_block_groups(&self) -> u64 {
|
||||
(((self.blocks_count - 1) / self.blocks_per_group) + 1) as u64
|
||||
}
|
||||
|
||||
pub fn inode_size(&self) -> u64 {
|
||||
if self.rev_level >= 1 {
|
||||
self.inode_size as u64
|
||||
} else {
|
||||
const DEFAULT_INODE_SIZE: u64 = 0x80;
|
||||
DEFAULT_INODE_SIZE
|
||||
}
|
||||
}
|
||||
|
||||
pub fn inode_table_block_size(&self) -> u64 {
|
||||
(self.inode_size() * self.inodes_per_group as u64) / self.block_size()
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Debug)]
|
||||
pub struct BlockGroupDescriptor {
|
||||
pub block_bitmap: u32,
|
||||
pub inode_bitmap: u32,
|
||||
pub inode_table: u32,
|
||||
pub free_blocks_count: u16,
|
||||
pub free_inodes_count: u16,
|
||||
pub used_dirs_count: u16,
|
||||
reserved: [u8; 14],
|
||||
}
|
||||
|
||||
const _: () = assert!(size_of::<BlockGroupDescriptor>() == 32);
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Inode {
|
||||
pub mode: u16,
|
||||
pub uid: u16,
|
||||
pub size: u32,
|
||||
pub atime: u32,
|
||||
pub ctime: u32,
|
||||
pub mtime: u32,
|
||||
pub dtime: u32,
|
||||
pub gid: u16,
|
||||
pub links_count: u16,
|
||||
pub blocks: u32,
|
||||
pub flags: u32,
|
||||
pub osd1: u32,
|
||||
pub block: [u32; 15],
|
||||
pub generation: u32,
|
||||
pub file_acl: u32,
|
||||
pub dir_acl: u32,
|
||||
pub faddr: u32,
|
||||
pub osd2: [u32; 3],
|
||||
}
|
||||
|
||||
const _: () = assert!(size_of::<Inode>() == 128);
|
||||
|
||||
pub const EXT2_FT_FILE: u8 = 0x1;
|
||||
pub const EXT2_FT_DIR: u8 = 0x2;
|
||||
|
||||
#[repr(C, packed)]
|
||||
pub struct DirEntry {
|
||||
pub inode: u32,
|
||||
pub record_length: u16,
|
||||
pub name_len: u8,
|
||||
pub file_type: u8,
|
||||
pub name: [u8; 256],
|
||||
}
|
||||
|
|
|
@ -94,10 +94,21 @@ impl MemoryRegion {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn raw_ptr_at_offset<T>(&self, offset: u64) -> *const T {
|
||||
// TODO: Come up with a better safety check here.
|
||||
// We can't use the size of T because it might not be sized.
|
||||
assert!(offset + size_of::<T>() as u64 <= self.size);
|
||||
(self.virt_addr + offset) as *const T
|
||||
}
|
||||
|
||||
pub fn cap(&self) -> &Capability {
|
||||
&self.mem_cap
|
||||
}
|
||||
|
||||
pub fn size(&self) -> u64 {
|
||||
self.size
|
||||
}
|
||||
|
||||
pub fn duplicate(&self, offset: u64, length: u64) -> Result<Capability, ZError> {
|
||||
syscall::memory_obj_duplicate(&self.mem_cap, offset, length)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use denali_client::{DenaliClient, DiskReader};
|
||||
use ext2::Ext2Driver;
|
||||
use mammoth::{cap::Capability, define_entry, zion::z_err_t};
|
||||
use mammoth::{define_entry, zion::z_err_t};
|
||||
use victoriafalls::{server::VictoriaFallsServerImpl, VFSServer};
|
||||
use yellowstone_yunq::RegisterEndpointRequest;
|
||||
use yunq::server::spawn_server_thread;
|
||||
use yunq::server::YunqServer;
|
||||
|
||||
define_entry!();
|
||||
|
||||
|
@ -12,13 +15,21 @@ extern "C" fn main() -> z_err_t {
|
|||
let yellowstone = yellowstone_yunq::from_init_endpoint();
|
||||
|
||||
let denali_info = yellowstone.get_denali().unwrap();
|
||||
let client = DenaliClient::new(Capability::take(denali_info.denali_endpoint));
|
||||
|
||||
let driver = Ext2Driver::new(DiskReader::new(
|
||||
client,
|
||||
denali_info.device_id,
|
||||
denali_info.lba_offset,
|
||||
));
|
||||
let driver = Ext2Driver::new(denali_info);
|
||||
|
||||
let vfs_server = VFSServer::new(VictoriaFallsServerImpl::new(driver)).unwrap();
|
||||
|
||||
let yellowstone = yellowstone_yunq::from_init_endpoint();
|
||||
yellowstone
|
||||
.register_endpoint(&RegisterEndpointRequest {
|
||||
endpoint_name: "victoriafalls".into(),
|
||||
endpoint_capability: vfs_server.create_client_cap().unwrap().release(),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let server_thread = spawn_server_thread(vfs_server);
|
||||
server_thread.join().unwrap();
|
||||
|
||||
0
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ include!(concat!(env!("OUT_DIR"), "/yunq.rs"));
|
|||
|
||||
pub mod dir;
|
||||
pub mod file;
|
||||
pub mod server;
|
||||
|
||||
static mut VFS_CLIENT: Option<VFSClient> = None;
|
||||
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
use alloc::{string::String, vec::Vec};
|
||||
use ext2::Ext2Driver;
|
||||
use mammoth::{debug, zion::ZError};
|
||||
|
||||
use crate::{Directory, GetDirectoryRequest, OpenFileRequest, OpenFileResponse, VFSServerHandler};
|
||||
|
||||
pub struct VictoriaFallsServerImpl {
|
||||
ext2_driver: Ext2Driver,
|
||||
}
|
||||
|
||||
impl VictoriaFallsServerImpl {
|
||||
pub fn new(ext2_driver: Ext2Driver) -> Self {
|
||||
VictoriaFallsServerImpl { ext2_driver }
|
||||
}
|
||||
|
||||
fn find_path_in_dir(&mut self, inode_num: u32, file_name: &str) -> Result<u32, ZError> {
|
||||
let files = self.ext2_driver.read_directory(inode_num)?;
|
||||
|
||||
files
|
||||
.iter()
|
||||
.find(|fi| fi.name == file_name)
|
||||
.map(|fi| fi.inode)
|
||||
.ok_or(ZError::NOT_FOUND)
|
||||
}
|
||||
}
|
||||
|
||||
impl VFSServerHandler for VictoriaFallsServerImpl {
|
||||
fn open_file(&mut self, req: OpenFileRequest) -> Result<OpenFileResponse, ZError> {
|
||||
debug!("Reading {}", req.path);
|
||||
let mut tokens = req.path.split('/');
|
||||
if tokens.next() != Some("") {
|
||||
debug!("Path must be absolute");
|
||||
return Err(ZError::INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
let mut inode_num = 2; // Start with root.
|
||||
|
||||
while let Some(path_token) = tokens.next() {
|
||||
inode_num = self.find_path_in_dir(inode_num, path_token)?;
|
||||
}
|
||||
|
||||
let inode = self.ext2_driver.get_inode(inode_num);
|
||||
Ok(OpenFileResponse {
|
||||
path: req.path,
|
||||
memory: self.ext2_driver.read_file(inode_num)?.release(),
|
||||
size: inode.size as u64,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_directory(&mut self, req: GetDirectoryRequest) -> Result<Directory, ZError> {
|
||||
debug!("Reading dir {}", req.path);
|
||||
let mut tokens = req.path.split('/');
|
||||
if tokens.next() != Some("") {
|
||||
debug!("Path must be absolute");
|
||||
return Err(ZError::INVALID_ARGUMENT);
|
||||
}
|
||||
|
||||
let mut inode_num = 2; // Start with root.
|
||||
|
||||
while let Some(path_token) = tokens.next() {
|
||||
inode_num = self.find_path_in_dir(inode_num, path_token)?;
|
||||
}
|
||||
|
||||
let files: Vec<String> = self
|
||||
.ext2_driver
|
||||
.read_directory(inode_num)?
|
||||
.into_iter()
|
||||
.map(|fi| fi.name)
|
||||
.collect();
|
||||
|
||||
Ok(Directory {
|
||||
filenames: files.join(","),
|
||||
})
|
||||
}
|
||||
}
|
|
@ -41,7 +41,7 @@ cp zion/zion efi/
|
|||
mkdir -p efi/sys
|
||||
cp ../sysroot/bin/yellowstone efi/sys/yellowstone
|
||||
cp ../sysroot/bin/denali efi/sys/denali
|
||||
cp sys/victoriafalls/victoriafalls efi/sys/victoriafalls
|
||||
cp ../sysroot/bin/victoriafalls efi/sys/victoriafalls
|
||||
|
||||
mkdir -p sysroot
|
||||
mount "${dev}p2" sysroot/
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
yunq_gen(lib/denali lib denali)
|
|
@ -1,27 +0,0 @@
|
|||
interface Denali {
|
||||
method Read(ReadRequest) -> (ReadResponse);
|
||||
method ReadMany(ReadManyRequest) -> (ReadResponse);
|
||||
}
|
||||
|
||||
message DiskBlock {
|
||||
u64 lba;
|
||||
u64 size;
|
||||
}
|
||||
|
||||
message ReadRequest {
|
||||
u64 device_id;
|
||||
DiskBlock block;
|
||||
}
|
||||
|
||||
|
||||
message ReadManyRequest {
|
||||
u64 device_id;
|
||||
repeated DiskBlock blocks;
|
||||
}
|
||||
|
||||
message ReadResponse {
|
||||
u64 device_id;
|
||||
u64 size;
|
||||
capability memory;
|
||||
}
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
// Generated file - DO NOT MODIFY
|
||||
#include "denali.yunq.client.h"
|
||||
|
||||
#include <glacier/buffer/byte_buffer.h>
|
||||
#include <glacier/buffer/cap_buffer.h>
|
||||
#include <mammoth/util/debug.h>
|
||||
#include <zcall.h>
|
||||
|
||||
|
||||
|
||||
|
||||
DenaliClient::~DenaliClient() {
|
||||
if (endpoint_ != 0) {
|
||||
check(ZCapRelease(endpoint_));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
glcr::Status DenaliClient::Read(const ReadRequest& request, ReadResponse& response) {
|
||||
|
||||
uint64_t buffer_size = kBufferSize;
|
||||
uint64_t cap_size = kCapBufferSize;
|
||||
|
||||
const uint32_t kSentinel = 0xBEEFDEAD;
|
||||
buffer_.WriteAt<uint32_t>(0, kSentinel);
|
||||
buffer_.WriteAt<uint64_t>(8, 0);
|
||||
|
||||
cap_buffer_.Reset();
|
||||
|
||||
uint64_t length = request.SerializeToBytes(buffer_, /*offset=*/16, cap_buffer_);
|
||||
|
||||
|
||||
buffer_.WriteAt<uint32_t>(4, 16 + length);
|
||||
|
||||
z_cap_t reply_port_cap;
|
||||
RET_ERR(ZEndpointSend(endpoint_, 16 + length, buffer_.RawPtr(), cap_buffer_.UsedSlots(), cap_buffer_.RawPtr(), &reply_port_cap));
|
||||
|
||||
// FIXME: Add a way to zero out the first buffer.
|
||||
RET_ERR(ZReplyPortRecv(reply_port_cap, &buffer_size, buffer_.RawPtr(), &cap_size, cap_buffer_.RawPtr()));
|
||||
|
||||
if (buffer_.At<uint32_t>(0) != kSentinel) {
|
||||
return glcr::InvalidResponse("Got an invalid response from server.");
|
||||
}
|
||||
|
||||
// Check Response Code.
|
||||
RET_ERR(buffer_.At<uint64_t>(8));
|
||||
|
||||
|
||||
yunq::MessageView resp_view(buffer_, 16);
|
||||
RETURN_ERROR(response.ParseFromBytes(resp_view, cap_buffer_));
|
||||
|
||||
|
||||
return glcr::OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
glcr::Status DenaliClient::ReadMany(const ReadManyRequest& request, ReadResponse& response) {
|
||||
|
||||
uint64_t buffer_size = kBufferSize;
|
||||
uint64_t cap_size = kCapBufferSize;
|
||||
|
||||
const uint32_t kSentinel = 0xBEEFDEAD;
|
||||
buffer_.WriteAt<uint32_t>(0, kSentinel);
|
||||
buffer_.WriteAt<uint64_t>(8, 1);
|
||||
|
||||
cap_buffer_.Reset();
|
||||
|
||||
uint64_t length = request.SerializeToBytes(buffer_, /*offset=*/16, cap_buffer_);
|
||||
|
||||
|
||||
buffer_.WriteAt<uint32_t>(4, 16 + length);
|
||||
|
||||
z_cap_t reply_port_cap;
|
||||
RET_ERR(ZEndpointSend(endpoint_, 16 + length, buffer_.RawPtr(), cap_buffer_.UsedSlots(), cap_buffer_.RawPtr(), &reply_port_cap));
|
||||
|
||||
// FIXME: Add a way to zero out the first buffer.
|
||||
RET_ERR(ZReplyPortRecv(reply_port_cap, &buffer_size, buffer_.RawPtr(), &cap_size, cap_buffer_.RawPtr()));
|
||||
|
||||
if (buffer_.At<uint32_t>(0) != kSentinel) {
|
||||
return glcr::InvalidResponse("Got an invalid response from server.");
|
||||
}
|
||||
|
||||
// Check Response Code.
|
||||
RET_ERR(buffer_.At<uint64_t>(8));
|
||||
|
||||
|
||||
yunq::MessageView resp_view(buffer_, 16);
|
||||
RETURN_ERROR(response.ParseFromBytes(resp_view, cap_buffer_));
|
||||
|
||||
|
||||
return glcr::OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
// Generated file - DO NOT MODIFY
|
||||
#pragma once
|
||||
|
||||
#include <glacier/buffer/byte_buffer.h>
|
||||
#include <glacier/buffer/cap_buffer.h>
|
||||
#include <glacier/status/error.h>
|
||||
#include <ztypes.h>
|
||||
|
||||
#include "denali.yunq.h"
|
||||
|
||||
|
||||
class DenaliClient {
|
||||
public:
|
||||
DenaliClient(z_cap_t Denali_cap) : endpoint_(Denali_cap) {}
|
||||
DenaliClient(const DenaliClient&) = delete;
|
||||
DenaliClient(DenaliClient&& other) : endpoint_(other.endpoint_) {other.endpoint_ = 0;};
|
||||
~DenaliClient();
|
||||
|
||||
z_cap_t Capability() { return endpoint_; }
|
||||
|
||||
|
||||
|
||||
[[nodiscard]] glcr::Status Read(const ReadRequest& request, ReadResponse& response);
|
||||
|
||||
|
||||
|
||||
[[nodiscard]] glcr::Status ReadMany(const ReadManyRequest& request, ReadResponse& response);
|
||||
|
||||
|
||||
private:
|
||||
z_cap_t endpoint_;
|
||||
uint64_t kBufferSize = 0x1000;
|
||||
glcr::ByteBuffer buffer_{kBufferSize};
|
||||
uint64_t kCapBufferSize = 0x10;
|
||||
glcr::CapBuffer cap_buffer_{kCapBufferSize};
|
||||
};
|
||||
|
|
@ -1,186 +0,0 @@
|
|||
// Generated file -- DO NOT MODIFY.
|
||||
#include "denali.yunq.h"
|
||||
|
||||
#include <yunq/message_view.h>
|
||||
#include <yunq/serialize.h>
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
const uint64_t header_size = 24; // 4x uint32, 1x uint64
|
||||
|
||||
struct ExtPointer {
|
||||
uint32_t offset;
|
||||
uint32_t length;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
glcr::Status DiskBlock::ParseFromBytes(const yunq::MessageView& message) {
|
||||
RETURN_ERROR(ParseFromBytesInternal(message));
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
glcr::Status DiskBlock::ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer& caps) {
|
||||
RETURN_ERROR(ParseFromBytesInternal(message));
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
glcr::Status DiskBlock::ParseFromBytesInternal(const yunq::MessageView& message) {
|
||||
RETURN_ERROR(message.CheckHeader());
|
||||
// Parse lba.
|
||||
ASSIGN_OR_RETURN(lba_, message.ReadField<uint64_t>(0));
|
||||
// Parse size.
|
||||
ASSIGN_OR_RETURN(size_, message.ReadField<uint64_t>(1));
|
||||
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
uint64_t DiskBlock::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const {
|
||||
yunq::Serializer serializer(bytes, offset, 2);
|
||||
return SerializeInternal(serializer);
|
||||
}
|
||||
|
||||
uint64_t DiskBlock::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const {
|
||||
yunq::Serializer serializer(bytes, offset, 2, caps);
|
||||
return SerializeInternal(serializer);
|
||||
}
|
||||
|
||||
uint64_t DiskBlock::SerializeInternal(yunq::Serializer& serializer) const {
|
||||
// Write lba.
|
||||
serializer.WriteField<uint64_t>(0, lba_);
|
||||
// Write size.
|
||||
serializer.WriteField<uint64_t>(1, size_);
|
||||
|
||||
serializer.WriteHeader();
|
||||
|
||||
return serializer.size();
|
||||
}
|
||||
glcr::Status ReadRequest::ParseFromBytes(const yunq::MessageView& message) {
|
||||
RETURN_ERROR(ParseFromBytesInternal(message));
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
glcr::Status ReadRequest::ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer& caps) {
|
||||
RETURN_ERROR(ParseFromBytesInternal(message));
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
glcr::Status ReadRequest::ParseFromBytesInternal(const yunq::MessageView& message) {
|
||||
RETURN_ERROR(message.CheckHeader());
|
||||
// Parse device_id.
|
||||
ASSIGN_OR_RETURN(device_id_, message.ReadField<uint64_t>(0));
|
||||
// Parse block.
|
||||
message.ReadMessage<DiskBlock>(1, block_);
|
||||
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
uint64_t ReadRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const {
|
||||
yunq::Serializer serializer(bytes, offset, 2);
|
||||
return SerializeInternal(serializer);
|
||||
}
|
||||
|
||||
uint64_t ReadRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const {
|
||||
yunq::Serializer serializer(bytes, offset, 2, caps);
|
||||
return SerializeInternal(serializer);
|
||||
}
|
||||
|
||||
uint64_t ReadRequest::SerializeInternal(yunq::Serializer& serializer) const {
|
||||
// Write device_id.
|
||||
serializer.WriteField<uint64_t>(0, device_id_);
|
||||
// Write block.
|
||||
serializer.WriteMessage<DiskBlock>(1, block_);
|
||||
|
||||
serializer.WriteHeader();
|
||||
|
||||
return serializer.size();
|
||||
}
|
||||
glcr::Status ReadManyRequest::ParseFromBytes(const yunq::MessageView& message) {
|
||||
RETURN_ERROR(ParseFromBytesInternal(message));
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
glcr::Status ReadManyRequest::ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer& caps) {
|
||||
RETURN_ERROR(ParseFromBytesInternal(message));
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
glcr::Status ReadManyRequest::ParseFromBytesInternal(const yunq::MessageView& message) {
|
||||
RETURN_ERROR(message.CheckHeader());
|
||||
// Parse device_id.
|
||||
ASSIGN_OR_RETURN(device_id_, message.ReadField<uint64_t>(0));
|
||||
// Parse blocks.
|
||||
message.ReadRepeatedMessage<DiskBlock>(1, blocks_);
|
||||
|
||||
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
uint64_t ReadManyRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const {
|
||||
yunq::Serializer serializer(bytes, offset, 2);
|
||||
return SerializeInternal(serializer);
|
||||
}
|
||||
|
||||
uint64_t ReadManyRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const {
|
||||
yunq::Serializer serializer(bytes, offset, 2, caps);
|
||||
return SerializeInternal(serializer);
|
||||
}
|
||||
|
||||
uint64_t ReadManyRequest::SerializeInternal(yunq::Serializer& serializer) const {
|
||||
// Write device_id.
|
||||
serializer.WriteField<uint64_t>(0, device_id_);
|
||||
// Write blocks.
|
||||
serializer.WriteRepeatedMessage<DiskBlock>(1, blocks_);
|
||||
|
||||
serializer.WriteHeader();
|
||||
|
||||
return serializer.size();
|
||||
}
|
||||
glcr::Status ReadResponse::ParseFromBytes(const yunq::MessageView& message) {
|
||||
RETURN_ERROR(ParseFromBytesInternal(message));
|
||||
// Parse memory.
|
||||
ASSIGN_OR_RETURN(memory_, message.ReadCapability(2));
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
glcr::Status ReadResponse::ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer& caps) {
|
||||
RETURN_ERROR(ParseFromBytesInternal(message));
|
||||
// Parse memory.
|
||||
ASSIGN_OR_RETURN(memory_, message.ReadCapability(2, caps));
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
glcr::Status ReadResponse::ParseFromBytesInternal(const yunq::MessageView& message) {
|
||||
RETURN_ERROR(message.CheckHeader());
|
||||
// Parse device_id.
|
||||
ASSIGN_OR_RETURN(device_id_, message.ReadField<uint64_t>(0));
|
||||
// Parse size.
|
||||
ASSIGN_OR_RETURN(size_, message.ReadField<uint64_t>(1));
|
||||
// Parse memory.
|
||||
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
uint64_t ReadResponse::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const {
|
||||
yunq::Serializer serializer(bytes, offset, 3);
|
||||
return SerializeInternal(serializer);
|
||||
}
|
||||
|
||||
uint64_t ReadResponse::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const {
|
||||
yunq::Serializer serializer(bytes, offset, 3, caps);
|
||||
return SerializeInternal(serializer);
|
||||
}
|
||||
|
||||
uint64_t ReadResponse::SerializeInternal(yunq::Serializer& serializer) const {
|
||||
// Write device_id.
|
||||
serializer.WriteField<uint64_t>(0, device_id_);
|
||||
// Write size.
|
||||
serializer.WriteField<uint64_t>(1, size_);
|
||||
// Write memory.
|
||||
serializer.WriteCapability(2, memory_);
|
||||
|
||||
serializer.WriteHeader();
|
||||
|
||||
return serializer.size();
|
||||
}
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
// Generated file - DO NOT MODIFY
|
||||
#pragma once
|
||||
|
||||
#include <glacier/buffer/byte_buffer.h>
|
||||
#include <glacier/buffer/cap_buffer.h>
|
||||
#include <glacier/status/status.h>
|
||||
#include <glacier/container/vector.h>
|
||||
#include <glacier/string/string.h>
|
||||
#include <yunq/message_view.h>
|
||||
#include <yunq/serialize.h>
|
||||
#include <ztypes.h>
|
||||
|
||||
|
||||
class DiskBlock {
|
||||
public:
|
||||
DiskBlock() {}
|
||||
// Delete copy and move until implemented.
|
||||
DiskBlock(const DiskBlock&) = delete;
|
||||
DiskBlock(DiskBlock&&) = default;
|
||||
DiskBlock& operator=(DiskBlock&&) = default;
|
||||
|
||||
[[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message);
|
||||
[[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer&);
|
||||
uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const;
|
||||
uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const;
|
||||
|
||||
const uint64_t& lba() const { return lba_; }
|
||||
uint64_t& mutable_lba() { return lba_; }
|
||||
void set_lba(const uint64_t& value) { lba_ = value; }
|
||||
|
||||
const uint64_t& size() const { return size_; }
|
||||
uint64_t& mutable_size() { return size_; }
|
||||
void set_size(const uint64_t& value) { size_ = value; }
|
||||
|
||||
private:
|
||||
uint64_t lba_;
|
||||
uint64_t size_;
|
||||
|
||||
// Parses everything except for caps.
|
||||
glcr::Status ParseFromBytesInternal(const yunq::MessageView& message);
|
||||
|
||||
uint64_t SerializeInternal(yunq::Serializer& serializer) const;
|
||||
};
|
||||
class ReadRequest {
|
||||
public:
|
||||
ReadRequest() {}
|
||||
// Delete copy and move until implemented.
|
||||
ReadRequest(const ReadRequest&) = delete;
|
||||
ReadRequest(ReadRequest&&) = default;
|
||||
ReadRequest& operator=(ReadRequest&&) = default;
|
||||
|
||||
[[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message);
|
||||
[[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer&);
|
||||
uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const;
|
||||
uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const;
|
||||
|
||||
const uint64_t& device_id() const { return device_id_; }
|
||||
uint64_t& mutable_device_id() { return device_id_; }
|
||||
void set_device_id(const uint64_t& value) { device_id_ = value; }
|
||||
|
||||
const DiskBlock& block() const { return block_; }
|
||||
DiskBlock& mutable_block() { return block_; }
|
||||
|
||||
private:
|
||||
uint64_t device_id_;
|
||||
DiskBlock block_;
|
||||
|
||||
// Parses everything except for caps.
|
||||
glcr::Status ParseFromBytesInternal(const yunq::MessageView& message);
|
||||
|
||||
uint64_t SerializeInternal(yunq::Serializer& serializer) const;
|
||||
};
|
||||
class ReadManyRequest {
|
||||
public:
|
||||
ReadManyRequest() {}
|
||||
// Delete copy and move until implemented.
|
||||
ReadManyRequest(const ReadManyRequest&) = delete;
|
||||
ReadManyRequest(ReadManyRequest&&) = default;
|
||||
ReadManyRequest& operator=(ReadManyRequest&&) = default;
|
||||
|
||||
[[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message);
|
||||
[[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer&);
|
||||
uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const;
|
||||
uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const;
|
||||
|
||||
const uint64_t& device_id() const { return device_id_; }
|
||||
uint64_t& mutable_device_id() { return device_id_; }
|
||||
void set_device_id(const uint64_t& value) { device_id_ = value; }
|
||||
|
||||
const glcr::Vector<DiskBlock>& blocks() const { return blocks_; }
|
||||
glcr::Vector<DiskBlock>& mutable_blocks() { return blocks_; }
|
||||
void add_blocks(DiskBlock&& value) { blocks_.PushBack(glcr::Move(value)); }
|
||||
|
||||
private:
|
||||
uint64_t device_id_;
|
||||
glcr::Vector<DiskBlock> blocks_;
|
||||
|
||||
// Parses everything except for caps.
|
||||
glcr::Status ParseFromBytesInternal(const yunq::MessageView& message);
|
||||
|
||||
uint64_t SerializeInternal(yunq::Serializer& serializer) const;
|
||||
};
|
||||
class ReadResponse {
|
||||
public:
|
||||
ReadResponse() {}
|
||||
// Delete copy and move until implemented.
|
||||
ReadResponse(const ReadResponse&) = delete;
|
||||
ReadResponse(ReadResponse&&) = default;
|
||||
ReadResponse& operator=(ReadResponse&&) = default;
|
||||
|
||||
[[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message);
|
||||
[[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer&);
|
||||
uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const;
|
||||
uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const;
|
||||
|
||||
const uint64_t& device_id() const { return device_id_; }
|
||||
uint64_t& mutable_device_id() { return device_id_; }
|
||||
void set_device_id(const uint64_t& value) { device_id_ = value; }
|
||||
|
||||
const uint64_t& size() const { return size_; }
|
||||
uint64_t& mutable_size() { return size_; }
|
||||
void set_size(const uint64_t& value) { size_ = value; }
|
||||
|
||||
const z_cap_t& memory() const { return memory_; }
|
||||
z_cap_t& mutable_memory() { return memory_; }
|
||||
void set_memory(const z_cap_t& value) { memory_ = value; }
|
||||
|
||||
private:
|
||||
uint64_t device_id_;
|
||||
uint64_t size_;
|
||||
z_cap_t memory_;
|
||||
|
||||
// Parses everything except for caps.
|
||||
glcr::Status ParseFromBytesInternal(const yunq::MessageView& message);
|
||||
|
||||
uint64_t SerializeInternal(yunq::Serializer& serializer) const;
|
||||
};
|
||||
|
|
@ -1,152 +0,0 @@
|
|||
// Generated file -- DO NOT MODIFY.
|
||||
#include "denali.yunq.server.h"
|
||||
|
||||
#include <mammoth/util/debug.h>
|
||||
#include <zcall.h>
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
const uint32_t kSentinel = 0xBEEFDEAD;
|
||||
const uint32_t kHeaderSize = 0x10;
|
||||
|
||||
void WriteError(glcr::ByteBuffer& buffer, glcr::ErrorCode err) {
|
||||
buffer.WriteAt<uint32_t>(0, kSentinel);
|
||||
buffer.WriteAt<uint32_t>(4, kHeaderSize);
|
||||
buffer.WriteAt<uint64_t>(8, err);
|
||||
}
|
||||
|
||||
void WriteHeader(glcr::ByteBuffer& buffer, uint64_t message_length) {
|
||||
buffer.WriteAt<uint32_t>(0, kSentinel);
|
||||
buffer.WriteAt<uint32_t>(4, kHeaderSize + message_length);
|
||||
buffer.WriteAt<uint64_t>(8, glcr::OK);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
|
||||
void DenaliServerBaseThreadBootstrap(void* server_base) {
|
||||
((DenaliServerBase*)server_base)->ServerThread();
|
||||
}
|
||||
|
||||
DenaliServerBase::~DenaliServerBase() {
|
||||
if (endpoint_ != 0) {
|
||||
check(ZCapRelease(endpoint_));
|
||||
}
|
||||
}
|
||||
|
||||
glcr::ErrorOr<z_cap_t> DenaliServerBase::CreateClientCap() {
|
||||
uint64_t client_cap;
|
||||
RET_ERR(ZCapDuplicate(endpoint_, ~(kZionPerm_Read), &client_cap));
|
||||
return client_cap;
|
||||
}
|
||||
|
||||
glcr::ErrorOr<DenaliClient> DenaliServerBase::CreateClient() {
|
||||
uint64_t client_cap;
|
||||
RET_ERR(ZCapDuplicate(endpoint_, ~(kZionPerm_Read), &client_cap));
|
||||
return DenaliClient(client_cap);
|
||||
}
|
||||
|
||||
Thread DenaliServerBase::RunServer() {
|
||||
return Thread(DenaliServerBaseThreadBootstrap, this);
|
||||
}
|
||||
|
||||
void DenaliServerBase::ServerThread() {
|
||||
glcr::ByteBuffer recv_buffer(0x1000);
|
||||
glcr::CapBuffer recv_cap(0x10);
|
||||
glcr::ByteBuffer resp_buffer(0x1000);
|
||||
glcr::CapBuffer resp_cap(0x10);
|
||||
z_cap_t reply_port_cap;
|
||||
|
||||
while (true) {
|
||||
uint64_t recv_cap_size = 0x10;
|
||||
uint64_t recv_buf_size = 0x1000;
|
||||
recv_cap.Reset();
|
||||
glcr::ErrorCode recv_err = static_cast<glcr::ErrorCode>(ZEndpointRecv(endpoint_, &recv_buf_size, recv_buffer.RawPtr(), &recv_cap_size, recv_cap.RawPtr(), &reply_port_cap));
|
||||
if (recv_err != glcr::OK) {
|
||||
dbgln("Error in receive: {x}", recv_err);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t resp_length = 0;
|
||||
|
||||
glcr::ErrorCode reply_err = glcr::OK;
|
||||
resp_cap.Reset();
|
||||
glcr::Status err = HandleRequest(recv_buffer, recv_cap, resp_buffer, resp_length, resp_cap);
|
||||
if (!err) {
|
||||
WriteError(resp_buffer, err.code());
|
||||
dbgln("Responding Error {}", err.message());
|
||||
reply_err = static_cast<glcr::ErrorCode>(ZReplyPortSend(reply_port_cap, kHeaderSize, resp_buffer.RawPtr(), 0, nullptr));
|
||||
} else {
|
||||
WriteHeader(resp_buffer, resp_length);
|
||||
reply_err = static_cast<glcr::ErrorCode>(ZReplyPortSend(reply_port_cap, kHeaderSize + resp_length, resp_buffer.RawPtr(), resp_cap.UsedSlots(), resp_cap.RawPtr()));
|
||||
}
|
||||
if (reply_err != glcr::OK) {
|
||||
dbgln("Error in reply: {x}", reply_err);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
glcr::Status DenaliServerBase::HandleRequest(const glcr::ByteBuffer& request,
|
||||
const glcr::CapBuffer& req_caps,
|
||||
glcr::ByteBuffer& response, uint64_t& resp_length,
|
||||
glcr::CapBuffer& resp_caps) {
|
||||
if (request.At<uint32_t>(0) != kSentinel) {
|
||||
return glcr::InvalidArgument("Request Not Valid");
|
||||
}
|
||||
|
||||
uint64_t method_select = request.At<uint64_t>(8);
|
||||
|
||||
switch(method_select) {
|
||||
case 0: {
|
||||
|
||||
|
||||
ReadRequest yunq_request;
|
||||
yunq::MessageView request_view(request, kHeaderSize);
|
||||
RETURN_ERROR(yunq_request.ParseFromBytes(request_view, req_caps));
|
||||
|
||||
|
||||
|
||||
ReadResponse yunq_response;
|
||||
|
||||
|
||||
|
||||
RETURN_ERROR(HandleRead(yunq_request, yunq_response));
|
||||
|
||||
|
||||
|
||||
resp_length = yunq_response.SerializeToBytes(response, kHeaderSize, resp_caps);
|
||||
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
|
||||
|
||||
ReadManyRequest yunq_request;
|
||||
yunq::MessageView request_view(request, kHeaderSize);
|
||||
RETURN_ERROR(yunq_request.ParseFromBytes(request_view, req_caps));
|
||||
|
||||
|
||||
|
||||
ReadResponse yunq_response;
|
||||
|
||||
|
||||
|
||||
RETURN_ERROR(HandleReadMany(yunq_request, yunq_response));
|
||||
|
||||
|
||||
|
||||
resp_length = yunq_response.SerializeToBytes(response, kHeaderSize, resp_caps);
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return glcr::Unimplemented("Method unimplemented by server.");
|
||||
}
|
||||
}
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
// Generated File -- DO NOT MODIFY.
|
||||
#pragma once
|
||||
|
||||
#include <glacier/status/error_or.h>
|
||||
#include <glacier/status/status.h>
|
||||
#include <mammoth/proc/thread.h>
|
||||
#include <ztypes.h>
|
||||
|
||||
#include "denali.yunq.h"
|
||||
#include "denali.yunq.client.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class DenaliServerBase {
|
||||
public:
|
||||
DenaliServerBase(z_cap_t Denali_cap) : endpoint_(Denali_cap) {}
|
||||
DenaliServerBase(const DenaliServerBase&) = delete;
|
||||
DenaliServerBase(DenaliServerBase&&) = delete;
|
||||
virtual ~DenaliServerBase();
|
||||
|
||||
glcr::ErrorOr<z_cap_t> CreateClientCap();
|
||||
glcr::ErrorOr<DenaliClient> CreateClient();
|
||||
|
||||
[[nodiscard]] Thread RunServer();
|
||||
|
||||
|
||||
|
||||
[[nodiscard]] virtual glcr::Status HandleRead(const ReadRequest&, ReadResponse&) = 0;
|
||||
|
||||
|
||||
|
||||
[[nodiscard]] virtual glcr::Status HandleReadMany(const ReadManyRequest&, ReadResponse&) = 0;
|
||||
|
||||
|
||||
|
||||
private:
|
||||
z_cap_t endpoint_;
|
||||
|
||||
friend void DenaliServerBaseThreadBootstrap(void*);
|
||||
void ServerThread();
|
||||
|
||||
[[nodiscard]] glcr::Status HandleRequest(const glcr::ByteBuffer& request, const glcr::CapBuffer& req_caps,
|
||||
glcr::ByteBuffer& response, uint64_t& resp_length,
|
||||
glcr::CapBuffer& resp_caps);
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
add_executable(victoriafalls
|
||||
fs/ext2/ext2_block_reader.cpp
|
||||
fs/ext2/ext2_driver.cpp
|
||||
fs/ext2/inode_table.cpp
|
||||
victoriafalls.cpp
|
||||
victoriafalls_server.cpp
|
||||
)
|
||||
|
||||
target_include_directories(victoriafalls
|
||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
|
||||
target_link_libraries(victoriafalls
|
||||
denali_yunq
|
||||
glacier
|
||||
mammoth
|
||||
victoriafalls_yunq
|
||||
yellowstone_yunq
|
||||
)
|
||||
|
||||
set_target_properties(victoriafalls PROPERTIES
|
||||
COMPILE_FLAGS "${CMAKE_CXX_FLAGS} ${BASE_COMPILE_FLAGS}"
|
||||
LINK_FLAGS "${CMAKE_EXE_LINK_FLAGS} ${BASE_LINK_FLAGS}"
|
||||
)
|
||||
|
||||
yunq_gen(lib/victoriafalls lib victoriafalls)
|
|
@ -1,76 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
struct Superblock {
|
||||
uint32_t inodes_count;
|
||||
uint32_t blocks_count;
|
||||
uint32_t reserved_blocks_count;
|
||||
uint32_t free_blocks_count;
|
||||
uint32_t free_inodes_count;
|
||||
uint32_t first_data_blok;
|
||||
uint32_t log_block_size;
|
||||
uint32_t log_frag_size;
|
||||
uint32_t blocks_per_group;
|
||||
uint32_t frags_per_group;
|
||||
uint32_t inodes_per_group;
|
||||
uint32_t mtime;
|
||||
uint32_t wtime;
|
||||
uint16_t mnt_count;
|
||||
uint16_t max_mnt_count;
|
||||
uint16_t magic;
|
||||
uint16_t state;
|
||||
uint16_t errors;
|
||||
uint16_t minor_rev_level;
|
||||
uint32_t lastcheck;
|
||||
uint32_t checkinterval;
|
||||
uint32_t creator_os;
|
||||
uint32_t rev_level;
|
||||
uint16_t def_resuid;
|
||||
uint16_t def_resgid;
|
||||
uint32_t first_ino;
|
||||
uint16_t inode_size;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct BlockGroupDescriptor {
|
||||
uint32_t block_bitmap;
|
||||
uint32_t inode_bitmap;
|
||||
uint32_t inode_table;
|
||||
uint16_t free_blocks_count;
|
||||
uint16_t free_inodes_count;
|
||||
uint16_t used_dirs_count;
|
||||
uint8_t reserved[14];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
struct Inode {
|
||||
uint16_t mode;
|
||||
uint16_t uid;
|
||||
uint32_t size;
|
||||
uint32_t atime;
|
||||
uint32_t ctime;
|
||||
uint32_t mtime;
|
||||
uint32_t dtime;
|
||||
uint16_t gid;
|
||||
uint16_t links_count;
|
||||
uint32_t blocks;
|
||||
uint32_t flags;
|
||||
uint32_t osd1;
|
||||
uint32_t block[15];
|
||||
uint32_t generation;
|
||||
uint32_t file_acl;
|
||||
uint32_t dir_acl;
|
||||
uint32_t faddr;
|
||||
uint32_t osd2[3];
|
||||
} __attribute__((__packed__));
|
||||
|
||||
constexpr uint8_t kExt2FtUnknown = 0;
|
||||
constexpr uint8_t kExt2FtFile = 1;
|
||||
constexpr uint8_t kExt2FtDirectory = 2;
|
||||
|
||||
struct DirEntry {
|
||||
uint32_t inode;
|
||||
uint16_t record_length;
|
||||
uint8_t name_len;
|
||||
uint8_t file_type;
|
||||
char name[256];
|
||||
} __attribute__((__packed__));
|
|
@ -1,117 +0,0 @@
|
|||
#include "fs/ext2/ext2_block_reader.h"
|
||||
|
||||
#include <mammoth/util/debug.h>
|
||||
|
||||
glcr::ErrorOr<glcr::SharedPtr<Ext2BlockReader>> Ext2BlockReader::Init(
|
||||
const yellowstone::DenaliInfo& denali_info) {
|
||||
// Read 1024 bytes from 1024 offset.
|
||||
// FIXME: Don't assume 512 byte sectors somehow.
|
||||
DenaliClient client(denali_info.denali_endpoint());
|
||||
ReadRequest req;
|
||||
req.set_device_id(denali_info.device_id());
|
||||
req.mutable_block().set_lba(denali_info.lba_offset() + 2);
|
||||
req.mutable_block().set_size(2);
|
||||
ReadResponse resp;
|
||||
auto status = client.Read(req, resp);
|
||||
if (!status.ok()) {
|
||||
dbgln("Failed to read superblock: {}", status.message());
|
||||
return status.code();
|
||||
}
|
||||
mmth::OwnedMemoryRegion superblock =
|
||||
mmth::OwnedMemoryRegion::FromCapability(resp.memory());
|
||||
|
||||
return glcr::SharedPtr<Ext2BlockReader>(
|
||||
new Ext2BlockReader(glcr::Move(client), denali_info.device_id(),
|
||||
denali_info.lba_offset(), glcr::Move(superblock)));
|
||||
}
|
||||
|
||||
Superblock* Ext2BlockReader::GetSuperblock() {
|
||||
return reinterpret_cast<Superblock*>(super_block_region_.vaddr());
|
||||
}
|
||||
|
||||
uint64_t Ext2BlockReader::SectorsPerBlock() {
|
||||
return 1 << (GetSuperblock()->log_block_size + 1);
|
||||
}
|
||||
|
||||
uint64_t Ext2BlockReader::BlockSize() {
|
||||
return 1024 << (GetSuperblock()->log_block_size);
|
||||
}
|
||||
|
||||
uint64_t Ext2BlockReader::NumberOfBlockGroups() {
|
||||
return ((GetSuperblock()->blocks_count - 1) /
|
||||
GetSuperblock()->blocks_per_group) +
|
||||
1;
|
||||
}
|
||||
|
||||
uint64_t Ext2BlockReader::BgdtBlockNum() {
|
||||
return (BlockSize() == 1024) ? 2 : 1;
|
||||
}
|
||||
|
||||
uint64_t Ext2BlockReader::BgdtBlockSize() {
|
||||
return ((NumberOfBlockGroups() * sizeof(BlockGroupDescriptor) - 1) /
|
||||
BlockSize()) +
|
||||
1;
|
||||
}
|
||||
|
||||
uint64_t Ext2BlockReader::InodeSize() {
|
||||
constexpr uint64_t kDefaultInodeSize = 0x80;
|
||||
return GetSuperblock()->rev_level >= 1 ? GetSuperblock()->inode_size
|
||||
: kDefaultInodeSize;
|
||||
}
|
||||
|
||||
uint64_t Ext2BlockReader::InodeTableBlockSize() {
|
||||
return (InodeSize() * GetSuperblock()->inodes_per_group) / BlockSize();
|
||||
}
|
||||
|
||||
glcr::ErrorOr<mmth::OwnedMemoryRegion> Ext2BlockReader::ReadBlock(
|
||||
uint64_t block_number) {
|
||||
return ReadBlocks(block_number, 1);
|
||||
}
|
||||
glcr::ErrorOr<mmth::OwnedMemoryRegion> Ext2BlockReader::ReadBlocks(
|
||||
uint64_t block_number, uint64_t num_blocks) {
|
||||
ReadRequest req;
|
||||
req.set_device_id(device_id_);
|
||||
req.mutable_block().set_lba(lba_offset_ + block_number * SectorsPerBlock());
|
||||
req.mutable_block().set_size(num_blocks * SectorsPerBlock());
|
||||
ReadResponse resp;
|
||||
auto status = denali_.Read(req, resp);
|
||||
if (!status.ok()) {
|
||||
dbgln("Failed to read block: {}", status.code());
|
||||
return status.code();
|
||||
}
|
||||
return mmth::OwnedMemoryRegion::FromCapability(resp.memory());
|
||||
}
|
||||
|
||||
glcr::ErrorOr<mmth::OwnedMemoryRegion> Ext2BlockReader::ReadBlocks(
|
||||
const glcr::Vector<uint64_t>& block_list) {
|
||||
ReadManyRequest req;
|
||||
req.set_device_id(device_id_);
|
||||
for (uint64_t i = 0; i < block_list.size(); i++) {
|
||||
uint64_t curr_start = lba_offset_ + block_list.at(i) * SectorsPerBlock();
|
||||
uint64_t curr_run_len = 1;
|
||||
while ((i + 1) < block_list.size() &&
|
||||
block_list.at(i + 1) == block_list.at(i) + 1) {
|
||||
i++;
|
||||
curr_run_len++;
|
||||
}
|
||||
DiskBlock block;
|
||||
block.set_lba(curr_start);
|
||||
block.set_size(curr_run_len * SectorsPerBlock());
|
||||
req.add_blocks(glcr::Move(block));
|
||||
}
|
||||
ReadResponse resp;
|
||||
auto status = denali_.ReadMany(req, resp);
|
||||
if (!status.ok()) {
|
||||
dbgln("Failed to read blocks: {}", status.code());
|
||||
return status.code();
|
||||
}
|
||||
return mmth::OwnedMemoryRegion::FromCapability(resp.memory());
|
||||
}
|
||||
|
||||
Ext2BlockReader::Ext2BlockReader(DenaliClient&& denali, uint64_t device_id,
|
||||
uint64_t lba_offset,
|
||||
mmth::OwnedMemoryRegion&& super_block)
|
||||
: denali_(glcr::Move(denali)),
|
||||
device_id_(device_id),
|
||||
lba_offset_(lba_offset),
|
||||
super_block_region_(glcr::Move(super_block)) {}
|
|
@ -1,49 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <denali/denali.yunq.client.h>
|
||||
#include <glacier/memory/shared_ptr.h>
|
||||
#include <glacier/status/error_or.h>
|
||||
#include <mammoth/util/memory_region.h>
|
||||
#include <yellowstone/yellowstone.yunq.h>
|
||||
|
||||
#include "fs/ext2/ext2.h"
|
||||
|
||||
/* Simple Wrapper class around the denali client to translate blocks to sectors.
|
||||
*
|
||||
* By necessity contains the Ext Superblock (to make the translation
|
||||
* calculation).
|
||||
* */
|
||||
class Ext2BlockReader {
|
||||
public:
|
||||
static glcr::ErrorOr<glcr::SharedPtr<Ext2BlockReader>> Init(
|
||||
const yellowstone::DenaliInfo& denali_info);
|
||||
|
||||
// TODO: Consider creating a new class wrapper with these computations.
|
||||
Superblock* GetSuperblock();
|
||||
uint64_t BlockSize();
|
||||
uint64_t NumberOfBlockGroups();
|
||||
uint64_t BgdtBlockNum();
|
||||
uint64_t BgdtBlockSize();
|
||||
uint64_t InodeSize();
|
||||
// FIXME: This probably needs to take into account the block group number
|
||||
// because the last table will likely be smaller.
|
||||
uint64_t InodeTableBlockSize();
|
||||
|
||||
glcr::ErrorOr<mmth::OwnedMemoryRegion> ReadBlock(uint64_t block_number);
|
||||
glcr::ErrorOr<mmth::OwnedMemoryRegion> ReadBlocks(uint64_t block_number,
|
||||
uint64_t num_blocks);
|
||||
|
||||
glcr::ErrorOr<mmth::OwnedMemoryRegion> ReadBlocks(
|
||||
const glcr::Vector<uint64_t>& block_list);
|
||||
|
||||
private:
|
||||
DenaliClient denali_;
|
||||
uint64_t device_id_;
|
||||
uint64_t lba_offset_;
|
||||
mmth::OwnedMemoryRegion super_block_region_;
|
||||
|
||||
Ext2BlockReader(DenaliClient&& denali, uint64_t device_id,
|
||||
uint64_t lba_offset, mmth::OwnedMemoryRegion&& super_block);
|
||||
|
||||
uint64_t SectorsPerBlock();
|
||||
};
|
|
@ -1,160 +0,0 @@
|
|||
#include "fs/ext2/ext2_driver.h"
|
||||
|
||||
#include <glacier/string/string.h>
|
||||
#include <mammoth/util/debug.h>
|
||||
|
||||
#define EXT2_DEBUG 0
|
||||
|
||||
glcr::ErrorOr<Ext2Driver> Ext2Driver::Init(
|
||||
const yellowstone::DenaliInfo& denali_info) {
|
||||
ASSIGN_OR_RETURN(glcr::SharedPtr<Ext2BlockReader> reader,
|
||||
Ext2BlockReader::Init(glcr::Move(denali_info)));
|
||||
|
||||
ASSIGN_OR_RETURN(
|
||||
mmth::OwnedMemoryRegion bgdt,
|
||||
reader->ReadBlocks(reader->BgdtBlockNum(), reader->BgdtBlockSize()));
|
||||
glcr::UniquePtr<InodeTable> inode_table(
|
||||
new InodeTable(reader, glcr::Move(bgdt)));
|
||||
|
||||
return Ext2Driver(reader, glcr::Move(inode_table));
|
||||
}
|
||||
|
||||
glcr::ErrorCode Ext2Driver::ProbePartition() {
|
||||
Superblock* superblock = ext2_reader_->GetSuperblock();
|
||||
if (superblock->magic != 0xEF53) {
|
||||
dbgln("Invalid EXT2 magic code: {x}");
|
||||
return glcr::INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
#if EXT2_DEBUG
|
||||
dbgln("Block size: 0x{x}", 1024 << superblock->log_block_size);
|
||||
|
||||
dbgln("Blocks: 0x{x} (0x{x} per group)", superblock->blocks_count,
|
||||
superblock->blocks_per_group);
|
||||
dbgln("Inodes: 0x{x} (0x{x} per group)", superblock->inodes_count,
|
||||
superblock->inodes_per_group);
|
||||
dbgln("Inode size: 0x{x}", superblock->inode_size);
|
||||
|
||||
dbgln("Mounts: 0x{x} out of 0x{x}", superblock->mnt_count,
|
||||
superblock->max_mnt_count);
|
||||
dbgln("State: {x}", superblock->state);
|
||||
|
||||
dbgln("Created by: {x}", superblock->creator_os);
|
||||
#endif
|
||||
|
||||
return glcr::OK;
|
||||
}
|
||||
|
||||
glcr::ErrorOr<Inode*> Ext2Driver::GetInode(uint32_t inode_number) {
|
||||
return inode_table_->GetInode(inode_number);
|
||||
}
|
||||
|
||||
glcr::ErrorOr<glcr::Vector<DirEntry>> Ext2Driver::ReadDirectory(
|
||||
uint32_t inode_number) {
|
||||
ASSIGN_OR_RETURN(Inode * inode, inode_table_->GetInode(inode_number));
|
||||
if (!(inode->mode & 0x4000)) {
|
||||
dbgln("Reading non directory.");
|
||||
return glcr::INVALID_ARGUMENT;
|
||||
}
|
||||
ASSIGN_OR_RETURN(mmth::OwnedMemoryRegion dir, ReadInode(inode_number, inode));
|
||||
|
||||
glcr::Vector<DirEntry> directory;
|
||||
|
||||
uint64_t addr = dir.vaddr();
|
||||
while (addr < dir.vaddr() + ext2_reader_->BlockSize()) {
|
||||
DirEntry* entry = reinterpret_cast<DirEntry*>(addr);
|
||||
directory.PushBack(*entry);
|
||||
glcr::StringView name(entry->name, entry->name_len);
|
||||
#if EXT2_DEBUG
|
||||
switch (entry->file_type) {
|
||||
case kExt2FtFile:
|
||||
dbgln("FILE (0x{x}): {}", entry->inode, name);
|
||||
break;
|
||||
case kExt2FtDirectory:
|
||||
dbgln("DIR (0x{x}): {}", entry->inode, name);
|
||||
break;
|
||||
default:
|
||||
dbgln("UNK (0x{x}): {}", entry->inode, name);
|
||||
}
|
||||
#endif
|
||||
addr += entry->record_length;
|
||||
}
|
||||
return directory;
|
||||
}
|
||||
|
||||
glcr::ErrorOr<mmth::OwnedMemoryRegion> Ext2Driver::ReadFile(
|
||||
uint64_t inode_number) {
|
||||
ASSIGN_OR_RETURN(Inode * inode, inode_table_->GetInode(inode_number));
|
||||
|
||||
if (!(inode->mode & 0x8000)) {
|
||||
dbgln("Reading non file.");
|
||||
return glcr::INVALID_ARGUMENT;
|
||||
}
|
||||
return ReadInode(inode_number, inode);
|
||||
}
|
||||
|
||||
glcr::ErrorOr<mmth::OwnedMemoryRegion> Ext2Driver::ReadInode(uint64_t inode_num,
|
||||
Inode* inode) {
|
||||
if (inode_cache_.Contains(inode_num)) {
|
||||
return inode_cache_.at(inode_num).Duplicate();
|
||||
}
|
||||
// This calculation is cursed.
|
||||
uint64_t real_block_cnt =
|
||||
(inode->blocks - 1) / (ext2_reader_->BlockSize() / 512) + 1;
|
||||
|
||||
if (inode->block[14]) {
|
||||
dbgln("Can't handle triply-indirect blocks yet.");
|
||||
return glcr::UNIMPLEMENTED;
|
||||
}
|
||||
|
||||
mmth::OwnedMemoryRegion double_indirect_block;
|
||||
if (inode->block[13]) {
|
||||
ASSIGN_OR_RETURN(double_indirect_block,
|
||||
ext2_reader_->ReadBlock(inode->block[13]));
|
||||
}
|
||||
|
||||
mmth::OwnedMemoryRegion indirect_block;
|
||||
if (inode->block[12]) {
|
||||
ASSIGN_OR_RETURN(indirect_block, ext2_reader_->ReadBlock(inode->block[12]));
|
||||
}
|
||||
|
||||
glcr::Vector<uint64_t> blocks_to_read;
|
||||
for (uint64_t i = 0; i < 12 && i < real_block_cnt; i++) {
|
||||
blocks_to_read.PushBack(inode->block[i]);
|
||||
}
|
||||
|
||||
uint32_t* indr_block_array =
|
||||
reinterpret_cast<uint32_t*>(indirect_block.vaddr());
|
||||
for (uint64_t i = 12; i < 268 && i < real_block_cnt; i++) {
|
||||
uint64_t offset = i - 12;
|
||||
blocks_to_read.PushBack(indr_block_array[offset]);
|
||||
}
|
||||
|
||||
uint32_t* dbl_indr_block_array =
|
||||
reinterpret_cast<uint32_t*>(double_indirect_block.vaddr());
|
||||
for (uint64_t i = 0; i < 256; i++) {
|
||||
uint64_t block = 268 + (256 * i);
|
||||
if (block >= real_block_cnt) {
|
||||
break;
|
||||
}
|
||||
ASSIGN_OR_RETURN(mmth::OwnedMemoryRegion single_indr_block,
|
||||
ext2_reader_->ReadBlock(dbl_indr_block_array[i]));
|
||||
uint32_t* single_indr_block_array =
|
||||
reinterpret_cast<uint32_t*>(single_indr_block.vaddr());
|
||||
for (uint64_t j = 0; j < 256; j++) {
|
||||
uint64_t block_inner = block + j;
|
||||
if (block_inner >= real_block_cnt) {
|
||||
break;
|
||||
}
|
||||
if (single_indr_block_array[j] != 0) {
|
||||
blocks_to_read.PushBack(single_indr_block_array[j]);
|
||||
} else {
|
||||
dbgln("WARN skipping 0 block in inode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ASSIGN_OR_RETURN(auto inode_mem, ext2_reader_->ReadBlocks(blocks_to_read));
|
||||
RET_ERR(inode_cache_.Insert(glcr::Move(inode_num), inode_mem.Duplicate()));
|
||||
return inode_mem;
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <glacier/container/hash_map.h>
|
||||
#include <glacier/memory/move.h>
|
||||
#include <glacier/memory/unique_ptr.h>
|
||||
#include <yellowstone/yellowstone.yunq.h>
|
||||
|
||||
#include "fs/ext2/ext2.h"
|
||||
#include "fs/ext2/ext2_block_reader.h"
|
||||
#include "fs/ext2/inode_table.h"
|
||||
|
||||
class Ext2Driver {
|
||||
public:
|
||||
static glcr::ErrorOr<Ext2Driver> Init(
|
||||
const yellowstone::DenaliInfo& denali_info);
|
||||
|
||||
glcr::ErrorCode ProbePartition();
|
||||
|
||||
glcr::ErrorOr<Inode*> GetInode(uint32_t inode_number);
|
||||
|
||||
glcr::ErrorOr<glcr::Vector<DirEntry>> ReadDirectory(uint32_t inode_number);
|
||||
|
||||
glcr::ErrorOr<mmth::OwnedMemoryRegion> ReadFile(uint64_t inode_number);
|
||||
|
||||
private:
|
||||
glcr::SharedPtr<Ext2BlockReader> ext2_reader_;
|
||||
glcr::UniquePtr<InodeTable> inode_table_;
|
||||
glcr::HashMap<uint64_t, mmth::OwnedMemoryRegion> inode_cache_;
|
||||
|
||||
Ext2Driver(const glcr::SharedPtr<Ext2BlockReader>& reader,
|
||||
glcr::UniquePtr<InodeTable> inode_table)
|
||||
: ext2_reader_(reader), inode_table_(glcr::Move(inode_table)) {}
|
||||
|
||||
glcr::ErrorOr<mmth::OwnedMemoryRegion> ReadInode(uint64_t inode_num,
|
||||
Inode* inode);
|
||||
};
|
|
@ -1,33 +0,0 @@
|
|||
#include "fs/ext2/inode_table.h"
|
||||
|
||||
InodeTable::InodeTable(const glcr::SharedPtr<Ext2BlockReader>& reader,
|
||||
mmth::OwnedMemoryRegion&& bgdt_region)
|
||||
: ext2_reader_(reader),
|
||||
bgdt_region_(glcr::Move(bgdt_region)),
|
||||
bgdt_(reinterpret_cast<BlockGroupDescriptor*>(bgdt_region_.vaddr())) {
|
||||
inode_tables_.Resize(ext2_reader_->NumberOfBlockGroups());
|
||||
}
|
||||
|
||||
glcr::ErrorOr<Inode*> InodeTable::GetInode(uint32_t inode_num) {
|
||||
uint64_t inodes_per_group = ext2_reader_->GetSuperblock()->inodes_per_group;
|
||||
uint64_t block_group_num = (inode_num - 1) / inodes_per_group;
|
||||
ASSIGN_OR_RETURN(Inode * root, GetRootOfInodeTable(block_group_num));
|
||||
uint64_t local_index = (inode_num - 1) % inodes_per_group;
|
||||
return reinterpret_cast<Inode*>(reinterpret_cast<uint64_t>(root) +
|
||||
local_index * ext2_reader_->InodeSize());
|
||||
}
|
||||
|
||||
glcr::ErrorOr<Inode*> InodeTable::GetRootOfInodeTable(
|
||||
uint64_t block_group_num) {
|
||||
if (block_group_num > ext2_reader_->NumberOfBlockGroups()) {
|
||||
return glcr::INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
if (!inode_tables_[block_group_num]) {
|
||||
ASSIGN_OR_RETURN(
|
||||
inode_tables_[block_group_num],
|
||||
ext2_reader_->ReadBlocks(bgdt_[block_group_num].inode_table,
|
||||
ext2_reader_->InodeTableBlockSize()));
|
||||
}
|
||||
return reinterpret_cast<Inode*>(inode_tables_[block_group_num].vaddr());
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <glacier/container/vector.h>
|
||||
#include <mammoth/util/memory_region.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "fs/ext2/ext2_block_reader.h"
|
||||
|
||||
class InodeTable {
|
||||
public:
|
||||
InodeTable(const glcr::SharedPtr<Ext2BlockReader>& driver,
|
||||
mmth::OwnedMemoryRegion&& bgdt_region);
|
||||
|
||||
glcr::ErrorOr<Inode*> GetInode(uint32_t inode_num);
|
||||
|
||||
private:
|
||||
glcr::SharedPtr<Ext2BlockReader> ext2_reader_;
|
||||
mmth::OwnedMemoryRegion bgdt_region_;
|
||||
BlockGroupDescriptor* bgdt_;
|
||||
|
||||
glcr::Vector<mmth::OwnedMemoryRegion> inode_tables_;
|
||||
|
||||
glcr::ErrorOr<Inode*> GetRootOfInodeTable(uint64_t block_group_num);
|
||||
};
|
|
@ -1,23 +0,0 @@
|
|||
interface VFS {
|
||||
method OpenFile(OpenFileRequest) -> (OpenFileResponse);
|
||||
method GetDirectory(GetDirectoryRequest) -> (Directory);
|
||||
}
|
||||
|
||||
message OpenFileRequest {
|
||||
string path;
|
||||
}
|
||||
|
||||
message OpenFileResponse {
|
||||
string path;
|
||||
u64 size;
|
||||
capability memory;
|
||||
}
|
||||
|
||||
message GetDirectoryRequest {
|
||||
string path;
|
||||
}
|
||||
|
||||
message Directory {
|
||||
// , separated list of filenames until we have repeated strings.
|
||||
string filenames;
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
// Generated file - DO NOT MODIFY
|
||||
#include "victoriafalls.yunq.client.h"
|
||||
|
||||
#include <glacier/buffer/byte_buffer.h>
|
||||
#include <glacier/buffer/cap_buffer.h>
|
||||
#include <mammoth/util/debug.h>
|
||||
#include <zcall.h>
|
||||
|
||||
|
||||
|
||||
|
||||
VFSClient::~VFSClient() {
|
||||
if (endpoint_ != 0) {
|
||||
check(ZCapRelease(endpoint_));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
glcr::Status VFSClient::OpenFile(const OpenFileRequest& request, OpenFileResponse& response) {
|
||||
|
||||
uint64_t buffer_size = kBufferSize;
|
||||
uint64_t cap_size = kCapBufferSize;
|
||||
|
||||
const uint32_t kSentinel = 0xBEEFDEAD;
|
||||
buffer_.WriteAt<uint32_t>(0, kSentinel);
|
||||
buffer_.WriteAt<uint64_t>(8, 0);
|
||||
|
||||
cap_buffer_.Reset();
|
||||
|
||||
uint64_t length = request.SerializeToBytes(buffer_, /*offset=*/16, cap_buffer_);
|
||||
|
||||
|
||||
buffer_.WriteAt<uint32_t>(4, 16 + length);
|
||||
|
||||
z_cap_t reply_port_cap;
|
||||
RET_ERR(ZEndpointSend(endpoint_, 16 + length, buffer_.RawPtr(), cap_buffer_.UsedSlots(), cap_buffer_.RawPtr(), &reply_port_cap));
|
||||
|
||||
// FIXME: Add a way to zero out the first buffer.
|
||||
RET_ERR(ZReplyPortRecv(reply_port_cap, &buffer_size, buffer_.RawPtr(), &cap_size, cap_buffer_.RawPtr()));
|
||||
|
||||
if (buffer_.At<uint32_t>(0) != kSentinel) {
|
||||
return glcr::InvalidResponse("Got an invalid response from server.");
|
||||
}
|
||||
|
||||
// Check Response Code.
|
||||
RET_ERR(buffer_.At<uint64_t>(8));
|
||||
|
||||
|
||||
yunq::MessageView resp_view(buffer_, 16);
|
||||
RETURN_ERROR(response.ParseFromBytes(resp_view, cap_buffer_));
|
||||
|
||||
|
||||
return glcr::OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
glcr::Status VFSClient::GetDirectory(const GetDirectoryRequest& request, Directory& response) {
|
||||
|
||||
uint64_t buffer_size = kBufferSize;
|
||||
uint64_t cap_size = kCapBufferSize;
|
||||
|
||||
const uint32_t kSentinel = 0xBEEFDEAD;
|
||||
buffer_.WriteAt<uint32_t>(0, kSentinel);
|
||||
buffer_.WriteAt<uint64_t>(8, 1);
|
||||
|
||||
cap_buffer_.Reset();
|
||||
|
||||
uint64_t length = request.SerializeToBytes(buffer_, /*offset=*/16, cap_buffer_);
|
||||
|
||||
|
||||
buffer_.WriteAt<uint32_t>(4, 16 + length);
|
||||
|
||||
z_cap_t reply_port_cap;
|
||||
RET_ERR(ZEndpointSend(endpoint_, 16 + length, buffer_.RawPtr(), cap_buffer_.UsedSlots(), cap_buffer_.RawPtr(), &reply_port_cap));
|
||||
|
||||
// FIXME: Add a way to zero out the first buffer.
|
||||
RET_ERR(ZReplyPortRecv(reply_port_cap, &buffer_size, buffer_.RawPtr(), &cap_size, cap_buffer_.RawPtr()));
|
||||
|
||||
if (buffer_.At<uint32_t>(0) != kSentinel) {
|
||||
return glcr::InvalidResponse("Got an invalid response from server.");
|
||||
}
|
||||
|
||||
// Check Response Code.
|
||||
RET_ERR(buffer_.At<uint64_t>(8));
|
||||
|
||||
|
||||
yunq::MessageView resp_view(buffer_, 16);
|
||||
RETURN_ERROR(response.ParseFromBytes(resp_view, cap_buffer_));
|
||||
|
||||
|
||||
return glcr::OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
// Generated file - DO NOT MODIFY
|
||||
#pragma once
|
||||
|
||||
#include <glacier/buffer/byte_buffer.h>
|
||||
#include <glacier/buffer/cap_buffer.h>
|
||||
#include <glacier/status/error.h>
|
||||
#include <ztypes.h>
|
||||
|
||||
#include "victoriafalls.yunq.h"
|
||||
|
||||
|
||||
class VFSClient {
|
||||
public:
|
||||
VFSClient(z_cap_t VFS_cap) : endpoint_(VFS_cap) {}
|
||||
VFSClient(const VFSClient&) = delete;
|
||||
VFSClient(VFSClient&& other) : endpoint_(other.endpoint_) {other.endpoint_ = 0;};
|
||||
~VFSClient();
|
||||
|
||||
z_cap_t Capability() { return endpoint_; }
|
||||
|
||||
|
||||
|
||||
[[nodiscard]] glcr::Status OpenFile(const OpenFileRequest& request, OpenFileResponse& response);
|
||||
|
||||
|
||||
|
||||
[[nodiscard]] glcr::Status GetDirectory(const GetDirectoryRequest& request, Directory& response);
|
||||
|
||||
|
||||
private:
|
||||
z_cap_t endpoint_;
|
||||
uint64_t kBufferSize = 0x1000;
|
||||
glcr::ByteBuffer buffer_{kBufferSize};
|
||||
uint64_t kCapBufferSize = 0x10;
|
||||
glcr::CapBuffer cap_buffer_{kCapBufferSize};
|
||||
};
|
||||
|
|
@ -1,173 +0,0 @@
|
|||
// Generated file -- DO NOT MODIFY.
|
||||
#include "victoriafalls.yunq.h"
|
||||
|
||||
#include <yunq/message_view.h>
|
||||
#include <yunq/serialize.h>
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
const uint64_t header_size = 24; // 4x uint32, 1x uint64
|
||||
|
||||
struct ExtPointer {
|
||||
uint32_t offset;
|
||||
uint32_t length;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
glcr::Status OpenFileRequest::ParseFromBytes(const yunq::MessageView& message) {
|
||||
RETURN_ERROR(ParseFromBytesInternal(message));
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
glcr::Status OpenFileRequest::ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer& caps) {
|
||||
RETURN_ERROR(ParseFromBytesInternal(message));
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
glcr::Status OpenFileRequest::ParseFromBytesInternal(const yunq::MessageView& message) {
|
||||
RETURN_ERROR(message.CheckHeader());
|
||||
// Parse path.
|
||||
ASSIGN_OR_RETURN(path_, message.ReadField<glcr::String>(0));
|
||||
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
uint64_t OpenFileRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const {
|
||||
yunq::Serializer serializer(bytes, offset, 1);
|
||||
return SerializeInternal(serializer);
|
||||
}
|
||||
|
||||
uint64_t OpenFileRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const {
|
||||
yunq::Serializer serializer(bytes, offset, 1, caps);
|
||||
return SerializeInternal(serializer);
|
||||
}
|
||||
|
||||
uint64_t OpenFileRequest::SerializeInternal(yunq::Serializer& serializer) const {
|
||||
// Write path.
|
||||
serializer.WriteField<glcr::String>(0, path_);
|
||||
|
||||
serializer.WriteHeader();
|
||||
|
||||
return serializer.size();
|
||||
}
|
||||
glcr::Status OpenFileResponse::ParseFromBytes(const yunq::MessageView& message) {
|
||||
RETURN_ERROR(ParseFromBytesInternal(message));
|
||||
// Parse memory.
|
||||
ASSIGN_OR_RETURN(memory_, message.ReadCapability(2));
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
glcr::Status OpenFileResponse::ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer& caps) {
|
||||
RETURN_ERROR(ParseFromBytesInternal(message));
|
||||
// Parse memory.
|
||||
ASSIGN_OR_RETURN(memory_, message.ReadCapability(2, caps));
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
glcr::Status OpenFileResponse::ParseFromBytesInternal(const yunq::MessageView& message) {
|
||||
RETURN_ERROR(message.CheckHeader());
|
||||
// Parse path.
|
||||
ASSIGN_OR_RETURN(path_, message.ReadField<glcr::String>(0));
|
||||
// Parse size.
|
||||
ASSIGN_OR_RETURN(size_, message.ReadField<uint64_t>(1));
|
||||
// Parse memory.
|
||||
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
uint64_t OpenFileResponse::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const {
|
||||
yunq::Serializer serializer(bytes, offset, 3);
|
||||
return SerializeInternal(serializer);
|
||||
}
|
||||
|
||||
uint64_t OpenFileResponse::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const {
|
||||
yunq::Serializer serializer(bytes, offset, 3, caps);
|
||||
return SerializeInternal(serializer);
|
||||
}
|
||||
|
||||
uint64_t OpenFileResponse::SerializeInternal(yunq::Serializer& serializer) const {
|
||||
// Write path.
|
||||
serializer.WriteField<glcr::String>(0, path_);
|
||||
// Write size.
|
||||
serializer.WriteField<uint64_t>(1, size_);
|
||||
// Write memory.
|
||||
serializer.WriteCapability(2, memory_);
|
||||
|
||||
serializer.WriteHeader();
|
||||
|
||||
return serializer.size();
|
||||
}
|
||||
glcr::Status GetDirectoryRequest::ParseFromBytes(const yunq::MessageView& message) {
|
||||
RETURN_ERROR(ParseFromBytesInternal(message));
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
glcr::Status GetDirectoryRequest::ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer& caps) {
|
||||
RETURN_ERROR(ParseFromBytesInternal(message));
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
glcr::Status GetDirectoryRequest::ParseFromBytesInternal(const yunq::MessageView& message) {
|
||||
RETURN_ERROR(message.CheckHeader());
|
||||
// Parse path.
|
||||
ASSIGN_OR_RETURN(path_, message.ReadField<glcr::String>(0));
|
||||
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
uint64_t GetDirectoryRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const {
|
||||
yunq::Serializer serializer(bytes, offset, 1);
|
||||
return SerializeInternal(serializer);
|
||||
}
|
||||
|
||||
uint64_t GetDirectoryRequest::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const {
|
||||
yunq::Serializer serializer(bytes, offset, 1, caps);
|
||||
return SerializeInternal(serializer);
|
||||
}
|
||||
|
||||
uint64_t GetDirectoryRequest::SerializeInternal(yunq::Serializer& serializer) const {
|
||||
// Write path.
|
||||
serializer.WriteField<glcr::String>(0, path_);
|
||||
|
||||
serializer.WriteHeader();
|
||||
|
||||
return serializer.size();
|
||||
}
|
||||
glcr::Status Directory::ParseFromBytes(const yunq::MessageView& message) {
|
||||
RETURN_ERROR(ParseFromBytesInternal(message));
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
glcr::Status Directory::ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer& caps) {
|
||||
RETURN_ERROR(ParseFromBytesInternal(message));
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
glcr::Status Directory::ParseFromBytesInternal(const yunq::MessageView& message) {
|
||||
RETURN_ERROR(message.CheckHeader());
|
||||
// Parse filenames.
|
||||
ASSIGN_OR_RETURN(filenames_, message.ReadField<glcr::String>(0));
|
||||
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
uint64_t Directory::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset) const {
|
||||
yunq::Serializer serializer(bytes, offset, 1);
|
||||
return SerializeInternal(serializer);
|
||||
}
|
||||
|
||||
uint64_t Directory::SerializeToBytes(glcr::ByteBuffer& bytes, uint64_t offset, glcr::CapBuffer& caps) const {
|
||||
yunq::Serializer serializer(bytes, offset, 1, caps);
|
||||
return SerializeInternal(serializer);
|
||||
}
|
||||
|
||||
uint64_t Directory::SerializeInternal(yunq::Serializer& serializer) const {
|
||||
// Write filenames.
|
||||
serializer.WriteField<glcr::String>(0, filenames_);
|
||||
|
||||
serializer.WriteHeader();
|
||||
|
||||
return serializer.size();
|
||||
}
|
||||
|
|
@ -1,124 +0,0 @@
|
|||
// Generated file - DO NOT MODIFY
|
||||
#pragma once
|
||||
|
||||
#include <glacier/buffer/byte_buffer.h>
|
||||
#include <glacier/buffer/cap_buffer.h>
|
||||
#include <glacier/status/status.h>
|
||||
#include <glacier/container/vector.h>
|
||||
#include <glacier/string/string.h>
|
||||
#include <yunq/message_view.h>
|
||||
#include <yunq/serialize.h>
|
||||
#include <ztypes.h>
|
||||
|
||||
|
||||
class OpenFileRequest {
|
||||
public:
|
||||
OpenFileRequest() {}
|
||||
// Delete copy and move until implemented.
|
||||
OpenFileRequest(const OpenFileRequest&) = delete;
|
||||
OpenFileRequest(OpenFileRequest&&) = default;
|
||||
OpenFileRequest& operator=(OpenFileRequest&&) = default;
|
||||
|
||||
[[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message);
|
||||
[[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer&);
|
||||
uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const;
|
||||
uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const;
|
||||
|
||||
const glcr::String& path() const { return path_; }
|
||||
glcr::String& mutable_path() { return path_; }
|
||||
void set_path(const glcr::String& value) { path_ = value; }
|
||||
|
||||
private:
|
||||
glcr::String path_;
|
||||
|
||||
// Parses everything except for caps.
|
||||
glcr::Status ParseFromBytesInternal(const yunq::MessageView& message);
|
||||
|
||||
uint64_t SerializeInternal(yunq::Serializer& serializer) const;
|
||||
};
|
||||
class OpenFileResponse {
|
||||
public:
|
||||
OpenFileResponse() {}
|
||||
// Delete copy and move until implemented.
|
||||
OpenFileResponse(const OpenFileResponse&) = delete;
|
||||
OpenFileResponse(OpenFileResponse&&) = default;
|
||||
OpenFileResponse& operator=(OpenFileResponse&&) = default;
|
||||
|
||||
[[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message);
|
||||
[[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer&);
|
||||
uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const;
|
||||
uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const;
|
||||
|
||||
const glcr::String& path() const { return path_; }
|
||||
glcr::String& mutable_path() { return path_; }
|
||||
void set_path(const glcr::String& value) { path_ = value; }
|
||||
|
||||
const uint64_t& size() const { return size_; }
|
||||
uint64_t& mutable_size() { return size_; }
|
||||
void set_size(const uint64_t& value) { size_ = value; }
|
||||
|
||||
const z_cap_t& memory() const { return memory_; }
|
||||
z_cap_t& mutable_memory() { return memory_; }
|
||||
void set_memory(const z_cap_t& value) { memory_ = value; }
|
||||
|
||||
private:
|
||||
glcr::String path_;
|
||||
uint64_t size_;
|
||||
z_cap_t memory_;
|
||||
|
||||
// Parses everything except for caps.
|
||||
glcr::Status ParseFromBytesInternal(const yunq::MessageView& message);
|
||||
|
||||
uint64_t SerializeInternal(yunq::Serializer& serializer) const;
|
||||
};
|
||||
class GetDirectoryRequest {
|
||||
public:
|
||||
GetDirectoryRequest() {}
|
||||
// Delete copy and move until implemented.
|
||||
GetDirectoryRequest(const GetDirectoryRequest&) = delete;
|
||||
GetDirectoryRequest(GetDirectoryRequest&&) = default;
|
||||
GetDirectoryRequest& operator=(GetDirectoryRequest&&) = default;
|
||||
|
||||
[[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message);
|
||||
[[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer&);
|
||||
uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const;
|
||||
uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const;
|
||||
|
||||
const glcr::String& path() const { return path_; }
|
||||
glcr::String& mutable_path() { return path_; }
|
||||
void set_path(const glcr::String& value) { path_ = value; }
|
||||
|
||||
private:
|
||||
glcr::String path_;
|
||||
|
||||
// Parses everything except for caps.
|
||||
glcr::Status ParseFromBytesInternal(const yunq::MessageView& message);
|
||||
|
||||
uint64_t SerializeInternal(yunq::Serializer& serializer) const;
|
||||
};
|
||||
class Directory {
|
||||
public:
|
||||
Directory() {}
|
||||
// Delete copy and move until implemented.
|
||||
Directory(const Directory&) = delete;
|
||||
Directory(Directory&&) = default;
|
||||
Directory& operator=(Directory&&) = default;
|
||||
|
||||
[[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message);
|
||||
[[nodiscard]] glcr::Status ParseFromBytes(const yunq::MessageView& message, const glcr::CapBuffer&);
|
||||
uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset) const;
|
||||
uint64_t SerializeToBytes(glcr::ByteBuffer&, uint64_t offset, glcr::CapBuffer&) const;
|
||||
|
||||
const glcr::String& filenames() const { return filenames_; }
|
||||
glcr::String& mutable_filenames() { return filenames_; }
|
||||
void set_filenames(const glcr::String& value) { filenames_ = value; }
|
||||
|
||||
private:
|
||||
glcr::String filenames_;
|
||||
|
||||
// Parses everything except for caps.
|
||||
glcr::Status ParseFromBytesInternal(const yunq::MessageView& message);
|
||||
|
||||
uint64_t SerializeInternal(yunq::Serializer& serializer) const;
|
||||
};
|
||||
|
|
@ -1,152 +0,0 @@
|
|||
// Generated file -- DO NOT MODIFY.
|
||||
#include "victoriafalls.yunq.server.h"
|
||||
|
||||
#include <mammoth/util/debug.h>
|
||||
#include <zcall.h>
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
const uint32_t kSentinel = 0xBEEFDEAD;
|
||||
const uint32_t kHeaderSize = 0x10;
|
||||
|
||||
void WriteError(glcr::ByteBuffer& buffer, glcr::ErrorCode err) {
|
||||
buffer.WriteAt<uint32_t>(0, kSentinel);
|
||||
buffer.WriteAt<uint32_t>(4, kHeaderSize);
|
||||
buffer.WriteAt<uint64_t>(8, err);
|
||||
}
|
||||
|
||||
void WriteHeader(glcr::ByteBuffer& buffer, uint64_t message_length) {
|
||||
buffer.WriteAt<uint32_t>(0, kSentinel);
|
||||
buffer.WriteAt<uint32_t>(4, kHeaderSize + message_length);
|
||||
buffer.WriteAt<uint64_t>(8, glcr::OK);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
|
||||
void VFSServerBaseThreadBootstrap(void* server_base) {
|
||||
((VFSServerBase*)server_base)->ServerThread();
|
||||
}
|
||||
|
||||
VFSServerBase::~VFSServerBase() {
|
||||
if (endpoint_ != 0) {
|
||||
check(ZCapRelease(endpoint_));
|
||||
}
|
||||
}
|
||||
|
||||
glcr::ErrorOr<z_cap_t> VFSServerBase::CreateClientCap() {
|
||||
uint64_t client_cap;
|
||||
RET_ERR(ZCapDuplicate(endpoint_, ~(kZionPerm_Read), &client_cap));
|
||||
return client_cap;
|
||||
}
|
||||
|
||||
glcr::ErrorOr<VFSClient> VFSServerBase::CreateClient() {
|
||||
uint64_t client_cap;
|
||||
RET_ERR(ZCapDuplicate(endpoint_, ~(kZionPerm_Read), &client_cap));
|
||||
return VFSClient(client_cap);
|
||||
}
|
||||
|
||||
Thread VFSServerBase::RunServer() {
|
||||
return Thread(VFSServerBaseThreadBootstrap, this);
|
||||
}
|
||||
|
||||
void VFSServerBase::ServerThread() {
|
||||
glcr::ByteBuffer recv_buffer(0x1000);
|
||||
glcr::CapBuffer recv_cap(0x10);
|
||||
glcr::ByteBuffer resp_buffer(0x1000);
|
||||
glcr::CapBuffer resp_cap(0x10);
|
||||
z_cap_t reply_port_cap;
|
||||
|
||||
while (true) {
|
||||
uint64_t recv_cap_size = 0x10;
|
||||
uint64_t recv_buf_size = 0x1000;
|
||||
recv_cap.Reset();
|
||||
glcr::ErrorCode recv_err = static_cast<glcr::ErrorCode>(ZEndpointRecv(endpoint_, &recv_buf_size, recv_buffer.RawPtr(), &recv_cap_size, recv_cap.RawPtr(), &reply_port_cap));
|
||||
if (recv_err != glcr::OK) {
|
||||
dbgln("Error in receive: {x}", recv_err);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t resp_length = 0;
|
||||
|
||||
glcr::ErrorCode reply_err = glcr::OK;
|
||||
resp_cap.Reset();
|
||||
glcr::Status err = HandleRequest(recv_buffer, recv_cap, resp_buffer, resp_length, resp_cap);
|
||||
if (!err) {
|
||||
WriteError(resp_buffer, err.code());
|
||||
dbgln("Responding Error {}", err.message());
|
||||
reply_err = static_cast<glcr::ErrorCode>(ZReplyPortSend(reply_port_cap, kHeaderSize, resp_buffer.RawPtr(), 0, nullptr));
|
||||
} else {
|
||||
WriteHeader(resp_buffer, resp_length);
|
||||
reply_err = static_cast<glcr::ErrorCode>(ZReplyPortSend(reply_port_cap, kHeaderSize + resp_length, resp_buffer.RawPtr(), resp_cap.UsedSlots(), resp_cap.RawPtr()));
|
||||
}
|
||||
if (reply_err != glcr::OK) {
|
||||
dbgln("Error in reply: {x}", reply_err);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
glcr::Status VFSServerBase::HandleRequest(const glcr::ByteBuffer& request,
|
||||
const glcr::CapBuffer& req_caps,
|
||||
glcr::ByteBuffer& response, uint64_t& resp_length,
|
||||
glcr::CapBuffer& resp_caps) {
|
||||
if (request.At<uint32_t>(0) != kSentinel) {
|
||||
return glcr::InvalidArgument("Request Not Valid");
|
||||
}
|
||||
|
||||
uint64_t method_select = request.At<uint64_t>(8);
|
||||
|
||||
switch(method_select) {
|
||||
case 0: {
|
||||
|
||||
|
||||
OpenFileRequest yunq_request;
|
||||
yunq::MessageView request_view(request, kHeaderSize);
|
||||
RETURN_ERROR(yunq_request.ParseFromBytes(request_view, req_caps));
|
||||
|
||||
|
||||
|
||||
OpenFileResponse yunq_response;
|
||||
|
||||
|
||||
|
||||
RETURN_ERROR(HandleOpenFile(yunq_request, yunq_response));
|
||||
|
||||
|
||||
|
||||
resp_length = yunq_response.SerializeToBytes(response, kHeaderSize, resp_caps);
|
||||
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
|
||||
|
||||
GetDirectoryRequest yunq_request;
|
||||
yunq::MessageView request_view(request, kHeaderSize);
|
||||
RETURN_ERROR(yunq_request.ParseFromBytes(request_view, req_caps));
|
||||
|
||||
|
||||
|
||||
Directory yunq_response;
|
||||
|
||||
|
||||
|
||||
RETURN_ERROR(HandleGetDirectory(yunq_request, yunq_response));
|
||||
|
||||
|
||||
|
||||
resp_length = yunq_response.SerializeToBytes(response, kHeaderSize, resp_caps);
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return glcr::Unimplemented("Method unimplemented by server.");
|
||||
}
|
||||
}
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
// Generated File -- DO NOT MODIFY.
|
||||
#pragma once
|
||||
|
||||
#include <glacier/status/error_or.h>
|
||||
#include <glacier/status/status.h>
|
||||
#include <mammoth/proc/thread.h>
|
||||
#include <ztypes.h>
|
||||
|
||||
#include "victoriafalls.yunq.h"
|
||||
#include "victoriafalls.yunq.client.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class VFSServerBase {
|
||||
public:
|
||||
VFSServerBase(z_cap_t VFS_cap) : endpoint_(VFS_cap) {}
|
||||
VFSServerBase(const VFSServerBase&) = delete;
|
||||
VFSServerBase(VFSServerBase&&) = delete;
|
||||
virtual ~VFSServerBase();
|
||||
|
||||
glcr::ErrorOr<z_cap_t> CreateClientCap();
|
||||
glcr::ErrorOr<VFSClient> CreateClient();
|
||||
|
||||
[[nodiscard]] Thread RunServer();
|
||||
|
||||
|
||||
|
||||
[[nodiscard]] virtual glcr::Status HandleOpenFile(const OpenFileRequest&, OpenFileResponse&) = 0;
|
||||
|
||||
|
||||
|
||||
[[nodiscard]] virtual glcr::Status HandleGetDirectory(const GetDirectoryRequest&, Directory&) = 0;
|
||||
|
||||
|
||||
|
||||
private:
|
||||
z_cap_t endpoint_;
|
||||
|
||||
friend void VFSServerBaseThreadBootstrap(void*);
|
||||
void ServerThread();
|
||||
|
||||
[[nodiscard]] glcr::Status HandleRequest(const glcr::ByteBuffer& request, const glcr::CapBuffer& req_caps,
|
||||
glcr::ByteBuffer& response, uint64_t& resp_length,
|
||||
glcr::CapBuffer& resp_caps);
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
#include <mammoth/util/debug.h>
|
||||
#include <mammoth/util/init.h>
|
||||
#include <yellowstone/yellowstone.yunq.client.h>
|
||||
|
||||
#include "fs/ext2/ext2_driver.h"
|
||||
#include "victoriafalls_server.h"
|
||||
|
||||
using yellowstone::DenaliInfo;
|
||||
using yellowstone::RegisterEndpointRequest;
|
||||
using yellowstone::YellowstoneClient;
|
||||
|
||||
uint64_t main(uint64_t init_cap) {
|
||||
ParseInitPort(init_cap);
|
||||
|
||||
dbgln("VFs Started");
|
||||
|
||||
YellowstoneClient yellowstone(gInitEndpointCap);
|
||||
DenaliInfo denali_info;
|
||||
check(yellowstone.GetDenali(denali_info));
|
||||
ASSIGN_OR_RETURN(Ext2Driver ext2, Ext2Driver::Init(denali_info));
|
||||
|
||||
ASSIGN_OR_RETURN(auto server, VFSServer::Create(ext2));
|
||||
|
||||
Thread server_thread = server->RunServer();
|
||||
|
||||
RegisterEndpointRequest req;
|
||||
req.set_endpoint_name("victoriafalls");
|
||||
ASSIGN_OR_RETURN(auto client_cap, server->CreateClientCap());
|
||||
req.set_endpoint_capability(client_cap);
|
||||
check(yellowstone.RegisterEndpoint(req));
|
||||
|
||||
RET_ERR(server_thread.Join());
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
#include "victoriafalls_server.h"
|
||||
|
||||
#include <glacier/string/str_split.h>
|
||||
#include <mammoth/util/debug.h>
|
||||
#include <zcall.h>
|
||||
|
||||
glcr::ErrorOr<glcr::UniquePtr<VFSServer>> VFSServer::Create(
|
||||
Ext2Driver& driver) {
|
||||
z_cap_t endpoint_cap;
|
||||
RET_ERR(ZEndpointCreate(&endpoint_cap));
|
||||
return glcr::UniquePtr<VFSServer>(new VFSServer(endpoint_cap, driver));
|
||||
}
|
||||
|
||||
glcr::Status VFSServer::HandleOpenFile(const OpenFileRequest& request,
|
||||
OpenFileResponse& response) {
|
||||
auto path_tokens = glcr::StrSplit(request.path(), '/');
|
||||
// Require all paths to be absolute rather than relative.
|
||||
// If the path starts with '/' then the first token will be empty.
|
||||
if (path_tokens.at(0) != "") {
|
||||
return glcr::InvalidArgument("Open file supports only absolute paths.");
|
||||
}
|
||||
|
||||
ASSIGN_OR_RETURN(auto files, driver_.ReadDirectory(2));
|
||||
for (uint64_t i = 1; i < path_tokens.size() - 1; i++) {
|
||||
bool found_token = false;
|
||||
for (uint64_t j = 0; j < files.size() && !found_token; j++) {
|
||||
if (path_tokens.at(i) ==
|
||||
glcr::StringView(files.at(j).name, files.at(j).name_len)) {
|
||||
ASSIGN_OR_RETURN(files, driver_.ReadDirectory(files.at(j).inode));
|
||||
found_token = true;
|
||||
}
|
||||
}
|
||||
if (!found_token) {
|
||||
return glcr::NotFound(glcr::StrFormat("Directory '{}' not found.",
|
||||
glcr::String(path_tokens.at(i))));
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t inode_num;
|
||||
mmth::OwnedMemoryRegion region;
|
||||
for (uint64_t j = 0; j < files.size(); j++) {
|
||||
if (path_tokens.at(path_tokens.size() - 1) ==
|
||||
glcr::StringView(files.at(j).name, files.at(j).name_len)) {
|
||||
inode_num = files.at(j).inode;
|
||||
ASSIGN_OR_RETURN(region, driver_.ReadFile(files.at(j).inode));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!region) {
|
||||
return glcr::NotFound(
|
||||
glcr::StrFormat("File '{}' not found.",
|
||||
glcr::String(path_tokens.at(path_tokens.size() - 1))));
|
||||
}
|
||||
|
||||
response.set_path(request.path());
|
||||
// FIXME: There isn't really a reason we need to map the file into memory then
|
||||
// duplicate the cap. In the future just get the cap from the read then pass
|
||||
// it to the caller directly.
|
||||
response.set_memory(region.DuplicateCap());
|
||||
// TODO: Consider folding this up into the actual read call.
|
||||
ASSIGN_OR_RETURN(Inode * inode, driver_.GetInode(inode_num));
|
||||
// FIXME: This technically only sets the lower 32 bits.
|
||||
response.set_size(inode->size);
|
||||
return glcr::Status::Ok();
|
||||
}
|
||||
|
||||
glcr::Status VFSServer::HandleGetDirectory(const GetDirectoryRequest& request,
|
||||
Directory& response) {
|
||||
auto path_tokens = glcr::StrSplit(request.path(), '/');
|
||||
|
||||
if (path_tokens.at(0) != "") {
|
||||
return glcr::InvalidArgument("Get Directory only supports absolute path.");
|
||||
}
|
||||
|
||||
// If there is a trailing slash we can get rid of the empty string.
|
||||
if (path_tokens.at(path_tokens.size() - 1) == "") {
|
||||
path_tokens.PopBack();
|
||||
}
|
||||
|
||||
ASSIGN_OR_RETURN(auto files, driver_.ReadDirectory(2));
|
||||
for (uint64_t i = 1; i < path_tokens.size(); i++) {
|
||||
bool found_token = false;
|
||||
for (uint64_t j = 0; j < files.size() && !found_token; j++) {
|
||||
if (path_tokens.at(i) ==
|
||||
glcr::StringView(files.at(j).name, files.at(j).name_len)) {
|
||||
ASSIGN_OR_RETURN(files, driver_.ReadDirectory(files.at(j).inode));
|
||||
found_token = true;
|
||||
}
|
||||
}
|
||||
if (!found_token) {
|
||||
return glcr::NotFound(glcr::StrFormat("Directory '{}' not found.",
|
||||
glcr::String(path_tokens.at(i))));
|
||||
}
|
||||
}
|
||||
|
||||
glcr::VariableStringBuilder filelist;
|
||||
for (const DirEntry& file : files) {
|
||||
filelist.PushBack(glcr::StringView(file.name, file.name_len));
|
||||
filelist.PushBack(',');
|
||||
}
|
||||
// Remove trailing comma.
|
||||
if (filelist.size() > 0) {
|
||||
filelist.DeleteLast();
|
||||
}
|
||||
|
||||
response.set_filenames(filelist.ToString());
|
||||
|
||||
return glcr::Status::Ok();
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <glacier/memory/unique_ptr.h>
|
||||
|
||||
#include "fs/ext2/ext2_driver.h"
|
||||
#include "victoriafalls/victoriafalls.yunq.server.h"
|
||||
|
||||
class VFSServer : public VFSServerBase {
|
||||
public:
|
||||
static glcr::ErrorOr<glcr::UniquePtr<VFSServer>> Create(Ext2Driver& driver);
|
||||
|
||||
glcr::Status HandleOpenFile(const OpenFileRequest&,
|
||||
OpenFileResponse&) override;
|
||||
|
||||
glcr::Status HandleGetDirectory(const GetDirectoryRequest&,
|
||||
Directory&) override;
|
||||
|
||||
private:
|
||||
// FIXME: Don't store this as a reference.
|
||||
Ext2Driver& driver_;
|
||||
|
||||
VFSServer(z_cap_t endpoint_cap, Ext2Driver& driver)
|
||||
: VFSServerBase(endpoint_cap), driver_(driver) {}
|
||||
};
|
|
@ -34,7 +34,7 @@ fn serialize_field(field: &Field) -> proc_macro2::TokenStream {
|
|||
{
|
||||
let rep_offset = next_extension;
|
||||
let rep_len = self.#name.len() as u32;
|
||||
next_extension = yunq::message::serialize_repeated(buf, next_extension as usize, &self.#name)? as u32;
|
||||
next_extension = yunq::message::serialize_repeated(buf, offset + next_extension as usize, &self.#name)? as u32;
|
||||
|
||||
buf.write_at(yunq::message::field_offset(offset, #ind), rep_offset)?;
|
||||
buf.write_at(yunq::message::field_offset(offset, #ind) + 4, rep_len)?;
|
||||
|
@ -46,7 +46,7 @@ fn serialize_field(field: &Field) -> proc_macro2::TokenStream {
|
|||
{
|
||||
let rep_offset = next_extension;
|
||||
let rep_len = self.#name.len() as u32;
|
||||
next_extension = yunq::message::serialize_repeated_message(buf, next_extension as usize, &self.#name, caps)? as u32;
|
||||
next_extension = yunq::message::serialize_repeated_message(buf, offset + next_extension as usize, &self.#name, caps)? as u32;
|
||||
|
||||
buf.write_at(yunq::message::field_offset(offset, #ind), rep_offset)?;
|
||||
buf.write_at(yunq::message::field_offset(offset, #ind) + 4, rep_len)?;
|
||||
|
@ -232,7 +232,7 @@ fn generate_message(message: &Message) -> TokenStream {
|
|||
let serialize = generate_serialize(message);
|
||||
let parse = generate_parse(message);
|
||||
quote! {
|
||||
#[derive(PartialEq, Eq)]
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct #name {
|
||||
#(pub #field_names: #field_types),*
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue