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