| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- use super::{File, FileType, SeekOption};
- use crate::{
- io::{Buffer, BufferFill, Stream},
- kernel::{
- constants::{EBADF, EFAULT, ENOTDIR, EOVERFLOW, ESPIPE},
- vfs::{
- dentry::Dentry,
- inode::{Inode, InodeUse, WriteOffset},
- types::Format,
- },
- },
- prelude::KResult,
- };
- use alloc::sync::Arc;
- use eonix_sync::Mutex;
- use posix_types::{
- getdent::{UserDirent, UserDirent64},
- open::OpenFlags,
- stat::StatX,
- };
- pub struct InodeFile {
- pub r: bool,
- pub w: bool,
- pub a: bool,
- /// Only a few modes those won't possibly change are cached here to speed up file operations.
- /// Specifically, `S_IFMT` masked bits.
- pub format: Format,
- cursor: Mutex<usize>,
- dentry: Arc<Dentry>,
- }
- impl InodeFile {
- pub fn new(dentry: Arc<Dentry>, flags: OpenFlags) -> File {
- // SAFETY: `dentry` used to create `InodeFile` is valid.
- // SAFETY: `mode` should never change with respect to the `S_IFMT` fields.
- let format = dentry.inode().expect("dentry should be invalid").format();
- let (r, w, a) = flags.as_rwa();
- File::new(
- flags,
- FileType::Inode(InodeFile {
- dentry,
- r,
- w,
- a,
- format,
- cursor: Mutex::new(0),
- }),
- )
- }
- pub fn sendfile_check(&self) -> KResult<()> {
- match self.format {
- Format::REG | Format::BLK => Ok(()),
- _ => Err(EBADF),
- }
- }
- pub async fn write(&self, stream: &mut dyn Stream, offset: Option<usize>) -> KResult<usize> {
- if !self.w {
- return Err(EBADF);
- }
- let mut cursor = self.cursor.lock().await;
- let (offset, update_offset) = match (self.a, offset) {
- (true, _) => (WriteOffset::End(&mut cursor), None),
- (false, Some(offset)) => (WriteOffset::Position(offset), None),
- (false, None) => (WriteOffset::Position(*cursor), Some(&mut *cursor)),
- };
- let nr_write = self.dentry.write(stream, offset).await?;
- if let Some(update_offset) = update_offset {
- *update_offset += nr_write;
- }
- Ok(nr_write)
- }
- pub async fn read(&self, buffer: &mut dyn Buffer, offset: Option<usize>) -> KResult<usize> {
- if !self.r {
- return Err(EBADF);
- }
- if let Some(offset) = offset {
- return Ok(self.dentry.read(buffer, offset).await?);
- }
- let mut cursor = self.cursor.lock().await;
- let nread = self.dentry.read(buffer, *cursor).await?;
- *cursor += nread;
- Ok(nread)
- }
- }
- impl File {
- pub fn get_inode(&self) -> KResult<Option<InodeUse<dyn Inode>>> {
- if let FileType::Inode(inode_file) = &**self {
- Ok(Some(inode_file.dentry.get_inode()?))
- } else {
- Ok(None)
- }
- }
- pub async fn getdents(&self, buffer: &mut dyn Buffer) -> KResult<()> {
- let FileType::Inode(inode_file) = &**self else {
- return Err(ENOTDIR);
- };
- let mut cursor = inode_file.cursor.lock().await;
- let nread = inode_file
- .dentry
- .readdir(*cursor, |filename, ino| {
- // + 1 for filename length padding '\0', + 1 for d_type.
- let real_record_len = core::mem::size_of::<UserDirent>() + filename.len() + 2;
- if buffer.available() < real_record_len {
- return Ok(false);
- }
- let record = UserDirent {
- d_ino: ino.as_raw() as u32,
- d_off: 0,
- d_reclen: real_record_len as u16,
- d_name: [0; 0],
- };
- buffer.copy(&record)?.ok_or(EFAULT)?;
- buffer.fill(filename)?.ok_or(EFAULT)?;
- buffer.fill(&[0, 0])?.ok_or(EFAULT)?;
- Ok(true)
- })
- .await??;
- *cursor += nread;
- Ok(())
- }
- pub async fn getdents64(&self, buffer: &mut dyn Buffer) -> KResult<()> {
- let FileType::Inode(inode_file) = &**self else {
- return Err(ENOTDIR);
- };
- let mut cursor = inode_file.cursor.lock().await;
- let nread = inode_file
- .dentry
- .readdir(*cursor, |filename, ino| {
- // Filename length + 1 for padding '\0'
- let real_record_len = core::mem::size_of::<UserDirent64>() + filename.len() + 1;
- if buffer.available() < real_record_len {
- return Ok(false);
- }
- let record = UserDirent64 {
- d_ino: ino.as_raw(),
- d_off: 0,
- d_reclen: real_record_len as u16,
- d_type: 0,
- d_name: [0; 0],
- };
- buffer.copy(&record)?.ok_or(EFAULT)?;
- buffer.fill(filename)?.ok_or(EFAULT)?;
- buffer.fill(&[0])?.ok_or(EFAULT)?;
- Ok(true)
- })
- .await??;
- *cursor += nread;
- Ok(())
- }
- pub async fn seek(&self, option: SeekOption) -> KResult<usize> {
- let FileType::Inode(inode_file) = &**self else {
- return Err(ESPIPE);
- };
- let mut cursor = inode_file.cursor.lock().await;
- let new_cursor = match option {
- SeekOption::Current(off) => cursor.checked_add_signed(off).ok_or(EOVERFLOW)?,
- SeekOption::Set(n) => n,
- SeekOption::End(off) => {
- let inode = inode_file.dentry.get_inode()?;
- let size = inode.info().lock().size as usize;
- size.checked_add_signed(off).ok_or(EOVERFLOW)?
- }
- };
- *cursor = new_cursor;
- Ok(new_cursor)
- }
- pub fn statx(&self, buffer: &mut StatX, mask: u32) -> KResult<()> {
- if let FileType::Inode(inode) = &**self {
- inode.dentry.statx(buffer, mask)
- } else {
- Err(EBADF)
- }
- }
- pub fn as_path(&self) -> Option<&Arc<Dentry>> {
- if let FileType::Inode(inode_file) = &**self {
- Some(&inode_file.dentry)
- } else {
- None
- }
- }
- }
|