Nothing to see here, move along meow
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}