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, left: Option, right: Option, 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, offset: usize, } impl FileDescriptor { const fn empty() -> Self { Self { node: None, offset: 0, } } } pub struct Vfs { root: Option, 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 { 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 { 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 { 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) -> 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 { 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 }