Nothing to see here, move along
at main 489 lines 17 kB view raw
1#![allow(clippy::missing_safety_doc)] 2 3use lancer_core::packet_ring::{PacketRingReader, PacketRingWriter}; 4use lancer_vfs_proto::{ 5 VFS_STATUS_CORRUPT, VFS_STATUS_CROSS_DEVICE, VFS_STATUS_DIR_NOT_EMPTY, VFS_STATUS_DISK_FULL, 6 VFS_STATUS_FILE_EXISTS, VFS_STATUS_HANDLE_TABLE_FULL, VFS_STATUS_INTEGRITY_ERROR, 7 VFS_STATUS_INVALID_HANDLE, VFS_STATUS_INVALID_OP, VFS_STATUS_IO_ERROR, 8 VFS_STATUS_IS_A_DIRECTORY, VFS_STATUS_NAME_TOO_LONG, VFS_STATUS_NOT_A_DIRECTORY, 9 VFS_STATUS_NOT_A_FILE, VFS_STATUS_NOT_FOUND, VFS_STATUS_OK, VFS_STATUS_PATH_TOO_DEEP, 10 VFS_STATUS_PERMISSION_DENIED, VFS_STATUS_STALE_CAP, VFS_STATUS_SYMLINK_DEPTH, 11 VFS_STATUS_UNSUPPORTED, VfsRequest, VfsResponse, 12}; 13 14pub use lancer_vfs_proto::{ 15 FsRights, MOUNT_INFO_ENTRY_SIZE, MountInfoEntry, READDIR_ENTRY_SIZE, ReadDirEntry, 16}; 17 18use crate::syscall; 19 20pub const VFS_RING_VADDR: u64 = 0x7000_0000; 21pub const VFS_RING_BASE_SLOT: u64 = 11; 22pub const VFS_RING_FRAME_COUNT: u64 = 16; 23pub const CLIENT_NOTIF_SLOT: u8 = 28; 24pub const VFS_NOTIF_SLOT: u8 = 29; 25 26pub const RING_HALF_SIZE: usize = 4096; 27pub const RING_DATA_OFFSET: usize = 8192; 28pub const RING_DATA_PAGES: usize = 14; 29const RING_DATA_SIZE: usize = RING_DATA_PAGES * 4096; 30const REQUEST_SLOT_SIZE: u32 = 48; 31const PUSH_RETRY_LIMIT: u32 = 100_000; 32const POLL_RETRY_LIMIT: u32 = 100_000; 33const MAX_PATH_COMPONENTS: usize = 32; 34 35#[derive(Debug, Clone, Copy, PartialEq, Eq)] 36#[repr(u8)] 37pub enum FsStatus { 38 NotFound = 1, 39 PermissionDenied = 2, 40 InvalidHandle = 3, 41 HandleTableFull = 4, 42 IoError = 5, 43 DiskFull = 6, 44 NotADirectory = 7, 45 NotAFile = 8, 46 IsADirectory = 9, 47 DirNotEmpty = 10, 48 FileExists = 11, 49 NameTooLong = 12, 50 PathTooDeep = 13, 51 IntegrityError = 14, 52 StaleCap = 15, 53 InvalidOp = 16, 54 SymlinkDepth = 17, 55 Corrupt = 18, 56 Unsupported = 19, 57 CrossDevice = 20, 58 Unknown = 255, 59} 60 61impl FsStatus { 62 pub fn from_u8(v: u8) -> Self { 63 match v { 64 0 => Self::Unknown, 65 VFS_STATUS_NOT_FOUND => Self::NotFound, 66 VFS_STATUS_PERMISSION_DENIED => Self::PermissionDenied, 67 VFS_STATUS_INVALID_HANDLE => Self::InvalidHandle, 68 VFS_STATUS_HANDLE_TABLE_FULL => Self::HandleTableFull, 69 VFS_STATUS_IO_ERROR => Self::IoError, 70 VFS_STATUS_DISK_FULL => Self::DiskFull, 71 VFS_STATUS_NOT_A_DIRECTORY => Self::NotADirectory, 72 VFS_STATUS_NOT_A_FILE => Self::NotAFile, 73 VFS_STATUS_IS_A_DIRECTORY => Self::IsADirectory, 74 VFS_STATUS_DIR_NOT_EMPTY => Self::DirNotEmpty, 75 VFS_STATUS_FILE_EXISTS => Self::FileExists, 76 VFS_STATUS_NAME_TOO_LONG => Self::NameTooLong, 77 VFS_STATUS_PATH_TOO_DEEP => Self::PathTooDeep, 78 VFS_STATUS_INTEGRITY_ERROR => Self::IntegrityError, 79 VFS_STATUS_STALE_CAP => Self::StaleCap, 80 VFS_STATUS_INVALID_OP => Self::InvalidOp, 81 VFS_STATUS_SYMLINK_DEPTH => Self::SymlinkDepth, 82 VFS_STATUS_CORRUPT => Self::Corrupt, 83 VFS_STATUS_UNSUPPORTED => Self::Unsupported, 84 VFS_STATUS_CROSS_DEVICE => Self::CrossDevice, 85 _ => Self::Unknown, 86 } 87 } 88 89 pub fn name(self) -> &'static str { 90 match self { 91 Self::NotFound => "NotFound", 92 Self::PermissionDenied => "PermissionDenied", 93 Self::InvalidHandle => "InvalidHandle", 94 Self::HandleTableFull => "HandleTableFull", 95 Self::IoError => "IoError", 96 Self::DiskFull => "DiskFull", 97 Self::NotADirectory => "NotADirectory", 98 Self::NotAFile => "NotAFile", 99 Self::IsADirectory => "IsADirectory", 100 Self::DirNotEmpty => "DirNotEmpty", 101 Self::FileExists => "FileExists", 102 Self::NameTooLong => "NameTooLong", 103 Self::PathTooDeep => "PathTooDeep", 104 Self::IntegrityError => "IntegrityError", 105 Self::StaleCap => "StaleCap", 106 Self::InvalidOp => "InvalidOp", 107 Self::SymlinkDepth => "SymlinkDepth", 108 Self::Corrupt => "Corrupt", 109 Self::Unsupported => "Unsupported", 110 Self::CrossDevice => "CrossDevice", 111 Self::Unknown => "Unknown", 112 } 113 } 114} 115 116#[derive(Debug, Clone, Copy)] 117pub struct FileStat { 118 pub size: u64, 119 pub inode_type: u8, 120 pub block_count: u64, 121} 122 123impl FileStat { 124 pub fn from_response(val0: u64, val1: u64) -> Self { 125 Self { 126 size: val0, 127 inode_type: (val1 >> 48) as u8, 128 block_count: val1 & 0xFFFF_FFFF_FFFF, 129 } 130 } 131} 132 133pub struct FsClient { 134 request_ring: PacketRingWriter, 135 response_ring: PacketRingReader, 136 data_area: *mut u8, 137 data_area_size: usize, 138 next_tag: u32, 139 vfs_notif_slot: u8, 140 client_notif_slot: u8, 141} 142 143impl FsClient { 144 pub unsafe fn new(ring_vaddr: usize, vfs_notif: u8, client_notif: u8) -> Self { 145 let base = ring_vaddr as *mut u8; 146 let request_ring = 147 unsafe { PacketRingWriter::init(base, RING_HALF_SIZE, REQUEST_SLOT_SIZE) }; 148 let response_ring = 149 unsafe { PacketRingReader::attach(base.wrapping_add(RING_HALF_SIZE), RING_HALF_SIZE) }; 150 let data_area = (ring_vaddr + RING_DATA_OFFSET) as *mut u8; 151 Self { 152 request_ring, 153 response_ring, 154 data_area, 155 data_area_size: RING_DATA_SIZE, 156 next_tag: 1, 157 vfs_notif_slot: vfs_notif, 158 client_notif_slot: client_notif, 159 } 160 } 161 162 pub unsafe fn reattach(ring_vaddr: usize, vfs_notif: u8, client_notif: u8) -> Self { 163 let base = ring_vaddr as *mut u8; 164 let request_ring = unsafe { PacketRingWriter::attach(base, RING_HALF_SIZE) }; 165 let response_ring = 166 unsafe { PacketRingReader::attach(base.wrapping_add(RING_HALF_SIZE), RING_HALF_SIZE) }; 167 let data_area = (ring_vaddr + RING_DATA_OFFSET) as *mut u8; 168 Self { 169 request_ring, 170 response_ring, 171 data_area, 172 data_area_size: RING_DATA_SIZE, 173 next_tag: 1, 174 vfs_notif_slot: vfs_notif, 175 client_notif_slot: client_notif, 176 } 177 } 178 179 fn alloc_tag(&mut self) -> u32 { 180 let tag = self.next_tag; 181 self.next_tag = self.next_tag.wrapping_add(1); 182 tag 183 } 184 185 fn write_data(&self, offset: usize, data: &[u8]) { 186 let len = data.len().min(self.data_area_size.saturating_sub(offset)); 187 unsafe { 188 core::ptr::copy_nonoverlapping(data.as_ptr(), self.data_area.add(offset), len); 189 } 190 } 191 192 fn read_data(&self, offset: usize, len: usize) -> &[u8] { 193 let actual = len.min(self.data_area_size.saturating_sub(offset)); 194 unsafe { core::slice::from_raw_parts(self.data_area.add(offset), actual) } 195 } 196 197 fn send_request(&mut self, req: &VfsRequest) -> Result<VfsResponse, FsStatus> { 198 let data = req.as_bytes(); 199 let pushed = (0..PUSH_RETRY_LIMIT).any(|_| self.request_ring.try_push(data)); 200 if !pushed { 201 return Err(FsStatus::IoError); 202 } 203 syscall::notify_signal(self.vfs_notif_slot as u64, 1); 204 205 let tag = req.tag; 206 let mut buf = [0u8; 24]; 207 (0..POLL_RETRY_LIMIT) 208 .find_map(|_| match self.response_ring.try_pop(&mut buf) { 209 Some(len) if len >= VfsResponse::SIZE => { 210 let resp = VfsResponse::from_bytes(&buf)?; 211 match resp.tag == tag { 212 true => Some(resp), 213 false => None, 214 } 215 } 216 _ => { 217 syscall::notify_wait(self.client_notif_slot as u64); 218 None 219 } 220 }) 221 .ok_or(FsStatus::IoError) 222 } 223 224 fn check_response(resp: &VfsResponse) -> Result<(), FsStatus> { 225 match resp.status { 226 VFS_STATUS_OK => Ok(()), 227 s => Err(FsStatus::from_u8(s)), 228 } 229 } 230 231 pub fn open(&mut self, dir_handle: u8, name: &[u8], mode: FsRights) -> Result<u8, FsStatus> { 232 self.write_data(0, name); 233 let tag = self.alloc_tag(); 234 let req = VfsRequest::open(tag, dir_handle, mode, 0, name.len() as u64); 235 let resp = self.send_request(&req)?; 236 Self::check_response(&resp)?; 237 Ok(resp.val0 as u8) 238 } 239 240 pub fn close(&mut self, handle: u8) -> Result<(), FsStatus> { 241 let tag = self.alloc_tag(); 242 let req = VfsRequest::close(tag, handle); 243 let resp = self.send_request(&req)?; 244 Self::check_response(&resp) 245 } 246 247 pub fn read(&mut self, handle: u8, offset: u64, buf: &mut [u8]) -> Result<usize, FsStatus> { 248 let req_len = buf.len().min(self.data_area_size) as u32; 249 let tag = self.alloc_tag(); 250 let req = VfsRequest::read(tag, handle, offset, req_len, 0); 251 let resp = self.send_request(&req)?; 252 Self::check_response(&resp)?; 253 let n = resp.val0 as usize; 254 let src = self.read_data(0, n); 255 let copy_len = src.len().min(buf.len()); 256 buf[..copy_len].copy_from_slice(&src[..copy_len]); 257 Ok(copy_len) 258 } 259 260 pub fn write(&mut self, handle: u8, offset: u64, data: &[u8]) -> Result<usize, FsStatus> { 261 let actual_len = data.len().min(self.data_area_size); 262 self.write_data(0, &data[..actual_len]); 263 let tag = self.alloc_tag(); 264 let req = VfsRequest::write(tag, handle, offset, actual_len as u32, 0); 265 let resp = self.send_request(&req)?; 266 Self::check_response(&resp)?; 267 Ok(resp.val0 as usize) 268 } 269 270 pub fn stat(&mut self, handle: u8) -> Result<FileStat, FsStatus> { 271 let tag = self.alloc_tag(); 272 let req = VfsRequest::stat(tag, handle); 273 let resp = self.send_request(&req)?; 274 Self::check_response(&resp)?; 275 Ok(FileStat::from_response(resp.val0, resp.val1)) 276 } 277 278 pub fn mkdir(&mut self, dir_handle: u8, name: &[u8]) -> Result<(), FsStatus> { 279 self.write_data(0, name); 280 let tag = self.alloc_tag(); 281 let req = VfsRequest::mkdir(tag, dir_handle, 0, name.len() as u64); 282 let resp = self.send_request(&req)?; 283 Self::check_response(&resp) 284 } 285 286 pub fn unlink(&mut self, dir_handle: u8, name: &[u8]) -> Result<(), FsStatus> { 287 self.write_data(0, name); 288 let tag = self.alloc_tag(); 289 let req = VfsRequest::unlink(tag, dir_handle, 0, name.len() as u64); 290 let resp = self.send_request(&req)?; 291 Self::check_response(&resp) 292 } 293 294 pub fn rename( 295 &mut self, 296 src_dir: u8, 297 src_name: &[u8], 298 dst_dir: u8, 299 dst_name: &[u8], 300 ) -> Result<(), FsStatus> { 301 self.write_data(0, src_name); 302 self.write_data(src_name.len(), dst_name); 303 let tag = self.alloc_tag(); 304 let req = VfsRequest::rename( 305 tag, 306 src_dir, 307 dst_dir, 308 0, 309 src_name.len() as u32, 310 src_name.len() as u32, 311 dst_name.len() as u32, 312 ); 313 let resp = self.send_request(&req)?; 314 Self::check_response(&resp) 315 } 316 317 pub fn readdir( 318 &mut self, 319 dir_handle: u8, 320 cursor: u64, 321 buf: &mut [u8], 322 ) -> Result<(usize, u64), FsStatus> { 323 let tag = self.alloc_tag(); 324 let req = VfsRequest::readdir(tag, dir_handle, cursor, 0, buf.len() as u64); 325 let resp = self.send_request(&req)?; 326 Self::check_response(&resp)?; 327 let entry_count = resp.val0 as usize; 328 let next_cursor = resp.val1; 329 let byte_count = entry_count * READDIR_ENTRY_SIZE; 330 let src = self.read_data(0, byte_count); 331 let copy_len = src.len().min(buf.len()); 332 buf[..copy_len].copy_from_slice(&src[..copy_len]); 333 let actual_entries = copy_len / READDIR_ENTRY_SIZE; 334 Ok((actual_entries, next_cursor)) 335 } 336 337 pub fn truncate(&mut self, handle: u8, new_size: u64) -> Result<(), FsStatus> { 338 let tag = self.alloc_tag(); 339 let req = VfsRequest::truncate(tag, handle, new_size); 340 let resp = self.send_request(&req)?; 341 Self::check_response(&resp) 342 } 343 344 pub fn mount_info(&mut self, buf: &mut [u8]) -> Result<usize, FsStatus> { 345 let tag = self.alloc_tag(); 346 let req = VfsRequest::mount_info(tag); 347 let resp = self.send_request(&req)?; 348 Self::check_response(&resp)?; 349 let count = resp.val0 as usize; 350 let byte_count = count * MOUNT_INFO_ENTRY_SIZE; 351 let src = self.read_data(0, byte_count); 352 let copy_len = src.len().min(buf.len()); 353 buf[..copy_len].copy_from_slice(&src[..copy_len]); 354 Ok(count) 355 } 356 357 pub fn sync(&mut self) -> Result<(), FsStatus> { 358 let tag = self.alloc_tag(); 359 let req = VfsRequest::sync(tag); 360 let resp = self.send_request(&req)?; 361 Self::check_response(&resp) 362 } 363 364 pub fn extended_raw( 365 &mut self, 366 sub_opcode: u8, 367 arg0: u64, 368 arg1: u64, 369 ) -> Result<VfsResponse, FsStatus> { 370 let tag = self.alloc_tag(); 371 let req = VfsRequest { 372 opcode: lancer_vfs_proto::VfsOpcode::Extended as u8, 373 handle: 0, 374 flags: sub_opcode, 375 _pad: 0, 376 tag, 377 arg0, 378 arg1, 379 arg2: 0, 380 }; 381 let resp = self.send_request(&req)?; 382 Self::check_response(&resp)?; 383 Ok(resp) 384 } 385} 386 387pub unsafe fn init() -> FsClient { 388 let map_ok = (0..VFS_RING_FRAME_COUNT).all(|i| { 389 syscall::frame_map(VFS_RING_BASE_SLOT + i, VFS_RING_VADDR + i * 4096, 1) >= 0 390 }); 391 match map_ok { 392 true => {} 393 false => { 394 syscall::debug_print("fs::init: failed to map VFS ring frames\n"); 395 syscall::exit(); 396 } 397 } 398 399 syscall::notify_wait(CLIENT_NOTIF_SLOT as u64); 400 401 unsafe { FsClient::new(VFS_RING_VADDR as usize, VFS_NOTIF_SLOT, CLIENT_NOTIF_SLOT) } 402} 403 404pub fn open_path(client: &mut FsClient, root_handle: u8, path: &[u8]) -> Result<u8, FsStatus> { 405 open_path_with_rights(client, root_handle, path, FsRights::TRAVERSE_INHERITABLE) 406} 407 408pub fn open_path_with_rights( 409 client: &mut FsClient, 410 root_handle: u8, 411 path: &[u8], 412 rights: FsRights, 413) -> Result<u8, FsStatus> { 414 let trimmed = match path.first() { 415 Some(&b'/') => &path[1..], 416 _ => path, 417 }; 418 match trimmed.is_empty() { 419 true => client.open(root_handle, b"", rights), 420 false => open_path_components(client, root_handle, trimmed, 0, rights), 421 } 422} 423 424fn open_path_components( 425 client: &mut FsClient, 426 dir_handle: u8, 427 remaining: &[u8], 428 depth: usize, 429 leaf_rights: FsRights, 430) -> Result<u8, FsStatus> { 431 if depth >= MAX_PATH_COMPONENTS { 432 return Err(FsStatus::PathTooDeep); 433 } 434 match remaining.iter().position(|&b| b == b'/') { 435 Some(slash_pos) => { 436 let component = &remaining[..slash_pos]; 437 let rest = &remaining[slash_pos + 1..]; 438 let intermediate = client.open(dir_handle, component, FsRights::ALL)?; 439 let result = open_path_components(client, intermediate, rest, depth + 1, leaf_rights); 440 let _ = client.close(intermediate); 441 result 442 } 443 None => client.open(dir_handle, remaining, leaf_rights), 444 } 445} 446 447fn strip_trailing_slashes(s: &[u8]) -> &[u8] { 448 match s.last() { 449 Some(&b'/') => strip_trailing_slashes(&s[..s.len() - 1]), 450 _ => s, 451 } 452} 453 454pub fn open_parent<'a>( 455 client: &mut FsClient, 456 root_handle: u8, 457 path: &'a [u8], 458) -> Result<(u8, &'a [u8]), FsStatus> { 459 open_parent_with_rights(client, root_handle, path, FsRights::ALL) 460} 461 462pub fn open_parent_with_rights<'a>( 463 client: &mut FsClient, 464 root_handle: u8, 465 path: &'a [u8], 466 parent_rights: FsRights, 467) -> Result<(u8, &'a [u8]), FsStatus> { 468 let stripped = strip_trailing_slashes(path); 469 let trimmed = match stripped.first() { 470 Some(&b'/') => &stripped[1..], 471 _ => stripped, 472 }; 473 match trimmed.is_empty() { 474 true => Err(FsStatus::NotFound), 475 false => match trimmed.iter().rposition(|&b| b == b'/') { 476 Some(last_slash) => { 477 let parent_path = &trimmed[..last_slash]; 478 let basename = &trimmed[last_slash + 1..]; 479 let parent_handle = 480 open_path_with_rights(client, root_handle, parent_path, parent_rights)?; 481 Ok((parent_handle, basename)) 482 } 483 None => { 484 let parent = client.open(root_handle, b"", parent_rights)?; 485 Ok((parent, trimmed)) 486 } 487 }, 488 } 489}