Repository: petitstrawberry/Scarlet
Pull request #64: Feature/#13 file operations
Author: login: petitstrawberry
Created: 2025-05-20T08:06:20Z | Merged: 2025-05-24T07:40:53Z

<!-- GitHub Copilot コードレビューへの指示: このプルリクエストをレビューしてコメントする際には、英語の本文を全て述べた後に、日本語でも併記するようにして下さい -->


Diff:
diff --git a/kernel/src/fs/drivers/cpio/mod.rs b/kernel/src/fs/drivers/cpio/mod.rs
index 6ceb0b066..151fcb2bd 100644
--- a/kernel/src/fs/drivers/cpio/mod.rs
+++ b/kernel/src/fs/drivers/cpio/mod.rs
@@ -33,8 +33,8 @@
 //! let fs = cpio_driver.create_from_memory(&initramfs_memory_area)?;
 //! vfs_manager.register_filesystem(fs)?;
 //! ```
-use alloc::{boxed::Box, format, string::{String, ToString}, vec::Vec};
-use spin::Mutex;
+use alloc::{boxed::Box, format, string::{String, ToString}, sync::Arc, vec::Vec};
+use spin::{Mutex, RwLock};
 
 use crate::{driver_initcall, fs::{
     get_vfs_manager, Directory, DirectoryEntry, FileHandle, FileMetadata, FileOperations, FileSystem, FileSystemDriver, FileSystemError, FileSystemErrorKind, FileSystemType, FileType, Result, VirtualFileSystem
@@ -254,13 +254,13 @@ impl FileSystem for Cpiofs {
 }
 
 impl FileOperations for Cpiofs {
-    fn open(&self, path: &str, _flags: u32) -> Result<Box<dyn crate::fs::FileHandle>> {
+    fn open(&self, path: &str, _flags: u32) -> Result<Arc<dyn crate::fs::FileHandle>> {
         let path = self.normalize_path(path);
         let entries = self.entries.lock();
         if let Some(entry) = entries.iter().find(|e| e.name == path) {
-            Ok(Box::new(CpiofsFileHandle {
-                content: entry.data.clone().unwrap_or_default(),
-                position: 0,
+            Ok(Arc::new(CpiofsFileHandle {
+                content: RwLock::new(entry.data.clone().unwrap_or_default()),
+                position: RwLock::new(0),
             }))
         } else {
             Err(FileSystemError {
@@ -352,45 +352,49 @@ impl FileOperations for Cpiofs {
     }
     
     fn root_dir(&self) -> Result<crate::fs::Directory> {
-        Ok(Directory::new(self.mount_point.clone() + "/"))
+        Ok(Directory::open(self.mount_point.clone() + "/"))
     }
 }
 
 struct CpiofsFileHandle {
-    content: Vec<u8>,
-    position: usize,
+    content: RwLock<Vec<u8>>,
+    position: RwLock<usize>,
 }
 
 impl FileHandle for CpiofsFileHandle {
-    fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
-        let available = self.content.len() - self.position;
+    fn read(&self, buffer: &mut [u8]) -> Result<usize> {
+        let content = self.content.read();
+        let mut position = self.position.write();
+        let available = content.len() - *position;
         let to_read = buffer.len().min(available);
-        buffer[..to_read].copy_from_slice(&self.content[self.position..self.position + to_read]);
-        self.position += to_read;
+        buffer[..to_read].copy_from_slice(&content[*position..*position + to_read]);
+        *position += to_read;
         Ok(to_read)
     }
 
-    fn write(&mut self, _buffer: &[u8]) -> Result<usize> {
+    fn write(&self, _buffer: &[u8]) -> Result<usize> {
         Err(FileSystemError {
             kind: FileSystemErrorKind::ReadOnly,
             message: "Initramfs is read-only".to_string(),
         })
     }
 
-    fn seek(&mut self, whence: crate::fs::SeekFrom) -> Result<u64> {
+    fn seek(&self, whence: crate::fs::SeekFrom) -> Result<u64> {
+        let mut position = self.position.write();
+        let content = self.content.read();
         let new_pos = match whence {
             crate::fs::SeekFrom::Start(offset) => offset as usize,
             crate::fs::SeekFrom::Current(offset) => {
-                if offset < 0 && self.position < offset.abs() as usize {
+                if offset < 0 && *position < offset.abs() as usize {
                     0
                 } else if offset < 0 {
-                    self.position - offset.abs() as usize
+                    *position - offset.abs() as usize
                 } else {
-                    self.position + offset as usize
+                    *position + offset as usize
                 }
             },
             crate::fs::SeekFrom::End(offset) => {
-                let end = self.content.len();
+                let end = content.len();
                 if offset < 0 && end < offset.abs() as usize {
                     0
                 } else if offset < 0 {
@@ -401,18 +405,19 @@ impl FileHandle for CpiofsFileHandle {
             },
         };
         
-        self.position = new_pos;
-        Ok(self.position as u64)
+        *position = new_pos;
+        Ok(*position as u64)
     }
 
-    fn close(&mut self) -> Result<()> {
+    fn release(&self) -> Result<()> {
         Ok(())
     }
 
     fn metadata(&self) -> Result<FileMetadata> {
+        let content = self.content.read();
         Ok(FileMetadata {
             file_type: FileType::RegularFile,
-            size: self.content.len(),
+            size: content.len(),
             permissions: crate::fs::FilePermission {
                 read: true,
                 write: false,
diff --git a/kernel/src/fs/helper.rs b/kernel/src/fs/helper.rs
new file mode 100644
index 000000000..b2e7d80e0
--- /dev/null
+++ b/kernel/src/fs/helper.rs
@@ -0,0 +1,31 @@
+use crate::alloc::string::ToString;
+use alloc::{string::String, vec::Vec};
+
+use super::MAX_PATH_LENGTH;
+
+pub fn get_path_str(path_ptr: *const u8) -> Result<String, &'static str> {
+    // Parse path as a null-terminated C string
+    let mut path_bytes = Vec::new();
+    let mut i = 0;
+    unsafe {
+        loop {
+            let byte = *path_ptr.add(i);
+            if byte == 0 {
+                break;
+            }
+            path_bytes.push(byte);
+            i += 1;
+
+            if i > MAX_PATH_LENGTH {
+                return Err("Path too long");
+            }
+        }
+    }
+
+    // Convert path bytes to string
+    let path_str = match str::from_utf8(&path_bytes) {
+        Ok(s) => s,
+        Err(_) => return Err("Invalid UTF-8"),
+    };
+    Ok(path_str.to_string())
+}
\ No newline at end of file
diff --git a/kernel/src/fs/mod.rs b/kernel/src/fs/mod.rs
index 1cf237083..238d0a30d 100644
--- a/kernel/src/fs/mod.rs
+++ b/kernel/src/fs/mod.rs
@@ -1,9 +1,11 @@
 pub mod drivers;
+pub mod syscall;
+pub mod helper;
 
 use alloc::{boxed::Box, collections::BTreeMap, format, string::{String, ToString}, sync::Arc, vec::Vec};
 use alloc::vec;
 use core::fmt;
-use crate::device::block::{request::{BlockIORequest, BlockIORequestType}, BlockDevice};
+use crate::{device::block::{request::{BlockIORequest, BlockIORequestType}, BlockDevice}, task::Task};
 
 use spin::{Mutex, RwLock};
 
@@ -30,6 +32,7 @@ pub enum FileSystemErrorKind {
     PermissionDenied,
     IoError,
     InvalidData,
+    InvalidPath,
     AlreadyExists,
     NotADirectory,
     NotAFile,
@@ -83,140 +86,83 @@ pub struct FileMetadata {
     pub accessed_time: u64,
 }
 
-pub struct File<'a> {
-    pub path: String,
-    handle: Option<Box<dyn FileHandle>>,
-    is_open: bool,
-    manager_ref: ManagerRef<'a>,
+#[derive(Clone)]
+pub struct File {
+    // pub path: String,
+    handle: Arc<dyn FileHandle>,
 }
-impl<'a> File<'a> {
-    //// Create a new file object (use the global manager by default)
-    pub fn new(path: String) -> Self {
-        Self {
-            path,
-            handle: None,
-            is_open: false,
-            manager_ref: ManagerRef::Global,
-        }
-    }
-    
-    /// Create a file object that uses a specific manager
-    pub fn with_manager(path: String, manager: &'a mut VfsManager) -> Self {
-        Self {
-            path,
-            handle: None,
-            is_open: false,
-            manager_ref: ManagerRef::Local(manager),
-        }
-    }
-    
-    // Internal method to get the manager to use
-    fn get_manager(&self) -> &VfsManager {
-        match &self.manager_ref {
-            ManagerRef::Global => get_vfs_manager(),
-            ManagerRef::Local(manager) => manager,
-        }
-    }
-    
-    /// Open the file
-    pub fn open(&mut self, flags: u32) -> Result<()> {
-        if self.is_open {
-            return Ok(());
-        }
-        
-        let handle = self.get_manager().open(&self.path, flags)?;
-        self.handle = Some(handle);
-        self.is_open = true;
-        Ok(())
+impl File {
+    //// Open a file using the global VFS manager
+    /// 
+    /// # Arguments
+    /// 
+    /// * `path` - The path to the file
+    /// 
+    /// # Returns
+    ///
+    /// * `Result<File>` - The opened file object
+    /// 
+    pub fn open(path: String) -> Result<Self>{
+        let handle = get_vfs_manager().open(&path, 0)?;
+        Ok(Self {
+            handle,
+        })
     }
     
-    /// Close the file
-    pub fn close(&mut self) -> Result<()> {
-        if !self.is_open {
-            return Ok(());
-        }
-        
-        if let Some(mut handle) = self.handle.take() {
-            let result = handle.close();
-            self.is_open = false;
-            result
-        } else {
-            Ok(())
-        }
+    /// Open a file using a specific VFS manager
+    /// 
+    /// # Arguments
+    /// 
+    /// * `path` - The path to the file
+    /// * `manager` - The VFS manager to use
+    /// 
+    /// # Returns
+    /// 
+    /// * `Result<File>` - The opened file object
+    /// 
+    pub fn open_with_manager(path: String, manager: &VfsManager) -> Result<Self> {
+        let handle = manager.open(&path, 0)?;
+        Ok(Self {
+            handle,
+        })
     }
-    
+
     /// Read data from the file
+    /// 
+    /// # Arguments
+    /// 
+    /// * `buffer` - The buffer to read data into
+    /// 
+    /// # Returns
+    /// 
+    /// * `Result<usize>` - The number of bytes read
+    /// 
     pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
-        if !self.is_open {
-            return Err(FileSystemError {
-                kind: FileSystemErrorKind::IoError,
-                message: "File not open".to_string(),
-            });
-        }
-        
-        if let Some(handle) = self.handle.as_mut() {
-            handle.read(buffer)
-        } else {
-            Err(FileSystemError {
-                kind: FileSystemErrorKind::IoError,
-                message: "Invalid file handle".to_string(),
-            })
-        }
+        self.handle.read(buffer)
     }
     
     /// Write data to the file
-    pub fn write(&mut self, buffer: &[u8]) -> Result<usize> {
-        if !self.is_open {
-            return Err(FileSystemError {
-                kind: FileSystemErrorKind::IoError,
-                message: "File not open".to_string(),
-            });
-        }
-        
-        if let Some(handle) = self.handle.as_mut() {
-            handle.write(buffer)
-        } else {
-            Err(FileSystemError {
-                kind: FileSystemErrorKind::IoError,
-                message: "Invalid file handle".to_string(),
-            })
-        }
+    /// 
+    /// # Arguments
+    /// 
+    /// * `buffer` - The buffer containing data to write
+    /// 
+    /// # Returns
+    /// 
+    /// * `Result<usize>` - The number of bytes written
+    /// 
+    pub fn write(&mut self, buffer: &[u8]) -> Result<usize> { 
+        self.handle.write(buffer)
     }
     
     /// Change the position within the file
     pub fn seek(&mut self, whence: SeekFrom) -> Result<u64> {
-        if !self.is_open {
-            return Err(FileSystemError {
-                kind: FileSystemErrorKind::IoError,
-                message: "File not open".to_string(),
-            });
-        }
-        
-        if let Some(handle) = self.handle.as_mut() {
-            handle.seek(whence)
-        } else {
-            Err(FileSystemError {
-                kind: FileSystemErrorKind::IoError,
-                message: "Invalid file handle".to_string(),
-            })
-        }
+        self.handle.seek(whence)
     }
     
     /// Get the metadata of the file
     pub fn metadata(&self) -> Result<FileMetadata> {
-        if !self.is_open {
-            // Metadata can be obtained even if the file is not open
-            return self.get_manager().metadata(&self.path);
-        }
-        
-        if let Some(handle) = self.handle.as_ref() {
-            handle.metadata()
-        } else {
-            Err(FileSystemError {
-                kind: FileSystemErrorKind::IoError,
-                message: "Invalid file handle".to_string(),
-            })
-        }
+        self.handle.metadata()
     }
     
     /// Get the size of the file
@@ -239,19 +185,11 @@ impl<'a> File<'a> {
         
         Ok(buffer)
     }
-    
-    /// Check if the file is open
-    pub fn is_open(&self) -> bool {
-        self.is_open
-    }
 }
 
-impl<'a> Drop for File<'a> {
+impl Drop for File {
     fn drop(&mut self) {
-        // Automatically close the file when dropped
-        if self.is_open {
-            let _ = self.close();
-        }
+        self.handle.release().unwrap();
     }
 }
 /// Structure representing a directory entry
@@ -270,14 +208,14 @@ pub struct Directory<'a> {
 }
 
 impl<'a> Directory<'a> {
-    pub fn new(path: String) -> Self {
+    pub fn open(path: String) -> Self {
         Self {
             path,
             manager_ref: ManagerRef::Global,
         }
     }
     
-    pub fn with_manager(path: String, manager: &'a mut VfsManager) -> Self {
+    pub fn open_with_manager(path: String, manager: &'a mut VfsManager) -> Self {
         Self {
             path,
             manager_ref: ManagerRef::Local(manager),
@@ -324,16 +262,16 @@ pub enum SeekFrom {
 /// Trait for file handlers
 pub trait FileHandle: Send + Sync {
     /// Read from the file
-    fn read(&mut self, buffer: &mut [u8]) -> Result<usize>;
+    fn read(&self, buffer: &mut [u8]) -> Result<usize>;
     
     /// Write to the file
-    fn write(&mut self, buffer: &[u8]) -> Result<usize>;
+    fn write(&self, buffer: &[u8]) -> Result<usize>;
     
     /// Move the position within the file
-    fn seek(&mut self, whence: SeekFrom) -> Result<u64>;
+    fn seek(&self, whence: SeekFrom) -> Result<u64>;
     
-    /// Close the file
-    fn close(&mut self) -> Result<()>;
+    /// Release the file resource
+    fn release(&self) -> Result<()>;
     
     /// Get the metadata
     fn metadata(&self) -> Result<FileMetadata>;
@@ -369,7 +307,7 @@ pub trait FileSystem: Send + Sync {
 /// Trait defining file operations
 pub trait FileOperations: Send + Sync {
     /// Open a file
-    fn open(&self, path: &str, flags: u32) -> Result<Box<dyn FileHandle>>;
+    fn open(&self, path: &str, flags: u32) -> Result<Arc<dyn FileHandle>>;
     
     /// Read directory entries
     fn read_dir(&self, path: &str) -> Result<Vec<DirectoryEntry>>;
@@ -771,7 +709,7 @@ impl VfsManager {
     ///
     /// # Arguments
     /// 
-    /// * `path` - The path to resolve
+    /// * `path` - The path to resolve (must be absolute)
     /// 
     /// # Returns
     /// 
@@ -782,6 +720,13 @@ impl VfsManager {
     /// * `FileSystemError` - If no file system is mounted for the specified path
     /// 
     fn resolve_path(&self, path: &str) -> Result<(FileSystemRef, String)> {
+        // Check if the path is absolute
+        if !path.starts_with('/') {
+            return Err(FileSystemError {
+                kind: FileSystemErrorKind::InvalidPath,
+                message: format!("Path must be absolute: {}", path),
+            });
+        }
         let path = Self::normalize_path(path);
         let mut best_match = "";
         let mount_points = self.mount_points.read();
@@ -823,9 +768,39 @@ impl VfsManager {
         Ok((fs, relative_path))
     }
 
+    /// Get absolute path from relative path and current working directory
+    /// 
+    /// # Arguments
+    /// 
+    /// * `task` - The task containing the current working directory
+    /// * `path` - The relative path to convert
+    /// 
+    pub fn to_absolute_path(task: &Task, path: &str) -> Result<String> {
+        if path.starts_with('/') {
+            // If the path is already absolute, return it as is
+            Ok(path.to_string())
+        } else {
+            let cwd = task.cwd.clone();
+            if cwd.is_none() {
+                return Err(FileSystemError {
+                    kind: FileSystemErrorKind::InvalidPath,
+                    message: "Current working directory is not set".to_string(),
+                });
+            }
+            // Combine the current working directory and the relative path to create an absolute path
+            let mut absolute_path = cwd.unwrap();
+            if !absolute_path.ends_with('/') {
+                absolute_path.push('/');
+            }
+            absolute_path.push_str(path);
+            // Normalize and return the result
+            Ok(Self::normalize_path(&absolute_path))
+        }
+    }
+
     
     // Open a file
-    pub fn open(&self, path: &str, flags: u32) -> Result<Box<dyn FileHandle>> {
+    pub fn open(&self, path: &str, flags: u32) -> Result<Arc<dyn FileHandle>> {
         self.with_resolve_path(path, |fs, relative_path| fs.read().open(relative_path, flags))
     }
     
diff --git a/kernel/src/fs/syscall.rs b/kernel/src/fs/syscall.rs
new file mode 100644
index 000000000..82bc98854
--- /dev/null
+++ b/kernel/src/fs/syscall.rs
@@ -0,0 +1,152 @@
+use alloc::{string::ToString, vec::Vec};
+
+use crate::{arch::Trapframe, task::mytask};
+
+use super::{File, SeekFrom, VfsManager, MAX_PATH_LENGTH};
+
+pub fn sys_open(trapframe: &mut Trapframe) -> usize {
+    let task = mytask().unwrap();
+    let path_ptr = task.vm_manager.translate_vaddr(trapframe.get_arg(0)).unwrap() as *const u8;
+    let _flags = trapframe.get_arg(1) as i32;
+    let _mode = trapframe.get_arg(2) as i32;
+
+    // Increment PC to avoid infinite loop if open fails
+    trapframe.epc += 4;
+
+    // Parse path as a null-terminated C string
+    let mut path_bytes = Vec::new();
+    let mut i = 0;
+    unsafe {
+        loop {
+            let byte = *path_ptr.add(i);
+            if byte == 0 {
+                break;
+            }
+            path_bytes.push(byte);
+            i += 1;
+
+            if i > MAX_PATH_LENGTH {
+                return usize::MAX; // Path too long
+            }
+        }
+    }
+
+    // Convert path bytes to string
+    let path_str = match str::from_utf8(&path_bytes) {
+        Ok(s) => VfsManager::to_absolute_path(&task, s).unwrap(),
+        Err(_) => return usize::MAX, // Invalid UTF-8
+    };
+
+    // Try to open the file
+    let file = File::open(path_str);
+    match file {
+        Ok(file) => {
+            // Register the file with the task
+            let fd = task.add_file(file);
+            if fd.is_err() {
+                return usize::MAX; // File descriptor error
+            }
+            fd.unwrap() as usize
+        }
+        Err(_) => usize::MAX, // File open error
+    }
+}
+
+pub fn sys_close(trapframe: &mut Trapframe) -> usize {
+    let task = mytask().unwrap();
+    let fd = trapframe.get_arg(0) as usize;
+    trapframe.epc += 4;
+    if task.remove_file(fd).is_ok() {
+        0
+    } else {
+        usize::MAX // -1
+    }
+}
+
+pub fn sys_read(trapframe: &mut Trapframe) -> usize {
+    let task = mytask().unwrap();
+    let fd = trapframe.get_arg(0) as usize;
+    let buf_ptr = task.vm_manager.translate_vaddr(trapframe.get_arg(1)).unwrap() as *mut u8;
+    let count = trapframe.get_arg(2) as usize;
+
+    // Increment PC to avoid infinite loop if read fails
+    trapframe.epc += 4;
+
+    let file = task.get_mut_file(fd);
+    if file.is_none() {
+        return usize::MAX; // Invalid file descriptor
+    }
+
+    let file = file.unwrap();
+
+    let buffer = unsafe { core::slice::from_raw_parts_mut(buf_ptr, count) };
+    
+    match file.read(buffer) {
+        Ok(n) => {
+            n
+        }
+        Err(_) => {
+            return usize::MAX; // Read error
+        }
+    }
+}
+
+pub fn sys_write(trapframe: &mut Trapframe) -> usize {
+    let task = mytask().unwrap();
+    let fd = trapframe.get_arg(0) as usize;
+    let buf_ptr = task.vm_manager.translate_vaddr(trapframe.get_arg(1)).unwrap() as *const u8;
+    let count = trapframe.get_arg(2) as usize;
+
+    // Increment PC to avoid infinite loop if write fails
+    trapframe.epc += 4;
+
+    let file = task.get_mut_file(fd);
+    if file.is_none() {
+        return usize::MAX; // Invalid file descriptor
+    }
+
+    let file = file.unwrap();
+
+    let buffer = unsafe { core::slice::from_raw_parts(buf_ptr, count) };
+    
+    match file.write(buffer) {
+        Ok(n) => {
+            n
+        }
+        Err(_) => {
+            return usize::MAX; // Write error
+        }
+    }
+}
+
+pub fn sys_lseek(trapframe: &mut Trapframe) -> usize {
+    let task = mytask().unwrap();
+    let fd = trapframe.get_arg(0) as usize;
+    let offset = trapframe.get_arg(1) as i64;
+    let whence = trapframe.get_arg(2) as i32;
+
+    // Increment PC to avoid infinite loop if lseek fails
+    trapframe.epc += 4;
+
+    let file = task.get_mut_file(fd);
+    if file.is_none() {
+        return usize::MAX; // Invalid file descriptor
+    }
+
+    let file = file.unwrap();
+    let whence  = match whence {
+        0 => SeekFrom::Start(offset as u64),
+        1 => SeekFrom::Current(offset),
+        2 => SeekFrom::End(offset),
+        _ => return usize::MAX, // Invalid whence
+    };
+
+    match file.seek(whence) {
+        Ok(pos) => {
+            pos as usize
+        }
+        Err(_) => {
+            return usize::MAX; // Lseek error
+        }
+    }
+}
\ No newline at end of file
diff --git a/kernel/src/fs/testfs.rs b/kernel/src/fs/testfs.rs
index a20d7c514..7360c1e91 100644
--- a/kernel/src/fs/testfs.rs
+++ b/kernel/src/fs/testfs.rs
@@ -2,6 +2,7 @@ use alloc::boxed::Box;
 use alloc::string::String;
 use alloc::vec;
 use alloc::vec::Vec;
+use spin::rwlock::RwLock;
 use spin::Mutex;
 
 use super::*;
@@ -193,73 +194,77 @@ impl FileSystem for TestFileSystem {
 // File handle for testing
 struct TestFileHandle {
     path: String,
-    position: u64,
-    content: Vec<u8>,
+    position: RwLock<u64>,
+    content: RwLock<Vec<u8>>,
 }
 
 impl FileHandle for TestFileHandle {
-    fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
-        if self.position as usize >= self.content.len() {
+    fn read(&self, buffer: &mut [u8]) -> Result<usize> {
+        let mut position = self.position.write();
+        let content = self.content.read();
+        if *position as usize >= content.len() {
             return Ok(0); // EOF
         }
         
-        let available = self.content.len() - self.position as usize;
+        let available = content.len() - *position as usize;
         let to_read = buffer.len().min(available);
         
-        buffer[..to_read].copy_from_slice(&self.content[self.position as usize..self.position as usize + to_read]);
-        self.position += to_read as u64;
+        buffer[..to_read].copy_from_slice(&content[*position as usize..*position as usize + to_read]);
+        *position += to_read as u64;
         
         Ok(to_read)
     }
     
-    fn write(&mut self, buffer: &[u8]) -> Result<usize> {
-        let pos = self.position as usize;
+    fn write(&self, buffer: &[u8]) -> Result<usize> {
+        let mut position = self.position.write();
+        let mut content = self.content.write();
         
         // Expand file size if necessary
-        if pos + buffer.len() > self.content.len() {
-            self.content.resize(pos + buffer.len(), 0);
+        if *position as usize + buffer.len() > content.len() {
+            content.resize(*position as usize + buffer.len(), 0);
         }
         
         // Write data
-        self.content[pos..pos + buffer.len()].copy_from_slice(buffer);
-        self.position += buffer.len() as u64;
+        content[*position as usize..*position as usize + buffer.len()].copy_from_slice(buffer);
+        *position += buffer.len() as u64;
         
         Ok(buffer.len())
     }
     
-    fn seek(&mut self, whence: SeekFrom) -> Result<u64> {
+    fn seek(&self, whence: SeekFrom) -> Result<u64> {
+        let mut position = self.position.write();
         match whence {
             SeekFrom::Start(offset) => {
-                self.position = offset;
+                *position = offset;
             },
             SeekFrom::Current(offset) => {
                 if offset >= 0 {
-                    self.position = self.position.saturating_add(offset as u64);
+                    *position = position.saturating_add(offset as u64);
                 } else {
-                    self.position = self.position.saturating_sub((-offset) as u64);
+                    *position = position.saturating_sub((-offset) as u64);
                 }
             },
             SeekFrom::End(offset) => {
-                let end = self.content.len() as u64;
+                let end = self.content.read().len() as u64;
                 if offset >= 0 {
-                    self.position = end.saturating_add(offset as u64);
+                    *position = end.saturating_add(offset as u64);
                 } else {
-                    self.position = end.saturating_sub((-offset) as u64);
+                    *position = end.saturating_sub((-offset) as u64);
                 }
             },
         }
         
-        Ok(self.position)
+        Ok(*position)
     }
     
-    fn close(&mut self) -> Result<()> {
+    fn release(&self) -> Result<()> {
         Ok(())
     }
     
     fn metadata(&self) -> Result<FileMetadata> {
         Ok(FileMetadata {
             file_type: FileType::RegularFile,
-            size: self.content.len(),
+            size: self.content.read().len(),
             permissions: FilePermission {
                 read: true,
                 write: true,
@@ -273,15 +278,15 @@ impl FileHandle for TestFileHandle {
 }
 
 impl FileOperations for TestFileSystem {
-    fn open(&self, path: &str, _flags: u32) -> Result<Box<dyn FileHandle>> {
+    fn open(&self, path: &str, _flags: u32) -> Result<Arc<dyn FileHandle>> {
         let normalized = self.normalize_path(path);
         
         // Simple implementation for testing (only check the beginning of the path)
         if normalized == "/test.txt" {
-            return Ok(Box::new(TestFileHandle {
+            return Ok(Arc::new(TestFileHandle {
                 path: normalized,
-                position: 0,
-                content: b"Hello, world!".to_vec(),
+                position: RwLock::new(0),
+                content: RwLock::new(b"Hello, world!".to_vec()),
             }));
         }
 
@@ -293,10 +298,10 @@ impl FileOperations for TestFileSystem {
         for (dir_path, entries) in self.directories.lock().iter() {
             if dir_path == parent_path {
                 if let Some(_) = entries.iter().find(|e| e.name == name && e.file_type == FileType::RegularFile) {
-                    return Ok(Box::new(TestFileHandle {
+                    return Ok(Arc::new(TestFileHandle {
                         path: normalized,
-                        position: 0,
-                        content: Vec::new(), // Newly created files are empty
+                        position: RwLock::new(0),
+                        content: RwLock::new(Vec::new()), // Newly created files are empty
                     }));
                 }
             }
@@ -541,7 +546,7 @@ impl FileOperations for TestFileSystem {
     }
     
     fn root_dir(&self) -> Result<Directory> {
-        Ok(Directory::new("/".to_string()))
+        Ok(Directory::open("/".to_string()))
     }
 }
 
diff --git a/kernel/src/fs/tests.rs b/kernel/src/fs/tests.rs
index 4a1d24fc1..23fab942d 100644
--- a/kernel/src/fs/tests.rs
+++ b/kernel/src/fs/tests.rs
@@ -2,6 +2,7 @@ use alloc::boxed::Box;
 use super::*;
 use crate::device::block::mockblk::MockBlockDevice;
 use crate::fs::testfs::{TestFileSystem, TestFileSystemDriver};
+use crate::task::new_user_task;
 
 // Test cases
 #[test_case]
@@ -195,11 +196,9 @@ fn test_file_creation() {
     let fs_id = manager.register_fs(fs); // fs_idを取得
     let _ = manager.mount(fs_id, "/mnt"); // fs_idを使用
 
-
     // Create an instance of the file structure
-    let file = File::with_manager("/mnt/test.txt".to_string(), &mut manager);
-    assert_eq!(file.path, "/mnt/test.txt");
-    assert!(!file.is_open());
+    // let file = File::open_with_manager("/mnt/test.txt".to_string(), &mut manager).unwrap();
+    // assert_eq!(file.path, "/mnt/test.txt");
 }
 
 #[test_case]
@@ -213,28 +212,10 @@ fn test_file_open_close() {
     let _ = manager.mount(fs_id, "/mnt"); // fs_idを使用
     
     // Create and open a file object
-    let mut file = File::with_manager("/mnt/test.txt".to_string(), &mut manager);
-
-    // Initially closed
-    assert!(!file.is_open());
+    let file = File::open_with_manager("/mnt/test.txt".to_string(), &mut manager);
     
     // Open the file
-    let result = file.open(0);
-    assert!(result.is_ok());
-    assert!(file.is_open());
-    
-    // Opening an already open file is fine
-    let result = file.open(0);
-    assert!(result.is_ok());
-    
-    // Close the file
-    let result = file.close();
-    assert!(result.is_ok());
-    assert!(!file.is_open());
-    
-    // Closing an already closed file is fine
-    let result = file.close();
-    assert!(result.is_ok());
+    assert!(file.is_ok());
 }
 
 #[test_case]
@@ -247,21 +228,8 @@ fn test_file_read_write() {
     let fs_id = manager.register_fs(fs); // fs_idを取得
     let _ = manager.mount(fs_id, "/mnt"); // fs_idを使用
     
-    let mut file = File::with_manager("/mnt/test.txt".to_string(), &mut manager);
-    
-    // Reading and writing while closed results in an error
-    let mut buffer = [0u8; 10];
-    let read_result = file.read(&mut buffer);
-    assert!(read_result.is_err());
-    assert_eq!(read_result.unwrap_err().kind, FileSystemErrorKind::IoError);
-    
-    let write_result = file.write(b"test");
-    assert!(write_result.is_err());
-    assert_eq!(write_result.unwrap_err().kind, FileSystemErrorKind::IoError);
-    
-    // Open the file
-    file.open(0).unwrap();
-    
+    let mut file = File::open_with_manager("/mnt/test.txt".to_string(), &mut manager).unwrap();
+
     // Read test
     let mut buffer = [0u8; 20];
     let bytes_read = file.read(&mut buffer).unwrap();
@@ -291,8 +259,7 @@ fn test_file_seek() {
     let fs_id = manager.register_fs(fs); // fs_idを取得
     let _ = manager.mount(fs_id, "/mnt"); // fs_idを使用
     
-    let mut file = File::with_manager("/mnt/test.txt".to_string(), &mut manager);
-    file.open(0).unwrap();
+    let mut file = File::open_with_manager("/mnt/test.txt".to_string(), &mut manager).unwrap();
     
     // Seek from the start
     let pos = file.seek(SeekFrom::Start(5)).unwrap();
@@ -321,14 +288,13 @@ fn test_file_metadata_and_size() {
     let fs_id = manager.register_fs(fs); // fs_idを取得
     let _ = manager.mount(fs_id, "/mnt"); // fs_idを使用
     
-    let mut file = File::with_manager("/mnt/test.txt".to_string(), &mut manager);
+    let mut file = File::open_with_manager("/mnt/test.txt".to_string(), &mut manager).unwrap();
     
     // Get metadata (possible even when not open)
     let metadata = file.metadata().unwrap();
     assert_eq!(metadata.file_type, FileType::RegularFile);
 
     // Write
-    file.open(0).unwrap();
     file.write(b"Hello, world!").unwrap();
     
     // Get size
@@ -347,10 +313,7 @@ fn test_file_read_all() {
     let _ = manager.mount(fs_id, "/mnt"); // fs_idを使用
     
     // let mut file = File::new("/mnt/test.txt".to_string(), 0);
-    let mut file = File::with_manager("/mnt/test.txt".to_string(), &mut manager);
-
-    file.open(0).unwrap();
-
+    let mut file = File::open_with_manager("/mnt/test.txt".to_string(), &mut manager).unwrap();
     // Write
     file.write(b"Hello, world!").unwrap();
     
@@ -381,17 +344,15 @@ fn test_file_auto_close() {
     // Open a file within a scope
     {
         // let mut file = File::new("/mnt/test.txt".to_string(), 0);
-        let mut file = File::with_manager("/mnt/test.txt".to_string(), &mut manager);
-        file.open(0).unwrap();
-        assert!(file.is_open());
+        let file = File::open_with_manager("/mnt/test.txt".to_string(), &mut manager);
+        assert!(file.is_ok());
         
         // Exiting the scope will automatically close the file due to the Drop trait
     }
     
     // Verify that a new file object can be created and opened
-    let mut file2 = File::with_manager("/mnt/test.txt".to_string(), &mut manager);
-    let result = file2.open(0);
-    assert!(result.is_ok());
+    let file2 = File::open_with_manager("/mnt/test.txt".to_string(), &mut manager);
+    assert!(file2.is_ok());
 }
 
 #[test_case]
@@ -405,7 +366,7 @@ fn test_directory_creation() {
     let _ = manager.mount(fs_id, "/mnt"); // fs_idを使用
     
     // Create an instance of the directory structure
-    let dir = Directory::with_manager("/mnt".to_string(), &mut manager);
+    let dir = Directory::open_with_manager("/mnt".to_string(), &mut manager);
     assert_eq!(dir.path, "/mnt");
 }
 
@@ -420,7 +381,7 @@ fn test_directory_read_entries() {
     let _ = manager.mount(fs_id, "/mnt"); // fs_idを使用
     
     // Read directory entries
-    let dir = Directory::with_manager("/mnt".to_string(), &mut manager);
+    let dir = Directory::open_with_manager("/mnt".to_string(), &mut manager);
     let entries = dir.read_entries().unwrap();
     
     assert_eq!(entries.len(), 2);
@@ -441,7 +402,7 @@ fn test_directory_create_file() {
     let _ = manager.mount(fs_id, "/mnt"); // fs_idを使用
     
     // Create a file in the directory
-    let dir = Directory::with_manager("/mnt".to_string(), &mut manager);
+    let dir = Directory::open_with_manager("/mnt".to_string(), &mut manager);
     let result = dir.create_file("newfile.txt");
     assert!(result.is_ok());
     
@@ -450,9 +411,8 @@ fn test_directory_create_file() {
     assert!(entries.iter().any(|e| e.name == "newfile.txt" && e.file_type == FileType::RegularFile));
     
     // Try opening the file
-    let mut file = File::with_manager("/mnt/newfile.txt".to_string(), &mut manager);
-    let file_result = file.open(0);
-    assert!(file_result.is_ok());
+    let file = File::open_with_manager("/mnt/newfile.txt".to_string(), &mut manager);
+    assert!(file.is_ok());
 }
 
 #[test_case]
@@ -466,7 +426,7 @@ fn test_directory_create_subdirectory() {
     let _ = manager.mount(fs_id, "/mnt"); // fs_idを使用
     
     // Create a subdirectory
-    let dir = Directory::with_manager("/mnt".to_string(), &mut manager);
+    let dir = Directory::open_with_manager("/mnt".to_string(), &mut manager);
     let result = dir.create_dir("subdir");
     assert!(result.is_ok());
     
@@ -475,7 +435,7 @@ fn test_directory_create_subdirectory() {
     assert!(entries.iter().any(|e| e.name == "subdir" && e.file_type == FileType::Directory));
     
     // Operate on the subdirectory
-    let subdir = Directory::with_manager("/mnt/subdir".to_string(), &mut manager);
+    let subdir = Directory::open_with_manager("/mnt/subdir".to_string(), &mut manager);
     let entries = subdir.read_entries().unwrap();
     assert!(entries.is_empty()); // Newly created directory is empty
 }
@@ -491,18 +451,18 @@ fn test_directory_nested_operations() {
     let _ = manager.mount(fs_id, "/mnt"); // fs_idを使用
     
     // Nested operations
-    let root_dir = Directory::with_manager("/mnt".to_string(), &mut manager);
+    let root_dir = Directory::open_with_manager("/mnt".to_string(), &mut manager);
     
     // Create a subdirectory
     root_dir.create_dir("level1").unwrap();
     
     // Operate on the subdirectory
-    let level1_dir = Directory::with_manager("/mnt/level1".to_string(), &mut manager);
+    let level1_dir = Directory::open_with_manager("/mnt/level1".to_string(), &mut manager);
     level1_dir.create_dir("level2").unwrap();
     level1_dir.create_file("file_in_level1.txt").unwrap();
     
     // Operate on a deeper level
-    let level2_dir = Directory::with_manager("/mnt/level1/level2".to_string(), &mut manager);
+    let level2_dir = Directory::open_with_manager("/mnt/level1/level2".to_string(), &mut manager);
     level2_dir.create_file("deep_file.txt").unwrap();
     
     // Verify
@@ -522,7 +482,7 @@ fn test_directory_error_handling() {
     let _ = manager.mount(fs_id, "/mnt"); // fs_idを使用
     
     // Non-existent directory
-    let nonexistent_dir = Directory::with_manager("/mnt/nonexistent".to_string(), &mut manager);
+    let nonexistent_dir = Directory::open_with_manager("/mnt/nonexistent".to_string(), &mut manager);
     let result = nonexistent_dir.read_entries();
     match result {
         Ok(_) => panic!("Expected an error, but got success"),
@@ -543,7 +503,7 @@ fn test_directory_error_handling() {
     }
     
     // Treat a file as a directory
-    let file_as_dir = Directory::with_manager("/mnt/test.txt".to_string(), &mut manager);
+    let file_as_dir = Directory::open_with_manager("/mnt/test.txt".to_string(), &mut manager);
     let result = file_as_dir.read_entries();
     match result {
         Ok(_) => panic!("Expected an error, but got success"),
@@ -565,7 +525,7 @@ fn test_directory_with_global_manager() {
     let _ = global_manager.mount(fs_id, "/mnt"); // fs_idを使用
     
     // Directory operations using the global manager
-    let dir = Directory::new("/mnt".to_string());
+    let dir = Directory::open("/mnt".to_string());
     let entries = dir.read_entries().unwrap();
     assert_eq!(entries.len(), 2);
     
@@ -857,4 +817,36 @@ fn test_path_normalization() {
     assert_eq!(VfsManager::normalize_path("../a/b/c"), "../a/b/c");
     assert_eq!(VfsManager::normalize_path("a/b/c/.."), "a/b");
     assert_eq!(VfsManager::normalize_path("a/b/c/../.."), "a");
+}
+
+#[test_case]
+fn test_to_absolute_path() {
+    let mut task = new_user_task("test".to_string(), 0);
+    task.cwd = Some("/mnt".to_string());
+
+    let relative_path = "test.txt";
+    let absolute_path = VfsManager::to_absolute_path(&task, relative_path).unwrap();
+    assert_eq!(absolute_path, "/mnt/test.txt");
+
+
+    let relative_path = "./test.txt";
+    let absolute_path = VfsManager::to_absolute_path(&task, relative_path).unwrap();
+    assert_eq!(absolute_path, "/mnt/test.txt");
+
+    let relative_path = "../test.txt";
+    let absolute_path = VfsManager::to_absolute_path(&task, relative_path).unwrap();
+    assert_eq!(absolute_path, "/test.txt"); // Should not resolve to /test.txt
+
+    let relative_path = "./a/../test.txt";
+    let absolute_path = VfsManager::to_absolute_path(&task, relative_path).unwrap();
+    assert_eq!(absolute_path, "/mnt/test.txt"); // Should resolve to /mnt/test.txt
+
+    let relative_path = "/a/b/test.txt";
+    let absolute_path = VfsManager::to_absolute_path(&task, relative_path).unwrap();
+    assert_eq!(absolute_path, "/a/b/test.txt"); // Should not resolve to /mnt/a/b/test.txt
+
+    task.cwd = None; // Reset current working directory
+    let relative_path = "test.txt";
+    let absolute_path = VfsManager::to_absolute_path(&task, relative_path);
+    assert!(absolute_path.is_err());
 }
\ No newline at end of file
diff --git a/kernel/src/main.rs b/kernel/src/main.rs
index 5c6d55a97..0d658a1ad 100644
--- a/kernel/src/main.rs
+++ b/kernel/src/main.rs
@@ -168,7 +168,7 @@ use fs::{drivers::initramfs::relocate_initramfs, File};
 use initcall::{call_initcalls, driver::driver_initcall_call, early::early_initcall_call};
 use slab_allocator_rs::MIN_HEAP_SIZE;
 
-use core::panic::PanicInfo;
+use core::panic::{self, PanicInfo};
 
 use arch::{get_cpu, init_arch};
 use task::{elf_loader::load_elf_into_task, new_user_task};
@@ -253,11 +253,13 @@ pub extern "C" fn start_kernel(cpu_id: usize) -> ! {
     println!("[Scarlet Kernel] Creating initial user task...");
     let mut task = new_user_task("init".to_string(), 0);
     task.init();
-    let mut file = File::new("/bin/init".to_string());
-    file.open(0).map_err(|e| {
-        early_println!("[Scarlet Kernel] Error opening init file: {:?}", e);
-        return;
-    }).unwrap();
+    task.cwd = Some("/".to_string());
+    let mut file = match  File::open("/bin/init".to_string()) {
+        Ok(file) => file,
+        Err(e) => {
+            panic!("Failed to open init file: {:?}", e);
+        },
+    };
 
     match load_elf_into_task(&mut file, &mut task) {
         Ok(_) => {
diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs
index 3db9b21a7..0542ae4b5 100644
--- a/kernel/src/syscall/mod.rs
+++ b/kernel/src/syscall/mod.rs
@@ -13,6 +13,7 @@
 //! 
 
 use crate::arch::Trapframe;
+use crate::fs::syscall::{sys_close, sys_lseek, sys_open, sys_read, sys_write};
 use crate::task::syscall::{sys_brk, sys_clone, sys_execve, sys_execve_abi, sys_exit, sys_getchar, sys_getpid, sys_getppid, sys_putchar, sys_sbrk, sys_waitpid};
 
 #[macro_use]
@@ -31,6 +32,13 @@ syscall_table! {
     Getppid = 8 => sys_getppid,
     Brk = 12 => sys_brk,
     Sbrk = 13 => sys_sbrk,
+    // BASIC I/O
     Putchar = 16 => sys_putchar,
     Getchar = 17 => sys_getchar,
+    // File operations
+    Open = 20 => sys_open,
+    Close = 21 => sys_close,
+    Read = 22 => sys_read,
+    Write = 23 => sys_write,
+    Lseek = 24 => sys_lseek,
 }
diff --git a/kernel/src/task/elf_loader/tests/mod.rs b/kernel/src/task/elf_loader/tests/mod.rs
index 8f27c1dec..2071d6da9 100644
--- a/kernel/src/task/elf_loader/tests/mod.rs
+++ b/kernel/src/task/elf_loader/tests/mod.rs
@@ -100,8 +100,7 @@ fn test_load_elf() {
     manager.mount(fs_id, "/").expect("Failed to mount test filesystem");
     let file_path = "/test.elf";
     manager.create_file(file_path).expect("Failed to create test file");
-    let mut file = File::with_manager(file_path.to_string(), &mut manager);
-    file.open(0).expect("Failed to open test file");
+    let mut file = File::open_with_manager(file_path.to_string(), &mut manager).map_err(|_| "Failed to create file").unwrap();
     file.write(include_bytes!("test.elf")).expect("Failed to write test ELF file");
     
     // Create a new task
@@ -136,13 +135,12 @@ fn test_load_elf_invalid_magic() {
     let fs = TestFileSystem::new(0, "test_fs", Box::new(blk_dev), 512);
     let fs_id = manager.register_fs(Box::new(fs));
     manager.mount(fs_id, "/").expect("Failed to mount test filesystem");
-    let file_path = "invalid.elf";
+    let file_path = "/invalid.elf";
     manager.create_file(file_path).expect("Failed to create test file");
 
     // Create a mock ELF file with an invalid magic number
     let invalid_elf_data = vec![0u8; 64]; // 64-byte ELF header with all zeros
-    let mut file = File::with_manager("invalid.elf".to_string(), &mut manager);
-    file.open(0).expect("Failed to open invalid ELF file");
+    let mut file = File::open_with_manager("/invalid.elf".to_string(), &mut manager).unwrap();
     file.write(&invalid_elf_data).expect("Failed to write invalid ELF data");
 
     // Create a new task
@@ -165,7 +163,7 @@ fn test_load_elf_invalid_alignment() {
     let fs = TestFileSystem::new(0, "test_fs", Box::new(blk_dev), 512);
     let fs_id = manager.register_fs(Box::new(fs));
     manager.mount(fs_id, "/").expect("Failed to mount test filesystem");
-    let file_path = "invalid_align.elf";
+    let file_path = "/invalid_align.elf";
     manager.create_file(file_path).expect("Failed to create test file");
 
     // Create a mock ELF file with an invalid alignment
@@ -194,8 +192,7 @@ fn test_load_elf_invalid_alignment() {
     invalid_elf_data.extend_from_slice(&[0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); // p_memsz
     invalid_elf_data.extend_from_slice(&[0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]); // p_align = 0
 
-    let mut file = File::with_manager("invalid_align.elf".to_string(), &mut manager);
-    file.open(0).expect("Failed to open invalid ELF file");
+    let mut file = File::open_with_manager("/invalid_align.elf".to_string(), &mut manager).map_err(|_| "Failed to create file").unwrap();
     file.write(&invalid_elf_data).expect("Failed to write invalid ELF data");
 
     // Create a new task
@@ -220,8 +217,7 @@ fn test_load_elf_bss_zeroed() {
     manager.mount(fs_id, "/").expect("Failed to mount test filesystem");
     let file_path = "/test_bss.elf";
     manager.create_file(file_path).expect("Failed to create test file");
-    let mut file = File::with_manager(file_path.to_string(), &mut manager);
-    file.open(0).expect("Failed to open test file");
+    let mut file = File::open_with_manager(file_path.to_string(), &mut manager).map_err(|_| "Failed to create file").unwrap();
 
     // Create a mock ELF file with a .bss section
     let mut elf_data = vec![0u8; 64];
diff --git a/kernel/src/task/mod.rs b/kernel/src/task/mod.rs
index 4fa473995..b8747940e 100644
--- a/kernel/src/task/mod.rs
+++ b/kernel/src/task/mod.rs
@@ -10,8 +10,13 @@ extern crate alloc;
 use alloc::{boxed::Box, string::{String, ToString}, vec::Vec};
 use spin::Mutex;
 
-use crate::{abi::{scarlet::ScarletAbi, AbiModule}, arch::{get_cpu, vcpu::Vcpu}, environment::{DEAFAULT_MAX_TASK_DATA_SIZE, DEAFAULT_MAX_TASK_STACK_SIZE, DEAFAULT_MAX_TASK_TEXT_SIZE, KERNEL_VM_STACK_END, PAGE_SIZE}, mem::page::{allocate_raw_pages, free_boxed_page, Page}, println, sched::scheduler::get_scheduler, vm::{manager::VirtualMemoryManager, user_kernel_vm_init, user_vm_init, vmem::{MemoryArea, VirtualMemoryMap, VirtualMemoryPermission, VirtualMemoryRegion}}};
+use crate::{arch::{get_cpu, vcpu::Vcpu}, environment::{DEAFAULT_MAX_TASK_DATA_SIZE, DEAFAULT_MAX_TASK_STACK_SIZE, DEAFAULT_MAX_TASK_TEXT_SIZE, KERNEL_VM_STACK_END, PAGE_SIZE}, fs::{File, FileHandle}, mem::page::{allocate_raw_pages, free_boxed_page, Page}, println, sched::scheduler::get_scheduler, vm::{manager::VirtualMemoryManager, user_kernel_vm_init, user_vm_init, vmem::{MemoryArea, VirtualMemoryMap, VirtualMemoryPermission, VirtualMemoryRegion}}};
+use crate::abi::{scarlet::ScarletAbi, AbiModule};
 
+/// The maximum number of file descriptors a task can have.
+/// This value is set to 256 as a reasonable default for most use cases,
+/// balancing resource usage and typical application needs. Adjust if necessary.
+const NUM_OF_FDS: usize = 256;
 
 #[derive(Debug, PartialEq, Clone, Copy)]
 pub enum TaskState {
@@ -55,6 +60,12 @@ pub struct Task {
 
     /// Dynamic ABI
     pub abi: Option<Box<dyn AbiModule>>,
+
+    // File descriptors (File) table
+    fd_table: Vec<usize>,
+    files: [Option<File>; 256],
+    // Current working directory
+    pub cwd: Option<String>,
 }
 
 #[derive(Debug, Clone)]
@@ -68,7 +79,7 @@ static TASK_ID: Mutex<usize> = Mutex::new(1);
 impl Task {
     pub fn new(name: String, priority: u32, task_type: TaskType) -> Self {
         let mut taskid = TASK_ID.lock();
-        let task = Task {
+        let mut task = Task {
             id: *taskid,
             name,
             priority,
@@ -91,8 +102,16 @@ impl Task {
             parent_id: None,
             children: Vec::new(),
             exit_status: None,
-            abi: Some(Box::new(ScarletAbi::default())) // Default ABI
+            abi: Some(Box::new(ScarletAbi::default())), // Default ABI
+            fd_table: Vec::new(),
+            files: [ const { None }; NUM_OF_FDS],
+            cwd: None,
         };
+        
+        for i in (0..NUM_OF_FDS).rev() {
+            task.fd_table.push(i);
+        }
+
         *taskid += 1;
         task
     }
@@ -567,6 +586,118 @@ impl Task {
         self.exit_status
     }
 
+    /// Get the file descriptor table
+    /// 
+    /// # Returns
+    /// A reference to the file descriptor table
+    /// 
+    pub fn get_fd_table(&self) -> &Vec<usize> {
+        &self.fd_table
+    }
+
+    /// Get the file at the specified index
+    /// 
+    /// # Arguments
+    /// * `index` - The index of the file
+    /// 
+    /// # Returns
+    /// The file at the specified index, or None if not found
+    /// 
+    pub fn get_file(&self, index: usize) -> Option<&File> {
+        if index < NUM_OF_FDS {
+            self.files[index].as_ref()
+        } else {
+            None
+        }
+    }
+
+    /// Get the mutable file at the specified file descriptor
+    /// 
+    /// # Arguments
+    /// * `fd` - The file descriptor of the file
+    /// 
+    /// # Returns
+    /// The mutable file at the specified file descriptor, or None if not found
+    /// 
+    pub fn get_mut_file(&mut self, fd: usize) -> Option<&mut File> {
+        if fd < NUM_OF_FDS {
+            self.files[fd].as_mut()
+        } else {
+            None
+        }
+    }
+
+    /// Set the file at the specified file descriptor
+    /// 
+    /// # Arguments
+    /// * `fd` - The file descriptor of the file
+    /// * `file` - The file to set
+    /// 
+    /// # Returns
+    /// The result of setting the file, which is Ok(()) if successful or an error message if not.
+    /// 
+    pub fn set_file(&mut self, fd: usize, file: File) -> Result<(), &'static str> {
+        if fd < NUM_OF_FDS {
+            self.files[fd] = Some(file);
+            Ok(())
+        } else {
+            Err("Index out of bounds")
+        }
+    }
+
+    /// Add a file to the task
+    /// 
+    /// # Arguments
+    /// * `file` - The file handle to add
+    /// 
+    /// # Returns
+    /// The file descriptor of the file, or an error message if the file descriptor table is full
+    /// 
+    pub fn add_file(&mut self, file: File) -> Result<usize, &'static str> {
+        if let Some(fd) = self.allocate_fd() {
+            self.files[fd] = Some(file);
+            Ok(fd)
+        } else {
+            Err("File descriptor table is full")
+        }
+    }
+
+    /// Remove a file from the task
+    /// 
+    /// # Arguments
+    /// * `fd` - The file descriptor to remove
+    /// 
+    /// # Returns
+    /// The result of removing the file, which is Ok(()) if successful or an error message if not.
+    /// 
+    pub fn remove_file(&mut self, fd: usize) -> Result<(), &'static str> {
+        if fd < NUM_OF_FDS {
+            if self.files[fd].is_none() {
+                return Err("File descriptor is already empty");
+            }
+            self.files[fd] = None;
+            self.fd_table.push(fd);
+            Ok(())
+        } else {
+            Err("File descriptor out of bounds")
+        }
+    }
+
+    /// Allocate a file descriptor
+    /// 
+    /// # Returns
+    /// The allocated file descriptor, or None if no file descriptors are available
+    /// 
+    fn allocate_fd(&mut self) -> Option<usize> {
+        if let Some(fd) = self.fd_table.pop() {
+            Some(fd)
+        } else {
+            None
+        }
+    }
+
+
+
     /// Clone this task, creating a near-identical copy
     /// 
     /// # Returns
@@ -645,6 +776,13 @@ impl Task {
         // Set the same entry point and PC
         child.entry = self.entry;
         child.vcpu.set_pc(self.vcpu.get_pc());
+
+        // Copy file descriptors
+        child.fd_table = self.fd_table.clone();
+        child.files = self.files.clone();
+
+        // Copy the current working directory
+        child.cwd = self.cwd.clone();
         
         // Set the state to Ready
         child.state = self.state;
diff --git a/kernel/src/task/syscall.rs b/kernel/src/task/syscall.rs
index da5795f05..b9bd42e2f 100644
--- a/kernel/src/task/syscall.rs
+++ b/kernel/src/task/syscall.rs
@@ -1,10 +1,9 @@
-use alloc::string::ToString;
 use alloc::vec::Vec;
-use core::{error, str};
+use core::str;
 
 use crate::abi::{AbiRegistry, MAX_ABI_LENGTH};
 use crate::device::manager::DeviceManager;
-use crate::fs::{File, MAX_PATH_LENGTH};
+use crate::fs::{File, VfsManager, MAX_PATH_LENGTH};
 use crate::task::elf_loader::load_elf_into_task;
 
 use crate::arch::{get_cpu, vm, Registers, Trapframe};
@@ -139,7 +138,17 @@ pub fn sys_execve(trapframe: &mut Trapframe) -> usize {
     
     // Convert path bytes to string
     let path_str = match str::from_utf8(&path_bytes) {
-        Ok(s) => s,
+        Ok(s) => match VfsManager::to_absolute_path(&task, s) {
+            Ok(path) => path,
+            Err(_) => {
+                // Restore the managed pages, memory mapping and sizes
+                task.managed_pages = backup_pages; // Restore the pages
+                task.vm_manager.restore_memory_maps(backup_vm_mapping).unwrap(); // Restore the memory mapping
+                task.text_size = backup_text_size; // Restore the text size
+                task.data_size = backup_data_size; // Restore the data size
+                return usize::MAX; // Path error
+            }
+        },
         Err(_) => {
             // Restore the managed pages, memory mapping and sizes
             task.managed_pages = backup_pages; // Restore the pages
@@ -151,8 +160,8 @@ pub fn sys_execve(trapframe: &mut Trapframe) -> usize {
     };
     
     // Try to open the executable file
-    let mut file = File::new(path_str.to_string());
-    if file.open(0).is_err() {
+    let file = File::open(path_str.clone());
+    if file.is_err() {
         // Restore the managed pages, memory mapping and sizes
         task.managed_pages = backup_pages; // Restore the pages
         task.vm_manager.restore_memory_maps(backup_vm_mapping).unwrap(); // Restore the memory mapping
@@ -160,6 +169,7 @@ pub fn sys_execve(trapframe: &mut Trapframe) -> usize {
         task.data_size = backup_data_size; // Restore the data size
         return usize::MAX; // File open error
     }
+    let mut file = file.unwrap();
 
     task.text_size = 0;
     task.data_size = 0;
@@ -169,7 +179,7 @@ pub fn sys_execve(trapframe: &mut Trapframe) -> usize {
     match load_elf_into_task(&mut file, task) {
         Ok(entry_point) => {
             // Set the name
-            task.name = path_str.to_string();
+            task.name = path_str;
             // Clear page table entries
             let idx = vm::get_root_page_table_idx(task.vm_manager.get_asid()).unwrap();
             let root_page_table = vm::get_page_table(idx).unwrap();
diff --git a/kernel/src/test.rs b/kernel/src/test.rs
index bac1e91a1..490c35b4b 100644
--- a/kernel/src/test.rs
+++ b/kernel/src/test.rs
@@ -14,10 +14,12 @@ fn panic(info: &PanicInfo) -> ! {
 
 #[cfg(test)]
 pub fn test_runner(tests: &[&dyn Fn()]) {
+
     use crate::println;
 
     println!("[Test Runner] Running {} tests", tests.len());
     for test in tests {
+        // println!("[Test Runner] Running test: {:?}", test as *const _);
         test();
     }
 
diff --git a/user/lib/src/fs.rs b/user/lib/src/fs.rs
new file mode 100644
index 000000000..198c25a46
--- /dev/null
+++ b/user/lib/src/fs.rs
@@ -0,0 +1,85 @@
+use crate::utils::str_to_cstr_bytes;
+use crate::boxed::Box;
+use crate::syscall::{syscall2, syscall3, Syscall};
+
+/// Open a file.
+/// 
+/// # Arguments
+/// * `path` - Path to the file
+/// * `flags` - Flags for opening the file
+/// 
+/// # Return Value
+/// - On success: file descriptor
+/// - On error: -1
+/// 
+pub fn open(path: &str, flags: usize) -> i32 {
+    let path_ptr = Box::into_raw(str_to_cstr_bytes(path).unwrap().into_boxed_slice()) as *const u8 as usize;
+    let res = syscall2(Syscall::Open, path_ptr, flags);
+    let _ = unsafe { Box::from_raw(path_ptr as *mut u8) }; // Free the allocated memory
+    // Return the result of the syscall
+    res as i32
+}
+
+/// Close a file.
+/// 
+/// # Arguments
+/// * `fd` - File descriptor
+/// 
+/// # Return Value
+/// - On success: 0
+/// - On error: -1
+/// 
+pub fn close(fd: i32) -> i32 {
+    let res = syscall2(Syscall::Close, fd as usize, 0);
+    // Return the result of the syscall
+    res as i32
+}
+
+/// Read from a file.
+/// 
+/// # Arguments
+/// * `fd` - File descriptor
+/// * `buf` - Buffer to read into
+///
+/// # Return Value
+/// - On success: number of bytes read
+/// - On error: -1
+/// 
+pub fn read(fd: i32, buf: &mut [u8]) -> i32 {
+    let res = syscall3(Syscall::Read, fd as usize, buf.as_mut_ptr() as usize, buf.len());
+    // Return the result of the syscall
+    res as i32
+}
+
+/// Write to a file.
+/// 
+/// # Arguments
+/// * `fd` - File descriptor
+/// * `buf` - Buffer to write from
+/// 
+/// # Return Value
+/// - On success: number of bytes written
+/// - On error: -1
+/// 
+pub fn write(fd: i32, buf: &[u8]) -> i32 {
+    let res = syscall3(Syscall::Write, fd as usize, buf.as_ptr() as usize, buf.len());
+    // Return the result of the syscall
+    res as i32
+}
+
+/// Seek to a position in a file.
+/// 
+/// # Arguments
+/// * `fd` - File descriptor
+/// * `offset` - Offset to seek to
+/// * `whence` - Whence for the seek operation
+/// 
+/// # Return Value
+/// - On success: new position in the file
+/// - On error: -1
+/// 
+pub fn lseek(fd: i32, offset: i64, whence: u32) -> i32 {
+    let res = syscall3(Syscall::Lseek, fd as usize, offset as usize, whence as usize);
+    // Return the result of the syscall
+    res as i32
+}
\ No newline at end of file
diff --git a/user/lib/src/lib.rs b/user/lib/src/lib.rs
index 04be5feae..13f968fa6 100644
--- a/user/lib/src/lib.rs
+++ b/user/lib/src/lib.rs
@@ -73,6 +73,8 @@ mod allocator;
 pub mod syscall;
 pub mod io;
 pub mod task;
+pub mod fs;
+pub mod utils;
 
 pub use core_exports::*;
 pub use alloc_exports::*;
diff --git a/user/lib/src/syscall.rs b/user/lib/src/syscall.rs
index 8248dadfb..e33d6a811 100644
--- a/user/lib/src/syscall.rs
+++ b/user/lib/src/syscall.rs
@@ -13,8 +13,15 @@ pub enum Syscall {
     Getppid = 8,
     Brk = 12,
     Sbrk = 13,
+    // BASIC I/O
     Putchar = 16,
     Getchar = 17,
+    // File operations
+    Open = 20,
+    Close = 21,
+    Read = 22,
+    Write = 23,
+    Lseek = 24,
 }
 
 pub fn syscall0(syscall: Syscall) -> usize {
diff --git a/user/lib/src/task.rs b/user/lib/src/task.rs
index 52dbcd9e7..233755f19 100644
--- a/user/lib/src/task.rs
+++ b/user/lib/src/task.rs
@@ -1,4 +1,3 @@
-use crate::println;
 use crate::syscall::{syscall0, syscall1, syscall3, syscall4, Syscall};
 use crate::vec::Vec;
 use crate::boxed::Box;
diff --git a/user/lib/src/utils.rs b/user/lib/src/utils.rs
new file mode 100644
index 000000000..7aaed1ff7
--- /dev/null
+++ b/user/lib/src/utils.rs
@@ -0,0 +1,27 @@
+use crate::vec::Vec;
+extern crate alloc;
+pub use alloc::string::*;
+
+/// Converts a Rust string slice (`&str`) into a null-terminated C-style string represented as a `Vec<u8>`.
+/// 
+/// # Arguments
+/// 
+/// * `s` - A string slice to be converted.
+///
+/// # Returns
+///
+/// * `Ok(Vec<u8>)` - A vector containing the bytes of the input string, followed by a null terminator.
+/// * `Err(())` - An error if the input string contains a null byte (`\0`), as null bytes are not allowed in C-style strings.
+///
+/// # Error Handling
+///
+/// If this function returns `Err(())`, the caller should sanitize the input string to remove null bytes before calling the function again.
+pub fn str_to_cstr_bytes(s: &str) -> Result<Vec<u8>, ()> {
+    if s.as_bytes().contains(&0) {
+        return Err(()); // Error if there is a null byte inside
+    }
+    let mut v = Vec::with_capacity(s.len() + 1);
+    v.extend_from_slice(s.as_bytes());
+    v.push(0); // Null terminator
+    Ok(v)
+}
\ No newline at end of file


Reviews:
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

Adds file operation support (#13) end-to-end in both userland and kernel.

- Introduces C-string helper and user-facing file-IO APIs (open/close/read/write/lseek).
- Extends kernel VFS: syscall handlers, `File`/`Directory` refactor, and task-level FD table.
- Updates task exec logic and tests to use new VFS and FD interfaces.

### Reviewed Changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated no comments.

<details>
<summary>Show a summary per file</summary>

| File                              | Description                                              |
|-----------------------------------|----------------------------------------------------------|
| user/lib/src/utils.rs             | Add `str_to_cstr_bytes` helper for C-string conversion   |
| user/lib/src/fs.rs                | Implement user-side open/close/read/write/lseek wrappers |
| kernel/src/fs/syscall.rs          | Add `sys_open`, `sys_close`, `sys_read`, `sys_write`, `sys_lseek` |
| kernel/src/fs/helper.rs           | Introduce `get_path_str` utility for C-string parsing    |
| kernel/src/task/mod.rs            | Extend `Task` with FD table and file handle management   |
| kernel/src/task/syscall.rs        | Update `sys_execve` to use VFS path resolution            |
</details>



<details>
<summary>Comments suppressed due to low confidence (5)</summary>

**kernel/src/fs/helper.rs:26**
* Missing import for `str`. Add `use core::str;` at the top of this file so `str::from_utf8` resolves correctly.
```
let path_str = match str::from_utf8(&path_bytes) {
```
**kernel/src/fs/syscall.rs:10**
* The flags argument is read but never used when opening the file. Pass `_flags` into the VFS `open` call instead of hardcoding mode or ignoring it.
```
let _flags = trapframe.get_arg(1) as i32;
```
**user/lib/src/fs.rs:17**
* User‐side `open` only uses `syscall2` but the kernel expects three arguments (path, flags, mode). You should call `syscall3` and include a mode argument.
```
let res = syscall2(Syscall::Open, path_ptr, flags);
```
**kernel/src/task/mod.rs:778**
* [nitpick] Task cloning leaves FD table uninitialized. Implement the logic to duplicate open file descriptors or remove this placeholder TODO.
```
// TODO: Copy file descriptors
```
**kernel/src/task/syscall.rs:142**
* Avoid `unwrap` in syscall context; invalid paths or UTF-8 should return an error code rather than panic the kernel.
```
Ok(s) => VfsManager::to_absolute_path(&task, s).unwrap(),
```
</details>


state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T08:08:29Z
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR introduces basic file operation support across the user library and kernel, including new syscalls, VFS path resolution, and task-level file descriptor management.

- Add `utils::str_to_cstr_bytes` and expose `fs` in the user library with `open`, `close`, `read`, `write`, and `lseek` wrappers
- Extend `Task` to track file descriptors, open files, and current working directory; update `execve` to resolve absolute paths
- Implement kernel-side VFS improvements, new fs syscalls, driver updates, and adapt existing tests to the new API

### Reviewed Changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 6 comments.

<details>
<summary>Show a summary per file</summary>

| File                                | Description                                        |
| ----------------------------------- | -------------------------------------------------- |
| user/lib/src/utils.rs               | Add string→C-string byte conversion helper         |
| user/lib/src/task.rs                | Remove unused `println` import                     |
| user/lib/src/syscall.rs             | Register new `Syscall` variants for file ops       |
| user/lib/src/lib.rs                 | Expose `fs` and `utils` modules                    |
| user/lib/src/fs.rs                  | Implement user‐side `open`/`close`/`read`/`write`/`lseek` |
| kernel/src/test.rs                  | Comment out verbose test runner logging            |
| kernel/src/task/syscall.rs          | Use VFS to convert to absolute path in `sys_execve`|
| kernel/src/task/mod.rs              | Add FD table, files array, and CWD to `Task`       |
| kernel/src/main.rs                  | Set initial CWD and use new `File::open`           |
| kernel/src/syscall/mod.rs           | Map new fs syscalls in syscall table               |
| kernel/src/fs/mod.rs                | Refactor `File`/`Directory` for global VFS and Arc |
| kernel/src/fs/syscall.rs            | Implement `sys_open`/`sys_read`/`sys_write`/etc.   |
| kernel/src/fs/helper.rs             | Extract path‐parsing helper                        |
| kernel/src/fs/drivers/cpio/mod.rs   | Update CPIO driver to use `Arc` and locks          |
| kernel/src/fs/tests.rs              | Adapt fs tests to `open_with_manager` API          |
</details>



<details>
<summary>Comments suppressed due to low confidence (2)</summary>

**kernel/src/fs/tests.rs:200**
* The `test_file_creation` test is currently empty after commenting out its assertions. Add meaningful assertions to validate file creation behavior or remove the test stub.
```
// let file = File::open_with_manager("/mnt/test.txt".to_string(), &mut manager).unwrap();
```
**kernel/src/fs/helper.rs:26**
* The code uses `str::from_utf8` without importing the `str` module, causing a compilation error. Add `use core::str;` at the top or qualify the call.
```
let path_str = match str::from_utf8(&path_bytes) {
```
</details>


state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T14:08:42Z

Review comments:
diff_hunk: @@ -0,0 +1,84 @@
+use crate::utils::str_to_cstr_bytes;
+use crate::boxed::Box;
+use crate::syscall::{syscall2, syscall3, Syscall};
+
+/// Open a file.
+/// 
+/// # Arguments
+/// * `path` - Path to the file
+/// * `flags` - Flags for opening the file
+/// 
+/// # Return Value
+/// - On success: file descriptor
+/// - On error: -1
+/// 
+pub fn open(path: &str, flags: usize) -> i32 {
+    let path_ptr = Box::into_raw(str_to_cstr_bytes(path).unwrap().into_boxed_slice()) as *const u8 as usize;
+    let res = syscall2(Syscall::Open, path_ptr, flags);
+    // Return the result of the syscall
+    res as i32
path: user/lib/src/fs.rs
user: login: Copilot
body: Calling `unwrap()` on `str_to_cstr_bytes` will panic on invalid input, and leaking the boxed slice may cause a memory leak. Consider propagating errors and freeing or reusing the buffer after the syscall.
```suggestion
    match str_to_cstr_bytes(path) {
        Ok(cstr_bytes) => {
            let boxed_slice = cstr_bytes.into_boxed_slice();
            let path_ptr = Box::into_raw(boxed_slice) as *const u8 as usize;
            let res = syscall2(Syscall::Open, path_ptr, flags);
            // Free the memory if the syscall fails
            if res < 0 {
                unsafe { Box::from_raw(path_ptr as *mut u8); }
            }
            res as i32
        }
        Err(_) => {
            // Return an error code (e.g., -1) if the conversion fails
            -1
        }
    }
```
created_at: 2025-05-21T14:08:40Z
author_association: CONTRIBUTOR
diff_hunk: @@ -139,7 +139,7 @@ pub fn sys_execve(trapframe: &mut Trapframe) -> usize {
     
     // Convert path bytes to string
     let path_str = match str::from_utf8(&path_bytes) {
-        Ok(s) => s,
+        Ok(s) => VfsManager::to_absolute_path(&task, s).unwrap(),
path: kernel/src/task/syscall.rs
user: login: Copilot
body: Using `unwrap()` on `to_absolute_path` can panic if path resolution fails. Handle the `Err` case to avoid kernel panic on invalid UTF-8 or missing CWD.
created_at: 2025-05-21T14:08:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -253,11 +253,8 @@ pub extern "C" fn start_kernel(cpu_id: usize) -> ! {
     println!("[Scarlet Kernel] Creating initial user task...");
     let mut task = new_user_task("init".to_string(), 0);
     task.init();
-    let mut file = File::new("/bin/init".to_string());
-    file.open(0).map_err(|e| {
-        early_println!("[Scarlet Kernel] Error opening init file: {:?}", e);
-        return;
-    }).unwrap();
+    task.cwd = Some("/".to_string());
+    let mut file = File::open("/bin/init".to_string()).unwrap();
path: kernel/src/main.rs
user: login: Copilot
body: Unwrapping here will panic if `/bin/init` fails to open. Consider returning an error or logging instead of a direct `unwrap()` in early boot.
```suggestion
    let mut file = match File::open("/bin/init".to_string()) {
        Ok(f) => f,
        Err(e) => {
            early_println!("[Scarlet Kernel] Failed to open /bin/init: {:?}", e);
            return;
        }
    };
```
created_at: 2025-05-21T14:08:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -352,45 +352,49 @@ impl FileOperations for Cpiofs {
     }
     
     fn root_dir(&self) -> Result<crate::fs::Directory> {
-        Ok(Directory::new(self.mount_point.clone() + "/"))
+        Ok(Directory::open(self.mount_point.clone() + "/"))
     }
 }
 
 struct CpiofsFileHandle {
-    content: Vec<u8>,
-    position: usize,
+    content: RwLock<Vec<u8>>,
+    position: RwLock<usize>,
 }
 
 impl FileHandle for CpiofsFileHandle {
-    fn read(&mut self, buffer: &mut [u8]) -> Result<usize> {
-        let available = self.content.len() - self.position;
+    fn read(&self, buffer: &mut [u8]) -> Result<usize> {
+        let content = self.content.read();
+        let mut position = self.position.write();
+        let available = self.content.read().len() - *position;
path: kernel/src/fs/drivers/cpio/mod.rs
user: login: Copilot
body: [nitpick] Calling `self.content.read()` twice incurs extra locking overhead. Cache the guard in a variable and reuse it to minimize lock contention.
```suggestion
        let available = content.len() - *position;
```
created_at: 2025-05-21T14:08:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,13 @@
+use crate::vec::Vec;
+extern crate alloc;
+pub use alloc::string::*;
+
path: user/lib/src/utils.rs
user: login: Copilot
body: [nitpick] Consider adding doc comments explaining when this function returns `Err(())` and what the caller should do to handle the error.
```suggestion

/// Converts a Rust string slice (`&str`) into a null-terminated C-style string represented as a `Vec<u8>`.
/// 
/// # Arguments
/// 
/// * `s` - A string slice to be converted.
///
/// # Returns
///
/// * `Ok(Vec<u8>)` - A vector containing the bytes of the input string, followed by a null terminator.
/// * `Err(())` - An error if the input string contains a null byte (`\0`), as null bytes are not allowed in C-style strings.
///
/// # Error Handling
///
/// If this function returns `Err(())`, the caller should sanitize the input string to remove null bytes before calling the function again.
```
created_at: 2025-05-21T14:08:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -10,8 +10,10 @@ extern crate alloc;
 use alloc::{boxed::Box, string::{String, ToString}, vec::Vec};
 use spin::Mutex;
 
-use crate::{abi::{scarlet::ScarletAbi, AbiModule}, arch::{get_cpu, vcpu::Vcpu}, environment::{DEAFAULT_MAX_TASK_DATA_SIZE, DEAFAULT_MAX_TASK_STACK_SIZE, DEAFAULT_MAX_TASK_TEXT_SIZE, KERNEL_VM_STACK_END, PAGE_SIZE}, mem::page::{allocate_raw_pages, free_boxed_page, Page}, println, sched::scheduler::get_scheduler, vm::{manager::VirtualMemoryManager, user_kernel_vm_init, user_vm_init, vmem::{MemoryArea, VirtualMemoryMap, VirtualMemoryPermission, VirtualMemoryRegion}}};
+use crate::{arch::{get_cpu, vcpu::Vcpu}, environment::{DEAFAULT_MAX_TASK_DATA_SIZE, DEAFAULT_MAX_TASK_STACK_SIZE, DEAFAULT_MAX_TASK_TEXT_SIZE, KERNEL_VM_STACK_END, PAGE_SIZE}, fs::{File, FileHandle}, mem::page::{allocate_raw_pages, free_boxed_page, Page}, println, sched::scheduler::get_scheduler, vm::{manager::VirtualMemoryManager, user_kernel_vm_init, user_vm_init, vmem::{MemoryArea, VirtualMemoryMap, VirtualMemoryPermission, VirtualMemoryRegion}}};
+use crate::abi::{scarlet::ScarletAbi, AbiModule};
 
path: kernel/src/task/mod.rs
user: login: Copilot
body: [nitpick] Add a comment explaining why `256` was chosen for `NUM_OF_FDS` or consider making it configurable via an existing constant.
```suggestion

/// The maximum number of file descriptors a task can have.
/// This value is set to 256 as a reasonable default for most use cases,
/// balancing resource usage and typical application needs. Adjust if necessary.
```
created_at: 2025-05-21T14:08:42Z
author_association: CONTRIBUTOR

PR comments:
user: login: petitstrawberry
created_at: 2025-05-20T08:12:00Z
body: fdのcloneをちゃんとしてからマージするからまだダメ
author_association: OWNER
user: login: petitstrawberry
created_at: 2025-05-22T08:37:08Z
body: stat系が欲しいかも
author_association: OWNER
user: login: petitstrawberry
created_at: 2025-05-24T07:40:38Z
body: stat周りはOS実装によるだろうから, ここでは一旦止める
author_association: OWNER