// Bounded read-only directory cache populated from actual TurkFS block reads. use super::*; use turkos_kernel::runtime::syscall_abi::Errno; use turkos_kernel::turkfs::{self, RootDirectory, RootEntry}; #[derive(Clone, Copy)] struct Open { fd: u64, cursor: usize } pub(super) struct LsDisk { root: RootDirectory, child: RootDirectory, inode: [u8; 512], open: Option, next_fd: u64, } impl LsDisk { pub(super) fn load(boot: &BootData, allocator: &mut memory::FrameAllocator) -> Result { let mut disk = virtio_ro_turkfs::RoDisk::open( boot, allocator, "TURKOS/OS/K1295L-DISK-READ")?; let result = Self::load_open(&mut disk); disk.close(allocator)?; result } fn load_open(disk: &mut virtio_ro_turkfs::RoDisk) -> Result { let mut superblock = [0u8; 512]; disk.read(turkfs::SUPERBLOCK_LBA, &mut superblock)?; turkfs::require_superblock_signature(&superblock) .map_err(|_| "ls signature")?; turkfs::require_format_version(&superblock) .map_err(|_| "ls version")?; let geometry = turkfs::require_block_geometry( &superblock, disk.capacity().checked_mul(512).ok_or("ls capacity")?) .map_err(|_| "ls geometry")?; let (root_lba, root_count) = turkfs::root_directory_location(&superblock, geometry) .map_err(|_| "ls root location")?; let inode_lba = turkfs::inode_table_location( &superblock, geometry, root_lba).map_err(|_| "ls inode location")?; let mut root_block = [0u8; 512]; let mut inode = [0u8; 512]; disk.read(root_lba, &mut root_block)?; disk.read(inode_lba, &mut inode)?; let root = turkfs::parse_root_directory(root_lba, root_count, &root_block) .map_err(|_| "ls root")?; turkfs::require_utf8_names(&root).map_err(|_| "ls root names")?; if root.count != 6 { return Err("ls root count"); } for (index, expected) in [b"README".as_slice(), b"KERNEL", b"MULTI", b"TAIL", b"ONE", b"SUB"].iter().enumerate() { if root.entries[index].name_len as usize != expected.len() || &root.entries[index].name[..expected.len()] != *expected { return Err("ls root order"); } } let (child_lba, child_count) = turkfs::known_child_directory_descriptor( &root, &inode, b"SUB", geometry).map_err(|_| "ls child descriptor")?; if child_lba == root_lba || child_lba == inode_lba || child_count != 2 { return Err("ls child extent"); } let mut child_block = [0u8; 512]; disk.read(child_lba, &mut child_block)?; let child = turkfs::parse_root_directory(child_lba, child_count, &child_block) .map_err(|_| "ls child")?; turkfs::require_utf8_names(&child).map_err(|_| "ls child names")?; for (index, expected) in [b"INNER".as_slice(), b"HIDDEN"].iter().enumerate() { if child.entries[index].name_len as usize != expected.len() || &child.entries[index].name[..expected.len()] != *expected { return Err("ls child order"); } } for entry in root.entries[..root.count].iter().chain( child.entries[..child.count].iter()) { let index = usize::try_from(entry.inode).map_err(|_| "ls inode id")?; if index == 0 || index > 16 { return Err("ls inode range"); } let offset = (index - 1) * 32; if u32::from_le_bytes(inode[offset..offset + 4].try_into() .map_err(|_| "ls inode bytes")?) != entry.inode { return Err("ls inode mismatch"); } } Ok(Self {root, child, inode, open: None, next_fd: 0x1295_0001}) } pub(super) fn open(&mut self, path: &[u8]) -> Result { if path == b"/SUB" { return Err(Errno::Access); } if path != b"/" { return Err(Errno::NoEnt); } if self.open.is_some() { return Err(Errno::Access); } let fd = self.next_fd; self.next_fd = fd.checked_add(1).ok_or(Errno::Overflow)?; self.open = Some(Open {fd, cursor: 0}); Ok(fd) } pub(super) fn next(&mut self, fd: u64) -> Result, Errno> { let mut open = self.open.ok_or(Errno::BadFd)?; if fd != open.fd { return Err(Errno::BadFd); } if open.cursor == self.root.count { return Ok(None); } let entry = self.root.entries[open.cursor]; let offset = (entry.inode as usize - 1) * 32; let kind = u32::from_le_bytes(self.inode[offset + 4..offset + 8] .try_into().map_err(|_| Errno::Io)?); let kind = match kind { 0 => 0, 2 => 1, _ => return Err(Errno::Io) }; open.cursor += 1; self.open = Some(open); Ok(Some((entry, kind))) } pub(super) fn close(&mut self, fd: u64) -> Result<(), Errno> { if self.open.map(|open| open.fd) != Some(fd) { return Err(Errno::BadFd); } self.open = None; Ok(()) } pub(super) const fn owner_released(&self) -> bool { self.open.is_none() } pub(super) fn child_verified(&self) -> bool { self.child.count == 2 && self.child.find(b"INNER").is_some() && self.child.find(b"HIDDEN").is_some() } }