1pub mod blkdev_scheme;
28pub mod console_scheme;
29pub mod fd;
30pub mod file;
31pub mod input_scheme;
32pub mod ipcfs;
33pub mod mount;
34pub mod pipe;
35pub mod procfs;
36pub mod pty_scheme;
37pub mod ramfs_scheme;
38pub mod scheme;
39pub mod scheme_router;
40
41use crate::{process::current_task_clone, sync::SpinLock, syscall::error::SyscallError};
42use alloc::{boxed::Box, string::String, sync::Arc};
43use core::fmt::Write;
44
45pub use blkdev_scheme::BlkDevScheme;
46pub use fd::{FileDescriptorTable, STDERR, STDIN, STDOUT};
47pub use file::OpenFile;
48pub use mount::{list_mounts, mount, resolve, unmount, Namespace};
49pub use pipe::PipeScheme;
50pub use procfs::ProcScheme;
51pub use ramfs_scheme::RamfsScheme;
52pub use scheme::{
53 DirEntry, DynScheme, FileFlags, FileStat, IpcScheme, KernelScheme, OpenFlags, Scheme,
54};
55pub use scheme_router::{
56 get_initfs_file_bytes, init_builtin_schemes, list_schemes, mount_scheme, register_initfs_file,
57 register_scheme,
58};
59
60use crate::memory::{UserSliceRead, UserSliceWrite};
61
62pub use crate::syscall::numbers::AT_FDCWD;
64
65pub fn open(path: &str, flags: OpenFlags) -> Result<u32, SyscallError> {
73 let (scheme, relative_path) = mount::resolve(path)?;
75
76 let open_result = scheme.open(&relative_path, flags)?;
78
79 let open_file = Arc::new(OpenFile::new(
82 scheme,
83 open_result.file_id,
84 String::from(path),
85 flags,
86 open_result.flags,
87 open_result.size,
88 ));
89
90 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
92 let fd = unsafe { (&mut *task.process.fd_table.get()).insert(open_file) };
94
95 Ok(fd)
96}
97
98fn open_resolved(path: &str, flags: OpenFlags) -> Result<u32, SyscallError> {
103 let (scheme, relative_path) = mount::resolve(path)?;
104 let open_result = scheme.open(&relative_path, flags)?;
105
106 let open_file = Arc::new(OpenFile::new(
107 scheme,
108 open_result.file_id,
109 String::from(path),
110 flags,
111 open_result.flags,
112 open_result.size,
113 ));
114
115 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
116 let fd = unsafe { (&mut *task.process.fd_table.get()).insert(open_file) };
117
118 Ok(fd)
119}
120
121pub fn resolve_and_check_path_for_current_task(
123 path: &str,
124 want_read: bool,
125 want_write: bool,
126 want_execute: bool,
127) -> Result<String, SyscallError> {
128 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
129 let cwd = unsafe { (&*task.process.cwd.get()).clone() };
130 let abs = resolve_path(path, &cwd);
131 crate::silo::enforce_path_for_current_task(&abs, want_read, want_write, want_execute)?;
132 Ok(abs)
133}
134
135pub fn open_at(dir_fd: u64, path: &str, flags: OpenFlags) -> Result<u32, SyscallError> {
144 if dir_fd == AT_FDCWD as u64 {
145 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
147 let cwd = unsafe { (&*task.process.cwd.get()).clone() };
148 let abs = resolve_path(path, &cwd);
149 crate::silo::enforce_path_for_current_task(
150 &abs,
151 flags.contains(OpenFlags::READ) || flags.contains(OpenFlags::DIRECTORY),
152 flags.contains(OpenFlags::WRITE) || flags.contains(OpenFlags::CREATE),
153 false,
154 )?;
155 open_resolved(&abs, flags)
156 } else {
157 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
159 let fd_table = unsafe { &*task.process.fd_table.get() };
160 let dir_file = fd_table.get(dir_fd as u32)?;
161 if !dir_file.flags().contains(FileFlags::DIRECTORY) {
162 return Err(SyscallError::NotADirectory);
163 }
164 let dir_path = dir_file.path();
165 let abs = resolve_path(path, dir_path);
166 crate::silo::enforce_path_for_current_task(
167 &abs,
168 flags.contains(OpenFlags::READ) || flags.contains(OpenFlags::DIRECTORY),
169 flags.contains(OpenFlags::WRITE) || flags.contains(OpenFlags::CREATE),
170 false,
171 )?;
172 open_resolved(&abs, flags)
173 }
174}
175
176pub fn fstat_at(dir_fd: u64, path: &str) -> Result<FileStat, SyscallError> {
180 if dir_fd == AT_FDCWD as u64 {
181 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
182 let cwd = unsafe { (&*task.process.cwd.get()).clone() };
183 let abs = resolve_path(path, &cwd);
184 stat_path(&abs)
185 } else {
186 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
187 let fd_table = unsafe { &*task.process.fd_table.get() };
188 let dir_file = fd_table.get(dir_fd as u32)?;
189 if !dir_file.flags().contains(FileFlags::DIRECTORY) {
190 return Err(SyscallError::NotADirectory);
191 }
192 let dir_path = dir_file.path();
193 let abs = resolve_path(path, dir_path);
194 stat_path(&abs)
195 }
196}
197
198pub fn mkdir(path: &str, mode: u32) -> Result<(), SyscallError> {
200 let (scheme, relative_path) = mount::resolve(path)?;
201 scheme.create_directory(&relative_path, mode)?;
202 Ok(())
203}
204
205pub fn create_file(path: &str, mode: u32) -> Result<(), SyscallError> {
207 let (scheme, relative_path) = mount::resolve(path)?;
208 scheme.create_file(&relative_path, mode)?;
209 Ok(())
210}
211
212pub fn unlink(path: &str) -> Result<(), SyscallError> {
214 let (scheme, relative_path) = mount::resolve(path)?;
215 scheme.unlink(&relative_path)?;
216 Ok(())
217}
218
219pub fn rename(old_path: &str, new_path: &str) -> Result<(), SyscallError> {
221 let (scheme, old_rel) = mount::resolve(old_path)?;
222 let (scheme2, new_rel) = mount::resolve(new_path)?;
223 if !Arc::ptr_eq(&scheme, &scheme2) {
224 return Err(SyscallError::NotSupported);
225 }
226 scheme.rename(&old_rel, &new_rel)
227}
228
229pub fn chmod(path: &str, mode: u32) -> Result<(), SyscallError> {
231 let (scheme, relative_path) = mount::resolve(path)?;
232 scheme.chmod(&relative_path, mode)
233}
234
235pub fn fchmod(fd: u32, mode: u32) -> Result<(), SyscallError> {
237 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
238 let fd_table = unsafe { &*task.process.fd_table.get() };
239 let file = fd_table.get(fd)?;
240 file.scheme().fchmod(file.file_id(), mode)
241}
242
243pub fn truncate(path: &str, length: u64) -> Result<(), SyscallError> {
248 let (scheme, relative_path) = mount::resolve(path)?;
249
250 match scheme.truncate_by_path(&relative_path, length) {
253 Ok(()) => return Ok(()),
254 Err(SyscallError::NotImplemented) => {
255 }
258 Err(e) => return Err(e),
259 }
260
261 let res = scheme.open(&relative_path, OpenFlags::WRITE)?;
262 let r = scheme.truncate(res.file_id, length);
263 let _ = scheme.close(res.file_id);
264 r
265}
266
267pub fn ftruncate(fd: u32, length: u64) -> Result<(), SyscallError> {
269 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
270 let fd_table = unsafe { &*task.process.fd_table.get() };
271 let file = fd_table.get(fd)?;
272 file.scheme().truncate(file.file_id(), length)
273}
274
275pub fn link(old_path: &str, new_path: &str) -> Result<(), SyscallError> {
277 let (scheme, old_rel) = mount::resolve(old_path)?;
278 let (scheme2, new_rel) = mount::resolve(new_path)?;
279 if !Arc::ptr_eq(&scheme, &scheme2) {
280 return Err(SyscallError::NotSupported);
281 }
282 scheme.link(&old_rel, &new_rel)
283}
284
285pub fn symlink(target: &str, link_path: &str) -> Result<(), SyscallError> {
287 let (scheme, link_rel) = mount::resolve(link_path)?;
288 scheme.symlink(target, &link_rel)
289}
290
291pub fn readlink(path: &str) -> Result<String, SyscallError> {
293 let (scheme, relative_path) = mount::resolve(path)?;
294 scheme.readlink(&relative_path)
295}
296
297pub fn read(fd: u32, buf: &mut [u8]) -> Result<usize, SyscallError> {
299 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
300 let fd_table = unsafe { &*task.process.fd_table.get() };
302 let file = fd_table.get(fd)?;
303 file.read(buf)
304}
305
306pub fn write(fd: u32, buf: &[u8]) -> Result<usize, SyscallError> {
308 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
309 let fd_table = unsafe { &*task.process.fd_table.get() };
311 let file = fd_table.get(fd)?;
312 file.write(buf)
313}
314
315pub fn close(fd: u32) -> Result<(), SyscallError> {
320 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
321 let fd_table = unsafe { &mut *task.process.fd_table.get() };
323 let _file = fd_table.remove(fd)?;
324 Ok(())
325 }
327
328pub fn seek(fd: u32, offset: u64) -> Result<u64, SyscallError> {
330 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
331 let fd_table = unsafe { &*task.process.fd_table.get() };
333 let file = fd_table.get(fd)?;
334 file.seek(offset)
335}
336
337pub fn tell(fd: u32) -> Result<u64, SyscallError> {
339 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
340 let fd_table = unsafe { &*task.process.fd_table.get() };
342 let file = fd_table.get(fd)?;
343 Ok(file.tell())
344}
345
346pub fn fsize(fd: u32) -> Result<u64, SyscallError> {
348 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
349 let fd_table = unsafe { &*task.process.fd_table.get() };
351 let file = fd_table.get(fd)?;
352 file.size()
353}
354
355pub fn fsync(fd: u32) -> Result<(), SyscallError> {
357 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
358 let fd_table = unsafe { &*task.process.fd_table.get() };
360 let file = fd_table.get(fd)?;
361 file.sync()
362}
363
364pub fn lseek(fd: u32, offset: i64, whence: u32) -> Result<u64, SyscallError> {
366 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
367 let fd_table = unsafe { &*task.process.fd_table.get() };
368 let file = fd_table.get(fd)?;
369 file.lseek(offset, whence)
370}
371
372pub fn fstat(fd: u32) -> Result<FileStat, SyscallError> {
374 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
375 let fd_table = unsafe { &*task.process.fd_table.get() };
376 let file = fd_table.get(fd)?;
377 file.stat()
378}
379
380pub fn stat_path(path: &str) -> Result<FileStat, SyscallError> {
382 let (scheme, relative_path) = mount::resolve(path)?;
383 let open_result = scheme.open(&relative_path, OpenFlags::READ)?;
384 let result = scheme.stat(open_result.file_id);
385 let _ = scheme.close(open_result.file_id);
386 result
387}
388
389pub fn getdents(fd: u32) -> Result<alloc::vec::Vec<DirEntry>, SyscallError> {
391 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
392 let fd_table = unsafe { &*task.process.fd_table.get() };
393 let file = fd_table.get(fd)?;
394 file.readdir()
395}
396
397pub fn create_background_stdin() -> Arc<OpenFile> {
402 let pipe_scheme = get_pipe_scheme();
403 let (base_id, pipe) = pipe_scheme.create_pipe();
404
405 pipe.close_write();
408
409 let dyn_scheme: DynScheme = pipe_scheme as Arc<dyn Scheme>;
410 Arc::new(OpenFile::new(
411 dyn_scheme,
412 base_id, String::from("pipe:[bg-stdin]"),
414 OpenFlags::READ,
415 FileFlags::PIPE,
416 None,
417 ))
418}
419
420pub fn pipe() -> Result<(u32, u32), SyscallError> {
422 let pipe_scheme = get_pipe_scheme();
423 let (base_id, _pipe) = pipe_scheme.create_pipe();
424
425 let dyn_scheme: DynScheme = pipe_scheme as Arc<dyn Scheme>;
426
427 let read_file = Arc::new(OpenFile::new(
428 dyn_scheme.clone(),
429 base_id,
430 String::from("pipe:[read]"),
431 OpenFlags::READ,
432 FileFlags::PIPE,
433 None,
434 ));
435 let write_file = Arc::new(OpenFile::new(
436 dyn_scheme.clone(),
437 base_id + 1,
438 String::from("pipe:[write]"),
439 OpenFlags::WRITE,
440 FileFlags::PIPE,
441 None,
442 ));
443
444 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
445 let fd_table = unsafe { &mut *task.process.fd_table.get() };
446 let read_fd = fd_table.insert(read_file);
447 let write_fd = fd_table.insert(write_file);
448
449 Ok((read_fd, write_fd))
450}
451
452pub fn dup(old_fd: u32) -> Result<u32, SyscallError> {
454 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
455 let fd_table = unsafe { &mut *task.process.fd_table.get() };
456 fd_table.duplicate(old_fd)
457}
458
459pub fn dup2(old_fd: u32, new_fd: u32) -> Result<u32, SyscallError> {
461 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
462 let fd_table = unsafe { &mut *task.process.fd_table.get() };
463 fd_table.duplicate_to(old_fd, new_fd)
464}
465
466pub fn read_all(fd: u32) -> Result<alloc::vec::Vec<u8>, SyscallError> {
468 let mut out = alloc::vec::Vec::new();
469 let mut buf = [0u8; 4096];
470 loop {
471 let n = read(fd, &mut buf)?;
472 if n == 0 {
473 break;
474 }
475 out.extend_from_slice(&buf[..n]);
476 }
477 Ok(out)
478}
479
480pub fn sys_open(path_ptr: u64, path_len: u64, flags: u64) -> Result<u64, SyscallError> {
486 let open_flags = OpenFlags::from_bits_truncate(flags as u32);
487 let want_read =
488 open_flags.contains(OpenFlags::READ) || open_flags.contains(OpenFlags::DIRECTORY);
489 let want_write = open_flags.contains(OpenFlags::WRITE)
490 || open_flags.contains(OpenFlags::CREATE)
491 || open_flags.contains(OpenFlags::TRUNCATE)
492 || open_flags.contains(OpenFlags::APPEND);
493 let path = resolve_for_syscall(path_ptr, path_len, want_read, want_write, false)?;
494 let fd = open_resolved(&path, open_flags)?;
495 Ok(fd as u64)
496}
497
498pub fn sys_openat(
500 dir_fd: u64,
501 path_ptr: u64,
502 path_len: u64,
503 flags: u64,
504) -> Result<u64, SyscallError> {
505 const MAX_PATH_LEN: usize = 4096;
506 if path_len == 0 || path_len as usize > MAX_PATH_LEN {
507 return Err(SyscallError::InvalidArgument);
508 }
509 let raw = read_user_path(path_ptr, path_len)?;
510 let open_flags = OpenFlags::from_bits_truncate(flags as u32);
511 let fd = open_at(dir_fd, &raw, open_flags)?;
512 Ok(fd as u64)
513}
514
515pub fn sys_fstatat(
517 dir_fd: u64,
518 path_ptr: u64,
519 path_len: u64,
520 _flags: u64,
521) -> Result<u64, SyscallError> {
522 const MAX_PATH_LEN: usize = 4096;
523 if path_len == 0 || path_len as usize > MAX_PATH_LEN {
524 return Err(SyscallError::InvalidArgument);
525 }
526 let raw = read_user_path(path_ptr, path_len)?;
527 let st = fstat_at(dir_fd, &raw)?;
528 let _ = st;
532 Ok(0)
533}
534
535pub fn sys_access(path_ptr: u64, path_len: u64, mode: u64) -> Result<u64, SyscallError> {
537 let path = resolve_for_syscall(path_ptr, path_len, true, false, false)?;
538 let st = stat_path(&path)?;
539 check_access_permissions(&st, mode as u32)?;
540 Ok(0)
541}
542
543pub fn sys_faccessat(
545 dir_fd: u64,
546 path_ptr: u64,
547 path_len: u64,
548 mode: u64,
549 _flags: u64,
550) -> Result<u64, SyscallError> {
551 let raw = read_user_path(path_ptr, path_len)?;
552
553 let abs = if dir_fd == AT_FDCWD as u64 {
554 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
555 let cwd = unsafe { (&*task.process.cwd.get()).clone() };
556 resolve_path(&raw, &cwd)
557 } else {
558 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
559 let fd_table = unsafe { &*task.process.fd_table.get() };
560 let dir_file = fd_table.get(dir_fd as u32)?;
561 let dir_path = dir_file.path();
562 resolve_path(&raw, dir_path)
563 };
564
565 crate::silo::enforce_path_for_current_task(&abs, true, false, false)?;
566 let st = stat_path(&abs)?;
567 check_access_permissions(&st, mode as u32)?;
568 Ok(0)
569}
570
571const F_OK: u32 = 0;
573const R_OK: u32 = 4;
574const W_OK: u32 = 2;
575const X_OK: u32 = 1;
576
577fn check_access_permissions(st: &FileStat, mode: u32) -> Result<(), SyscallError> {
583 if mode == F_OK {
584 return Ok(()); }
586
587 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
589 let uid = task.uid.load(core::sync::atomic::Ordering::Relaxed);
590 let gid = task.gid.load(core::sync::atomic::Ordering::Relaxed);
591
592 let file_uid = st.st_uid;
593 let file_gid = st.st_gid;
594 let file_mode = st.st_mode;
595
596 let mut granted = 0u32;
597
598 if uid == file_uid {
600 if file_mode & 0o400 != 0 {
601 granted |= R_OK;
602 }
603 if file_mode & 0o200 != 0 {
604 granted |= W_OK;
605 }
606 if file_mode & 0o100 != 0 {
607 granted |= X_OK;
608 }
609 }
610 if gid == file_gid {
612 if file_mode & 0o040 != 0 {
613 granted |= R_OK;
614 }
615 if file_mode & 0o020 != 0 {
616 granted |= W_OK;
617 }
618 if file_mode & 0o010 != 0 {
619 granted |= X_OK;
620 }
621 }
622 if file_mode & 0o004 != 0 {
624 granted |= R_OK;
625 }
626 if file_mode & 0o002 != 0 {
627 granted |= W_OK;
628 }
629 if file_mode & 0o001 != 0 {
630 granted |= X_OK;
631 }
632
633 if mode & !granted != 0 {
635 return Err(SyscallError::PermissionDenied);
636 }
637 Ok(())
638}
639
640pub fn sys_read(fd: u32, buf_ptr: u64, buf_len: u64) -> Result<u64, SyscallError> {
642 if buf_len == 0 {
643 return Ok(0);
644 }
645
646 let mut kbuf = [0u8; 4096];
648 let mut total_read = 0;
649
650 while total_read < buf_len as usize {
651 let to_read = core::cmp::min(kbuf.len(), buf_len as usize - total_read);
652 let n = read(fd, &mut kbuf[..to_read])?;
653 if n == 0 {
654 break;
655 }
656
657 let chunk_addr = buf_ptr
658 .checked_add(total_read as u64)
659 .ok_or(SyscallError::Fault)?;
660 let chunk_user = UserSliceWrite::new(chunk_addr, n)?;
661 chunk_user.copy_from(&kbuf[..n]);
662
663 total_read += n;
664 if n < to_read {
665 break;
666 }
667 }
668
669 Ok(total_read as u64)
670}
671
672pub fn sys_write(fd: u32, buf_ptr: u64, buf_len: u64) -> Result<u64, SyscallError> {
674 if buf_len == 0 {
675 return Ok(0);
676 }
677
678 if fd == 1 || fd == 2 {
682 let use_console = match current_task_clone() {
683 Some(t) => {
684 let fd_table = unsafe { &*t.process.fd_table.get() };
685 !fd_table.contains(fd)
686 }
687 None => true,
688 };
689 if use_console {
690 crate::silo::enforce_console_access()?;
691 let len = core::cmp::min(buf_len as usize, 16 * 1024);
692 let mut kbuf = [0u8; 4096];
693 let mut total_written = 0;
694 while total_written < len {
695 let to_write = core::cmp::min(kbuf.len(), len - total_written);
696 let chunk_addr = buf_ptr
697 .checked_add(total_written as u64)
698 .ok_or(SyscallError::Fault)?;
699 let chunk = UserSliceRead::new(chunk_addr, to_write)?;
700 let n = chunk.copy_to(&mut kbuf[..to_write]);
701 if crate::arch::x86_64::vga::is_available() {
702 if let Ok(s) = core::str::from_utf8(&kbuf[..n]) {
703 crate::serial_print!("{}", s);
704 crate::vga_print!("{}", s);
705 } else {
706 for &byte in &kbuf[..n] {
707 crate::serial_print!("{}", byte as char);
708 }
709 }
710 } else {
711 for &byte in &kbuf[..n] {
712 crate::serial_print!("{}", byte as char);
713 }
714 }
715 total_written += n;
716 }
717 return Ok(total_written as u64);
718 }
719 }
720
721 let mut kbuf = [0u8; 4096];
722 let mut total_written = 0;
723
724 while total_written < buf_len as usize {
725 let to_write = core::cmp::min(kbuf.len(), buf_len as usize - total_written);
726 let chunk_addr = buf_ptr
727 .checked_add(total_written as u64)
728 .ok_or(SyscallError::Fault)?;
729 let chunk_user = UserSliceRead::new(chunk_addr, to_write)?;
730 chunk_user.copy_to(&mut kbuf[..to_write]);
731
732 let n = write(fd, &kbuf[..to_write])?;
733 total_written += n;
734 if n < to_write {
735 break;
736 }
737 }
738
739 Ok(total_written as u64)
740}
741
742pub fn sys_close(fd: u32) -> Result<u64, SyscallError> {
744 close(fd)?;
745 Ok(0)
746}
747
748pub fn sys_lseek(fd: u32, offset: i64, whence: u32) -> Result<u64, SyscallError> {
750 lseek(fd, offset, whence)
751}
752
753pub fn sys_fstat(fd: u32, stat_ptr: u64) -> Result<u64, SyscallError> {
755 let st = fstat(fd)?;
756 let user = UserSliceWrite::new(stat_ptr, core::mem::size_of::<FileStat>())?;
757 let bytes = unsafe {
758 core::slice::from_raw_parts(
759 &st as *const FileStat as *const u8,
760 core::mem::size_of::<FileStat>(),
761 )
762 };
763 user.copy_from(bytes);
764 Ok(0)
765}
766
767pub fn sys_stat(path_ptr: u64, path_len: u64, stat_ptr: u64) -> Result<u64, SyscallError> {
769 let path = resolve_for_syscall(path_ptr, path_len, true, false, false)?;
770 let st = stat_path(&path)?;
771 let user_out = UserSliceWrite::new(stat_ptr, core::mem::size_of::<FileStat>())?;
772 let out_bytes = unsafe {
773 core::slice::from_raw_parts(
774 &st as *const FileStat as *const u8,
775 core::mem::size_of::<FileStat>(),
776 )
777 };
778 user_out.copy_from(out_bytes);
779 Ok(0)
780}
781
782pub fn sys_getdents(fd: u32, buf_ptr: u64, buf_len: u64) -> Result<u64, SyscallError> {
787 use strat9_abi::data::DirentHeader;
788
789 let entries = getdents(fd)?;
790 let mut offset: usize = 0;
791 let buf_size = buf_len as usize;
792
793 for entry in &entries {
794 let name_bytes = entry.name.as_bytes();
795 let name_len = core::cmp::min(name_bytes.len(), 255) as u16;
796 let entry_size = DirentHeader::SIZE + name_len as usize + 1;
797
798 if offset + entry_size > buf_size {
799 break;
800 }
801
802 let user = UserSliceWrite::new(buf_ptr + offset as u64, entry_size)?;
803 let mut kbuf = [0u8; 268];
804 kbuf[0..8].copy_from_slice(&entry.ino.to_le_bytes());
805 kbuf[8] = entry.file_type;
806 kbuf[9..11].copy_from_slice(&name_len.to_le_bytes());
807 kbuf[11] = 0; kbuf[12..12 + name_len as usize].copy_from_slice(&name_bytes[..name_len as usize]);
809 kbuf[12 + name_len as usize] = 0;
810 user.copy_from(&kbuf[..entry_size]);
811
812 offset += entry_size;
813 }
814
815 Ok(offset as u64)
816}
817
818pub fn sys_pipe(fds_ptr: u64) -> Result<u64, SyscallError> {
820 let (read_fd, write_fd) = pipe()?;
821 let user = UserSliceWrite::new(fds_ptr, 8)?; let mut buf = [0u8; 8];
823 buf[0..4].copy_from_slice(&read_fd.to_le_bytes());
824 buf[4..8].copy_from_slice(&write_fd.to_le_bytes());
825 user.copy_from(&buf);
826 Ok(0)
827}
828
829pub fn sys_dup(old_fd: u32) -> Result<u64, SyscallError> {
831 let new_fd = dup(old_fd)?;
832 Ok(new_fd as u64)
833}
834
835pub fn sys_dup2(old_fd: u32, new_fd: u32) -> Result<u64, SyscallError> {
837 let fd = dup2(old_fd, new_fd)?;
838 Ok(fd as u64)
839}
840
841fn read_user_path(path_ptr: u64, path_len: u64) -> Result<alloc::string::String, SyscallError> {
848 const MAX_PATH: usize = 4096;
849 let len = if path_len == 0 || path_len as usize > MAX_PATH {
850 MAX_PATH
851 } else {
852 path_len as usize
853 };
854 let user = UserSliceRead::new(path_ptr, len)?;
855 let bytes = user.read_to_vec();
856 let trimmed = bytes.split(|&b| b == 0).next().unwrap_or(&bytes);
858 if trimmed.is_empty() {
859 return Err(SyscallError::InvalidArgument);
860 }
861 core::str::from_utf8(trimmed)
862 .map(|s| alloc::string::String::from(s))
863 .map_err(|_| SyscallError::InvalidArgument)
864}
865
866fn resolve_for_syscall(
871 path_ptr: u64,
872 path_len: u64,
873 want_read: bool,
874 want_write: bool,
875 want_execute: bool,
876) -> Result<alloc::string::String, SyscallError> {
877 const MAX_PATH_LEN: usize = 4096;
878 if path_len == 0 || path_len as usize > MAX_PATH_LEN {
879 return Err(SyscallError::InvalidArgument);
880 }
881 let raw = read_user_path(path_ptr, path_len)?;
882 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
883 let cwd = unsafe { (&*task.process.cwd.get()).clone() };
884 let abs = resolve_path(&raw, &cwd);
885 crate::silo::enforce_path_for_current_task(&abs, want_read, want_write, want_execute)?;
886 Ok(abs)
887}
888
889fn resolve_path(path: &str, cwd: &str) -> alloc::string::String {
892 let raw = if path.starts_with('/') {
893 alloc::string::String::from(path)
894 } else if cwd.ends_with('/') {
895 alloc::format!("{}{}", cwd, path)
896 } else {
897 alloc::format!("{}/{}", cwd, path)
898 };
899 normalize_path(&raw)
900}
901
902fn normalize_path(path: &str) -> alloc::string::String {
904 let mut parts: alloc::vec::Vec<&str> = alloc::vec::Vec::new();
905 for seg in path.split('/') {
906 match seg {
907 "" | "." => {}
908 ".." => {
909 parts.pop();
910 }
911 other => parts.push(other),
912 }
913 }
914 let mut out = alloc::string::String::with_capacity(path.len());
915 if parts.is_empty() {
916 out.push('/');
917 } else {
918 for p in &parts {
919 out.push('/');
920 out.push_str(p);
921 }
922 }
923 out
924}
925
926pub fn sys_chdir(path_ptr: u64, path_len: u64) -> Result<u64, SyscallError> {
930 let abs = resolve_for_syscall(path_ptr, path_len, true, false, false)?;
931
932 let (scheme, rel) = mount::resolve(&abs)?;
933 let res = scheme.open(&rel, OpenFlags::READ | OpenFlags::DIRECTORY)?;
934 let _ = scheme.close(res.file_id);
935
936 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
937 unsafe { *task.process.cwd.get() = abs };
938 Ok(0)
939}
940
941pub fn sys_fchdir(fd: u32) -> Result<u64, SyscallError> {
943 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
944 let path = {
945 let fd_table = unsafe { &*task.process.fd_table.get() };
946 let file = fd_table.get(fd)?;
947 alloc::string::String::from(file.path())
948 };
949 unsafe { *task.process.cwd.get() = path };
950 Ok(0)
951}
952
953pub fn sys_getcwd(buf_ptr: u64, buf_len: u64) -> Result<u64, SyscallError> {
955 if buf_len == 0 {
956 return Err(SyscallError::InvalidArgument);
957 }
958 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
959 let cwd = unsafe { (&*task.process.cwd.get()).clone() };
960 let bytes = cwd.as_bytes();
961 let needed = bytes.len() + 1;
962 if needed > buf_len as usize {
963 return Err(SyscallError::Range);
964 }
965 let out = UserSliceWrite::new(buf_ptr, needed)?;
966 let mut tmp = alloc::vec![0u8; needed];
967 tmp[..bytes.len()].copy_from_slice(bytes);
968 tmp[bytes.len()] = 0;
969 out.copy_from(&tmp);
970 Ok(needed as u64)
971}
972
973pub fn sys_ioctl(_fd: u32, _request: u64, _arg: u64) -> Result<u64, SyscallError> {
978 Err(SyscallError::NotATty)
979}
980
981pub fn sys_umask(mask: u64) -> Result<u64, SyscallError> {
983 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
984 let old = task
985 .process
986 .umask
987 .swap(mask as u32 & 0o777, core::sync::atomic::Ordering::Relaxed);
988 Ok(old as u64)
989}
990
991pub fn sys_unlink(path_ptr: u64, path_len: u64) -> Result<u64, SyscallError> {
993 let abs = resolve_for_syscall(path_ptr, path_len, false, true, false)?;
994 unlink(&abs)?;
995 Ok(0)
996}
997
998pub fn sys_rmdir(path_ptr: u64, path_len: u64) -> Result<u64, SyscallError> {
1000 let abs = resolve_for_syscall(path_ptr, path_len, false, true, false)?;
1001 unlink(&abs)?;
1002 Ok(0)
1003}
1004
1005pub fn sys_mkdir(path_ptr: u64, path_len: u64, mode: u64) -> Result<u64, SyscallError> {
1007 let abs = resolve_for_syscall(path_ptr, path_len, false, true, false)?;
1008 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1009 let umask = task
1010 .process
1011 .umask
1012 .load(core::sync::atomic::Ordering::Relaxed);
1013 let effective_mode = (mode as u32) & !umask;
1014 mkdir(&abs, effective_mode)?;
1015 Ok(0)
1016}
1017
1018pub fn sys_rename(
1020 old_ptr: u64,
1021 old_len: u64,
1022 new_ptr: u64,
1023 new_len: u64,
1024) -> Result<u64, SyscallError> {
1025 let old_abs = resolve_for_syscall(old_ptr, old_len, true, true, false)?;
1026 let new_abs = resolve_for_syscall(new_ptr, new_len, false, true, false)?;
1027 rename(&old_abs, &new_abs)?;
1028 Ok(0)
1029}
1030
1031pub fn sys_link(
1033 old_ptr: u64,
1034 old_len: u64,
1035 new_ptr: u64,
1036 new_len: u64,
1037) -> Result<u64, SyscallError> {
1038 let old_abs = resolve_for_syscall(old_ptr, old_len, true, false, false)?;
1039 let new_abs = resolve_for_syscall(new_ptr, new_len, false, true, false)?;
1040 link(&old_abs, &new_abs)?;
1041 Ok(0)
1042}
1043
1044pub fn sys_symlink(
1046 target_ptr: u64,
1047 target_len: u64,
1048 linkpath_ptr: u64,
1049 linkpath_len: u64,
1050) -> Result<u64, SyscallError> {
1051 let target = read_user_path(target_ptr, target_len)?;
1052 let link_abs = resolve_for_syscall(linkpath_ptr, linkpath_len, false, true, false)?;
1053 symlink(&target, &link_abs)?;
1054 Ok(0)
1055}
1056
1057pub fn sys_readlink(
1059 path_ptr: u64,
1060 path_len: u64,
1061 buf_ptr: u64,
1062 buf_len: u64,
1063) -> Result<u64, SyscallError> {
1064 let abs = resolve_for_syscall(path_ptr, path_len, true, false, false)?;
1065 let target = readlink(&abs)?;
1066 let bytes = target.as_bytes();
1067 let n = bytes.len().min(buf_len as usize);
1068 let user = UserSliceWrite::new(buf_ptr, n)?;
1069 user.copy_from(&bytes[..n]);
1070 Ok(n as u64)
1071}
1072
1073pub fn sys_chmod(path_ptr: u64, path_len: u64, mode: u64) -> Result<u64, SyscallError> {
1075 let abs = resolve_for_syscall(path_ptr, path_len, false, true, false)?;
1076 chmod(&abs, mode as u32)?;
1077 Ok(0)
1078}
1079
1080pub fn sys_truncate(path_ptr: u64, path_len: u64, length: u64) -> Result<u64, SyscallError> {
1082 let abs = resolve_for_syscall(path_ptr, path_len, false, true, false)?;
1083 truncate(&abs, length)?;
1084 Ok(0)
1085}
1086
1087pub fn sys_fchmod(fd: u32, mode: u64) -> Result<u64, SyscallError> {
1089 fchmod(fd, mode as u32)?;
1090 Ok(0)
1091}
1092
1093pub fn sys_ftruncate(fd: u32, length: u64) -> Result<u64, SyscallError> {
1095 ftruncate(fd, length)?;
1096 Ok(0)
1097}
1098
1099pub fn sys_pread(fd: u32, buf_ptr: u64, buf_len: u64, offset: u64) -> Result<u64, SyscallError> {
1101 if buf_len == 0 {
1102 return Ok(0);
1103 }
1104 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1105 let fd_table = unsafe { &*task.process.fd_table.get() };
1106 let file = fd_table.get(fd)?;
1107 let mut kbuf = [0u8; 4096];
1108 let mut total = 0usize;
1109 let mut off = offset;
1110 while total < buf_len as usize {
1111 let to_read = core::cmp::min(kbuf.len(), buf_len as usize - total);
1112 let n = file.pread(off, &mut kbuf[..to_read])?;
1113 if n == 0 {
1114 break;
1115 }
1116 let user = UserSliceWrite::new(
1117 buf_ptr
1118 .checked_add(total as u64)
1119 .ok_or(SyscallError::Fault)?,
1120 n,
1121 )?;
1122 user.copy_from(&kbuf[..n]);
1123 total += n;
1124 off += n as u64;
1125 if n < to_read {
1126 break;
1127 }
1128 }
1129 Ok(total as u64)
1130}
1131
1132pub fn sys_pwrite(fd: u32, buf_ptr: u64, buf_len: u64, offset: u64) -> Result<u64, SyscallError> {
1134 if buf_len == 0 {
1135 return Ok(0);
1136 }
1137 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1138 let fd_table = unsafe { &*task.process.fd_table.get() };
1139 let file = fd_table.get(fd)?;
1140 let mut kbuf = [0u8; 4096];
1141 let mut total = 0usize;
1142 let mut off = offset;
1143 while total < buf_len as usize {
1144 let to_write = core::cmp::min(kbuf.len(), buf_len as usize - total);
1145 let user = UserSliceRead::new(
1146 buf_ptr
1147 .checked_add(total as u64)
1148 .ok_or(SyscallError::Fault)?,
1149 to_write,
1150 )?;
1151 user.copy_to(&mut kbuf[..to_write]);
1152 let n = file.pwrite(off, &kbuf[..to_write])?;
1153 total += n;
1154 off += n as u64;
1155 if n < to_write {
1156 break;
1157 }
1158 }
1159 Ok(total as u64)
1160}
1161
1162static PIPE_SCHEME: SpinLock<Option<Arc<PipeScheme>>> = SpinLock::new(None);
1167
1168fn get_pipe_scheme() -> Arc<PipeScheme> {
1170 let mut guard = PIPE_SCHEME.lock();
1171 if let Some(ref scheme) = *guard {
1172 return scheme.clone();
1173 }
1174 let scheme = Arc::new(PipeScheme::new());
1175 *guard = Some(scheme.clone());
1176 scheme
1177}
1178
1179fn build_pci_inventory_text() -> String {
1181 #[cfg(target_arch = "x86_64")]
1182 {
1183 let devices = crate::hardware::pci_client::all_devices();
1184 let mut out = String::new();
1185 out.push_str("bus dev fn vendor device class subclass prog_if irq\n");
1186 for dev in devices.iter() {
1187 let _ = writeln!(
1188 out,
1189 "{:02x} {:02x} {} {:04x} {:04x} {:02x} {:02x} {:02x} {}",
1190 dev.address.bus,
1191 dev.address.device,
1192 dev.address.function,
1193 dev.vendor_id,
1194 dev.device_id,
1195 dev.class_code,
1196 dev.subclass,
1197 dev.prog_if,
1198 dev.interrupt_line
1199 );
1200 }
1201 return out;
1202 }
1203
1204 #[cfg(not(target_arch = "x86_64"))]
1205 {
1206 String::from("unsupported-arch\n")
1207 }
1208}
1209
1210pub fn init() {
1216 log::info!("[VFS] Initializing virtual file system");
1217
1218 let rootfs = alloc::sync::Arc::new(RamfsScheme::new());
1222 if let Err(e) = mount::mount("/", rootfs.clone()) {
1223 log::error!("[VFS] Failed to mount /: {:?}", e);
1224 } else {
1225 for dir in &[
1227 "bin", "sbin", "etc", "tmp", "usr", "lib", "lib64", "home", "root", "run", "var",
1228 "mnt", "opt", "srv", "dev", "proc", "sys",
1229 ] {
1230 rootfs.ensure_dir(dir);
1231 }
1232 rootfs.ensure_dir("usr/bin");
1234 rootfs.ensure_dir("usr/sbin");
1235 rootfs.ensure_dir("usr/lib");
1236 rootfs.ensure_dir("var/log");
1237 rootfs.ensure_dir("var/tmp");
1238 rootfs.ensure_dir("run/lock");
1239 log::info!("[VFS] Mounted / (ramfs) with standard directory tree");
1240 }
1241
1242 if let Err(e) = scheme_router::init_builtin_schemes() {
1244 log::error!("[VFS] Failed to init builtin schemes: {:?}", e);
1245 }
1246
1247 let kernel_scheme = KernelScheme::new();
1249
1250 static VERSION: &[u8] = b"Strat9-OS v0.1.0 (Bedrock)\n";
1252 kernel_scheme.register("version", VERSION.as_ptr(), VERSION.len());
1253
1254 static CMDLINE: &[u8] = b"quiet loglevel=debug\n";
1255 kernel_scheme.register("cmdline", CMDLINE.as_ptr(), CMDLINE.len());
1256
1257 let pci_inventory = build_pci_inventory_text().into_bytes().into_boxed_slice();
1258 let pci_inventory = Box::leak(pci_inventory);
1259 kernel_scheme.register("pci/inventory", pci_inventory.as_ptr(), pci_inventory.len());
1260
1261 let pci_count = pci_inventory
1262 .split(|b| *b == b'\n')
1263 .skip(1)
1264 .filter(|line| !line.is_empty())
1265 .count();
1266 let mut pci_count_str = String::new();
1267 let _ = writeln!(pci_count_str, "{}", pci_count);
1268 let pci_count = Box::leak(pci_count_str.into_bytes().into_boxed_slice());
1269 kernel_scheme.register("pci/count", pci_count.as_ptr(), pci_count.len());
1270
1271 {
1273 let host = crate::arch::x86_64::cpuid::host();
1274 let cpu_count = crate::arch::x86_64::percpu::get_cpu_count().max(1);
1277
1278 let count_s = Box::leak(
1279 alloc::format!("{}\n", cpu_count)
1280 .into_bytes()
1281 .into_boxed_slice(),
1282 );
1283 kernel_scheme.register("cpu/count", count_s.as_ptr(), count_s.len());
1284
1285 let vendor_s = Box::leak(
1286 alloc::format!("{}\n", host.vendor_string())
1287 .into_bytes()
1288 .into_boxed_slice(),
1289 );
1290 kernel_scheme.register("cpu/vendor", vendor_s.as_ptr(), vendor_s.len());
1291
1292 let model_s = Box::leak(
1293 alloc::format!("{}\n", host.model_name_str())
1294 .into_bytes()
1295 .into_boxed_slice(),
1296 );
1297 kernel_scheme.register("cpu/model", model_s.as_ptr(), model_s.len());
1298
1299 let features_s = Box::leak(
1300 alloc::format!(
1301 "{}\n",
1302 crate::arch::x86_64::cpuid::features_to_flags_string(host.features)
1303 )
1304 .into_bytes()
1305 .into_boxed_slice(),
1306 );
1307 kernel_scheme.register("cpu/features", features_s.as_ptr(), features_s.len());
1308
1309 let xcr0_s = Box::leak(
1310 alloc::format!("{:#x}\n", host.max_xcr0)
1311 .into_bytes()
1312 .into_boxed_slice(),
1313 );
1314 kernel_scheme.register("cpu/xcr0", xcr0_s.as_ptr(), xcr0_s.len());
1315
1316 let xsave_s = Box::leak(
1317 alloc::format!("{}\n", host.xsave_size)
1318 .into_bytes()
1319 .into_boxed_slice(),
1320 );
1321 kernel_scheme.register("cpu/xsave_size", xsave_s.as_ptr(), xsave_s.len());
1322 }
1323
1324 let kernel_scheme = Arc::new(kernel_scheme);
1325
1326 if let Err(e) = mount::mount("/sys", kernel_scheme.clone()) {
1328 log::error!("[VFS] Failed to mount /sys: {:?}", e);
1329 } else {
1330 log::info!("[VFS] Mounted /sys (kernel scheme)");
1331 }
1332
1333 let proc_scheme = Arc::new(ProcScheme::new());
1335 if let Err(e) = register_scheme("proc", proc_scheme.clone()) {
1336 log::error!("[VFS] Failed to register proc scheme: {:?}", e);
1337 } else {
1338 log::info!("[VFS] Registered proc scheme");
1339 }
1340
1341 if let Err(e) = mount::mount("/proc", proc_scheme) {
1342 log::error!("[VFS] Failed to mount /proc: {:?}", e);
1343 } else {
1344 log::info!("[VFS] Mounted /proc (procfs)");
1345 }
1346
1347 let ipc_scheme = Arc::new(ipcfs::IpcControlScheme::new());
1348 if let Err(e) = mount::mount("/ipc", ipc_scheme) {
1349 log::error!("[VFS] Failed to mount /ipc: {:?}", e);
1350 } else {
1351 log::info!("[VFS] Mounted /ipc (kernel ipc control scheme)");
1352 }
1353
1354 let dev_scheme = Arc::new(BlkDevScheme::new());
1358 if let Err(e) = mount::mount("/dev", dev_scheme) {
1359 log::error!("[VFS] Failed to mount /dev: {:?}", e);
1360 } else {
1361 log::info!("[VFS] Mounted /dev (block-device scheme)");
1362 }
1363
1364 let console = console_scheme::init_console_scheme();
1366 if let Err(e) = mount::mount("/dev/console", console) {
1367 log::error!("[VFS] Failed to mount /dev/console: {:?}", e);
1368 } else {
1369 log::info!("[VFS] Mounted /dev/console (serial + keyboard)");
1370 }
1371
1372 pty_scheme::init_pty_scheme();
1374 log::info!("[VFS] Mounted /dev/pts (PTY scheme)");
1375
1376 let input_scheme = Arc::new(input_scheme::InputScheme::new());
1378 if let Err(e) = mount::mount("/dev/input", input_scheme) {
1379 log::error!("[VFS] Failed to mount /dev/input: {:?}", e);
1380 } else {
1381 log::info!("[VFS] Mounted /dev/input (raw keyboard + mouse events)");
1382 }
1383
1384 log::info!("[VFS] VFS ready");
1385}