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 let port_owner = port.owner;
296 port.send(msg).map_err(|_| SyscallError::BadHandle)?;
297 drop(port);
300
301 Ok(crate::ipc::reply::wait_for_reply(task_id, port_owner))
302 }
303}
304
305impl Scheme for IpcScheme {
306 fn open(&self, path: &str, flags: OpenFlags) -> Result<OpenResult, SyscallError> {
308 let msg = Self::build_open_msg(path, flags)?;
309 let reply = self.call(msg)?;
310
311 Self::parse_status(&reply)?;
313
314 let file_id = u64::from_le_bytes([
315 reply.payload[4],
316 reply.payload[5],
317 reply.payload[6],
318 reply.payload[7],
319 reply.payload[8],
320 reply.payload[9],
321 reply.payload[10],
322 reply.payload[11],
323 ]);
324
325 let size = u64::from_le_bytes([
326 reply.payload[12],
327 reply.payload[13],
328 reply.payload[14],
329 reply.payload[15],
330 reply.payload[16],
331 reply.payload[17],
332 reply.payload[18],
333 reply.payload[19],
334 ]);
335
336 let file_flags = u32::from_le_bytes([
337 reply.payload[20],
338 reply.payload[21],
339 reply.payload[22],
340 reply.payload[23],
341 ]);
342
343 Ok(OpenResult {
344 file_id,
345 size: if size == u64::MAX { None } else { Some(size) },
346 flags: FileFlags::from_bits_truncate(file_flags),
347 })
348 }
349
350 fn read(&self, file_id: u64, offset: u64, buf: &mut [u8]) -> Result<usize, SyscallError> {
352 let msg = Self::build_read_msg(file_id, offset, buf.len() as u32);
353 let reply = self.call(msg)?;
354
355 Self::parse_status(&reply)?;
357
358 let bytes_read = u32::from_le_bytes([
359 reply.payload[4],
360 reply.payload[5],
361 reply.payload[6],
362 reply.payload[7],
363 ]) as usize;
364
365 let available = core::cmp::min(bytes_read, reply.payload.len() - 8);
366 let to_copy = core::cmp::min(available, buf.len());
367 buf[..to_copy].copy_from_slice(&reply.payload[8..8 + to_copy]);
368
369 Ok(to_copy)
370 }
371
372 fn write(&self, file_id: u64, offset: u64, buf: &[u8]) -> Result<usize, SyscallError> {
374 let (msg, packed) = Self::build_write_msg(file_id, offset, buf);
375 let reply = self.call(msg)?;
376
377 Self::parse_status(&reply)?;
379
380 let bytes_written = u32::from_le_bytes([
381 reply.payload[4],
382 reply.payload[5],
383 reply.payload[6],
384 reply.payload[7],
385 ]) as usize;
386
387 Ok(bytes_written.min(packed))
389 }
390
391 fn close(&self, file_id: u64) -> Result<(), SyscallError> {
393 let msg = Self::build_close_msg(file_id);
394 let reply = self.call(msg)?;
395
396 Self::parse_status(&reply)?;
397
398 Ok(())
399 }
400
401 fn create_file(&self, path: &str, mode: u32) -> Result<OpenResult, SyscallError> {
403 const OPCODE_CREATE_FILE: u32 = 0x05;
404 self.handle_create_op(OPCODE_CREATE_FILE, path, mode)
405 }
406
407 fn create_directory(&self, path: &str, mode: u32) -> Result<OpenResult, SyscallError> {
409 const OPCODE_CREATE_DIR: u32 = 0x06;
410 self.handle_create_op(OPCODE_CREATE_DIR, path, mode)
411 }
412
413 fn unlink(&self, path: &str) -> Result<(), SyscallError> {
415 const OPCODE_UNLINK: u32 = 0x07;
416 let mut msg = IpcMessage::new(OPCODE_UNLINK);
417
418 if path.len() > 42 {
419 return Err(SyscallError::InvalidArgument);
420 }
421
422 msg.payload[0..2].copy_from_slice(&(path.len() as u16).to_le_bytes());
423 msg.payload[2..2 + path.len()].copy_from_slice(path.as_bytes());
424
425 let reply = self.call(msg)?;
426 Self::parse_status(&reply)?;
427
428 Ok(())
429 }
430
431 fn readdir(&self, file_id: u64) -> Result<Vec<DirEntry>, SyscallError> {
433 let mut cursor: u16 = 0;
434 let mut entries = Vec::new();
435
436 loop {
437 let msg = Self::build_readdir_msg(file_id, cursor);
438 let reply = self.call(msg)?;
439 Self::parse_status(&reply)?;
440
441 let next_cursor = u16::from_le_bytes([reply.payload[4], reply.payload[5]]);
442 let entry_count = reply.payload[6] as usize;
443 let used_bytes = reply.payload[7] as usize;
444 if used_bytes > reply.payload.len() - 8 {
445 return Err(SyscallError::IoError);
446 }
447
448 let mut offset = 8usize;
449 for _ in 0..entry_count {
450 if offset + 10 > 8 + used_bytes {
451 return Err(SyscallError::IoError);
452 }
453
454 let ino = u64::from_le_bytes([
455 reply.payload[offset],
456 reply.payload[offset + 1],
457 reply.payload[offset + 2],
458 reply.payload[offset + 3],
459 reply.payload[offset + 4],
460 reply.payload[offset + 5],
461 reply.payload[offset + 6],
462 reply.payload[offset + 7],
463 ]);
464 let file_type = reply.payload[offset + 8];
465 let name_len = reply.payload[offset + 9] as usize;
466 if offset + 10 + name_len > 8 + used_bytes {
467 return Err(SyscallError::IoError);
468 }
469 let name_bytes = &reply.payload[offset + 10..offset + 10 + name_len];
470 let name = core::str::from_utf8(name_bytes)
471 .map_err(|_| SyscallError::IoError)?
472 .to_string();
473
474 entries.push(DirEntry {
475 ino,
476 file_type,
477 name,
478 });
479 offset += 10 + name_len;
480 }
481
482 if next_cursor == u16::MAX {
483 break;
484 }
485 if next_cursor <= cursor {
486 return Err(SyscallError::IoError);
487 }
488 cursor = next_cursor;
489 }
490
491 Ok(entries)
492 }
493}
494
495impl IpcScheme {
496 fn handle_create_op(
498 &self,
499 opcode: u32,
500 path: &str,
501 mode: u32,
502 ) -> Result<OpenResult, SyscallError> {
503 let mut msg = IpcMessage::new(opcode);
504
505 if path.len() > 40 {
506 return Err(SyscallError::InvalidArgument);
507 }
508
509 msg.payload[0..4].copy_from_slice(&mode.to_le_bytes());
510 msg.payload[4..6].copy_from_slice(&(path.len() as u16).to_le_bytes());
511 msg.payload[6..6 + path.len()].copy_from_slice(path.as_bytes());
512
513 let reply = self.call(msg)?;
514
515 Self::parse_status(&reply)?;
516
517 let file_id = u64::from_le_bytes([
518 reply.payload[4],
519 reply.payload[5],
520 reply.payload[6],
521 reply.payload[7],
522 reply.payload[8],
523 reply.payload[9],
524 reply.payload[10],
525 reply.payload[11],
526 ]);
527
528 Ok(OpenResult {
529 file_id,
530 size: Some(0),
531 flags: FileFlags::empty(),
532 })
533 }
534}
535
536pub struct KernelScheme {
538 files: SpinLock<BTreeMap<String, KernelFile>>,
539 by_id: SpinLock<BTreeMap<u64, String>>,
540}
541
542#[derive(Clone)]
543struct KernelFile {
544 id: u64,
545 base: *const u8,
546 len: usize,
547}
548
549unsafe impl Send for KernelFile {}
551unsafe impl Sync for KernelFile {}
552
553impl KernelScheme {
554 pub fn new() -> Self {
556 KernelScheme {
557 files: SpinLock::new(BTreeMap::new()),
558 by_id: SpinLock::new(BTreeMap::new()),
559 }
560 }
561
562 pub fn register(&self, path: &str, base: *const u8, len: usize) {
564 static NEXT_ID: core::sync::atomic::AtomicU64 = core::sync::atomic::AtomicU64::new(1);
565 let id = NEXT_ID.fetch_add(1, core::sync::atomic::Ordering::SeqCst);
566 self.files
567 .lock()
568 .insert(String::from(path), KernelFile { id, base, len });
569 self.by_id.lock().insert(id, String::from(path));
570 }
571
572 fn get_by_id(&self, file_id: u64) -> Option<KernelFile> {
574 let name = self.by_id.lock().get(&file_id)?.clone();
575 self.files.lock().get(&name).cloned()
576 }
577
578 pub fn lookup_bytes(&self, path: &str) -> Option<&'static [u8]> {
580 let file = self.files.lock().get(path).cloned()?;
581 Some(unsafe { core::slice::from_raw_parts(file.base, file.len) })
584 }
585}
586
587impl Scheme for KernelScheme {
588 fn open(&self, path: &str, _flags: OpenFlags) -> Result<OpenResult, SyscallError> {
590 if path.is_empty() || path == "/" {
591 return Ok(OpenResult {
592 file_id: 0, size: None,
594 flags: FileFlags::DIRECTORY,
595 });
596 }
597
598 let files = self.files.lock();
599 let file = files.get(path).ok_or(SyscallError::BadHandle)?;
600 Ok(OpenResult {
601 file_id: file.id,
602 size: Some(file.len as u64),
603 flags: FileFlags::empty(),
604 })
605 }
606
607 fn read(&self, file_id: u64, offset: u64, buf: &mut [u8]) -> Result<usize, SyscallError> {
609 if file_id == 0 {
610 let mut list = String::new();
612 let files = self.files.lock();
613 for name in files.keys() {
614 list.push_str(name);
615 list.push('\n');
616 }
617
618 if offset >= list.len() as u64 {
619 return Ok(0);
620 }
621
622 let start = offset as usize;
623 let end = core::cmp::min(start + buf.len(), list.len());
624 let to_copy = end - start;
625 buf[..to_copy].copy_from_slice(&list.as_bytes()[start..end]);
626 return Ok(to_copy);
627 }
628
629 let file = self.get_by_id(file_id).ok_or(SyscallError::BadHandle)?;
630
631 if offset >= file.len as u64 {
632 return Ok(0);
633 }
634
635 let remaining = file.len - offset as usize;
636 let to_copy = core::cmp::min(remaining, buf.len());
637
638 unsafe {
640 let src = file.base.add(offset as usize);
641 core::ptr::copy_nonoverlapping(src, buf.as_mut_ptr(), to_copy);
642 }
643
644 Ok(to_copy)
645 }
646
647 fn write(&self, _file_id: u64, _offset: u64, _buf: &[u8]) -> Result<usize, SyscallError> {
649 Err(SyscallError::PermissionDenied) }
651
652 fn close(&self, _file_id: u64) -> Result<(), SyscallError> {
654 Ok(()) }
656
657 fn size(&self, file_id: u64) -> Result<u64, SyscallError> {
659 let file = self.get_by_id(file_id).ok_or(SyscallError::BadHandle)?;
660 Ok(file.len as u64)
661 }
662
663 fn stat(&self, file_id: u64) -> Result<FileStat, SyscallError> {
665 if file_id == 0 {
666 return Ok(finalize_pseudo_stat(
667 FileStat {
668 st_ino: 0,
669 st_mode: 0o040555,
670 st_nlink: 2,
671 st_size: 0,
672 st_blksize: 512,
673 st_blocks: 0,
674 ..FileStat::zeroed()
675 },
676 DEV_SYSFS,
677 0,
678 ));
679 }
680 let file = self.get_by_id(file_id).ok_or(SyscallError::BadHandle)?;
681 Ok(finalize_pseudo_stat(
682 FileStat {
683 st_ino: file_id,
684 st_mode: 0o100444,
685 st_nlink: 1,
686 st_size: file.len as u64,
687 st_blksize: 512,
688 st_blocks: ((file.len as u64) + 511) / 512,
689 ..FileStat::zeroed()
690 },
691 DEV_SYSFS,
692 0,
693 ))
694 }
695
696 fn readdir(&self, file_id: u64) -> Result<Vec<DirEntry>, SyscallError> {
698 if file_id != 0 {
699 return Err(SyscallError::InvalidArgument);
700 }
701 let files = self.files.lock();
702 let mut entries = Vec::new();
703 for (name, kf) in files.iter() {
704 entries.push(DirEntry {
705 ino: kf.id,
706 file_type: DT_REG,
707 name: name.clone(),
708 });
709 }
710 Ok(entries)
711 }
712}