123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629 |
- use crate::{
- io::copy_offset_count,
- kernel::vfs::{
- dentry::Dentry,
- inode::{Ino, Inode, InodeCache, InodeData, Mode},
- mount::{register_filesystem, Mount, MountCreator, MS_RDONLY},
- s_isblk, s_ischr,
- vfs::Vfs,
- DevId, ReadDirCallback, TimeSpec,
- },
- prelude::*,
- };
- use alloc::sync::{Arc, Weak};
- use bindings::{
- fs::{D_DIRECTORY, D_LOADED, D_PRESENT, D_SYMLINK},
- EINVAL, EIO, EISDIR, ENODEV, ENOTDIR, EROFS, S_IFBLK, S_IFCHR, S_IFDIR,
- S_IFLNK, S_IFREG,
- };
- type TmpFsFile = Vec<u8>;
- type TmpFsDirectory = Vec<(Ino, String)>;
- enum TmpFsData {
- File(TmpFsFile),
- Device(DevId),
- Directory(TmpFsDirectory),
- Symlink(String),
- }
- struct TmpFsInode {
- idata: Mutex<InodeData>,
- fsdata: Mutex<TmpFsData>,
- vfs: Weak<Mutex<TmpFs>>,
- }
- impl TmpFsInode {
- pub fn new(
- idata: InodeData,
- fsdata: TmpFsData,
- vfs: Weak<Mutex<TmpFs>>,
- ) -> Arc<Self> {
- Arc::new(Self {
- idata: Mutex::new(idata),
- fsdata: Mutex::new(fsdata),
- vfs,
- })
- }
- fn vfs(&self) -> KResult<Arc<Mutex<TmpFs>>> {
- self.vfs.upgrade().ok_or(EIO)
- }
- /// Link a child inode to the parent inode
- ///
- /// # Safety
- /// If parent is not a directory, this function will panic
- ///
- fn link_unchecked(
- parent_fsdata: &mut TmpFsData,
- parent_idata: &mut InodeData,
- name: &str,
- child_idata: &mut InodeData,
- ) {
- match parent_fsdata {
- TmpFsData::Directory(dir) => {
- dir.push((child_idata.ino, String::from(name)));
- parent_idata.size += size_of::<TmpFsData>() as u64;
- child_idata.nlink += 1;
- }
- _ => panic!("Parent is not a directory"),
- }
- }
- /// Link a inode to itself
- ///
- /// # Safety
- /// If the inode is not a directory, this function will panic
- ///
- fn self_link_unchecked(
- fsdata: &mut TmpFsData,
- idata: &mut InodeData,
- name: &str,
- ) {
- match fsdata {
- TmpFsData::Directory(dir) => {
- dir.push((idata.ino, String::from(name)));
- idata.size += size_of::<TmpFsData>() as u64;
- idata.nlink += 1;
- }
- _ => panic!("parent is not a directory"),
- }
- }
- }
- impl Inode for TmpFsInode {
- fn idata(&self) -> &Mutex<InodeData> {
- &self.idata
- }
- fn as_any(&self) -> &dyn Any {
- self
- }
- fn readdir(
- &self,
- offset: usize,
- callback: &mut ReadDirCallback,
- ) -> KResult<usize> {
- let _vfs = self.vfs.upgrade().ok_or(EIO)?;
- let vfs = _vfs.lock();
- match *self.fsdata.lock() {
- TmpFsData::Directory(ref dir) => {
- let icache = vfs.icache.lock();
- let mut nread = 0;
- for (ino, filename) in dir.iter().skip(offset) {
- let inode = icache.get(*ino).unwrap();
- let ret =
- callback(filename, &inode, &inode.idata().lock(), 0)?;
- if ret != 0 {
- break;
- }
- nread += 1;
- }
- Ok(nread)
- }
- _ => Err(ENOTDIR),
- }
- }
- fn read(&self, buffer: &mut [u8], offset: usize) -> KResult<usize> {
- self.vfs()?;
- match *self.fsdata.lock() {
- TmpFsData::File(ref file) => Ok(copy_offset_count(
- file,
- buffer,
- offset as usize,
- buffer.len(),
- )),
- _ => Err(EINVAL),
- }
- }
- fn write(&self, buffer: &[u8], offset: usize) -> KResult<usize> {
- if self.vfs()?.lock().readonly {
- return Err(EROFS);
- }
- match *self.fsdata.lock() {
- TmpFsData::File(ref mut file) => {
- if file.len() < offset + buffer.len() {
- file.resize(offset + buffer.len(), 0);
- }
- file[offset..offset + buffer.len()].copy_from_slice(&buffer);
- self.idata.lock().size = file.len() as u64;
- Ok(buffer.len())
- }
- _ => Err(EINVAL),
- }
- }
- fn creat(&self, at: &mut Dentry, mode: Mode) -> KResult<()> {
- let _vfs = self.vfs()?;
- let mut vfs = _vfs.lock();
- if vfs.readonly {
- return Err(EROFS);
- }
- {
- let self_fsdata = self.fsdata.lock();
- match *self_fsdata {
- TmpFsData::Directory(_) => {}
- _ => return Err(ENOTDIR),
- }
- }
- let ino = vfs.assign_ino();
- let file = {
- let mut locked_icache = vfs.icache.lock();
- let file = TmpFsInode::new(
- InodeData {
- ino,
- nlink: 0,
- size: 0,
- mode: S_IFREG | (mode & 0o777),
- atime: TimeSpec::new(),
- mtime: TimeSpec::new(),
- ctime: TimeSpec::new(),
- uid: 0,
- gid: 0,
- },
- TmpFsData::File(vec![]),
- locked_icache.get_vfs(),
- );
- locked_icache.submit(ino, file.clone())?;
- file
- };
- {
- let mut self_fsdata = self.fsdata.lock();
- let mut self_idata = self.idata.lock();
- let mut child_idata = file.idata.lock();
- TmpFsInode::link_unchecked(
- &mut self_fsdata,
- &mut self_idata,
- at.get_name(),
- &mut child_idata,
- );
- }
- at.save_inode(file);
- at.flags |= D_PRESENT;
- Ok(())
- }
- fn mknod(&self, at: &mut Dentry, mode: Mode, dev: DevId) -> KResult<()> {
- let _vfs = self.vfs()?;
- let mut vfs = _vfs.lock();
- if vfs.readonly {
- return Err(EROFS);
- }
- if !s_ischr(mode) && !s_isblk(mode) {
- return Err(EINVAL);
- }
- {
- let self_fsdata = self.fsdata.lock();
- match *self_fsdata {
- TmpFsData::Directory(_) => {}
- _ => return Err(ENOTDIR),
- }
- }
- let ino = vfs.assign_ino();
- let file = {
- let mut locked_icache = vfs.icache.lock();
- let file = TmpFsInode::new(
- InodeData {
- ino,
- nlink: 0,
- size: 0,
- mode: mode & (0o777 | S_IFBLK | S_IFCHR),
- atime: TimeSpec::new(),
- mtime: TimeSpec::new(),
- ctime: TimeSpec::new(),
- uid: 0,
- gid: 0,
- },
- TmpFsData::Device(dev),
- locked_icache.get_vfs(),
- );
- locked_icache.submit(ino, file.clone())?;
- file
- };
- {
- let mut self_fsdata = self.fsdata.lock();
- let mut self_idata = self.idata.lock();
- let mut child_idata = file.idata.lock();
- TmpFsInode::link_unchecked(
- &mut self_fsdata,
- &mut self_idata,
- at.get_name(),
- &mut child_idata,
- );
- }
- at.save_inode(file);
- at.flags |= D_PRESENT;
- Ok(())
- }
- fn mkdir(&self, at: &mut Dentry, mode: Mode) -> KResult<()> {
- let _vfs = self.vfs()?;
- let mut vfs = _vfs.lock();
- if vfs.readonly {
- return Err(EROFS);
- }
- {
- let self_fsdata = self.fsdata.lock();
- match *self_fsdata {
- TmpFsData::Directory(_) => {}
- _ => return Err(ENOTDIR),
- }
- }
- let ino = vfs.assign_ino();
- let dir = {
- let mut locked_icache = vfs.icache.lock();
- let file = TmpFsInode::new(
- InodeData {
- ino,
- nlink: 0,
- size: 0,
- mode: S_IFDIR | (mode & 0o777),
- atime: TimeSpec::new(),
- mtime: TimeSpec::new(),
- ctime: TimeSpec::new(),
- uid: 0,
- gid: 0,
- },
- TmpFsData::Directory(vec![]),
- locked_icache.get_vfs(),
- );
- locked_icache.submit(ino, file.clone())?;
- file
- };
- {
- let mut self_fsdata = self.fsdata.lock();
- let mut self_idata = self.idata.lock();
- let mut child_fsdata = dir.fsdata.lock();
- let mut child_idata = dir.idata.lock();
- TmpFsInode::link_unchecked(
- &mut child_fsdata,
- &mut child_idata,
- "..",
- &mut self_idata,
- );
- TmpFsInode::self_link_unchecked(
- &mut child_fsdata,
- &mut child_idata,
- ".",
- );
- TmpFsInode::link_unchecked(
- &mut self_fsdata,
- &mut self_idata,
- at.get_name(),
- &mut child_idata,
- );
- }
- at.save_inode(dir);
- // TODO: try remove D_LOADED and check if it works
- at.flags |= D_PRESENT | D_DIRECTORY | D_LOADED;
- Ok(())
- }
- fn symlink(&self, at: &mut Dentry, target: &str) -> KResult<()> {
- let _vfs = self.vfs()?;
- let mut vfs = _vfs.lock();
- if vfs.readonly {
- return Err(EROFS);
- }
- {
- let self_fsdata = self.fsdata.lock();
- match *self_fsdata {
- TmpFsData::Directory(_) => {}
- _ => return Err(ENOTDIR),
- }
- }
- let ino = vfs.assign_ino();
- let file = {
- let mut locked_icache = vfs.icache.lock();
- let file = TmpFsInode::new(
- InodeData {
- ino,
- nlink: 0,
- size: target.len() as u64,
- mode: S_IFLNK | 0o777,
- atime: TimeSpec::new(),
- mtime: TimeSpec::new(),
- ctime: TimeSpec::new(),
- uid: 0,
- gid: 0,
- },
- TmpFsData::Symlink(String::from(target)),
- locked_icache.get_vfs(),
- );
- locked_icache.submit(ino, file.clone())?;
- file
- };
- {
- let mut self_fsdata = self.fsdata.lock();
- let mut self_idata = self.idata.lock();
- let mut child_idata = file.idata.lock();
- TmpFsInode::link_unchecked(
- &mut self_fsdata,
- &mut self_idata,
- at.get_name(),
- &mut child_idata,
- );
- }
- at.save_inode(file);
- at.flags |= D_PRESENT | D_SYMLINK;
- Ok(())
- }
- fn readlink(&self, buffer: &mut [u8]) -> KResult<usize> {
- match *self.fsdata.lock() {
- TmpFsData::Symlink(ref target) => {
- let len = target.len().min(buffer.len());
- buffer[..len].copy_from_slice(target.as_bytes());
- Ok(len)
- }
- _ => Err(EINVAL),
- }
- }
- fn devid(&self) -> KResult<DevId> {
- match *self.fsdata.lock() {
- TmpFsData::Device(dev) => Ok(dev),
- _ => Err(ENODEV),
- }
- }
- fn truncate(&self, length: usize) -> KResult<()> {
- if self.vfs()?.lock().readonly {
- return Err(EROFS);
- }
- match *self.fsdata.lock() {
- TmpFsData::File(ref mut file) => {
- file.resize(length, 0);
- self.idata.lock().size = length as u64;
- Ok(())
- }
- _ => Err(EINVAL),
- }
- }
- fn unlink(&self, at: &mut Dentry) -> KResult<()> {
- if self.vfs()?.lock().readonly {
- return Err(EROFS);
- }
- let file = at.get_inode_clone();
- let file = file.as_any().downcast_ref::<TmpFsInode>().unwrap();
- match *file.fsdata.lock() {
- TmpFsData::Directory(_) => return Err(EISDIR),
- _ => {}
- }
- let file_data = file.idata.lock();
- let mut self_fsdata = self.fsdata.lock();
- match *self_fsdata {
- TmpFsData::Directory(ref mut dirs) => {
- let idx = 'label: {
- for (idx, (ino, _)) in dirs.iter().enumerate() {
- if *ino != file_data.ino {
- continue;
- }
- break 'label idx;
- }
- panic!("file not found in directory");
- };
- drop(file_data);
- {
- self.idata.lock().size -= size_of::<TmpFsData>() as u64;
- file.idata.lock().nlink -= 1;
- }
- dirs.remove(idx);
- // TODO!!!: CHANGE THIS SINCE IT WILL CAUSE MEMORY LEAK
- // AND WILL CREATE A RACE CONDITION
- at.flags &= !D_PRESENT;
- at.take_inode();
- at.take_fs();
- Ok(())
- }
- _ => return Err(ENOTDIR),
- }
- }
- fn vfs_weak(&self) -> Weak<Mutex<dyn Vfs>> {
- self.vfs.clone()
- }
- fn vfs_strong(&self) -> Option<Arc<Mutex<dyn Vfs>>> {
- match self.vfs.upgrade() {
- Some(vfs) => Some(vfs),
- None => None,
- }
- }
- }
- /// # Lock order
- /// vfs -> icache -> fsdata -> data
- struct TmpFs {
- icache: Mutex<InodeCache<TmpFs>>,
- next_ino: Ino,
- readonly: bool,
- }
- impl TmpFs {
- fn assign_ino(&mut self) -> Ino {
- let ino = self.next_ino;
- self.next_ino += 1;
- ino
- }
- pub fn create(
- readonly: bool,
- ) -> KResult<(Arc<Mutex<TmpFs>>, Arc<TmpFsInode>)> {
- let tmpfs = Arc::new(Mutex::new(Self {
- icache: Mutex::new(InodeCache::new()),
- next_ino: 1,
- readonly,
- }));
- let root_inode = {
- let locked_tmpfs = tmpfs.lock();
- let mut locked_icache = locked_tmpfs.icache.lock();
- locked_icache.set_vfs(Arc::downgrade(&tmpfs));
- let file = TmpFsInode::new(
- InodeData {
- ino: 0,
- nlink: 0,
- size: 0,
- mode: S_IFDIR | 0o755,
- atime: TimeSpec::new(),
- mtime: TimeSpec::new(),
- ctime: TimeSpec::new(),
- uid: 0,
- gid: 0,
- },
- TmpFsData::Directory(vec![]),
- locked_icache.get_vfs(),
- );
- locked_icache.submit(0, file.clone())?;
- file
- };
- {
- let mut fsdata = root_inode.fsdata.lock();
- let mut idata = root_inode.idata.lock();
- TmpFsInode::self_link_unchecked(&mut fsdata, &mut idata, ".");
- TmpFsInode::self_link_unchecked(&mut fsdata, &mut idata, "..");
- }
- Ok((tmpfs, root_inode))
- }
- }
- impl Vfs for TmpFs {
- fn io_blksize(&self) -> usize {
- 4096
- }
- fn fs_devid(&self) -> DevId {
- 2
- }
- fn as_any(&self) -> &dyn Any {
- self
- }
- }
- struct TmpFsMountCreator;
- impl MountCreator for TmpFsMountCreator {
- fn create_mount(
- &self,
- _source: &str,
- flags: u64,
- _data: &[u8],
- ) -> KResult<Mount> {
- let (fs, root_inode) = TmpFs::create(flags & MS_RDONLY != 0)?;
- Ok(Mount::new(fs, root_inode))
- }
- }
- pub fn init() {
- register_filesystem("tmpfs", Box::new(TmpFsMountCreator)).unwrap();
- }
|