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:
@@ -0,0 +1,86 @@
|
|||||||
|
name: Build & Release XunilOS
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build (${{ matrix.karch }})
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
karch: [x86_64, aarch64]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install build dependencies
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y \
|
||||||
|
build-essential \
|
||||||
|
gcc \
|
||||||
|
gcc-aarch64-linux-gnu \
|
||||||
|
binutils-aarch64-linux-gnu \
|
||||||
|
nasm \
|
||||||
|
xorriso \
|
||||||
|
grub-pc-bin \
|
||||||
|
grub-efi-amd64-bin \
|
||||||
|
grub-efi-arm64-bin \
|
||||||
|
mtools \
|
||||||
|
curl
|
||||||
|
|
||||||
|
- name: Install Rust toolchain
|
||||||
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: |
|
||||||
|
x86_64-unknown-none
|
||||||
|
aarch64-unknown-none
|
||||||
|
|
||||||
|
- name: Run build script
|
||||||
|
run: bash build_all.sh
|
||||||
|
env:
|
||||||
|
KARCH: ${{ matrix.karch }}
|
||||||
|
|
||||||
|
- name: Upload ISO artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: XunilOS-${{ matrix.karch }}.iso
|
||||||
|
path: XunilOS-${{ matrix.karch }}.iso
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
release:
|
||||||
|
name: Publish Release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: build
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Download x86_64 ISO
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: XunilOS-x86_64.iso
|
||||||
|
|
||||||
|
- name: Download aarch64 ISO
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: XunilOS-aarch64.iso
|
||||||
|
|
||||||
|
- name: Create GitHub Release
|
||||||
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
tag_name: commit-${{ github.sha }}
|
||||||
|
name: Commit ${{ github.sha }}
|
||||||
|
body: |
|
||||||
|
Automated release for commit [${{ github.sha }}](${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }})
|
||||||
|
files: |
|
||||||
|
XunilOS-x86_64.iso
|
||||||
|
XunilOS-aarch64.iso
|
||||||
|
fail_on_unmatched_files: true
|
||||||
@@ -1,34 +1,46 @@
|
|||||||
# XunilOS
|
# XunilOS
|
||||||
XunilOS is an OS made from scratch in Rust.
|
XunilOS is an OS made from scratch in Rust.
|
||||||
|
|
||||||
The repo is based on the limine-rust-template.
|
The repo is based on the limine-rust-template.
|
||||||
|
|
||||||
## How to use this?
|
It supports aarch64 inside QEMU, and x86_64 even on bare machines!
|
||||||
|
|
||||||
### Dependencies
|
# Features
|
||||||
|
|
||||||
Any `make` command depends on GNU make (`gmake`) and is expected to be run using it. This usually means using `make` on most GNU/Linux distros, or `gmake` on other non-GNU systems.
|
## Kernel
|
||||||
|
- x86_64 IDT, GDT, interrupts, kernel heap, PS2 mouse/keyboard, paging, syscalls and usermode.
|
||||||
|
- aarch64 kernel heap, paging, interrupts, Virtio mouse/keyboard, syscalls, usermode
|
||||||
|
- Scheduler which does round-robin switching as well as sleeping and waking processes
|
||||||
|
- ELF64 ET_EXEC and ET_DYN loading, verifying and running support
|
||||||
|
- A readonly VFS which currently includes the ELF files which can be ran
|
||||||
|
- IPC with granular permissions (read, write, manage)
|
||||||
|
- basic (and insecure) SHM support
|
||||||
|
- Limine bootloader
|
||||||
|
- Framebuffer and serial support
|
||||||
|
- Timing support based on IRQ
|
||||||
|
- Per-process address space, kernel & user stack
|
||||||
|
- Copy to and from userspace
|
||||||
|
|
||||||
All `make all*` targets depend on Rust.
|
## Apps
|
||||||
|
- doomgeneric: Doom ported to XunilOS. Isn't as easy, as i am using Rust and had to write my own libc stub.
|
||||||
|
- badapple: Grayscale Bad Apple by using numbers to represent shades of gray. Pretty easy, but this is the only place where I also used Python.
|
||||||
|
- shell: simple shell with elf running, echo and file read commands.
|
||||||
|
- helloworld: just prints helloworld to serial
|
||||||
|
|
||||||
Additionally, building an ISO with `make all` requires `xorriso`, and building a HDD/USB image with `make all-hdd` requires `sgdisk` (usually from `gdisk` or `gptfdisk` packages) and `mtools`.
|
## Init
|
||||||
|
- Desktop-like experience
|
||||||
|
- Window Management (close and minimize)
|
||||||
|
- Start menu to open applications
|
||||||
|
- BMP background
|
||||||
|
- Mouse support with a BMP image
|
||||||
|
- Dock where you can see currently open applications which can be minimized or unminimized
|
||||||
|
|
||||||
### Architectural targets
|
## Libxunil (libc stub)
|
||||||
|
- Basic functions of C (printf, strlen, etc)
|
||||||
The `KARCH` make variable determines the target architecture to build the kernel and image for.
|
- File I\O, IPC, time
|
||||||
|
- Input reading from Window Management
|
||||||
The default `KARCH` is `x86_64`. Other options include: `aarch64`, `riscv64`, and `loongarch64`.
|
- SHM
|
||||||
|
- User Heap
|
||||||
Other architectures will need to be enabled in kernel/rust-toolchain.toml
|
- Window management
|
||||||
|
- IPC support
|
||||||
### Makefile targets
|
- Syscalls to call back to the kernel
|
||||||
|
- Primitives, Framebuffer and font rendering
|
||||||
Running `make all` will compile the kernel (from the `kernel/` directory) and then generate a bootable ISO image.
|
|
||||||
|
|
||||||
Running `make all-hdd` will compile the kernel and then generate a raw image suitable to be flashed onto a USB stick or hard drive/SSD.
|
|
||||||
|
|
||||||
Running `make run` will build the kernel and a bootable ISO (equivalent to make all) and then run it using `qemu` (if installed).
|
|
||||||
|
|
||||||
Running `make run-hdd` will build the kernel and a raw HDD image (equivalent to make all-hdd) and then run it using `qemu` (if installed).
|
|
||||||
|
|
||||||
The `run-uefi` and `run-hdd-uefi` targets are equivalent to their non `-uefi` counterparts except that they boot `qemu` using a UEFI-compatible firmware.
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 100 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.5 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.4 MiB |
@@ -0,0 +1,7 @@
|
|||||||
|
bash build_rust_app.sh libxunil
|
||||||
|
bash build_rust_app.sh init
|
||||||
|
bash build_doomgeneric.sh
|
||||||
|
bash build_helloworld.sh
|
||||||
|
bash build_rust_app.sh badapple
|
||||||
|
bash build_rust_app.sh shell
|
||||||
|
make all
|
||||||
+2
-7
@@ -1,8 +1,3 @@
|
|||||||
export KARCH=x86_64
|
export KARCH=aarch64
|
||||||
bash build_rust_app.sh libxunil
|
bash build_all.sh
|
||||||
bash build_rust_app.sh init
|
|
||||||
bash build_doomgeneric.sh
|
|
||||||
bash build_helloworld.sh
|
|
||||||
bash build_rust_app.sh badapple
|
|
||||||
bash build_rust_app.sh shell
|
|
||||||
make run
|
make run
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ use crate::{
|
|||||||
},
|
},
|
||||||
arch::KERNEL_MAPPER,
|
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,
|
mm::shm::init_shm,
|
||||||
};
|
};
|
||||||
use limine::response::{ExecutableAddressResponse, HhdmResponse, MemoryMapResponse};
|
use limine::response::{ExecutableAddressResponse, HhdmResponse, MemoryMapResponse};
|
||||||
@@ -40,6 +43,7 @@ pub extern "C" fn init_aarch64(mapper: &mut AArchPageTable) {
|
|||||||
init_interrupts();
|
init_interrupts();
|
||||||
init_ipc();
|
init_ipc();
|
||||||
init_shm();
|
init_shm();
|
||||||
|
init_vfs();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn preinit_aarch64<'a>(
|
pub fn preinit_aarch64<'a>(
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ use core::sync::atomic::Ordering;
|
|||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use crate::arch::x86_64::paging::create_and_map_multiple_pages;
|
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};
|
use crate::driver::io::input::{InputEvent, process_input};
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
use crate::driver::io::ps2::process_scancodes;
|
use crate::driver::io::ps2::process_scancodes;
|
||||||
|
use crate::mm::usercopy::copy_from_user;
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
use alloc::{string::String, vec::Vec};
|
use alloc::{string::String, vec::Vec};
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
@@ -204,6 +206,39 @@ fn open(path: isize, mode: isize) -> isize {
|
|||||||
.unwrap_or(-1)
|
.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 {
|
fn close(fd: isize) -> isize {
|
||||||
vfs_close(fd as i64) as isize
|
vfs_close(fd as i64) as isize
|
||||||
}
|
}
|
||||||
@@ -656,6 +691,7 @@ pub unsafe extern "C" fn syscall_dispatch(
|
|||||||
BRK => unsafe { sbrk(arg0) },
|
BRK => unsafe { sbrk(arg0) },
|
||||||
READ => read(arg0, arg1, arg2, arg3) as isize,
|
READ => read(arg0, arg1, arg2, arg3) as isize,
|
||||||
WRITE => {
|
WRITE => {
|
||||||
|
if arg0 == 1 {
|
||||||
let buf_ptr = arg1 as *const u8;
|
let buf_ptr = arg1 as *const u8;
|
||||||
let len = arg2 as usize;
|
let len = arg2 as usize;
|
||||||
let bytes: &[u8] = unsafe { core::slice::from_raw_parts(buf_ptr, len) };
|
let bytes: &[u8] = unsafe { core::slice::from_raw_parts(buf_ptr, len) };
|
||||||
@@ -669,6 +705,9 @@ pub unsafe extern "C" fn syscall_dispatch(
|
|||||||
print!("{}", *byte as char);
|
print!("{}", *byte as char);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
write_fd(arg0, arg1, arg2, arg3);
|
||||||
|
}
|
||||||
|
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
@@ -678,7 +717,9 @@ pub unsafe extern "C" fn syscall_dispatch(
|
|||||||
EXIT => kill(current_pid().unwrap() as isize, arg0),
|
EXIT => kill(current_pid().unwrap() as isize, arg0),
|
||||||
SLEEP => sleep(arg0),
|
SLEEP => sleep(arg0),
|
||||||
EXECVE => exec(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(),
|
MAP_FRAMEBUFFER => map_framebuffer(),
|
||||||
INPUT_READ => input_read(arg0 as *mut InputEvent, arg1),
|
INPUT_READ => input_read(arg0 as *mut InputEvent, arg1),
|
||||||
GETPID => {
|
GETPID => {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use crate::{
|
|||||||
syscall::init_syscalls,
|
syscall::init_syscalls,
|
||||||
},
|
},
|
||||||
config::TIMER_FREQUENCY_HZ,
|
config::TIMER_FREQUENCY_HZ,
|
||||||
driver::ipc::init_ipc,
|
driver::{io::fs::vfs::init_vfs, ipc::init_ipc},
|
||||||
mm::shm::init_shm,
|
mm::shm::init_shm,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -93,6 +93,7 @@ pub fn init_x86_64<'a>(
|
|||||||
|
|
||||||
init_ipc();
|
init_ipc();
|
||||||
init_shm();
|
init_shm();
|
||||||
|
init_vfs();
|
||||||
|
|
||||||
return mapper;
|
return mapper;
|
||||||
}
|
}
|
||||||
|
|||||||
+167
-148
@@ -1,180 +1,110 @@
|
|||||||
|
use alloc::{
|
||||||
|
collections::btree_map::BTreeMap,
|
||||||
|
string::{String, ToString},
|
||||||
|
vec::Vec,
|
||||||
|
};
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
use crate::driver::io::fs::assets::*;
|
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)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct FILE {
|
pub struct FILE {
|
||||||
pub data: *const u8, // pointer to the file's data
|
pub name: String,
|
||||||
pub size: usize, // total size
|
pub size: usize,
|
||||||
pub cursor: usize, // current position
|
pub data: Vec<u8>,
|
||||||
pub writable: bool, // is this a write buffer?
|
pub cursor: usize,
|
||||||
pub write_buf: *mut u8, // for writable fake files
|
pub writable: bool,
|
||||||
pub write_cap: usize,
|
|
||||||
pub fd: i64,
|
pub fd: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FILE {
|
impl FILE {
|
||||||
pub const fn zeroed() -> FILE {
|
pub fn new(name: String, data: Vec<u8>, writable: bool) -> FILE {
|
||||||
FILE {
|
FILE {
|
||||||
data: null(),
|
name,
|
||||||
size: 0,
|
data: data.clone(),
|
||||||
cursor: 0,
|
cursor: 0,
|
||||||
writable: false,
|
writable,
|
||||||
write_buf: null_mut(),
|
|
||||||
write_cap: 0,
|
|
||||||
fd: -1,
|
fd: -1,
|
||||||
|
size: data.len(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FakeFileEntry {
|
pub struct VFS {
|
||||||
name: &'static str,
|
files: Vec<FILE>,
|
||||||
data: &'static [u8],
|
next_fd: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Fd = i64;
|
impl VFS {
|
||||||
const MAX_FD: usize = 16;
|
pub fn new() -> VFS {
|
||||||
|
VFS {
|
||||||
fn fd_ok(fd: Fd) -> bool {
|
files: Vec::new(),
|
||||||
fd >= 0 && (fd as usize) < MAX_FD
|
next_fd: 0,
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn file_mut(fd: Fd) -> Option<&'static mut FILE> {
|
let fd = self.next_fd;
|
||||||
if !fd_ok(fd) {
|
self.next_fd += 1;
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let idx = fd as usize;
|
|
||||||
|
|
||||||
if unsafe { !FILE_POOL_USED[idx] } {
|
let empty_data = &"".as_bytes();
|
||||||
return None;
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
return unsafe { Some(&mut FILE_POOL[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);
|
||||||
#[unsafe(no_mangle)]
|
0
|
||||||
pub fn vfs_open(name: &str, _mode: &str) -> Fd {
|
} else {
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-1
|
-1
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub fn vfs_close(fd: Fd) -> i32 {
|
|
||||||
if !fd_ok(fd) {
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
pub fn lseek(&mut self, fd: i64, offset: i64, whence: i32) -> i64 {
|
||||||
let idx = fd as usize;
|
let f = match self.files.iter_mut().find(|file| file.fd == fd) {
|
||||||
if !FILE_POOL_USED[idx] {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
FILE_POOL_USED[idx] = false;
|
|
||||||
FILE_POOL[idx] = FILE::zeroed();
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
count
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
|
||||||
pub fn vfs_read(fd: Fd, len: usize) -> Option<(*const u8, usize)> {
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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,
|
Some(f) => f,
|
||||||
None => return -1,
|
None => return -1,
|
||||||
};
|
};
|
||||||
@@ -212,3 +142,92 @@ pub extern "C" fn vfs_lseek(fd: Fd, offset: i64, whence: i32) -> i64 {
|
|||||||
f.cursor = new_pos;
|
f.cursor = new_pos;
|
||||||
f.cursor as i64
|
f.cursor as i64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static mut VFS_INSTANCE: Option<VFS> = None;
|
||||||
|
|
||||||
|
pub fn init_vfs() {
|
||||||
|
unsafe {
|
||||||
|
VFS_INSTANCE = Some(VFS {
|
||||||
|
files: Vec::new(),
|
||||||
|
next_fd: 3,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub fn vfs_open(name: &str, mode: &str) -> i64 {
|
||||||
|
#[allow(static_mut_refs)]
|
||||||
|
unsafe {
|
||||||
|
VFS_INSTANCE.as_mut().unwrap().open(name, mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub fn vfs_close(fd: i64) -> i32 {
|
||||||
|
#[allow(static_mut_refs)]
|
||||||
|
unsafe {
|
||||||
|
VFS_INSTANCE.as_mut().unwrap().close(fd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub fn vfs_write(ptr: *const u8, size: usize, count: usize, fd: i64) -> Option<usize> {
|
||||||
|
if ptr.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
Submodule user/apps/shell updated: d8d08dcf9f...53a381d7e9
+1
-1
Submodule user/init updated: 007debaade...bb40e45b2b
+1
-1
Submodule user/libxunil updated: 04e87c6099...82a04e045e
Reference in New Issue
Block a user