This commit is contained in:
pjht 2024-06-06 22:01:41 -05:00
parent c60dea71ba
commit 6f0094c039
Signed by: pjht
GPG Key ID: 7B5F6AFBEC7EE78E
13 changed files with 23 additions and 1887 deletions

View File

@ -1,6 +1,5 @@
[build]
target = "x86_64-unknown-none"
rustflags = ["-C", "relocation-model=static"]
target = "x86_64-unknown-mikros"
[install]
root = "../kernel/sysroot"
root = "../os_build/sysroot"

395
Cargo.lock generated
View File

@ -2,204 +2,23 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "ahash"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "arrayvec"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "autocfg"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
[[package]]
name = "binread"
version = "2.2.0"
dependencies = [
"binread_derive",
"rustversion",
"std",
]
[[package]]
name = "binread_derive"
version = "2.1.0"
dependencies = [
"either",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "bit_field"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cobs"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15"
[[package]]
name = "core2"
version = "0.4.0"
dependencies = [
"memchr",
"serde",
]
[[package]]
name = "crossbeam-queue"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
[[package]]
name = "derive-try-from-primitive"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "302ccf094df1151173bb6f5a2282fcd2f45accd5eae1bdf82dcbfefbc501ad5c"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "dev_driver_rpc"
version = "0.1.0"
dependencies = [
"postcard",
"serde",
"spin",
"std",
]
[[package]]
name = "either"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b"
[[package]]
name = "elf"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b"
[[package]]
name = "elfloader"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a7b18d35bf8ec3bac59c3ec29cf1f1b46e764e00b42a9c0c754d06e38e78f3b"
dependencies = [
"bitflags 1.3.2",
"log",
"xmas-elf",
]
[[package]]
name = "embedded-io"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
[[package]]
name = "hashbrown"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
dependencies = [
"ahash",
]
[[package]]
name = "init"
version = "0.1.0"
dependencies = [
"binread",
"bitflags 1.3.2",
"dev_driver_rpc",
"itertools",
"postcard",
"serde",
"spin",
"std",
"tap",
"tar-no-std",
"x86_64 0.15.1",
]
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "linked_list_allocator"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286"
dependencies = [
"spinning_top",
]
[[package]]
name = "lock_api"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
@ -215,216 +34,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
[[package]]
name = "once_cell"
version = "1.19.0"
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "postcard"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a55c51ee6c0db07e68448e336cf8ea4131a620edefebf9893e759b2d793420f8"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"cobs",
"embedded-io",
"serde",
"autocfg",
]
[[package]]
name = "proc-macro2"
version = "1.0.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rustversion"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.203"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.203"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
]
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
dependencies = [
"lock_api",
]
[[package]]
name = "spinning_top"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b9eb1a2f4c41445a3a0ff9abc5221c5fcd28e1f13cd7c0397706f9ac938ddb0"
dependencies = [
"lock_api",
]
[[package]]
name = "std"
version = "0.1.0"
dependencies = [
"core2",
"crossbeam-queue",
"derive-try-from-primitive",
"elf",
"elfloader",
"hashbrown",
"linked_list_allocator",
"postcard",
"serde",
"spin",
"x86_64 0.14.12",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tap"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]]
name = "tar-no-std"
version = "0.1.7"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d897790ee033615752cc7bf882343881ad748438c01bc7e1b9d6242bf14a2c6"
dependencies = [
"arrayvec",
"bitflags 1.3.2",
"bitflags",
"log",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "volatile"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793"
[[package]]
name = "x86_64"
version = "0.14.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96cb6fd45bfeab6a5055c5bffdb08768bd0c069f1d946debe585bbb380a7c062"
dependencies = [
"bit_field",
"bitflags 2.5.0",
"rustversion",
"volatile",
]
[[package]]
name = "x86_64"
version = "0.15.1"
source = "git+https://gitea.pterpstra.com/mikros/x86_64#8debcc3504d7e5c39c10b28f2a0661b19ee3a6f0"
dependencies = [
"bit_field",
"bitflags 2.5.0",
"rustversion",
"volatile",
]
[[package]]
name = "xmas-elf"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d29b4d8e7beaceb4e77447ba941a7600d23d0319ab52da0461abea214832d5a"
dependencies = [
"zero",
]
[[package]]
name = "zero"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fe21bcc34ca7fe6dd56cc2cb1261ea59d6b93620215aefb5ea6032265527784"
[[package]]
name = "zerocopy"
version = "0.7.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.66",
"memchr",
"num-traits",
]

View File

@ -6,17 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
tar-no-std = { path = "../tar-no-std" }
std = { path = "../std" }
bitflags = "1.3.2"
spin = "0.9.4"
tap = "1.0.1"
x86_64 = { git = "https://gitea.pterpstra.com/mikros/x86_64" }
binread = { version = "2.2.0", path = "../binread/binread", default-features = false }
itertools = { version = "0.10.3", default-features = false, features = ["use_alloc"] }
serde = { version = "1.0.144", default-features = false, features = ["alloc", "derive"] }
postcard = { version = "1.0.2", default-features = false, features = ["alloc"] }
dev_driver_rpc = { version = "0.1.0", path = "../dev_driver_rpc" }
tar-no-std = "0.3.1"
[profile.dev]
panic = "abort"

View File

@ -1,2 +1,2 @@
[toolchain]
channel = "nightly"
channel = "dev-x86_64-unknown-mikros"

View File

@ -1,412 +0,0 @@
use std::cmp::Ordering;
use bitflags::bitflags;
use spin::{Lazy, Mutex};
use std::io::{self, Read, Seek};
use tap::Tap;
use x86_64::instructions::port::{
PortRead, PortSafe, PortSafeReadOnly, PortSafeWriteOnly, PortWrite,
};
bitflags! {
pub struct RawError: u8 {
const AMNF = 0b0000_0001;
const TKZNF = 0b0000_0010;
const ABRT = 0b0000_0100;
const MCR = 0b0000_1000;
const IDNF = 0b0001_0000;
const MC = 0b0010_0000;
const UNC = 0b0100_0000;
const BBK = 0b1000_0000;
}
}
impl From<RawError> for Error {
fn from(err: RawError) -> Self {
if err.contains(RawError::AMNF) {
Self::AddressMarkNotFound
} else if err.contains(RawError::TKZNF) {
Self::TrackZeroNotFound
} else if err.contains(RawError::ABRT) {
Self::AbortedCommand
} else if err.contains(RawError::MCR) {
Self::MediaChangeRequest
} else if err.contains(RawError::IDNF) {
Self::IDMarkNotFound
} else if err.contains(RawError::MC) {
Self::MediaChanged
} else if err.contains(RawError::UNC) {
Self::UncorrectableData
} else if err.contains(RawError::BBK) {
Self::BadBlock
} else {
Self::Generic
}
}
}
impl PortRead for RawError {
unsafe fn read_from_port(port: u16) -> Self {
unsafe { Self::from_bits_unchecked(u8::read_from_port(port)) }
}
}
impl PortWrite for RawError {
unsafe fn write_to_port(port: u16, value: Self) {
unsafe { u8::write_to_port(port, value.bits) };
}
}
bitflags! {
pub struct Status: u8 {
const ERR = 0b0000_0001;
const DRQ = 0b0000_1000;
const SRV = 0b0001_0000;
const DF = 0b0010_0000;
const RDY = 0b0100_0000;
const BSY = 0b1000_0000;
}
}
impl PortRead for Status {
unsafe fn read_from_port(port: u16) -> Self {
unsafe { Self::from_bits_unchecked(u8::read_from_port(port)) }
}
}
impl PortWrite for Status {
unsafe fn write_to_port(port: u16, value: Self) {
unsafe { u8::write_to_port(port, value.bits) };
}
}
bitflags! {
pub struct DeviceControl: u8 {
const N_IEN = 0b0000_0010;
const SRST = 0b0000_0100;
const HOB = 0b1000_0000;
}
}
impl PortRead for DeviceControl {
unsafe fn read_from_port(port: u16) -> Self {
unsafe { Self::from_bits_unchecked(u8::read_from_port(port)) }
}
}
impl PortWrite for DeviceControl {
unsafe fn write_to_port(port: u16, value: Self) {
unsafe { u8::write_to_port(port, value.bits) };
}
}
bitflags! {
#[repr(transparent)]
pub struct DriveHead: u8 {
const DT0 = 0b0000_0001;
const DT1 = 0b0000_0010;
const DT2 = 0b0000_0100;
const DT3 = 0b0000_1000;
const DRV = 0b0001_0000;
const LBA = 0b0100_0000;
}
}
impl DriveHead {
fn set_head_block(&mut self, data: u8) {
assert!(data < 16);
self.bits = (self.bits & 0xF0) | data;
}
}
impl PortRead for DriveHead {
unsafe fn read_from_port(port: u16) -> Self {
unsafe { Self::from_bits_unchecked(u8::read_from_port(port)) }
}
}
impl PortWrite for DriveHead {
unsafe fn write_to_port(port: u16, value: Self) {
unsafe { u8::write_to_port(port, value.bits) };
}
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DriveNumber {
Master = 0,
Slave = 1,
}
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq)]
#[allow(unused)]
enum Command {
Identify = 0xEC,
IdentifyPacket = 0xA1,
ReadSectors = 0x20,
WriteSectors = 0x30,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Error {
NonexistentDrive,
Generic,
AbortedCommand,
TrackZeroNotFound,
AddressMarkNotFound,
MediaChangeRequest,
IDMarkNotFound,
MediaChanged,
UncorrectableData,
BadBlock,
}
impl From<Error> for io::Error {
fn from(_: Error) -> Self {
Self::new(io::ErrorKind::Other, "ATA read error")
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[allow(clippy::upper_case_acronyms)]
pub enum DriveKind {
ATA,
ATAPI,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum IdentifyError {
SATADrive,
Error(Error),
}
impl From<Error> for IdentifyError {
fn from(err: Error) -> Self {
Self::Error(err)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Bus {
data: PortSafe<u16>,
error: PortSafeReadOnly<RawError>,
sector_count: PortSafe<u8>,
sector_num_lba_lo: PortSafe<u8>,
cyl_low_lba_mid: PortSafe<u8>,
cyl_high_lba_hi: PortSafe<u8>,
drive_head: PortSafe<DriveHead>,
status: PortSafeReadOnly<Status>,
command: PortSafeWriteOnly<u8>,
selected_drive: Option<DriveNumber>, // None represents the initial state with an unknown drive selected
}
impl Bus {
/// SAFETY
/// io_base must be a valid base for the ATA bus's IO registers
const unsafe fn new(io_base: u16) -> Self {
unsafe {
Self {
data: PortSafe::new(io_base),
error: PortSafeReadOnly::new(io_base + 1),
sector_count: PortSafe::new(io_base + 2),
sector_num_lba_lo: PortSafe::new(io_base + 3),
cyl_low_lba_mid: PortSafe::new(io_base + 4),
cyl_high_lba_hi: PortSafe::new(io_base + 5),
drive_head: PortSafe::new(io_base + 6),
status: PortSafeReadOnly::new(io_base + 7),
command: PortSafeWriteOnly::new(io_base + 7),
selected_drive: None,
}
}
}
pub fn select(&mut self, drive: DriveNumber, lba: bool, data: u8) {
if self.selected_drive != Some(drive) {
self.drive_head.write(DriveHead::empty().tap_mut(|v| {
v.set(DriveHead::DRV, drive == DriveNumber::Slave);
v.set(DriveHead::LBA, lba);
v.set_head_block(data);
}));
for _ in 0..14 {
self.status.read();
}
self.selected_drive = Some(drive);
}
}
pub fn read_data(&mut self) -> [u16; 256] {
let mut arr = [0; 256];
arr.fill_with(|| self.data.read());
arr
}
fn send_command(&mut self, command: Command) -> Result<(), Error> {
self.command.write(command as u8);
while self.status.read().contains(Status::BSY) {}
while !(self.status.read().intersects(Status::DRQ | Status::ERR)) {}
self.error()?;
Ok(())
}
pub fn identify(&mut self) -> Result<(DriveKind, [u16; 256]), IdentifyError> {
self.command.write(Command::Identify as u8);
if self.status.read().bits() == 0 {
return Err(IdentifyError::Error(Error::NonexistentDrive));
};
while self.status.read().contains(Status::BSY) {}
while !(self.status.read().intersects(Status::DRQ | Status::ERR)) {}
if self.status.read().contains(Status::ERR) {
if self.cyl_low_lba_mid.read() == 0x14 && self.cyl_high_lba_hi.read() == 0xEB {
self.command.write(Command::IdentifyPacket as u8);
if self.status.read().bits() == 0 {
return Err(IdentifyError::Error(Error::NonexistentDrive));
};
while self.status.read().contains(Status::BSY) {}
while !(self.status.read().intersects(Status::DRQ | Status::ERR)) {}
self.error()?;
Ok((DriveKind::ATAPI, self.read_data()))
} else {
Err(IdentifyError::SATADrive)
}
} else {
Ok((DriveKind::ATA, self.read_data()))
}
}
fn error(&mut self) -> Result<(), Error> {
if self.status.read().contains(Status::ERR) {
Err(self.error.read().into())
} else {
Ok(())
}
}
}
#[allow(unused)]
// SAFETY
// This is safe because 0x1F0 is the standard primary bus base address
static PRIMARY_BUS: Mutex<Bus> = Mutex::new(unsafe { Bus::new(0x1F0) });
#[allow(unused)]
// SAFETY
// This is safe because 0x170 is the standard secondary bus base address
static SECONDARY_BUS: Mutex<Bus> = Mutex::new(unsafe { Bus::new(0x170) });
#[allow(unused)]
pub static PRIMARY_MASTER: Lazy<Result<Device, NewDeviceError>> =
Lazy::new(|| Device::new(&PRIMARY_BUS, DriveNumber::Master));
#[allow(unused)]
pub static PRIMARY_SLAVE: Lazy<Result<Device, NewDeviceError>> =
Lazy::new(|| Device::new(&PRIMARY_BUS, DriveNumber::Slave));
#[allow(unused)]
pub static SECONDARY_MASTER: Lazy<Result<Device, NewDeviceError>> =
Lazy::new(|| Device::new(&SECONDARY_BUS, DriveNumber::Master));
#[allow(unused)]
pub static SECONDARY_SLAVE: Lazy<Result<Device, NewDeviceError>> =
Lazy::new(|| Device::new(&SECONDARY_BUS, DriveNumber::Slave));
#[derive(Copy, Clone, Debug)]
pub struct Device {
bus: &'static Mutex<Bus>,
drive_number: DriveNumber,
lba: bool,
pos: u64,
len: u64,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum NewDeviceError {
IdentifyError(IdentifyError),
ATAPIDevice,
}
impl Device {
fn new(bus: &'static Mutex<Bus>, drive_number: DriveNumber) -> Result<Self, NewDeviceError> {
bus.lock().select(drive_number, true, 0);
let id_result = bus
.lock()
.identify()
.map_err(NewDeviceError::IdentifyError)?;
if let (DriveKind::ATA, ident_data) = id_result {
Ok(Self {
bus,
drive_number,
lba: true,
pos: 0,
len: (u64::from(ident_data[60]) * 512),
})
} else {
Err(NewDeviceError::ATAPIDevice)
}
}
fn select(&self, data: u8) {
self.bus.lock().select(self.drive_number, self.lba, data);
}
}
impl Read for Device {
#[allow(clippy::similar_names)]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let avail_len = u64::min(u64::min(self.len - self.pos, buf.len() as u64), 512 * 250);
if avail_len == 0 {
return Ok(0);
}
let sector = self.pos / 512;
assert!(sector < 0x1_0000_0000_0000);
let offset = (self.pos % 512) as usize;
let length = avail_len as usize + offset;
let num_sectors = length.div_ceil(512);
assert!(num_sectors < 256);
if sector < 0x1000_0000 {
self.select(((sector & 0xF00_0000) >> 24) as u8);
let mut bus = self.bus.lock();
bus.sector_count.write(num_sectors as u8);
bus.sector_num_lba_lo.write(sector as u8);
bus.cyl_low_lba_mid.write((sector >> 8) as u8);
bus.cyl_high_lba_hi.write((sector >> 16) as u8);
bus.send_command(Command::ReadSectors)?;
} else {
unimplemented!();
}
let mut byte_num = 0;
let mut i = 0;
for _ in 0..num_sectors {
for byte in self
.bus
.lock()
.read_data()
.into_iter()
.flat_map(u16::to_le_bytes)
{
if byte_num >= offset && byte_num < (offset + buf.len()) {
buf[i] = byte;
i += 1;
}
byte_num += 1;
}
}
self.pos += avail_len as u64;
Ok(avail_len as usize)
}
}
impl Seek for Device {
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
match pos {
io::SeekFrom::Start(x) => self.pos = x,
io::SeekFrom::End(_) => {
return Err(io::Error::new(
io::ErrorKind::Other,
"End seek not implemented yet",
))
}
io::SeekFrom::Current(x) => match x.cmp(&0) {
Ordering::Equal => (),
Ordering::Greater => self.pos += x.unsigned_abs(),
Ordering::Less => self.pos -= x.unsigned_abs(),
},
};
Ok(self.pos)
}
}

View File

@ -1,164 +0,0 @@
mod block_group_table;
mod block_reader;
mod dir;
mod file;
mod metadata;
mod structs;
use block_group_table::BlockGroupDescriptorTable;
use block_reader::BlockReader;
use std::dbg;
use std::io::{self, Read};
use std::path::{Component, Path};
use std::string::{String, ToString};
use std::vec::Vec;
use structs::{Inode, Superblock};
pub use dir::{DirEntry, ReadDir};
pub use file::File;
pub use metadata::{FileType, Metadata, Permissions};
use std::fs::File as StdFile;
#[derive(Debug)]
pub struct Ext2 {
reader: BlockReader,
descriptor_table: BlockGroupDescriptorTable,
}
impl Ext2 {
pub fn new(mut disk: StdFile) -> io::Result<Self> {
let superblock = Superblock::read_from_disk(&mut disk)?;
let reader = BlockReader::new(disk, &superblock);
Ok(Self {
descriptor_table: dbg!(BlockGroupDescriptorTable::new(&superblock, &reader)?),
reader,
})
}
fn path_inode<P: AsRef<Path>>(&self, path: P) -> io::Result<u32> {
let path = path.as_ref();
path.parent().map_or(Ok(2), |parent| {
self.read_dir_no_path(parent)?
.get_entry(path.file_name().ok_or_else(|| {
io::Error::new(
io::ErrorKind::Other,
"A path ending in .. or . was passed to path_inode",
)
})?)
.map(|x| x.inode)
})
}
/// Returns an iterator over the entries within a directory.
///
/// The iterator will yield instances of <code>[`io::Result`]<[`DirEntry`]></code>.
/// New errors may be encountered after an iterator is initially constructed.
/// Entries for the current and parent directories (`.` and `..`) are
/// skipped.
///
///
/// # Errors
///
/// This function will return an error in the following situations, but is not
/// limited to just these cases:
///
/// * The provided `path` doesn't exist.
/// * The process lacks permissions to view the contents.
/// * The `path` points at a non-directory file.
#[allow(unused)]
pub fn read_dir<P: AsRef<Path>>(&self, path: P) -> io::Result<ReadDir> {
path.as_ref().components().try_fold(
ReadDir::read_inode(self, 2, Some("/".to_string().into()))?,
|dir, component| match component {
Component::Normal(os_str) => dir.get_entry(os_str)?.read_dir(),
_ => Ok(dir),
},
)
}
/// Returns an iterator over the entries within a directory, without allocating the full path
/// for the directory on the heap. This saves unnecessary allocations when you will not use
/// `DirEntry::path`.
///
/// The iterator will yield instances of <code>[`io::Result`]<[`DirEntry`]></code>.
/// New errors may be encountered after an iterator is initially constructed.
/// Entries for the current and parent directories (`.` and `..`) are
/// skipped.
///
///
/// # Errors
///
/// This function will return an error in the following situations, but is not
/// limited to just these cases:
///
/// * The provided `path` doesn't exist.
/// * The process lacks permissions to view the contents.
/// * The `path` points at a non-directory file.
pub fn read_dir_no_path<P: AsRef<Path>>(&self, path: P) -> io::Result<ReadDir> {
path.as_ref().components().try_fold(
ReadDir::read_inode(self, 2, None)?,
|dir, component| match component {
Component::Normal(os_str) => dir.get_entry(os_str)?.read_dir(),
_ => Ok(dir),
},
)
}
/// Attempts to open a file in read-only mode.
///
/// # Errors
///
/// This function will return an error if `path` does not already exist.
#[allow(unused)]
pub fn open<P: AsRef<Path>>(&self, path: P) -> io::Result<File> {
File::from_inode(Inode::read(self, self.path_inode(path)?)?, self)
}
/// Read the entire contents of a file into a string.
///
/// This is a convenience function for using [`File::open`] and [`read_to_string`]
/// with fewer imports and without an intermediate variable.
///
/// [`read_to_string`]: core2::io::Read::read_to_string
///
/// # Errors
///
/// This function will return an error if `path` does not already exist.
/// Other errors may also be returned according to [`OpenOptions::open`].
///
/// It will also return an error if it encounters while reading an error
/// of a kind other than [`io::ErrorKind::Interrupted`],
/// or if the contents of the file are not valid UTF-8.
#[allow(unused)]
pub fn read_to_string<P: AsRef<Path>>(&self, path: P) -> io::Result<String> {
let mut string = String::new();
self.open(path)?.read_to_string(&mut string)?;
Ok(string)
}
/// Read the entire contents of a file into a bytes vector.
///
/// This is a convenience function for using [`File::open`] and [`read_to_end`]
/// with fewer imports and without an intermediate variable.
///
/// [`read_to_end`]: core2::io::Read::read_to_end
///
/// # Errors
///
/// This function will return an error if `path` does not already exist.
/// Other errors may also be returned according to [`OpenOptions::open`].
///
/// It will also return an error if it encounters while reading an error
/// of a kind other than [`io::ErrorKind::Interrupted`].
#[allow(unused)]
pub fn read<P: AsRef<Path>>(&self, path: P) -> io::Result<Vec<u8>> {
let mut bytes = Vec::new();
self.open(path)?.read_to_end(&mut bytes)?;
Ok(bytes)
}
#[allow(unused)]
pub fn metadata<P: AsRef<Path>>(&self, path: P) -> io::Result<Metadata> {
Inode::read(self, self.path_inode(path)?).map(|x| Metadata::from_inode(&x))
}
}

View File

@ -1,83 +0,0 @@
use super::{
block_reader::BlockReader,
structs::{BlockGroupDescriptor, Inode, Superblock},
};
use binread::prelude::*;
use std::vec::Vec;
use std::{dbg, io};
const INODE_SIZE: usize = 256;
#[derive(Debug, BinRead)]
#[br(import(len: usize, block_group_inode_count: u32, block_size: usize))]
pub struct BlockGroupDescriptorTable {
#[br(count = len)]
table: Vec<BlockGroupDescriptor>,
#[br(calc = block_group_inode_count)]
block_group_inode_count: u32,
#[br(calc = block_size)]
block_size: usize,
}
impl BlockGroupDescriptorTable {
pub fn new(superblock: &Superblock, reader: &BlockReader) -> io::Result<Self> {
let num_block_groups = superblock
.total_blocks
.div_ceil(superblock.block_group_block_count) as usize;
let table_blocks = num_block_groups.div_ceil(reader.block_size / 32);
Ok(reader
.read_blocks(superblock.superblock_block + 1, table_blocks)?
.read_le_args((
num_block_groups,
superblock.block_group_inode_count,
1024 << superblock.block_size_raw,
))
.expect("Parsing the block group descriptor table should never fail"))
}
fn inode_group(&self, inode: u32) -> &BlockGroupDescriptor {
&self.table[((inode - 1) / self.block_group_inode_count) as usize]
}
fn inode_group_offset(&self, inode: u32) -> usize {
((inode - 1) % self.block_group_inode_count) as usize
}
fn inode_block(&self, inode: u32) -> u32 {
(((self.inode_group_offset(inode) * INODE_SIZE) / self.block_size) as u32)
+ self.inode_group(inode).inode_table_start
}
fn inode_block_byte_start(&self, inode: u32) -> usize {
(self.inode_group_offset(inode) * INODE_SIZE) % self.block_size
}
pub fn inode_loc(&self, inode: u32) -> InodeLoc {
// dbg!(self);
InodeLoc {
block: self.inode_block(inode),
byte_offset: self.inode_block_byte_start(inode),
num: inode,
}
}
}
#[derive(Debug)]
pub struct InodeLoc {
block: u32,
byte_offset: usize,
num: u32,
}
impl InodeLoc {
pub fn read(&self, reader: &BlockReader) -> io::Result<Inode> {
// dbg!(self.num);
reader
.read_block_offset(self.block, self.byte_offset as u32)?
.read_le_args((self.num,))
.map_err(|err| match err {
binread::Error::Io(_) => io::Error::new(io::ErrorKind::Other, "Binread IO error"),
_ => io::Error::new(io::ErrorKind::InvalidData, "Inode data was invalid"),
})
}
}

View File

@ -1,54 +0,0 @@
use std::dbg;
use std::vec::Vec;
use super::structs::Superblock;
use std::fs::File as StdFile;
use std::io::{self, Cursor};
#[derive(Debug)]
pub struct BlockReader {
pub disk: StdFile,
pub block_size: usize,
}
impl BlockReader {
pub fn new(disk: StdFile, superblock: &Superblock) -> Self {
Self {
disk,
block_size: 1024 << superblock.block_size_raw,
}
}
pub fn read_block(&self, block: u32) -> io::Result<Cursor<Vec<u8>>> {
self.read_blocks_offset(block, 0, 1)
}
pub fn read_block_offset(&self, block: u32, offset: u32) -> io::Result<Cursor<Vec<u8>>> {
self.read_blocks_offset(block, offset, 1)
}
pub fn read_blocks(&self, block: u32, count: usize) -> io::Result<Cursor<Vec<u8>>> {
self.read_blocks_offset(block, 0, count)
}
pub fn read_blocks_offset(
&self,
block: u32,
offset: u32,
count: usize,
) -> io::Result<Cursor<Vec<u8>>> {
// dbg!(block, offset, count, self.block_size);
let start = self.block_to_byte_offset(block) + u64::from(offset);
let size = (self.block_size * count) - (offset as usize);
// dbg!(start, size);
let mut vec = Vec::new();
vec.resize(size, 0);
self.disk.read_at(&mut vec, start)?;
// dbg!(&vec[0..16]);
Ok(Cursor::new(vec))
}
fn block_to_byte_offset(&self, block: u32) -> u64 {
u64::from(block) * (self.block_size as u64)
}
}

View File

@ -1,180 +0,0 @@
use super::{
file::File,
metadata::{FileType, Metadata},
structs::{DirEntryDisk, Inode},
Ext2,
};
use binread::BinReaderExt;
use std::io;
use std::path::PathBuf;
use std::string::String;
/// Entries returned by the [`ReadDir`] iterator.
///
/// An instance of `DirEntry` represents an entry inside of a directory on the
/// filesystem. Each entry can be inspected via methods to learn about the full
/// path or possibly other metadata through per-platform extension traits.
#[allow(unused)]
#[allow(clippy::module_name_repetitions)]
pub struct DirEntry<'a> {
pub(super) inode: u32,
pub(super) name: String,
pub(super) metadata: Metadata,
pub(super) directory_path: Option<PathBuf>,
fs: &'a Ext2,
}
impl<'a> core::fmt::Debug for DirEntry<'a> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("DirEntry")
.field("inode", &self.inode)
.field("name", &self.name)
.field("metadata", &self.metadata)
.field("directory_path", &self.directory_path)
.finish()
}
}
#[allow(unused)]
impl<'a> DirEntry<'a> {
/// Returns the bare file name of this directory entry without any other
/// leading path component.
pub fn file_name(&self) -> String {
self.name.clone()
}
/// Returns the file type for the file that this entry points at.
///
/// This function will not traverse symlinks if this entry points at a
/// symlink.
pub fn file_type(&self) -> FileType {
self.metadata.file_type()
}
/// Returns the metadata for the file that this entry points at.
///
/// This function will not traverse symlinks if this entry points at a
/// symlink.
pub fn metadata(&self) -> Metadata {
self.metadata
}
/// Returns the full path to the file that this entry represents.
///
/// The full path is created by joining the original path to `read_dir`
/// with the filename of this entry.
///
/// # Panics
///
/// This function will panic if this entry came either directly from a `ReadDir` made with
/// `Ext2::read_dir_no_path`, or indirectly from such a `ReadDir` by use of the
/// `DirEntry::read_dir` function. This is an explicit decision, so such a panic indicates a
/// definite bug in your code.
pub fn path(&self) -> PathBuf {
self.try_path().expect("Path was None")
}
/// Returns the full path to the file that this entry represents.
///
/// The full path is created by joining the original path to `read_dir`
/// with the filename of this entry.
pub fn try_path(&self) -> Option<PathBuf> {
self.directory_path
.as_ref()
.map(|x| x.join(self.name.clone()))
}
pub fn open(&self) -> io::Result<File> {
File::from_inode(Inode::read(self.fs, self.inode)?, self.fs)
}
pub fn read_dir(&self) -> io::Result<ReadDir<'a>> {
ReadDir::read_inode(self.fs, self.inode, self.try_path())
}
}
/// Iterator over the entries in a directory.
///
/// This iterator is returned from the [`super::Ext2::read_dir`] function and
/// will yield instances of <code>[`io::Result`]<[`DirEntry`]></code>. Through a [`DirEntry`]
/// information like the entry's path and possibly other metadata can be
/// learned.
///
/// The order in which this iterator returns entries is platform and filesystem
/// dependent.
///
/// # Errors
///
/// This [`io::Result`] will be an `Err` if there's some sort of intermittent
/// IO error during iteration.
#[derive(Debug)]
#[allow(clippy::module_name_repetitions)]
pub struct ReadDir<'a> {
fs: &'a Ext2,
file: File<'a>,
directory_path: Option<PathBuf>,
}
impl<'a> ReadDir<'a> {
pub fn read_inode(fs: &'a Ext2, inode: u32, path: Option<PathBuf>) -> io::Result<ReadDir<'a>> {
Ok(Self {
fs,
file: File::from_inode_dir(Inode::read(fs, inode)?, fs)?,
directory_path: path,
})
}
pub(super) fn get_entry(mut self, name: &str) -> io::Result<DirEntry<'a>> {
self.find(|entry| {
if let Ok(entry) = entry {
entry.name == name
} else {
true
}
})
.unwrap_or_else(|| Err(io::Error::new(io::ErrorKind::NotFound, "File not found")))
}
fn next_impl(&mut self) -> io::Result<Option<DirEntry<'a>>> {
let entry: DirEntryDisk = match self.file.read_le() {
Ok(entry) => entry,
Err(binread::Error::Io(io_error)) => {
return if io_error.kind() == io::ErrorKind::UnexpectedEof {
Ok(None)
} else {
Err(io_error)
}
}
Err(_) => {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"Directory entry data was invalid",
))
}
};
let name = String::from_utf8(entry.name).map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidData,
"Non UTF-8 directory entry names are unspported",
)
})?;
if name == "." || name == ".." || entry.inode == 0 {
return self.next_impl();
}
let entry_inode = Inode::read(self.fs, entry.inode)?;
Ok(Some(DirEntry {
inode: entry.inode,
name,
metadata: Metadata::from_inode(&entry_inode),
directory_path: self.directory_path.clone(),
fs: self.fs,
}))
}
}
impl<'a> Iterator for ReadDir<'a> {
type Item = io::Result<DirEntry<'a>>;
fn next(&mut self) -> Option<Self::Item> {
self.next_impl().transpose()
}
}

View File

@ -1,133 +0,0 @@
use super::Ext2;
use super::{structs::Inode, Metadata};
use core::{cmp::Ordering, str};
use std::io::{self, Read};
use std::string::String;
/// A reference to an open file on the filesystem.
///
/// An instance of a `File` can be read and/or written depending on what options
/// it was opened with. Files also implement [`Seek`] to alter the logical cursor
/// that the file contains internally.
///
/// Files are automatically closed when they go out of scope. Errors detected
/// on closing are ignored by the implementation of `Drop`.
#[derive(Debug)]
pub struct File<'a> {
pos: u64,
inode: Inode,
fs: &'a Ext2,
block_size: usize,
}
impl<'a> File<'a> {
pub(super) fn from_inode(inode: Inode, fs: &'a Ext2) -> io::Result<Self> {
// if !Metadata::from_inode(&inode).file_type().is_file() {
// return Err(io::Error::new(io::ErrorKind::Other, "Not a file"));
// }
Ok(Self {
pos: 0,
block_size: fs.reader.block_size,
fs,
inode,
})
}
/// This version of `from_inode` skips checks that the provided inode is a directory, not a file. It
/// should, only be used by `ReadDir::read_inode` to read the raw directory data.
pub(super) fn from_inode_dir(inode: Inode, fs: &'a Ext2) -> io::Result<Self> {
if !Metadata::from_inode(&inode).file_type().is_dir() {
return Err(io::Error::new(io::ErrorKind::Other, "Not a directory"));
}
Ok(Self {
pos: 0,
block_size: fs.reader.block_size,
fs,
inode,
})
}
/// Queries metadata about the underlying file.
#[allow(unused)]
pub fn metadata(&self) -> Metadata {
Metadata::from_inode(&self.inode)
}
pub fn read_at(&mut self, buf: &mut [u8], pos: u64) -> io::Result<usize> {
let bytes_to_end = u64::from(self.inode.size_lower32).saturating_sub(pos);
if bytes_to_end == 0 {
return Ok(0);
}
let start_block = (pos / self.block_size as u64) as u32;
let offset = (pos % self.block_size as u64) as usize;
let length = core::cmp::min(bytes_to_end, buf.len() as u64);
self.fs
.reader
.read_block_offset(
self.inode
.log_to_phys_block(start_block)
.ok_or_else(|| io::Error::new(io::ErrorKind::UnexpectedEof, "EOF reached"))?,
offset as u32,
)?
.read(buf)?;
let mut remaining_bytes = length.saturating_sub((self.block_size - offset) as u64);
for block in (start_block + 1)..=u32::MAX {
if remaining_bytes == 0 {
break;
};
self.fs
.reader
.read_block(
self.inode.log_to_phys_block(block).ok_or_else(|| {
io::Error::new(io::ErrorKind::UnexpectedEof, "EOF reached")
})?,
)?
.read(buf)?;
remaining_bytes = remaining_bytes.saturating_sub(self.block_size as u64);
}
Ok((length - remaining_bytes) as usize)
}
pub fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
unsafe {
let buf = buf.as_mut_vec();
let ret = self.read_to_end(buf)?;
str::from_utf8(buf).map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidData,
"stream did not contain valid UTF-8",
)
})?;
Ok(ret)
}
}
}
impl io::Seek for File<'_> {
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
match pos {
io::SeekFrom::Start(x) => self.pos = x,
io::SeekFrom::End(x) => match x.cmp(&0) {
Ordering::Equal => self.pos = u64::from(self.inode.size_lower32),
Ordering::Greater => unimplemented!(),
Ordering::Less => self.pos = u64::from(self.inode.size_lower32) - x.unsigned_abs(),
},
io::SeekFrom::Current(x) => match x.cmp(&0) {
Ordering::Equal => (),
Ordering::Greater => self.pos += x.unsigned_abs(),
Ordering::Less => self.pos -= x.unsigned_abs(),
},
};
Ok(self.pos)
}
}
impl io::Read for File<'_> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let res = self.read_at(buf, self.pos);
if let Ok(bytes_read) = res {
self.pos += bytes_read as u64;
}
res
}
}

View File

@ -1,252 +0,0 @@
#![allow(unused)]
use super::structs::Inode;
#[derive(Copy, Clone, Debug)]
pub struct Permissions(u32);
impl Permissions {
/// Returns `true` if these permissions describe a readonly (unwritable) file.
pub fn readonly(self) -> bool {
// check if any class (owner, group, others) has write permission
self.0 & 0o222 == 0
}
/// Modifies the readonly flag for this set of permissions. If the
/// `readonly` argument is `true`, using the resulting `Permission` will
/// update file permissions to forbid writing. Conversely, if it's `false`,
/// using the resulting `Permission` will update file permissions to allow
/// writing.
///
/// This operation does **not** modify the filesystem.
pub fn set_readonly(mut self, readonly: bool) {
if readonly {
// remove write permission for all classes; equivalent to `chmod a-w <file>`
self.0 &= !0o222;
} else {
// add write permission for all classes; equivalent to `chmod a+w <file>`
self.0 |= 0o222;
}
}
/// Returns the underlying raw `st_mode` bits that contain the standard
/// Unix permissions for this file.
pub fn mode(self) -> u32 {
self.0 as u32
}
/// Sets the underlying raw bits for this set of permissions.
pub fn set_mode(mut self, mode: u32) {
self.0 = mode;
}
/// Creates a new instance of `Permissions` from the given set of Unix
/// permission bits.
pub fn from_mode(mode: u32) -> Self {
Self(mode)
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum FileType {
Fifo,
CharDev,
Directory,
BlockDev,
File,
SymLink,
Socket,
}
impl FileType {
/// Tests whether this file type represents a directory. The
/// result is mutually exclusive to the results of
/// [`is_file`] and [`is_symlink`]; only zero or one of these
/// tests may pass.
pub fn is_dir(self) -> bool {
self == Self::Directory
}
/// Tests whether this file type represents a regular file.
/// The result is mutually exclusive to the results of
/// [`is_dir`] and [`is_symlink`]; only zero or one of these
/// tests may pass.
pub fn is_file(self) -> bool {
self == Self::File
}
/// Tests whether this file type represents a symbolic link.
/// The result is mutually exclusive to the results of
/// [`is_dir`] and [`is_file`]; only zero or one of these
/// tests may pass.
pub fn is_symlink(self) -> bool {
self == Self::SymLink
}
/// Returns `true` if this file type is a fifo.
pub fn is_fifo(self) -> bool {
self == Self::Fifo
}
/// Returns `true` if this file type is a block device.
pub fn is_block_device(self) -> bool {
self == Self::BlockDev
}
/// Returns `true` if this file type is a char device.
pub fn is_char_device(self) -> bool {
self == Self::CharDev
}
/// Returns `true` if this file type is a socket.
pub fn is_socket(self) -> bool {
self == Self::Socket
}
}
#[derive(Copy, Clone, Debug)]
pub struct Metadata {
accessed: u32,
created: u32,
file_type: FileType,
length: u64,
modified: u32,
permissions: Permissions,
ino: u64,
nlink: u64,
uid: u32,
gid: u32,
// blksize: u64,
blocks: u64,
}
impl Metadata {
pub(super) fn from_inode(inode: &Inode) -> Self {
let file_type = match (inode.type_perms & 0xF000) >> 12 {
1 => FileType::Fifo,
2 => FileType::CharDev,
4 => FileType::Directory,
6 => FileType::BlockDev,
8 => FileType::File,
0xA => FileType::SymLink,
0xC => FileType::Socket,
x => panic!("Invalid inode file type {}", x),
};
let permissions = Permissions(u32::from(inode.type_perms & 0xFFF));
Self {
accessed: inode.last_access_time,
created: inode.creation_time,
file_type,
length: u64::from(inode.size_lower32),
modified: inode.last_modification_time,
permissions,
ino: u64::from(inode.number),
nlink: u64::from(inode.hard_links_count),
uid: u32::from(inode.uid),
gid: u32::from(inode.gid),
// blksize: inode.reader.block_size as u64,
blocks: u64::from(inode.sectors_used),
}
}
/// Returns the last access time of this metadata.
///
/// The returned value corresponds to the `atime` field of `stat`
pub fn accessed(&self) -> u32 {
self.accessed
}
/// Returns the creation time listed in this metadata.
///
/// The returned value corresponds to the `btime` field of `statx` on
/// Linux kernel starting from to 4.11, and the `birthtime` field of `stat` on
/// other Unix platforms.
pub fn created(&self) -> u32 {
self.created
}
/// Returns the last modification time listed in this metadata.
///
/// The returned value corresponds to the `mtime` field of `stat`
pub fn modified(&self) -> u32 {
self.modified
}
/// Returns the file type for this metadata.
pub fn file_type(&self) -> FileType {
self.file_type
}
/// Returns `true` if this metadata is for a directory. The
/// result is mutually exclusive to the result of
/// [`Metadata::is_file`], and will be false for symlink metadata
/// obtained from [`symlink_metadata`].
pub fn is_dir(&self) -> bool {
self.file_type.is_dir()
}
/// Returns `true` if this metadata is for a regular file. The
/// result is mutually exclusive to the result of
/// [`Metadata::is_dir`], and will be false for symlink metadata
/// obtained from [`symlink_metadata`].
///
/// When the goal is simply to read from (or write to) the source, the most0.227s
/// reliable way to test the source can be read (or written to) is to open
/// it. Only using `is_file` can break workflows like `diff <( prog_a )` on
/// a Unix-like system for example. See [`File::open`] or
/// [`OpenOptions::open`] for more information.
pub fn is_file(&self) -> bool {
self.file_type.is_file()
}
/// Returns `true` if this metadata is for a symbolic link.
pub fn is_symlink(&self) -> bool {
self.file_type.is_symlink()
}
/// Returns the size of the file, in bytes, this metadata is for.
pub fn len(&self) -> u64 {
self.length
}
/// Returns the permissions of the file this metadata is for.
pub fn permissions(&self) -> Permissions {
self.permissions
}
/// Returns the inode number.
pub fn ino(&self) -> u64 {
self.ino
}
/// Returns the rights applied to this file.
pub fn mode(&self) -> u32 {
self.permissions.0
}
/// Returns the number of hard links pointing to this file.
pub fn nlink(&self) -> u64 {
self.nlink
}
/// Returns the user ID of the owner of this file.
pub fn uid(&self) -> u32 {
self.uid
}
/// Returns the group ID of the owner of this file.
pub fn gid(&self) -> u32 {
self.gid
}
// /// Returns the block size for filesystem I/O.
// pub fn blksize(&self) -> u64 {
// self.blksize
// }
/// Returns the number of blocks allocated to the file, in 512-byte units.
///
/// Please note that this may be smaller than `st_size / 512` when the file has holes.
pub fn blocks(&self) -> u64 {
self.blocks
}
}

View File

@ -1,159 +0,0 @@
use super::{block_reader::BlockReader, Ext2};
use binread::prelude::*;
use itertools::Itertools;
use std::dbg;
use std::io::{self, Read, Seek, SeekFrom};
use std::vec::Vec;
use std::fs::File as StdFile;
#[repr(u16)]
#[derive(Debug, BinRead)]
#[br(repr=u16)]
pub enum FSState {
Clean = 1,
HasErrors = 2,
}
#[repr(u16)]
#[derive(Debug, BinRead)]
#[br(repr=u16)]
pub enum ErrorHandlingMethod {
Ignore = 1,
RemountRO = 2,
Panic = 3,
}
#[derive(Debug, BinRead)]
#[allow(unused)]
pub struct Superblock {
pub total_inodes: u32,
pub total_blocks: u32,
pub superuser_blocks: u32,
pub unallocated_blocks: u32,
pub unallocated_inodes: u32,
pub superblock_block: u32,
pub block_size_raw: u32,
pub fragment_size_raw: u32,
pub block_group_block_count: u32,
pub block_group_fragment_count: u32,
pub block_group_inode_count: u32,
pub last_mount_time: u32,
pub last_written_time: u32,
pub times_mounted_since_last_check: u16,
pub max_mounts_before_check: u16,
pub ext2_sig: u16,
pub fs_state: FSState,
pub error_handling_method: ErrorHandlingMethod,
pub version_minor: u16,
pub last_check_time: u32,
pub max_interval_between_check: u32,
pub creation_os_id: u32,
pub version_major: u32,
pub reserved_uid: u16,
pub reserved_gid: u16,
}
impl Superblock {
pub fn read_from_disk(disk: &mut StdFile) -> io::Result<Self> {
disk.seek(SeekFrom::Start(1024))?;
Ok(disk.read_le().unwrap())
}
}
#[derive(Debug, Clone, BinRead)]
#[allow(unused)]
pub struct BlockGroupDescriptor {
pub block_usage_block: u32,
pub inode_usage_block: u32,
pub inode_table_start: u32,
pub unallocated_blocks: u16,
pub unallocated_inodes: u16,
pub num_directories: u16,
pub reserved: [u8; 14],
}
#[derive(Debug, BinRead)]
#[allow(unused)]
pub struct DirEntryDisk {
pub inode: u32,
pub entry_size: u16,
pub name_len: u8,
pub high_len_or_type: u8,
#[br(count = name_len)]
#[br(pad_after(i64::from(entry_size - u16::from(name_len) - 8)))]
pub name: Vec<u8>,
}
#[derive(Debug, Clone, BinRead)]
#[br(import(number: u32))]
#[allow(unused)]
pub struct Inode {
pub type_perms: u16,
pub uid: u16,
pub size_lower32: u32,
pub last_access_time: u32,
pub creation_time: u32,
pub last_modification_time: u32,
pub deletion_time: u32,
pub gid: u16,
pub hard_links_count: u16,
pub sectors_used: u32,
pub flags: u32,
pub os_specific_1: u32,
pub direct_block_pointers: [u32; 12],
pub singly_indirect_block_pointer: u32,
pub doubly_indirect_block_pointer: u32,
pub triply_indirect_block_pointer: u32,
pub generation_number: u32,
pub file_acl: u32,
pub file_size_upper32_dir_acl: u32,
pub fragment_block_address: u32,
pub os_specific_2: [u8; 12],
#[br(calc = number)]
pub number: u32,
}
impl Inode {
pub(super) fn read(fs: &Ext2, inode_num: u32) -> io::Result<Self> {
// dbg!();
let ino = fs.descriptor_table.inode_loc(inode_num).read(&fs.reader);
// dbg!();
ino
}
#[allow(unused)]
pub fn pointer_block_blocks(
&self,
block: u32,
reader: &mut BlockReader,
) -> io::Result<impl Iterator<Item = Result<u32, io::Error>>> {
Ok(reader
.read_block(block)?
.bytes()
.tuples::<(_, _, _, _)>()
.map(|raw| {
if let (Ok(b1), Ok(b2), Ok(b3), Ok(b4)) = raw {
Ok(u32::from_le_bytes([b1, b2, b3, b4]))
} else {
Err([raw.0, raw.1, raw.2, raw.3]
.into_iter()
.find_map(Result::err)
.expect("Not all bytes were Ok, yet all are not Err"))
}
})
.filter(|x| if let &Ok(x) = x { x != 0 } else { true }))
}
pub fn log_to_phys_block(&self, block: u32) -> Option<u32> {
if block < 12 {
if self.direct_block_pointers[block as usize] == 0 {
None
} else {
Some(self.direct_block_pointers[block as usize])
}
} else {
unimplemented!()
}
}
}

View File

@ -1,59 +1,18 @@
#![no_std]
#![no_main]
#![feature(int_roundings)]
#![deny(unsafe_op_in_unsafe_fn)]
use ext2::Ext2;
use std::fmt::Debug;
use std::fs::File;
use std::loader::Loader;
use std::path::Path;
use std::prelude::*;
use std::syscalls::{get_initrd, new_process};
use std::os::mikros::loader::Loader;
use std::os::mikros::syscalls::{get_initrd, new_process};
use tar_no_std::TarArchiveRef;
mod ata;
mod ext2;
main!({
dbg!();
// let primary_slave = PRIMARY_SLAVE.unwrap();
let initrd = TarArchiveRef::new(get_initrd());
dbg!();
let test_proc =
fn main() {
let initrd = TarArchiveRef::new(get_initrd()).unwrap();
let vfs =
initrd
.entries()
.find(|entry| entry.filename() == *"bin/test_proc")
.expect("test_proc not found")
.find(|entry| entry.filename().as_str().unwrap() == "bin/vfs")
.expect("vfs not found")
.data();
dbg!();
let (space, entry) = Loader::load(&test_proc);
dbg!();
let pid = new_process(entry as _, space).expect("Failed to create process");
dbg!();
let client = dev_driver_rpc::Client::new(pid);
dbg!();
let fd = client.open("sdb").unwrap();
dbg!();
let sdb = File::from_pid_fd(pid, fd);
dbg!();
let fs = Ext2::new(sdb).unwrap();
dbg!();
print_all_files("/", &fs);
dbg!();
});
fn print_all_files<P: AsRef<Path> + Debug>(path: P, fs: &Ext2) {
for entry in fs.read_dir(path).unwrap() {
let entry = entry.unwrap();
println!("{:?}", entry.path());
// if entry.metadata().file_type().is_dir() {
// // print_all_files(entry.path(), fs);
// } else {
// // let mut file = fs.open(entry.path()).unwrap();
// // let mut buf = String::new();
// // file.read_to_string(&mut buf).unwrap();
// // print!("{:?}:\n{}", entry.path(), buf);
// }
}
let (space, entry) = Loader::load(&vfs);
new_process(entry as _, space).unwrap();
}