Nothing to see here, move along meow
0

Configure Feed

Select the types of activity you want to include in your feed.

at main 38 kB View raw
1#![no_std] 2#![no_main] 3#![allow(dead_code)] 4 5mod service { 6 use lancer_core::packet_ring::{PacketRingReader, PacketRingWriter}; 7 use lancer_core::static_vec::StaticVec; 8 use lancer_user::syscall; 9 use lancer_vfs_proto::{ 10 READDIR_ENTRY_SIZE, READDIR_MAX_NAME, ReadDirEntry, VFS_STATUS_DIR_NOT_EMPTY, 11 VFS_STATUS_DISK_FULL, VFS_STATUS_FILE_EXISTS, VFS_STATUS_HANDLE_TABLE_FULL, 12 VFS_STATUS_INVALID_HANDLE, VFS_STATUS_IS_A_DIRECTORY, VFS_STATUS_NAME_TOO_LONG, 13 VFS_STATUS_NOT_A_DIRECTORY, VFS_STATUS_NOT_FOUND, VFS_STATUS_OK, VFS_STATUS_UNSUPPORTED, 14 VfsOpcode, VfsRequest, VfsResponse, 15 }; 16 17 const CLIENT_RING_BASE_SLOT: u64 = 64; 18 const CLIENT_RING_FRAME_COUNT: u64 = 16; 19 const NOTIF_SLOT: u64 = 3; 20 const VFS_NOTIF_SLOT: u64 = 4; 21 22 const CLIENT_RING_VADDR: u64 = 0x5000_0000; 23 const RING_DATA_OFFSET: usize = 8192; 24 const RING_DATA_PAGES: usize = 14; 25 const RING_DATA_SIZE: usize = RING_DATA_PAGES * 4096; 26 27 const MAX_INODES: usize = 256; 28 const MAX_HANDLES: usize = 64; 29 const MAX_CHILDREN: usize = 32; 30 const MAX_NAME_LEN: usize = 48; 31 const MAX_BATCH: u32 = 64; 32 33 const BLOCK_SIZE: usize = 4096; 34 const TOTAL_BLOCKS: usize = 256; 35 const BLOCKS_PER_FILE: usize = 16; 36 const MAX_FILE_SIZE: usize = BLOCKS_PER_FILE * BLOCK_SIZE; 37 const NO_BLOCK: u16 = u16::MAX; 38 39 #[repr(u8)] 40 #[derive(Clone, Copy, PartialEq, Eq)] 41 enum InodeType { 42 Free = 0, 43 File = 1, 44 Directory = 2, 45 } 46 47 impl InodeType { 48 const fn to_wire(self) -> u8 { 49 match self { 50 Self::File => 0, 51 Self::Directory => 1, 52 Self::Free => 0xFF, 53 } 54 } 55 } 56 57 #[repr(C)] 58 #[derive(Clone, Copy)] 59 struct ChildEntry { 60 name_hash: u64, 61 inode_idx: u16, 62 } 63 64 const EMPTY_CHILD: ChildEntry = ChildEntry { 65 name_hash: 0, 66 inode_idx: 0, 67 }; 68 69 #[repr(C)] 70 struct RamInode { 71 inode_type: InodeType, 72 child_count: u8, 73 block_count: u8, 74 _pad: [u8; 5], 75 size: u64, 76 object_id: u64, 77 generation: u64, 78 blocks: [u16; BLOCKS_PER_FILE], 79 children: [ChildEntry; MAX_CHILDREN], 80 names: [[u8; MAX_NAME_LEN]; MAX_CHILDREN], 81 name_lens: [u8; MAX_CHILDREN], 82 } 83 84 const EMPTY_INODE: RamInode = RamInode { 85 inode_type: InodeType::Free, 86 child_count: 0, 87 block_count: 0, 88 _pad: [0; 5], 89 size: 0, 90 object_id: 0, 91 generation: 0, 92 blocks: [NO_BLOCK; BLOCKS_PER_FILE], 93 children: [EMPTY_CHILD; MAX_CHILDREN], 94 names: [[0; MAX_NAME_LEN]; MAX_CHILDREN], 95 name_lens: [0; MAX_CHILDREN], 96 }; 97 98 static mut BLOCK_POOL: [[u8; BLOCK_SIZE]; TOTAL_BLOCKS] = [[0; BLOCK_SIZE]; TOTAL_BLOCKS]; 99 static mut BLOCK_BITMAP: BlockBitmap = BlockBitmap::new(); 100 101 struct BlockBitmap { 102 words: [u64; 4], 103 } 104 105 impl BlockBitmap { 106 const fn new() -> Self { 107 Self { words: [0; 4] } 108 } 109 110 fn alloc(&mut self) -> Option<u16> { 111 (0..4u32).find_map(|word_idx| { 112 let w = self.words[word_idx as usize]; 113 match w { 114 u64::MAX => None, 115 _ => { 116 let bit = w.trailing_ones(); 117 self.words[word_idx as usize] |= 1u64 << bit; 118 Some(word_idx as u16 * 64 + bit as u16) 119 } 120 } 121 }) 122 } 123 124 fn free(&mut self, idx: u16) { 125 self.words[(idx / 64) as usize] &= !(1u64 << (idx % 64)); 126 } 127 128 fn count_free(&self) -> u64 { 129 (TOTAL_BLOCKS as u64) 130 - self 131 .words 132 .iter() 133 .fold(0u64, |acc, &w| acc + w.count_ones() as u64) 134 } 135 } 136 137 fn block_bitmap() -> *mut BlockBitmap { 138 &raw mut BLOCK_BITMAP 139 } 140 141 fn block_pool() -> *mut [[u8; BLOCK_SIZE]; TOTAL_BLOCKS] { 142 &raw mut BLOCK_POOL 143 } 144 145 fn block_data(pool_idx: u16) -> *mut [u8; BLOCK_SIZE] { 146 unsafe { &raw mut (*block_pool())[pool_idx as usize] } 147 } 148 149 fn free_file_blocks(inode: &mut RamInode) { 150 let bmap = unsafe { &mut *block_bitmap() }; 151 (0..inode.block_count as usize).for_each(|i| { 152 let blk = inode.blocks[i]; 153 match blk { 154 NO_BLOCK => {} 155 idx => bmap.free(idx), 156 } 157 inode.blocks[i] = NO_BLOCK; 158 }); 159 inode.block_count = 0; 160 } 161 162 fn ensure_blocks(inode: &mut RamInode, needed: usize) -> bool { 163 let current = inode.block_count as usize; 164 match needed <= current { 165 true => true, 166 false => { 167 let bmap = unsafe { &mut *block_bitmap() }; 168 (current..needed).all(|i| match bmap.alloc() { 169 Some(blk) => { 170 inode.blocks[i] = blk; 171 unsafe { (&mut *block_data(blk)).fill(0) }; 172 inode.block_count = (i + 1) as u8; 173 true 174 } 175 None => false, 176 }) 177 } 178 } 179 } 180 181 struct InodeBitmap { 182 words: [u64; 4], 183 } 184 185 impl InodeBitmap { 186 const fn new() -> Self { 187 Self { words: [0; 4] } 188 } 189 190 fn alloc(&mut self) -> Option<u16> { 191 (0..4u32).find_map(|word_idx| { 192 let w = self.words[word_idx as usize]; 193 match w { 194 u64::MAX => None, 195 _ => { 196 let bit = w.trailing_ones(); 197 self.words[word_idx as usize] |= 1u64 << bit; 198 Some(word_idx as u16 * 64 + bit as u16) 199 } 200 } 201 }) 202 } 203 204 fn free(&mut self, idx: u16) { 205 self.words[(idx / 64) as usize] &= !(1u64 << (idx % 64)); 206 } 207 208 fn set(&mut self, idx: u16) { 209 self.words[(idx / 64) as usize] |= 1u64 << (idx % 64); 210 } 211 212 fn count_allocated(&self) -> u64 { 213 self.words 214 .iter() 215 .fold(0u64, |acc, &w| acc + w.count_ones() as u64) 216 } 217 } 218 219 #[derive(Clone, Copy)] 220 struct RamHandle { 221 inode_idx: u16, 222 generation: u64, 223 active: bool, 224 } 225 226 const EMPTY_HANDLE: RamHandle = RamHandle { 227 inode_idx: 0, 228 generation: 0, 229 active: false, 230 }; 231 232 static mut INODES: [RamInode; MAX_INODES] = [EMPTY_INODE; MAX_INODES]; 233 static mut BITMAP: InodeBitmap = InodeBitmap::new(); 234 static mut HANDLES: [RamHandle; MAX_HANDLES] = [EMPTY_HANDLE; MAX_HANDLES]; 235 static mut NEXT_OBJECT_ID: u64 = 1; 236 237 fn fnv1a(data: &[u8]) -> u64 { 238 data.iter().fold(0xcbf29ce484222325u64, |hash, &byte| { 239 (hash ^ byte as u64).wrapping_mul(0x100000001b3u64) 240 }) 241 } 242 243 fn next_object_id() -> u64 { 244 #[allow(clippy::deref_addrof)] 245 let id = unsafe { &mut *(&raw mut NEXT_OBJECT_ID) }; 246 let val = *id; 247 *id = val.wrapping_add(1); 248 val 249 } 250 251 fn inodes() -> *mut [RamInode; MAX_INODES] { 252 &raw mut INODES 253 } 254 255 fn bitmap() -> *mut InodeBitmap { 256 &raw mut BITMAP 257 } 258 259 fn handles() -> *mut [RamHandle; MAX_HANDLES] { 260 &raw mut HANDLES 261 } 262 263 fn resolve_handle(h: u8) -> Option<u16> { 264 let (handles_r, inodes_r) = unsafe { (&*handles(), &*inodes()) }; 265 match h { 266 0 => Some(0), 267 h if (h as usize) < MAX_HANDLES && handles_r[h as usize].active => { 268 let handle = &handles_r[h as usize]; 269 let inode = &inodes_r[handle.inode_idx as usize]; 270 match inode.generation == handle.generation && inode.inode_type != InodeType::Free { 271 true => Some(handle.inode_idx), 272 false => None, 273 } 274 } 275 _ => None, 276 } 277 } 278 279 fn alloc_handle(inode_idx: u16) -> Option<u8> { 280 let generation = unsafe { (*inodes())[inode_idx as usize].generation }; 281 let handles_m = unsafe { &mut *handles() }; 282 (1..MAX_HANDLES as u8) 283 .find(|&i| !handles_m[i as usize].active) 284 .inspect(|&i| { 285 handles_m[i as usize] = RamHandle { 286 inode_idx, 287 generation, 288 active: true, 289 }; 290 }) 291 } 292 293 fn free_handle(h: u8) { 294 match h { 295 0 => {} 296 _ => { 297 unsafe { (*handles())[h as usize].active = false }; 298 } 299 } 300 } 301 302 fn alloc_inode(inode_type: InodeType) -> Option<u16> { 303 unsafe { &mut *bitmap() }.alloc().inspect(|&idx| { 304 let oid = next_object_id(); 305 let inode = unsafe { &mut (*inodes())[idx as usize] }; 306 let next_gen = inode.generation.wrapping_add(1); 307 *inode = RamInode { 308 inode_type, 309 child_count: 0, 310 block_count: 0, 311 _pad: [0; 5], 312 size: 0, 313 object_id: oid, 314 generation: next_gen, 315 blocks: [NO_BLOCK; BLOCKS_PER_FILE], 316 children: [EMPTY_CHILD; MAX_CHILDREN], 317 names: [[0; MAX_NAME_LEN]; MAX_CHILDREN], 318 name_lens: [0; MAX_CHILDREN], 319 }; 320 }) 321 } 322 323 fn free_inode(idx: u16) { 324 let inode = unsafe { &mut (*inodes())[idx as usize] }; 325 free_file_blocks(inode); 326 inode.inode_type = InodeType::Free; 327 unsafe { &mut *bitmap() }.free(idx); 328 } 329 330 fn recursive_free(root_idx: u16) { 331 let mut stack: StaticVec<u16, MAX_INODES> = StaticVec::new(); 332 let _ = stack.push(root_idx); 333 loop { 334 match stack.pop() { 335 None => break, 336 Some(idx) => { 337 let (itype, count, children) = { 338 let inode = unsafe { &(*inodes())[idx as usize] }; 339 (inode.inode_type, inode.child_count, inode.children) 340 }; 341 if itype == InodeType::Directory { 342 (0..count as usize).for_each(|i| { 343 let _ = stack.push(children[i].inode_idx); 344 }); 345 } 346 free_inode(idx); 347 } 348 } 349 } 350 } 351 352 fn find_child(inode: &RamInode, name: &[u8]) -> Option<usize> { 353 let hash = fnv1a(name); 354 (0..inode.child_count as usize).find(|&i| { 355 inode.children[i].name_hash == hash 356 && inode.name_lens[i] as usize == name.len() 357 && inode.names[i][..name.len()] == name[..] 358 }) 359 } 360 361 fn add_child(inode: &mut RamInode, name: &[u8], child_idx: u16) -> bool { 362 match inode.child_count as usize >= MAX_CHILDREN || name.len() > MAX_NAME_LEN { 363 true => false, 364 false => { 365 let slot = inode.child_count as usize; 366 inode.children[slot] = ChildEntry { 367 name_hash: fnv1a(name), 368 inode_idx: child_idx, 369 }; 370 inode.names[slot][..name.len()].copy_from_slice(name); 371 inode.name_lens[slot] = name.len() as u8; 372 inode.child_count += 1; 373 true 374 } 375 } 376 } 377 378 fn remove_child(inode: &mut RamInode, slot: usize) { 379 let last = inode.child_count as usize - 1; 380 match slot == last { 381 true => {} 382 false => { 383 inode.children[slot] = inode.children[last]; 384 inode.names[slot] = inode.names[last]; 385 inode.name_lens[slot] = inode.name_lens[last]; 386 } 387 } 388 inode.children[last] = EMPTY_CHILD; 389 inode.names[last] = [0; MAX_NAME_LEN]; 390 inode.name_lens[last] = 0; 391 inode.child_count -= 1; 392 } 393 394 fn read_name_from_data( 395 data_area: *const u8, 396 data_area_size: usize, 397 offset: usize, 398 len: usize, 399 ) -> Option<([u8; MAX_NAME_LEN], usize)> { 400 match len == 0 || len > MAX_NAME_LEN || offset.saturating_add(len) > data_area_size { 401 true => None, 402 false => { 403 let mut buf = [0u8; MAX_NAME_LEN]; 404 unsafe { 405 core::ptr::copy_nonoverlapping(data_area.add(offset), buf.as_mut_ptr(), len); 406 } 407 Some((buf, len)) 408 } 409 } 410 } 411 412 fn dispatch_request( 413 req: &VfsRequest, 414 data_area: *mut u8, 415 data_area_size: usize, 416 ) -> VfsResponse { 417 match VfsOpcode::from_u8(req.opcode) { 418 Some(VfsOpcode::Open) => handle_open(req, data_area, data_area_size), 419 Some(VfsOpcode::Read) => handle_read(req, data_area, data_area_size), 420 Some(VfsOpcode::Write) => handle_write(req, data_area, data_area_size), 421 Some(VfsOpcode::Close) => handle_close(req), 422 Some(VfsOpcode::Stat) => handle_stat(req), 423 Some(VfsOpcode::Mkdir) => handle_mkdir(req, data_area, data_area_size), 424 Some(VfsOpcode::Unlink) => handle_unlink(req, data_area, data_area_size), 425 Some(VfsOpcode::ReadDir) => handle_readdir(req, data_area, data_area_size), 426 Some(VfsOpcode::Sync) => VfsResponse::success(req.opcode, req.tag, 0, 0), 427 Some(VfsOpcode::StatFs) => { 428 let free_blocks = unsafe { &*block_bitmap() }.count_free(); 429 VfsResponse::success(req.opcode, req.tag, TOTAL_BLOCKS as u64, free_blocks) 430 } 431 Some(VfsOpcode::Rename) => handle_rename(req, data_area, data_area_size), 432 Some(VfsOpcode::Truncate) => handle_truncate(req), 433 _ => VfsResponse::error(req.opcode, req.tag, VFS_STATUS_UNSUPPORTED), 434 } 435 } 436 437 fn handle_open(req: &VfsRequest, data_area: *mut u8, data_area_size: usize) -> VfsResponse { 438 let (opcode, tag) = (req.opcode, req.tag); 439 440 let dir_idx = match resolve_handle(req.handle) { 441 Some(idx) => idx, 442 None => return VfsResponse::error(opcode, tag, VFS_STATUS_INVALID_HANDLE), 443 }; 444 445 match unsafe { (*inodes())[dir_idx as usize].inode_type } { 446 InodeType::Directory => {} 447 _ => return VfsResponse::error(opcode, tag, VFS_STATUS_NOT_A_DIRECTORY), 448 } 449 450 let (name_buf, name_len) = match read_name_from_data( 451 data_area as *const u8, 452 data_area_size, 453 req.arg0 as usize, 454 req.arg1 as usize, 455 ) { 456 Some(pair) => pair, 457 None => return VfsResponse::error(opcode, tag, VFS_STATUS_NAME_TOO_LONG), 458 }; 459 460 let child_slot = match find_child( 461 unsafe { &(*inodes())[dir_idx as usize] }, 462 &name_buf[..name_len], 463 ) { 464 Some(slot) => slot, 465 None => { 466 let flags = req.flags; 467 match flags & 0x04 != 0 { 468 true => { 469 let new_idx = match alloc_inode(InodeType::File) { 470 Some(idx) => idx, 471 None => return VfsResponse::error(opcode, tag, VFS_STATUS_DISK_FULL), 472 }; 473 match add_child( 474 unsafe { &mut (*inodes())[dir_idx as usize] }, 475 &name_buf[..name_len], 476 new_idx, 477 ) { 478 true => match alloc_handle(new_idx) { 479 Some(h) => return VfsResponse::success(opcode, tag, h as u64, 0), 480 None => { 481 if let Some(slot) = find_child( 482 unsafe { &(*inodes())[dir_idx as usize] }, 483 &name_buf[..name_len], 484 ) { 485 remove_child( 486 unsafe { &mut (*inodes())[dir_idx as usize] }, 487 slot, 488 ); 489 } 490 free_inode(new_idx); 491 return VfsResponse::error( 492 opcode, 493 tag, 494 VFS_STATUS_HANDLE_TABLE_FULL, 495 ); 496 } 497 }, 498 false => { 499 free_inode(new_idx); 500 return VfsResponse::error(opcode, tag, VFS_STATUS_DISK_FULL); 501 } 502 } 503 } 504 false => return VfsResponse::error(opcode, tag, VFS_STATUS_NOT_FOUND), 505 } 506 } 507 }; 508 509 let child_idx = unsafe { (*inodes())[dir_idx as usize].children[child_slot].inode_idx }; 510 match alloc_handle(child_idx) { 511 Some(h) => VfsResponse::success(opcode, tag, h as u64, 0), 512 None => VfsResponse::error(opcode, tag, VFS_STATUS_HANDLE_TABLE_FULL), 513 } 514 } 515 516 fn handle_read(req: &VfsRequest, data_area: *mut u8, data_area_size: usize) -> VfsResponse { 517 let (opcode, tag) = (req.opcode, req.tag); 518 let offset = req.arg0 as usize; 519 let len = req.arg1 as usize; 520 let buf_offset = req.arg2 as usize; 521 522 let inode_idx = match resolve_handle(req.handle) { 523 Some(idx) => idx, 524 None => return VfsResponse::error(opcode, tag, VFS_STATUS_INVALID_HANDLE), 525 }; 526 527 let inode = unsafe { &(*inodes())[inode_idx as usize] }; 528 match inode.inode_type { 529 InodeType::File => {} 530 InodeType::Directory => { 531 return VfsResponse::error(opcode, tag, VFS_STATUS_IS_A_DIRECTORY); 532 } 533 _ => return VfsResponse::error(opcode, tag, VFS_STATUS_INVALID_HANDLE), 534 } 535 536 let file_size = inode.size as usize; 537 let start = offset.min(file_size); 538 let avail = file_size.saturating_sub(start); 539 let dst_avail = data_area_size.saturating_sub(buf_offset); 540 let to_copy = len.min(avail).min(dst_avail); 541 542 let first_blk = start / BLOCK_SIZE; 543 let last_blk = match to_copy { 544 0 => first_blk, 545 _ => (start + to_copy - 1) / BLOCK_SIZE, 546 }; 547 (first_blk..=last_blk).fold( 548 (start, buf_offset, to_copy), 549 |(pos, dst_pos, rem), blk_idx| { 550 let blk_off = pos % BLOCK_SIZE; 551 let chunk = rem.min(BLOCK_SIZE - blk_off); 552 let pool_idx = inode.blocks[blk_idx]; 553 match pool_idx { 554 NO_BLOCK => unsafe { 555 core::ptr::write_bytes(data_area.add(dst_pos), 0, chunk); 556 }, 557 idx => unsafe { 558 core::ptr::copy_nonoverlapping( 559 (*block_data(idx)).as_ptr().add(blk_off), 560 data_area.add(dst_pos), 561 chunk, 562 ); 563 }, 564 } 565 (pos + chunk, dst_pos + chunk, rem - chunk) 566 }, 567 ); 568 569 VfsResponse::success(opcode, tag, to_copy as u64, 0) 570 } 571 572 fn handle_write(req: &VfsRequest, data_area: *mut u8, data_area_size: usize) -> VfsResponse { 573 let (opcode, tag) = (req.opcode, req.tag); 574 let offset = req.arg0 as usize; 575 let len = req.arg1 as usize; 576 let buf_offset = req.arg2 as usize; 577 578 let inode_idx = match resolve_handle(req.handle) { 579 Some(idx) => idx, 580 None => return VfsResponse::error(opcode, tag, VFS_STATUS_INVALID_HANDLE), 581 }; 582 583 let inode = unsafe { &mut (*inodes())[inode_idx as usize] }; 584 match inode.inode_type { 585 InodeType::File => {} 586 InodeType::Directory => { 587 return VfsResponse::error(opcode, tag, VFS_STATUS_IS_A_DIRECTORY); 588 } 589 _ => return VfsResponse::error(opcode, tag, VFS_STATUS_INVALID_HANDLE), 590 } 591 592 let src_avail = data_area_size.saturating_sub(buf_offset); 593 let to_copy = len.min(src_avail); 594 let end = offset.saturating_add(to_copy); 595 596 if end > MAX_FILE_SIZE { 597 return VfsResponse::error(opcode, tag, VFS_STATUS_DISK_FULL); 598 } 599 600 let blocks_needed = end.div_ceil(BLOCK_SIZE); 601 if !ensure_blocks(inode, blocks_needed) { 602 return VfsResponse::error(opcode, tag, VFS_STATUS_DISK_FULL); 603 } 604 605 let first_blk = offset / BLOCK_SIZE; 606 let last_blk = match to_copy { 607 0 => first_blk, 608 _ => (offset + to_copy - 1) / BLOCK_SIZE, 609 }; 610 (first_blk..=last_blk).fold( 611 (offset, buf_offset, to_copy), 612 |(pos, src_pos, rem), blk_idx| { 613 let blk_off = pos % BLOCK_SIZE; 614 let chunk = rem.min(BLOCK_SIZE - blk_off); 615 let pool_idx = inode.blocks[blk_idx]; 616 unsafe { 617 core::ptr::copy_nonoverlapping( 618 data_area.add(src_pos) as *const u8, 619 (*block_data(pool_idx)).as_mut_ptr().add(blk_off), 620 chunk, 621 ); 622 } 623 (pos + chunk, src_pos + chunk, rem - chunk) 624 }, 625 ); 626 627 if end > inode.size as usize { 628 inode.size = end as u64; 629 } 630 631 VfsResponse::success(opcode, tag, to_copy as u64, 0) 632 } 633 634 fn handle_close(req: &VfsRequest) -> VfsResponse { 635 let (opcode, tag) = (req.opcode, req.tag); 636 match req.handle { 637 0 => VfsResponse::error(opcode, tag, VFS_STATUS_INVALID_HANDLE), 638 h => match resolve_handle(h) { 639 Some(_) => { 640 free_handle(h); 641 VfsResponse::success(opcode, tag, 0, 0) 642 } 643 None => VfsResponse::error(opcode, tag, VFS_STATUS_INVALID_HANDLE), 644 }, 645 } 646 } 647 648 fn handle_stat(req: &VfsRequest) -> VfsResponse { 649 let (opcode, tag) = (req.opcode, req.tag); 650 651 let inode_idx = match resolve_handle(req.handle) { 652 Some(idx) => idx, 653 None => return VfsResponse::error(opcode, tag, VFS_STATUS_INVALID_HANDLE), 654 }; 655 656 let inode = unsafe { &(*inodes())[inode_idx as usize] }; 657 VfsResponse::success( 658 opcode, 659 tag, 660 inode.size, 661 (inode.inode_type.to_wire() as u64) << 48, 662 ) 663 } 664 665 fn handle_mkdir(req: &VfsRequest, data_area: *mut u8, data_area_size: usize) -> VfsResponse { 666 let (opcode, tag) = (req.opcode, req.tag); 667 668 let dir_idx = match resolve_handle(req.handle) { 669 Some(idx) => idx, 670 None => return VfsResponse::error(opcode, tag, VFS_STATUS_INVALID_HANDLE), 671 }; 672 673 match unsafe { (*inodes())[dir_idx as usize].inode_type } { 674 InodeType::Directory => {} 675 _ => return VfsResponse::error(opcode, tag, VFS_STATUS_NOT_A_DIRECTORY), 676 } 677 678 let (name_buf, name_len) = match read_name_from_data( 679 data_area as *const u8, 680 data_area_size, 681 req.arg0 as usize, 682 req.arg1 as usize, 683 ) { 684 Some(pair) => pair, 685 None => return VfsResponse::error(opcode, tag, VFS_STATUS_NAME_TOO_LONG), 686 }; 687 688 if find_child( 689 unsafe { &(*inodes())[dir_idx as usize] }, 690 &name_buf[..name_len], 691 ) 692 .is_some() 693 { 694 return VfsResponse::error(opcode, tag, VFS_STATUS_FILE_EXISTS); 695 } 696 697 let new_idx = match alloc_inode(InodeType::Directory) { 698 Some(idx) => idx, 699 None => return VfsResponse::error(opcode, tag, VFS_STATUS_DISK_FULL), 700 }; 701 702 match add_child( 703 unsafe { &mut (*inodes())[dir_idx as usize] }, 704 &name_buf[..name_len], 705 new_idx, 706 ) { 707 true => match alloc_handle(new_idx) { 708 Some(h) => VfsResponse::success(opcode, tag, h as u64, 0), 709 None => { 710 if let Some(slot) = find_child( 711 unsafe { &(*inodes())[dir_idx as usize] }, 712 &name_buf[..name_len], 713 ) { 714 remove_child(unsafe { &mut (*inodes())[dir_idx as usize] }, slot); 715 } 716 free_inode(new_idx); 717 VfsResponse::error(opcode, tag, VFS_STATUS_HANDLE_TABLE_FULL) 718 } 719 }, 720 false => { 721 free_inode(new_idx); 722 VfsResponse::error(opcode, tag, VFS_STATUS_DISK_FULL) 723 } 724 } 725 } 726 727 fn handle_unlink(req: &VfsRequest, data_area: *mut u8, data_area_size: usize) -> VfsResponse { 728 let (opcode, tag) = (req.opcode, req.tag); 729 730 let dir_idx = match resolve_handle(req.handle) { 731 Some(idx) => idx, 732 None => return VfsResponse::error(opcode, tag, VFS_STATUS_INVALID_HANDLE), 733 }; 734 735 match unsafe { (*inodes())[dir_idx as usize].inode_type } { 736 InodeType::Directory => {} 737 _ => return VfsResponse::error(opcode, tag, VFS_STATUS_NOT_A_DIRECTORY), 738 } 739 740 let (name_buf, name_len) = match read_name_from_data( 741 data_area as *const u8, 742 data_area_size, 743 req.arg0 as usize, 744 req.arg1 as usize, 745 ) { 746 Some(pair) => pair, 747 None => return VfsResponse::error(opcode, tag, VFS_STATUS_NAME_TOO_LONG), 748 }; 749 750 let (slot, child_idx) = { 751 let dir = unsafe { &(*inodes())[dir_idx as usize] }; 752 let s = match find_child(dir, &name_buf[..name_len]) { 753 Some(s) => s, 754 None => return VfsResponse::error(opcode, tag, VFS_STATUS_NOT_FOUND), 755 }; 756 (s, dir.children[s].inode_idx) 757 }; 758 759 { 760 let child = unsafe { &(*inodes())[child_idx as usize] }; 761 match child.inode_type { 762 InodeType::Directory if child.child_count > 0 => { 763 return VfsResponse::error(opcode, tag, VFS_STATUS_DIR_NOT_EMPTY); 764 } 765 _ => {} 766 } 767 } 768 769 remove_child(unsafe { &mut (*inodes())[dir_idx as usize] }, slot); 770 recursive_free(child_idx); 771 772 VfsResponse::success(opcode, tag, 0, 0) 773 } 774 775 fn handle_readdir(req: &VfsRequest, data_area: *mut u8, data_area_size: usize) -> VfsResponse { 776 let (opcode, tag) = (req.opcode, req.tag); 777 let cursor = req.arg0 as usize; 778 let buf_offset = req.arg1 as usize; 779 let buf_len = req.arg2 as usize; 780 781 let inode_idx = match resolve_handle(req.handle) { 782 Some(idx) => idx, 783 None => return VfsResponse::error(opcode, tag, VFS_STATUS_INVALID_HANDLE), 784 }; 785 786 let inodes_r = unsafe { &*inodes() }; 787 let inode = &inodes_r[inode_idx as usize]; 788 789 match inode.inode_type { 790 InodeType::Directory => {} 791 _ => return VfsResponse::error(opcode, tag, VFS_STATUS_NOT_A_DIRECTORY), 792 } 793 794 let avail_buf = data_area_size.saturating_sub(buf_offset).min(buf_len); 795 let max_entries = avail_buf / READDIR_ENTRY_SIZE; 796 let remaining = (inode.child_count as usize).saturating_sub(cursor); 797 let entry_count = remaining.min(max_entries); 798 799 let _bytes_written = (0..entry_count).fold(0usize, |written, i| { 800 let child_slot = cursor + i; 801 let child_idx = inode.children[child_slot].inode_idx; 802 let child = &inodes_r[child_idx as usize]; 803 let name_len = inode.name_lens[child_slot] as usize; 804 805 let mut entry = ReadDirEntry { 806 object_id: child.object_id, 807 size: child.size, 808 name_len: name_len as u16, 809 inode_type: child.inode_type.to_wire(), 810 _pad: 0, 811 name: [0; READDIR_MAX_NAME], 812 _pad2: [0; 4], 813 }; 814 let copy_len = name_len.min(READDIR_MAX_NAME); 815 entry.name[..copy_len].copy_from_slice(&inode.names[child_slot][..copy_len]); 816 817 let entry_bytes = entry.as_bytes(); 818 let dst = buf_offset + written; 819 unsafe { 820 core::ptr::copy_nonoverlapping( 821 entry_bytes.as_ptr(), 822 data_area.add(dst), 823 READDIR_ENTRY_SIZE, 824 ); 825 } 826 written + READDIR_ENTRY_SIZE 827 }); 828 829 let next_cursor = cursor + entry_count; 830 let more = match next_cursor < inode.child_count as usize { 831 true => next_cursor as u64, 832 false => u64::MAX, 833 }; 834 835 VfsResponse { 836 opcode, 837 status: VFS_STATUS_OK, 838 _pad: [0; 2], 839 tag, 840 val0: entry_count as u64, 841 val1: more, 842 } 843 } 844 845 fn handle_rename(req: &VfsRequest, data_area: *mut u8, data_area_size: usize) -> VfsResponse { 846 let (opcode, tag) = (req.opcode, req.tag); 847 let src_dir_handle = req.handle; 848 let dst_dir_handle = req.rename_dst_dir_handle(); 849 let src_name_offset = req.rename_src_name_offset() as usize; 850 let src_name_len = req.rename_src_name_len() as usize; 851 let dst_name_offset = req.rename_dst_name_offset() as usize; 852 let dst_name_len = req.rename_dst_name_len() as usize; 853 854 let src_dir_idx = match resolve_handle(src_dir_handle) { 855 Some(idx) => idx, 856 None => return VfsResponse::error(opcode, tag, VFS_STATUS_INVALID_HANDLE), 857 }; 858 let dst_dir_idx = match resolve_handle(dst_dir_handle) { 859 Some(idx) => idx, 860 None => return VfsResponse::error(opcode, tag, VFS_STATUS_INVALID_HANDLE), 861 }; 862 863 let mut src_name_buf = [0u8; MAX_NAME_LEN]; 864 match src_name_len == 0 865 || src_name_len > MAX_NAME_LEN 866 || src_name_offset.saturating_add(src_name_len) > data_area_size 867 { 868 true => return VfsResponse::error(opcode, tag, VFS_STATUS_NAME_TOO_LONG), 869 false => unsafe { 870 core::ptr::copy_nonoverlapping( 871 data_area.add(src_name_offset), 872 src_name_buf.as_mut_ptr(), 873 src_name_len, 874 ); 875 }, 876 } 877 let src_name = &src_name_buf[..src_name_len]; 878 879 let mut dst_name_buf = [0u8; MAX_NAME_LEN]; 880 match dst_name_len == 0 881 || dst_name_len > MAX_NAME_LEN 882 || dst_name_offset.saturating_add(dst_name_len) > data_area_size 883 { 884 true => return VfsResponse::error(opcode, tag, VFS_STATUS_NAME_TOO_LONG), 885 false => unsafe { 886 core::ptr::copy_nonoverlapping( 887 data_area.add(dst_name_offset), 888 dst_name_buf.as_mut_ptr(), 889 dst_name_len, 890 ); 891 }, 892 } 893 let dst_name = &dst_name_buf[..dst_name_len]; 894 895 let (src_slot, child_idx) = { 896 let inodes_r = unsafe { &*inodes() }; 897 match inodes_r[src_dir_idx as usize].inode_type { 898 InodeType::Directory => {} 899 _ => return VfsResponse::error(opcode, tag, VFS_STATUS_NOT_A_DIRECTORY), 900 } 901 match inodes_r[dst_dir_idx as usize].inode_type { 902 InodeType::Directory => {} 903 _ => return VfsResponse::error(opcode, tag, VFS_STATUS_NOT_A_DIRECTORY), 904 } 905 906 let src_slot = match find_child(&inodes_r[src_dir_idx as usize], src_name) { 907 Some(s) => s, 908 None => return VfsResponse::error(opcode, tag, VFS_STATUS_NOT_FOUND), 909 }; 910 let child_idx = inodes_r[src_dir_idx as usize].children[src_slot].inode_idx; 911 912 if src_dir_idx == dst_dir_idx 913 && src_name_len == dst_name_len 914 && src_name[..src_name_len] == dst_name[..dst_name_len] 915 { 916 return VfsResponse::success(opcode, tag, 0, 0); 917 } 918 919 if find_child(&inodes_r[dst_dir_idx as usize], dst_name).is_some() { 920 return VfsResponse::error(opcode, tag, VFS_STATUS_FILE_EXISTS); 921 } 922 923 if src_dir_idx != dst_dir_idx 924 && inodes_r[dst_dir_idx as usize].child_count as usize >= MAX_CHILDREN 925 { 926 return VfsResponse::error(opcode, tag, VFS_STATUS_DISK_FULL); 927 } 928 929 (src_slot, child_idx) 930 }; 931 932 remove_child(unsafe { &mut (*inodes())[src_dir_idx as usize] }, src_slot); 933 match add_child( 934 unsafe { &mut (*inodes())[dst_dir_idx as usize] }, 935 dst_name, 936 child_idx, 937 ) { 938 true => VfsResponse::success(opcode, tag, 0, 0), 939 false => { 940 let _ = add_child( 941 unsafe { &mut (*inodes())[src_dir_idx as usize] }, 942 src_name, 943 child_idx, 944 ); 945 VfsResponse::error(opcode, tag, VFS_STATUS_DISK_FULL) 946 } 947 } 948 } 949 950 fn handle_truncate(req: &VfsRequest) -> VfsResponse { 951 let (opcode, tag) = (req.opcode, req.tag); 952 let new_size = req.arg0 as usize; 953 954 let inode_idx = match resolve_handle(req.handle) { 955 Some(idx) => idx, 956 None => return VfsResponse::error(opcode, tag, VFS_STATUS_INVALID_HANDLE), 957 }; 958 959 let inode = unsafe { &mut (*inodes())[inode_idx as usize] }; 960 match inode.inode_type { 961 InodeType::File => {} 962 InodeType::Directory => { 963 return VfsResponse::error(opcode, tag, VFS_STATUS_IS_A_DIRECTORY); 964 } 965 _ => return VfsResponse::error(opcode, tag, VFS_STATUS_INVALID_HANDLE), 966 } 967 968 if new_size > MAX_FILE_SIZE { 969 return VfsResponse::error(opcode, tag, VFS_STATUS_DISK_FULL); 970 } 971 972 let new_blocks = new_size.div_ceil(BLOCK_SIZE); 973 let old_blocks = inode.block_count as usize; 974 975 if new_size < inode.size as usize { 976 let tail_blk = new_size / BLOCK_SIZE; 977 let tail_off = new_size % BLOCK_SIZE; 978 if tail_off > 0 && tail_blk < old_blocks { 979 let pool_idx = inode.blocks[tail_blk]; 980 if pool_idx != NO_BLOCK { 981 unsafe { (&mut *block_data(pool_idx))[tail_off..].fill(0) }; 982 } 983 } 984 } 985 986 let bmap = unsafe { &mut *block_bitmap() }; 987 (new_blocks..old_blocks).for_each(|i| { 988 let blk = inode.blocks[i]; 989 match blk { 990 NO_BLOCK => {} 991 idx => bmap.free(idx), 992 } 993 inode.blocks[i] = NO_BLOCK; 994 }); 995 inode.block_count = new_blocks as u8; 996 inode.size = new_size as u64; 997 998 VfsResponse::success(opcode, tag, new_size as u64, 0) 999 } 1000 1001 #[unsafe(no_mangle)] 1002 pub extern "C" fn lancer_main() -> ! { 1003 lancer_user::show!(ramfs, "starting"); 1004 1005 match (0..CLIENT_RING_FRAME_COUNT).all(|i| { 1006 syscall::frame_map(CLIENT_RING_BASE_SLOT + i, CLIENT_RING_VADDR + i * 4096, 1) >= 0 1007 }) { 1008 true => {} 1009 false => { 1010 lancer_user::show!(ramfs, error, "failed to map ring"); 1011 syscall::exit(); 1012 } 1013 } 1014 1015 let base = CLIENT_RING_VADDR as *mut u8; 1016 let request_reader = unsafe { PacketRingReader::attach(base, 4096) }; 1017 let response_writer = unsafe { PacketRingWriter::init(base.wrapping_add(4096), 4096, 32) }; 1018 let data_area = (CLIENT_RING_VADDR as usize + RING_DATA_OFFSET) as *mut u8; 1019 1020 { 1021 unsafe { &mut *bitmap() }.set(0); 1022 let root = unsafe { &mut (*inodes())[0] }; 1023 root.inode_type = InodeType::Directory; 1024 root.object_id = next_object_id(); 1025 } 1026 1027 lancer_user::show!(ramfs, "root inode initialized"); 1028 1029 syscall::notify_signal(VFS_NOTIF_SLOT, 2); 1030 1031 lancer_user::show!(ramfs, "entering dispatch loop"); 1032 1033 let mut req_buf = [0u8; 64]; 1034 loop { 1035 syscall::notify_wait(NOTIF_SLOT); 1036 1037 let processed = (0..MAX_BATCH).fold(0u32, |count, _| { 1038 match request_reader.try_pop(&mut req_buf) { 1039 Some(len) if len >= VfsRequest::SIZE => { 1040 match VfsRequest::from_bytes(&req_buf) { 1041 Some(req) => { 1042 let resp = dispatch_request(&req, data_area, RING_DATA_SIZE); 1043 match response_writer.try_push(resp.as_bytes()) { 1044 true => count + 1, 1045 false => { 1046 lancer_user::show!( 1047 ramfs, 1048 warn, 1049 "response ring full, dropped" 1050 ); 1051 count 1052 } 1053 } 1054 } 1055 None => count, 1056 } 1057 } 1058 _ => count, 1059 } 1060 }); 1061 1062 if processed > 0 { 1063 let _ = syscall::notify_signal(VFS_NOTIF_SLOT, 1); 1064 } 1065 } 1066 } 1067}