| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523 |
- use super::{
- dentry::Dentry,
- inode::{Mode, WriteOffset},
- s_isblk, s_isdir, s_isreg,
- };
- use crate::{
- io::{Buffer, BufferFill, ByteBuffer, Chunks, IntoStream},
- kernel::{
- constants::{TCGETS, TCSETS, TIOCGPGRP, TIOCGWINSZ, TIOCSPGRP},
- mem::{paging::Page, AsMemoryBlock as _},
- task::Thread,
- terminal::{Terminal, TerminalIORequest},
- user::{UserPointer, UserPointerMut},
- CharDevice,
- },
- prelude::*,
- sync::CondVar,
- };
- use crate::{
- io::{Stream, StreamRead},
- kernel::constants::{
- EBADF, EFAULT, EINTR, EINVAL, ENOTDIR, ENOTTY, EOVERFLOW, EPIPE, ESPIPE, S_IFMT,
- },
- };
- use alloc::{collections::vec_deque::VecDeque, sync::Arc};
- use bitflags::bitflags;
- use core::{ops::ControlFlow, sync::atomic::Ordering};
- use eonix_runtime::task::Task;
- use eonix_sync::Mutex;
- use posix_types::{signal::Signal, stat::StatX};
- pub struct InodeFile {
- read: bool,
- write: bool,
- append: bool,
- /// Only a few modes those won't possibly change are cached here to speed up file operations.
- /// Specifically, `S_IFMT` masked bits.
- mode: Mode,
- cursor: Mutex<usize>,
- dentry: Arc<Dentry>,
- }
- pub struct PipeInner {
- buffer: VecDeque<u8>,
- read_closed: bool,
- write_closed: bool,
- }
- pub struct Pipe {
- inner: Mutex<PipeInner>,
- cv_read: CondVar,
- cv_write: CondVar,
- }
- pub struct PipeReadEnd {
- pipe: Arc<Pipe>,
- }
- pub struct PipeWriteEnd {
- pipe: Arc<Pipe>,
- }
- pub struct TerminalFile {
- terminal: Arc<Terminal>,
- }
- // TODO: We should use `File` as the base type, instead of `Arc<File>`
- // If we need shared states, like for `InodeFile`, the files themselves should
- // have their own shared semantics. All `File` variants will just keep the
- // `Clone` semantics.
- //
- // e.g. The `CharDevice` itself is stateless.
- pub enum File {
- Inode(InodeFile),
- PipeRead(PipeReadEnd),
- PipeWrite(PipeWriteEnd),
- TTY(TerminalFile),
- CharDev(Arc<CharDevice>),
- }
- pub enum SeekOption {
- Set(usize),
- Current(isize),
- End(isize),
- }
- bitflags! {
- pub struct PollEvent: u16 {
- const Readable = 0x0001;
- }
- }
- impl Drop for PipeReadEnd {
- fn drop(&mut self) {
- self.pipe.close_read();
- }
- }
- impl Drop for PipeWriteEnd {
- fn drop(&mut self) {
- self.pipe.close_write();
- }
- }
- fn send_sigpipe_to_current() {
- let current = Thread::current();
- current.raise(Signal::SIGPIPE);
- }
- impl Pipe {
- const PIPE_SIZE: usize = 4096;
- pub fn new() -> Arc<Self> {
- Arc::new(Self {
- inner: Mutex::new(PipeInner {
- buffer: VecDeque::with_capacity(Self::PIPE_SIZE),
- read_closed: false,
- write_closed: false,
- }),
- cv_read: CondVar::new(),
- cv_write: CondVar::new(),
- })
- }
- /// # Return
- /// `(read_end, write_end)`
- pub fn split(self: &Arc<Self>) -> (Arc<File>, Arc<File>) {
- (
- Arc::new(File::PipeRead(PipeReadEnd { pipe: self.clone() })),
- Arc::new(File::PipeWrite(PipeWriteEnd { pipe: self.clone() })),
- )
- }
- fn close_read(&self) {
- let mut inner = Task::block_on(self.inner.lock());
- if inner.read_closed {
- return;
- }
- inner.read_closed = true;
- self.cv_write.notify_all();
- }
- fn close_write(&self) {
- let mut inner = Task::block_on(self.inner.lock());
- if inner.write_closed {
- return;
- }
- inner.write_closed = true;
- self.cv_read.notify_all();
- }
- async fn read(&self, buffer: &mut dyn Buffer) -> KResult<usize> {
- let mut inner = self.inner.lock().await;
- while !inner.write_closed && inner.buffer.is_empty() {
- inner = self.cv_read.wait(inner).await;
- if Thread::current().signal_list.has_pending_signal() {
- return Err(EINTR);
- }
- }
- let (data1, data2) = inner.buffer.as_slices();
- let nread = buffer.fill(data1)?.allow_partial() + buffer.fill(data2)?.allow_partial();
- inner.buffer.drain(..nread);
- self.cv_write.notify_all();
- Ok(nread)
- }
- async fn write_atomic(&self, data: &[u8]) -> KResult<usize> {
- let mut inner = self.inner.lock().await;
- if inner.read_closed {
- send_sigpipe_to_current();
- return Err(EPIPE);
- }
- while inner.buffer.len() + data.len() > Self::PIPE_SIZE {
- inner = self.cv_write.wait(inner).await;
- if Thread::current().signal_list.has_pending_signal() {
- return Err(EINTR);
- }
- if inner.read_closed {
- send_sigpipe_to_current();
- return Err(EPIPE);
- }
- }
- inner.buffer.extend(data);
- self.cv_read.notify_all();
- return Ok(data.len());
- }
- async fn write(&self, stream: &mut dyn Stream) -> KResult<usize> {
- let mut buffer = [0; Self::PIPE_SIZE];
- let mut total = 0;
- while let Some(data) = stream.poll_data(&mut buffer)? {
- let nwrote = self.write_atomic(data).await?;
- total += nwrote;
- if nwrote != data.len() {
- break;
- }
- }
- Ok(total)
- }
- }
- #[derive(Copy, Clone, Debug)]
- #[repr(C, packed)]
- struct UserDirent64 {
- /// Inode number
- d_ino: u64,
- /// Implementation defined. We ignore it
- d_off: u64,
- /// Length of this record
- d_reclen: u16,
- /// File type. Set to 0
- d_type: u8,
- /// Filename with a padding '\0'
- d_name: [u8; 0],
- }
- /// File type is at offset `d_reclen - 1`. Set it to 0
- #[derive(Copy, Clone, Debug)]
- #[repr(C, packed)]
- struct UserDirent {
- /// Inode number
- d_ino: u32,
- /// Implementation defined. We ignore it
- d_off: u32,
- /// Length of this record
- d_reclen: u16,
- /// Filename with a padding '\0'
- d_name: [u8; 0],
- }
- impl InodeFile {
- pub fn new(dentry: Arc<Dentry>, rwa: (bool, bool, bool)) -> Arc<File> {
- // SAFETY: `dentry` used to create `InodeFile` is valid.
- // SAFETY: `mode` should never change with respect to the `S_IFMT` fields.
- let cached_mode = dentry
- .get_inode()
- .expect("`dentry` is invalid")
- .mode
- .load(Ordering::Relaxed)
- & S_IFMT;
- Arc::new(File::Inode(InodeFile {
- dentry,
- read: rwa.0,
- write: rwa.1,
- append: rwa.2,
- mode: cached_mode,
- cursor: Mutex::new(0),
- }))
- }
- fn seek(&self, option: SeekOption) -> KResult<usize> {
- let mut cursor = Task::block_on(self.cursor.lock());
- 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 = self.dentry.get_inode()?;
- let size = inode.size.load(Ordering::Relaxed) as usize;
- size.checked_add_signed(off).ok_or(EOVERFLOW)?
- }
- };
- *cursor = new_cursor;
- Ok(new_cursor)
- }
- fn write(&self, stream: &mut dyn Stream) -> KResult<usize> {
- if !self.write {
- return Err(EBADF);
- }
- let mut cursor = Task::block_on(self.cursor.lock());
- if self.append {
- let nwrote = self.dentry.write(stream, WriteOffset::End(&mut cursor))?;
- Ok(nwrote)
- } else {
- let nwrote = self.dentry.write(stream, WriteOffset::Position(*cursor))?;
- *cursor += nwrote;
- Ok(nwrote)
- }
- }
- fn read(&self, buffer: &mut dyn Buffer) -> KResult<usize> {
- if !self.read {
- return Err(EBADF);
- }
- let mut cursor = Task::block_on(self.cursor.lock());
- let nread = self.dentry.read(buffer, *cursor)?;
- *cursor += nread;
- Ok(nread)
- }
- fn getdents64(&self, buffer: &mut dyn Buffer) -> KResult<()> {
- let mut cursor = Task::block_on(self.cursor.lock());
- let nread = self.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(ControlFlow::Break(()));
- }
- let record = UserDirent64 {
- d_ino: ino,
- 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(ControlFlow::Continue(()))
- })?;
- *cursor += nread;
- Ok(())
- }
- fn getdents(&self, buffer: &mut dyn Buffer) -> KResult<()> {
- let mut cursor = Task::block_on(self.cursor.lock());
- let nread = self.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(ControlFlow::Break(()));
- }
- let record = UserDirent {
- d_ino: ino 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(ControlFlow::Continue(()))
- })?;
- *cursor += nread;
- Ok(())
- }
- }
- impl TerminalFile {
- pub fn new(tty: Arc<Terminal>) -> Arc<File> {
- Arc::new(File::TTY(TerminalFile { terminal: tty }))
- }
- async fn read(&self, buffer: &mut dyn Buffer) -> KResult<usize> {
- self.terminal.read(buffer).await
- }
- fn write(&self, stream: &mut dyn Stream) -> KResult<usize> {
- stream.read_till_end(&mut [0; 128], |data| {
- self.terminal.write(data);
- Ok(())
- })
- }
- async fn poll(&self, event: PollEvent) -> KResult<PollEvent> {
- if !event.contains(PollEvent::Readable) {
- unimplemented!("Poll event not supported.")
- }
- self.terminal.poll_in().await.map(|_| PollEvent::Readable)
- }
- fn ioctl(&self, request: usize, arg3: usize) -> KResult<()> {
- Task::block_on(self.terminal.ioctl(match request as u32 {
- TCGETS => TerminalIORequest::GetTermios(UserPointerMut::new_vaddr(arg3)?),
- TCSETS => TerminalIORequest::SetTermios(UserPointer::new_vaddr(arg3)?),
- TIOCGPGRP => TerminalIORequest::GetProcessGroup(UserPointerMut::new_vaddr(arg3)?),
- TIOCSPGRP => TerminalIORequest::SetProcessGroup(UserPointer::new_vaddr(arg3)?),
- TIOCGWINSZ => TerminalIORequest::GetWindowSize(UserPointerMut::new_vaddr(arg3)?),
- _ => return Err(EINVAL),
- }))
- }
- }
- impl File {
- pub async fn read(&self, buffer: &mut dyn Buffer) -> KResult<usize> {
- match self {
- File::Inode(inode) => inode.read(buffer),
- File::PipeRead(pipe) => pipe.pipe.read(buffer).await,
- File::TTY(tty) => tty.read(buffer).await,
- File::CharDev(device) => device.read(buffer),
- _ => Err(EBADF),
- }
- }
- // TODO
- // /// Read from the file into the given buffers.
- // ///
- // /// Reads are atomic, not intermingled with other reads or writes.
- // pub fn readv<'r, 'i, I: Iterator<Item = &'i mut dyn Buffer>>(
- // &'r self,
- // buffers: I,
- // ) -> KResult<usize> {
- // match self {
- // File::Inode(inode) => inode.readv(buffers),
- // File::PipeRead(pipe) => pipe.pipe.readv(buffers),
- // _ => Err(EBADF),
- // }
- // }
- pub async fn write(&self, stream: &mut dyn Stream) -> KResult<usize> {
- match self {
- File::Inode(inode) => inode.write(stream),
- File::PipeWrite(pipe) => pipe.pipe.write(stream).await,
- File::TTY(tty) => tty.write(stream),
- File::CharDev(device) => device.write(stream),
- _ => Err(EBADF),
- }
- }
- pub fn seek(&self, option: SeekOption) -> KResult<usize> {
- match self {
- File::Inode(inode) => inode.seek(option),
- _ => Err(ESPIPE),
- }
- }
- pub fn getdents(&self, buffer: &mut dyn Buffer) -> KResult<()> {
- match self {
- File::Inode(inode) => inode.getdents(buffer),
- _ => Err(ENOTDIR),
- }
- }
- pub fn getdents64(&self, buffer: &mut dyn Buffer) -> KResult<()> {
- match self {
- File::Inode(inode) => inode.getdents64(buffer),
- _ => Err(ENOTDIR),
- }
- }
- pub async fn sendfile(&self, dest_file: &Self, count: usize) -> KResult<usize> {
- let buffer_page = Page::alloc();
- // SAFETY: We are the only owner of the page.
- let buffer = unsafe { buffer_page.as_memblk().as_bytes_mut() };
- match self {
- File::Inode(file) if s_isblk(file.mode) || s_isreg(file.mode) => (),
- _ => return Err(EINVAL),
- }
- let mut nsent = 0;
- for (cur, len) in Chunks::new(0, count, buffer.len()) {
- if Thread::current().signal_list.has_pending_signal() {
- return if cur == 0 { Err(EINTR) } else { Ok(cur) };
- }
- let nread = self.read(&mut ByteBuffer::new(&mut buffer[..len])).await?;
- if nread == 0 {
- break;
- }
- let nwrote = dest_file.write(&mut buffer[..nread].into_stream()).await?;
- nsent += nwrote;
- if nwrote != len {
- break;
- }
- }
- Ok(nsent)
- }
- pub fn ioctl(&self, request: usize, arg3: usize) -> KResult<usize> {
- match self {
- File::TTY(tty) => tty.ioctl(request, arg3).map(|_| 0),
- _ => Err(ENOTTY),
- }
- }
- pub async fn poll(&self, event: PollEvent) -> KResult<PollEvent> {
- match self {
- File::Inode(_) => Ok(event),
- File::TTY(tty) => tty.poll(event).await,
- _ => unimplemented!("Poll event not supported."),
- }
- }
- pub fn statx(&self, buffer: &mut StatX, mask: u32) -> KResult<()> {
- match self {
- File::Inode(inode) => inode.dentry.statx(buffer, mask),
- _ => Err(EBADF),
- }
- }
- pub fn as_path(&self) -> Option<&Arc<Dentry>> {
- match self {
- File::Inode(inode_file) if s_isdir(inode_file.mode) => Some(&inode_file.dentry),
- _ => None,
- }
- }
- }
|