Files
ZeroOS/src/fs/vfs.rs
T
2026-07-29 12:35:25 +08:00

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
}