1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
#![no_std]
//! This crate contains an implementation of an in-memory filesystem backed by MappedPages from the memory crate
//! This crate allocates memory at page-size granularity, so it's inefficient with memory when creating small files
//! Currently, the read and write operations of the RamFile follows the interface of the std::io read/write operations of the Rust standard library
// #[macro_use] extern crate log;
extern crate alloc;
extern crate spin;
extern crate fs_node;
extern crate memory;
extern crate irq_safety;
extern crate io;
use alloc::string::String;
use fs_node::{DirRef, WeakDirRef, File, FsNode};
use memory::{MappedPages, get_kernel_mmi_ref, allocate_pages_by_bytes, PteFlags};
use alloc::sync::Arc;
use spin::Mutex;
use fs_node::{FileOrDir, FileRef};
use io::{ByteReader, ByteWriter, IoError, KnownLength};
/// The struct that represents a file in memory that is backed by MappedPages
pub struct MemFile {
/// The name of the file.
name: String,
/// The length in bytes of the file.
/// Note that this is not the same as the capacity of its underlying MappedPages object.
len: usize,
/// The underlying contents of this file in memory.
mp: MappedPages,
/// The parent directory that contains this file.
parent: WeakDirRef,
}
impl MemFile {
/// Allocates writable memory space for the given `contents` and creates a new file containing that content in the given `parent` directory.
pub fn create(name: String, parent: &DirRef) -> Result<FileRef, &'static str> {
let new_file = Self::from_mapped_pages(MappedPages::empty(), name, 0, parent)?;
Ok(new_file)
}
/// Creates a new `MemFile` in the given `parent` directory with the contents of the given `mapped_pages`.
pub fn from_mapped_pages(mapped_pages: MappedPages, name: String, len: usize, parent: &DirRef) -> Result<FileRef, &'static str> {
let memfile = MemFile {
name,
len,
mp: mapped_pages,
parent: Arc::downgrade(parent),
};
let file_ref = Arc::new(Mutex::new(memfile)) as FileRef;
parent.lock().insert(FileOrDir::File(file_ref.clone()))?; // adds the newly created file to the tree
Ok(file_ref)
}
}
impl ByteReader for MemFile {
// read will throw an error if the read offset extends past the end of the file
fn read_at(&mut self, buffer: &mut [u8], offset: usize) -> Result<usize, IoError> {
if offset >= self.len {
return Err(IoError::InvalidInput);
}
// read from the offset until the end of the file, but not more than the buffer length
let read_bytes = core::cmp::min(self.len - offset, buffer.len());
buffer[..read_bytes].copy_from_slice(
self.mp.as_slice(offset, read_bytes).map_err(IoError::from)?
);
Ok(read_bytes)
}
}
impl ByteWriter for MemFile {
fn write_at(&mut self, buffer: &[u8], offset: usize) -> Result<usize, IoError> {
// error out if the underlying mapped pages are already allocated and not writeable
if !self.mp.flags().is_writable() && self.mp.size_in_bytes() != 0 {
return Err(IoError::from("MemFile::write(): existing MappedPages were not writable"));
}
let end = buffer.len() + offset;
// check to see if we can fit the write buffer into the existing mapped pages region
if end <= self.mp.size_in_bytes() {
let dest_slice = self.mp.as_slice_mut::<u8>(offset, buffer.len())?;
// actually perform the write operation
dest_slice.copy_from_slice(buffer);
// if the buffer written into the mapped pages exceeds the current size, we set the new size equal to
// this value, otherwise, the size remains the same
if end > self.len {
self.len = end;
}
Ok(buffer.len()) // we wrote all of the requested bytes successfully
}
// if not, we need to reallocate a new mapped pages
else {
// If the mapped pages are empty (this is the first allocation), we make them writable
let prev_flags = if self.mp.size_in_bytes() == 0 {
PteFlags::new().valid(true).writable(true).into()
}
// Otherwise, use the existing mapped pages flags
else {
self.mp.flags()
};
let kernel_mmi_ref = get_kernel_mmi_ref().ok_or("KERNEL_MMI was not yet initialized!")?;
let pages = allocate_pages_by_bytes(end).ok_or("could not allocate pages")?;
let mut new_mapped_pages = kernel_mmi_ref.lock().page_table.map_allocated_pages(pages, prev_flags)?;
// first, we need to copy over the bytes from the previous mapped pages
{
// copy_limit copies bytes to min(the write offset, all the bytes of the existing mapped pages)
// The write does not overlap with existing content, so we copy all existing content
let copy_limit = if offset > self.len {
self.len
} else { // Otherwise, we only copy up to where the overlap begins
offset
};
let existing_bytes = self.mp.as_slice(0, copy_limit)?;
let copy_slice = new_mapped_pages.as_slice_mut::<u8>(0, copy_limit)?;
copy_slice.copy_from_slice(existing_bytes);
}
// second, we write the new content into the reallocated mapped pages
{
let dest_slice = new_mapped_pages.as_slice_mut::<u8>(offset, buffer.len())?;
dest_slice.copy_from_slice(buffer); // writes the desired contents into the correct area in the mapped page
}
self.mp = new_mapped_pages;
self.len = end;
Ok(buffer.len())
}
}
fn flush(&mut self) -> Result<(), IoError> { Ok(()) }
}
impl KnownLength for MemFile {
fn len(&self) -> usize {
self.len
}
}
impl File for MemFile {
fn as_mapping(&self) -> Result<&MappedPages, &'static str> {
Ok(&self.mp)
}
}
impl FsNode for MemFile {
fn get_name(&self) -> String {
self.name.clone()
}
fn get_parent_dir(&self) -> Option<DirRef> {
self.parent.upgrade()
}
fn set_parent_dir(&mut self, new_parent: WeakDirRef) {
self.parent = new_parent;
}
}