Nothing to see here, move along
at main 1326 lines 39 kB view raw
1#![no_std] 2#![no_main] 3 4use lancer_core::ipc::{MAX_IPC_BYTES, unpack_bytes}; 5use lancer_core::packet_ring::{PacketRingReader, PacketRingWriter}; 6use lancer_user::path::{ 7 build_resolved_line, bytes_eq, extract_filename, first_word, skip_leading_spaces, 8}; 9use lancer_user::print; 10use lancer_user::show; 11use lancer_user::syscall; 12 13const INPUT_ENDPOINT: u64 = 1; 14const NETSTACK_RING_BASE_SLOT: u64 = 64; 15const NETSTACK_RING_FRAME_COUNT: u64 = 2; 16 17const SLOT_CHILD_PROC: u64 = 4; 18const SLOT_CHILD_SCHED: u64 = 5; 19const SLOT_CHILD_ENDPOINT: u64 = 6; 20const SLOT_CHILD_NOTIF: u64 = 7; 21 22const TAG_ENDPOINT: u64 = 1; 23const TAG_NOTIFICATION: u64 = 2; 24const TAG_SCHED_CONTEXT: u64 = 5; 25const TAG_FRAME: u64 = 11; 26const RIGHTS_ALL: u64 = 0b1111; 27 28const SLOT_UNTYPED: u64 = 28; 29 30const BOOT_PROCESS_COUNT: u64 = 7; 31 32const NETSOCK_MAP_VADDR: u64 = 0x5000_0000; 33const NETSOCK_RING_HALF: usize = 2048; 34const NETSOCK_SLOT_SIZE: u32 = 64; 35const SLOT_NETSOCK_MEM: u64 = 8; 36 37const SLOT_SHELL_NOTIF: u64 = 9; 38const SLOT_NETSTACK_NOTIF: u64 = 10; 39const VFS_RING_BASE_SLOT: u64 = 128; 40const VFS_RING_FRAME_COUNT_SHELL: u64 = 16; 41const SLOT_VFS_CLIENT_NOTIF: u64 = 12; 42const SLOT_VFS_NOTIF: u64 = 13; 43const SLOT_CHILD_NETSOCK_NOTIF: u64 = 14; 44const SLOT_VFS_PROC: u64 = 15; 45const SLOT_CHILD_PROC_B: u64 = 16; 46const SLOT_CHILD_SCHED_B: u64 = 17; 47const SLOT_CHILD_ENDPOINT_B: u64 = 18; 48const SLOT_NETSOCK_MEM_B: u64 = 19; 49const SLOT_CHILD_NETSOCK_NOTIF_B: u64 = 20; 50const SLOT_TEMP_CLIENT_RING: u64 = 200; 51const SLOT_TEMP_CLIENT_NOTIF: u64 = 216; 52const SLOT_TEMP_CLIENT_RING_B: u64 = 220; 53const SLOT_TEMP_CLIENT_NOTIF_B: u64 = 236; 54 55const VFS_RING_VADDR: u64 = 0x7000_0000; 56const VFS_CLIENT_INIT_VADDR: u64 = 0x4000_0000; 57const NETSTACK_RING_VADDR: u64 = 0x6000_0000; 58const NETSTACK_RING_HALF: usize = 4096; 59const VFS_RING_HALF: usize = 4096; 60const VFS_RING_SLOT_SIZE: u32 = 48; 61const VFS_RING_PAGES: u64 = 16; 62const VFS_CLIENT_RING_BASE_SLOT: u64 = 160; 63const VFS_CLIENT_SLOT_STRIDE: u64 = 32; 64 65const INPUT_RING_VADDR: u64 = 0x3000_0000; 66const INPUT_RING_SIZE: usize = 4096; 67 68const NOTIFY_BIT_CHILD_DEATH: u64 = 0x01; 69 70struct LineBuf { 71 data: [u8; 128], 72 len: usize, 73} 74 75impl LineBuf { 76 const fn new() -> Self { 77 Self { 78 data: [0u8; 128], 79 len: 0, 80 } 81 } 82 83 fn push(&mut self, b: u8) -> bool { 84 match self.len < self.data.len() { 85 true => { 86 self.data[self.len] = b; 87 self.len += 1; 88 true 89 } 90 false => false, 91 } 92 } 93 94 fn pop(&mut self) -> bool { 95 match self.len > 0 { 96 true => { 97 self.len -= 1; 98 true 99 } 100 false => false, 101 } 102 } 103 104 fn clear(&mut self) { 105 self.len = 0; 106 } 107 108 fn as_bytes(&self) -> &[u8] { 109 &self.data[..self.len] 110 } 111} 112 113const MAX_VFS_CLIENTS: usize = 8; 114 115struct VfsClientTracker { 116 free: [bool; MAX_VFS_CLIENTS], 117} 118 119impl VfsClientTracker { 120 const fn new() -> Self { 121 Self { 122 free: [false, true, true, true, true, true, true, true], 123 } 124 } 125 126 fn alloc(&mut self) -> Option<u8> { 127 (1..MAX_VFS_CLIENTS as u8) 128 .find(|&i| self.free[i as usize]) 129 .inspect(|&i| { 130 self.free[i as usize] = false; 131 }) 132 } 133 134 fn release(&mut self, idx: u8) { 135 if (idx as usize) < MAX_VFS_CLIENTS && idx > 0 { 136 self.free[idx as usize] = true; 137 } 138 } 139} 140 141fn create_vfs_client(client_idx: u8, child_proc_slot: u64, ring_tmp: u64, notif_tmp: u64) -> bool { 142 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_FRAME, 0, ring_tmp, VFS_RING_PAGES); 143 if r < 0 { 144 return false; 145 } 146 147 let map_ok = (0..VFS_RING_PAGES) 148 .all(|i| syscall::frame_map(ring_tmp + i, VFS_CLIENT_INIT_VADDR + i * 4096, 1) >= 0); 149 if !map_ok { 150 (0..VFS_RING_PAGES).for_each(|i| { 151 syscall::frame_unmap(ring_tmp + i, VFS_CLIENT_INIT_VADDR + i * 4096); 152 }); 153 (0..VFS_RING_PAGES).for_each(|i| { 154 syscall::cap_revoke(ring_tmp + i); 155 }); 156 return false; 157 } 158 159 let base = VFS_CLIENT_INIT_VADDR as *mut u8; 160 let _ = unsafe { PacketRingWriter::init(base, VFS_RING_HALF, VFS_RING_SLOT_SIZE) }; 161 let _ = unsafe { 162 PacketRingWriter::init( 163 base.wrapping_add(VFS_RING_HALF), 164 VFS_RING_HALF, 165 VFS_RING_SLOT_SIZE, 166 ) 167 }; 168 169 (0..VFS_RING_PAGES).for_each(|i| { 170 syscall::frame_unmap(ring_tmp + i, VFS_CLIENT_INIT_VADDR + i * 4096); 171 }); 172 173 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_NOTIFICATION, 0, notif_tmp, 1); 174 if r < 0 { 175 (0..VFS_RING_PAGES).for_each(|i| { 176 syscall::cap_revoke(ring_tmp + i); 177 }); 178 return false; 179 } 180 181 let child_ring_ok = (0..VFS_RING_PAGES) 182 .all(|i| syscall::cap_grant(ring_tmp + i, child_proc_slot, 11 + i, RIGHTS_ALL) >= 0); 183 if !child_ring_ok { 184 syscall::cap_revoke(notif_tmp); 185 (0..VFS_RING_PAGES).for_each(|i| { 186 syscall::cap_revoke(ring_tmp + i); 187 }); 188 return false; 189 } 190 191 let r = syscall::cap_grant(notif_tmp, child_proc_slot, 12 + VFS_RING_PAGES, RIGHTS_ALL); 192 if r < 0 { 193 syscall::cap_revoke(notif_tmp); 194 (0..VFS_RING_PAGES).for_each(|i| { 195 syscall::cap_revoke(ring_tmp + i); 196 }); 197 return false; 198 } 199 200 let r = syscall::cap_grant( 201 SLOT_VFS_NOTIF, 202 child_proc_slot, 203 13 + VFS_RING_PAGES, 204 RIGHTS_ALL, 205 ); 206 if r < 0 { 207 syscall::cap_revoke(notif_tmp); 208 (0..VFS_RING_PAGES).for_each(|i| { 209 syscall::cap_revoke(ring_tmp + i); 210 }); 211 return false; 212 } 213 214 let vfs_ring_base = VFS_CLIENT_RING_BASE_SLOT + (client_idx as u64) * VFS_CLIENT_SLOT_STRIDE; 215 let vfs_notif_slot = vfs_ring_base + VFS_RING_PAGES; 216 217 let vfs_ring_ok = (0..VFS_RING_PAGES).all(|i| { 218 syscall::cap_grant(ring_tmp + i, SLOT_VFS_PROC, vfs_ring_base + i, RIGHTS_ALL) >= 0 219 }); 220 if !vfs_ring_ok { 221 syscall::cap_revoke(notif_tmp); 222 (0..VFS_RING_PAGES).for_each(|i| { 223 syscall::cap_revoke(ring_tmp + i); 224 }); 225 return false; 226 } 227 228 let r = syscall::cap_grant(notif_tmp, SLOT_VFS_PROC, vfs_notif_slot, RIGHTS_ALL); 229 if r < 0 { 230 syscall::cap_revoke(notif_tmp); 231 (0..VFS_RING_PAGES).for_each(|i| { 232 syscall::cap_revoke(ring_tmp + i); 233 }); 234 return false; 235 } 236 237 let mut fs_client = unsafe { 238 lancer_user::fs::FsClient::reattach( 239 VFS_RING_VADDR as usize, 240 SLOT_VFS_NOTIF as u8, 241 SLOT_VFS_CLIENT_NOTIF as u8, 242 ) 243 }; 244 let reg_result = 245 fs_client.extended_raw(lancer_vfs_proto::EXT_REGISTER_CLIENT, client_idx as u64, 0); 246 247 reg_result.is_ok() 248} 249 250fn destroy_vfs_client(client_idx: u8) { 251 let mut fs_client = unsafe { 252 lancer_user::fs::FsClient::reattach( 253 VFS_RING_VADDR as usize, 254 SLOT_VFS_NOTIF as u8, 255 SLOT_VFS_CLIENT_NOTIF as u8, 256 ) 257 }; 258 let _ = fs_client.extended_raw( 259 lancer_vfs_proto::EXT_UNREGISTER_CLIENT, 260 client_idx as u64, 261 0, 262 ); 263} 264 265fn print_prompt(cwd: &[u8], cwd_len: usize) { 266 let path = &cwd[..cwd_len]; 267 print!("lancer:"); 268 match cwd_len <= 32 { 269 true => { 270 if let Ok(s) = core::str::from_utf8(path) { 271 print!("{s}"); 272 } 273 } 274 false => { 275 let tail = &path[cwd_len - 29..]; 276 print!("..."); 277 if let Ok(s) = core::str::from_utf8(tail) { 278 print!("{s}"); 279 } 280 } 281 } 282 print!("> "); 283} 284 285fn find_module_by_name(name: &[u8]) -> Option<u64> { 286 let mut dummy = [0u8; 0]; 287 let total = syscall::module_info(u64::MAX, &mut dummy); 288 if total < 0 { 289 return None; 290 } 291 292 let mut path_buf = [0u8; 128]; 293 (BOOT_PROCESS_COUNT..total as u64).find(|&i| { 294 path_buf = [0u8; 128]; 295 let path_len = syscall::module_info(i, &mut path_buf); 296 match path_len < 0 { 297 true => false, 298 false => { 299 let used = (path_len as usize).min(path_buf.len()); 300 let path = &path_buf[..used]; 301 let filename = extract_filename(path); 302 bytes_eq(filename, name) 303 } 304 } 305 }) 306} 307 308struct ChildNetsock { 309 tx: PacketRingWriter, 310 rx: PacketRingReader, 311} 312 313fn setup_child_netsock( 314 line: &[u8], 315 has_netring: bool, 316 child_proc_slot: u64, 317 mem_slot: u64, 318 notif_slot: u64, 319) -> Option<ChildNetsock> { 320 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_FRAME, 0, mem_slot, 1); 321 if r < 0 { 322 return None; 323 } 324 325 let r = syscall::frame_map(mem_slot, NETSOCK_MAP_VADDR, 1); 326 if r < 0 { 327 syscall::cap_revoke(mem_slot); 328 return None; 329 } 330 331 let init_to_child = NETSOCK_MAP_VADDR as *mut u8; 332 let child_to_init = (NETSOCK_MAP_VADDR + NETSOCK_RING_HALF as u64) as *mut u8; 333 334 let tx = unsafe { PacketRingWriter::init(init_to_child, NETSOCK_RING_HALF, NETSOCK_SLOT_SIZE) }; 335 let _ = unsafe { PacketRingWriter::init(child_to_init, NETSOCK_RING_HALF, NETSOCK_SLOT_SIZE) }; 336 let rx = unsafe { PacketRingReader::attach(child_to_init, NETSOCK_RING_HALF) }; 337 338 let _ = tx.try_push(line); 339 340 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_NOTIFICATION, 0, notif_slot, 1); 341 if r < 0 { 342 syscall::frame_unmap(mem_slot, NETSOCK_MAP_VADDR); 343 syscall::cap_revoke(mem_slot); 344 return None; 345 } 346 347 let r = syscall::cap_grant(mem_slot, child_proc_slot, 3, RIGHTS_ALL); 348 if r < 0 { 349 syscall::cap_revoke(notif_slot); 350 syscall::frame_unmap(mem_slot, NETSOCK_MAP_VADDR); 351 syscall::cap_revoke(mem_slot); 352 return None; 353 } 354 355 let r = syscall::cap_grant(notif_slot, child_proc_slot, 4, RIGHTS_ALL); 356 if r < 0 { 357 syscall::cap_revoke(notif_slot); 358 syscall::frame_unmap(mem_slot, NETSOCK_MAP_VADDR); 359 syscall::cap_revoke(mem_slot); 360 return None; 361 } 362 363 if has_netring { 364 let r = syscall::cap_grant(SLOT_SHELL_NOTIF, child_proc_slot, 5, 0b0010); 365 if r < 0 { 366 syscall::cap_revoke(notif_slot); 367 syscall::frame_unmap(mem_slot, NETSOCK_MAP_VADDR); 368 syscall::cap_revoke(mem_slot); 369 return None; 370 } 371 } 372 373 Some(ChildNetsock { tx, rx }) 374} 375 376fn cleanup_child_netsock(mem_slot: u64, notif_slot: u64) { 377 syscall::frame_unmap(mem_slot, NETSOCK_MAP_VADDR); 378 syscall::cap_revoke(mem_slot); 379 syscall::cap_revoke(notif_slot); 380} 381 382fn relay_child_to_netstack(child_rx: &PacketRingReader, netstack_tx: &PacketRingWriter) -> bool { 383 let mut buf = [0u8; 64]; 384 let mut relayed = false; 385 386 core::iter::from_fn(|| match netstack_tx.has_space() { 387 false => None, 388 true => match child_rx.try_pop(&mut buf[1..]) { 389 Some(n) if n > 0 => { 390 buf[0] = 0; 391 let _ = netstack_tx.try_push(&buf[..1 + n]); 392 relayed = true; 393 Some(()) 394 } 395 _ => None, 396 }, 397 }) 398 .take(16) 399 .count(); 400 401 if relayed { 402 syscall::notify_signal(SLOT_NETSTACK_NOTIF, 4); 403 } 404 405 relayed 406} 407 408fn relay_netstack_to_child(netstack_rx: &PacketRingReader, child_tx: &PacketRingWriter) -> bool { 409 let mut buf = [0u8; 128]; 410 let mut relayed = false; 411 412 core::iter::from_fn(|| match netstack_rx.try_pop(&mut buf) { 413 Some(n) if n >= 2 => match child_tx.try_push(&buf[1..n]) { 414 true => { 415 relayed = true; 416 Some(()) 417 } 418 false => None, 419 }, 420 _ => None, 421 }) 422 .take(16) 423 .count(); 424 425 if relayed { 426 syscall::notify_signal(SLOT_CHILD_NETSOCK_NOTIF, 1); 427 } 428 429 relayed 430} 431 432#[allow(clippy::too_many_arguments)] 433fn spawn_module( 434 module_idx: u64, 435 line: &[u8], 436 has_netring: bool, 437 has_vfs: bool, 438 netstack_tx: Option<&PacketRingWriter>, 439 netstack_rx: Option<&PacketRingReader>, 440 vfs_tracker: &mut VfsClientTracker, 441 remote: bool, 442) -> Option<bool> { 443 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_ENDPOINT, 0, SLOT_CHILD_ENDPOINT, 1); 444 if r < 0 { 445 print!("error: failed to create endpoint\n"); 446 return None; 447 } 448 449 let child_pid = syscall::proc_create(SLOT_UNTYPED, SLOT_CHILD_PROC); 450 if child_pid < 0 { 451 print!("error: failed to create process\n"); 452 syscall::cap_revoke(SLOT_CHILD_ENDPOINT); 453 return None; 454 } 455 456 let r = syscall::proc_load_module(SLOT_CHILD_PROC, module_idx); 457 if r < 0 { 458 print!("error: failed to load module\n"); 459 syscall::proc_destroy(SLOT_CHILD_PROC); 460 syscall::cap_revoke(SLOT_CHILD_ENDPOINT); 461 return None; 462 } 463 464 let r = syscall::cap_grant(SLOT_CHILD_ENDPOINT, SLOT_CHILD_PROC, 2, RIGHTS_ALL); 465 if r < 0 { 466 print!("error: failed to grant endpoint\n"); 467 syscall::proc_destroy(SLOT_CHILD_PROC); 468 syscall::cap_revoke(SLOT_CHILD_ENDPOINT); 469 return None; 470 } 471 472 let vfs_client_idx = match has_vfs { 473 true => match vfs_tracker.alloc() { 474 None => None, 475 Some(idx) => { 476 syscall::cap_revoke(SLOT_TEMP_CLIENT_RING); 477 syscall::cap_revoke(SLOT_TEMP_CLIENT_NOTIF); 478 match create_vfs_client( 479 idx, 480 SLOT_CHILD_PROC, 481 SLOT_TEMP_CLIENT_RING, 482 SLOT_TEMP_CLIENT_NOTIF, 483 ) { 484 true => Some(idx), 485 false => { 486 vfs_tracker.release(idx); 487 None 488 } 489 } 490 } 491 }, 492 false => None, 493 }; 494 495 let _ = syscall::cap_grant(SLOT_UNTYPED, SLOT_CHILD_PROC, 30, RIGHTS_ALL); 496 497 let netsock = setup_child_netsock( 498 line, 499 has_netring, 500 SLOT_CHILD_PROC, 501 SLOT_NETSOCK_MEM, 502 SLOT_CHILD_NETSOCK_NOTIF, 503 ); 504 505 let death_notif_slot = match has_netring { 506 true => SLOT_SHELL_NOTIF, 507 false => { 508 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_NOTIFICATION, 0, SLOT_CHILD_NOTIF, 1); 509 if r < 0 { 510 if netsock.is_some() { 511 cleanup_child_netsock(SLOT_NETSOCK_MEM, SLOT_CHILD_NETSOCK_NOTIF); 512 } 513 if let Some(idx) = vfs_client_idx { 514 destroy_vfs_client(idx); 515 vfs_tracker.release(idx); 516 } 517 syscall::proc_destroy(SLOT_CHILD_PROC); 518 syscall::cap_revoke(SLOT_CHILD_ENDPOINT); 519 return None; 520 } 521 SLOT_CHILD_NOTIF 522 } 523 }; 524 525 syscall::proc_bind_death_notif(SLOT_CHILD_PROC, death_notif_slot, NOTIFY_BIT_CHILD_DEATH); 526 527 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_SCHED_CONTEXT, 0, SLOT_CHILD_SCHED, 1); 528 if r < 0 { 529 print!("error: failed to create sched context\n"); 530 if netsock.is_some() { 531 cleanup_child_netsock(SLOT_NETSOCK_MEM, SLOT_CHILD_NETSOCK_NOTIF); 532 } 533 if let Some(idx) = vfs_client_idx { 534 destroy_vfs_client(idx); 535 vfs_tracker.release(idx); 536 } 537 syscall::proc_destroy(SLOT_CHILD_PROC); 538 if !has_netring { 539 syscall::cap_revoke(SLOT_CHILD_NOTIF); 540 } 541 syscall::cap_revoke(SLOT_CHILD_ENDPOINT); 542 return None; 543 } 544 545 let r = syscall::sched_configure(SLOT_CHILD_SCHED, 10_000_000, 10_000_000, 100); 546 if r < 0 { 547 print!("error: failed to configure sched context\n"); 548 if netsock.is_some() { 549 cleanup_child_netsock(SLOT_NETSOCK_MEM, SLOT_CHILD_NETSOCK_NOTIF); 550 } 551 if let Some(idx) = vfs_client_idx { 552 destroy_vfs_client(idx); 553 vfs_tracker.release(idx); 554 } 555 syscall::proc_destroy(SLOT_CHILD_PROC); 556 syscall::cap_revoke(SLOT_CHILD_SCHED); 557 if !has_netring { 558 syscall::cap_revoke(SLOT_CHILD_NOTIF); 559 } 560 syscall::cap_revoke(SLOT_CHILD_ENDPOINT); 561 return None; 562 } 563 564 let r = syscall::sched_attach(SLOT_CHILD_SCHED, child_pid as u64); 565 if r < 0 { 566 print!("error: failed to attach sched context\n"); 567 if netsock.is_some() { 568 cleanup_child_netsock(SLOT_NETSOCK_MEM, SLOT_CHILD_NETSOCK_NOTIF); 569 } 570 if let Some(idx) = vfs_client_idx { 571 destroy_vfs_client(idx); 572 vfs_tracker.release(idx); 573 } 574 syscall::proc_destroy(SLOT_CHILD_PROC); 575 syscall::cap_revoke(SLOT_CHILD_SCHED); 576 if !has_netring { 577 syscall::cap_revoke(SLOT_CHILD_NOTIF); 578 } 579 syscall::cap_revoke(SLOT_CHILD_ENDPOINT); 580 return None; 581 } 582 583 let bootstrap_val = match remote { 584 true => 2, 585 false => 0, 586 }; 587 let r = syscall::proc_start(SLOT_CHILD_PROC, bootstrap_val); 588 if r < 0 { 589 print!("error: failed to start process\n"); 590 if netsock.is_some() { 591 cleanup_child_netsock(SLOT_NETSOCK_MEM, SLOT_CHILD_NETSOCK_NOTIF); 592 } 593 if let Some(idx) = vfs_client_idx { 594 destroy_vfs_client(idx); 595 vfs_tracker.release(idx); 596 } 597 syscall::proc_destroy(SLOT_CHILD_PROC); 598 syscall::cap_revoke(SLOT_CHILD_SCHED); 599 if !has_netring { 600 syscall::cap_revoke(SLOT_CHILD_NOTIF); 601 } 602 syscall::cap_revoke(SLOT_CHILD_ENDPOINT); 603 return None; 604 } 605 606 let can_relay = 607 has_netring && netsock.is_some() && netstack_tx.is_some() && netstack_rx.is_some(); 608 609 let had_output = match (can_relay, remote) { 610 (true, false) => { 611 let child_netsock = netsock.unwrap(); 612 let ns_tx = netstack_tx.unwrap(); 613 let ns_rx = netstack_rx.unwrap(); 614 run_relay_loop(&child_netsock, ns_tx, ns_rx); 615 cleanup_child_netsock(SLOT_NETSOCK_MEM, SLOT_CHILD_NETSOCK_NOTIF); 616 true 617 } 618 (true, true) => { 619 let child_netsock = netsock.unwrap(); 620 let ns_tx = netstack_tx.unwrap(); 621 let ns_rx = netstack_rx.unwrap(); 622 run_relay_loop_remote(&child_netsock, ns_tx, ns_rx); 623 cleanup_child_netsock(SLOT_NETSOCK_MEM, SLOT_CHILD_NETSOCK_NOTIF); 624 true 625 } 626 (_, true) => { 627 run_simple_wait_remote(death_notif_slot); 628 if netsock.is_some() { 629 cleanup_child_netsock(SLOT_NETSOCK_MEM, SLOT_CHILD_NETSOCK_NOTIF); 630 } 631 false 632 } 633 (_, false) => { 634 run_simple_wait(death_notif_slot); 635 if netsock.is_some() { 636 cleanup_child_netsock(SLOT_NETSOCK_MEM, SLOT_CHILD_NETSOCK_NOTIF); 637 } 638 false 639 } 640 }; 641 642 if let Some(idx) = vfs_client_idx { 643 destroy_vfs_client(idx); 644 vfs_tracker.release(idx); 645 } 646 647 syscall::cap_revoke(SLOT_CHILD_SCHED); 648 if !has_netring { 649 syscall::cap_revoke(SLOT_CHILD_NOTIF); 650 } 651 syscall::cap_revoke(SLOT_CHILD_ENDPOINT); 652 653 Some(had_output) 654} 655 656fn run_simple_wait(death_notif_slot: u64) { 657 loop { 658 let (_, bits) = syscall::notify_wait(death_notif_slot); 659 if bits & NOTIFY_BIT_CHILD_DEATH != 0 { 660 syscall::proc_destroy(SLOT_CHILD_PROC); 661 break; 662 } 663 } 664} 665 666fn run_simple_wait_remote(death_notif_slot: u64) { 667 syscall::ntfn_bind(death_notif_slot); 668 loop { 669 let (status, msg) = syscall::recv(SLOT_CHILD_ENDPOINT); 670 match status == syscall::BOUND_NOTIFICATION_BADGE { 671 true => { 672 if msg[1] & NOTIFY_BIT_CHILD_DEATH != 0 { 673 syscall::proc_destroy(SLOT_CHILD_PROC); 674 break; 675 } 676 } 677 false if status >= 0 => { 678 let _ = syscall::send(2, msg); 679 } 680 _ => {} 681 } 682 } 683 syscall::ntfn_unbind(); 684} 685 686fn drain_stale_netstack_replies(netstack_rx: &PacketRingReader) { 687 let mut discard = [0u8; 128]; 688 core::iter::from_fn(|| netstack_rx.try_pop(&mut discard).map(|_| ())) 689 .take(64) 690 .count(); 691} 692 693fn run_relay_loop( 694 child_netsock: &ChildNetsock, 695 netstack_tx: &PacketRingWriter, 696 netstack_rx: &PacketRingReader, 697) { 698 drain_stale_netstack_replies(netstack_rx); 699 700 loop { 701 let (_, bits) = syscall::notify_wait(SLOT_SHELL_NOTIF); 702 703 relay_child_to_netstack(&child_netsock.rx, netstack_tx); 704 relay_netstack_to_child(netstack_rx, &child_netsock.tx); 705 706 if bits & NOTIFY_BIT_CHILD_DEATH != 0 { 707 relay_child_to_netstack(&child_netsock.rx, netstack_tx); 708 syscall::proc_destroy(SLOT_CHILD_PROC); 709 break; 710 } 711 } 712} 713 714fn run_relay_loop_remote( 715 child_netsock: &ChildNetsock, 716 netstack_tx: &PacketRingWriter, 717 netstack_rx: &PacketRingReader, 718) { 719 drain_stale_netstack_replies(netstack_rx); 720 721 syscall::ntfn_bind(SLOT_SHELL_NOTIF); 722 loop { 723 let (status, msg) = syscall::recv(SLOT_CHILD_ENDPOINT); 724 match status == syscall::BOUND_NOTIFICATION_BADGE { 725 true => { 726 let bits = msg[1]; 727 relay_child_to_netstack(&child_netsock.rx, netstack_tx); 728 relay_netstack_to_child(netstack_rx, &child_netsock.tx); 729 730 if bits & NOTIFY_BIT_CHILD_DEATH != 0 { 731 relay_child_to_netstack(&child_netsock.rx, netstack_tx); 732 syscall::proc_destroy(SLOT_CHILD_PROC); 733 break; 734 } 735 } 736 false if status >= 0 => { 737 let _ = syscall::send(2, msg); 738 } 739 _ => {} 740 } 741 } 742 syscall::ntfn_unbind(); 743} 744 745#[allow(clippy::too_many_arguments)] 746fn spawn_conc_child( 747 module_idx: u64, 748 args: &[u8], 749 proc_slot: u64, 750 sched_slot: u64, 751 endpoint_slot: u64, 752 death_notif_slot: u64, 753 death_bit: u64, 754 vfs_client_idx: u8, 755 vfs_tracker: &mut VfsClientTracker, 756 netsock_mem: u64, 757 netsock_notif: u64, 758 vfs_ring_tmp: u64, 759 vfs_notif_tmp: u64, 760) -> bool { 761 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_ENDPOINT, 0, endpoint_slot, 1); 762 if r < 0 { 763 return false; 764 } 765 766 let child_pid = syscall::proc_create(SLOT_UNTYPED, proc_slot); 767 if child_pid < 0 { 768 syscall::cap_revoke(endpoint_slot); 769 return false; 770 } 771 772 let r = syscall::proc_load_module(proc_slot, module_idx); 773 if r < 0 { 774 syscall::proc_destroy(proc_slot); 775 syscall::cap_revoke(endpoint_slot); 776 return false; 777 } 778 779 let r = syscall::cap_grant(endpoint_slot, proc_slot, 2, RIGHTS_ALL); 780 if r < 0 { 781 syscall::proc_destroy(proc_slot); 782 syscall::cap_revoke(endpoint_slot); 783 return false; 784 } 785 786 match create_vfs_client(vfs_client_idx, proc_slot, vfs_ring_tmp, vfs_notif_tmp) { 787 true => {} 788 false => { 789 vfs_tracker.release(vfs_client_idx); 790 syscall::proc_destroy(proc_slot); 791 syscall::cap_revoke(endpoint_slot); 792 return false; 793 } 794 } 795 796 let _ = syscall::cap_grant(SLOT_UNTYPED, proc_slot, 30, RIGHTS_ALL); 797 798 let netsock = setup_child_netsock(args, false, proc_slot, netsock_mem, netsock_notif); 799 if netsock.is_none() { 800 destroy_vfs_client(vfs_client_idx); 801 vfs_tracker.release(vfs_client_idx); 802 syscall::cap_revoke(vfs_ring_tmp); 803 syscall::cap_revoke(vfs_notif_tmp); 804 syscall::proc_destroy(proc_slot); 805 syscall::cap_revoke(endpoint_slot); 806 return false; 807 } 808 809 syscall::proc_bind_death_notif(proc_slot, death_notif_slot, death_bit); 810 811 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_SCHED_CONTEXT, 0, sched_slot, 1); 812 if r < 0 { 813 cleanup_child_netsock(netsock_mem, netsock_notif); 814 destroy_vfs_client(vfs_client_idx); 815 vfs_tracker.release(vfs_client_idx); 816 syscall::cap_revoke(vfs_ring_tmp); 817 syscall::cap_revoke(vfs_notif_tmp); 818 syscall::proc_destroy(proc_slot); 819 syscall::cap_revoke(endpoint_slot); 820 return false; 821 } 822 823 let r = syscall::sched_configure(sched_slot, 10_000_000, 10_000_000, 100); 824 if r < 0 { 825 cleanup_child_netsock(netsock_mem, netsock_notif); 826 destroy_vfs_client(vfs_client_idx); 827 vfs_tracker.release(vfs_client_idx); 828 syscall::cap_revoke(vfs_ring_tmp); 829 syscall::cap_revoke(vfs_notif_tmp); 830 syscall::proc_destroy(proc_slot); 831 syscall::cap_revoke(sched_slot); 832 syscall::cap_revoke(endpoint_slot); 833 return false; 834 } 835 836 let r = syscall::sched_attach(sched_slot, child_pid as u64); 837 if r < 0 { 838 cleanup_child_netsock(netsock_mem, netsock_notif); 839 destroy_vfs_client(vfs_client_idx); 840 vfs_tracker.release(vfs_client_idx); 841 syscall::cap_revoke(vfs_ring_tmp); 842 syscall::cap_revoke(vfs_notif_tmp); 843 syscall::proc_destroy(proc_slot); 844 syscall::cap_revoke(sched_slot); 845 syscall::cap_revoke(endpoint_slot); 846 return false; 847 } 848 849 let r = syscall::proc_start(proc_slot, 0); 850 if r < 0 { 851 cleanup_child_netsock(netsock_mem, netsock_notif); 852 destroy_vfs_client(vfs_client_idx); 853 vfs_tracker.release(vfs_client_idx); 854 syscall::cap_revoke(vfs_ring_tmp); 855 syscall::cap_revoke(vfs_notif_tmp); 856 syscall::proc_destroy(proc_slot); 857 syscall::cap_revoke(sched_slot); 858 syscall::cap_revoke(endpoint_slot); 859 return false; 860 } 861 862 syscall::frame_unmap(netsock_mem, NETSOCK_MAP_VADDR); 863 true 864} 865 866fn cleanup_conc_child( 867 proc_slot: u64, 868 sched_slot: u64, 869 endpoint_slot: u64, 870 vfs_client_idx: u8, 871 vfs_tracker: &mut VfsClientTracker, 872) { 873 destroy_vfs_client(vfs_client_idx); 874 vfs_tracker.release(vfs_client_idx); 875 syscall::proc_destroy(proc_slot); 876 syscall::cap_revoke(sched_slot); 877 syscall::cap_revoke(endpoint_slot); 878} 879 880fn run_conctest(vfs_tracker: &mut VfsClientTracker) { 881 let module_idx = match find_module_by_name(b"fstest") { 882 Some(idx) => idx, 883 None => { 884 show!(rsh, error, "fstest module not found"); 885 return; 886 } 887 }; 888 889 let vfs_idx_a = match vfs_tracker.alloc() { 890 Some(idx) => idx, 891 None => { 892 show!(rsh, error, "no free vfs client slots"); 893 return; 894 } 895 }; 896 897 let vfs_idx_b = match vfs_tracker.alloc() { 898 Some(idx) => idx, 899 None => { 900 vfs_tracker.release(vfs_idx_a); 901 show!(rsh, error, "no free vfs client slots for b"); 902 return; 903 } 904 }; 905 906 let r = syscall::untyped_retype(SLOT_UNTYPED, TAG_NOTIFICATION, 0, SLOT_CHILD_NOTIF, 1); 907 if r < 0 { 908 vfs_tracker.release(vfs_idx_b); 909 vfs_tracker.release(vfs_idx_a); 910 show!(rsh, error, "failed to create death notif"); 911 return; 912 } 913 914 syscall::cap_revoke(SLOT_TEMP_CLIENT_RING); 915 syscall::cap_revoke(SLOT_TEMP_CLIENT_NOTIF); 916 syscall::cap_revoke(SLOT_NETSOCK_MEM); 917 syscall::cap_revoke(SLOT_CHILD_NETSOCK_NOTIF); 918 919 let ok_a = spawn_conc_child( 920 module_idx, 921 b"fstest concurrent-a", 922 SLOT_CHILD_PROC, 923 SLOT_CHILD_SCHED, 924 SLOT_CHILD_ENDPOINT, 925 SLOT_CHILD_NOTIF, 926 0x01, 927 vfs_idx_a, 928 vfs_tracker, 929 SLOT_NETSOCK_MEM, 930 SLOT_CHILD_NETSOCK_NOTIF, 931 SLOT_TEMP_CLIENT_RING, 932 SLOT_TEMP_CLIENT_NOTIF, 933 ); 934 935 if !ok_a { 936 vfs_tracker.release(vfs_idx_b); 937 syscall::cap_revoke(SLOT_CHILD_NOTIF); 938 show!(rsh, error, "failed to spawn child a"); 939 return; 940 } 941 942 let ok_b = spawn_conc_child( 943 module_idx, 944 b"fstest concurrent-b", 945 SLOT_CHILD_PROC_B, 946 SLOT_CHILD_SCHED_B, 947 SLOT_CHILD_ENDPOINT_B, 948 SLOT_CHILD_NOTIF, 949 0x02, 950 vfs_idx_b, 951 vfs_tracker, 952 SLOT_NETSOCK_MEM_B, 953 SLOT_CHILD_NETSOCK_NOTIF_B, 954 SLOT_TEMP_CLIENT_RING_B, 955 SLOT_TEMP_CLIENT_NOTIF_B, 956 ); 957 958 if !ok_b { 959 cleanup_conc_child( 960 SLOT_CHILD_PROC, 961 SLOT_CHILD_SCHED, 962 SLOT_CHILD_ENDPOINT, 963 vfs_idx_a, 964 vfs_tracker, 965 ); 966 syscall::cap_revoke(SLOT_TEMP_CLIENT_RING); 967 syscall::cap_revoke(SLOT_TEMP_CLIENT_NOTIF); 968 syscall::cap_revoke(SLOT_NETSOCK_MEM); 969 syscall::cap_revoke(SLOT_CHILD_NETSOCK_NOTIF); 970 syscall::cap_revoke(SLOT_CHILD_NOTIF); 971 show!(rsh, error, "failed to spawn child b"); 972 return; 973 } 974 975 let mut deaths: u64 = 0; 976 core::iter::from_fn(|| match deaths & 0x03 == 0x03 { 977 true => None, 978 false => { 979 let (_, bits) = syscall::notify_wait(SLOT_CHILD_NOTIF); 980 deaths |= bits; 981 Some(()) 982 } 983 }) 984 .take(100) 985 .count(); 986 987 let both_dead = deaths & 0x03 == 0x03; 988 if !both_dead { 989 show!(rsh, error, "timed out waiting for children"); 990 } 991 992 cleanup_conc_child( 993 SLOT_CHILD_PROC, 994 SLOT_CHILD_SCHED, 995 SLOT_CHILD_ENDPOINT, 996 vfs_idx_a, 997 vfs_tracker, 998 ); 999 cleanup_conc_child( 1000 SLOT_CHILD_PROC_B, 1001 SLOT_CHILD_SCHED_B, 1002 SLOT_CHILD_ENDPOINT_B, 1003 vfs_idx_b, 1004 vfs_tracker, 1005 ); 1006 syscall::cap_revoke(SLOT_TEMP_CLIENT_RING); 1007 syscall::cap_revoke(SLOT_TEMP_CLIENT_NOTIF); 1008 syscall::cap_revoke(SLOT_TEMP_CLIENT_RING_B); 1009 syscall::cap_revoke(SLOT_TEMP_CLIENT_NOTIF_B); 1010 syscall::cap_revoke(SLOT_NETSOCK_MEM); 1011 syscall::cap_revoke(SLOT_CHILD_NETSOCK_NOTIF); 1012 syscall::cap_revoke(SLOT_NETSOCK_MEM_B); 1013 syscall::cap_revoke(SLOT_CHILD_NETSOCK_NOTIF_B); 1014 syscall::cap_revoke(SLOT_CHILD_NOTIF); 1015 1016 if !both_dead { 1017 return; 1018 } 1019 1020 let mut fs_client = unsafe { 1021 lancer_user::fs::FsClient::reattach( 1022 VFS_RING_VADDR as usize, 1023 SLOT_VFS_NOTIF as u8, 1024 SLOT_VFS_CLIENT_NOTIF as u8, 1025 ) 1026 }; 1027 1028 let mut pass = 0u32; 1029 let mut fail = 0u32; 1030 1031 let mut buf_a = [0u8; 64]; 1032 match fs_client.open(0, b"conc_a", lancer_vfs_proto::FsRights::READ) { 1033 Ok(h) => { 1034 match fs_client.read(h, 0, &mut buf_a) { 1035 Ok(n) => match &buf_a[..n] == b"alpha_data" { 1036 true => pass += 1, 1037 false => { 1038 show!(rsh, error, "conc_a mismatch"); 1039 fail += 1; 1040 } 1041 }, 1042 Err(_) => { 1043 show!(rsh, error, "read conc_a failed"); 1044 fail += 1; 1045 } 1046 } 1047 let _ = fs_client.close(h); 1048 let _ = fs_client.unlink(0, b"conc_a"); 1049 } 1050 Err(_) => { 1051 show!(rsh, error, "open conc_a failed"); 1052 fail += 1; 1053 } 1054 } 1055 1056 let mut buf_b = [0u8; 64]; 1057 match fs_client.open(0, b"conc_b", lancer_vfs_proto::FsRights::READ) { 1058 Ok(h) => { 1059 match fs_client.read(h, 0, &mut buf_b) { 1060 Ok(n) => match &buf_b[..n] == b"bravo_data" { 1061 true => pass += 1, 1062 false => { 1063 show!(rsh, error, "conc_b mismatch"); 1064 fail += 1; 1065 } 1066 }, 1067 Err(_) => { 1068 show!(rsh, error, "read conc_b failed"); 1069 fail += 1; 1070 } 1071 } 1072 let _ = fs_client.close(h); 1073 let _ = fs_client.unlink(0, b"conc_b"); 1074 } 1075 Err(_) => { 1076 show!(rsh, error, "open conc_b failed"); 1077 fail += 1; 1078 } 1079 } 1080 1081 match fail { 1082 0 => show!(rsh, "results: {pass} pass, {fail} fail - ok"), 1083 _ => show!(rsh, error, "results: {pass} pass, {fail} fail - failed"), 1084 } 1085} 1086 1087#[allow(clippy::too_many_arguments)] 1088fn process_line( 1089 line: &[u8], 1090 has_netring: bool, 1091 has_vfs: bool, 1092 netstack_tx: Option<&PacketRingWriter>, 1093 netstack_rx: Option<&PacketRingReader>, 1094 cwd: &mut [u8; 256], 1095 cwd_len: &mut usize, 1096 vfs_tracker: &mut VfsClientTracker, 1097 remote: bool, 1098) { 1099 if line.is_empty() { 1100 return; 1101 } 1102 1103 let cmd = first_word(line); 1104 1105 if bytes_eq(cmd, b"exit") { 1106 syscall::exit(); 1107 } 1108 1109 if bytes_eq(cmd, b"pwd") { 1110 if let Ok(s) = core::str::from_utf8(&cwd[..*cwd_len]) { 1111 print!("{s}\n"); 1112 } 1113 return; 1114 } 1115 1116 if bytes_eq(cmd, b"conctest") { 1117 match has_vfs { 1118 true => run_conctest(vfs_tracker), 1119 false => show!(rsh, warn, "vfs not available"), 1120 } 1121 return; 1122 } 1123 1124 let mut resolved = [0u8; 256]; 1125 let resolved_len = build_resolved_line(&cwd[..], *cwd_len, line, &mut resolved); 1126 let child_line = &resolved[..resolved_len]; 1127 1128 if bytes_eq(cmd, b"cd") { 1129 let orig_rest = skip_leading_spaces(&line[cmd.len()..]); 1130 if first_word(orig_rest).is_empty() { 1131 return; 1132 } 1133 let after_cmd = &resolved[cmd.len()..resolved_len]; 1134 let rest = skip_leading_spaces(after_cmd); 1135 let target = first_word(rest); 1136 match find_module_by_name(b"cd") { 1137 Some(idx) => { 1138 if let Some(false) = spawn_module( 1139 idx, 1140 child_line, 1141 false, 1142 has_vfs, 1143 None, 1144 None, 1145 vfs_tracker, 1146 remote, 1147 ) { 1148 let t_len = target.len().min(256); 1149 cwd[..t_len].copy_from_slice(&target[..t_len]); 1150 *cwd_len = t_len; 1151 } 1152 } 1153 None => print!("cd: command not found\n"), 1154 } 1155 return; 1156 } 1157 1158 match find_module_by_name(cmd) { 1159 Some(idx) => { 1160 let _ = spawn_module( 1161 idx, 1162 child_line, 1163 has_netring, 1164 has_vfs, 1165 netstack_tx, 1166 netstack_rx, 1167 vfs_tracker, 1168 remote, 1169 ); 1170 } 1171 None => match core::str::from_utf8(cmd) { 1172 Ok(s) => print!("{s}: command not found\n"), 1173 Err(_) => print!("(invalid): command not found\n"), 1174 }, 1175 } 1176} 1177 1178#[allow(clippy::too_many_arguments)] 1179fn process_input_byte( 1180 b: u8, 1181 line: &mut LineBuf, 1182 has_netring: bool, 1183 has_vfs: bool, 1184 netstack_tx: Option<&PacketRingWriter>, 1185 netstack_rx: Option<&PacketRingReader>, 1186 cwd: &mut [u8; 256], 1187 cwd_len: &mut usize, 1188 vfs_tracker: &mut VfsClientTracker, 1189 remote: bool, 1190) { 1191 match b { 1192 0x0D | 0x0A => { 1193 print!("\n"); 1194 process_line( 1195 line.as_bytes(), 1196 has_netring, 1197 has_vfs, 1198 netstack_tx, 1199 netstack_rx, 1200 cwd, 1201 cwd_len, 1202 vfs_tracker, 1203 remote, 1204 ); 1205 line.clear(); 1206 print_prompt(cwd, *cwd_len); 1207 } 1208 0x7F | 0x08 => { 1209 if line.pop() { 1210 print!("\x08 \x08"); 1211 } 1212 } 1213 0x20..=0x7E => { 1214 if line.push(b) { 1215 let ch = [b]; 1216 if let Ok(s) = core::str::from_utf8(&ch) { 1217 print!("{s}"); 1218 } 1219 } 1220 } 1221 _ => {} 1222 } 1223} 1224 1225#[unsafe(no_mangle)] 1226pub extern "C" fn lancer_main() -> ! { 1227 let remote = !lancer_user::io::debug_print_enabled(); 1228 1229 let has_netring = (0..NETSTACK_RING_FRAME_COUNT).all(|i| { 1230 syscall::frame_map( 1231 NETSTACK_RING_BASE_SLOT + i, 1232 NETSTACK_RING_VADDR + i * 4096, 1233 1, 1234 ) >= 0 1235 }); 1236 let (netstack_tx, netstack_rx) = match has_netring { 1237 true => { 1238 let rx_base = NETSTACK_RING_VADDR as *mut u8; 1239 let tx_base = (NETSTACK_RING_VADDR + NETSTACK_RING_HALF as u64) as *mut u8; 1240 let _ = unsafe { PacketRingWriter::init(rx_base, NETSTACK_RING_HALF, 128) }; 1241 let tx = unsafe { PacketRingWriter::init(tx_base, NETSTACK_RING_HALF, 128) }; 1242 let rx = unsafe { PacketRingReader::attach(rx_base, NETSTACK_RING_HALF) }; 1243 show!(rsh, "netstack ring mapped"); 1244 (Some(tx), Some(rx)) 1245 } 1246 false => { 1247 show!(rsh, warn, "netstack ring not available"); 1248 (None, None) 1249 } 1250 }; 1251 1252 let has_vfs = (0..VFS_RING_FRAME_COUNT_SHELL) 1253 .all(|i| syscall::frame_map(VFS_RING_BASE_SLOT + i, VFS_RING_VADDR + i * 4096, 1) >= 0); 1254 match has_vfs { 1255 true => show!(rsh, "vfs ring mapped"), 1256 false => show!(rsh, warn, "vfs not available"), 1257 } 1258 1259 let mut cwd = [0u8; 256]; 1260 cwd[0] = b'/'; 1261 let mut cwd_len: usize = 1; 1262 let mut vfs_tracker = VfsClientTracker::new(); 1263 1264 print_prompt(&cwd, cwd_len); 1265 1266 let mut line = LineBuf::new(); 1267 1268 match remote { 1269 true => { 1270 let r = syscall::frame_map(1, INPUT_RING_VADDR, 1); 1271 if r < 0 { 1272 show!(rsh, error, "input ring frame_map failed"); 1273 syscall::exit(); 1274 } 1275 let input_rx = 1276 unsafe { PacketRingReader::attach(INPUT_RING_VADDR as *mut u8, INPUT_RING_SIZE) }; 1277 1278 loop { 1279 let (_, _bits) = syscall::notify_wait(SLOT_SHELL_NOTIF); 1280 1281 let mut buf = [0u8; 64]; 1282 core::iter::from_fn(|| { 1283 input_rx.try_pop(&mut buf).map(|n| { 1284 (0..n).fold((), |(), i| { 1285 process_input_byte( 1286 buf[i], 1287 &mut line, 1288 has_netring, 1289 has_vfs, 1290 netstack_tx.as_ref(), 1291 netstack_rx.as_ref(), 1292 &mut cwd, 1293 &mut cwd_len, 1294 &mut vfs_tracker, 1295 true, 1296 ); 1297 }); 1298 }) 1299 }) 1300 .take(64) 1301 .count(); 1302 } 1303 } 1304 false => loop { 1305 let (_status, msg) = syscall::recv(INPUT_ENDPOINT); 1306 1307 let mut raw_bytes = [0u8; MAX_IPC_BYTES]; 1308 let count = unpack_bytes(&msg, &mut raw_bytes); 1309 1310 (0..count).fold((), |(), i| { 1311 process_input_byte( 1312 raw_bytes[i], 1313 &mut line, 1314 has_netring, 1315 has_vfs, 1316 netstack_tx.as_ref(), 1317 netstack_rx.as_ref(), 1318 &mut cwd, 1319 &mut cwd_len, 1320 &mut vfs_tracker, 1321 false, 1322 ); 1323 }); 1324 }, 1325 } 1326}