Skip to main content

strat9_kernel/vfs/
scheme.rs

1//! Scheme abstraction - backends for VFS operations.
2//!
3//! Schemes provide the actual implementation for file operations.
4//! Examples: IPC-based schemes (ext4, network), kernel schemes (devfs, procfs).
5
6use 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/// A single directory entry returned by readdir.
23#[derive(Debug, Clone)]
24pub struct DirEntry {
25    pub ino: u64,
26    pub file_type: u8,
27    pub name: String,
28}
29
30/// Result of an open operation.
31#[derive(Debug, Clone)]
32pub struct OpenResult {
33    /// Unique file handle (opaque to caller).
34    pub file_id: u64,
35    /// Size of the file (if known).
36    pub size: Option<u64>,
37    /// Flags describing the file (directory, device, etc.).
38    pub flags: FileFlags,
39}
40
41bitflags::bitflags! {
42    /// Flags describing a file's properties.
43    #[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
54/// Abstraction for a filesystem/service backend.
55pub trait Scheme: Send + Sync {
56    /// Open a file/resource at the given path within this scheme.
57    ///
58    /// `path` is relative to the scheme's mount point.
59    /// Returns a unique file handle + metadata.
60    fn open(&self, path: &str, flags: OpenFlags) -> Result<OpenResult, SyscallError>;
61
62    /// Read bytes from an open file.
63    fn read(&self, file_id: u64, offset: u64, buf: &mut [u8]) -> Result<usize, SyscallError>;
64
65    /// Write bytes to an open file.
66    fn write(&self, file_id: u64, offset: u64, buf: &[u8]) -> Result<usize, SyscallError>;
67
68    /// Close an open file.
69    fn close(&self, file_id: u64) -> Result<(), SyscallError>;
70
71    /// Get file size (if supported).
72    fn size(&self, file_id: u64) -> Result<u64, SyscallError> {
73        let _ = file_id;
74        Err(SyscallError::NotImplemented)
75    }
76
77    /// Truncate/resize a file (if supported).
78    fn truncate(&self, file_id: u64, new_size: u64) -> Result<(), SyscallError> {
79        let _ = (file_id, new_size);
80        Err(SyscallError::NotImplemented)
81    }
82
83    /// Sync file to storage (if applicable).
84    fn sync(&self, file_id: u64) -> Result<(), SyscallError> {
85        let _ = file_id;
86        Ok(()) // No-op by default
87    }
88
89    /// Create a new regular file.
90    fn create_file(&self, path: &str, mode: u32) -> Result<OpenResult, SyscallError> {
91        let _ = (path, mode);
92        Err(SyscallError::NotImplemented)
93    }
94
95    /// Create a new directory.
96    fn create_directory(&self, path: &str, mode: u32) -> Result<OpenResult, SyscallError> {
97        let _ = (path, mode);
98        Err(SyscallError::NotImplemented)
99    }
100
101    /// Remove a file or directory.
102    fn unlink(&self, path: &str) -> Result<(), SyscallError> {
103        let _ = path;
104        Err(SyscallError::NotImplemented)
105    }
106
107    /// Get metadata for an open file.
108    fn stat(&self, file_id: u64) -> Result<FileStat, SyscallError> {
109        let _ = file_id;
110        Err(SyscallError::NotImplemented)
111    }
112
113    /// Read directory entries from an open directory handle.
114    fn readdir(&self, file_id: u64) -> Result<Vec<DirEntry>, SyscallError> {
115        let _ = file_id;
116        Err(SyscallError::NotImplemented)
117    }
118
119    /// Rename/move an entry within this scheme.
120    fn rename(&self, old_path: &str, new_path: &str) -> Result<(), SyscallError> {
121        let _ = (old_path, new_path);
122        Err(SyscallError::NotImplemented)
123    }
124
125    /// Change permission bits on a path.
126    fn chmod(&self, path: &str, mode: u32) -> Result<(), SyscallError> {
127        let _ = (path, mode);
128        Err(SyscallError::NotImplemented)
129    }
130
131    /// Change permission bits on an open file handle.
132    fn fchmod(&self, file_id: u64, mode: u32) -> Result<(), SyscallError> {
133        let _ = (file_id, mode);
134        Err(SyscallError::NotImplemented)
135    }
136
137    /// Create a hard link.
138    fn link(&self, old_path: &str, new_path: &str) -> Result<(), SyscallError> {
139        let _ = (old_path, new_path);
140        Err(SyscallError::NotImplemented)
141    }
142
143    /// Create a symbolic link.
144    fn symlink(&self, target: &str, link_path: &str) -> Result<(), SyscallError> {
145        let _ = (target, link_path);
146        Err(SyscallError::NotImplemented)
147    }
148
149    /// Read the target of a symbolic link.
150    fn readlink(&self, path: &str) -> Result<String, SyscallError> {
151        let _ = path;
152        Err(SyscallError::NotImplemented)
153    }
154}
155
156/// Type-erased Scheme reference.
157pub 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
168/// Finalize pseudo-filesystem stats with a stable device identity and
169/// synthetic timestamps.
170pub 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
180// ============================================================================
181// Built-in Schemes
182// ============================================================================
183
184/// IPC-based scheme: forwards operations to a userspace server via IPC.
185pub struct IpcScheme {
186    port_id: PortId,
187}
188
189impl IpcScheme {
190    /// Creates a new instance.
191    pub fn new(port_id: PortId) -> Self {
192        IpcScheme { port_id }
193    }
194
195    /// Build an IPC message for open operation.
196    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        // Encode: [flags: u32][path_len: u16][path bytes...]
201        if path.len() > 42 {
202            return Err(SyscallError::InvalidArgument); // Path too long for inline
203        }
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    /// Build an IPC message for read operation.
212    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    /// Build an IPC message for write operation.
222    ///
223    /// Returns the message and the number of bytes actually packed.
224    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        // payload[18..48] leaves 30 bytes for data.
231        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    /// Build an IPC message for close operation.
238    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    /// Performs the build readdir msg operation.
246    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    /// Parses status.
255    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        // Accept both forms:
271        // - positive errno (2 => ENOENT)
272        // - raw signed -errno encoded in u32
273        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    /// Perform a synchronous IPC call: send `msg` to the server port and block
285    /// the current task until the server calls `ipc_reply`.  This mirrors
286    /// `sys_ipc_call` exactly so that `sys_ipc_reply` can correctly route the
287    /// reply back to us via `reply::deliver_reply`.
288    fn call(&self, mut msg: IpcMessage) -> Result<IpcMessage, SyscallError> {
289        let task_id = crate::process::current_task_id().ok_or(SyscallError::PermissionDenied)?;
290
291        // Stamp our task-id so the server knows where to deliver the reply.
292        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 the Arc before blocking so we don't hold the port alive across
298        // a potentially long sleep.
299        drop(port);
300
301        Ok(crate::ipc::reply::wait_for_reply(task_id, port_owner))
302    }
303}
304
305impl Scheme for IpcScheme {
306    /// Performs the open operation.
307    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        // Parse reply: [status: u32][file_id: u64][size: u64][flags: u32]
312        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    /// Performs the read operation.
351    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        // Parse reply: [status: u32][bytes_read: u32][data...]
356        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    /// Performs the write operation.
373    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        // Parse reply: [status: u32][bytes_written: u32]
378        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        // Never report more bytes than we actually sent.
388        Ok(bytes_written.min(packed))
389    }
390
391    /// Performs the close operation.
392    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    /// Creates file.
402    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    /// Creates directory.
408    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    /// Performs the unlink operation.
414    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    /// Performs the readdir operation.
432    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    /// Handles create op.
497    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
536/// Kernel-backed scheme: serves files from kernel memory (read-only).
537pub 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
549// SAFETY: KernelFile only stores kernel-static pointers
550unsafe impl Send for KernelFile {}
551unsafe impl Sync for KernelFile {}
552
553impl KernelScheme {
554    /// Creates a new instance.
555    pub fn new() -> Self {
556        KernelScheme {
557            files: SpinLock::new(BTreeMap::new()),
558            by_id: SpinLock::new(BTreeMap::new()),
559        }
560    }
561
562    /// Register a static kernel file.
563    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    /// Returns by id.
573    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    /// Returns the bytes of a registered static kernel file.
579    pub fn lookup_bytes(&self, path: &str) -> Option<&'static [u8]> {
580        let file = self.files.lock().get(path).cloned()?;
581        // SAFETY: initfs files are bootloader-provided mappings kept alive for
582        // the full kernel lifetime.
583        Some(unsafe { core::slice::from_raw_parts(file.base, file.len) })
584    }
585}
586
587impl Scheme for KernelScheme {
588    /// Performs the open operation.
589    fn open(&self, path: &str, _flags: OpenFlags) -> Result<OpenResult, SyscallError> {
590        if path.is_empty() || path == "/" {
591            return Ok(OpenResult {
592                file_id: 0, // Root directory ID
593                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    /// Performs the read operation.
608    fn read(&self, file_id: u64, offset: u64, buf: &mut [u8]) -> Result<usize, SyscallError> {
609        if file_id == 0 {
610            // Handle directory listing for root
611            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        // SAFETY: file.base is a kernel-static pointer, bounds checked above
639        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    /// Performs the write operation.
648    fn write(&self, _file_id: u64, _offset: u64, _buf: &[u8]) -> Result<usize, SyscallError> {
649        Err(SyscallError::PermissionDenied) // Read-only
650    }
651
652    /// Performs the close operation.
653    fn close(&self, _file_id: u64) -> Result<(), SyscallError> {
654        Ok(()) // No-op for kernel files
655    }
656
657    /// Performs the size operation.
658    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    /// Performs the stat operation.
664    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    /// Performs the readdir operation.
697    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}