1use crate::{
7 ipc::{message::IpcMessage, port::PortId},
8 memory::{UserSliceRead, UserSliceWrite},
9 sync::SpinLock,
10 syscall::error::SyscallError,
11};
12use alloc::{
13 collections::BTreeMap,
14 string::{String, ToString},
15 sync::Arc,
16 vec::Vec,
17};
18
19pub use strat9_abi::data::{
20 FileStat, DT_BLK, DT_CHR, DT_DIR, DT_FIFO, DT_LNK, DT_REG, DT_SOCK, DT_UNKNOWN,
21 IPC_FILE_FLAG_APPEND, IPC_FILE_FLAG_CHUNK_READ, IPC_FILE_FLAG_CHUNK_WRITE,
22 IPC_FILE_FLAG_DEVICE, IPC_FILE_FLAG_DIRECTORY, IPC_FILE_FLAG_PIPE,
23};
24use strat9_abi::{
25 ipc_codec::{get_u32, put_u16_len_prefixed},
26 ipc_payload::{
27 CloseRequest, CreateRequest, OpenReply, OpenRequest, ReadReply, ReadRequest, WriteRequest,
28 OPCODE_CLOSE, OPCODE_CREATE_DIR, OPCODE_CREATE_FILE, OPCODE_OPEN, OPCODE_READ,
29 OPCODE_READDIR, OPCODE_UNLINK, OPCODE_WRITE,
30 },
31};
32
33#[derive(Debug, Clone)]
35pub struct DirEntry {
36 pub ino: u64,
37 pub file_type: u8,
38 pub name: String,
39}
40
41#[derive(Debug, Clone)]
43pub struct OpenResult {
44 pub file_id: u64,
46 pub size: Option<u64>,
48 pub flags: FileFlags,
50}
51
52bitflags::bitflags! {
53 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
55 pub struct FileFlags: u32 {
56 const DIRECTORY = IPC_FILE_FLAG_DIRECTORY;
57 const DEVICE = IPC_FILE_FLAG_DEVICE;
58 const PIPE = IPC_FILE_FLAG_PIPE;
59 const APPEND = IPC_FILE_FLAG_APPEND;
60 const CHUNK_READ = IPC_FILE_FLAG_CHUNK_READ;
61 const CHUNK_WRITE = IPC_FILE_FLAG_CHUNK_WRITE;
62 }
63}
64
65pub use strat9_abi::flag::OpenFlags;
66
67pub trait Scheme: Send + Sync {
69 fn open(&self, path: &str, flags: OpenFlags) -> Result<OpenResult, SyscallError>;
74
75 fn read(&self, file_id: u64, offset: u64, buf: &mut [u8]) -> Result<usize, SyscallError>;
77
78 fn write(&self, file_id: u64, offset: u64, buf: &[u8]) -> Result<usize, SyscallError>;
80
81 fn async_read(
87 &self,
88 file_id: u64,
89 offset: u64,
90 user_buf_vaddr: u64,
91 len: usize,
92 _ring_id: u64,
93 _user_data: u64,
94 ) -> Result<AsyncSubmitResult, SyscallError> {
95 const MAX_SYNC_FALLBACK_LEN: usize = 4 * 1024 * 1024; if len > MAX_SYNC_FALLBACK_LEN {
99 return Err(SyscallError::InvalidArgument);
100 }
101 let user_buf = UserSliceWrite::new(user_buf_vaddr, len)?;
102 let mut kernel_buf = alloc::vec![0u8; len];
103 let n = self.read(file_id, offset, &mut kernel_buf)?;
104 user_buf.copy_from(&kernel_buf[..n]);
105 Ok(AsyncSubmitResult::Completed(n as i32))
106 }
107
108 fn async_write(
113 &self,
114 file_id: u64,
115 offset: u64,
116 user_buf_vaddr: u64,
117 len: usize,
118 _ring_id: u64,
119 _user_data: u64,
120 ) -> Result<AsyncSubmitResult, SyscallError> {
121 const MAX_SYNC_FALLBACK_LEN: usize = 4 * 1024 * 1024; if len > MAX_SYNC_FALLBACK_LEN {
124 return Err(SyscallError::InvalidArgument);
125 }
126 let user_buf = UserSliceRead::new(user_buf_vaddr, len)?;
127 let kernel_buf = user_buf.read_to_vec();
128 let n = self.write(file_id, offset, &kernel_buf)?;
129 Ok(AsyncSubmitResult::Completed(n as i32))
130 }
131
132 fn close(&self, file_id: u64) -> Result<(), SyscallError>;
134
135 fn size(&self, file_id: u64) -> Result<u64, SyscallError> {
137 let _ = file_id;
138 Err(SyscallError::NotImplemented)
139 }
140
141 fn truncate(&self, file_id: u64, new_size: u64) -> Result<(), SyscallError> {
143 let _ = (file_id, new_size);
144 Err(SyscallError::NotImplemented)
145 }
146
147 fn truncate_by_path(&self, _path: &str, _new_size: u64) -> Result<(), SyscallError> {
152 Err(SyscallError::NotImplemented)
153 }
154
155 fn sync(&self, file_id: u64) -> Result<(), SyscallError> {
157 let _ = file_id;
158 Ok(()) }
160
161 fn create_file(&self, path: &str, mode: u32) -> Result<OpenResult, SyscallError> {
163 let _ = (path, mode);
164 Err(SyscallError::NotImplemented)
165 }
166
167 fn create_directory(&self, path: &str, mode: u32) -> Result<OpenResult, SyscallError> {
169 let _ = (path, mode);
170 Err(SyscallError::NotImplemented)
171 }
172
173 fn unlink(&self, path: &str) -> Result<(), SyscallError> {
175 let _ = path;
176 Err(SyscallError::NotImplemented)
177 }
178
179 fn stat(&self, file_id: u64) -> Result<FileStat, SyscallError> {
181 let _ = file_id;
182 Err(SyscallError::NotImplemented)
183 }
184
185 fn readdir(&self, file_id: u64) -> Result<Vec<DirEntry>, SyscallError> {
187 let _ = file_id;
188 Err(SyscallError::NotImplemented)
189 }
190
191 fn rename(&self, old_path: &str, new_path: &str) -> Result<(), SyscallError> {
193 let _ = (old_path, new_path);
194 Err(SyscallError::NotImplemented)
195 }
196
197 fn chmod(&self, path: &str, mode: u32) -> Result<(), SyscallError> {
199 let _ = (path, mode);
200 Err(SyscallError::NotImplemented)
201 }
202
203 fn fchmod(&self, file_id: u64, mode: u32) -> Result<(), SyscallError> {
205 let _ = (file_id, mode);
206 Err(SyscallError::NotImplemented)
207 }
208
209 fn link(&self, old_path: &str, new_path: &str) -> Result<(), SyscallError> {
211 let _ = (old_path, new_path);
212 Err(SyscallError::NotImplemented)
213 }
214
215 fn symlink(&self, target: &str, link_path: &str) -> Result<(), SyscallError> {
217 let _ = (target, link_path);
218 Err(SyscallError::NotImplemented)
219 }
220
221 fn readlink(&self, path: &str) -> Result<String, SyscallError> {
223 let _ = path;
224 Err(SyscallError::NotImplemented)
225 }
226}
227
228pub type DynScheme = Arc<dyn Scheme>;
230
231#[derive(Debug, Clone, Copy, PartialEq, Eq)]
232pub enum AsyncSubmitResult {
233 Completed(i32),
234 InFlight,
235}
236
237pub const DEV_RAMFS: u64 = 1;
238pub const DEV_SYSFS: u64 = 2;
239pub const DEV_PROCFS: u64 = 3;
240pub const DEV_DEVFS: u64 = 4;
241pub const DEV_CONSOLE: u64 = 5;
242pub const DEV_PIPEFS: u64 = 6;
243pub const DEV_IPCFS: u64 = 7;
244pub const DEV_NETFS: u64 = 8;
245pub const DEV_CHAR_FS: u64 = 9;
246pub const DEV_INPUT: u64 = 10;
247pub const DEV_THREADFS: u64 = 11;
248
249pub fn finalize_pseudo_stat(mut st: FileStat, st_dev: u64, st_rdev: u64) -> FileStat {
252 let now = strat9_abi::data::TimeSpec::from_nanos(crate::syscall::time::current_time_ns());
253 st.st_dev = st_dev;
254 st.st_rdev = st_rdev;
255 st.st_atime = now;
256 st.st_mtime = now;
257 st.st_ctime = now;
258 st
259}
260
261pub struct IpcScheme {
267 port_id: PortId,
268 open_file_flags: SpinLock<BTreeMap<u64, FileFlags>>,
269}
270
271impl IpcScheme {
272 pub fn new(port_id: PortId) -> Self {
274 IpcScheme {
275 port_id,
276 open_file_flags: SpinLock::new(BTreeMap::new()),
277 }
278 }
279
280 fn remember_open_flags(&self, file_id: u64, flags: FileFlags) {
281 self.open_file_flags.lock().insert(file_id, flags);
282 }
283
284 fn take_open_flags(&self, file_id: u64) {
285 self.open_file_flags.lock().remove(&file_id);
286 }
287
288 fn open_flags_for(&self, file_id: u64) -> FileFlags {
289 self.open_file_flags
290 .lock()
291 .get(&file_id)
292 .copied()
293 .unwrap_or_else(FileFlags::empty)
294 }
295
296 fn build_open_msg(path: &str, flags: OpenFlags) -> Result<IpcMessage, SyscallError> {
298 OpenRequest::encode(OPCODE_OPEN, flags.bits(), path).ok_or(SyscallError::InvalidArgument)
300 }
302
303 fn build_read_msg(file_id: u64, offset: u64, count: u32) -> IpcMessage {
305 ReadRequest::encode(OPCODE_READ, file_id, offset, count)
306 }
307
308 fn build_write_msg(
315 file_id: u64,
316 offset: u64,
317 data: &[u8],
318 ) -> Result<(IpcMessage, usize), SyscallError> {
319 WriteRequest::encode(OPCODE_WRITE, file_id, offset, data).ok_or(SyscallError::MessageSize)
320 }
321
322 fn build_close_msg(file_id: u64) -> IpcMessage {
324 CloseRequest::encode(OPCODE_CLOSE, file_id)
325 }
326
327 fn build_readdir_msg(file_id: u64, cursor: u16) -> IpcMessage {
329 let mut msg = IpcMessage::new(OPCODE_READDIR);
331 msg.payload[0..8].copy_from_slice(&file_id.to_le_bytes());
332 msg.payload[8..10].copy_from_slice(&cursor.to_le_bytes());
333 msg
334 }
335
336 fn parse_status(reply: &IpcMessage) -> Result<(), SyscallError> {
338 if reply.msg_type != IpcMessage::REPLY_MSG_TYPE {
339 return Err(SyscallError::IoError);
340 }
341
342 let status = get_u32(&reply.payload, 0).ok_or(SyscallError::IoError)?;
343 if status == 0 {
344 return Ok(());
345 }
346
347 let signed = status as i32;
351 let code = if signed < 0 {
352 signed as i64
353 } else {
354 -(signed as i64)
355 };
356 Err(SyscallError::from_code(code))
357 }
358}
359
360impl IpcScheme {
361 fn call(&self, mut msg: IpcMessage) -> Result<IpcMessage, SyscallError> {
366 let task_id = crate::process::current_task_id().ok_or(SyscallError::PermissionDenied)?;
367
368 msg.sender = task_id.as_u64();
370
371 let port = crate::ipc::port::get_port(self.port_id).ok_or(SyscallError::BadHandle)?;
372 let port_owner = port.owner;
373 port.send(msg).map_err(|_| SyscallError::BadHandle)?;
374 drop(port);
377
378 Ok(crate::ipc::reply::wait_for_reply(task_id, port_owner))
379 }
380}
381
382impl Scheme for IpcScheme {
383 fn open(&self, path: &str, flags: OpenFlags) -> Result<OpenResult, SyscallError> {
385 let msg = Self::build_open_msg(path, flags)?;
386 let reply = self.call(msg)?;
387
388 Self::parse_status(&reply)?;
391
392 let reply = OpenReply::parse(&reply.payload).ok_or(SyscallError::IoError)?;
393 let file_id = reply.file_id;
394 let size = reply.size;
395 let file_flags = reply.file_flags;
396 let flags = FileFlags::from_bits_truncate(file_flags);
397 self.remember_open_flags(file_id, flags);
398
399 Ok(OpenResult {
400 file_id,
401 size: if size == u64::MAX { None } else { Some(size) },
402 flags,
403 })
404 }
405
406 fn read(&self, file_id: u64, offset: u64, buf: &mut [u8]) -> Result<usize, SyscallError> {
408 let flags = self.open_flags_for(file_id);
409 let chunked = flags.contains(FileFlags::CHUNK_READ);
410 let chunk_size = if chunked {
411 IpcMessage::READ_INLINE_CAPACITY
412 } else {
413 buf.len()
414 };
415
416 let mut total = 0usize;
417 let mut current_offset = offset;
418 while total < buf.len() {
419 let request_len = core::cmp::min(buf.len() - total, chunk_size);
420 let msg = Self::build_read_msg(file_id, current_offset, request_len as u32);
421 let reply = self.call(msg)?;
422
423 Self::parse_status(&reply)?;
424
425 let count = get_u32(&reply.payload, 4).ok_or(SyscallError::IoError)? as usize;
427 let available = core::cmp::min(count, reply.payload.len() - ReadReply::DATA_OFFSET);
428 let to_copy = core::cmp::min(available, request_len);
429 buf[total..total + to_copy].copy_from_slice(
430 &reply.payload[ReadReply::DATA_OFFSET..ReadReply::DATA_OFFSET + to_copy],
431 );
432
433 total += to_copy;
434 current_offset += to_copy as u64;
435
436 if !chunked || to_copy < request_len {
437 break;
438 }
439 }
440
441 Ok(total)
442 }
443
444 fn write(&self, file_id: u64, offset: u64, buf: &[u8]) -> Result<usize, SyscallError> {
446 let flags = self.open_flags_for(file_id);
447 let chunked = flags.contains(FileFlags::CHUNK_WRITE);
448 if !chunked && buf.len() > IpcMessage::WRITE_INLINE_CAPACITY {
451 return Err(SyscallError::MessageSize);
452 }
453 let chunk_size = if chunked {
454 IpcMessage::WRITE_INLINE_CAPACITY
455 } else {
456 buf.len()
457 };
458
459 let mut total = 0usize;
460 let mut current_offset = offset;
461 while total < buf.len() {
462 let request_len = core::cmp::min(buf.len() - total, chunk_size);
463 let (msg, packed) =
464 Self::build_write_msg(file_id, current_offset, &buf[total..total + request_len])?;
465 let reply = self.call(msg)?;
466
467 Self::parse_status(&reply)?;
468
469 let bytes_written = get_u32(&reply.payload, 4).ok_or(SyscallError::IoError)? as usize;
471
472 let chunk_written = bytes_written.min(packed);
473 total += chunk_written;
474 current_offset += chunk_written as u64;
475
476 if !chunked || chunk_written < packed {
477 break;
478 }
479 }
480
481 Ok(total)
482 }
483
484 fn close(&self, file_id: u64) -> Result<(), SyscallError> {
486 let msg = Self::build_close_msg(file_id);
487 let reply = self.call(msg)?;
488 Self::parse_status(&reply)?;
489 self.take_open_flags(file_id);
490 Ok(())
491 }
492
493 fn create_file(&self, path: &str, mode: u32) -> Result<OpenResult, SyscallError> {
495 self.handle_create_op(OPCODE_CREATE_FILE, path, mode)
496 }
497
498 fn create_directory(&self, path: &str, mode: u32) -> Result<OpenResult, SyscallError> {
500 self.handle_create_op(OPCODE_CREATE_DIR, path, mode)
501 }
502
503 fn unlink(&self, path: &str) -> Result<(), SyscallError> {
505 let mut msg = IpcMessage::new(OPCODE_UNLINK);
506
507 if path.len() > IpcMessage::UNLINK_INLINE_CAPACITY {
508 return Err(SyscallError::InvalidArgument);
509 }
510
511 put_u16_len_prefixed(&mut msg.payload, 0, path.as_bytes())
513 .ok_or(SyscallError::InvalidArgument)?;
514
515 let reply = self.call(msg)?;
516 Self::parse_status(&reply)?;
517
518 Ok(())
519 }
520
521 fn readdir(&self, file_id: u64) -> Result<Vec<DirEntry>, SyscallError> {
523 const MAX_READDIR_ENTRIES: usize = 8192;
524 let mut cursor: u16 = 0;
525 let mut entries = Vec::new();
526
527 loop {
528 let msg = Self::build_readdir_msg(file_id, cursor);
529 let reply = self.call(msg)?;
530 Self::parse_status(&reply)?;
531
532 let next_cursor = u16::from_le_bytes([reply.payload[4], reply.payload[5]]);
533 let entry_count = reply.payload[6] as usize;
534 let used_bytes = reply.payload[7] as usize;
535 if used_bytes > reply.payload.len() - 8 {
536 return Err(SyscallError::IoError);
537 }
538
539 let mut offset = 8usize;
540 for _ in 0..entry_count {
541 if offset + 10 > 8 + used_bytes {
542 return Err(SyscallError::IoError);
543 }
544
545 let ino = u64::from_le_bytes([
546 reply.payload[offset],
547 reply.payload[offset + 1],
548 reply.payload[offset + 2],
549 reply.payload[offset + 3],
550 reply.payload[offset + 4],
551 reply.payload[offset + 5],
552 reply.payload[offset + 6],
553 reply.payload[offset + 7],
554 ]);
555 let file_type = reply.payload[offset + 8];
556 let name_len = reply.payload[offset + 9] as usize;
557 if offset + 10 + name_len > 8 + used_bytes {
558 return Err(SyscallError::IoError);
559 }
560 let name_bytes = &reply.payload[offset + 10..offset + 10 + name_len];
561 let name = core::str::from_utf8(name_bytes)
562 .map_err(|_| SyscallError::IoError)?
563 .to_string();
564
565 entries.push(DirEntry {
566 ino,
567 file_type,
568 name,
569 });
570 offset += 10 + name_len;
571 }
572
573 if entries.len() > MAX_READDIR_ENTRIES {
578 return Err(SyscallError::IoError);
579 }
580
581 if next_cursor == u16::MAX {
582 break;
583 }
584 if next_cursor <= cursor {
585 return Err(SyscallError::IoError);
586 }
587 cursor = next_cursor;
588 }
589
590 Ok(entries)
591 }
592}
593
594impl IpcScheme {
595 fn handle_create_op(
597 &self,
598 opcode: u32,
599 path: &str,
600 mode: u32,
601 ) -> Result<OpenResult, SyscallError> {
602 let msg = CreateRequest::encode(opcode, mode, path).ok_or(SyscallError::InvalidArgument)?;
604
605 let reply = self.call(msg)?;
606
607 Self::parse_status(&reply)?;
608
609 let file_id = {
611 let payload = &reply.payload;
612 if payload.len() < 12 {
613 return Err(SyscallError::IoError);
614 }
615 u64::from_le_bytes(
616 payload[4..12]
617 .try_into()
618 .map_err(|_| SyscallError::IoError)?,
619 )
620 };
621
622 Ok(OpenResult {
623 file_id,
624 size: Some(0),
625 flags: FileFlags::empty(),
626 })
627 }
628}
629
630pub struct KernelScheme {
635 files: SpinLock<BTreeMap<String, (u64, *const u8, usize)>>,
637 by_id: SpinLock<BTreeMap<u64, String>>,
639}
640
641unsafe impl Send for KernelScheme {}
644unsafe impl Sync for KernelScheme {}
645
646impl KernelScheme {
647 pub fn new() -> Self {
649 KernelScheme {
650 files: SpinLock::new(BTreeMap::new()),
651 by_id: SpinLock::new(BTreeMap::new()),
652 }
653 }
654
655 pub fn register(&self, path: &str, base: *const u8, len: usize) {
657 static NEXT_ID: core::sync::atomic::AtomicU64 = core::sync::atomic::AtomicU64::new(1);
658 let id = NEXT_ID.fetch_add(1, core::sync::atomic::Ordering::SeqCst);
659 self.files
660 .lock()
661 .insert(String::from(path), (id, base, len));
662 self.by_id.lock().insert(id, String::from(path));
663 }
664
665 fn get_by_id(&self, file_id: u64) -> Option<(u64, *const u8, usize)> {
667 let name = self.by_id.lock().get(&file_id)?.clone();
668 let entry = self.files.lock().get(&name).cloned()?;
669 Some(entry)
670 }
671
672 pub fn lookup_bytes(&self, path: &str) -> Option<&'static [u8]> {
674 let (_, base, len) = self.files.lock().get(path).cloned()?;
675 Some(unsafe { core::slice::from_raw_parts(base, len) })
678 }
679}
680
681impl Scheme for KernelScheme {
682 fn open(&self, path: &str, _flags: OpenFlags) -> Result<OpenResult, SyscallError> {
684 if path.is_empty() || path == "/" {
685 return Ok(OpenResult {
686 file_id: 0, size: None,
688 flags: FileFlags::DIRECTORY,
689 });
690 }
691
692 let files = self.files.lock();
693 let (id, _, len) = files.get(path).ok_or(SyscallError::BadHandle)?;
694 Ok(OpenResult {
695 file_id: *id,
696 size: Some(*len as u64),
697 flags: FileFlags::empty(),
698 })
699 }
700
701 fn read(&self, file_id: u64, offset: u64, buf: &mut [u8]) -> Result<usize, SyscallError> {
703 if file_id == 0 {
704 let mut list = String::new();
706 let files = self.files.lock();
707 for name in files.keys() {
708 list.push_str(name);
709 list.push('\n');
710 }
711
712 if offset >= list.len() as u64 {
713 return Ok(0);
714 }
715
716 let start = offset as usize;
717 let end = core::cmp::min(start + buf.len(), list.len());
718 let to_copy = end - start;
719 buf[..to_copy].copy_from_slice(&list.as_bytes()[start..end]);
720 return Ok(to_copy);
721 }
722
723 let (_, base, len) = self.get_by_id(file_id).ok_or(SyscallError::BadHandle)?;
724
725 if offset >= len as u64 {
726 return Ok(0);
727 }
728
729 let remaining = len - offset as usize;
730 let to_copy = core::cmp::min(remaining, buf.len());
731
732 unsafe {
734 let src = base.add(offset as usize);
735 core::ptr::copy_nonoverlapping(src, buf.as_mut_ptr(), to_copy);
736 }
737
738 Ok(to_copy)
739 }
740
741 fn write(&self, _file_id: u64, _offset: u64, _buf: &[u8]) -> Result<usize, SyscallError> {
743 Err(SyscallError::PermissionDenied) }
745
746 fn close(&self, _file_id: u64) -> Result<(), SyscallError> {
748 Ok(()) }
750
751 fn size(&self, file_id: u64) -> Result<u64, SyscallError> {
753 let (_, _, len) = self.get_by_id(file_id).ok_or(SyscallError::BadHandle)?;
754 Ok(len as u64)
755 }
756
757 fn stat(&self, file_id: u64) -> Result<FileStat, SyscallError> {
759 if file_id == 0 {
760 return Ok(finalize_pseudo_stat(
761 FileStat {
762 st_ino: 0,
763 st_mode: 0o040555,
764 st_nlink: 2,
765 st_size: 0,
766 st_blksize: 512,
767 st_blocks: 0,
768 ..FileStat::zeroed()
769 },
770 DEV_SYSFS,
771 0,
772 ));
773 }
774 let (_, _, len) = self.get_by_id(file_id).ok_or(SyscallError::BadHandle)?;
775 Ok(finalize_pseudo_stat(
776 FileStat {
777 st_ino: file_id,
778 st_mode: 0o100444,
779 st_nlink: 1,
780 st_size: len as u64,
781 st_blksize: 512,
782 st_blocks: ((len as u64) + 511) / 512,
783 ..FileStat::zeroed()
784 },
785 DEV_SYSFS,
786 0,
787 ))
788 }
789
790 fn readdir(&self, file_id: u64) -> Result<Vec<DirEntry>, SyscallError> {
792 if file_id != 0 {
793 return Err(SyscallError::InvalidArgument);
794 }
795 let files = self.files.lock();
796 let mut entries = Vec::new();
797 for (name, (id, _, _)) in files.iter() {
798 entries.push(DirEntry {
799 ino: *id,
800 file_type: DT_REG,
801 name: name.clone(),
802 });
803 }
804 Ok(entries)
805 }
806}