内存缓存和ext4完整支持

This commit is contained in:
wcjbr
2026-07-29 12:35:25 +08:00
parent 86dbf7482f
commit a9f3f71063
27 changed files with 1813 additions and 175 deletions
+2
View File
@@ -8,6 +8,8 @@ members = ["boot"]
[dependencies]
[build-dependencies]
[profile.dev]
panic = "abort"
+112
View File
@@ -0,0 +1,112 @@
use std::{env, path::PathBuf, process::Command};
fn main() {
let target = env::var("TARGET").unwrap();
if target != "x86_64-unknown-uefi" {
return;
}
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let lwext4_dir = manifest_dir.join("third_party/lwext4");
let config_dir = manifest_dir.join("third_party/lwext4_config/include/generated");
let shim = manifest_dir.join("src/fs/lwext4_shim.c");
println!("cargo:rerun-if-changed={}", shim.display());
println!(
"cargo:rerun-if-changed={}",
lwext4_dir.join("src").display()
);
println!(
"cargo:rerun-if-changed={}",
manifest_dir
.join("third_party/lwext4_config/include/generated/ext4_config.h")
.display()
);
let sources = [
"ext4.c",
"ext4_balloc.c",
"ext4_bitmap.c",
"ext4_bcache.c",
"ext4_block_group.c",
"ext4_blockdev.c",
"ext4_crc32.c",
"ext4_debug.c",
"ext4_dir.c",
"ext4_dir_idx.c",
"ext4_extent.c",
"ext4_fs.c",
"ext4_hash.c",
"ext4_ialloc.c",
"ext4_inode.c",
"ext4_journal.c",
"ext4_mbr.c",
"ext4_super.c",
"ext4_trans.c",
"ext4_xattr.c",
];
let clang = env::var("CLANG").unwrap_or_else(|_| "clang".to_string());
let ar = env::var("AR").unwrap_or_else(|_| "llvm-ar".to_string());
let mut objects = Vec::new();
for source in sources {
let input = lwext4_dir.join("src").join(source);
let output = out_dir.join(format!("{source}.obj"));
compile_c(&clang, &input, &output, &lwext4_dir, &config_dir);
objects.push(output);
}
let shim_obj = out_dir.join("zeroos_lwext4_shim.obj");
compile_c(&clang, &shim, &shim_obj, &lwext4_dir, &config_dir);
objects.push(shim_obj);
let lib = out_dir.join("libzeroos_lwext4.a");
let status = Command::new(&ar)
.arg("crs")
.arg(&lib)
.args(&objects)
.status()
.unwrap_or_else(|error| panic!("failed to run {ar}: {error}"));
assert!(status.success(), "{ar} failed while archiving lwext4");
println!("cargo:rustc-link-search=native={}", out_dir.display());
println!("cargo:rustc-link-lib=static=zeroos_lwext4");
}
fn compile_c(
clang: &str,
input: &PathBuf,
output: &PathBuf,
lwext4_dir: &PathBuf,
config_dir: &PathBuf,
) {
let status = Command::new(clang)
.arg("-target")
.arg("x86_64-unknown-windows")
.arg("-ffreestanding")
.arg("-fno-stack-protector")
.arg("-fno-builtin")
.arg("-mno-red-zone")
.arg("-O2")
.arg("-Wall")
.arg("-Wno-unused-parameter")
.arg("-Wno-missing-braces")
.arg("-DCONFIG_USE_DEFAULT_CFG=0")
.arg("-I")
.arg(lwext4_dir.join("include"))
.arg("-I")
.arg(config_dir.parent().unwrap())
.arg("-c")
.arg(input)
.arg("-o")
.arg(output)
.status()
.unwrap_or_else(|error| panic!("failed to run {clang}: {error}"));
assert!(
status.success(),
"{clang} failed while compiling {}",
input.display()
);
}
+2
View File
@@ -1,3 +1,5 @@
#![allow(dead_code)]
use crate::drivers::storage::{
ahci::AhciDisk,
block::{BlockDevice, BlockError},
+706
View File
@@ -0,0 +1,706 @@
use crate::drivers::storage::{
ahci::AhciDisk,
block::{BlockDevice, BlockError},
partition::PartitionBlockDevice,
};
use crate::fs::uefi::LoadedFile;
use crate::memory::{PAGE_SIZE, PhysicalMemoryManager};
use core::ffi::c_void;
const EIO: isize = 5;
const ENOENT: isize = 2;
const ENODEV: isize = 19;
const EFBIG: isize = 27;
const EXT4_DE_REG_FILE: u8 = 1;
const EXT4_DE_DIR: u8 = 2;
const MAX_PATH: usize = 256;
const CACHE_SLOTS: usize = 64;
const EXEC_CACHE_SLOTS: usize = 8;
static mut ROOT_DEVICE: Option<PartitionBlockDevice<AhciDisk>> = None;
static mut MOUNTED: bool = false;
static mut MEMORY_MANAGER: *mut PhysicalMemoryManager = core::ptr::null_mut();
static mut CACHE: [CacheEntry; CACHE_SLOTS] = [CacheEntry::empty(); CACHE_SLOTS];
static mut CACHE_CLOCK: u64 = 0;
static mut EXEC_CACHE: [ExecCacheEntry; EXEC_CACHE_SLOTS] =
[ExecCacheEntry::empty(); EXEC_CACHE_SLOTS];
unsafe extern "C" {
fn zeroos_lwext4_mount(block_count: u64) -> i32;
fn zeroos_lwext4_read(
path: *const u8,
offset: u64,
buf: *mut u8,
len: usize,
done: *mut usize,
) -> i32;
fn zeroos_lwext4_write(
path: *const u8,
offset: u64,
buf: *const u8,
len: usize,
done: *mut usize,
) -> i32;
fn zeroos_lwext4_truncate(path: *const u8, size: u64) -> i32;
fn zeroos_lwext4_size(path: *const u8, size: *mut u64) -> i32;
fn zeroos_lwext4_create_file(path: *const u8) -> i32;
fn zeroos_lwext4_mkdir(path: *const u8) -> i32;
fn ext4_dir_open(dir: *mut Ext4Dir, path: *const u8) -> i32;
fn ext4_dir_close(dir: *mut Ext4Dir) -> i32;
fn ext4_dir_entry_next(dir: *mut Ext4Dir) -> *const Ext4DirEntry;
}
#[repr(C)]
struct Ext4File {
mp: *mut c_void,
inode: u32,
flags: u32,
fsize: u64,
fpos: u64,
}
#[repr(C)]
struct Ext4DirEntry {
inode: u32,
entry_length: u16,
name_length: u8,
inode_type: u8,
name: [u8; 255],
}
#[repr(C)]
struct Ext4Dir {
f: Ext4File,
de: Ext4DirEntry,
next_off: u64,
}
#[derive(Clone, Copy)]
struct CacheEntry {
valid: bool,
dirty: bool,
stamp: u64,
page_index: u64,
path: [u8; MAX_PATH],
data: [u8; PAGE_SIZE],
}
impl CacheEntry {
const fn empty() -> Self {
Self {
valid: false,
dirty: false,
stamp: 0,
page_index: 0,
path: [0; MAX_PATH],
data: [0; PAGE_SIZE],
}
}
}
#[derive(Clone, Copy)]
struct ExecCacheEntry {
valid: bool,
hits: u64,
stamp: u64,
path: [u8; MAX_PATH],
ptr: *const u8,
len: usize,
pages: usize,
}
impl ExecCacheEntry {
const fn empty() -> Self {
Self {
valid: false,
hits: 0,
stamp: 0,
path: [0; MAX_PATH],
ptr: core::ptr::null(),
len: 0,
pages: 0,
}
}
}
pub unsafe fn mount_root(
device: PartitionBlockDevice<AhciDisk>,
sector_count: u64,
) -> Result<(), isize> {
unsafe {
ROOT_DEVICE = Some(device);
let rc = zeroos_lwext4_mount(sector_count);
if rc == 0 {
MOUNTED = true;
Ok(())
} else {
ROOT_DEVICE = None;
Err(map_error(rc))
}
}
}
pub fn is_mounted() -> bool {
unsafe { MOUNTED }
}
pub fn init_cache(memory: *mut PhysicalMemoryManager) {
unsafe {
MEMORY_MANAGER = memory;
}
}
pub fn read_path_at(
path: &[u8],
offset: usize,
buffer: *mut u8,
len: usize,
) -> Result<usize, isize> {
if !is_mounted() {
return Err(-ENODEV);
}
if buffer.is_null() {
return Err(-EIO);
}
let path = c_path(path)?;
cache_read(&path, offset, buffer, len)
}
pub fn write_path_at(
path: &[u8],
offset: usize,
buffer: *const u8,
len: usize,
) -> Result<usize, isize> {
if !is_mounted() {
return Err(-ENODEV);
}
if buffer.is_null() {
return Err(-EIO);
}
let Some(end) = offset.checked_add(len) else {
return Err(-EFBIG);
};
if end as u64 > u64::MAX {
return Err(-EFBIG);
}
let path = c_path(path)?;
invalidate_exec_cache(&path);
cache_write(&path, offset, buffer, len)
}
pub fn truncate_path(path: &[u8], size: usize) -> Result<(), isize> {
if !is_mounted() {
return Err(-ENODEV);
}
let path = c_path(path)?;
flush_path_cache(&path)?;
invalidate_exec_cache(&path);
let rc = unsafe { zeroos_lwext4_truncate(path.as_ptr(), size as u64) };
if rc == 0 {
invalidate_path_cache(&path);
Ok(())
} else {
Err(map_error(rc))
}
}
pub fn size_path(path: &[u8]) -> Result<usize, isize> {
if !is_mounted() {
return Err(-ENODEV);
}
let path = c_path(path)?;
let mut size = 0u64;
let rc = unsafe { zeroos_lwext4_size(path.as_ptr(), &mut size) };
if rc == 0 {
usize::try_from(size).map_err(|_| -EFBIG)
} else {
Err(map_error(rc))
}
}
pub fn load_path(memory: &mut PhysicalMemoryManager, path: &[u8]) -> Result<LoadedFile, isize> {
let path = c_path(path)?;
if let Some(image) = exec_cache_get(&path) {
return Ok(image);
}
let len = size_path(&path)?;
let pages = len.div_ceil(PAGE_SIZE).max(1);
let Some(buffer) = memory.alloc_pages(pages) else {
return Err(-EIO);
};
let slice = unsafe { core::slice::from_raw_parts_mut(buffer, pages * PAGE_SIZE) };
slice[..len].fill(0);
match cache_read(&path, 0, slice.as_mut_ptr(), len) {
Ok(_) => {
let image = LoadedFile {
ptr: buffer as *const u8,
len,
};
exec_cache_insert(&path, image.ptr, image.len, pages);
Ok(image)
}
Err(error) => {
unsafe {
memory.free_pages(buffer, pages);
}
Err(error)
}
}
}
pub fn create_file(path: &[u8]) -> Result<(), isize> {
if !is_mounted() {
return Err(-ENODEV);
}
let path = c_path(path)?;
flush_path_cache(&path)?;
invalidate_exec_cache(&path);
let rc = unsafe { zeroos_lwext4_create_file(path.as_ptr()) };
if rc == 0 {
invalidate_path_cache(&path);
Ok(())
} else {
Err(map_error(rc))
}
}
pub fn create_dir(path: &[u8]) -> Result<(), isize> {
if !is_mounted() {
return Err(-ENODEV);
}
let path = c_path(path)?;
let rc = unsafe { zeroos_lwext4_mkdir(path.as_ptr()) };
if rc == 0 { Ok(()) } else { Err(map_error(rc)) }
}
pub fn mount_vfs_tree() -> Result<(), isize> {
let mut root = [0u8; MAX_PATH];
root[0] = b'/';
mount_vfs_dir(&root, 0)
}
pub fn flush_cache() -> Result<(), isize> {
for index in 0..CACHE_SLOTS {
flush_entry(index)?;
}
Ok(())
}
#[unsafe(no_mangle)]
pub extern "C" fn zeroos_lwext4_read_blocks(
buffer: *mut u8,
block_id: u64,
block_count: u32,
) -> i32 {
if buffer.is_null() {
return EIO as i32;
}
let byte_count = block_count as usize * 512;
let offset = block_id.saturating_mul(512);
unsafe {
let Some(mut device) = ROOT_DEVICE else {
return ENODEV as i32;
};
let slice = core::slice::from_raw_parts_mut(buffer, byte_count);
let result = device.read_at(offset, slice);
ROOT_DEVICE = Some(device);
block_result(result)
}
}
#[unsafe(no_mangle)]
pub extern "C" fn zeroos_lwext4_write_blocks(
buffer: *const u8,
block_id: u64,
block_count: u32,
) -> i32 {
if buffer.is_null() {
return EIO as i32;
}
let byte_count = block_count as usize * 512;
let offset = block_id.saturating_mul(512);
unsafe {
let Some(mut device) = ROOT_DEVICE else {
return ENODEV as i32;
};
let slice = core::slice::from_raw_parts(buffer, byte_count);
let result = device.write_at(offset, slice);
ROOT_DEVICE = Some(device);
block_result(result)
}
}
fn block_result(result: Result<(), BlockError>) -> i32 {
match result {
Ok(()) => 0,
Err(_) => EIO as i32,
}
}
fn cache_read(
path: &[u8; MAX_PATH],
offset: usize,
buffer: *mut u8,
len: usize,
) -> Result<usize, isize> {
if len == 0 {
return Ok(0);
}
let mut copied = 0usize;
while copied < len {
let absolute = offset + copied;
let page_index = (absolute / PAGE_SIZE) as u64;
let page_offset = absolute % PAGE_SIZE;
let chunk = (PAGE_SIZE - page_offset).min(len - copied);
let slot = cache_get_page_slot(path, page_index, true)?;
unsafe {
core::ptr::copy_nonoverlapping(
CACHE[slot].data.as_ptr().add(page_offset),
buffer.add(copied),
chunk,
);
}
copied += chunk;
}
Ok(copied)
}
fn cache_write(
path: &[u8; MAX_PATH],
offset: usize,
buffer: *const u8,
len: usize,
) -> Result<usize, isize> {
if len == 0 {
return Ok(0);
}
let mut written = 0usize;
while written < len {
let absolute = offset + written;
let page_index = (absolute / PAGE_SIZE) as u64;
let page_offset = absolute % PAGE_SIZE;
let chunk = (PAGE_SIZE - page_offset).min(len - written);
let slot = cache_get_page_slot(path, page_index, true)?;
unsafe {
core::ptr::copy_nonoverlapping(
buffer.add(written),
CACHE[slot].data.as_mut_ptr().add(page_offset),
chunk,
);
CACHE[slot].dirty = true;
CACHE[slot].stamp = next_stamp();
}
written += chunk;
}
Ok(written)
}
fn cache_get_page_slot(path: &[u8; MAX_PATH], page_index: u64, load: bool) -> Result<usize, isize> {
unsafe {
let mut free_slot = None;
let mut lru_slot = 0usize;
let mut lru_stamp = u64::MAX;
for index in 0..CACHE_SLOTS {
let entry = &mut CACHE[index];
if entry.valid {
if entry.page_index == page_index && entry.path == *path {
entry.stamp = next_stamp();
return Ok(index);
}
if entry.stamp < lru_stamp {
lru_stamp = entry.stamp;
lru_slot = index;
}
} else if free_slot.is_none() {
free_slot = Some(index);
}
}
let slot = free_slot.unwrap_or(lru_slot);
if CACHE[slot].valid && CACHE[slot].dirty {
flush_entry(slot)?;
}
let entry = &mut CACHE[slot];
entry.valid = true;
entry.dirty = false;
entry.stamp = next_stamp();
entry.page_index = page_index;
entry.path = *path;
entry.data.fill(0);
if load {
load_page(slot, path, page_index)?;
}
Ok(slot)
}
}
fn load_page(slot: usize, path: &[u8; MAX_PATH], page_index: u64) -> Result<(), isize> {
let mut done = 0usize;
let offset = page_index as usize * PAGE_SIZE;
let rc = unsafe {
zeroos_lwext4_read(
path.as_ptr(),
offset as u64,
CACHE[slot].data.as_mut_ptr(),
PAGE_SIZE,
&mut done,
)
};
if rc == 0 {
if done < PAGE_SIZE {
unsafe {
CACHE[slot].data[done..].fill(0);
}
}
Ok(())
} else {
Err(map_error(rc))
}
}
fn flush_path_cache(path: &[u8; MAX_PATH]) -> Result<(), isize> {
unsafe {
for index in 0..CACHE_SLOTS {
if CACHE[index].valid && CACHE[index].path == *path && CACHE[index].dirty {
flush_entry(index)?;
}
}
}
Ok(())
}
fn invalidate_path_cache(path: &[u8; MAX_PATH]) {
unsafe {
for index in 0..CACHE_SLOTS {
if CACHE[index].valid && CACHE[index].path == *path {
CACHE[index] = CacheEntry::empty();
}
}
}
}
fn flush_entry(index: usize) -> Result<(), isize> {
unsafe {
let entry = &mut CACHE[index];
if !entry.valid || !entry.dirty {
return Ok(());
}
let mut done = 0usize;
let rc = zeroos_lwext4_write(
entry.path.as_ptr(),
entry.page_index * PAGE_SIZE as u64,
entry.data.as_ptr(),
PAGE_SIZE,
&mut done,
);
if rc == 0 {
entry.dirty = false;
Ok(())
} else {
Err(map_error(rc))
}
}
}
fn exec_cache_get(path: &[u8; MAX_PATH]) -> Option<LoadedFile> {
unsafe {
for index in 0..EXEC_CACHE_SLOTS {
let entry = &mut *core::ptr::addr_of_mut!(EXEC_CACHE[index]);
if entry.valid && entry.path == *path {
entry.hits = entry.hits.saturating_add(1);
entry.stamp = next_stamp();
return Some(LoadedFile {
ptr: entry.ptr,
len: entry.len,
});
}
}
}
None
}
fn exec_cache_insert(path: &[u8; MAX_PATH], ptr: *const u8, len: usize, pages: usize) {
unsafe {
let mut free_slot = None;
let mut lru_slot = 0usize;
let mut lru_score = u64::MAX;
for index in 0..EXEC_CACHE_SLOTS {
let entry = EXEC_CACHE[index];
if !entry.valid {
free_slot = Some(index);
break;
}
let score = entry.stamp.saturating_sub(entry.hits.min(64));
if score < lru_score {
lru_score = score;
lru_slot = index;
}
}
let slot = free_slot.unwrap_or(lru_slot);
if EXEC_CACHE[slot].valid {
free_exec_entry(slot);
}
let entry = &mut EXEC_CACHE[slot];
entry.valid = true;
entry.hits = 1;
entry.stamp = next_stamp();
entry.path = *path;
entry.ptr = ptr;
entry.len = len;
entry.pages = pages;
}
}
fn invalidate_exec_cache(path: &[u8; MAX_PATH]) {
unsafe {
for index in 0..EXEC_CACHE_SLOTS {
if EXEC_CACHE[index].valid && EXEC_CACHE[index].path == *path {
free_exec_entry(index);
}
}
}
}
fn free_exec_entry(index: usize) {
unsafe {
let entry = &mut *core::ptr::addr_of_mut!(EXEC_CACHE[index]);
if entry.valid && !entry.ptr.is_null() && !MEMORY_MANAGER.is_null() && entry.pages > 0 {
(*MEMORY_MANAGER).free_pages(entry.ptr as *mut u8, entry.pages);
}
EXEC_CACHE[index] = ExecCacheEntry::empty();
}
}
fn next_stamp() -> u64 {
unsafe {
CACHE_CLOCK = CACHE_CLOCK.wrapping_add(1);
CACHE_CLOCK
}
}
fn map_error(error: i32) -> isize {
if error == 0 {
return 0;
}
let errno = error.unsigned_abs() as isize;
match errno {
2 => -ENOENT,
5 => -EIO,
19 => -ENODEV,
27 => -EFBIG,
_ => -EIO,
}
}
fn mount_vfs_dir(path: &[u8], depth: usize) -> Result<(), isize> {
if depth > 16 {
return Ok(());
}
let mut dir = Ext4Dir {
f: Ext4File {
mp: core::ptr::null_mut(),
inode: 0,
flags: 0,
fsize: 0,
fpos: 0,
},
de: Ext4DirEntry {
inode: 0,
entry_length: 0,
name_length: 0,
inode_type: 0,
name: [0; 255],
},
next_off: 0,
};
let c_path = c_path(path)?;
let rc = unsafe { ext4_dir_open(&mut dir, c_path.as_ptr()) };
if rc != 0 {
return Err(map_error(rc));
}
loop {
let entry = unsafe { ext4_dir_entry_next(&mut dir) };
if entry.is_null() {
break;
}
let entry = unsafe { &*entry };
let name_len = entry.name_length as usize;
if name_len == 0 || name_len > entry.name.len() {
continue;
}
let name = &entry.name[..name_len];
if name == b"." || name == b".." {
continue;
}
let mut child_path = [0u8; MAX_PATH];
if !join_path(path, name, &mut child_path) {
continue;
}
match entry.inode_type {
EXT4_DE_DIR => {
let _ = crate::fs::vfs::mount_ext4_node(
&child_path,
crate::fs::vfs::NodeKind::Directory,
);
let _ = mount_vfs_dir(&child_path, depth + 1);
}
EXT4_DE_REG_FILE => {
let _ =
crate::fs::vfs::mount_ext4_node(&child_path, crate::fs::vfs::NodeKind::Regular);
}
_ => {}
}
}
unsafe {
let _ = ext4_dir_close(&mut dir);
}
Ok(())
}
fn c_path(path: &[u8]) -> Result<[u8; MAX_PATH], isize> {
let len = nul_len(path);
if len == 0 || len >= MAX_PATH {
return Err(-ENOENT);
}
let mut out = [0u8; MAX_PATH];
out[..len].copy_from_slice(&path[..len]);
Ok(out)
}
fn join_path(parent: &[u8], name: &[u8], out: &mut [u8; MAX_PATH]) -> bool {
let parent_len = nul_len(parent);
if parent_len == 0 || name.is_empty() {
return false;
}
let mut pos = 0;
while pos < parent_len && pos < MAX_PATH - 1 {
out[pos] = parent[pos];
pos += 1;
}
if !(pos == 1 && out[0] == b'/') {
if pos >= MAX_PATH - 1 {
return false;
}
out[pos] = b'/';
pos += 1;
}
if pos + name.len() >= MAX_PATH {
return false;
}
out[pos..pos + name.len()].copy_from_slice(name);
true
}
fn nul_len(path: &[u8]) -> usize {
path.iter()
.position(|byte| *byte == 0)
.unwrap_or(path.len())
}
+329
View File
@@ -0,0 +1,329 @@
#include <ext4.h>
#include <ext4_blockdev.h>
#include <ext4_oflags.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
extern int zeroos_lwext4_read_blocks(void *buf, uint64_t blk_id, uint32_t blk_cnt);
extern int zeroos_lwext4_write_blocks(const void *buf, uint64_t blk_id, uint32_t blk_cnt);
#define ZEROOS_LWEXT4_HEAP_SIZE (4u * 1024u * 1024u)
#define ZEROOS_LWEXT4_ALIGN 16u
struct zeroos_alloc_header {
size_t size;
int free;
struct zeroos_alloc_header *next;
};
static uint8_t zeroos_lwext4_heap[ZEROOS_LWEXT4_HEAP_SIZE];
static struct zeroos_alloc_header *zeroos_heap_head;
static uint8_t zeroos_bbuf[512];
static struct ext4_blockdev zeroos_bdev;
static struct ext4_blockdev_iface zeroos_bdev_iface;
static int zeroos_mounted;
static size_t zeroos_align_up(size_t value) {
return (value + ZEROOS_LWEXT4_ALIGN - 1u) & ~(ZEROOS_LWEXT4_ALIGN - 1u);
}
static void zeroos_heap_init(void) {
if (zeroos_heap_head) {
return;
}
zeroos_heap_head = (struct zeroos_alloc_header *)zeroos_lwext4_heap;
zeroos_heap_head->size = ZEROOS_LWEXT4_HEAP_SIZE - sizeof(struct zeroos_alloc_header);
zeroos_heap_head->free = 1;
zeroos_heap_head->next = 0;
}
void *ext4_user_malloc(size_t size) {
zeroos_heap_init();
size = zeroos_align_up(size);
struct zeroos_alloc_header *node = zeroos_heap_head;
while (node) {
if (node->free && node->size >= size) {
size_t remaining = node->size - size;
if (remaining > sizeof(struct zeroos_alloc_header) + ZEROOS_LWEXT4_ALIGN) {
struct zeroos_alloc_header *next =
(struct zeroos_alloc_header *)((uint8_t *)(node + 1) + size);
next->size = remaining - sizeof(struct zeroos_alloc_header);
next->free = 1;
next->next = node->next;
node->next = next;
node->size = size;
}
node->free = 0;
return node + 1;
}
node = node->next;
}
return 0;
}
void *ext4_user_calloc(size_t count, size_t size) {
size_t total = count * size;
void *ptr = ext4_user_malloc(total);
if (ptr) {
memset(ptr, 0, total);
}
return ptr;
}
void ext4_user_free(void *ptr) {
if (!ptr) {
return;
}
struct zeroos_alloc_header *node = ((struct zeroos_alloc_header *)ptr) - 1;
node->free = 1;
while (node->next && node->next->free) {
node->size += sizeof(struct zeroos_alloc_header) + node->next->size;
node->next = node->next->next;
}
}
void *memcpy(void *dest, const void *src, size_t n) {
uint8_t *d = (uint8_t *)dest;
const uint8_t *s = (const uint8_t *)src;
for (size_t i = 0; i < n; i++) {
d[i] = s[i];
}
return dest;
}
void *memmove(void *dest, const void *src, size_t n) {
uint8_t *d = (uint8_t *)dest;
const uint8_t *s = (const uint8_t *)src;
if (d < s) {
for (size_t i = 0; i < n; i++) {
d[i] = s[i];
}
} else {
while (n > 0) {
n--;
d[n] = s[n];
}
}
return dest;
}
void *memset(void *dest, int c, size_t n) {
uint8_t *d = (uint8_t *)dest;
for (size_t i = 0; i < n; i++) {
d[i] = (uint8_t)c;
}
return dest;
}
int memcmp(const void *a, const void *b, size_t n) {
const uint8_t *pa = (const uint8_t *)a;
const uint8_t *pb = (const uint8_t *)b;
for (size_t i = 0; i < n; i++) {
if (pa[i] != pb[i]) {
return (int)pa[i] - (int)pb[i];
}
}
return 0;
}
size_t strlen(const char *s) {
size_t len = 0;
while (s[len]) {
len++;
}
return len;
}
int strcmp(const char *a, const char *b) {
while (*a && *a == *b) {
a++;
b++;
}
return (int)(uint8_t)*a - (int)(uint8_t)*b;
}
int strncmp(const char *a, const char *b, size_t n) {
for (size_t i = 0; i < n; i++) {
if (a[i] != b[i] || a[i] == 0 || b[i] == 0) {
return (int)(uint8_t)a[i] - (int)(uint8_t)b[i];
}
}
return 0;
}
char *strcpy(char *dest, const char *src) {
char *out = dest;
while ((*dest++ = *src++)) {
}
return out;
}
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)) {
uint8_t *items = (uint8_t *)base;
uint8_t *tmp = (uint8_t *)ext4_user_malloc(size);
if (!tmp) {
return;
}
for (size_t i = 1; i < nmemb; i++) {
memcpy(tmp, items + i * size, size);
size_t j = i;
while (j > 0 && compar(items + (j - 1) * size, tmp) > 0) {
memmove(items + j * size, items + (j - 1) * size, size);
j--;
}
memcpy(items + j * size, tmp, size);
}
ext4_user_free(tmp);
}
static int zeroos_bdev_open(struct ext4_blockdev *bdev) {
(void)bdev;
return EOK;
}
static int zeroos_bdev_close(struct ext4_blockdev *bdev) {
(void)bdev;
return EOK;
}
static int zeroos_bdev_lock(struct ext4_blockdev *bdev) {
(void)bdev;
return EOK;
}
static int zeroos_bdev_unlock(struct ext4_blockdev *bdev) {
(void)bdev;
return EOK;
}
static int zeroos_bdev_bread(
struct ext4_blockdev *bdev,
void *buf,
uint64_t blk_id,
uint32_t blk_cnt
) {
(void)bdev;
return zeroos_lwext4_read_blocks(buf, blk_id, blk_cnt);
}
static int zeroos_bdev_bwrite(
struct ext4_blockdev *bdev,
const void *buf,
uint64_t blk_id,
uint32_t blk_cnt
) {
(void)bdev;
return zeroos_lwext4_write_blocks(buf, blk_id, blk_cnt);
}
int zeroos_lwext4_mount(uint64_t block_count) {
if (zeroos_mounted) {
return EOK;
}
zeroos_bdev_iface.open = zeroos_bdev_open;
zeroos_bdev_iface.bread = zeroos_bdev_bread;
zeroos_bdev_iface.bwrite = zeroos_bdev_bwrite;
zeroos_bdev_iface.close = zeroos_bdev_close;
zeroos_bdev_iface.lock = zeroos_bdev_lock;
zeroos_bdev_iface.unlock = zeroos_bdev_unlock;
zeroos_bdev_iface.ph_bsize = 512;
zeroos_bdev_iface.ph_bcnt = block_count;
zeroos_bdev_iface.ph_bbuf = zeroos_bbuf;
zeroos_bdev_iface.ph_refctr = 0;
zeroos_bdev_iface.bread_ctr = 0;
zeroos_bdev_iface.bwrite_ctr = 0;
zeroos_bdev_iface.p_user = 0;
memset(&zeroos_bdev, 0, sizeof(zeroos_bdev));
zeroos_bdev.bdif = &zeroos_bdev_iface;
zeroos_bdev.part_offset = 0;
zeroos_bdev.part_size = block_count * 512u;
int rc = ext4_device_register(&zeroos_bdev, "zeroos");
if (rc != EOK) {
return rc;
}
rc = ext4_mount("zeroos", "/", false);
if (rc != EOK) {
return rc;
}
ext4_cache_write_back("/", false);
zeroos_mounted = 1;
return EOK;
}
int zeroos_lwext4_read(const char *path, uint64_t offset, void *buf, size_t len, size_t *done) {
ext4_file file;
*done = 0;
int rc = ext4_fopen2(&file, path, O_RDONLY);
if (rc != EOK) {
return rc;
}
rc = ext4_fseek(&file, (int64_t)offset, SEEK_SET);
if (rc == EOK) {
rc = ext4_fread(&file, buf, len, done);
}
ext4_fclose(&file);
return rc;
}
int zeroos_lwext4_write(const char *path, uint64_t offset, const void *buf, size_t len, size_t *done) {
ext4_file file;
*done = 0;
int rc = ext4_fopen2(&file, path, O_RDWR | O_CREAT);
if (rc != EOK) {
return rc;
}
rc = ext4_fseek(&file, (int64_t)offset, SEEK_SET);
if (rc == EOK) {
rc = ext4_fwrite(&file, buf, len, done);
}
ext4_fclose(&file);
ext4_cache_flush("/");
return rc;
}
int zeroos_lwext4_truncate(const char *path, uint64_t size) {
ext4_file file;
int rc = ext4_fopen2(&file, path, O_RDWR | O_CREAT);
if (rc != EOK) {
return rc;
}
rc = ext4_ftruncate(&file, size);
ext4_fclose(&file);
ext4_cache_flush("/");
return rc;
}
int zeroos_lwext4_size(const char *path, uint64_t *size) {
ext4_file file;
*size = 0;
int rc = ext4_fopen2(&file, path, O_RDONLY);
if (rc != EOK) {
return rc;
}
*size = ext4_fsize(&file);
ext4_fclose(&file);
return EOK;
}
int zeroos_lwext4_create_file(const char *path) {
ext4_file file;
int rc = ext4_fopen2(&file, path, O_RDWR | O_CREAT);
if (rc == EOK) {
ext4_fclose(&file);
ext4_cache_flush("/");
}
return rc;
}
int zeroos_lwext4_mkdir(const char *path) {
int rc = ext4_dir_mk(path);
if (rc == EEXIST) {
return EOK;
}
ext4_cache_flush("/");
return rc;
}
+1
View File
@@ -1,3 +1,4 @@
pub mod ext4;
pub mod lwext4;
pub mod uefi;
pub mod vfs;
+106 -19
View File
@@ -15,7 +15,6 @@ const EINVAL: isize = 22;
const ENOENT: isize = 2;
const ENOTDIR: isize = 20;
const EMFILE: isize = 24;
const EIO: isize = 5;
#[derive(Clone, Copy, PartialEq, Eq)]
enum Color {
@@ -226,12 +225,25 @@ impl Vfs {
return -EINVAL;
}
let offset = self.fds[fd].offset.min(node.len);
let available = node.len - offset;
if node.data.is_null() {
let count =
match crate::fs::lwext4::read_path_at(&node.path, self.fds[fd].offset, buffer, len)
{
Ok(count) => count,
Err(error) => return error,
};
self.fds[fd].offset = self.fds[fd].offset.saturating_add(count);
return count as isize;
}
let node_len = self.node_len(node_index);
let offset = self.fds[fd].offset.min(node_len);
let available = node_len - offset;
let count = len.min(available);
if count > 0 {
unsafe {
core::ptr::copy_nonoverlapping(node.data.add(offset), buffer, count);
let source = node.data.add(offset);
core::ptr::copy_nonoverlapping(source, buffer, count);
}
}
self.fds[fd].offset = offset + count;
@@ -245,6 +257,9 @@ impl Vfs {
let Some(node_index) = self.fds[fd].node else {
return -EBADF;
};
if self.nodes[node_index].kind == NodeKind::Regular {
return self.write_regular_fd(fd, node_index, buffer, len);
}
if self.nodes[node_index].kind != NodeKind::CharDevice {
return -EINVAL;
}
@@ -253,6 +268,31 @@ impl Vfs {
crate::console::write(buffer) as isize
}
fn write_regular_fd(
&mut self,
fd: usize,
node_index: usize,
buffer: *const u8,
len: usize,
) -> isize {
let offset = self.fds[fd].offset;
let count = match crate::fs::lwext4::write_path_at(
&self.nodes[node_index].path,
offset,
buffer,
len,
) {
Ok(count) => count,
Err(error) => return error,
};
let end = offset.saturating_add(count);
self.fds[fd].offset = end;
if end > self.nodes[node_index].len {
self.nodes[node_index].len = end;
}
count as isize
}
fn close_fd(&mut self, fd: usize) -> isize {
if fd < 3 || fd >= MAX_FDS || self.fds[fd].node.is_none() {
return -EBADF;
@@ -298,7 +338,7 @@ impl Vfs {
let Some(node_index) = self.fds[fd].node else {
return -EBADF;
};
let len = self.nodes[node_index].len as isize;
let len = self.node_len(node_index) as isize;
let current = self.fds[fd].offset as isize;
let next = match whence {
0 => offset,
@@ -323,6 +363,24 @@ impl Vfs {
write_stat(stat, self.nodes[node_index])
}
fn ftruncate_fd(&mut self, fd: usize, size: usize) -> isize {
if fd >= MAX_FDS {
return -EBADF;
}
let Some(node_index) = self.fds[fd].node else {
return -EBADF;
};
if self.nodes[node_index].kind != NodeKind::Regular {
return -EINVAL;
}
match crate::fs::lwext4::truncate_path(&self.nodes[node_index].path, size) {
Ok(()) => {}
Err(error) => return error,
}
self.nodes[node_index].len = size;
0
}
fn is_tty_fd(&self, fd: usize) -> bool {
if fd >= MAX_FDS {
return false;
@@ -387,6 +445,22 @@ impl Vfs {
write_stat(stat, self.nodes[node_index])
}
fn truncate_path(&mut self, path: &[u8], size: usize) -> isize {
let Some(node_index) = self.find(path) else {
return -ENOENT;
};
if self.nodes[node_index].kind != NodeKind::Regular {
return -EINVAL;
}
match crate::fs::lwext4::truncate_path(&self.nodes[node_index].path, size) {
Ok(()) => {
self.nodes[node_index].len = size;
0
}
Err(error) => error,
}
}
fn chdir_path(&mut self, path: &[u8]) -> isize {
let Some(node_index) = self.find(path) else {
return -ENOENT;
@@ -414,6 +488,15 @@ impl Vfs {
}
}
fn node_len(&self, node_index: usize) -> usize {
let node = self.nodes[node_index];
if node.kind == NodeKind::Regular && node.data.is_null() {
crate::fs::lwext4::size_path(&node.path).unwrap_or(node.len)
} else {
node.len
}
}
fn getcwd(&self, buffer: *mut u8, len: usize) -> isize {
if buffer.is_null() {
return -EFAULT;
@@ -576,10 +659,7 @@ pub unsafe fn init() {
}
}
pub unsafe fn mount_static_file(path: &'static [u8], data: *const u8, len: usize) -> Option<()> {
unsafe { vfs_mut().insert(path, NodeKind::Regular, data, len) }
}
#[allow(dead_code)]
pub fn mount_ext4_node(path: &[u8], kind: NodeKind) -> Option<()> {
unsafe { vfs_mut().insert(path, kind, core::ptr::null(), 0) }
}
@@ -655,6 +735,10 @@ pub fn fstat(fd: usize, stat: *mut u8) -> isize {
unsafe { vfs_ref().stat_fd(fd, stat) }
}
pub fn ftruncate(fd: usize, size: usize) -> isize {
unsafe { vfs_mut().ftruncate_fd(fd, size) }
}
pub fn is_tty(fd: usize) -> bool {
unsafe { vfs_ref().is_tty_fd(fd) }
}
@@ -684,6 +768,13 @@ pub fn stat_user_path(path: *const u8, stat: *mut u8) -> isize {
unsafe { vfs_ref().stat_path(&path, stat) }
}
pub fn truncate_user_path(path: *const u8, size: usize) -> isize {
let Some(path) = (unsafe { read_user_path(path) }) else {
return -EFAULT;
};
unsafe { vfs_mut().truncate_path(&path, size) }
}
pub fn statat_user_path(dirfd: isize, path: *const u8, stat: *mut u8) -> isize {
if unsafe { path_starts_with_slash(path) } || dirfd == AT_FDCWD {
return stat_user_path(path, stat);
@@ -761,9 +852,9 @@ fn create_directory_path(path: &[u8]) -> isize {
if unsafe { vfs_ref().find(path).is_some() } {
return 0;
}
match crate::fs::ext4::create_dir(path) {
match crate::fs::lwext4::create_dir(path) {
Ok(()) => unsafe { vfs_mut().create_path(path, NodeKind::Directory) },
Err(_) => -EIO,
Err(error) => error,
}
}
@@ -771,9 +862,9 @@ fn create_regular_path(path: &[u8]) -> isize {
if unsafe { vfs_ref().find(path).is_some() } {
return 0;
}
match crate::fs::ext4::create_file(path) {
match crate::fs::lwext4::create_file(path) {
Ok(()) => unsafe { vfs_mut().create_path(path, NodeKind::Regular) },
Err(_) => -EIO,
Err(error) => error,
}
}
@@ -781,15 +872,11 @@ pub fn getcwd(buffer: *mut u8, len: usize) -> isize {
unsafe { vfs_ref().getcwd(buffer, len) }
}
pub fn file_data_user_path(path: *const u8) -> Option<(*const u8, usize)> {
pub fn exec_path_user(path: *const u8) -> Option<[u8; MAX_PATH]> {
let path = unsafe { read_user_path(path) }?;
unsafe {
let index = vfs_ref().resolve_executable_path(&path)?;
let node = &vfs_ref().nodes[index];
if node.kind != NodeKind::Regular {
return None;
}
Some((node.data, node.len))
Some(vfs_ref().nodes[index].path)
}
}
+37 -101
View File
@@ -84,65 +84,34 @@ extern "efiapi" fn efi_main(
let _ = (stats.total_pages, stats.free_pages, stats.used_pages);
logger.ok(b"physical memory manager");
fs::vfs::init();
services::vfs::init();
logger.ok(b"vfs");
ipc::init();
logger.ok(b"ipc");
let ahci_disks = drivers::storage::ahci::init(memory);
let mut ext4_user_image = None;
let mut ext4_user_path = None;
if ahci_disks > 0 {
let storage = services::storage::init(memory);
if storage.ahci_disks > 0 {
logger.ok(b"ahci");
if let Some(mut disk) = drivers::storage::ahci::first_disk() {
match drivers::storage::partition::find_linux_partition(&mut disk) {
Ok(partition) => {
let mut root =
drivers::storage::partition::PartitionBlockDevice::new(disk, partition);
match fs::ext4::mount(&mut root) {
Ok(ext4) => {
logger.ok(b"ext4");
let _ = fs::ext4::mount_vfs_tree(&mut root, &ext4);
fs::ext4::register_root(root, ext4);
match fs::ext4::load_path(
&mut root,
&ext4,
memory,
b"/usr/bin/busybox",
) {
Ok(image) => {
ext4_user_image = Some(image);
ext4_user_path = Some(b"/usr/bin/busybox" as &'static [u8]);
logger.ok(b"ext4 /usr/bin/busybox");
}
Err(_) => match fs::ext4::load_path(
&mut root,
&ext4,
memory,
b"/usr/bin/sh",
) {
Ok(image) => {
ext4_user_image = Some(image);
ext4_user_path = Some(b"/usr/bin/sh" as &'static [u8]);
logger.ok(b"ext4 /usr/bin/sh");
}
Err(_) => logger.info(b"ext4 userspace not loaded"),
},
}
}
Err(_) => logger.info(b"ext4 not mounted"),
}
}
Err(_) => logger.info(b"no linux gpt partition"),
}
}
} else {
logger.info(b"no ahci disk");
}
if storage.ext4_mounted {
logger.ok(b"ext4");
} else if storage.ahci_disks > 0 {
logger.info(b"ext4 not mounted");
}
if storage.userspace_path == Some(b"/usr/bin/busybox" as &'static [u8]) {
logger.ok(b"ext4 /usr/bin/busybox");
} else if storage.userspace_path == Some(b"/usr/bin/sh" as &'static [u8]) {
logger.ok(b"ext4 /usr/bin/sh");
} else if storage.ext4_mounted {
logger.info(b"ext4 userspace not loaded");
}
let (loaded_user_image, loaded_user_path) = if let Some(image) = ext4_user_image {
(Some(image), ext4_user_path)
} else if let Some(image) =
let (loaded_user_image, loaded_user_path) = match storage.userspace_image {
Some(image) => (Some(image), storage.userspace_path),
None => {
if let Some(image) =
fs::uefi::load_file(image_handle, boot_services, memory, fs::uefi::BASH_PATH)
{
(Some(image), Some(b"/bin/bash" as &'static [u8]))
@@ -152,19 +121,11 @@ extern "efiapi" fn efi_main(
(Some(image), Some(b"/bin/sh" as &'static [u8]))
} else {
(None, None)
};
let userspace_image_found = loaded_user_image.is_some();
if loaded_user_image.is_some() {
if let (Some(image), Some(path)) = (&loaded_user_image, loaded_user_path) {
let _ = fs::vfs::mount_static_file(path, image.ptr, image.len);
let _ = fs::vfs::mount_static_file(b"/usr/bin/busybox", image.ptr, image.len);
let _ = fs::vfs::mount_static_file(b"/bin/busybox", image.ptr, image.len);
let _ = fs::vfs::mount_static_file(b"/usr/bin/sh", image.ptr, image.len);
let _ = fs::vfs::mount_static_file(b"/usr/bin/bash", image.ptr, image.len);
let _ = fs::vfs::mount_static_file(b"/bin/sh", image.ptr, image.len);
let _ = fs::vfs::mount_static_file(b"/bin/bash", image.ptr, image.len);
}
logger.ok(b"userspace image loaded from esp");
}
};
if loaded_user_image.is_some() {
logger.ok(b"userspace image loaded");
} else {
logger.info(b"no /bin/bash or /bin/sh on esp, using built-in user program");
}
@@ -189,8 +150,8 @@ extern "efiapi" fn efi_main(
logger.ok(b"address space");
let mut userspace_program = None;
let user_entry = if let Some(image) = loaded_user_image {
match elf::load_user_elf(&image, memory) {
let user_entry = if let Some(ref image) = loaded_user_image {
match elf::load_user_elf(image, memory) {
Some(program) => {
logger.ok(b"elf userspace program");
userspace_program = Some(user::UserAux {
@@ -209,6 +170,13 @@ extern "efiapi" fn efi_main(
} else {
None
};
let user_args: &[&[u8]] = match loaded_user_path {
Some(b"/usr/bin/busybox") => &[b"/usr/bin/busybox", b"sh"],
Some(b"/usr/bin/sh") => &[b"/usr/bin/sh"],
Some(b"/usr/bin/bash") => &[b"/usr/bin/bash"],
Some(b"/bin/sh") => &[b"/bin/sh"],
_ => &[b"/bin/bash"],
};
let Some(test_page) = memory.alloc_page() else {
halt_forever();
@@ -251,16 +219,10 @@ extern "efiapi" fn efi_main(
logger.ok(b"cr3");
tty.clear();
if user_entry.is_some() {
if loaded_user_path == Some(b"/usr/bin/busybox" as &'static [u8]) {
console::write(b"enter userspace: /usr/bin/busybox sh\r\n");
} else if loaded_user_path == Some(b"/usr/bin/sh" as &'static [u8]) {
console::write(b"enter userspace: /usr/bin/sh\r\n");
} else if loaded_user_path == Some(b"/usr/bin/bash" as &'static [u8]) {
console::write(b"enter userspace: /usr/bin/bash\r\n");
} else {
console::write(b"enter userspace: /bin/bash\r\n");
}
} else if userspace_image_found {
console::write(b"enter userspace: ");
console::write(user_args[0]);
console::write(b"\r\n");
} else if loaded_user_image.is_some() {
console::write(b"/bin/bash found but ELF load failed; enter built-in userspace\r\n");
} else {
console::write(b"/bin/bash not found on ESP; enter built-in userspace\r\n");
@@ -268,37 +230,11 @@ extern "efiapi" fn efi_main(
syscall::init(memory, &raw mut address_space);
io::enable_interrupts();
if let Some(entry) = user_entry {
if loaded_user_path == Some(b"/usr/bin/busybox" as &'static [u8]) {
user::enter_elf_with_args(
entry,
&[b"/usr/bin/busybox", b"sh"],
user_args,
user_program_or_default(user_entry, userspace_program),
);
} else if loaded_user_path == Some(b"/usr/bin/sh" as &'static [u8]) {
user::enter_elf_with_args(
entry,
&[b"/usr/bin/sh"],
user_program_or_default(user_entry, userspace_program),
);
} else if loaded_user_path == Some(b"/usr/bin/bash" as &'static [u8]) {
user::enter_elf_with_args(
entry,
&[b"/usr/bin/bash"],
user_program_or_default(user_entry, userspace_program),
);
} else if loaded_user_path == Some(b"/bin/sh" as &'static [u8]) {
user::enter_elf_with_args(
entry,
&[b"/bin/sh"],
user_program_or_default(user_entry, userspace_program),
);
} else {
user::enter_elf_with_args(
entry,
&[b"/bin/bash"],
user_program_or_default(user_entry, userspace_program),
);
}
} else {
user::enter();
}
+2
View File
@@ -1,10 +1,12 @@
#![allow(dead_code)]
pub mod storage;
pub mod tty;
pub mod vfs;
pub const SERVICE_VFS: usize = 1;
pub const SERVICE_TTY: usize = 2;
pub const SERVICE_STORAGE: usize = 3;
#[derive(Clone, Copy)]
pub enum ServiceRequest {
+54
View File
@@ -0,0 +1,54 @@
use crate::{drivers, fs, memory::PhysicalMemoryManager};
pub struct StorageInitResult {
pub ahci_disks: usize,
pub ext4_mounted: bool,
pub userspace_image: Option<fs::uefi::LoadedFile>,
pub userspace_path: Option<&'static [u8]>,
}
pub fn init(memory: &mut PhysicalMemoryManager) -> StorageInitResult {
let ahci_disks = unsafe { drivers::storage::ahci::init(memory) };
let mut result = StorageInitResult {
ahci_disks,
ext4_mounted: false,
userspace_image: None,
userspace_path: None,
};
if ahci_disks == 0 {
return result;
}
let Some(mut disk) = drivers::storage::ahci::first_disk() else {
return result;
};
let Ok(partition) = drivers::storage::partition::find_linux_partition(&mut disk) else {
return result;
};
let root = drivers::storage::partition::PartitionBlockDevice::new(disk, partition);
let root_sector_count = partition.last_lba - partition.first_lba + 1;
if unsafe { fs::lwext4::mount_root(root, root_sector_count) }.is_err() {
return result;
}
fs::lwext4::init_cache(memory as *mut PhysicalMemoryManager);
result.ext4_mounted = true;
let _ = fs::lwext4::mount_vfs_tree();
match fs::lwext4::load_path(memory, b"/usr/bin/busybox") {
Ok(image) => {
result.userspace_image = Some(image);
result.userspace_path = Some(b"/usr/bin/busybox");
}
Err(_) => {
if let Ok(image) = fs::lwext4::load_path(memory, b"/usr/bin/sh") {
result.userspace_image = Some(image);
result.userspace_path = Some(b"/usr/bin/sh");
}
}
}
result
}
+63 -3
View File
@@ -1,9 +1,69 @@
use crate::ipc::Message;
use crate::{console, fs::vfs, ipc::Message};
pub const OP_READ: u16 = 1;
pub const OP_WRITE: u16 = 2;
pub const OP_IOCTL: u16 = 3;
pub const OP_RESET: u16 = 4;
pub const OP_SET_RAW: u16 = 5;
pub const OP_SIZE: u16 = 6;
pub const OP_IS_TTY: u16 = 7;
pub fn dispatch(_message: Message) -> isize {
-38
pub fn dispatch(message: Message) -> isize {
match message.opcode {
OP_WRITE => console::write(unsafe {
core::slice::from_raw_parts(message.args[0] as *const u8, message.args[1])
}) as isize,
OP_RESET => {
console::reset_terminal();
0
}
OP_SET_RAW => {
console::set_raw_mode(message.args[0] != 0);
0
}
OP_SIZE => {
let (columns, rows) = console::size();
((rows & 0xffff) << 16 | (columns & 0xffff)) as isize
}
OP_IS_TTY => {
if message.args[0] <= 2 || vfs::is_tty(message.args[0]) {
1
} else {
0
}
}
_ => -38,
}
}
pub fn write(bytes: &[u8]) -> usize {
dispatch(message(
OP_WRITE,
[bytes.as_ptr() as usize, bytes.len(), 0, 0, 0, 0],
)) as usize
}
pub fn reset_terminal() {
let _ = dispatch(message(OP_RESET, [0, 0, 0, 0, 0, 0]));
}
pub fn set_raw_mode(enabled: bool) {
let _ = dispatch(message(OP_SET_RAW, [enabled as usize, 0, 0, 0, 0, 0]));
}
pub fn size() -> (usize, usize) {
let packed = dispatch(message(OP_SIZE, [0, 0, 0, 0, 0, 0])) as usize;
(packed & 0xffff, (packed >> 16) & 0xffff)
}
pub fn is_tty(fd: usize) -> bool {
dispatch(message(OP_IS_TTY, [fd, 0, 0, 0, 0, 0])) != 0
}
fn message(opcode: u16, args: [usize; 6]) -> Message {
Message {
sender: crate::task::current_pid(),
opcode,
args,
}
}
+181 -3
View File
@@ -1,10 +1,188 @@
use crate::ipc::Message;
use crate::{fs::vfs, ipc::Message};
pub const OP_OPEN: u16 = 1;
pub const OP_READ: u16 = 2;
pub const OP_WRITE: u16 = 3;
pub const OP_CLOSE: u16 = 4;
pub const OP_OPENAT: u16 = 5;
pub const OP_ACCESS: u16 = 6;
pub const OP_FSTAT: u16 = 7;
pub const OP_LSEEK: u16 = 8;
pub const OP_DUP: u16 = 9;
pub const OP_DUP_MIN: u16 = 10;
pub const OP_DUP2: u16 = 11;
pub const OP_GETCWD: u16 = 12;
pub const OP_CHDIR: u16 = 13;
pub const OP_MKDIR: u16 = 14;
pub const OP_MKDIRAT: u16 = 15;
pub const OP_TOUCH: u16 = 16;
pub const OP_GETDENTS64: u16 = 17;
pub const OP_STAT: u16 = 18;
pub const OP_STATAT: u16 = 19;
pub const OP_FTRUNCATE: u16 = 20;
pub const OP_TRUNCATE: u16 = 21;
pub fn dispatch(_message: Message) -> isize {
-38
pub fn dispatch(message: Message) -> isize {
match message.opcode {
OP_OPEN => vfs::open_user_path_with_flags(message.args[0] as *const u8, message.args[1]),
OP_OPENAT => vfs::openat_user_path(
message.args[0] as isize,
message.args[1] as *const u8,
message.args[2],
),
OP_ACCESS => vfs::access_user_path(message.args[0] as *const u8),
OP_READ => vfs::read(message.args[0], message.args[1] as *mut u8, message.args[2]),
OP_WRITE => vfs::write(
message.args[0],
message.args[1] as *const u8,
message.args[2],
),
OP_CLOSE => vfs::close(message.args[0]),
OP_FSTAT => vfs::fstat(message.args[0], message.args[1] as *mut u8),
OP_LSEEK => vfs::lseek(message.args[0], message.args[1] as isize, message.args[2]),
OP_DUP => vfs::dup(message.args[0]),
OP_DUP_MIN => vfs::dup_min(message.args[0], message.args[1]),
OP_DUP2 => vfs::dup2(message.args[0], message.args[1]),
OP_GETCWD => vfs::getcwd(message.args[0] as *mut u8, message.args[1]),
OP_CHDIR => vfs::chdir_user_path(message.args[0] as *const u8),
OP_MKDIR => vfs::mkdir_user_path(message.args[0] as *const u8),
OP_MKDIRAT => {
vfs::mkdirat_user_path(message.args[0] as isize, message.args[1] as *const u8)
}
OP_TOUCH => vfs::touch_user_path(message.args[0] as isize, message.args[1] as *const u8),
OP_GETDENTS64 => {
vfs::getdents64(message.args[0], message.args[1] as *mut u8, message.args[2])
}
OP_STAT => vfs::stat_user_path(message.args[0] as *const u8, message.args[1] as *mut u8),
OP_STATAT => vfs::statat_user_path(
message.args[0] as isize,
message.args[1] as *const u8,
message.args[2] as *mut u8,
),
OP_FTRUNCATE => vfs::ftruncate(message.args[0], message.args[1]),
OP_TRUNCATE => vfs::truncate_user_path(message.args[0] as *const u8, message.args[1]),
_ => -38,
}
}
pub const AT_FDCWD: isize = vfs::AT_FDCWD;
pub unsafe fn init() {
unsafe { vfs::init() }
}
pub fn open_user_path_with_flags(path: *const u8, flags: usize) -> isize {
dispatch(message(OP_OPEN, [path as usize, flags, 0, 0, 0, 0]))
}
pub fn openat_user_path(dirfd: isize, path: *const u8, flags: usize) -> isize {
dispatch(message(
OP_OPENAT,
[dirfd as usize, path as usize, flags, 0, 0, 0],
))
}
pub fn access_user_path(path: *const u8) -> isize {
dispatch(message(OP_ACCESS, [path as usize, 0, 0, 0, 0, 0]))
}
pub fn is_tty(fd: usize) -> bool {
vfs::is_tty(fd)
}
pub fn read(fd: usize, buffer: *mut u8, len: usize) -> isize {
dispatch(message(OP_READ, [fd, buffer as usize, len, 0, 0, 0]))
}
pub fn write(fd: usize, buffer: *const u8, len: usize) -> isize {
dispatch(message(OP_WRITE, [fd, buffer as usize, len, 0, 0, 0]))
}
pub fn poll(fd: usize, events: i16) -> Option<i16> {
vfs::poll(fd, events)
}
pub fn close(fd: usize) -> isize {
dispatch(message(OP_CLOSE, [fd, 0, 0, 0, 0, 0]))
}
pub fn fstat(fd: usize, stat: *mut u8) -> isize {
dispatch(message(OP_FSTAT, [fd, stat as usize, 0, 0, 0, 0]))
}
pub fn ftruncate(fd: usize, size: usize) -> isize {
dispatch(message(OP_FTRUNCATE, [fd, size, 0, 0, 0, 0]))
}
pub fn truncate_user_path(path: *const u8, size: usize) -> isize {
dispatch(message(OP_TRUNCATE, [path as usize, size, 0, 0, 0, 0]))
}
pub fn lseek(fd: usize, offset: isize, whence: usize) -> isize {
dispatch(message(OP_LSEEK, [fd, offset as usize, whence, 0, 0, 0]))
}
pub fn dup(fd: usize) -> isize {
dispatch(message(OP_DUP, [fd, 0, 0, 0, 0, 0]))
}
pub fn dup_min(fd: usize, min_newfd: usize) -> isize {
dispatch(message(OP_DUP_MIN, [fd, min_newfd, 0, 0, 0, 0]))
}
pub fn dup2(oldfd: usize, newfd: usize) -> isize {
dispatch(message(OP_DUP2, [oldfd, newfd, 0, 0, 0, 0]))
}
pub fn exec_path_user(path: *const u8) -> Option<[u8; 256]> {
vfs::exec_path_user(path)
}
pub fn getcwd(buffer: *mut u8, len: usize) -> isize {
dispatch(message(OP_GETCWD, [buffer as usize, len, 0, 0, 0, 0]))
}
pub fn chdir_user_path(path: *const u8) -> isize {
dispatch(message(OP_CHDIR, [path as usize, 0, 0, 0, 0, 0]))
}
pub fn mkdir_user_path(path: *const u8) -> isize {
dispatch(message(OP_MKDIR, [path as usize, 0, 0, 0, 0, 0]))
}
pub fn mkdirat_user_path(dirfd: isize, path: *const u8) -> isize {
dispatch(message(
OP_MKDIRAT,
[dirfd as usize, path as usize, 0, 0, 0, 0],
))
}
pub fn touch_user_path(dirfd: isize, path: *const u8) -> isize {
dispatch(message(
OP_TOUCH,
[dirfd as usize, path as usize, 0, 0, 0, 0],
))
}
pub fn getdents64(fd: usize, buffer: *mut u8, len: usize) -> isize {
dispatch(message(OP_GETDENTS64, [fd, buffer as usize, len, 0, 0, 0]))
}
pub fn stat_user_path(path: *const u8, stat: *mut u8) -> isize {
dispatch(message(OP_STAT, [path as usize, stat as usize, 0, 0, 0, 0]))
}
pub fn statat_user_path(dirfd: isize, path: *const u8, stat: *mut u8) -> isize {
dispatch(message(
OP_STATAT,
[dirfd as usize, path as usize, stat as usize, 0, 0, 0],
))
}
fn message(opcode: u16, args: [usize; 6]) -> Message {
Message {
sender: crate::task::current_pid(),
opcode,
args,
}
}
+65 -36
View File
@@ -1,13 +1,11 @@
use core::arch::{asm, global_asm};
use crate::{
console,
drivers::platform::timer,
elf,
fs::vfs,
gdt, ipc,
elf, gdt, ipc,
memory::{PAGE_SIZE, PhysicalMemoryManager},
paging::{AddressSpace, PageFlags},
services::{tty as tty_service, vfs},
task, user,
};
@@ -48,12 +46,17 @@ pub const SYS_FORK: usize = 57;
pub const SYS_VFORK: usize = 58;
pub const SYS_EXECVE: usize = 59;
pub const SYS_WAIT4: usize = 61;
pub const SYS_FSYNC: usize = 74;
pub const SYS_FDATASYNC: usize = 75;
pub const SYS_TRUNCATE: usize = 76;
pub const SYS_FTRUNCATE: usize = 77;
pub const SYS_FCNTL: usize = 72;
pub const SYS_DUP: usize = 32;
pub const SYS_DUP2: usize = 33;
pub const SYS_GETDENTS64: usize = 217;
pub const SYS_GETRANDOM: usize = 318;
pub const SYS_PRLIMIT64: usize = 302;
pub const SYS_SYNCFS: usize = 306;
pub const SYS_GETPGID: usize = 121;
pub const SYS_SETPGID: usize = 109;
pub const SYS_GETSID: usize = 124;
@@ -63,6 +66,7 @@ pub const SYS_CHDIR: usize = 80;
pub const SYS_MKDIR: usize = 83;
pub const SYS_READLINK: usize = 89;
pub const SYS_PRCTL: usize = 157;
pub const SYS_SYNC: usize = 162;
pub const SYS_TIME: usize = 201;
pub const SYS_GETUID: usize = 102;
pub const SYS_GETGID: usize = 104;
@@ -401,11 +405,15 @@ pub extern "C" fn zeroos_syscall_dispatch(
SYS_VFORK => sys_vfork(frame),
SYS_EXECVE => sys_execve(arg0 as *const u8, arg1 as *const *const u8, frame),
SYS_WAIT4 => sys_wait4(arg0 as isize, arg1 as *mut i32),
SYS_FSYNC | SYS_FDATASYNC => sys_sync(),
SYS_TRUNCATE => vfs::truncate_user_path(arg0 as *const u8, arg1),
SYS_FTRUNCATE => vfs::ftruncate(arg0, arg1),
SYS_FCNTL => sys_fcntl(arg0, arg1, arg2),
SYS_GETCWD => sys_getcwd(arg0 as *mut u8, arg1),
SYS_CHDIR => sys_chdir(arg0 as *const u8),
SYS_READLINK => -ENOENT,
SYS_PRCTL => 0,
SYS_SYNC => sys_sync(),
SYS_TIME => sys_time(arg0 as *mut u64),
SYS_UNAME => sys_uname(arg0 as *mut u8),
SYS_GETUID | SYS_GETGID | SYS_GETEUID | SYS_GETEGID => 0,
@@ -424,6 +432,7 @@ pub extern "C" fn zeroos_syscall_dispatch(
SYS_RSEQ => sys_rseq(arg0, arg1, arg2),
SYS_GETRANDOM => sys_getrandom(arg0 as *mut u8, arg1, arg2),
SYS_PRLIMIT64 => sys_prlimit64(arg0, arg1, arg2 as *const u8, arg3 as *mut u8),
SYS_SYNCFS => sys_sync(),
SYS_GETDENTS64 => trace_fs_result(
b"getdents",
arg0,
@@ -464,9 +473,9 @@ fn trace_syscall(number: usize) {
}
SYSCALL_TRACE_COUNT += 1;
}
console::write(b"sys ");
tty_service::write(b"sys ");
write_usize(number);
console::write(b"\n");
tty_service::write(b"\n");
}
fn sys_read(fd: usize, buffer: *mut u8, len: usize) -> isize {
@@ -477,6 +486,16 @@ fn sys_read(fd: usize, buffer: *mut u8, len: usize) -> isize {
return -EBADF;
}
if vfs::is_tty(fd) {
loop {
let read = vfs::read(fd, buffer, len);
if read != 0 {
return read;
}
core::hint::spin_loop();
}
}
loop {
if let Some(read) = ipc::read(fd, buffer, len) {
if read != -EAGAIN {
@@ -710,9 +729,9 @@ fn sys_fcntl(fd: usize, command: usize, arg: usize) -> isize {
F_GETFL => 0,
F_SETFL => 0,
_ => {
console::write(b"unsupported fcntl ");
tty_service::write(b"unsupported fcntl ");
write_usize(command);
console::write(b"\n");
tty_service::write(b"\n");
-EINVAL
}
}
@@ -726,7 +745,7 @@ fn sys_execve(path: *const u8, argv: *const *const u8, frame: *mut TrapFrame) ->
if path.is_null() || frame.is_null() {
return -EFAULT;
}
let Some((ptr, len)) = vfs::file_data_user_path(path) else {
let Some(exec_path) = vfs::exec_path_user(path) else {
return -ENOENT;
};
@@ -738,11 +757,13 @@ fn sys_execve(path: *const u8, argv: *const *const u8, frame: *mut TrapFrame) ->
args[index] = &arg_storage[index][..arg_lens[index]];
}
let image = crate::fs::uefi::LoadedFile { ptr, len };
unsafe {
if MEMORY.is_null() {
return -ENODEV;
}
let Ok(image) = crate::fs::lwext4::load_path(&mut *MEMORY, &exec_path) else {
return -ENOENT;
};
let Some(program) = elf::load_user_elf(&image, &mut *MEMORY) else {
return -ENOENT;
};
@@ -973,7 +994,7 @@ fn sys_ioctl(fd: usize, request: usize, arg: *mut u8, frame: *mut TrapFrame) ->
if arg.is_null() {
return -EBADF;
}
if fd > STDERR_FILENO && !vfs::is_tty(fd) {
if fd > STDERR_FILENO && !tty_service::is_tty(fd) {
return -EBADF;
}
@@ -1000,7 +1021,7 @@ fn sys_ioctl(fd: usize, request: usize, arg: *mut u8, frame: *mut TrapFrame) ->
0
}
TIOCGWINSZ => {
let (columns, rows) = console::size();
let (columns, rows) = tty_service::size();
let winsize = arg as *mut u16;
winsize.add(0).write(rows as u16);
winsize.add(1).write(columns as u16);
@@ -1034,7 +1055,7 @@ unsafe fn set_termios_mode(arg: *mut u8, _termios2: bool) {
unsafe {
let lflag = (arg as *const u32).add(3).read();
const ICANON: u32 = 0o0000002;
crate::console::set_raw_mode(lflag & ICANON == 0);
tty_service::set_raw_mode(lflag & ICANON == 0);
}
}
@@ -1134,7 +1155,7 @@ fn sys_arch_prctl(code: usize, addr: usize) -> isize {
}
fn sys_isatty(fd: usize) -> isize {
if fd <= STDERR_FILENO || vfs::is_tty(fd) {
if fd <= STDERR_FILENO || tty_service::is_tty(fd) {
1
} else {
0
@@ -1316,7 +1337,8 @@ fn sys_prlimit64(
}
fn sys_exit(code: isize, frame: *mut TrapFrame) -> isize {
crate::console::reset_terminal();
let _ = crate::fs::lwext4::flush_cache();
tty_service::reset_terminal();
if !frame.is_null() && task::exit_current(code, unsafe { &mut *frame }) {
restore_user_heap();
return unsafe { (*frame).rax as isize };
@@ -1329,6 +1351,13 @@ fn sys_exit(code: isize, frame: *mut TrapFrame) -> isize {
}
}
fn sys_sync() -> isize {
match crate::fs::lwext4::flush_cache() {
Ok(()) => 0,
Err(error) => error,
}
}
pub fn snapshot_user_heap() {
unsafe {
snapshot_user_elf_image();
@@ -1426,9 +1455,9 @@ fn trace_unsupported_syscall(number: usize) {
LAST_UNSUPPORTED_SYSCALL = number;
}
console::write(b"unsupported syscall ");
tty_service::write(b"unsupported syscall ");
write_usize(number);
console::write(b"\n");
tty_service::write(b"\n");
}
fn write_usize(mut value: usize) {
@@ -1436,7 +1465,7 @@ fn write_usize(mut value: usize) {
let mut index = buffer.len();
if value == 0 {
console::write(b"0");
tty_service::write(b"0");
return;
}
@@ -1446,19 +1475,19 @@ fn write_usize(mut value: usize) {
value /= 10;
}
console::write(&buffer[index..]);
tty_service::write(&buffer[index..]);
}
fn trace_alloc(name: &[u8], addr: usize, len: usize) {
if !TRACE_ALLOC {
return;
}
console::write(name);
console::write(b" ");
tty_service::write(name);
tty_service::write(b" ");
write_hex_usize(addr);
console::write(b" ");
tty_service::write(b" ");
write_hex_usize(len);
console::write(b"\n");
tty_service::write(b"\n");
}
fn trace_ioctl(request: usize, arg: usize, frame: *mut TrapFrame) {
@@ -1471,42 +1500,42 @@ fn trace_ioctl(request: usize, arg: usize, frame: *mut TrapFrame) {
}
IOCTL_TRACE_COUNT += 1;
}
console::write(b"ioctl req=");
tty_service::write(b"ioctl req=");
write_hex_usize(request);
console::write(b" arg=");
tty_service::write(b" arg=");
write_hex_usize(arg);
if !frame.is_null() {
let frame = unsafe { &*frame };
console::write(b" ret=");
tty_service::write(b" ret=");
write_hex_usize(frame.rcx);
console::write(b" rsp=");
tty_service::write(b" rsp=");
write_hex_usize(frame.rsp);
}
console::write(b"\n");
tty_service::write(b"\n");
}
fn trace_fs_result(name: &[u8], arg0: usize, arg1: usize, result: isize) -> isize {
if !TRACE_FS_FDS {
return result;
}
console::write(name);
console::write(b" a0=");
tty_service::write(name);
tty_service::write(b" a0=");
write_hex_usize(arg0);
console::write(b" a1=");
tty_service::write(b" a1=");
write_hex_usize(arg1);
console::write(b" -> ");
tty_service::write(b" -> ");
if result < 0 {
console::write(b"-");
tty_service::write(b"-");
write_usize((-result) as usize);
} else {
write_usize(result as usize);
}
console::write(b"\n");
tty_service::write(b"\n");
result
}
fn write_hex_usize(value: usize) {
console::write(b"0x");
tty_service::write(b"0x");
let mut shift = usize::BITS as usize;
let mut started = false;
while shift > 0 {
@@ -1519,7 +1548,7 @@ fn write_hex_usize(value: usize) {
} else {
b'a' + digit - 10
};
console::write(&[ch]);
tty_service::write(&[ch]);
}
}
}
Vendored Submodule
+1
Submodule third_party/lwext4 added at 58bcf89a12
+6
View File
@@ -0,0 +1,6 @@
#ifndef ZEROOS_ASSERT_H_
#define ZEROOS_ASSERT_H_
#define assert(x) ((void)(x))
#endif
+6
View File
@@ -0,0 +1,6 @@
#ifndef ZEROOS_ERRNO_H_
#define ZEROOS_ERRNO_H_
#include <ext4_errno.h>
#endif
+6
View File
@@ -0,0 +1,6 @@
#ifndef ZEROOS_FCNTL_H_
#define ZEROOS_FCNTL_H_
#include <ext4_oflags.h>
#endif
@@ -0,0 +1,23 @@
#ifndef ZEROOS_LWEXT4_GENERATED_CONFIG_H_
#define ZEROOS_LWEXT4_GENERATED_CONFIG_H_
#define CONFIG_EXT_FEATURE_SET_LVL F_SET_EXT4
#define CONFIG_JOURNALING_ENABLE 0
#define CONFIG_XATTR_ENABLE 1
#define CONFIG_EXTENTS_ENABLE 1
#define CONFIG_HAVE_OWN_ERRNO 1
#define CONFIG_DEBUG_PRINTF 0
#define CONFIG_DEBUG_ASSERT 0
#define CONFIG_HAVE_OWN_ASSERT 1
#define CONFIG_BLOCK_DEV_ENABLE_STATS 0
#define CONFIG_BLOCK_DEV_CACHE_SIZE 16
#define CONFIG_EXT4_MAX_BLOCKDEV_NAME 32
#define CONFIG_EXT4_BLOCKDEVS_COUNT 2
#define CONFIG_EXT4_MAX_MP_NAME 32
#define CONFIG_EXT4_MOUNTPOINTS_COUNT 2
#define CONFIG_HAVE_OWN_OFLAGS 1
#define CONFIG_MAX_TRUNCATE_SIZE (16ul * 1024ul * 1024ul)
#define CONFIG_UNALIGNED_ACCESS 0
#define CONFIG_USE_USER_MALLOC 1
#endif
+9
View File
@@ -0,0 +1,9 @@
#ifndef ZEROOS_INTTYPES_H_
#define ZEROOS_INTTYPES_H_
#include <stdint.h>
#define PRIu64 "llu"
#define PRId64 "lld"
#endif
+10
View File
@@ -0,0 +1,10 @@
#ifndef ZEROOS_STDARG_H_
#define ZEROOS_STDARG_H_
typedef __builtin_va_list va_list;
#define va_start(ap, last) __builtin_va_start(ap, last)
#define va_end(ap) __builtin_va_end(ap)
#define va_arg(ap, type) __builtin_va_arg(ap, type)
#define va_copy(dest, src) __builtin_va_copy(dest, src)
#endif
+8
View File
@@ -0,0 +1,8 @@
#ifndef ZEROOS_STDBOOL_H_
#define ZEROOS_STDBOOL_H_
#define bool _Bool
#define true 1
#define false 0
#endif
+8
View File
@@ -0,0 +1,8 @@
#ifndef ZEROOS_STDDEF_H_
#define ZEROOS_STDDEF_H_
#define NULL ((void *)0)
typedef __SIZE_TYPE__ size_t;
typedef __PTRDIFF_TYPE__ ptrdiff_t;
#endif
+18
View File
@@ -0,0 +1,18 @@
#ifndef ZEROOS_STDINT_H_
#define ZEROOS_STDINT_H_
typedef signed char int8_t;
typedef unsigned char uint8_t;
typedef short int16_t;
typedef unsigned short uint16_t;
typedef int int32_t;
typedef unsigned int uint32_t;
typedef long long int64_t;
typedef unsigned long long uint64_t;
typedef __INTPTR_TYPE__ intptr_t;
typedef __UINTPTR_TYPE__ uintptr_t;
#define UINT32_MAX 0xffffffffu
#define UINT64_MAX 0xffffffffffffffffull
#endif
+8
View File
@@ -0,0 +1,8 @@
#ifndef ZEROOS_STDIO_H_
#define ZEROOS_STDIO_H_
#define stdout ((void *)0)
int printf(const char *fmt, ...);
int fflush(void *stream);
#endif
+16
View File
@@ -0,0 +1,16 @@
#ifndef ZEROOS_STDLIB_H_
#define ZEROOS_STDLIB_H_
#include <stddef.h>
void *ext4_user_malloc(size_t size);
void *ext4_user_calloc(size_t count, size_t size);
void ext4_user_free(void *ptr);
void qsort(
void *base,
size_t nmemb,
size_t size,
int (*compar)(const void *, const void *)
);
#endif
+15
View File
@@ -0,0 +1,15 @@
#ifndef ZEROOS_STRING_H_
#define ZEROOS_STRING_H_
#include <stddef.h>
void *memcpy(void *dest, const void *src, size_t n);
void *memmove(void *dest, const void *src, size_t n);
void *memset(void *dest, int c, size_t n);
int memcmp(const void *a, const void *b, size_t n);
size_t strlen(const char *s);
int strcmp(const char *a, const char *b);
int strncmp(const char *a, const char *b, size_t n);
char *strcpy(char *dest, const char *src);
#endif
+6
View File
@@ -0,0 +1,6 @@
#ifndef ZEROOS_UNISTD_H_
#define ZEROOS_UNISTD_H_
#include <ext4_oflags.h>
#endif