mirror of
https://github.com/XunilGroup/XunilOS.git
synced 2026-06-02 12:44:24 +02:00
Add a proper VFS in a struct which now lives on the heap with no hacky
static lifetimes, move cursor.bmp to image assets and add wallpapers, and a logo, add Github Action that auto-releases, add build_all script which just builds without running
This commit is contained in:
@@ -7,7 +7,10 @@ use crate::{
|
||||
},
|
||||
arch::KERNEL_MAPPER,
|
||||
},
|
||||
driver::{io::virtio::scan_virtio_devices, ipc::init_ipc},
|
||||
driver::{
|
||||
io::{fs::vfs::init_vfs, virtio::scan_virtio_devices},
|
||||
ipc::init_ipc,
|
||||
},
|
||||
mm::shm::init_shm,
|
||||
};
|
||||
use limine::response::{ExecutableAddressResponse, HhdmResponse, MemoryMapResponse};
|
||||
@@ -40,6 +43,7 @@ pub extern "C" fn init_aarch64(mapper: &mut AArchPageTable) {
|
||||
init_interrupts();
|
||||
init_ipc();
|
||||
init_shm();
|
||||
init_vfs();
|
||||
}
|
||||
|
||||
pub fn preinit_aarch64<'a>(
|
||||
|
||||
+52
-11
@@ -4,9 +4,11 @@ use core::sync::atomic::Ordering;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::arch::x86_64::paging::create_and_map_multiple_pages;
|
||||
use crate::driver::io::fs::vfs::vfs_write;
|
||||
use crate::driver::io::input::{InputEvent, process_input};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::driver::io::ps2::process_scancodes;
|
||||
use crate::mm::usercopy::copy_from_user;
|
||||
use alloc::vec;
|
||||
use alloc::{string::String, vec::Vec};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
@@ -204,6 +206,39 @@ fn open(path: isize, mode: isize) -> isize {
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
|
||||
fn write_fd(ptr: isize, size: isize, count: isize, fd: isize) -> isize {
|
||||
let pid = current_pid().unwrap_or(0);
|
||||
if pid == 0 {
|
||||
return -1;
|
||||
}
|
||||
|
||||
SCHEDULER
|
||||
.with_process(pid, |process| {
|
||||
let len = (size as usize).checked_mul(count as usize).ok_or(-1isize)?;
|
||||
if len == 0 {
|
||||
return Ok(0isize);
|
||||
}
|
||||
|
||||
let address_space = process.address_space.as_mut().ok_or(-1isize)?;
|
||||
|
||||
let mut buf: alloc::vec::Vec<u8> = alloc::vec![0u8; len];
|
||||
copy_from_user(
|
||||
&mut address_space.mapper,
|
||||
buf.as_mut_ptr(),
|
||||
ptr as *const u8,
|
||||
len,
|
||||
)
|
||||
.map_err(|_| -14isize)?;
|
||||
|
||||
Ok(
|
||||
vfs_write(buf.as_ptr(), size as usize, count as usize, fd as i64).unwrap_or(0)
|
||||
as isize,
|
||||
)
|
||||
})
|
||||
.unwrap_or(Err(-1))
|
||||
.unwrap_or(-1)
|
||||
}
|
||||
|
||||
fn close(fd: isize) -> isize {
|
||||
vfs_close(fd as i64) as isize
|
||||
}
|
||||
@@ -656,18 +691,22 @@ pub unsafe extern "C" fn syscall_dispatch(
|
||||
BRK => unsafe { sbrk(arg0) },
|
||||
READ => read(arg0, arg1, arg2, arg3) as isize,
|
||||
WRITE => {
|
||||
let buf_ptr = arg1 as *const u8;
|
||||
let len = arg2 as usize;
|
||||
let bytes: &[u8] = unsafe { core::slice::from_raw_parts(buf_ptr, len) };
|
||||
if let Ok(s) = core::str::from_utf8(bytes) {
|
||||
print!("{}", s);
|
||||
} else {
|
||||
for byte in bytes {
|
||||
if *byte == b'\0' {
|
||||
continue;
|
||||
if arg0 == 1 {
|
||||
let buf_ptr = arg1 as *const u8;
|
||||
let len = arg2 as usize;
|
||||
let bytes: &[u8] = unsafe { core::slice::from_raw_parts(buf_ptr, len) };
|
||||
if let Ok(s) = core::str::from_utf8(bytes) {
|
||||
print!("{}", s);
|
||||
} else {
|
||||
for byte in bytes {
|
||||
if *byte == b'\0' {
|
||||
continue;
|
||||
}
|
||||
print!("{}", *byte as char);
|
||||
}
|
||||
print!("{}", *byte as char);
|
||||
}
|
||||
} else {
|
||||
write_fd(arg0, arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
0
|
||||
@@ -678,7 +717,9 @@ pub unsafe extern "C" fn syscall_dispatch(
|
||||
EXIT => kill(current_pid().unwrap() as isize, arg0),
|
||||
SLEEP => sleep(arg0),
|
||||
EXECVE => exec(arg0),
|
||||
CLOCK_GETTIME => ((TIMER.now().elapsed() as usize) * (TIMER_FREQUENCY_HZ / 1000)) as isize,
|
||||
CLOCK_GETTIME => {
|
||||
(TIMER.now().elapsed() + TIMER.get_date_at_boot() * TIMER_FREQUENCY_HZ as u64) as isize
|
||||
}
|
||||
MAP_FRAMEBUFFER => map_framebuffer(),
|
||||
INPUT_READ => input_read(arg0 as *mut InputEvent, arg1),
|
||||
GETPID => {
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::{
|
||||
syscall::init_syscalls,
|
||||
},
|
||||
config::TIMER_FREQUENCY_HZ,
|
||||
driver::ipc::init_ipc,
|
||||
driver::{io::fs::vfs::init_vfs, ipc::init_ipc},
|
||||
mm::shm::init_shm,
|
||||
};
|
||||
|
||||
@@ -93,6 +93,7 @@ pub fn init_x86_64<'a>(
|
||||
|
||||
init_ipc();
|
||||
init_shm();
|
||||
init_vfs();
|
||||
|
||||
return mapper;
|
||||
}
|
||||
|
||||
+193
-174
@@ -1,214 +1,233 @@
|
||||
use alloc::{
|
||||
collections::btree_map::BTreeMap,
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use crate::driver::io::fs::assets::*;
|
||||
use core::ptr::{null, null_mut};
|
||||
|
||||
lazy_static! {
|
||||
static ref FILE_CONTENT: BTreeMap<&'static str, &'static [u8]> = {
|
||||
let mut map = BTreeMap::new();
|
||||
map.insert("testfile", &b"Hello, World!"[..]);
|
||||
map.insert("helloworld.elf", HELLOWORLD_ELF);
|
||||
map.insert("badapple", BADAPPLE_ELF);
|
||||
map.insert("doomgeneric", DOOM_ELF);
|
||||
map.insert("shell", SHELL_ELF);
|
||||
map.insert("doom1.wad", DOOM_WAD);
|
||||
map.insert("doom.cfg", &b""[..]);
|
||||
map.insert("default.cfg", &b""[..]);
|
||||
map
|
||||
};
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FILE {
|
||||
pub data: *const u8, // pointer to the file's data
|
||||
pub size: usize, // total size
|
||||
pub cursor: usize, // current position
|
||||
pub writable: bool, // is this a write buffer?
|
||||
pub write_buf: *mut u8, // for writable fake files
|
||||
pub write_cap: usize,
|
||||
pub name: String,
|
||||
pub size: usize,
|
||||
pub data: Vec<u8>,
|
||||
pub cursor: usize,
|
||||
pub writable: bool,
|
||||
pub fd: i64,
|
||||
}
|
||||
|
||||
impl FILE {
|
||||
pub const fn zeroed() -> FILE {
|
||||
pub fn new(name: String, data: Vec<u8>, writable: bool) -> FILE {
|
||||
FILE {
|
||||
data: null(),
|
||||
size: 0,
|
||||
name,
|
||||
data: data.clone(),
|
||||
cursor: 0,
|
||||
writable: false,
|
||||
write_buf: null_mut(),
|
||||
write_cap: 0,
|
||||
writable,
|
||||
fd: -1,
|
||||
size: data.len(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct FakeFileEntry {
|
||||
name: &'static str,
|
||||
data: &'static [u8],
|
||||
pub struct VFS {
|
||||
files: Vec<FILE>,
|
||||
next_fd: i64,
|
||||
}
|
||||
|
||||
pub type Fd = i64;
|
||||
const MAX_FD: usize = 16;
|
||||
|
||||
fn fd_ok(fd: Fd) -> bool {
|
||||
fd >= 0 && (fd as usize) < MAX_FD
|
||||
}
|
||||
|
||||
static FILES: &[FakeFileEntry] = &[
|
||||
FakeFileEntry {
|
||||
name: "testfile",
|
||||
data: b"Hello, World!",
|
||||
},
|
||||
FakeFileEntry {
|
||||
name: "helloworld.elf",
|
||||
data: HELLOWORLD_ELF,
|
||||
},
|
||||
FakeFileEntry {
|
||||
name: "badapple",
|
||||
data: BADAPPLE_ELF,
|
||||
},
|
||||
FakeFileEntry {
|
||||
name: "doomgeneric",
|
||||
data: DOOM_ELF,
|
||||
},
|
||||
FakeFileEntry {
|
||||
name: "shell",
|
||||
data: SHELL_ELF,
|
||||
},
|
||||
FakeFileEntry {
|
||||
name: "doom1.wad",
|
||||
data: DOOM_WAD,
|
||||
},
|
||||
FakeFileEntry {
|
||||
name: "default.cfg",
|
||||
data: b"",
|
||||
},
|
||||
FakeFileEntry {
|
||||
name: "doom.cfg",
|
||||
data: b"",
|
||||
},
|
||||
];
|
||||
|
||||
static mut FILE_POOL: [FILE; 16] = [FILE::zeroed(); 16];
|
||||
static mut FILE_POOL_USED: [bool; 16] = [false; 16];
|
||||
|
||||
pub unsafe fn get_file_pool_slot() -> (*mut FILE, i64) {
|
||||
unsafe {
|
||||
for i in 0..16 {
|
||||
if !FILE_POOL_USED[i] {
|
||||
FILE_POOL_USED[i] = true;
|
||||
return (&mut FILE_POOL[i], i as i64);
|
||||
}
|
||||
}
|
||||
|
||||
(null_mut(), -1)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn file_mut(fd: Fd) -> Option<&'static mut FILE> {
|
||||
if !fd_ok(fd) {
|
||||
return None;
|
||||
}
|
||||
let idx = fd as usize;
|
||||
|
||||
if unsafe { !FILE_POOL_USED[idx] } {
|
||||
return None;
|
||||
}
|
||||
|
||||
return unsafe { Some(&mut FILE_POOL[idx]) };
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub fn vfs_open(name: &str, _mode: &str) -> Fd {
|
||||
for entry in FILES {
|
||||
if entry.name.contains(name) {
|
||||
let (slot, fd) = unsafe { get_file_pool_slot() };
|
||||
if slot.is_null() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
(*slot).data = entry.data.as_ptr();
|
||||
(*slot).size = entry.data.len();
|
||||
(*slot).cursor = 0;
|
||||
(*slot).writable = false;
|
||||
(*slot).write_buf = null_mut();
|
||||
(*slot).write_cap = 0;
|
||||
(*slot).fd = fd;
|
||||
}
|
||||
return fd;
|
||||
impl VFS {
|
||||
pub fn new() -> VFS {
|
||||
VFS {
|
||||
files: Vec::new(),
|
||||
next_fd: 0,
|
||||
}
|
||||
}
|
||||
-1
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub fn vfs_close(fd: Fd) -> i32 {
|
||||
if !fd_ok(fd) {
|
||||
return -1;
|
||||
pub fn open(&mut self, name: &str, mode: &str) -> i64 {
|
||||
let is_write = mode.contains("w") || mode.contains("a");
|
||||
|
||||
if let Some(file) = self
|
||||
.files
|
||||
.iter_mut()
|
||||
.find(|file| file.name.as_str() == name)
|
||||
{
|
||||
file.cursor = 0;
|
||||
file.writable = is_write;
|
||||
|
||||
return file.fd;
|
||||
}
|
||||
|
||||
let fd = self.next_fd;
|
||||
self.next_fd += 1;
|
||||
|
||||
let empty_data = &"".as_bytes();
|
||||
|
||||
let data = FILE_CONTENT.get(name).unwrap_or(empty_data);
|
||||
|
||||
let file = FILE {
|
||||
name: name.to_string(),
|
||||
data: data.to_vec(),
|
||||
cursor: 0,
|
||||
writable: is_write,
|
||||
fd,
|
||||
size: data.len(),
|
||||
};
|
||||
|
||||
self.files.push(file);
|
||||
|
||||
fd
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let idx = fd as usize;
|
||||
if !FILE_POOL_USED[idx] {
|
||||
pub fn close(&mut self, fd: i64) -> i32 {
|
||||
if let Some(file_pos) = self.files.iter().position(|file| file.fd == fd) {
|
||||
self.files.remove(file_pos);
|
||||
0
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lseek(&mut self, fd: i64, offset: i64, whence: i32) -> i64 {
|
||||
let f = match self.files.iter_mut().find(|file| file.fd == fd) {
|
||||
Some(f) => f,
|
||||
None => return -1,
|
||||
};
|
||||
|
||||
let new_pos = match whence {
|
||||
0 => {
|
||||
if offset < 0 {
|
||||
return -1;
|
||||
}
|
||||
offset as usize
|
||||
}
|
||||
1 => {
|
||||
let cur = f.cursor as i64;
|
||||
let pos = cur.saturating_add(offset);
|
||||
if pos < 0 {
|
||||
return -1;
|
||||
}
|
||||
pos as usize
|
||||
}
|
||||
2 => {
|
||||
let end = f.size as i64;
|
||||
let pos = end.saturating_add(offset);
|
||||
if pos < 0 {
|
||||
return -1;
|
||||
}
|
||||
pos as usize
|
||||
}
|
||||
_ => return -1,
|
||||
};
|
||||
|
||||
if new_pos > f.size {
|
||||
return -1;
|
||||
}
|
||||
FILE_POOL_USED[idx] = false;
|
||||
FILE_POOL[idx] = FILE::zeroed();
|
||||
|
||||
f.cursor = new_pos;
|
||||
f.cursor as i64
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
pub fn read(&mut self, fd: i64, len: usize) -> Option<(*const u8, usize)> {
|
||||
if let Some(f) = self.files.iter_mut().find(|file| file.fd == fd) {
|
||||
if f.cursor > f.size {
|
||||
return Some((f.data.as_ptr(), 0));
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
#[allow(unused_variables)]
|
||||
pub fn vfs_write(ptr: *mut u8, size: usize, count: usize, fp: *mut FILE) -> usize {
|
||||
if ptr.is_null() || fp.is_null() || unsafe { (*fp).fd < 0 || (*fp).fd >= 16 } {
|
||||
return 0;
|
||||
let available = f.size - f.cursor;
|
||||
let to_read = len.min(available);
|
||||
|
||||
let src = unsafe { f.data.as_ptr().add(f.cursor) };
|
||||
f.cursor = f.cursor.saturating_add(to_read);
|
||||
|
||||
Some((src, to_read))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(&mut self, fd: i64, data: &[u8]) -> Option<usize> {
|
||||
let file = self.files.iter_mut().find(|f| f.fd == fd)?;
|
||||
|
||||
if !file.writable {
|
||||
return None;
|
||||
}
|
||||
|
||||
file.data.extend_from_slice(data);
|
||||
file.size = file.data.len();
|
||||
Some(data.len())
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub fn vfs_read(fd: Fd, len: usize) -> Option<(*const u8, usize)> {
|
||||
pub static mut VFS_INSTANCE: Option<VFS> = None;
|
||||
|
||||
pub fn init_vfs() {
|
||||
unsafe {
|
||||
let f = file_mut(fd)?;
|
||||
if f.cursor > f.size {
|
||||
return Some((f.data, 0));
|
||||
}
|
||||
|
||||
let available = f.size - f.cursor;
|
||||
let to_read = len.min(available);
|
||||
|
||||
let src = f.data.add(f.cursor);
|
||||
f.cursor = f.cursor.saturating_add(to_read);
|
||||
|
||||
Some((src, to_read))
|
||||
VFS_INSTANCE = Some(VFS {
|
||||
files: Vec::new(),
|
||||
next_fd: 3,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn vfs_lseek(fd: Fd, offset: i64, whence: i32) -> i64 {
|
||||
let f = match unsafe { file_mut(fd) } {
|
||||
Some(f) => f,
|
||||
None => return -1,
|
||||
};
|
||||
pub fn vfs_open(name: &str, mode: &str) -> i64 {
|
||||
#[allow(static_mut_refs)]
|
||||
unsafe {
|
||||
VFS_INSTANCE.as_mut().unwrap().open(name, mode)
|
||||
}
|
||||
}
|
||||
|
||||
let new_pos = match whence {
|
||||
0 => {
|
||||
if offset < 0 {
|
||||
return -1;
|
||||
}
|
||||
offset as usize
|
||||
}
|
||||
1 => {
|
||||
let cur = f.cursor as i64;
|
||||
let pos = cur.saturating_add(offset);
|
||||
if pos < 0 {
|
||||
return -1;
|
||||
}
|
||||
pos as usize
|
||||
}
|
||||
2 => {
|
||||
let end = f.size as i64;
|
||||
let pos = end.saturating_add(offset);
|
||||
if pos < 0 {
|
||||
return -1;
|
||||
}
|
||||
pos as usize
|
||||
}
|
||||
_ => return -1,
|
||||
};
|
||||
#[unsafe(no_mangle)]
|
||||
pub fn vfs_close(fd: i64) -> i32 {
|
||||
#[allow(static_mut_refs)]
|
||||
unsafe {
|
||||
VFS_INSTANCE.as_mut().unwrap().close(fd)
|
||||
}
|
||||
}
|
||||
|
||||
if new_pos > f.size {
|
||||
return -1;
|
||||
#[unsafe(no_mangle)]
|
||||
pub fn vfs_write(ptr: *const u8, size: usize, count: usize, fd: i64) -> Option<usize> {
|
||||
if ptr.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
f.cursor = new_pos;
|
||||
f.cursor as i64
|
||||
let len = size.checked_mul(count)?;
|
||||
|
||||
unsafe {
|
||||
let slice = core::slice::from_raw_parts(ptr, len);
|
||||
#[allow(static_mut_refs)]
|
||||
VFS_INSTANCE.as_mut().unwrap().write(fd, slice)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub fn vfs_read(fd: i64, len: usize) -> Option<(*const u8, usize)> {
|
||||
#[allow(static_mut_refs)]
|
||||
unsafe {
|
||||
VFS_INSTANCE.as_mut().unwrap().read(fd, len)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn vfs_lseek(fd: i64, offset: i64, whence: i32) -> i64 {
|
||||
#[allow(static_mut_refs)]
|
||||
unsafe {
|
||||
VFS_INSTANCE.as_mut().unwrap().lseek(fd, offset, whence)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user