1pub mod blkdev_scheme;
28pub mod console_scheme;
29pub mod fd;
30pub mod file;
31pub mod ipcfs;
32pub mod mount;
33pub mod pipe;
34pub mod procfs;
35pub mod pty_scheme;
36pub mod ramfs_scheme;
37pub mod scheme;
38pub mod scheme_router;
39
40use crate::{process::current_task_clone, sync::SpinLock, syscall::error::SyscallError};
41use alloc::{boxed::Box, string::String, sync::Arc};
42use core::fmt::Write;
43
44pub use blkdev_scheme::BlkDevScheme;
45pub use fd::{FileDescriptorTable, STDERR, STDIN, STDOUT};
46pub use file::OpenFile;
47pub use mount::{list_mounts, mount, resolve, unmount, Namespace};
48pub use pipe::PipeScheme;
49pub use procfs::ProcScheme;
50pub use ramfs_scheme::RamfsScheme;
51pub use scheme::{
52 DirEntry, DynScheme, FileFlags, FileStat, IpcScheme, KernelScheme, OpenFlags, Scheme,
53};
54pub use scheme_router::{
55 get_initfs_file_bytes, init_builtin_schemes, list_schemes, mount_scheme, register_initfs_file,
56 register_scheme,
57};
58
59use crate::memory::{UserSliceRead, UserSliceWrite};
60
61pub use crate::syscall::numbers::AT_FDCWD;
63
64pub fn open(path: &str, flags: OpenFlags) -> Result<u32, SyscallError> {
72 let (scheme, relative_path) = mount::resolve(path)?;
74
75 let open_result = scheme.open(&relative_path, flags)?;
77
78 let open_file = Arc::new(OpenFile::new(
80 scheme,
81 open_result.file_id,
82 String::from(path),
83 flags,
84 open_result.flags,
85 open_result.size,
86 ));
87
88 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
90 let fd = unsafe { (&mut *task.process.fd_table.get()).insert(open_file) };
92
93 Ok(fd)
94}
95
96pub fn open_at(dir_fd: u64, path: &str, flags: OpenFlags) -> Result<u32, SyscallError> {
105 if dir_fd == AT_FDCWD as u64 {
106 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
108 let cwd = unsafe { (&*task.process.cwd.get()).clone() };
109 let abs = resolve_path(path, &cwd);
110 crate::silo::enforce_path_for_current_task(
111 &abs,
112 flags.contains(OpenFlags::READ) || flags.contains(OpenFlags::DIRECTORY),
113 flags.contains(OpenFlags::WRITE) || flags.contains(OpenFlags::CREATE),
114 false,
115 )?;
116 open(&abs, flags)
117 } else {
118 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
120 let fd_table = unsafe { &*task.process.fd_table.get() };
121 let dir_file = fd_table.get(dir_fd as u32)?;
122 if !dir_file.flags().contains(FileFlags::DIRECTORY) {
123 return Err(SyscallError::NotADirectory);
124 }
125 let dir_path = dir_file.path();
126 let abs = resolve_path(path, dir_path);
127 crate::silo::enforce_path_for_current_task(
128 &abs,
129 flags.contains(OpenFlags::READ) || flags.contains(OpenFlags::DIRECTORY),
130 flags.contains(OpenFlags::WRITE) || flags.contains(OpenFlags::CREATE),
131 false,
132 )?;
133 open(&abs, flags)
134 }
135}
136
137pub fn fstat_at(dir_fd: u64, path: &str) -> Result<FileStat, SyscallError> {
141 if dir_fd == AT_FDCWD as u64 {
142 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
143 let cwd = unsafe { (&*task.process.cwd.get()).clone() };
144 let abs = resolve_path(path, &cwd);
145 stat_path(&abs)
146 } else {
147 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
148 let fd_table = unsafe { &*task.process.fd_table.get() };
149 let dir_file = fd_table.get(dir_fd as u32)?;
150 if !dir_file.flags().contains(FileFlags::DIRECTORY) {
151 return Err(SyscallError::NotADirectory);
152 }
153 let dir_path = dir_file.path();
154 let abs = resolve_path(path, dir_path);
155 stat_path(&abs)
156 }
157}
158
159pub fn mkdir(path: &str, mode: u32) -> Result<(), SyscallError> {
161 let (scheme, relative_path) = mount::resolve(path)?;
162 scheme.create_directory(&relative_path, mode)?;
163 Ok(())
164}
165
166pub fn create_file(path: &str, mode: u32) -> Result<(), SyscallError> {
168 let (scheme, relative_path) = mount::resolve(path)?;
169 scheme.create_file(&relative_path, mode)?;
170 Ok(())
171}
172
173pub fn unlink(path: &str) -> Result<(), SyscallError> {
175 let (scheme, relative_path) = mount::resolve(path)?;
176 scheme.unlink(&relative_path)?;
177 Ok(())
178}
179
180pub fn rename(old_path: &str, new_path: &str) -> Result<(), SyscallError> {
182 let (scheme, old_rel) = mount::resolve(old_path)?;
183 let (scheme2, new_rel) = mount::resolve(new_path)?;
184 if !Arc::ptr_eq(&scheme, &scheme2) {
185 return Err(SyscallError::NotSupported);
186 }
187 scheme.rename(&old_rel, &new_rel)
188}
189
190pub fn chmod(path: &str, mode: u32) -> Result<(), SyscallError> {
192 let (scheme, relative_path) = mount::resolve(path)?;
193 scheme.chmod(&relative_path, mode)
194}
195
196pub fn fchmod(fd: u32, mode: u32) -> Result<(), SyscallError> {
198 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
199 let fd_table = unsafe { &*task.process.fd_table.get() };
200 let file = fd_table.get(fd)?;
201 file.scheme().fchmod(file.file_id(), mode)
202}
203
204pub fn truncate(path: &str, length: u64) -> Result<(), SyscallError> {
206 let (scheme, relative_path) = mount::resolve(path)?;
207 let res = scheme.open(&relative_path, OpenFlags::WRITE)?;
208 let r = scheme.truncate(res.file_id, length);
209 let _ = scheme.close(res.file_id);
210 r
211}
212
213pub fn ftruncate(fd: u32, length: u64) -> Result<(), SyscallError> {
215 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
216 let fd_table = unsafe { &*task.process.fd_table.get() };
217 let file = fd_table.get(fd)?;
218 file.scheme().truncate(file.file_id(), length)
219}
220
221pub fn link(old_path: &str, new_path: &str) -> Result<(), SyscallError> {
223 let (scheme, old_rel) = mount::resolve(old_path)?;
224 let (scheme2, new_rel) = mount::resolve(new_path)?;
225 if !Arc::ptr_eq(&scheme, &scheme2) {
226 return Err(SyscallError::NotSupported);
227 }
228 scheme.link(&old_rel, &new_rel)
229}
230
231pub fn symlink(target: &str, link_path: &str) -> Result<(), SyscallError> {
233 let (scheme, link_rel) = mount::resolve(link_path)?;
234 scheme.symlink(target, &link_rel)
235}
236
237pub fn readlink(path: &str) -> Result<String, SyscallError> {
239 let (scheme, relative_path) = mount::resolve(path)?;
240 scheme.readlink(&relative_path)
241}
242
243pub fn read(fd: u32, buf: &mut [u8]) -> Result<usize, SyscallError> {
245 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
246 let fd_table = unsafe { &*task.process.fd_table.get() };
248 let file = fd_table.get(fd)?;
249 file.read(buf)
250}
251
252pub fn write(fd: u32, buf: &[u8]) -> Result<usize, SyscallError> {
254 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
255 let fd_table = unsafe { &*task.process.fd_table.get() };
257 let file = fd_table.get(fd)?;
258 file.write(buf)
259}
260
261pub fn close(fd: u32) -> Result<(), SyscallError> {
266 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
267 let fd_table = unsafe { &mut *task.process.fd_table.get() };
269 let _file = fd_table.remove(fd)?;
270 Ok(())
271 }
273
274pub fn seek(fd: u32, offset: u64) -> Result<u64, SyscallError> {
276 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
277 let fd_table = unsafe { &*task.process.fd_table.get() };
279 let file = fd_table.get(fd)?;
280 file.seek(offset)
281}
282
283pub fn tell(fd: u32) -> Result<u64, SyscallError> {
285 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
286 let fd_table = unsafe { &*task.process.fd_table.get() };
288 let file = fd_table.get(fd)?;
289 Ok(file.tell())
290}
291
292pub fn fsize(fd: u32) -> Result<u64, SyscallError> {
294 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
295 let fd_table = unsafe { &*task.process.fd_table.get() };
297 let file = fd_table.get(fd)?;
298 file.size()
299}
300
301pub fn fsync(fd: u32) -> Result<(), SyscallError> {
303 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
304 let fd_table = unsafe { &*task.process.fd_table.get() };
306 let file = fd_table.get(fd)?;
307 file.sync()
308}
309
310pub fn lseek(fd: u32, offset: i64, whence: u32) -> Result<u64, SyscallError> {
312 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
313 let fd_table = unsafe { &*task.process.fd_table.get() };
314 let file = fd_table.get(fd)?;
315 file.lseek(offset, whence)
316}
317
318pub fn fstat(fd: u32) -> Result<FileStat, SyscallError> {
320 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
321 let fd_table = unsafe { &*task.process.fd_table.get() };
322 let file = fd_table.get(fd)?;
323 file.stat()
324}
325
326pub fn stat_path(path: &str) -> Result<FileStat, SyscallError> {
328 let (scheme, relative_path) = mount::resolve(path)?;
329 let open_result = scheme.open(&relative_path, OpenFlags::READ)?;
330 let result = scheme.stat(open_result.file_id);
331 let _ = scheme.close(open_result.file_id);
332 result
333}
334
335pub fn getdents(fd: u32) -> Result<alloc::vec::Vec<DirEntry>, SyscallError> {
337 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
338 let fd_table = unsafe { &*task.process.fd_table.get() };
339 let file = fd_table.get(fd)?;
340 file.readdir()
341}
342
343pub fn create_background_stdin() -> Arc<OpenFile> {
348 let pipe_scheme = get_pipe_scheme();
349 let (base_id, pipe) = pipe_scheme.create_pipe();
350
351 pipe.close_write();
354
355 let dyn_scheme: DynScheme = pipe_scheme as Arc<dyn Scheme>;
356 Arc::new(OpenFile::new(
357 dyn_scheme,
358 base_id, String::from("pipe:[bg-stdin]"),
360 OpenFlags::READ,
361 FileFlags::PIPE,
362 None,
363 ))
364}
365
366pub fn pipe() -> Result<(u32, u32), SyscallError> {
368 let pipe_scheme = get_pipe_scheme();
369 let (base_id, _pipe) = pipe_scheme.create_pipe();
370
371 let dyn_scheme: DynScheme = pipe_scheme as Arc<dyn Scheme>;
372
373 let read_file = Arc::new(OpenFile::new(
374 dyn_scheme.clone(),
375 base_id,
376 String::from("pipe:[read]"),
377 OpenFlags::READ,
378 FileFlags::PIPE,
379 None,
380 ));
381 let write_file = Arc::new(OpenFile::new(
382 dyn_scheme.clone(),
383 base_id + 1,
384 String::from("pipe:[write]"),
385 OpenFlags::WRITE,
386 FileFlags::PIPE,
387 None,
388 ));
389
390 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
391 let fd_table = unsafe { &mut *task.process.fd_table.get() };
392 let read_fd = fd_table.insert(read_file);
393 let write_fd = fd_table.insert(write_file);
394
395 Ok((read_fd, write_fd))
396}
397
398pub fn dup(old_fd: u32) -> Result<u32, SyscallError> {
400 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
401 let fd_table = unsafe { &mut *task.process.fd_table.get() };
402 fd_table.duplicate(old_fd)
403}
404
405pub fn dup2(old_fd: u32, new_fd: u32) -> Result<u32, SyscallError> {
407 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
408 let fd_table = unsafe { &mut *task.process.fd_table.get() };
409 fd_table.duplicate_to(old_fd, new_fd)
410}
411
412pub fn read_all(fd: u32) -> Result<alloc::vec::Vec<u8>, SyscallError> {
414 let mut out = alloc::vec::Vec::new();
415 let mut buf = [0u8; 4096];
416 loop {
417 let n = read(fd, &mut buf)?;
418 if n == 0 {
419 break;
420 }
421 out.extend_from_slice(&buf[..n]);
422 }
423 Ok(out)
424}
425
426pub fn sys_open(path_ptr: u64, path_len: u64, flags: u64) -> Result<u64, SyscallError> {
432 const MAX_PATH_LEN: usize = 4096;
433 if path_len == 0 || path_len as usize > MAX_PATH_LEN {
434 return Err(SyscallError::InvalidArgument);
435 }
436
437 let raw = read_user_path(path_ptr, path_len)?;
438 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
439 let cwd = unsafe { (&*task.process.cwd.get()).clone() };
440 let path = resolve_path(&raw, &cwd);
441
442 let open_flags = OpenFlags::from_bits_truncate(flags as u32);
443
444 let want_read =
445 open_flags.contains(OpenFlags::READ) || open_flags.contains(OpenFlags::DIRECTORY);
446 let want_write = open_flags.contains(OpenFlags::WRITE)
447 || open_flags.contains(OpenFlags::CREATE)
448 || open_flags.contains(OpenFlags::TRUNCATE)
449 || open_flags.contains(OpenFlags::APPEND);
450 crate::silo::enforce_path_for_current_task(&path, want_read, want_write, false)?;
451
452 let fd = open(&path, open_flags)?;
453 Ok(fd as u64)
454}
455
456pub fn sys_openat(
458 dir_fd: u64,
459 path_ptr: u64,
460 path_len: u64,
461 flags: u64,
462) -> Result<u64, SyscallError> {
463 const MAX_PATH_LEN: usize = 4096;
464 if path_len == 0 || path_len as usize > MAX_PATH_LEN {
465 return Err(SyscallError::InvalidArgument);
466 }
467 let raw = read_user_path(path_ptr, path_len)?;
468 let open_flags = OpenFlags::from_bits_truncate(flags as u32);
469 let fd = open_at(dir_fd, &raw, open_flags)?;
470 Ok(fd as u64)
471}
472
473pub fn sys_fstatat(
475 dir_fd: u64,
476 path_ptr: u64,
477 path_len: u64,
478 _flags: u64,
479) -> Result<u64, SyscallError> {
480 const MAX_PATH_LEN: usize = 4096;
481 if path_len == 0 || path_len as usize > MAX_PATH_LEN {
482 return Err(SyscallError::InvalidArgument);
483 }
484 let raw = read_user_path(path_ptr, path_len)?;
485 let st = fstat_at(dir_fd, &raw)?;
486 let _ = st;
488 Err(SyscallError::NotImplemented)
489}
490
491pub fn sys_read(fd: u32, buf_ptr: u64, buf_len: u64) -> Result<u64, SyscallError> {
493 if buf_len == 0 {
494 return Ok(0);
495 }
496
497 let mut kbuf = [0u8; 4096];
499 let mut total_read = 0;
500
501 while total_read < buf_len as usize {
502 let to_read = core::cmp::min(kbuf.len(), buf_len as usize - total_read);
503 let n = read(fd, &mut kbuf[..to_read])?;
504 if n == 0 {
505 break;
506 }
507
508 let chunk_user = UserSliceWrite::new(buf_ptr + total_read as u64, n)?;
509 chunk_user.copy_from(&kbuf[..n]);
510
511 total_read += n;
512 if n < to_read {
513 break;
514 }
515 }
516
517 Ok(total_read as u64)
518}
519
520pub fn sys_write(fd: u32, buf_ptr: u64, buf_len: u64) -> Result<u64, SyscallError> {
522 if buf_len == 0 {
523 return Ok(0);
524 }
525
526 if fd == 1 || fd == 2 {
530 let use_console = match current_task_clone() {
531 Some(t) => {
532 let fd_table = unsafe { &*t.process.fd_table.get() };
533 !fd_table.contains(fd)
534 }
535 None => true,
536 };
537 if use_console {
538 crate::silo::enforce_console_access()?;
539 let len = core::cmp::min(buf_len as usize, 16 * 1024);
540 let mut kbuf = [0u8; 4096];
541 let mut total_written = 0;
542 while total_written < len {
543 let to_write = core::cmp::min(kbuf.len(), len - total_written);
544 let chunk = UserSliceRead::new(buf_ptr + total_written as u64, to_write)?;
545 let n = chunk.copy_to(&mut kbuf[..to_write]);
546 if crate::arch::x86_64::vga::is_available() {
547 if let Ok(s) = core::str::from_utf8(&kbuf[..n]) {
548 crate::serial_print!("{}", s);
549 crate::vga_print!("{}", s);
550 } else {
551 for &byte in &kbuf[..n] {
552 crate::serial_print!("{}", byte as char);
553 }
554 }
555 } else {
556 for &byte in &kbuf[..n] {
557 crate::serial_print!("{}", byte as char);
558 }
559 }
560 total_written += n;
561 }
562 return Ok(total_written as u64);
563 }
564 }
565
566 let mut kbuf = [0u8; 4096];
567 let mut total_written = 0;
568
569 while total_written < buf_len as usize {
570 let to_write = core::cmp::min(kbuf.len(), buf_len as usize - total_written);
571 let chunk_user = UserSliceRead::new(buf_ptr + total_written as u64, to_write)?;
572 chunk_user.copy_to(&mut kbuf[..to_write]);
573
574 let n = write(fd, &kbuf[..to_write])?;
575 total_written += n;
576 if n < to_write {
577 break;
578 }
579 }
580
581 Ok(total_written as u64)
582}
583
584pub fn sys_close(fd: u32) -> Result<u64, SyscallError> {
586 close(fd)?;
587 Ok(0)
588}
589
590pub fn sys_lseek(fd: u32, offset: i64, whence: u32) -> Result<u64, SyscallError> {
592 lseek(fd, offset, whence)
593}
594
595pub fn sys_fstat(fd: u32, stat_ptr: u64) -> Result<u64, SyscallError> {
597 let st = fstat(fd)?;
598 let user = UserSliceWrite::new(stat_ptr, core::mem::size_of::<FileStat>())?;
599 let bytes = unsafe {
600 core::slice::from_raw_parts(
601 &st as *const FileStat as *const u8,
602 core::mem::size_of::<FileStat>(),
603 )
604 };
605 user.copy_from(bytes);
606 Ok(0)
607}
608
609pub fn sys_stat(path_ptr: u64, path_len: u64, stat_ptr: u64) -> Result<u64, SyscallError> {
611 const MAX_PATH_LEN: usize = 4096;
612 if path_len == 0 || path_len as usize > MAX_PATH_LEN {
613 return Err(SyscallError::InvalidArgument);
614 }
615 let raw = read_user_path(path_ptr, path_len)?;
616 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
617 let cwd = unsafe { (&*task.process.cwd.get()).clone() };
618 let path = resolve_path(&raw, &cwd);
619 crate::silo::enforce_path_for_current_task(&path, true, false, false)?;
620
621 let st = stat_path(&path)?;
622 let user_out = UserSliceWrite::new(stat_ptr, core::mem::size_of::<FileStat>())?;
623 let out_bytes = unsafe {
624 core::slice::from_raw_parts(
625 &st as *const FileStat as *const u8,
626 core::mem::size_of::<FileStat>(),
627 )
628 };
629 user_out.copy_from(out_bytes);
630 Ok(0)
631}
632
633pub fn sys_getdents(fd: u32, buf_ptr: u64, buf_len: u64) -> Result<u64, SyscallError> {
638 use strat9_abi::data::DirentHeader;
639
640 let entries = getdents(fd)?;
641 let mut offset: usize = 0;
642 let buf_size = buf_len as usize;
643
644 for entry in &entries {
645 let name_bytes = entry.name.as_bytes();
646 let name_len = core::cmp::min(name_bytes.len(), 255) as u16;
647 let entry_size = DirentHeader::SIZE + name_len as usize + 1;
648
649 if offset + entry_size > buf_size {
650 break;
651 }
652
653 let user = UserSliceWrite::new(buf_ptr + offset as u64, entry_size)?;
654 let mut kbuf = [0u8; 268];
655 kbuf[0..8].copy_from_slice(&entry.ino.to_le_bytes());
656 kbuf[8] = entry.file_type;
657 kbuf[9..11].copy_from_slice(&name_len.to_le_bytes());
658 kbuf[11] = 0; kbuf[12..12 + name_len as usize].copy_from_slice(&name_bytes[..name_len as usize]);
660 kbuf[12 + name_len as usize] = 0;
661 user.copy_from(&kbuf[..entry_size]);
662
663 offset += entry_size;
664 }
665
666 Ok(offset as u64)
667}
668
669pub fn sys_pipe(fds_ptr: u64) -> Result<u64, SyscallError> {
671 let (read_fd, write_fd) = pipe()?;
672 let user = UserSliceWrite::new(fds_ptr, 8)?; let mut buf = [0u8; 8];
674 buf[0..4].copy_from_slice(&read_fd.to_le_bytes());
675 buf[4..8].copy_from_slice(&write_fd.to_le_bytes());
676 user.copy_from(&buf);
677 Ok(0)
678}
679
680pub fn sys_dup(old_fd: u32) -> Result<u64, SyscallError> {
682 let new_fd = dup(old_fd)?;
683 Ok(new_fd as u64)
684}
685
686pub fn sys_dup2(old_fd: u32, new_fd: u32) -> Result<u64, SyscallError> {
688 let fd = dup2(old_fd, new_fd)?;
689 Ok(fd as u64)
690}
691
692fn read_user_path(path_ptr: u64, path_len: u64) -> Result<alloc::string::String, SyscallError> {
699 const MAX_PATH: usize = 4096;
700 let len = if path_len == 0 || path_len as usize > MAX_PATH {
701 MAX_PATH
702 } else {
703 path_len as usize
704 };
705 let user = UserSliceRead::new(path_ptr, len)?;
706 let bytes = user.read_to_vec();
707 let trimmed = bytes.split(|&b| b == 0).next().unwrap_or(&bytes);
709 if trimmed.is_empty() {
710 return Err(SyscallError::InvalidArgument);
711 }
712 core::str::from_utf8(trimmed)
713 .map(|s| alloc::string::String::from(s))
714 .map_err(|_| SyscallError::InvalidArgument)
715}
716
717fn resolve_path(path: &str, cwd: &str) -> alloc::string::String {
720 let raw = if path.starts_with('/') {
721 alloc::string::String::from(path)
722 } else if cwd.ends_with('/') {
723 alloc::format!("{}{}", cwd, path)
724 } else {
725 alloc::format!("{}/{}", cwd, path)
726 };
727 normalize_path(&raw)
728}
729
730fn normalize_path(path: &str) -> alloc::string::String {
732 let mut parts: alloc::vec::Vec<&str> = alloc::vec::Vec::new();
733 for seg in path.split('/') {
734 match seg {
735 "" | "." => {}
736 ".." => {
737 parts.pop();
738 }
739 other => parts.push(other),
740 }
741 }
742 let mut out = alloc::string::String::with_capacity(path.len());
743 if parts.is_empty() {
744 out.push('/');
745 } else {
746 for p in &parts {
747 out.push('/');
748 out.push_str(p);
749 }
750 }
751 out
752}
753
754pub fn sys_chdir(path_ptr: u64, path_len: u64) -> Result<u64, SyscallError> {
758 let raw = read_user_path(path_ptr, path_len)?;
759 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
760 let cwd = unsafe { &*task.process.cwd.get() };
761 let abs = resolve_path(&raw, cwd);
762 crate::silo::enforce_path_for_current_task(&abs, true, false, false)?;
763
764 let (scheme, rel) = mount::resolve(&abs)?;
765 let res = scheme.open(&rel, OpenFlags::READ | OpenFlags::DIRECTORY)?;
766 let _ = scheme.close(res.file_id);
767
768 unsafe { *task.process.cwd.get() = abs };
769 Ok(0)
770}
771
772pub fn sys_fchdir(fd: u32) -> Result<u64, SyscallError> {
774 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
775 let path = {
776 let fd_table = unsafe { &*task.process.fd_table.get() };
777 let file = fd_table.get(fd)?;
778 alloc::string::String::from(file.path())
779 };
780 unsafe { *task.process.cwd.get() = path };
781 Ok(0)
782}
783
784pub fn sys_getcwd(buf_ptr: u64, buf_len: u64) -> Result<u64, SyscallError> {
786 if buf_len == 0 {
787 return Err(SyscallError::InvalidArgument);
788 }
789 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
790 let cwd = unsafe { (&*task.process.cwd.get()).clone() };
791 let bytes = cwd.as_bytes();
792 let needed = bytes.len() + 1; if needed > buf_len as usize {
794 return Err(SyscallError::Range);
795 }
796 let out = UserSliceWrite::new(buf_ptr, needed)?;
797 let mut tmp = alloc::vec![0u8; needed];
798 tmp[..bytes.len()].copy_from_slice(bytes);
799 tmp[bytes.len()] = 0;
800 out.copy_from(&tmp);
801 Ok(needed as u64) }
803
804pub fn sys_ioctl(_fd: u32, _request: u64, _arg: u64) -> Result<u64, SyscallError> {
809 Err(SyscallError::NotATty)
810}
811
812pub fn sys_umask(mask: u64) -> Result<u64, SyscallError> {
814 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
815 let old = task
816 .process
817 .umask
818 .swap(mask as u32 & 0o777, core::sync::atomic::Ordering::Relaxed);
819 Ok(old as u64)
820}
821
822pub fn sys_unlink(path_ptr: u64, path_len: u64) -> Result<u64, SyscallError> {
824 let raw = read_user_path(path_ptr, path_len)?;
825 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
826 let cwd = unsafe { (&*task.process.cwd.get()).clone() };
827 let abs = resolve_path(&raw, &cwd);
828 crate::silo::enforce_path_for_current_task(&abs, false, true, false)?;
829 unlink(&abs)?;
830 Ok(0)
831}
832
833pub fn sys_rmdir(path_ptr: u64, path_len: u64) -> Result<u64, SyscallError> {
835 let raw = read_user_path(path_ptr, path_len)?;
836 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
837 let cwd = unsafe { (&*task.process.cwd.get()).clone() };
838 let abs = resolve_path(&raw, &cwd);
839 crate::silo::enforce_path_for_current_task(&abs, false, true, false)?;
840 unlink(&abs)?;
841 Ok(0)
842}
843
844pub fn sys_mkdir(path_ptr: u64, path_len: u64, mode: u64) -> Result<u64, SyscallError> {
846 let raw = read_user_path(path_ptr, path_len)?;
847 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
848 let umask = task
849 .process
850 .umask
851 .load(core::sync::atomic::Ordering::Relaxed);
852 let cwd = unsafe { (&*task.process.cwd.get()).clone() };
853 let abs = resolve_path(&raw, &cwd);
854 crate::silo::enforce_path_for_current_task(&abs, false, true, false)?;
855 let effective_mode = (mode as u32) & !umask;
856 mkdir(&abs, effective_mode)?;
857 Ok(0)
858}
859
860pub fn sys_rename(
862 old_ptr: u64,
863 old_len: u64,
864 new_ptr: u64,
865 new_len: u64,
866) -> Result<u64, SyscallError> {
867 let old = read_user_path(old_ptr, old_len)?;
868 let new = read_user_path(new_ptr, new_len)?;
869 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
870 let cwd = unsafe { (&*task.process.cwd.get()).clone() };
871 let old_abs = resolve_path(&old, &cwd);
872 let new_abs = resolve_path(&new, &cwd);
873 crate::silo::enforce_path_for_current_task(&old_abs, true, true, false)?;
874 crate::silo::enforce_path_for_current_task(&new_abs, false, true, false)?;
875 rename(&old_abs, &new_abs)?;
876 Ok(0)
877}
878
879pub fn sys_link(
881 old_ptr: u64,
882 old_len: u64,
883 new_ptr: u64,
884 new_len: u64,
885) -> Result<u64, SyscallError> {
886 let old = read_user_path(old_ptr, old_len)?;
887 let new = read_user_path(new_ptr, new_len)?;
888 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
889 let cwd = unsafe { (&*task.process.cwd.get()).clone() };
890 let old_abs = resolve_path(&old, &cwd);
891 let new_abs = resolve_path(&new, &cwd);
892 crate::silo::enforce_path_for_current_task(&old_abs, true, false, false)?;
893 crate::silo::enforce_path_for_current_task(&new_abs, false, true, false)?;
894 link(&old_abs, &new_abs)?;
895 Ok(0)
896}
897
898pub fn sys_symlink(
900 target_ptr: u64,
901 target_len: u64,
902 linkpath_ptr: u64,
903 linkpath_len: u64,
904) -> Result<u64, SyscallError> {
905 let target = read_user_path(target_ptr, target_len)?;
906 let linkpath = read_user_path(linkpath_ptr, linkpath_len)?;
907 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
908 let cwd = unsafe { (&*task.process.cwd.get()).clone() };
909 let link_abs = resolve_path(&linkpath, &cwd);
910 crate::silo::enforce_path_for_current_task(&link_abs, false, true, false)?;
911 symlink(&target, &link_abs)?;
912 Ok(0)
913}
914
915pub fn sys_readlink(
917 path_ptr: u64,
918 path_len: u64,
919 buf_ptr: u64,
920 buf_len: u64,
921) -> Result<u64, SyscallError> {
922 let path = read_user_path(path_ptr, path_len)?;
923 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
924 let cwd = unsafe { (&*task.process.cwd.get()).clone() };
925 let abs = resolve_path(&path, &cwd);
926 crate::silo::enforce_path_for_current_task(&abs, true, false, false)?;
927 let target = readlink(&abs)?;
928 let bytes = target.as_bytes();
929 let n = bytes.len().min(buf_len as usize);
930 let user = UserSliceWrite::new(buf_ptr, n)?;
931 user.copy_from(&bytes[..n]);
932 Ok(n as u64)
933}
934
935pub fn sys_chmod(path_ptr: u64, path_len: u64, mode: u64) -> Result<u64, SyscallError> {
937 let path = read_user_path(path_ptr, path_len)?;
938 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
939 let cwd = unsafe { (&*task.process.cwd.get()).clone() };
940 let abs = resolve_path(&path, &cwd);
941 crate::silo::enforce_path_for_current_task(&abs, false, true, false)?;
942 chmod(&abs, mode as u32)?;
943 Ok(0)
944}
945
946pub fn sys_fchmod(fd: u32, mode: u64) -> Result<u64, SyscallError> {
948 fchmod(fd, mode as u32)?;
949 Ok(0)
950}
951
952pub fn sys_truncate(path_ptr: u64, path_len: u64, length: u64) -> Result<u64, SyscallError> {
954 let path = read_user_path(path_ptr, path_len)?;
955 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
956 let cwd = unsafe { (&*task.process.cwd.get()).clone() };
957 let abs = resolve_path(&path, &cwd);
958 crate::silo::enforce_path_for_current_task(&abs, false, true, false)?;
959 truncate(&abs, length)?;
960 Ok(0)
961}
962
963pub fn sys_ftruncate(fd: u32, length: u64) -> Result<u64, SyscallError> {
965 ftruncate(fd, length)?;
966 Ok(0)
967}
968
969pub fn sys_pread(fd: u32, buf_ptr: u64, buf_len: u64, offset: u64) -> Result<u64, SyscallError> {
971 if buf_len == 0 {
972 return Ok(0);
973 }
974 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
975 let fd_table = unsafe { &*task.process.fd_table.get() };
976 let file = fd_table.get(fd)?;
977 let mut kbuf = [0u8; 4096];
978 let mut total = 0usize;
979 let mut off = offset;
980 while total < buf_len as usize {
981 let to_read = core::cmp::min(kbuf.len(), buf_len as usize - total);
982 let n = file.pread(off, &mut kbuf[..to_read])?;
983 if n == 0 {
984 break;
985 }
986 let user = UserSliceWrite::new(buf_ptr + total as u64, n)?;
987 user.copy_from(&kbuf[..n]);
988 total += n;
989 off += n as u64;
990 if n < to_read {
991 break;
992 }
993 }
994 Ok(total as u64)
995}
996
997pub fn sys_pwrite(fd: u32, buf_ptr: u64, buf_len: u64, offset: u64) -> Result<u64, SyscallError> {
999 if buf_len == 0 {
1000 return Ok(0);
1001 }
1002 let task = current_task_clone().ok_or(SyscallError::PermissionDenied)?;
1003 let fd_table = unsafe { &*task.process.fd_table.get() };
1004 let file = fd_table.get(fd)?;
1005 let mut kbuf = [0u8; 4096];
1006 let mut total = 0usize;
1007 let mut off = offset;
1008 while total < buf_len as usize {
1009 let to_write = core::cmp::min(kbuf.len(), buf_len as usize - total);
1010 let user = UserSliceRead::new(buf_ptr + total as u64, to_write)?;
1011 user.copy_to(&mut kbuf[..to_write]);
1012 let n = file.pwrite(off, &kbuf[..to_write])?;
1013 total += n;
1014 off += n as u64;
1015 if n < to_write {
1016 break;
1017 }
1018 }
1019 Ok(total as u64)
1020}
1021
1022static PIPE_SCHEME: SpinLock<Option<Arc<PipeScheme>>> = SpinLock::new(None);
1027
1028fn get_pipe_scheme() -> Arc<PipeScheme> {
1030 let mut guard = PIPE_SCHEME.lock();
1031 if let Some(ref scheme) = *guard {
1032 return scheme.clone();
1033 }
1034 let scheme = Arc::new(PipeScheme::new());
1035 *guard = Some(scheme.clone());
1036 scheme
1037}
1038
1039fn build_pci_inventory_text() -> String {
1041 #[cfg(target_arch = "x86_64")]
1042 {
1043 let devices = crate::hardware::pci_client::all_devices();
1044 let mut out = String::new();
1045 out.push_str("bus dev fn vendor device class subclass prog_if irq\n");
1046 for dev in devices.iter() {
1047 let _ = writeln!(
1048 out,
1049 "{:02x} {:02x} {} {:04x} {:04x} {:02x} {:02x} {:02x} {}",
1050 dev.address.bus,
1051 dev.address.device,
1052 dev.address.function,
1053 dev.vendor_id,
1054 dev.device_id,
1055 dev.class_code,
1056 dev.subclass,
1057 dev.prog_if,
1058 dev.interrupt_line
1059 );
1060 }
1061 return out;
1062 }
1063
1064 #[cfg(not(target_arch = "x86_64"))]
1065 {
1066 String::from("unsupported-arch\n")
1067 }
1068}
1069
1070pub fn init() {
1076 log::info!("[VFS] Initializing virtual file system");
1077
1078 let rootfs = alloc::sync::Arc::new(RamfsScheme::new());
1082 if let Err(e) = mount::mount("/", rootfs.clone()) {
1083 log::error!("[VFS] Failed to mount /: {:?}", e);
1084 } else {
1085 for dir in &[
1087 "bin", "sbin", "etc", "tmp", "usr", "lib", "lib64", "home", "root", "run", "var",
1088 "mnt", "opt", "srv", "dev", "proc", "sys",
1089 ] {
1090 rootfs.ensure_dir(dir);
1091 }
1092 rootfs.ensure_dir("usr/bin");
1094 rootfs.ensure_dir("usr/sbin");
1095 rootfs.ensure_dir("usr/lib");
1096 rootfs.ensure_dir("var/log");
1097 rootfs.ensure_dir("var/tmp");
1098 rootfs.ensure_dir("run/lock");
1099 log::info!("[VFS] Mounted / (ramfs) with standard directory tree");
1100 }
1101
1102 if let Err(e) = scheme_router::init_builtin_schemes() {
1104 log::error!("[VFS] Failed to init builtin schemes: {:?}", e);
1105 }
1106
1107 let kernel_scheme = KernelScheme::new();
1109
1110 static VERSION: &[u8] = b"Strat9-OS v0.1.0 (Bedrock)\n";
1112 kernel_scheme.register("version", VERSION.as_ptr(), VERSION.len());
1113
1114 static CMDLINE: &[u8] = b"quiet loglevel=debug\n";
1115 kernel_scheme.register("cmdline", CMDLINE.as_ptr(), CMDLINE.len());
1116
1117 let pci_inventory = build_pci_inventory_text().into_bytes().into_boxed_slice();
1118 let pci_inventory = Box::leak(pci_inventory);
1119 kernel_scheme.register("pci/inventory", pci_inventory.as_ptr(), pci_inventory.len());
1120
1121 let pci_count = pci_inventory
1122 .split(|b| *b == b'\n')
1123 .skip(1)
1124 .filter(|line| !line.is_empty())
1125 .count();
1126 let mut pci_count_str = String::new();
1127 let _ = writeln!(pci_count_str, "{}", pci_count);
1128 let pci_count = Box::leak(pci_count_str.into_bytes().into_boxed_slice());
1129 kernel_scheme.register("pci/count", pci_count.as_ptr(), pci_count.len());
1130
1131 {
1133 let host = crate::arch::x86_64::cpuid::host();
1134 let cpu_count = crate::arch::x86_64::percpu::get_cpu_count().max(1);
1137
1138 let count_s = Box::leak(
1139 alloc::format!("{}\n", cpu_count)
1140 .into_bytes()
1141 .into_boxed_slice(),
1142 );
1143 kernel_scheme.register("cpu/count", count_s.as_ptr(), count_s.len());
1144
1145 let vendor_s = Box::leak(
1146 alloc::format!("{}\n", host.vendor_string())
1147 .into_bytes()
1148 .into_boxed_slice(),
1149 );
1150 kernel_scheme.register("cpu/vendor", vendor_s.as_ptr(), vendor_s.len());
1151
1152 let model_s = Box::leak(
1153 alloc::format!("{}\n", host.model_name_str())
1154 .into_bytes()
1155 .into_boxed_slice(),
1156 );
1157 kernel_scheme.register("cpu/model", model_s.as_ptr(), model_s.len());
1158
1159 let features_s = Box::leak(
1160 alloc::format!(
1161 "{}\n",
1162 crate::arch::x86_64::cpuid::features_to_flags_string(host.features)
1163 )
1164 .into_bytes()
1165 .into_boxed_slice(),
1166 );
1167 kernel_scheme.register("cpu/features", features_s.as_ptr(), features_s.len());
1168
1169 let xcr0_s = Box::leak(
1170 alloc::format!("{:#x}\n", host.max_xcr0)
1171 .into_bytes()
1172 .into_boxed_slice(),
1173 );
1174 kernel_scheme.register("cpu/xcr0", xcr0_s.as_ptr(), xcr0_s.len());
1175
1176 let xsave_s = Box::leak(
1177 alloc::format!("{}\n", host.xsave_size)
1178 .into_bytes()
1179 .into_boxed_slice(),
1180 );
1181 kernel_scheme.register("cpu/xsave_size", xsave_s.as_ptr(), xsave_s.len());
1182 }
1183
1184 let kernel_scheme = Arc::new(kernel_scheme);
1185
1186 if let Err(e) = mount::mount("/sys", kernel_scheme.clone()) {
1188 log::error!("[VFS] Failed to mount /sys: {:?}", e);
1189 } else {
1190 log::info!("[VFS] Mounted /sys (kernel scheme)");
1191 }
1192
1193 let proc_scheme = Arc::new(ProcScheme::new());
1195 if let Err(e) = register_scheme("proc", proc_scheme.clone()) {
1196 log::error!("[VFS] Failed to register proc scheme: {:?}", e);
1197 } else {
1198 log::info!("[VFS] Registered proc scheme");
1199 }
1200
1201 if let Err(e) = mount::mount("/proc", proc_scheme) {
1202 log::error!("[VFS] Failed to mount /proc: {:?}", e);
1203 } else {
1204 log::info!("[VFS] Mounted /proc (procfs)");
1205 }
1206
1207 let ipc_scheme = Arc::new(ipcfs::IpcControlScheme::new());
1208 if let Err(e) = mount::mount("/ipc", ipc_scheme) {
1209 log::error!("[VFS] Failed to mount /ipc: {:?}", e);
1210 } else {
1211 log::info!("[VFS] Mounted /ipc (kernel ipc control scheme)");
1212 }
1213
1214 let dev_scheme = Arc::new(BlkDevScheme::new());
1218 if let Err(e) = mount::mount("/dev", dev_scheme) {
1219 log::error!("[VFS] Failed to mount /dev: {:?}", e);
1220 } else {
1221 log::info!("[VFS] Mounted /dev (block-device scheme)");
1222 }
1223
1224 let console = console_scheme::init_console_scheme();
1226 if let Err(e) = mount::mount("/dev/console", console) {
1227 log::error!("[VFS] Failed to mount /dev/console: {:?}", e);
1228 } else {
1229 log::info!("[VFS] Mounted /dev/console (serial + keyboard)");
1230 }
1231
1232 pty_scheme::init_pty_scheme();
1234 log::info!("[VFS] Mounted /dev/pts (PTY scheme)");
1235
1236 log::info!("[VFS] VFS ready");
1237}