#![no_std]
#[macro_use] extern crate alloc;
#[macro_use] extern crate log;
extern crate spin;
extern crate fs_node;
extern crate memory;
extern crate task;
extern crate path;
extern crate root;
extern crate io;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use spin::Mutex;
use alloc::sync::Arc;
use fs_node::{DirRef, WeakDirRef, Directory, FileOrDir, File, FileRef, FsNode};
use memory::MappedPages;
use task::WeakTaskRef;
use path::{Path, PathBuf};
use io::{ByteReader, ByteWriter, KnownLength, IoError};
pub const TASKS_DIRECTORY_NAME: &str = "tasks";
pub const TASKS_DIRECTORY_PATH: &str = "/tasks";
pub fn init() -> Result<(), &'static str> {
TaskFs::create()?;
Ok(())
}
pub struct TaskFs { }
impl TaskFs {
fn create() -> Result<DirRef, &'static str> {
let root = root::get_root();
let dir_ref = Arc::new(Mutex::new(TaskFs { })) as DirRef;
root.lock().insert(FileOrDir::Dir(dir_ref.clone()))?;
Ok(dir_ref)
}
fn get_self_pointer(&self) -> Option<DirRef> {
root::get_root().lock().get_dir(&self.get_name())
}
fn get_internal(&self, node: &str) -> Result<FileOrDir, &'static str> {
let id = node.parse::<usize>().map_err(|_e| "could not parse Task ID as usize")?;
let task_ref = task::get_task(id).ok_or("No task existed for Task ID")?;
let parent_dir = self.get_self_pointer().ok_or("BUG: tasks directory wasn't in root")?;
let dir_name = id.to_string();
let task_dir = TaskDir::new(dir_name, &parent_dir, id, task_ref)?;
let boxed_task_dir = Arc::new(Mutex::new(task_dir)) as DirRef;
Ok(FileOrDir::Dir(boxed_task_dir))
}
}
impl FsNode for TaskFs {
fn get_absolute_path(&self) -> String {
String::from(TASKS_DIRECTORY_PATH)
}
fn get_name(&self) -> String {
String::from(TASKS_DIRECTORY_NAME)
}
fn get_parent_dir(&self) -> Option<DirRef> {
Some(root::get_root().clone())
}
fn set_parent_dir(&mut self, _new_parent: WeakDirRef) {
}
}
impl Directory for TaskFs {
fn insert(&mut self, _node: FileOrDir) -> Result<Option<FileOrDir>, &'static str> {
Err("cannot insert node into read-only TaskFs")
}
fn get(&self, node: &str) -> Option<FileOrDir> {
match self.get_internal(node) {
Ok(d) => Some(d),
Err(e) => {
error!("TaskFs::get() error: {:?}", e);
None
}
}
}
fn list(&self) -> Vec<String> {
let mut tasks_string = Vec::new();
for (id, _taskref) in task::all_tasks() {
tasks_string.push(format!("{id}"));
}
tasks_string
}
fn remove(&mut self, _node: &FileOrDir) -> Option<FileOrDir> {
None
}
}
pub struct TaskDir {
pub name: String,
path: PathBuf,
task_id: usize,
taskref: WeakTaskRef,
parent: DirRef,
}
impl TaskDir {
pub fn new(
name: String,
parent: &DirRef,
task_id: usize,
taskref: WeakTaskRef,
) -> Result<TaskDir, &'static str> {
let directory = TaskDir {
name,
path: PathBuf::from(format!("{TASKS_DIRECTORY_PATH}/{task_id}")),
task_id,
taskref,
parent: Arc::clone(parent),
};
Ok(directory)
}
}
impl Directory for TaskDir {
fn insert(&mut self, _node: FileOrDir) -> Result<Option<FileOrDir>, &'static str> {
Err("cannot insert node into read-only TaskFs")
}
fn get(&self, child_name: &str) -> Option<FileOrDir> {
if child_name == "taskInfo" {
let task_file = TaskFile::new(self.task_id, self.taskref.clone());
return Some(FileOrDir::File(Arc::new(Mutex::new(task_file)) as FileRef));
}
if child_name == "mmi" {
let mmi_dir = MmiDir::new(self.task_id, self.taskref.clone());
return Some(FileOrDir::Dir(Arc::new(Mutex::new(mmi_dir)) as DirRef));
}
None
}
fn list(&self) -> Vec<String> {
let children = vec!["mmi".to_string(), "taskInfo".to_string()];
children
}
fn remove(&mut self, _: &FileOrDir) -> Option<FileOrDir> {
None
}
}
impl FsNode for TaskDir {
fn get_absolute_path(&self) -> String {
self.path.clone().into()
}
fn get_name(&self) -> String {
self.name.clone()
}
fn get_parent_dir(&self) -> Option<DirRef> {
Some(self.parent.clone())
}
fn set_parent_dir(&mut self, _: WeakDirRef) {
}
}
pub struct TaskFile {
taskref: WeakTaskRef,
task_id: usize,
path: PathBuf,
}
impl TaskFile {
pub fn new(task_id: usize, taskref: WeakTaskRef) -> TaskFile {
TaskFile {
taskref,
task_id,
path: PathBuf::from(format!("{TASKS_DIRECTORY_PATH}/{task_id}/task_info")),
}
}
fn generate(&self) -> String {
let Some(taskref) = self.taskref.upgrade() else {
return String::from("Task Not Found");
};
let cpu = taskref.running_on_cpu().map(|cpu| format!("{cpu}")).unwrap_or_else(|| String::from("-"));
let pinned = &taskref.pinned_cpu().map(|pin| format!("{pin}")).unwrap_or_else(|| String::from("-"));
let task_type = if taskref.is_an_idle_task {
"I"
} else if taskref.is_application() {
"A"
} else {
" "
};
format!("{0:<10} {1}\n{2:<10} {3}\n{4:<10} {5:?}\n{6:<10} {7}\n{8:<10} {9}\n{10:<10} {11:<10}",
"name", taskref.name,
"task id", taskref.id,
"runstate", taskref.runstate(),
"cpu", cpu,
"pinned", pinned,
"task type", task_type
)
}
}
impl FsNode for TaskFile {
fn get_absolute_path(&self) -> String {
self.path.clone().into()
}
fn get_name(&self) -> String {
self.taskref.upgrade().map_or_else(
|| String::from("Task Not Found"),
|taskref| taskref.name.clone(),
)
}
fn get_parent_dir(&self) -> Option<DirRef> {
let path = PathBuf::from(format!("{}/{}", TASKS_DIRECTORY_PATH, self.task_id));
match Path::get_absolute(&path) {
Some(FileOrDir::Dir(d)) => Some(d),
_ => None,
}
}
fn set_parent_dir(&mut self, _: WeakDirRef) {
}
}
impl ByteReader for TaskFile {
fn read_at(&mut self, buf: &mut [u8], offset: usize) -> Result<usize, IoError> {
let output = self.generate();
if offset > output.len() {
return Err(IoError::InvalidInput);
}
let count = core::cmp::min(buf.len(), output.len() - offset);
buf[..count].copy_from_slice(&output.as_bytes()[offset..(offset + count)]);
Ok(count)
}
}
impl ByteWriter for TaskFile {
fn write_at(&mut self, _buffer: &[u8], _offset: usize) -> Result<usize, IoError> {
Err(IoError::from("not permitted to write task contents through the task VFS"))
}
fn flush(&mut self) -> Result<(), IoError> { Ok(()) }
}
impl KnownLength for TaskFile {
fn len(&self) -> usize {
self.generate().len()
}
}
impl File for TaskFile {
fn as_mapping(&self) -> Result<&MappedPages, &'static str> {
Err("task files are autogenerated, cannot be memory mapped")
}
}
pub struct MmiDir {
taskref: WeakTaskRef,
task_id: usize,
path: PathBuf,
}
impl MmiDir {
pub fn new(task_id: usize, taskref: WeakTaskRef) -> MmiDir {
MmiDir {
taskref,
task_id,
path: PathBuf::from(format!("{TASKS_DIRECTORY_PATH}/{task_id}/mmi")),
}
}
}
impl Directory for MmiDir {
fn insert(&mut self, _node: FileOrDir) -> Result<Option<FileOrDir>, &'static str> {
Err("cannot insert node into read-only TaskFs")
}
fn get(&self, child_name: &str) -> Option<FileOrDir> {
if child_name == "MmiInfo" {
let task_file = MmiFile::new(self.task_id, self.taskref.clone());
Some(FileOrDir::File(Arc::new(Mutex::new(task_file)) as FileRef))
} else {
None
}
}
fn list(&self) -> Vec<String> {
let children = vec!["MmiInfo".to_string()];
children
}
fn remove(&mut self, _: &FileOrDir) -> Option<FileOrDir> {
None
}
}
impl FsNode for MmiDir {
fn get_absolute_path(&self) -> String {
self.path.clone().into()
}
fn get_name(&self) -> String {
"mmi".to_string()
}
fn get_parent_dir(&self) -> Option<DirRef> {
let path = PathBuf::from(format!("{}/{}", TASKS_DIRECTORY_PATH, self.task_id));
match Path::get_absolute(&path) {
Some(FileOrDir::Dir(d)) => Some(d),
_ => None,
}
}
fn set_parent_dir(&mut self, _: WeakDirRef) {
}
}
pub struct MmiFile {
taskref: WeakTaskRef,
task_id: usize,
path: PathBuf,
}
impl MmiFile {
pub fn new(task_id: usize, taskref: WeakTaskRef) -> MmiFile {
MmiFile {
taskref,
task_id,
path: PathBuf::from(format!("{TASKS_DIRECTORY_PATH}/{task_id}/mmi/MmiInfo")),
}
}
fn generate(&self) -> String {
if let Some(taskref) = self.taskref.upgrade() {
format!("Page table: {:?}\n", taskref.mmi.lock().page_table)
} else {
String::from("Task Not Found")
}
}
}
impl FsNode for MmiFile {
fn get_absolute_path(&self) -> String {
self.path.clone().into()
}
fn get_name(&self) -> String {
"MmiInfo".to_string()
}
fn get_parent_dir(&self) -> Option<DirRef> {
let path = PathBuf::from(format!("{}/{}/mmi", TASKS_DIRECTORY_PATH, self.task_id));
match Path::get_absolute(&path) {
Some(FileOrDir::Dir(d)) => Some(d),
_ => None,
}
}
fn set_parent_dir(&mut self, _: WeakDirRef) {
}
}
impl ByteReader for MmiFile {
fn read_at(&mut self, buf: &mut [u8], offset: usize) -> Result<usize, IoError> {
let output = self.generate();
if offset > output.len() {
return Err(IoError::InvalidInput);
}
let count = core::cmp::min(buf.len(), output.len() - offset);
buf[..count].copy_from_slice(&output.as_bytes()[offset..(offset + count)]);
Ok(count)
}
}
impl ByteWriter for MmiFile {
fn write_at(&mut self, _buffer: &[u8], _offset: usize) -> Result<usize, IoError> {
Err(IoError::from("not permitted to write task contents through the task VFS"))
}
fn flush(&mut self) -> Result<(), IoError> { Ok(()) }
}
impl KnownLength for MmiFile {
fn len(&self) -> usize {
self.generate().len()
}
}
impl File for MmiFile {
fn as_mapping(&self) -> Result<&MappedPages, &'static str> {
Err("task files are autogenerated, cannot be memory mapped")
}
}