//! File handle for process use alloc::{string::String, sync::Arc}; use core::fmt; use rcore_fs::vfs::{FsError, INode, Metadata, PollStatus, Result}; #[derive(Clone)] pub struct FileHandle { inode: Arc, offset: u64, options: OpenOptions, // for debugging #[cfg(debug_assertions)] path: String, } #[derive(Debug, Clone)] pub struct OpenOptions { pub read: bool, pub write: bool, /// Before each write, the file offset is positioned at the end of the file. pub append: bool, } #[derive(Debug)] pub enum SeekFrom { Start(u64), End(i64), Current(i64), } impl FileHandle { pub fn new(inode: Arc, options: OpenOptions) -> Self { #[cfg(debug_assertions)] return FileHandle { inode, offset: 0, options, path: String::from("unknown"), }; #[cfg(not(debug_assertions))] return FileHandle { inode, offset: 0, options, }; } #[cfg(debug_assertions)] pub fn set_path(&mut self, path: &str) { self.path = String::from(path); } #[cfg(not(debug_assertions))] pub fn set_path(&mut self, _path: &str) { unreachable!() } pub fn read(&mut self, buf: &mut [u8]) -> Result { let len = self.read_at(self.offset as usize, buf)?; self.offset += len as u64; Ok(len) } pub fn read_at(&mut self, offset: usize, buf: &mut [u8]) -> Result { if !self.options.read { return Err(FsError::InvalidParam); // FIXME: => EBADF } let len = self.inode.read_at(offset, buf)?; Ok(len) } pub fn write(&mut self, buf: &[u8]) -> Result { let offset = match self.options.append { true => self.inode.metadata()?.size as u64, false => self.offset, } as usize; let len = self.write_at(offset, buf)?; self.offset = (offset + len) as u64; Ok(len) } pub fn write_at(&mut self, offset: usize, buf: &[u8]) -> Result { if !self.options.write { return Err(FsError::InvalidParam); // FIXME: => EBADF } let len = self.inode.write_at(offset, buf)?; Ok(len) } pub fn seek(&mut self, pos: SeekFrom) -> Result { self.offset = match pos { SeekFrom::Start(offset) => offset, SeekFrom::End(offset) => (self.inode.metadata()?.size as i64 + offset) as u64, SeekFrom::Current(offset) => (self.offset as i64 + offset) as u64, }; Ok(self.offset) } pub fn set_len(&mut self, len: u64) -> Result<()> { if !self.options.write { return Err(FsError::InvalidParam); // FIXME: => EBADF } self.inode.resize(len as usize)?; Ok(()) } pub fn sync_all(&mut self) -> Result<()> { self.inode.sync_all() } pub fn sync_data(&mut self) -> Result<()> { self.inode.sync_data() } pub fn metadata(&self) -> Result { self.inode.metadata() } pub fn lookup_follow(&self, path: &str, max_follow: usize) -> Result> { self.inode.lookup_follow(path, max_follow) } pub fn read_entry(&mut self) -> Result { if !self.options.read { return Err(FsError::InvalidParam); // FIXME: => EBADF } let name = self.inode.get_entry(self.offset as usize)?; self.offset += 1; Ok(name) } pub fn poll(&self) -> Result { self.inode.poll() } pub fn io_control(&self, cmd: u32, arg: usize) -> Result<()> { self.inode.io_control(cmd, arg) } pub fn inode(&self) -> Arc { self.inode.clone() } } impl fmt::Debug for FileHandle { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // for debugging #[cfg(debug_assertions)] return f .debug_struct("FileHandle") .field("offset", &self.offset) .field("options", &self.options) .field("path", &self.path) .finish(); #[cfg(not(debug_assertions))] return f .debug_struct("FileHandle") .field("offset", &self.offset) .field("options", &self.options) .finish(); } }