mirror of
https://github.com/ZeroOSProject/ZeroOS.git
synced 2026-09-13 00:24:41 +08:00
1158 lines
33 KiB
Rust
1158 lines
33 KiB
Rust
const MAX_VFS_NODES: usize = 1024;
|
|
const MAX_FDS: usize = 512;
|
|
const MAX_AUTO_VFS_FDS: usize = 256;
|
|
const MAX_PATH: usize = 256;
|
|
|
|
pub const AT_FDCWD: isize = -100;
|
|
|
|
const S_IFDIR: u32 = 0o040000;
|
|
const S_IFCHR: u32 = 0o020000;
|
|
const S_IFREG: u32 = 0o100000;
|
|
|
|
const EBADF: isize = 9;
|
|
const EFAULT: isize = 14;
|
|
const EINVAL: isize = 22;
|
|
const ENOENT: isize = 2;
|
|
const ENOTDIR: isize = 20;
|
|
const EMFILE: isize = 24;
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
enum Color {
|
|
Red,
|
|
Black,
|
|
}
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
pub enum NodeKind {
|
|
Directory,
|
|
Regular,
|
|
CharDevice,
|
|
}
|
|
|
|
#[derive(Clone, Copy)]
|
|
struct VfsNode {
|
|
path: [u8; MAX_PATH],
|
|
kind: NodeKind,
|
|
data: *const u8,
|
|
len: usize,
|
|
parent: Option<usize>,
|
|
left: Option<usize>,
|
|
right: Option<usize>,
|
|
color: Color,
|
|
}
|
|
|
|
impl VfsNode {
|
|
const fn empty() -> Self {
|
|
Self {
|
|
path: [0; MAX_PATH],
|
|
kind: NodeKind::Regular,
|
|
data: core::ptr::null(),
|
|
len: 0,
|
|
parent: None,
|
|
left: None,
|
|
right: None,
|
|
color: Color::Black,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy)]
|
|
struct FileDescriptor {
|
|
node: Option<usize>,
|
|
offset: usize,
|
|
}
|
|
|
|
impl FileDescriptor {
|
|
const fn empty() -> Self {
|
|
Self {
|
|
node: None,
|
|
offset: 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct Vfs {
|
|
root: Option<usize>,
|
|
nodes: [VfsNode; MAX_VFS_NODES],
|
|
len: usize,
|
|
fds: [FileDescriptor; MAX_FDS],
|
|
cwd: [u8; MAX_PATH],
|
|
}
|
|
|
|
impl Vfs {
|
|
const fn new() -> Self {
|
|
Self {
|
|
root: None,
|
|
nodes: [VfsNode::empty(); MAX_VFS_NODES],
|
|
len: 0,
|
|
fds: [FileDescriptor::empty(); MAX_FDS],
|
|
cwd: [0; MAX_PATH],
|
|
}
|
|
}
|
|
|
|
fn reset(&mut self) {
|
|
self.root = None;
|
|
self.len = 0;
|
|
self.fds.fill(FileDescriptor::empty());
|
|
self.cwd = [0; MAX_PATH];
|
|
self.cwd[0] = b'/';
|
|
}
|
|
|
|
fn insert(&mut self, path: &[u8], kind: NodeKind, data: *const u8, len: usize) -> Option<()> {
|
|
if self.len >= MAX_VFS_NODES {
|
|
return None;
|
|
}
|
|
|
|
let mut parent = None;
|
|
let mut cursor = self.root;
|
|
while let Some(index) = cursor {
|
|
parent = cursor;
|
|
match compare_path(path, &self.nodes[index].path) {
|
|
core::cmp::Ordering::Less => cursor = self.nodes[index].left,
|
|
core::cmp::Ordering::Greater => cursor = self.nodes[index].right,
|
|
core::cmp::Ordering::Equal => {
|
|
self.nodes[index].kind = kind;
|
|
self.nodes[index].data = data;
|
|
self.nodes[index].len = len;
|
|
return Some(());
|
|
}
|
|
}
|
|
}
|
|
|
|
let index = self.len;
|
|
self.len += 1;
|
|
let mut stored_path = [0u8; MAX_PATH];
|
|
let path_len = nul_len(path).min(MAX_PATH - 1);
|
|
stored_path[..path_len].copy_from_slice(&path[..path_len]);
|
|
|
|
self.nodes[index] = VfsNode {
|
|
path: stored_path,
|
|
kind,
|
|
data,
|
|
len,
|
|
parent,
|
|
left: None,
|
|
right: None,
|
|
color: Color::Red,
|
|
};
|
|
|
|
if let Some(parent) = parent {
|
|
if compare_path(path, &self.nodes[parent].path).is_lt() {
|
|
self.nodes[parent].left = Some(index);
|
|
} else {
|
|
self.nodes[parent].right = Some(index);
|
|
}
|
|
} else {
|
|
self.root = Some(index);
|
|
}
|
|
|
|
self.insert_fixup(index);
|
|
Some(())
|
|
}
|
|
|
|
fn find(&self, path: &[u8]) -> Option<usize> {
|
|
let mut cursor = self.root;
|
|
while let Some(index) = cursor {
|
|
match compare_path(path, &self.nodes[index].path) {
|
|
core::cmp::Ordering::Less => cursor = self.nodes[index].left,
|
|
core::cmp::Ordering::Greater => cursor = self.nodes[index].right,
|
|
core::cmp::Ordering::Equal => return Some(index),
|
|
}
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
fn resolve_executable_path(&self, path: &[u8]) -> Option<usize> {
|
|
let node = self.find(path);
|
|
if let Some(index) = node {
|
|
let node = self.nodes[index];
|
|
if !(node.kind == NodeKind::Regular
|
|
&& node.len == 0
|
|
&& node.data.is_null()
|
|
&& is_busybox_applet_path(path))
|
|
{
|
|
return Some(index);
|
|
}
|
|
}
|
|
self.busybox_fallback(path).or(node)
|
|
}
|
|
|
|
fn open_path(&mut self, path: &[u8]) -> isize {
|
|
let Some(node) = self.resolve_executable_path(path) else {
|
|
return -ENOENT;
|
|
};
|
|
|
|
for fd in 3..MAX_AUTO_VFS_FDS {
|
|
if self.fds[fd].node.is_none() {
|
|
self.fds[fd] = FileDescriptor {
|
|
node: Some(node),
|
|
offset: 0,
|
|
};
|
|
return fd as isize;
|
|
}
|
|
}
|
|
|
|
-EMFILE
|
|
}
|
|
|
|
fn fd_dir_path(&self, fd: usize) -> Result<[u8; MAX_PATH], isize> {
|
|
if fd >= MAX_FDS {
|
|
return Err(-EBADF);
|
|
}
|
|
let Some(node) = self.fds[fd].node else {
|
|
return Err(-EBADF);
|
|
};
|
|
if self.nodes[node].kind != NodeKind::Directory {
|
|
return Err(-ENOTDIR);
|
|
}
|
|
Ok(self.nodes[node].path)
|
|
}
|
|
|
|
fn read_fd(&mut self, fd: usize, buffer: *mut u8, len: usize) -> isize {
|
|
if fd >= MAX_FDS || buffer.is_null() {
|
|
return -EBADF;
|
|
}
|
|
let Some(node_index) = self.fds[fd].node else {
|
|
return -EBADF;
|
|
};
|
|
let node = self.nodes[node_index];
|
|
if node.kind == NodeKind::CharDevice {
|
|
let buffer = unsafe { core::slice::from_raw_parts_mut(buffer, len) };
|
|
return crate::console::read(buffer) as isize;
|
|
}
|
|
if node.kind != NodeKind::Regular {
|
|
return -EINVAL;
|
|
}
|
|
|
|
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 {
|
|
let source = node.data.add(offset);
|
|
core::ptr::copy_nonoverlapping(source, buffer, count);
|
|
}
|
|
}
|
|
self.fds[fd].offset = offset + count;
|
|
count as isize
|
|
}
|
|
|
|
fn write_fd(&mut self, fd: usize, buffer: *const u8, len: usize) -> isize {
|
|
if fd >= MAX_FDS || buffer.is_null() {
|
|
return -EBADF;
|
|
}
|
|
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;
|
|
}
|
|
|
|
let buffer = unsafe { core::slice::from_raw_parts(buffer, len) };
|
|
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;
|
|
}
|
|
self.fds[fd] = FileDescriptor::empty();
|
|
0
|
|
}
|
|
|
|
fn dup_fd(&mut self, oldfd: usize, min_newfd: usize) -> isize {
|
|
if oldfd >= MAX_FDS || self.fds[oldfd].node.is_none() {
|
|
return -EBADF;
|
|
}
|
|
let start = min_newfd.max(3);
|
|
let end = if start < MAX_AUTO_VFS_FDS {
|
|
MAX_AUTO_VFS_FDS
|
|
} else {
|
|
MAX_FDS
|
|
};
|
|
for fd in start..end {
|
|
if self.fds[fd].node.is_none() {
|
|
self.fds[fd] = self.fds[oldfd];
|
|
return fd as isize;
|
|
}
|
|
}
|
|
-EMFILE
|
|
}
|
|
|
|
fn dup2_fd(&mut self, oldfd: usize, newfd: usize) -> isize {
|
|
if oldfd >= MAX_FDS || newfd >= MAX_FDS || self.fds[oldfd].node.is_none() {
|
|
return -EBADF;
|
|
}
|
|
if oldfd == newfd {
|
|
return newfd as isize;
|
|
}
|
|
self.fds[newfd] = self.fds[oldfd];
|
|
newfd as isize
|
|
}
|
|
|
|
fn lseek_fd(&mut self, fd: usize, offset: isize, whence: usize) -> isize {
|
|
if fd >= MAX_FDS {
|
|
return -EBADF;
|
|
}
|
|
let Some(node_index) = self.fds[fd].node else {
|
|
return -EBADF;
|
|
};
|
|
let len = self.node_len(node_index) as isize;
|
|
let current = self.fds[fd].offset as isize;
|
|
let next = match whence {
|
|
0 => offset,
|
|
1 => current.saturating_add(offset),
|
|
2 => len.saturating_add(offset),
|
|
_ => return -EINVAL,
|
|
};
|
|
if next < 0 {
|
|
return -EINVAL;
|
|
}
|
|
self.fds[fd].offset = next as usize;
|
|
next
|
|
}
|
|
|
|
fn stat_fd(&self, fd: usize, stat: *mut u8) -> isize {
|
|
if fd >= MAX_FDS || stat.is_null() {
|
|
return -EBADF;
|
|
}
|
|
let Some(node_index) = self.fds[fd].node else {
|
|
return -EBADF;
|
|
};
|
|
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;
|
|
}
|
|
let Some(node_index) = self.fds[fd].node else {
|
|
return false;
|
|
};
|
|
self.nodes[node_index].kind == NodeKind::CharDevice
|
|
}
|
|
|
|
fn getdents64_fd(&mut self, fd: usize, buffer: *mut u8, len: usize) -> isize {
|
|
if fd >= MAX_FDS || buffer.is_null() {
|
|
return -EBADF;
|
|
}
|
|
let Some(dir_index) = self.fds[fd].node else {
|
|
return -EBADF;
|
|
};
|
|
let dir = self.nodes[dir_index];
|
|
if dir.kind != NodeKind::Directory {
|
|
return -ENOTDIR;
|
|
}
|
|
|
|
let mut written = 0usize;
|
|
let mut cursor = self.fds[fd].offset;
|
|
if cursor == 0 {
|
|
if !write_dirent(buffer, len, &mut written, 1, NodeKind::Directory, b".") {
|
|
return 0;
|
|
}
|
|
cursor = 1;
|
|
}
|
|
if cursor == 1 {
|
|
if !write_dirent(buffer, len, &mut written, 2, NodeKind::Directory, b"..") {
|
|
self.fds[fd].offset = 1;
|
|
return written as isize;
|
|
}
|
|
cursor = 2;
|
|
}
|
|
while cursor < self.len {
|
|
let node = self.nodes[cursor];
|
|
cursor += 1;
|
|
let Some(name) = child_name(&dir.path, &node.path) else {
|
|
continue;
|
|
};
|
|
|
|
if !write_dirent(buffer, len, &mut written, cursor as u64, node.kind, name) {
|
|
cursor -= 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
self.fds[fd].offset = cursor;
|
|
written as isize
|
|
}
|
|
|
|
fn stat_path(&self, path: &[u8], stat: *mut u8) -> isize {
|
|
if stat.is_null() {
|
|
return -EFAULT;
|
|
}
|
|
let Some(node_index) = self.resolve_executable_path(path) else {
|
|
return -ENOENT;
|
|
};
|
|
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;
|
|
};
|
|
if self.nodes[node_index].kind != NodeKind::Directory {
|
|
return -ENOTDIR;
|
|
}
|
|
self.cwd = [0; MAX_PATH];
|
|
let path_len = nul_len(path).min(MAX_PATH - 1);
|
|
self.cwd[..path_len].copy_from_slice(&path[..path_len]);
|
|
if path_len == 0 {
|
|
self.cwd[0] = b'/';
|
|
}
|
|
0
|
|
}
|
|
|
|
fn create_path(&mut self, path: &[u8], kind: NodeKind) -> isize {
|
|
if self.find(path).is_some() {
|
|
return 0;
|
|
}
|
|
if self.insert(path, kind, core::ptr::null(), 0).is_some() {
|
|
0
|
|
} else {
|
|
-EMFILE
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
let cwd_len = nul_len(&self.cwd);
|
|
if len <= cwd_len {
|
|
return -EINVAL;
|
|
}
|
|
unsafe {
|
|
core::ptr::copy_nonoverlapping(self.cwd.as_ptr(), buffer, cwd_len);
|
|
buffer.add(cwd_len).write(0);
|
|
}
|
|
(cwd_len + 1) as isize
|
|
}
|
|
|
|
fn busybox_fallback(&self, path: &[u8]) -> Option<usize> {
|
|
if is_busybox_applet_path(path) {
|
|
self.find(b"/usr/bin/busybox")
|
|
.or_else(|| self.find(b"/bin/busybox"))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn insert_fixup(&mut self, mut node: usize) {
|
|
while self.color(self.nodes[node].parent) == Color::Red {
|
|
let parent = self.nodes[node].parent.unwrap();
|
|
let grandparent = self.nodes[parent].parent.unwrap();
|
|
|
|
if self.nodes[grandparent].left == Some(parent) {
|
|
let uncle = self.nodes[grandparent].right;
|
|
if self.color(uncle) == Color::Red {
|
|
self.nodes[parent].color = Color::Black;
|
|
if let Some(uncle) = uncle {
|
|
self.nodes[uncle].color = Color::Black;
|
|
}
|
|
self.nodes[grandparent].color = Color::Red;
|
|
node = grandparent;
|
|
} else {
|
|
if self.nodes[parent].right == Some(node) {
|
|
node = parent;
|
|
self.rotate_left(node);
|
|
}
|
|
let parent = self.nodes[node].parent.unwrap();
|
|
let grandparent = self.nodes[parent].parent.unwrap();
|
|
self.nodes[parent].color = Color::Black;
|
|
self.nodes[grandparent].color = Color::Red;
|
|
self.rotate_right(grandparent);
|
|
}
|
|
} else {
|
|
let uncle = self.nodes[grandparent].left;
|
|
if self.color(uncle) == Color::Red {
|
|
self.nodes[parent].color = Color::Black;
|
|
if let Some(uncle) = uncle {
|
|
self.nodes[uncle].color = Color::Black;
|
|
}
|
|
self.nodes[grandparent].color = Color::Red;
|
|
node = grandparent;
|
|
} else {
|
|
if self.nodes[parent].left == Some(node) {
|
|
node = parent;
|
|
self.rotate_right(node);
|
|
}
|
|
let parent = self.nodes[node].parent.unwrap();
|
|
let grandparent = self.nodes[parent].parent.unwrap();
|
|
self.nodes[parent].color = Color::Black;
|
|
self.nodes[grandparent].color = Color::Red;
|
|
self.rotate_left(grandparent);
|
|
}
|
|
}
|
|
}
|
|
|
|
if let Some(root) = self.root {
|
|
self.nodes[root].color = Color::Black;
|
|
}
|
|
}
|
|
|
|
fn rotate_left(&mut self, node: usize) {
|
|
let Some(right) = self.nodes[node].right else {
|
|
return;
|
|
};
|
|
self.nodes[node].right = self.nodes[right].left;
|
|
if let Some(right_left) = self.nodes[right].left {
|
|
self.nodes[right_left].parent = Some(node);
|
|
}
|
|
|
|
self.nodes[right].parent = self.nodes[node].parent;
|
|
if let Some(parent) = self.nodes[node].parent {
|
|
if self.nodes[parent].left == Some(node) {
|
|
self.nodes[parent].left = Some(right);
|
|
} else {
|
|
self.nodes[parent].right = Some(right);
|
|
}
|
|
} else {
|
|
self.root = Some(right);
|
|
}
|
|
|
|
self.nodes[right].left = Some(node);
|
|
self.nodes[node].parent = Some(right);
|
|
}
|
|
|
|
fn rotate_right(&mut self, node: usize) {
|
|
let Some(left) = self.nodes[node].left else {
|
|
return;
|
|
};
|
|
self.nodes[node].left = self.nodes[left].right;
|
|
if let Some(left_right) = self.nodes[left].right {
|
|
self.nodes[left_right].parent = Some(node);
|
|
}
|
|
|
|
self.nodes[left].parent = self.nodes[node].parent;
|
|
if let Some(parent) = self.nodes[node].parent {
|
|
if self.nodes[parent].right == Some(node) {
|
|
self.nodes[parent].right = Some(left);
|
|
} else {
|
|
self.nodes[parent].left = Some(left);
|
|
}
|
|
} else {
|
|
self.root = Some(left);
|
|
}
|
|
|
|
self.nodes[left].right = Some(node);
|
|
self.nodes[node].parent = Some(left);
|
|
}
|
|
|
|
fn color(&self, node: Option<usize>) -> Color {
|
|
node.map(|index| self.nodes[index].color)
|
|
.unwrap_or(Color::Black)
|
|
}
|
|
}
|
|
|
|
static mut VFS: Vfs = Vfs::new();
|
|
|
|
pub unsafe fn init() {
|
|
unsafe {
|
|
let vfs = vfs_mut();
|
|
vfs.reset();
|
|
let _ = vfs.insert(b"/", NodeKind::Directory, core::ptr::null(), 0);
|
|
let _ = vfs.insert(b"/bin", NodeKind::Directory, core::ptr::null(), 0);
|
|
let _ = vfs.insert(b"/dev", NodeKind::Directory, core::ptr::null(), 0);
|
|
let _ = vfs.insert(b"/dev/tty", NodeKind::CharDevice, core::ptr::null(), 0);
|
|
let _ = vfs.insert(b"/etc", NodeKind::Directory, core::ptr::null(), 0);
|
|
let _ = vfs.insert(b"/tmp", NodeKind::Directory, core::ptr::null(), 0);
|
|
let _ = vfs.insert(b"/usr", NodeKind::Directory, core::ptr::null(), 0);
|
|
let _ = vfs.insert(b"/usr/bin", NodeKind::Directory, core::ptr::null(), 0);
|
|
if let Some(tty) = vfs.find(b"/dev/tty") {
|
|
vfs.fds[0] = FileDescriptor {
|
|
node: Some(tty),
|
|
offset: 0,
|
|
};
|
|
vfs.fds[1] = FileDescriptor {
|
|
node: Some(tty),
|
|
offset: 0,
|
|
};
|
|
vfs.fds[2] = FileDescriptor {
|
|
node: Some(tty),
|
|
offset: 0,
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn mount_ext4_node(path: &[u8], kind: NodeKind) -> Option<()> {
|
|
unsafe { vfs_mut().insert(path, kind, core::ptr::null(), 0) }
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub fn open_user_path(path: *const u8) -> isize {
|
|
open_user_path_with_flags(path, 0)
|
|
}
|
|
|
|
pub fn open_user_path_with_flags(path: *const u8, flags: usize) -> isize {
|
|
let Some(path) = (unsafe { read_user_path(path) }) else {
|
|
return -EFAULT;
|
|
};
|
|
if flags & 0o100 != 0 && unsafe { vfs_ref().find(&path).is_none() } {
|
|
let result = create_regular_path(&path);
|
|
if result < 0 {
|
|
return result;
|
|
}
|
|
}
|
|
unsafe { vfs_mut().open_path(&path) }
|
|
}
|
|
|
|
pub fn openat_user_path(dirfd: isize, path: *const u8, flags: usize) -> isize {
|
|
if unsafe { path_starts_with_slash(path) } || dirfd == AT_FDCWD {
|
|
return open_user_path_with_flags(path, flags);
|
|
}
|
|
|
|
let base = match unsafe { vfs_ref().fd_dir_path(dirfd as usize) } {
|
|
Ok(path) => path,
|
|
Err(error) => return error,
|
|
};
|
|
let Some(path) = (unsafe { read_user_path_relative(&base, path) }) else {
|
|
return -EFAULT;
|
|
};
|
|
if flags & 0o100 != 0 && unsafe { vfs_ref().find(&path).is_none() } {
|
|
let result = create_regular_path(&path);
|
|
if result < 0 {
|
|
return result;
|
|
}
|
|
}
|
|
unsafe { vfs_mut().open_path(&path) }
|
|
}
|
|
|
|
pub fn read(fd: usize, buffer: *mut u8, len: usize) -> isize {
|
|
unsafe { vfs_mut().read_fd(fd, buffer, len) }
|
|
}
|
|
|
|
pub fn write(fd: usize, buffer: *const u8, len: usize) -> isize {
|
|
unsafe { vfs_mut().write_fd(fd, buffer, len) }
|
|
}
|
|
|
|
pub fn close(fd: usize) -> isize {
|
|
unsafe { vfs_mut().close_fd(fd) }
|
|
}
|
|
|
|
pub fn dup(fd: usize) -> isize {
|
|
unsafe { vfs_mut().dup_fd(fd, 3) }
|
|
}
|
|
|
|
pub fn dup_min(fd: usize, min_newfd: usize) -> isize {
|
|
unsafe { vfs_mut().dup_fd(fd, min_newfd) }
|
|
}
|
|
|
|
pub fn dup2(oldfd: usize, newfd: usize) -> isize {
|
|
unsafe { vfs_mut().dup2_fd(oldfd, newfd) }
|
|
}
|
|
|
|
pub fn lseek(fd: usize, offset: isize, whence: usize) -> isize {
|
|
unsafe { vfs_mut().lseek_fd(fd, offset, whence) }
|
|
}
|
|
|
|
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) }
|
|
}
|
|
|
|
pub fn poll(fd: usize, events: i16) -> Option<i16> {
|
|
if !unsafe { vfs_ref().is_tty_fd(fd) } {
|
|
return None;
|
|
}
|
|
let mut revents = 0;
|
|
if events & 0x0001 != 0 && crate::console::has_input() {
|
|
revents |= 0x0001;
|
|
}
|
|
if events & 0x0004 != 0 {
|
|
revents |= 0x0004;
|
|
}
|
|
Some(revents)
|
|
}
|
|
|
|
pub fn getdents64(fd: usize, buffer: *mut u8, len: usize) -> isize {
|
|
unsafe { vfs_mut().getdents64_fd(fd, buffer, len) }
|
|
}
|
|
|
|
pub fn stat_user_path(path: *const u8, stat: *mut u8) -> isize {
|
|
let Some(path) = (unsafe { read_user_path(path) }) else {
|
|
return -EFAULT;
|
|
};
|
|
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);
|
|
}
|
|
|
|
let base = match unsafe { vfs_ref().fd_dir_path(dirfd as usize) } {
|
|
Ok(path) => path,
|
|
Err(_) => return stat_user_path(path, stat),
|
|
};
|
|
let Some(path) = (unsafe { read_user_path_relative(&base, path) }) else {
|
|
return -EFAULT;
|
|
};
|
|
unsafe { vfs_ref().stat_path(&path, stat) }
|
|
}
|
|
|
|
pub fn access_user_path(path: *const u8) -> isize {
|
|
let Some(path) = (unsafe { read_user_path(path) }) else {
|
|
return -EFAULT;
|
|
};
|
|
unsafe {
|
|
if vfs_ref().resolve_executable_path(&path).is_some() {
|
|
0
|
|
} else {
|
|
-ENOENT
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn chdir_user_path(path: *const u8) -> isize {
|
|
let Some(path) = (unsafe { read_user_path(path) }) else {
|
|
return -EFAULT;
|
|
};
|
|
unsafe { vfs_mut().chdir_path(&path) }
|
|
}
|
|
|
|
pub fn mkdir_user_path(path: *const u8) -> isize {
|
|
let Some(path) = (unsafe { read_user_path(path) }) else {
|
|
return -EFAULT;
|
|
};
|
|
create_directory_path(&path)
|
|
}
|
|
|
|
pub fn mkdirat_user_path(dirfd: isize, path: *const u8) -> isize {
|
|
if unsafe { path_starts_with_slash(path) } || dirfd == AT_FDCWD {
|
|
return mkdir_user_path(path);
|
|
}
|
|
|
|
let base = match unsafe { vfs_ref().fd_dir_path(dirfd as usize) } {
|
|
Ok(path) => path,
|
|
Err(error) => return error,
|
|
};
|
|
let Some(path) = (unsafe { read_user_path_relative(&base, path) }) else {
|
|
return -EFAULT;
|
|
};
|
|
create_directory_path(&path)
|
|
}
|
|
|
|
pub fn touch_user_path(dirfd: isize, path: *const u8) -> isize {
|
|
let resolved = if unsafe { path_starts_with_slash(path) } || dirfd == AT_FDCWD {
|
|
unsafe { read_user_path(path) }
|
|
} else {
|
|
let base = match unsafe { vfs_ref().fd_dir_path(dirfd as usize) } {
|
|
Ok(path) => path,
|
|
Err(error) => return error,
|
|
};
|
|
unsafe { read_user_path_relative(&base, path) }
|
|
};
|
|
let Some(path) = resolved else {
|
|
return -EFAULT;
|
|
};
|
|
create_regular_path(&path)
|
|
}
|
|
|
|
fn create_directory_path(path: &[u8]) -> isize {
|
|
if unsafe { vfs_ref().find(path).is_some() } {
|
|
return 0;
|
|
}
|
|
match crate::fs::lwext4::create_dir(path) {
|
|
Ok(()) => unsafe { vfs_mut().create_path(path, NodeKind::Directory) },
|
|
Err(error) => error,
|
|
}
|
|
}
|
|
|
|
fn create_regular_path(path: &[u8]) -> isize {
|
|
if unsafe { vfs_ref().find(path).is_some() } {
|
|
return 0;
|
|
}
|
|
match crate::fs::lwext4::create_file(path) {
|
|
Ok(()) => unsafe { vfs_mut().create_path(path, NodeKind::Regular) },
|
|
Err(error) => error,
|
|
}
|
|
}
|
|
|
|
pub fn getcwd(buffer: *mut u8, len: usize) -> isize {
|
|
unsafe { vfs_ref().getcwd(buffer, len) }
|
|
}
|
|
|
|
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)?;
|
|
Some(vfs_ref().nodes[index].path)
|
|
}
|
|
}
|
|
|
|
unsafe fn vfs_mut() -> &'static mut Vfs {
|
|
unsafe { &mut *(&raw mut VFS) }
|
|
}
|
|
|
|
unsafe fn vfs_ref() -> &'static Vfs {
|
|
unsafe { &*(&raw const VFS) }
|
|
}
|
|
|
|
unsafe fn read_user_path(path: *const u8) -> Option<[u8; MAX_PATH]> {
|
|
if path.is_null() {
|
|
return None;
|
|
}
|
|
|
|
let first = unsafe { path.read() };
|
|
|
|
if first == 0 {
|
|
return None;
|
|
}
|
|
if first != b'/' {
|
|
return unsafe { read_user_path_relative(&vfs_ref().cwd, path) };
|
|
}
|
|
unsafe { normalize_user_path(path, None) }
|
|
}
|
|
|
|
unsafe fn read_user_path_relative(base: &[u8], path: *const u8) -> Option<[u8; MAX_PATH]> {
|
|
if path.is_null() {
|
|
return None;
|
|
}
|
|
if unsafe { path_starts_with_slash(path) } {
|
|
return unsafe { normalize_user_path(path, None) };
|
|
}
|
|
|
|
unsafe { normalize_user_path(path, Some(base)) }
|
|
}
|
|
|
|
unsafe fn normalize_user_path(path: *const u8, base: Option<&[u8]>) -> Option<[u8; MAX_PATH]> {
|
|
let mut buffer = [0u8; MAX_PATH];
|
|
let mut output_index = 0usize;
|
|
|
|
if let Some(base) = base {
|
|
let base_len = nul_len(base);
|
|
if base_len == 0 {
|
|
return None;
|
|
}
|
|
for byte in &base[..base_len] {
|
|
if output_index >= MAX_PATH - 1 {
|
|
return None;
|
|
}
|
|
buffer[output_index] = *byte;
|
|
output_index += 1;
|
|
}
|
|
} else {
|
|
buffer[output_index] = b'/';
|
|
output_index += 1;
|
|
}
|
|
|
|
let mut input_index = 0;
|
|
loop {
|
|
while unsafe { path.add(input_index).read() } == b'/' {
|
|
input_index += 1;
|
|
}
|
|
|
|
let component_start = input_index;
|
|
while {
|
|
let byte = unsafe { path.add(input_index).read() };
|
|
byte != 0 && byte != b'/'
|
|
} {
|
|
input_index += 1;
|
|
}
|
|
let component_len = input_index - component_start;
|
|
|
|
if component_len == 0 {
|
|
if output_index == 0 {
|
|
buffer[0] = b'/';
|
|
}
|
|
return Some(buffer);
|
|
}
|
|
|
|
if component_len == 1 && unsafe { path.add(component_start).read() } == b'.' {
|
|
continue;
|
|
}
|
|
|
|
if component_len == 2
|
|
&& unsafe { path.add(component_start).read() } == b'.'
|
|
&& unsafe { path.add(component_start + 1).read() } == b'.'
|
|
{
|
|
pop_path_component(&mut buffer, &mut output_index);
|
|
continue;
|
|
}
|
|
|
|
if output_index == 0 {
|
|
buffer[output_index] = b'/';
|
|
output_index += 1;
|
|
}
|
|
if !(output_index == 1 && buffer[0] == b'/') {
|
|
if output_index >= MAX_PATH - 1 {
|
|
return None;
|
|
}
|
|
buffer[output_index] = b'/';
|
|
output_index += 1;
|
|
}
|
|
if output_index + component_len >= MAX_PATH {
|
|
return None;
|
|
}
|
|
for index in 0..component_len {
|
|
buffer[output_index + index] = unsafe { path.add(component_start + index).read() };
|
|
}
|
|
output_index += component_len;
|
|
}
|
|
}
|
|
|
|
fn pop_path_component(buffer: &mut [u8; MAX_PATH], output_index: &mut usize) {
|
|
if *output_index <= 1 {
|
|
buffer[0] = b'/';
|
|
for byte in &mut buffer[1..] {
|
|
*byte = 0;
|
|
}
|
|
*output_index = 1;
|
|
return;
|
|
}
|
|
|
|
let mut index = *output_index;
|
|
while index > 1 && buffer[index - 1] == b'/' {
|
|
index -= 1;
|
|
}
|
|
while index > 1 && buffer[index - 1] != b'/' {
|
|
index -= 1;
|
|
}
|
|
if index <= 1 {
|
|
buffer[0] = b'/';
|
|
index = 1;
|
|
} else {
|
|
index -= 1;
|
|
}
|
|
for byte in &mut buffer[index..*output_index] {
|
|
*byte = 0;
|
|
}
|
|
*output_index = index;
|
|
}
|
|
|
|
unsafe fn path_starts_with_slash(path: *const u8) -> bool {
|
|
if path.is_null() {
|
|
return false;
|
|
}
|
|
unsafe { path.read() == b'/' }
|
|
}
|
|
|
|
fn compare_path(left: &[u8], right: &[u8]) -> core::cmp::Ordering {
|
|
let mut index = 0;
|
|
loop {
|
|
let left_byte = byte_at(left, index);
|
|
let right_byte = byte_at(right, index);
|
|
match left_byte.cmp(&right_byte) {
|
|
core::cmp::Ordering::Equal => {
|
|
if left_byte == 0 {
|
|
return core::cmp::Ordering::Equal;
|
|
}
|
|
}
|
|
ordering => return ordering,
|
|
}
|
|
index += 1;
|
|
}
|
|
}
|
|
|
|
fn byte_at(path: &[u8], index: usize) -> u8 {
|
|
path.get(index).copied().unwrap_or(0)
|
|
}
|
|
|
|
fn is_busybox_applet_path(path: &[u8]) -> bool {
|
|
let path_len = nul_len(path);
|
|
if path_len == 0 {
|
|
return false;
|
|
}
|
|
|
|
let name = if path_len > b"/bin/".len() && &path[..b"/bin/".len()] == b"/bin/" {
|
|
&path[b"/bin/".len()..path_len]
|
|
} else if path_len > b"/usr/bin/".len() && &path[..b"/usr/bin/".len()] == b"/usr/bin/" {
|
|
&path[b"/usr/bin/".len()..path_len]
|
|
} else {
|
|
return false;
|
|
};
|
|
|
|
!name.is_empty() && !name.contains(&b'/')
|
|
}
|
|
|
|
fn align_up(value: usize, align: usize) -> usize {
|
|
(value + align - 1) & !(align - 1)
|
|
}
|
|
|
|
fn dirent_type(kind: NodeKind) -> u8 {
|
|
match kind {
|
|
NodeKind::Directory => 4,
|
|
NodeKind::Regular => 8,
|
|
NodeKind::CharDevice => 2,
|
|
}
|
|
}
|
|
|
|
fn write_dirent(
|
|
buffer: *mut u8,
|
|
buffer_len: usize,
|
|
written: &mut usize,
|
|
ino: u64,
|
|
kind: NodeKind,
|
|
name: &[u8],
|
|
) -> bool {
|
|
let reclen = align_up(19 + name.len() + 1, 8);
|
|
if reclen > u16::MAX as usize || *written + reclen > buffer_len {
|
|
return false;
|
|
}
|
|
|
|
unsafe {
|
|
let entry = buffer.add(*written);
|
|
(entry as *mut u64).write(ino);
|
|
(entry.add(8) as *mut i64).write(ino as i64);
|
|
(entry.add(16) as *mut u16).write(reclen as u16);
|
|
entry.add(18).write(dirent_type(kind));
|
|
core::ptr::copy_nonoverlapping(name.as_ptr(), entry.add(19), name.len());
|
|
entry.add(19 + name.len()).write(0);
|
|
if reclen > 20 + name.len() {
|
|
core::ptr::write_bytes(entry.add(20 + name.len()), 0, reclen - 20 - name.len());
|
|
}
|
|
}
|
|
|
|
*written += reclen;
|
|
true
|
|
}
|
|
|
|
fn child_name<'a>(parent: &[u8], child: &'a [u8]) -> Option<&'a [u8]> {
|
|
if compare_path(parent, child).is_eq() {
|
|
return None;
|
|
}
|
|
let parent_len = nul_len(parent);
|
|
let child_len = nul_len(child);
|
|
if parent_len == 1 && parent[0] == b'/' {
|
|
let name = &child[1..child_len];
|
|
if !name.is_empty() && !name.contains(&b'/') {
|
|
return Some(name);
|
|
}
|
|
return None;
|
|
}
|
|
if child_len <= parent_len + 1
|
|
|| &child[..parent_len] != &parent[..parent_len]
|
|
|| child[parent_len] != b'/'
|
|
{
|
|
return None;
|
|
}
|
|
let name = &child[parent_len + 1..child_len];
|
|
if !name.is_empty() && !name.contains(&b'/') {
|
|
Some(name)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn nul_len(path: &[u8]) -> usize {
|
|
path.iter()
|
|
.position(|byte| *byte == 0)
|
|
.unwrap_or(path.len())
|
|
}
|
|
|
|
fn write_stat(stat: *mut u8, node: VfsNode) -> isize {
|
|
unsafe {
|
|
core::ptr::write_bytes(stat, 0, 144);
|
|
let mode = match node.kind {
|
|
NodeKind::Directory => S_IFDIR | 0o755,
|
|
NodeKind::Regular => S_IFREG | 0o644,
|
|
NodeKind::CharDevice => S_IFCHR | 0o600,
|
|
};
|
|
(stat.add(24) as *mut u32).write(mode);
|
|
(stat.add(48) as *mut u64).write(node.len as u64);
|
|
(stat.add(56) as *mut u64).write(4096);
|
|
(stat.add(64) as *mut u64).write(node.len.div_ceil(512) as u64);
|
|
}
|
|
0
|
|
}
|