1use crate::{
7 ipc::{message::IpcMessage, port::PortId},
8 sync::SpinLock,
9 syscall::error::SyscallError,
10};
11use alloc::{
12 collections::BTreeMap,
13 string::{String, ToString},
14 sync::Arc,
15 vec::Vec,
16};
17
18pub use strat9_abi::data::{
19 FileStat, DT_BLK, DT_CHR, DT_DIR, DT_FIFO, DT_LNK, DT_REG, DT_SOCK, DT_UNKNOWN,
20};
21
22#[derive(Debug, Clone)]
24pub struct DirEntry {
25 pub ino: u64,
26 pub file_type: u8,
27 pub name: String,
28}
29
30#[derive(Debug, Clone)]
32pub struct OpenResult {
33 pub file_id: u64,
35 pub size: Option<u64>,
37 pub flags: FileFlags,
39}
40
41bitflags::bitflags! {
42 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
44 pub struct FileFlags: u32 {
45 const DIRECTORY = 1 << 0;
46 const DEVICE = 1 << 1;
47 const PIPE = 1 << 2;
48 const APPEND = 1 << 3;
49 }
50}
51
52pub use strat9_abi::flag::OpenFlags;
53
54pub trait Scheme: Send + Sync {
56 fn open(&self, path: &str, flags: OpenFlags) -> Result<OpenResult, SyscallError>;
61
62 fn read(&self, file_id: u64, offset: u64, buf: &mut [u8]) -> Result<usize, SyscallError>;
64
65 fn write(&self, file_id: u64, offset: u64, buf: &[u8]) -> Result<usize, SyscallError>;
67
68 fn close(&self, file_id: u64) -> Result<(), SyscallError>;
70
71 fn size(&self, file_id: u64) -> Result<u64, SyscallError> {
73 let _ = file_id;
74 Err(SyscallError::NotImplemented)
75 }
76
77 fn truncate(&self, file_id: u64, new_size: u64) -> Result<(), SyscallError> {
79 let _ = (file_id, new_size);
80 Err(SyscallError::NotImplemented)
81 }
82
83 fn sync(&self, file_id: u64) -> Result<(), SyscallError> {
85 let _ = file_id;
86 Ok(()) }
88
89 fn create_file(&self, path: &str, mode: u32) -> Result<OpenResult, SyscallError> {
91 let _ = (path, mode);
92 Err(SyscallError::NotImplemented)
93 }
94
95 fn create_directory(&self, path: &str, mode: u32) -> Result<OpenResult, SyscallError> {
97 let _ = (path, mode);
98 Err(SyscallError::NotImplemented)
99 }
100
101 fn unlink(&self, path: &str) -> Result<(), SyscallError> {
103 let _ = path;
104 Err(SyscallError::NotImplemented)
105 }
106
107 fn stat(&self, file_id: u64) -> Result<FileStat, SyscallError> {
109 let _ = file_id;
110 Err(SyscallError::NotImplemented)
111 }
112
113 fn readdir(&self, file_id: u64) -> Result<Vec<DirEntry>, SyscallError> {
115 let _ = file_id;
116 Err(SyscallError::NotImplemented)
117 }
118
119 fn rename(&self, old_path: &str, new_path: &str) -> Result<(), SyscallError> {
121 let _ = (old_path, new_path);
122 Err(SyscallError::NotImplemented)
123 }
124
125 fn chmod(&self, path: &str, mode: u32) -> Result<(), SyscallError> {
127 let _ = (path, mode);
128 Err(SyscallError::NotImplemented)
129 }
130
131 fn fchmod(&self, file_id: u64, mode: u32) -> Result<(), SyscallError> {
133 let _ = (file_id, mode);
134 Err(SyscallError::NotImplemented)
135 }
136
137 fn link(&self, old_path: &str, new_path: &str) -> Result<(), SyscallError> {
139 let _ = (old_path, new_path);
140 Err(SyscallError::NotImplemented)
141 }
142
143 fn symlink(&self, target: &str, link_path: &str) -> Result<(), SyscallError> {
145 let _ = (target, link_path);
146 Err(SyscallError::NotImplemented)
147 }
148
149 fn readlink(&self, path: &str) -> Result<String, SyscallError> {
151 let _ = path;
152 Err(SyscallError::NotImplemented)
153 }
154}
155
156pub type DynScheme = Arc<dyn Scheme>;
158
159pub const DEV_RAMFS: u64 = 1;
160pub const DEV_SYSFS: u64 = 2;
161pub const DEV_PROCFS: u64 = 3;
162pub const DEV_DEVFS: u64 = 4;
163pub const DEV_CONSOLE: u64 = 5;
164pub const DEV_PIPEFS: u64 = 6;
165pub const DEV_IPCFS: u64 = 7;
166pub const DEV_NETFS: u64 = 8;
167
168pub fn finalize_pseudo_stat(mut st: FileStat, st_dev: u64, st_rdev: u64) -> FileStat {
171 let now = strat9_abi::data::TimeSpec::from_nanos(crate::syscall::time::current_time_ns());
172 st.st_dev = st_dev;
173 st.st_rdev = st_rdev;
174 st.st_atime = now;
175 st.st_mtime = now;
176 st.st_ctime = now;
177 st
178}
179
180pub struct IpcScheme {
186 port_id: PortId,
187}
188
189impl IpcScheme {
190 pub fn new(port_id: PortId) -> Self {
192 IpcScheme { port_id }
193 }
194
195 fn build_open_msg(path: &str, flags: OpenFlags) -> Result<IpcMessage, SyscallError> {
197 const OPCODE_OPEN: u32 = 0x01;
198 let mut msg = IpcMessage::new(OPCODE_OPEN);
199
200 if path.len() > 42 {
202 return Err(SyscallError::InvalidArgument); }
204
205 msg.payload[0..4].copy_from_slice(&flags.bits().to_le_bytes());
206 msg.payload[4..6].copy_from_slice(&(path.len() as u16).to_le_bytes());
207 msg.payload[6..6 + path.len()].copy_from_slice(path.as_bytes());
208 Ok(msg)
209 }
210
211 fn build_read_msg(file_id: u64, offset: u64, count: u32) -> IpcMessage {
213 const OPCODE_READ: u32 = 0x02;
214 let mut msg = IpcMessage::new(OPCODE_READ);
215 msg.payload[0..8].copy_from_slice(&file_id.to_le_bytes());
216 msg.payload[8..16].copy_from_slice(&offset.to_le_bytes());
217 msg.payload[16..20].copy_from_slice(&count.to_le_bytes());
218 msg
219 }
220
221 fn build_write_msg(file_id: u64, offset: u64, data: &[u8]) -> (IpcMessage, usize) {
225 const OPCODE_WRITE: u32 = 0x03;
226 let mut msg = IpcMessage::new(OPCODE_WRITE);
227 msg.payload[0..8].copy_from_slice(&file_id.to_le_bytes());
228 msg.payload[8..16].copy_from_slice(&offset.to_le_bytes());
229
230 let packed = core::cmp::min(data.len(), 30);
232 msg.payload[16..18].copy_from_slice(&(packed as u16).to_le_bytes());
233 msg.payload[18..18 + packed].copy_from_slice(&data[..packed]);
234 (msg, packed)
235 }
236
237 fn build_close_msg(file_id: u64) -> IpcMessage {
239 const OPCODE_CLOSE: u32 = 0x04;
240 let mut msg = IpcMessage::new(OPCODE_CLOSE);
241 msg.payload[0..8].copy_from_slice(&file_id.to_le_bytes());
242 msg
243 }
244
245 fn build_readdir_msg(file_id: u64, cursor: u16) -> IpcMessage {
247 const OPCODE_READDIR: u32 = 0x08;
248 let mut msg = IpcMessage::new(OPCODE_READDIR);
249 msg.payload[0..8].copy_from_slice(&file_id.to_le_bytes());
250 msg.payload[8..10].copy_from_slice(&cursor.to_le_bytes());
251 msg
252 }
253
254 fn parse_status(reply: &IpcMessage) -> Result<(), SyscallError> {
256 if reply.msg_type != 0x80 {
257 return Err(SyscallError::IoError);
258 }
259
260 let status = u32::from_le_bytes([
261 reply.payload[0],
262 reply.payload[1],
263 reply.payload[2],
264 reply.payload[3],
265 ]);
266 if status == 0 {
267 return Ok(());
268 }
269
270 let signed = status as i32;
274 let code = if signed < 0 {
275 signed as i64
276 } else {
277 -(signed as i64)
278 };
279 Err(SyscallError::from_code(code))
280 }
281}
282
283impl IpcScheme {
284 fn call(&self, mut msg: IpcMessage) -> Result<IpcMessage, SyscallError> {
289 let task_id = crate::process::current_task_id().ok_or(SyscallError::PermissionDenied)?;
290
291 msg.sender = task_id.as_u64();
293
294 let port = crate::ipc::port::get_port(self.port_id).ok_or(SyscallError::BadHandle)?;
295 port.send(msg).map_err(|_| SyscallError::BadHandle)?;
296 drop(port);
299
300 Ok(crate::ipc::reply::wait_for_reply(task_id))
301 }
302}
303
304impl Scheme for IpcScheme {
305 fn open(&self, path: &str, flags: OpenFlags) -> Result<OpenResult, SyscallError> {
307 let msg = Self::build_open_msg(path, flags)?;
308 let reply = self.call(msg)?;
309
310 Self::parse_status(&reply)?;
312
313 let file_id = u64::from_le_bytes([
314 reply.payload[4],
315 reply.payload[5],
316 reply.payload[6],
317 reply.payload[7],
318 reply.payload[8],
319 reply.payload[9],
320 reply.payload[10],
321 reply.payload[11],
322 ]);
323
324 let size = u64::from_le_bytes([
325 reply.payload[12],
326 reply.payload[13],
327 reply.payload[14],
328 reply.payload[15],
329 reply.payload[16],
330 reply.payload[17],
331 reply.payload[18],
332 reply.payload[19],
333 ]);
334
335 let file_flags = u32::from_le_bytes([
336 reply.payload[20],
337 reply.payload[21],
338 reply.payload[22],
339 reply.payload[23],
340 ]);
341
342 Ok(OpenResult {
343 file_id,
344 size: if size == u64::MAX { None } else { Some(size) },
345 flags: FileFlags::from_bits_truncate(file_flags),
346 })
347 }
348
349 fn read(&self, file_id: u64, offset: u64, buf: &mut [u8]) -> Result<usize, SyscallError> {
351 let msg = Self::build_read_msg(file_id, offset, buf.len() as u32);
352 let reply = self.call(msg)?;
353
354 Self::parse_status(&reply)?;
356
357 let bytes_read = u32::from_le_bytes([
358 reply.payload[4],
359 reply.payload[5],
360 reply.payload[6],
361 reply.payload[7],
362 ]) as usize;
363
364 let available = core::cmp::min(bytes_read, reply.payload.len() - 8);
365 let to_copy = core::cmp::min(available, buf.len());
366 buf[..to_copy].copy_from_slice(&reply.payload[8..8 + to_copy]);
367
368 Ok(to_copy)
369 }
370
371 fn write(&self, file_id: u64, offset: u64, buf: &[u8]) -> Result<usize, SyscallError> {
373 let (msg, packed) = Self::build_write_msg(file_id, offset, buf);
374 let reply = self.call(msg)?;
375
376 Self::parse_status(&reply)?;
378
379 let bytes_written = u32::from_le_bytes([
380 reply.payload[4],
381 reply.payload[5],
382 reply.payload[6],
383 reply.payload[7],
384 ]) as usize;
385
386 Ok(bytes_written.min(packed))
388 }
389
390 fn close(&self, file_id: u64) -> Result<(), SyscallError> {
392 let msg = Self::build_close_msg(file_id);
393 let reply = self.call(msg)?;
394
395 Self::parse_status(&reply)?;
396
397 Ok(())
398 }
399
400 fn create_file(&self, path: &str, mode: u32) -> Result<OpenResult, SyscallError> {
402 const OPCODE_CREATE_FILE: u32 = 0x05;
403 self.handle_create_op(OPCODE_CREATE_FILE, path, mode)
404 }
405
406 fn create_directory(&self, path: &str, mode: u32) -> Result<OpenResult, SyscallError> {
408 const OPCODE_CREATE_DIR: u32 = 0x06;
409 self.handle_create_op(OPCODE_CREATE_DIR, path, mode)
410 }
411
412 fn unlink(&self, path: &str) -> Result<(), SyscallError> {
414 const OPCODE_UNLINK: u32 = 0x07;
415 let mut msg = IpcMessage::new(OPCODE_UNLINK);
416
417 if path.len() > 42 {
418 return Err(SyscallError::InvalidArgument);
419 }
420
421 msg.payload[0..2].copy_from_slice(&(path.len() as u16).to_le_bytes());
422 msg.payload[2..2 + path.len()].copy_from_slice(path.as_bytes());
423
424 let reply = self.call(msg)?;
425 Self::parse_status(&reply)?;
426
427 Ok(())
428 }
429
430 fn readdir(&self, file_id: u64) -> Result<Vec<DirEntry>, SyscallError> {
432 let mut cursor: u16 = 0;
433 let mut entries = Vec::new();
434
435 loop {
436 let msg = Self::build_readdir_msg(file_id, cursor);
437 let reply = self.call(msg)?;
438 Self::parse_status(&reply)?;
439
440 let next_cursor = u16::from_le_bytes([reply.payload[4], reply.payload[5]]);
441 let entry_count = reply.payload[6] as usize;
442 let used_bytes = reply.payload[7] as usize;
443 if used_bytes > reply.payload.len() - 8 {
444 return Err(SyscallError::IoError);
445 }
446
447 let mut offset = 8usize;
448 for _ in 0..entry_count {
449 if offset + 10 > 8 + used_bytes {
450 return Err(SyscallError::IoError);
451 }
452
453 let ino = u64::from_le_bytes([
454 reply.payload[offset],
455 reply.payload[offset + 1],
456 reply.payload[offset + 2],
457 reply.payload[offset + 3],
458 reply.payload[offset + 4],
459 reply.payload[offset + 5],
460 reply.payload[offset + 6],
461 reply.payload[offset + 7],
462 ]);
463 let file_type = reply.payload[offset + 8];
464 let name_len = reply.payload[offset + 9] as usize;
465 if offset + 10 + name_len > 8 + used_bytes {
466 return Err(SyscallError::IoError);
467 }
468 let name_bytes = &reply.payload[offset + 10..offset + 10 + name_len];
469 let name = core::str::from_utf8(name_bytes)
470 .map_err(|_| SyscallError::IoError)?
471 .to_string();
472
473 entries.push(DirEntry {
474 ino,
475 file_type,
476 name,
477 });
478 offset += 10 + name_len;
479 }
480
481 if next_cursor == u16::MAX {
482 break;
483 }
484 if next_cursor <= cursor {
485 return Err(SyscallError::IoError);
486 }
487 cursor = next_cursor;
488 }
489
490 Ok(entries)
491 }
492}
493
494impl IpcScheme {
495 fn handle_create_op(
497 &self,
498 opcode: u32,
499 path: &str,
500 mode: u32,
501 ) -> Result<OpenResult, SyscallError> {
502 let mut msg = IpcMessage::new(opcode);
503
504 if path.len() > 40 {
505 return Err(SyscallError::InvalidArgument);
506 }
507
508 msg.payload[0..4].copy_from_slice(&mode.to_le_bytes());
509 msg.payload[4..6].copy_from_slice(&(path.len() as u16).to_le_bytes());
510 msg.payload[6..6 + path.len()].copy_from_slice(path.as_bytes());
511
512 let reply = self.call(msg)?;
513
514 Self::parse_status(&reply)?;
515
516 let file_id = u64::from_le_bytes([
517 reply.payload[4],
518 reply.payload[5],
519 reply.payload[6],
520 reply.payload[7],
521 reply.payload[8],
522 reply.payload[9],
523 reply.payload[10],
524 reply.payload[11],
525 ]);
526
527 Ok(OpenResult {
528 file_id,
529 size: Some(0),
530 flags: FileFlags::empty(),
531 })
532 }
533}
534
535pub struct KernelScheme {
537 files: SpinLock<BTreeMap<String, KernelFile>>,
538 by_id: SpinLock<BTreeMap<u64, String>>,
539}
540
541#[derive(Clone)]
542struct KernelFile {
543 id: u64,
544 base: *const u8,
545 len: usize,
546}
547
548unsafe impl Send for KernelFile {}
550unsafe impl Sync for KernelFile {}
551
552impl KernelScheme {
553 pub fn new() -> Self {
555 KernelScheme {
556 files: SpinLock::new(BTreeMap::new()),
557 by_id: SpinLock::new(BTreeMap::new()),
558 }
559 }
560
561 pub fn register(&self, path: &str, base: *const u8, len: usize) {
563 static NEXT_ID: core::sync::atomic::AtomicU64 = core::sync::atomic::AtomicU64::new(1);
564 let id = NEXT_ID.fetch_add(1, core::sync::atomic::Ordering::SeqCst);
565 self.files
566 .lock()
567 .insert(String::from(path), KernelFile { id, base, len });
568 self.by_id.lock().insert(id, String::from(path));
569 }
570
571 fn get_by_id(&self, file_id: u64) -> Option<KernelFile> {
573 let name = self.by_id.lock().get(&file_id)?.clone();
574 self.files.lock().get(&name).cloned()
575 }
576}
577
578impl Scheme for KernelScheme {
579 fn open(&self, path: &str, _flags: OpenFlags) -> Result<OpenResult, SyscallError> {
581 if path.is_empty() || path == "/" {
582 return Ok(OpenResult {
583 file_id: 0, size: None,
585 flags: FileFlags::DIRECTORY,
586 });
587 }
588
589 let files = self.files.lock();
590 let file = files.get(path).ok_or(SyscallError::BadHandle)?;
591 Ok(OpenResult {
592 file_id: file.id,
593 size: Some(file.len as u64),
594 flags: FileFlags::empty(),
595 })
596 }
597
598 fn read(&self, file_id: u64, offset: u64, buf: &mut [u8]) -> Result<usize, SyscallError> {
600 if file_id == 0 {
601 let mut list = String::new();
603 let files = self.files.lock();
604 for name in files.keys() {
605 list.push_str(name);
606 list.push('\n');
607 }
608
609 if offset >= list.len() as u64 {
610 return Ok(0);
611 }
612
613 let start = offset as usize;
614 let end = core::cmp::min(start + buf.len(), list.len());
615 let to_copy = end - start;
616 buf[..to_copy].copy_from_slice(&list.as_bytes()[start..end]);
617 return Ok(to_copy);
618 }
619
620 let file = self.get_by_id(file_id).ok_or(SyscallError::BadHandle)?;
621
622 if offset >= file.len as u64 {
623 return Ok(0);
624 }
625
626 let remaining = file.len - offset as usize;
627 let to_copy = core::cmp::min(remaining, buf.len());
628
629 unsafe {
631 let src = file.base.add(offset as usize);
632 core::ptr::copy_nonoverlapping(src, buf.as_mut_ptr(), to_copy);
633 }
634
635 Ok(to_copy)
636 }
637
638 fn write(&self, _file_id: u64, _offset: u64, _buf: &[u8]) -> Result<usize, SyscallError> {
640 Err(SyscallError::PermissionDenied) }
642
643 fn close(&self, _file_id: u64) -> Result<(), SyscallError> {
645 Ok(()) }
647
648 fn size(&self, file_id: u64) -> Result<u64, SyscallError> {
650 let file = self.get_by_id(file_id).ok_or(SyscallError::BadHandle)?;
651 Ok(file.len as u64)
652 }
653
654 fn stat(&self, file_id: u64) -> Result<FileStat, SyscallError> {
656 if file_id == 0 {
657 return Ok(finalize_pseudo_stat(
658 FileStat {
659 st_ino: 0,
660 st_mode: 0o040555,
661 st_nlink: 2,
662 st_size: 0,
663 st_blksize: 512,
664 st_blocks: 0,
665 ..FileStat::zeroed()
666 },
667 DEV_SYSFS,
668 0,
669 ));
670 }
671 let file = self.get_by_id(file_id).ok_or(SyscallError::BadHandle)?;
672 Ok(finalize_pseudo_stat(
673 FileStat {
674 st_ino: file_id,
675 st_mode: 0o100444,
676 st_nlink: 1,
677 st_size: file.len as u64,
678 st_blksize: 512,
679 st_blocks: ((file.len as u64) + 511) / 512,
680 ..FileStat::zeroed()
681 },
682 DEV_SYSFS,
683 0,
684 ))
685 }
686
687 fn readdir(&self, file_id: u64) -> Result<Vec<DirEntry>, SyscallError> {
689 if file_id != 0 {
690 return Err(SyscallError::InvalidArgument);
691 }
692 let files = self.files.lock();
693 let mut entries = Vec::new();
694 for (name, kf) in files.iter() {
695 entries.push(DirEntry {
696 ino: kf.id,
697 file_type: DT_REG,
698 name: name.clone(),
699 });
700 }
701 Ok(entries)
702 }
703}