Merge branch 'external-initfs' into 'master'
External initfs See merge request redox-os/kernel!192
This commit is contained in:
17
Cargo.lock
generated
17
Cargo.lock
generated
@@ -76,6 +76,7 @@ dependencies = [
|
||||
"log",
|
||||
"memoffset",
|
||||
"raw-cpuid",
|
||||
"redox-initfs",
|
||||
"redox_syscall",
|
||||
"rmm",
|
||||
"rustc-cfg",
|
||||
@@ -114,9 +115,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
version = "0.4.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
@@ -138,13 +139,21 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
|
||||
|
||||
[[package]]
|
||||
name = "raw-cpuid"
|
||||
version = "10.2.0"
|
||||
version = "10.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "929f54e29691d4e6a9cc558479de70db7aa3d98cd6fe7ab86d7507aa2886b9d2"
|
||||
checksum = "738bc47119e3eeccc7e94c4a506901aea5e7b4944ecd0829cbebf4af04ceda12"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox-initfs"
|
||||
version = "0.1.0"
|
||||
source = "git+https://gitlab.redox-os.org/redox-os/redox-initfs.git#89b8fb8984cf96c418880b7dcd9ce3d6afc3f71c"
|
||||
dependencies = [
|
||||
"plain",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.12"
|
||||
|
||||
@@ -24,6 +24,7 @@ slab_allocator = { path = "slab_allocator", optional = true }
|
||||
# FIXME: There is some undefined behavior probably in the kernel, which forces us to use spin 0.9.0 and not 0.9.2.
|
||||
spin = "=0.9.0"
|
||||
rmm = { path = "rmm", default-features = false }
|
||||
redox-initfs = { git = "https://gitlab.redox-os.org/redox-os/redox-initfs.git", features = ["kernel"], default-features = false }
|
||||
|
||||
[dependencies.goblin]
|
||||
version = "0.2.1"
|
||||
|
||||
133
build.rs
133
build.rs
@@ -1,96 +1,5 @@
|
||||
use rustc_cfg::Cfg;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::io::{Error, Write};
|
||||
use std::path::Path;
|
||||
|
||||
// View loc folder with subfolders, get listings
|
||||
// Returns touple (folder_map, file_list)
|
||||
// folder_map keys are folders, and values are lists of direct childs
|
||||
// file_list is a vector of all detected files with full path
|
||||
fn scan_folder(loc: &Path) -> (HashMap<String, Vec<String>>, Vec<String>) {
|
||||
let mut folders: HashMap<String, Vec<String>> = HashMap::new();
|
||||
let mut files: Vec<String> = Vec::new();
|
||||
let mut current = Vec::new();
|
||||
|
||||
if loc.is_dir() {
|
||||
for entry in fs::read_dir(loc).unwrap() {
|
||||
let entry = entry.unwrap();
|
||||
let path = entry.path();
|
||||
let path_str = String::from(path.to_str().unwrap()).replace("\\", "/");
|
||||
|
||||
current.push(path_str.clone());
|
||||
|
||||
// if folder then scan recursively
|
||||
if path.is_dir() {
|
||||
let (d, mut f) = scan_folder(&path);
|
||||
for (key, value) in d.into_iter() {
|
||||
folders.insert(key, value);
|
||||
}
|
||||
|
||||
files.append(&mut f);
|
||||
} else {
|
||||
files.push(path_str);
|
||||
}
|
||||
}
|
||||
|
||||
current.sort();
|
||||
folders
|
||||
.entry(String::from(loc.to_str().unwrap()).replace("\\", "/"))
|
||||
.or_insert(current);
|
||||
} else {
|
||||
panic!("{:?} is not a folder!", loc);
|
||||
}
|
||||
|
||||
(folders, files)
|
||||
}
|
||||
|
||||
// Write folder/file information to output file
|
||||
fn fill_from_location(f: &mut fs::File, loc: &Path) -> Result<(), Error> {
|
||||
let (folders, mut files) = scan_folder(loc);
|
||||
let mut folder_it: Vec<_> = folders.keys().collect();
|
||||
|
||||
let loc_str = loc.to_str().unwrap();
|
||||
let mut idx = loc_str.len();
|
||||
|
||||
if !loc_str.ends_with("/") {
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
folder_it.sort();
|
||||
files.sort();
|
||||
for dir in folder_it.iter() {
|
||||
let strip: String = dir.chars().skip(idx).collect();
|
||||
write!(f, " files.insert(b\"{}\", (b\"", strip)?;
|
||||
|
||||
// Write child elements separated with \n
|
||||
let sub = folders.get(*dir).unwrap();
|
||||
let mut first = true;
|
||||
for child in sub.iter() {
|
||||
let idx = child.rfind('/').unwrap() + 1;
|
||||
let (_, c) = child.split_at(idx);
|
||||
if first {
|
||||
write!(f, "{}", c)?;
|
||||
first = false;
|
||||
} else {
|
||||
write!(f, "\\n{}", c)?;
|
||||
}
|
||||
}
|
||||
write!(f, "\", true));\n")?;
|
||||
}
|
||||
|
||||
for name in files.iter() {
|
||||
let (_, strip) = name.split_at(idx);
|
||||
write!(
|
||||
f,
|
||||
" files.insert(b\"{}\", (include_bytes!(\"{}\"), false));\n",
|
||||
strip, name
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "x86_64"))]
|
||||
fn asm(_out_dir: &str) {}
|
||||
@@ -114,52 +23,10 @@ fn asm(out_dir: &str) {
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rustc-env=TARGET={}", env::var("TARGET").unwrap());
|
||||
println!("cargo:rerun-if-env-changed=INITFS_FOLDER");
|
||||
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let dest_path = Path::new(&out_dir).join("gen.rs");
|
||||
let mut f = fs::File::create(&dest_path).unwrap();
|
||||
let src = env::var("INITFS_FOLDER");
|
||||
|
||||
asm(&out_dir);
|
||||
|
||||
// Write header
|
||||
f.write_all(
|
||||
b"
|
||||
mod gen {
|
||||
use alloc::collections::BTreeMap;
|
||||
pub fn gen() -> BTreeMap<&'static [u8], (&'static [u8], bool)> {
|
||||
let mut files: BTreeMap<&'static [u8], (&'static [u8], bool)> = BTreeMap::new();
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
match src {
|
||||
Ok(v) => {
|
||||
println!("cargo:rerun-if-changed={}", v);
|
||||
fill_from_location(&mut f, Path::new(&v)).unwrap()
|
||||
}
|
||||
Err(e) => {
|
||||
f.write_all(
|
||||
b" files.clear();", // Silence mutability warning
|
||||
)
|
||||
.unwrap();
|
||||
println!(
|
||||
"cargo:warning=location not found: {}, please set proper INITFS_FOLDER.",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
f.write_all(
|
||||
b"
|
||||
files
|
||||
}
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Build pre kstart init asm code for aarch64
|
||||
let cfg = Cfg::new(env::var_os("TARGET").unwrap()).unwrap();
|
||||
if cfg.target_arch == "aarch64" {
|
||||
|
||||
@@ -80,7 +80,8 @@ unsafe fn inner<A: Arch>(
|
||||
kernel_base: usize, kernel_size_aligned: usize,
|
||||
stack_base: usize, stack_size_aligned: usize,
|
||||
env_base: usize, env_size_aligned: usize,
|
||||
acpi_base: usize, acpi_size_aligned: usize
|
||||
acpi_base: usize, acpi_size_aligned: usize,
|
||||
initfs_base: usize, initfs_size_aligned: usize,
|
||||
) -> BuddyAllocator<A> {
|
||||
// First, calculate how much memory we have
|
||||
let mut size = 0;
|
||||
@@ -137,44 +138,26 @@ unsafe fn inner<A: Arch>(
|
||||
flush.ignore(); // Not the active table
|
||||
}
|
||||
|
||||
// Map stack with identity mapping
|
||||
for i in 0..stack_size_aligned / A::PAGE_SIZE {
|
||||
let phys = PhysicalAddress::new(stack_base + i * A::PAGE_SIZE);
|
||||
let virt = A::phys_to_virt(phys);
|
||||
let flags = page_flags::<A>(virt);
|
||||
let flush = mapper.map_phys(
|
||||
virt,
|
||||
phys,
|
||||
flags
|
||||
).expect("failed to map frame");
|
||||
flush.ignore(); // Not the active table
|
||||
}
|
||||
let mut identity_map = |base, size_aligned| {
|
||||
// Map stack with identity mapping
|
||||
for i in 0..size / A::PAGE_SIZE {
|
||||
let phys = PhysicalAddress::new(base + i * A::PAGE_SIZE);
|
||||
let virt = A::phys_to_virt(phys);
|
||||
let flags = page_flags::<A>(virt);
|
||||
let flush = mapper.map_phys(
|
||||
virt,
|
||||
phys,
|
||||
flags
|
||||
).expect("failed to map frame");
|
||||
flush.ignore(); // Not the active table
|
||||
}
|
||||
};
|
||||
|
||||
// Map env with identity mapping
|
||||
for i in 0..env_size_aligned / A::PAGE_SIZE {
|
||||
let phys = PhysicalAddress::new(env_base + i * A::PAGE_SIZE);
|
||||
let virt = A::phys_to_virt(phys);
|
||||
let flags = page_flags::<A>(virt);
|
||||
let flush = mapper.map_phys(
|
||||
virt,
|
||||
phys,
|
||||
flags
|
||||
).expect("failed to map frame");
|
||||
flush.ignore(); // Not the active table
|
||||
}
|
||||
|
||||
// Map acpi with identity mapping
|
||||
for i in 0..acpi_size_aligned / A::PAGE_SIZE {
|
||||
let phys = PhysicalAddress::new(acpi_base + i * A::PAGE_SIZE);
|
||||
let virt = A::phys_to_virt(phys);
|
||||
let flags = page_flags::<A>(virt);
|
||||
let flush = mapper.map_phys(
|
||||
virt,
|
||||
phys,
|
||||
flags
|
||||
).expect("failed to map frame");
|
||||
flush.ignore(); // Not the active table
|
||||
}
|
||||
identity_map(stack_base, stack_size_aligned);
|
||||
identity_map(env_base, env_size_aligned);
|
||||
identity_map(acpi_base, acpi_size_aligned);
|
||||
identity_map(initfs_base, initfs_size_aligned);
|
||||
|
||||
// Ensure graphical debug region remains paged
|
||||
#[cfg(feature = "graphical_debug")]
|
||||
@@ -289,6 +272,7 @@ pub unsafe fn init(
|
||||
env_base: usize, env_size: usize,
|
||||
acpi_base: usize, acpi_size: usize,
|
||||
areas_base: usize, areas_size: usize,
|
||||
initfs_base: usize, initfs_size: usize,
|
||||
) {
|
||||
type A = RmmA;
|
||||
|
||||
@@ -308,6 +292,9 @@ pub unsafe fn init(
|
||||
let acpi_size_aligned = ((acpi_size + (A::PAGE_SIZE - 1))/A::PAGE_SIZE) * A::PAGE_SIZE;
|
||||
let acpi_end = acpi_base + acpi_size_aligned;
|
||||
|
||||
let initfs_size_aligned = ((initfs_size + (A::PAGE_SIZE - 1))/A::PAGE_SIZE) * A::PAGE_SIZE;
|
||||
let initfs_end = initfs_base + initfs_size_aligned;
|
||||
|
||||
let bootloader_areas = slice::from_raw_parts(
|
||||
areas_base as *const BootloaderMemoryEntry,
|
||||
areas_size / mem::size_of::<BootloaderMemoryEntry>()
|
||||
@@ -370,6 +357,10 @@ pub unsafe fn init(
|
||||
log::warn!("{:X}:{:X} overlaps with acpi {:X}:{:X}", base, size, acpi_base, acpi_size);
|
||||
new_base = cmp::max(new_base, acpi_end);
|
||||
}
|
||||
if base < initfs_end && base + size > initfs_base {
|
||||
log::warn!("{:X}:{:X} overlaps with initfs {:X}:{:X}", base, size, initfs_base, initfs_size);
|
||||
new_base = cmp::max(new_base, initfs_end);
|
||||
}
|
||||
|
||||
if new_base != base {
|
||||
let end = base + size;
|
||||
@@ -394,7 +385,8 @@ pub unsafe fn init(
|
||||
kernel_base, kernel_size_aligned,
|
||||
stack_base, stack_size_aligned,
|
||||
env_base, env_size_aligned,
|
||||
acpi_base, acpi_size_aligned
|
||||
acpi_base, acpi_size_aligned,
|
||||
initfs_base, initfs_size_aligned,
|
||||
);
|
||||
*FRAME_ALLOCATOR.inner.lock() = Some(allocator);
|
||||
}
|
||||
|
||||
@@ -52,13 +52,17 @@ pub struct KernelArgs {
|
||||
///
|
||||
/// This field can be NULL, and if so, the system has not booted with UEFI or in some other way
|
||||
/// retrieved the RSDPs. The kernel or a userspace driver will thus try searching the BIOS
|
||||
/// memory instead. On UEFI systems, searching is not guaranteed to actually work though.
|
||||
/// memory instead. On UEFI systems, BIOS-like searching is not guaranteed to actually work though.
|
||||
acpi_rsdps_base: u64,
|
||||
/// The size of the RSDPs region.
|
||||
acpi_rsdps_size: u64,
|
||||
|
||||
areas_base: u64,
|
||||
areas_size: u64,
|
||||
|
||||
/// The physical base 64-bit pointer to the contiguous initfs.
|
||||
initfs_base: u64,
|
||||
initfs_size: u64,
|
||||
}
|
||||
|
||||
/// The entry to Rust, all things must be initialized
|
||||
@@ -77,6 +81,8 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! {
|
||||
let acpi_rsdps_size = args.acpi_rsdps_size;
|
||||
let areas_base = args.areas_base as usize;
|
||||
let areas_size = args.areas_size as usize;
|
||||
let initfs_base = args.initfs_base as usize;
|
||||
let initfs_size = args.initfs_size as usize;
|
||||
|
||||
// BSS should already be zero
|
||||
{
|
||||
@@ -89,6 +95,7 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! {
|
||||
|
||||
// Convert env to slice
|
||||
let env = slice::from_raw_parts((env_base + crate::PHYS_OFFSET) as *const u8, env_size);
|
||||
let initfs = slice::from_raw_parts((initfs_base + crate::PHYS_OFFSET) as *const u8, initfs_size);
|
||||
|
||||
// Set up graphical debug
|
||||
#[cfg(feature = "graphical_debug")]
|
||||
@@ -115,6 +122,7 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! {
|
||||
info!("Env: {:X}:{:X}", env_base, env_base + env_size);
|
||||
info!("RSDPs: {:X}:{:X}", acpi_rsdps_base, acpi_rsdps_base + acpi_rsdps_size);
|
||||
info!("Areas: {:X}:{:X}", areas_base, areas_base + areas_size);
|
||||
info!("Initfs: {:X}:{:X}", initfs_base, initfs_base + initfs_size);
|
||||
|
||||
// Set up GDT before paging
|
||||
gdt::init();
|
||||
@@ -129,6 +137,7 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! {
|
||||
env_base, env_size,
|
||||
acpi_rsdps_base as usize, acpi_rsdps_size as usize,
|
||||
areas_base, areas_size,
|
||||
initfs_base, initfs_size,
|
||||
);
|
||||
|
||||
// Initialize paging
|
||||
@@ -187,6 +196,8 @@ pub unsafe extern fn kstart(args_ptr: *const KernelArgs) -> ! {
|
||||
// Initialize all of the non-core devices not otherwise needed to complete initialization
|
||||
device::init_noncore();
|
||||
|
||||
crate::scheme::initfs::init(initfs);
|
||||
|
||||
// Stop graphical debug
|
||||
#[cfg(feature = "graphical_debug")]
|
||||
graphical_debug::fini();
|
||||
|
||||
@@ -177,8 +177,6 @@ pub extern fn userspace_init() {
|
||||
|
||||
if let Err(err) = syscall::chdir("initfs:") {
|
||||
info!("Failed to enter initfs ({}).", err);
|
||||
info!("Perhaps the kernel was compiled with an incorrect INITFS_FOLDER \
|
||||
environment variable value?");
|
||||
panic!("Unexpected error while trying to enter initfs:.");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,137 +1,275 @@
|
||||
use alloc::collections::BTreeMap;
|
||||
use core::convert::TryFrom;
|
||||
use core::str;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
use spin::RwLock;
|
||||
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
|
||||
use spin::{Once, RwLock};
|
||||
|
||||
use redox_initfs::{InitFs, InodeStruct, Inode, InodeDir, InodeKind, types::Timespec};
|
||||
|
||||
use crate::syscall::data::Stat;
|
||||
use crate::syscall::error::*;
|
||||
use crate::syscall::flag::{MODE_DIR, MODE_FILE};
|
||||
use crate::syscall::scheme::{calc_seek_offset_usize, Scheme};
|
||||
|
||||
#[cfg(test)]
|
||||
mod gen {
|
||||
use alloc::collections::BTreeMap;
|
||||
pub fn gen() -> BTreeMap<&'static [u8], (&'static [u8], bool)> { BTreeMap::new() }
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
include!(concat!(env!("OUT_DIR"), "/gen.rs"));
|
||||
|
||||
struct Handle {
|
||||
path: &'static [u8],
|
||||
data: &'static [u8],
|
||||
mode: u16,
|
||||
seek: usize
|
||||
inode: Inode,
|
||||
seek: usize,
|
||||
// TODO: Any better way to implement fpath? Or maybe work around it, e.g. by giving paths such
|
||||
// as `initfs:__inodes__/<inode>`?
|
||||
filename: String,
|
||||
}
|
||||
|
||||
pub struct InitFsScheme {
|
||||
next_id: AtomicUsize,
|
||||
files: BTreeMap<&'static [u8], (&'static [u8], bool)>,
|
||||
handles: RwLock<BTreeMap<usize, Handle>>
|
||||
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
|
||||
static HANDLES: RwLock<BTreeMap<usize, Handle>> = RwLock::new(BTreeMap::new());
|
||||
|
||||
static FS: Once<InitFs<'static>> = Once::new();
|
||||
|
||||
fn fs() -> Result<InitFs<'static>> {
|
||||
FS.get().copied().ok_or(Error::new(ENODEV))
|
||||
}
|
||||
fn get_inode(inode: Inode) -> Result<InodeStruct<'static>> {
|
||||
fs()?.get_inode(inode).ok_or_else(|| Error::new(EIO))
|
||||
}
|
||||
|
||||
impl InitFsScheme {
|
||||
pub fn new() -> InitFsScheme {
|
||||
InitFsScheme {
|
||||
next_id: AtomicUsize::new(0),
|
||||
files: gen::gen(),
|
||||
handles: RwLock::new(BTreeMap::new())
|
||||
pub fn init(bytes: &'static [u8]) {
|
||||
let mut called = false;
|
||||
|
||||
FS.call_once(|| {
|
||||
called = true;
|
||||
|
||||
InitFs::new(bytes)
|
||||
.expect("failed to parse initfs header")
|
||||
});
|
||||
|
||||
assert!(called, "called initfs::init more than once");
|
||||
}
|
||||
|
||||
fn next_id() -> usize {
|
||||
let old = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||
assert_ne!(old, usize::MAX, "usize overflow in initfs scheme");
|
||||
old
|
||||
}
|
||||
|
||||
pub struct InitFsScheme;
|
||||
|
||||
struct Iter {
|
||||
dir: InodeDir<'static>,
|
||||
idx: u32,
|
||||
}
|
||||
impl Iterator for Iter {
|
||||
type Item = Result<redox_initfs::Entry<'static>>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let entry = self.dir.get_entry(self.idx).map_err(|_| Error::new(EIO));
|
||||
self.idx += 1;
|
||||
entry.transpose()
|
||||
}
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
match self.dir.entry_count().ok() {
|
||||
Some(size) => {
|
||||
let size = usize::try_from(size).expect("expected u32 to be convertible into usize");
|
||||
(size, Some(size))
|
||||
}
|
||||
None => (0, None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn entries_iter(dir: InodeDir<'static>) -> impl IntoIterator<Item = Result<redox_initfs::Entry<'static>>> + 'static {
|
||||
let mut index = 0_u32;
|
||||
|
||||
core::iter::from_fn(move || {
|
||||
let idx = index;
|
||||
index += 1;
|
||||
|
||||
dir.get_entry(idx).map_err(|_| Error::new(EIO)).transpose()
|
||||
})
|
||||
}
|
||||
fn inode_len(inode: InodeStruct<'static>) -> Result<usize> {
|
||||
Ok(match inode.kind() {
|
||||
InodeKind::File(file) => file.data().map_err(|_| Error::new(EIO))?.len(),
|
||||
InodeKind::Dir(dir) => (Iter { dir, idx: 0 })
|
||||
.fold(0, |len, entry| len + entry.and_then(|entry| entry.name().map_err(|_| Error::new(EIO))).map_or(0, |name| name.len() + 1)),
|
||||
InodeKind::Unknown => return Err(Error::new(EIO)),
|
||||
})
|
||||
}
|
||||
|
||||
impl Scheme for InitFsScheme {
|
||||
fn open(&self, path: &str, _flags: usize, _uid: u32, _gid: u32) -> Result<usize> {
|
||||
let path_trimmed = path.trim_matches('/');
|
||||
let mut components = path
|
||||
// trim leading and trailing slash
|
||||
.trim_matches('/')
|
||||
// divide into components
|
||||
.split('/')
|
||||
// filter out double slashes (e.g. /usr//bin/...)
|
||||
.filter(|c| !c.is_empty());
|
||||
|
||||
//Have to iterate to get the path without allocation
|
||||
for entry in self.files.iter() {
|
||||
if entry.0 == &path_trimmed.as_bytes() {
|
||||
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
|
||||
self.handles.write().insert(id, Handle {
|
||||
path: entry.0,
|
||||
data: (entry.1).0,
|
||||
mode: if (entry.1).1 { MODE_DIR | 0o755 } else { MODE_FILE | 0o744 },
|
||||
seek: 0
|
||||
});
|
||||
let mut current_inode = InitFs::ROOT_INODE;
|
||||
|
||||
return Ok(id);
|
||||
while let Some(component) = components.next() {
|
||||
match component {
|
||||
"." => continue,
|
||||
".." => {
|
||||
let _ = components.next_back();
|
||||
continue
|
||||
}
|
||||
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let current_inode_struct = get_inode(current_inode)?;
|
||||
|
||||
let dir = match current_inode_struct.kind() {
|
||||
InodeKind::Dir(dir) => dir,
|
||||
|
||||
// If we still have more components in the path, and the file tree for that
|
||||
// particular branch is not all directories except the last, then that file cannot
|
||||
// exist.
|
||||
InodeKind::File(_) | InodeKind::Unknown => return Err(Error::new(ENOENT)),
|
||||
};
|
||||
|
||||
let mut entries = Iter {
|
||||
dir,
|
||||
idx: 0,
|
||||
};
|
||||
|
||||
current_inode = loop {
|
||||
let entry_res = match entries.next() {
|
||||
Some(e) => e,
|
||||
None => return Err(Error::new(ENOENT)),
|
||||
};
|
||||
let entry = entry_res?;
|
||||
let name = entry.name().map_err(|_| Error::new(EIO))?;
|
||||
if name == component.as_bytes() {
|
||||
break entry.inode();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Err(Error::new(ENOENT))
|
||||
let id = next_id();
|
||||
let old = HANDLES.write().insert(id, Handle {
|
||||
inode: current_inode,
|
||||
seek: 0_usize,
|
||||
filename: path.into(),
|
||||
});
|
||||
assert!(old.is_none());
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
fn read(&self, id: usize, buffer: &mut [u8]) -> Result<usize> {
|
||||
let mut handles = self.handles.write();
|
||||
let mut handles = HANDLES.write();
|
||||
let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
let mut i = 0;
|
||||
while i < buffer.len() && handle.seek < handle.data.len() {
|
||||
buffer[i] = handle.data[handle.seek];
|
||||
i += 1;
|
||||
handle.seek += 1;
|
||||
}
|
||||
match get_inode(handle.inode)?.kind() {
|
||||
InodeKind::Dir(dir) => {
|
||||
let mut bytes_read = 0;
|
||||
let mut bytes_skipped = 0;
|
||||
|
||||
Ok(i)
|
||||
for entry_res in (Iter { dir, idx: 0 }) {
|
||||
let entry = entry_res?;
|
||||
let name = entry.name().map_err(|_| Error::new(EIO))?;
|
||||
let entry_len = name.len() + 1;
|
||||
|
||||
let to_skip = core::cmp::min(handle.seek - bytes_skipped, entry_len);
|
||||
let max_to_read = core::cmp::min(entry_len - to_skip, buffer.len());
|
||||
|
||||
let to_copy = entry_len.saturating_sub(to_skip).saturating_sub(1);
|
||||
buffer[bytes_read..bytes_read + to_copy].copy_from_slice(&name[..to_copy]);
|
||||
|
||||
if to_copy.saturating_sub(to_skip) == 1 {
|
||||
buffer[bytes_read + to_copy] = b'\n';
|
||||
bytes_read += 1;
|
||||
}
|
||||
|
||||
bytes_read += to_copy;
|
||||
bytes_skipped += to_skip;
|
||||
}
|
||||
|
||||
handle.seek = handle.seek.checked_add(bytes_read).ok_or(Error::new(EOVERFLOW))?;
|
||||
|
||||
Ok(bytes_read)
|
||||
}
|
||||
InodeKind::File(file) => {
|
||||
let data = file.data().map_err(|_| Error::new(EIO))?;
|
||||
let src_buf = &data[core::cmp::min(handle.seek, data.len())..];
|
||||
|
||||
let to_copy = core::cmp::min(src_buf.len(), buffer.len());
|
||||
buffer[..to_copy].copy_from_slice(&src_buf[..to_copy]);
|
||||
|
||||
handle.seek = handle.seek.checked_add(to_copy).ok_or(Error::new(EOVERFLOW))?;
|
||||
|
||||
Ok(to_copy)
|
||||
}
|
||||
InodeKind::Unknown => return Err(Error::new(EIO)),
|
||||
}
|
||||
}
|
||||
|
||||
fn seek(&self, id: usize, pos: isize, whence: usize) -> Result<isize> {
|
||||
let mut handles = self.handles.write();
|
||||
let mut handles = HANDLES.write();
|
||||
let handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
||||
let new_offset = calc_seek_offset_usize(handle.seek, pos, whence, handle.data.len())?;
|
||||
|
||||
let new_offset = calc_seek_offset_usize(handle.seek, pos, whence, inode_len(get_inode(handle.inode)?)?)?;
|
||||
handle.seek = new_offset as usize;
|
||||
Ok(new_offset)
|
||||
}
|
||||
|
||||
fn fcntl(&self, id: usize, _cmd: usize, _arg: usize) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
let handles = HANDLES.read();
|
||||
let _handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fpath(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
let handles = HANDLES.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
//TODO: Copy scheme part in kernel
|
||||
let mut i = 0;
|
||||
// TODO: Copy scheme part in kernel
|
||||
let scheme_path = b"initfs:";
|
||||
while i < buf.len() && i < scheme_path.len() {
|
||||
buf[i] = scheme_path[i];
|
||||
i += 1;
|
||||
}
|
||||
let scheme_bytes = core::cmp::min(scheme_path.len(), buf.len());
|
||||
buf[..scheme_bytes].copy_from_slice(&scheme_path[..scheme_bytes]);
|
||||
|
||||
let mut j = 0;
|
||||
while i < buf.len() && j < handle.path.len() {
|
||||
buf[i] = handle.path[j];
|
||||
i += 1;
|
||||
j += 1;
|
||||
}
|
||||
let source = handle.filename.as_bytes();
|
||||
let path_bytes = core::cmp::min(buf.len() - scheme_bytes, source.len());
|
||||
buf[scheme_bytes..scheme_bytes + path_bytes].copy_from_slice(&source[..path_bytes]);
|
||||
|
||||
Ok(i)
|
||||
Ok(scheme_bytes + path_bytes)
|
||||
}
|
||||
|
||||
fn fstat(&self, id: usize, stat: &mut Stat) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
let handles = HANDLES.read();
|
||||
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
|
||||
stat.st_mode = handle.mode;
|
||||
stat.st_uid = 0;
|
||||
stat.st_gid = 0;
|
||||
stat.st_size = handle.data.len() as u64;
|
||||
let Timespec { sec, nsec } = fs()?.image_creation_time();
|
||||
|
||||
let inode = get_inode(handle.inode)?;
|
||||
|
||||
stat.st_mode = inode.mode() | match inode.kind() { InodeKind::Dir(_) => MODE_DIR, InodeKind::File(_) => MODE_FILE, _ => 0 };
|
||||
stat.st_uid = inode.uid();
|
||||
stat.st_gid = inode.gid();
|
||||
stat.st_size = u64::try_from(inode_len(inode)?).unwrap_or(u64::MAX);
|
||||
|
||||
stat.st_ctime = sec.get();
|
||||
stat.st_ctime_nsec = nsec.get();
|
||||
stat.st_mtime = sec.get();
|
||||
stat.st_mtime_nsec = nsec.get();
|
||||
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn fsync(&self, id: usize) -> Result<usize> {
|
||||
let handles = self.handles.read();
|
||||
let handles = HANDLES.read();
|
||||
let _handle = handles.get(&id).ok_or(Error::new(EBADF))?;
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
fn close(&self, id: usize) -> Result<usize> {
|
||||
self.handles.write().remove(&id).ok_or(Error::new(EBADF)).and(Ok(0))
|
||||
let _ = HANDLES.write().remove(&id).ok_or(Error::new(EBADF))?;
|
||||
Ok(0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +165,7 @@ impl SchemeList {
|
||||
self.insert(ns, "kernel/acpi", |scheme_id| Arc::new(AcpiScheme::new(scheme_id))).unwrap();
|
||||
}
|
||||
self.insert(ns, "debug", |scheme_id| Arc::new(DebugScheme::new(scheme_id))).unwrap();
|
||||
self.insert(ns, "initfs", |_| Arc::new(InitFsScheme::new())).unwrap();
|
||||
self.insert(ns, "initfs", |_| Arc::new(InitFsScheme)).unwrap();
|
||||
self.insert(ns, "irq", |scheme_id| Arc::new(IrqScheme::new(scheme_id))).unwrap();
|
||||
self.insert(ns, "proc", |scheme_id| Arc::new(ProcScheme::new(scheme_id))).unwrap();
|
||||
self.insert(ns, "thisproc", |_| Arc::new(ProcScheme::restricted())).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user