ext2 merge plus fix
This commit is contained in:
parent
7faebe1a5c
commit
d2aef77b7f
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -306,7 +306,6 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "ext2"
|
||||
version = "0.1.1"
|
||||
source = "git+https://git.ablecorp.us/able/ext2-rs.git#15bcf9f72e2523e7ebe2a8d09c1231ca9139f326"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"genfs",
|
||||
|
|
|
@ -1,9 +1,3 @@
|
|||
[workspace]
|
||||
|
||||
members = [
|
||||
"ableos",
|
||||
"kernel",
|
||||
"facepalm",
|
||||
"shadeable",
|
||||
"repbuild",
|
||||
]
|
||||
members = ["ableos", "ext2-rs", "kernel", "facepalm", "shadeable", "repbuild"]
|
||||
|
|
|
@ -95,7 +95,7 @@ features = ["size_64", "alloc"]
|
|||
git = "https://git.ablecorp.us:443/able/y-compositor-protocol.git"
|
||||
|
||||
[dependencies.ext2]
|
||||
git = "https://git.ablecorp.us:443/able/ext2-rs.git"
|
||||
path = "../ext2-rs"
|
||||
|
||||
[dependencies.toml]
|
||||
git = "https://github.com/diondokter/toml-rs"
|
||||
|
|
|
@ -12,6 +12,7 @@ use cpuio::inb;
|
|||
use cpuio::outb;
|
||||
use genfs::Fs;
|
||||
use genfs::OpenOptions;
|
||||
use kernel::proccess::PID;
|
||||
use vga::writers::GraphicsWriter;
|
||||
|
||||
// TODO: move to a better place
|
||||
|
@ -113,6 +114,15 @@ pub fn scratchpad() {
|
|||
// */
|
||||
// kernel::aalloc::AAlloc::intialize();
|
||||
|
||||
let mut chan = Channel::new();
|
||||
let perms = ChannelPermission {
|
||||
owner: true,
|
||||
producer: false,
|
||||
consumer: false,
|
||||
distructive_consumer: false,
|
||||
};
|
||||
chan.permission_list.push((0, perms));
|
||||
|
||||
real_shell();
|
||||
}
|
||||
use crate::graphics::VgaBuffer;
|
||||
|
@ -270,3 +280,47 @@ pub fn sound_off() {
|
|||
};
|
||||
reset_pit_for_cpu();
|
||||
}
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
///
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ChannelPermission {
|
||||
owner: bool,
|
||||
producer: bool,
|
||||
|
||||
consumer: bool,
|
||||
/// Whether or not the process can be destructive about reading
|
||||
distructive_consumer: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Channel {
|
||||
inner: Vec<u8>,
|
||||
|
||||
permission_list: Vec<(PID, ChannelPermission)>,
|
||||
}
|
||||
|
||||
impl Channel {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
inner: vec![],
|
||||
permission_list: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ChannelErrors {}
|
||||
|
|
16
ext2-rs/Cargo.toml
Normal file
16
ext2-rs/Cargo.toml
Normal file
|
@ -0,0 +1,16 @@
|
|||
[package]
|
||||
name = "ext2"
|
||||
version = "0.1.1"
|
||||
authors = ["Szymon Walter <walter.szymon.98@gmail.com>",
|
||||
"able <abl3theabove@gmail.com>"]
|
||||
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1.0"
|
||||
rlibc = { version = "1.0", optional = true }
|
||||
spin = "0.9.2"
|
||||
genfs = "^0.1.4"
|
||||
|
||||
[features]
|
||||
default = ["no_std"]
|
||||
no_std = ["rlibc"]
|
22
ext2-rs/LICENSE.md
Normal file
22
ext2-rs/LICENSE.md
Normal file
|
@ -0,0 +1,22 @@
|
|||
# ext2-rs
|
||||
## an ext2 implementation
|
||||
|
||||
Copyright © 2018, Szymon Walter
|
||||
|
||||
This software is provided 'as-is', without any express or implied warranty.
|
||||
In no event will the authors be held liable for any damages arising from
|
||||
the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not
|
||||
be misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
#### walter.szymon.98@gmail.com
|
3
ext2-rs/README.md
Normal file
3
ext2-rs/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# ext2-rs
|
||||
|
||||
An OS and architecture independent implementation of ext2 in pure Rust.
|
BIN
ext2-rs/ext2.img
Normal file
BIN
ext2-rs/ext2.img
Normal file
Binary file not shown.
2
ext2-rs/rustfmt.toml
Normal file
2
ext2-rs/rustfmt.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
max_width = 80
|
||||
wrap_comments = true
|
124
ext2-rs/src/error.rs
Normal file
124
ext2-rs/src/error.rs
Normal file
|
@ -0,0 +1,124 @@
|
|||
//! Errors
|
||||
use {
|
||||
alloc::string::String,
|
||||
core::fmt::{self, Display},
|
||||
};
|
||||
|
||||
#[cfg(any(test, not(feature = "no_std")))]
|
||||
use std::io;
|
||||
|
||||
/// The set of all possible errors
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// Generic error
|
||||
Other(String),
|
||||
/// Bad magic number
|
||||
BadMagic {
|
||||
/// The magic number
|
||||
magic: u16,
|
||||
},
|
||||
/// Out of bounds error
|
||||
OutOfBounds {
|
||||
/// index
|
||||
index: usize,
|
||||
},
|
||||
/// Address out of bounds
|
||||
AddressOutOfBounds {
|
||||
///
|
||||
sector: u32,
|
||||
///
|
||||
offset: u32,
|
||||
|
||||
///
|
||||
size: usize,
|
||||
},
|
||||
/// Bad block group count
|
||||
BadBlockGroupCount {
|
||||
///
|
||||
by_blocks: u32,
|
||||
///
|
||||
by_inodes: u32,
|
||||
},
|
||||
/// Inode Not Found
|
||||
InodeNotFound {
|
||||
/// inode number
|
||||
inode: u32,
|
||||
},
|
||||
/// Inode is not a directory
|
||||
NotADirectory {
|
||||
/// inode number
|
||||
inode: u32,
|
||||
/// inode name
|
||||
name: String,
|
||||
},
|
||||
/// Not Absolute Path
|
||||
NotAbsolute {
|
||||
/// path name
|
||||
name: String,
|
||||
},
|
||||
|
||||
/// Not Found
|
||||
NotFound {
|
||||
/// inode name
|
||||
name: String,
|
||||
},
|
||||
#[cfg(any(test, not(feature = "no_std")))]
|
||||
Io { inner: io::Error },
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Error::Other(ref msg) => write!(f, "{}", msg),
|
||||
Error::BadMagic {
|
||||
magic,
|
||||
} => write!(f, "invalid magic value: {}", magic),
|
||||
Error::OutOfBounds {
|
||||
index,
|
||||
} => write!(f, "index ouf of bounds: {}", index),
|
||||
Error::AddressOutOfBounds {
|
||||
sector,
|
||||
offset,
|
||||
size,
|
||||
} => write!(f, "address ouf of bounds: {}:{} with a block size of: {}",
|
||||
sector, offset, size),
|
||||
Error::BadBlockGroupCount {
|
||||
by_blocks,
|
||||
by_inodes,
|
||||
} => write!(f, "conflicting block group count data; by blocks: {}, by inodes: {}", by_blocks, by_inodes),
|
||||
Error::InodeNotFound {
|
||||
inode,
|
||||
} => write!(f, "couldn't find inode no. {}", &inode),
|
||||
Error::NotADirectory {
|
||||
inode,
|
||||
ref name,
|
||||
} => write!(f, "inode no. {} at: {} is not a directory", inode, &name),
|
||||
Error::NotAbsolute {
|
||||
ref name,
|
||||
} => write!(f, "{} is not an absolute path", &name),
|
||||
Error::NotFound {
|
||||
ref name,
|
||||
} => write!(f, "couldn't find {}", &name),
|
||||
#[cfg(any(test, not(feature = "no_std")))]
|
||||
Error::Io {
|
||||
ref inner,
|
||||
} => write!(f, "io error: {}", inner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Infallible> for Error {
|
||||
fn from(_: Infallible) -> Error {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, not(feature = "no_std")))]
|
||||
impl From<io::Error> for Error {
|
||||
fn from(inner: io::Error) -> Error {
|
||||
Error::Io { inner }
|
||||
}
|
||||
}
|
||||
|
||||
/// Infalliable
|
||||
pub enum Infallible {}
|
177
ext2-rs/src/fs/mod.rs
Normal file
177
ext2-rs/src/fs/mod.rs
Normal file
|
@ -0,0 +1,177 @@
|
|||
//!
|
||||
|
||||
use {
|
||||
alloc::vec::Vec,
|
||||
core::mem,
|
||||
error::Error,
|
||||
sector::{Address, SectorSize},
|
||||
sys::{
|
||||
block_group::BlockGroupDescriptor, inode::Inode as RawInode,
|
||||
superblock::Superblock,
|
||||
},
|
||||
volume::Volume,
|
||||
};
|
||||
|
||||
pub mod sync;
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) struct Struct<T, S: SectorSize> {
|
||||
pub inner: T,
|
||||
pub offset: Address<S>,
|
||||
}
|
||||
|
||||
impl<T, S: SectorSize> From<(T, Address<S>)> for Struct<T, S> {
|
||||
#[inline]
|
||||
fn from((inner, offset): (T, Address<S>)) -> Struct<T, S> {
|
||||
Struct { inner, offset }
|
||||
}
|
||||
}
|
||||
|
||||
/// Safe wrapper for raw sys structs
|
||||
pub struct Ext2<S: SectorSize, V: Volume<u8, S>> {
|
||||
// TODO: should this have some different vis?
|
||||
pub(crate) volume: V,
|
||||
pub(crate) superblock: Struct<Superblock, S>,
|
||||
pub(crate) block_groups: Struct<Vec<BlockGroupDescriptor>, S>,
|
||||
}
|
||||
|
||||
impl<S: SectorSize, V: Volume<u8, S>> Ext2<S, V> {
|
||||
///
|
||||
pub fn new(volume: V) -> Result<Ext2<S, V>, Error> {
|
||||
let superblock = unsafe { Struct::from(Superblock::find(&volume)?) };
|
||||
let block_groups_offset = Address::with_block_size(
|
||||
superblock.inner.first_data_block + 1,
|
||||
0,
|
||||
superblock.inner.log_block_size + 10,
|
||||
);
|
||||
let block_groups_count = superblock
|
||||
.inner
|
||||
.block_group_count()
|
||||
.map(|count| count as usize)
|
||||
.map_err(|(a, b)| Error::BadBlockGroupCount {
|
||||
by_blocks: a,
|
||||
by_inodes: b,
|
||||
})?;
|
||||
let block_groups = unsafe {
|
||||
BlockGroupDescriptor::find_descriptor_table(
|
||||
&volume,
|
||||
block_groups_offset,
|
||||
block_groups_count,
|
||||
)?
|
||||
};
|
||||
let block_groups = Struct::from(block_groups);
|
||||
Ok(Ext2 {
|
||||
volume,
|
||||
superblock,
|
||||
block_groups,
|
||||
})
|
||||
}
|
||||
/// Return the version of the filesystem
|
||||
pub fn version(&self) -> (u32, u16) {
|
||||
(
|
||||
self.superblock.inner.rev_major,
|
||||
self.superblock.inner.rev_minor,
|
||||
)
|
||||
}
|
||||
/// Return inode size
|
||||
pub fn inode_size(&self) -> usize {
|
||||
if self.version().0 == 0 {
|
||||
mem::size_of::<RawInode>()
|
||||
} else {
|
||||
// note: inodes bigger than 128 are not supported
|
||||
self.superblock.inner.inode_size as usize
|
||||
}
|
||||
}
|
||||
///
|
||||
pub fn inodes_count(&self) -> usize {
|
||||
self.superblock.inner.inodes_per_group as _
|
||||
}
|
||||
///
|
||||
pub fn total_inodes_count(&self) -> usize {
|
||||
self.superblock.inner.inodes_count as _
|
||||
}
|
||||
///
|
||||
pub fn block_group_count(&self) -> Result<usize, Error> {
|
||||
self.superblock
|
||||
.inner
|
||||
.block_group_count()
|
||||
.map(|count| count as usize)
|
||||
.map_err(|(a, b)| Error::BadBlockGroupCount {
|
||||
by_blocks: a,
|
||||
by_inodes: b,
|
||||
})
|
||||
}
|
||||
///
|
||||
pub fn total_block_count(&self) -> usize {
|
||||
self.superblock.inner.blocks_count as _
|
||||
}
|
||||
///
|
||||
pub fn free_block_count(&self) -> usize {
|
||||
self.superblock.inner.free_blocks_count as _
|
||||
}
|
||||
///
|
||||
pub fn block_size(&self) -> usize {
|
||||
self.superblock.inner.block_size()
|
||||
}
|
||||
///
|
||||
pub fn log_block_size(&self) -> u32 {
|
||||
self.superblock.inner.log_block_size + 10
|
||||
}
|
||||
///
|
||||
pub fn sector_size(&self) -> usize {
|
||||
S::SIZE
|
||||
}
|
||||
///
|
||||
pub fn log_sector_size(&self) -> u32 {
|
||||
S::LOG_SIZE
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::cell::RefCell;
|
||||
use std::fs::File;
|
||||
|
||||
use sector::{Address, Size512};
|
||||
use volume::Volume;
|
||||
|
||||
use super::Ext2;
|
||||
|
||||
#[test]
|
||||
fn file_len() {
|
||||
let file = RefCell::new(File::open("ext2.img").unwrap());
|
||||
assert_eq!(
|
||||
Address::<Size512>::from(2048_u64)
|
||||
- Address::<Size512>::from(1024_u64),
|
||||
Address::<Size512>::new(2, 0)
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe {
|
||||
file.slice_unchecked(
|
||||
Address::<Size512>::from(1024_u64)
|
||||
..Address::<Size512>::from(2048_u64),
|
||||
)
|
||||
.len()
|
||||
},
|
||||
1024
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file() {
|
||||
let file = RefCell::new(File::open("ext2.img").unwrap());
|
||||
let fs = Ext2::<Size512, _>::new(file);
|
||||
|
||||
assert!(
|
||||
fs.is_ok(),
|
||||
"Err({:?})",
|
||||
fs.err().unwrap_or_else(|| unreachable!()),
|
||||
);
|
||||
|
||||
let fs = fs.unwrap();
|
||||
|
||||
let vers = fs.version();
|
||||
println!("version: {}.{}", vers.0, vers.1);
|
||||
assert_eq!(128, fs.inode_size());
|
||||
}
|
||||
}
|
889
ext2-rs/src/fs/sync.rs
Normal file
889
ext2-rs/src/fs/sync.rs
Normal file
|
@ -0,0 +1,889 @@
|
|||
//!
|
||||
|
||||
use {
|
||||
super::Ext2,
|
||||
alloc::{
|
||||
sync::Arc,
|
||||
{string::String, vec::Vec},
|
||||
},
|
||||
core::{
|
||||
fmt::{self, Debug},
|
||||
iter::Iterator,
|
||||
num::NonZeroU32,
|
||||
},
|
||||
error::Error,
|
||||
genfs::*,
|
||||
sector::{Address, SectorSize},
|
||||
spin::{Mutex, MutexGuard},
|
||||
sys::inode::Inode as RawInode,
|
||||
volume::Volume,
|
||||
};
|
||||
/// DOCME: what is this?
|
||||
pub struct Synced<T> {
|
||||
inner: Arc<Mutex<T>>,
|
||||
}
|
||||
|
||||
impl<T> Synced<T> {
|
||||
/// DOCME: what is this?
|
||||
pub fn with_inner(inner: T) -> Synced<T> {
|
||||
Synced {
|
||||
inner: Arc::new(Mutex::new(inner)),
|
||||
}
|
||||
}
|
||||
|
||||
/// DOCME: what is this?
|
||||
pub fn inner<'a>(&'a self) -> MutexGuard<'a, T> {
|
||||
self.inner.lock()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for Synced<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Synced {
|
||||
inner: self.inner.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SectorSize, V: Volume<u8, S>> Synced<Ext2<S, V>> {
|
||||
/// DOCME: what is this?
|
||||
pub fn new(volume: V) -> Result<Synced<Ext2<S, V>>, Error> {
|
||||
Ext2::new(volume).map(|inner| Synced::with_inner(inner))
|
||||
}
|
||||
/// Get the root inode.
|
||||
pub fn root_inode(&self) -> Inode<S, V> {
|
||||
self.inode_nth(2).unwrap()
|
||||
}
|
||||
/// Get the inode at the given index.
|
||||
pub fn inode_nth(&self, index: usize) -> Option<Inode<S, V>> {
|
||||
self.inodes_nth(index).next()
|
||||
}
|
||||
/// DOCME: what is this?
|
||||
pub fn inodes(&self) -> Inodes<S, V> {
|
||||
self.inodes_nth(1)
|
||||
}
|
||||
/// DOCME: what is this?
|
||||
pub fn inodes_nth(&self, index: usize) -> Inodes<S, V> {
|
||||
assert!(index > 0, "inodes are 1-indexed");
|
||||
let inner = self.inner();
|
||||
Inodes {
|
||||
fs: self.clone(),
|
||||
log_block_size: inner.log_block_size(),
|
||||
inode_size: inner.inode_size(),
|
||||
inodes_per_group: inner.inodes_count(),
|
||||
inodes_count: inner.total_inodes_count(),
|
||||
index,
|
||||
}
|
||||
}
|
||||
/// DOCME: what is this?
|
||||
pub fn sector_size(&self) -> usize {
|
||||
S::SIZE
|
||||
}
|
||||
/// DOCME: what is this?
|
||||
pub fn log_sector_size(&self) -> u32 {
|
||||
S::LOG_SIZE
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SectorSize, V: Volume<u8, S>> Fs for Synced<Ext2<S, V>> {
|
||||
type Path = [u8];
|
||||
type PathOwned = Vec<u8>;
|
||||
type File = Inode<S, V>;
|
||||
type Dir = Directory<S, V>;
|
||||
type DirEntry = DirectoryEntry;
|
||||
type Metadata = (); // TODO
|
||||
type Permissions = (); // TODO
|
||||
type Error = Error;
|
||||
|
||||
fn open(
|
||||
&self,
|
||||
abs_path: &Self::Path,
|
||||
_options: &OpenOptions<Self::Permissions>,
|
||||
) -> Result<Self::File, Self::Error> {
|
||||
fn inner<'a, S, V, I>(
|
||||
fs: &Synced<Ext2<S, V>>,
|
||||
inode: Inode<S, V>,
|
||||
mut path: I,
|
||||
abs_path: &[u8],
|
||||
) -> Result<Inode<S, V>, Error>
|
||||
where
|
||||
S: SectorSize,
|
||||
V: Volume<u8, S>,
|
||||
I: Iterator<Item = &'a [u8]>,
|
||||
{
|
||||
let name = match path.next() {
|
||||
Some(name) => name,
|
||||
None => return Ok(inode),
|
||||
};
|
||||
|
||||
let mut dir =
|
||||
inode.directory().ok_or_else(|| Error::NotADirectory {
|
||||
inode: inode.num,
|
||||
name: String::from_utf8_lossy(abs_path).into_owned(),
|
||||
})?;
|
||||
|
||||
let entry = dir
|
||||
.find(|entry| {
|
||||
entry.is_err() || entry.as_ref().unwrap().name == name
|
||||
})
|
||||
.ok_or_else(|| Error::NotFound {
|
||||
name: String::from_utf8_lossy(abs_path).into_owned(),
|
||||
})??;
|
||||
|
||||
let inode = fs
|
||||
.inode_nth(entry.inode)
|
||||
.ok_or(Error::InodeNotFound { inode: inode.num })?;
|
||||
|
||||
inner(fs, inode, path, abs_path)
|
||||
}
|
||||
|
||||
if abs_path.len() == 0 || abs_path[0] != b'/' {
|
||||
return Err(Error::NotAbsolute {
|
||||
name: String::from_utf8_lossy(abs_path).into_owned(),
|
||||
});
|
||||
}
|
||||
|
||||
if abs_path == b"/" {
|
||||
return Ok(self.root_inode());
|
||||
}
|
||||
|
||||
let mut path = abs_path.split(|byte| *byte == b'/');
|
||||
path.next();
|
||||
let root = self.root_inode();
|
||||
|
||||
inner(self, root, path, abs_path)
|
||||
}
|
||||
|
||||
fn remove_file(&mut self, _path: &Self::Path) -> Result<(), Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn metadata(
|
||||
&self,
|
||||
_path: &Self::Path,
|
||||
) -> Result<Self::Metadata, Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn symlink_metadata(
|
||||
&self,
|
||||
_path: &Self::Path,
|
||||
) -> Result<Self::Metadata, Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn rename(
|
||||
&mut self,
|
||||
_from: &Self::Path,
|
||||
_to: &Self::Path,
|
||||
) -> Result<(), Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn copy(
|
||||
&mut self,
|
||||
_from: &Self::Path,
|
||||
_to: &Self::Path,
|
||||
) -> Result<u64, Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn hard_link(
|
||||
&mut self,
|
||||
_src: &Self::Path,
|
||||
_dst: &Self::Path,
|
||||
) -> Result<(), Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn symlink(
|
||||
&mut self,
|
||||
_src: &Self::Path,
|
||||
_dst: &Self::Path,
|
||||
) -> Result<(), Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn read_link(
|
||||
&self,
|
||||
_path: &Self::Path,
|
||||
) -> Result<Self::PathOwned, Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn canonicalize(
|
||||
&self,
|
||||
_path: &Self::Path,
|
||||
) -> Result<Self::PathOwned, Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn create_dir(
|
||||
&mut self,
|
||||
_path: &Self::Path,
|
||||
_options: &DirOptions<Self::Permissions>,
|
||||
) -> Result<(), Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn remove_dir(&mut self, _path: &Self::Path) -> Result<(), Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn remove_dir_all(
|
||||
&mut self,
|
||||
_path: &Self::Path,
|
||||
) -> Result<(), Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn read_dir(&self, path: &Self::Path) -> Result<Self::Dir, Self::Error> {
|
||||
let inode = self.open(path, OpenOptions::new().read(true))?;
|
||||
inode.directory().ok_or(Error::NotADirectory {
|
||||
inode: inode.num,
|
||||
name: String::from_utf8_lossy(path).into_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
fn set_permissions(
|
||||
&mut self,
|
||||
_path: &Self::Path,
|
||||
_perm: Self::Permissions,
|
||||
) -> Result<(), Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SectorSize, V: Volume<u8, S>> Debug for Synced<Ext2<S, V>> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Synced<Ext2<{}>>", S::SIZE)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// A collection of inodes.
|
||||
pub struct Inodes<S: SectorSize, V: Volume<u8, S>> {
|
||||
fs: Synced<Ext2<S, V>>,
|
||||
log_block_size: u32,
|
||||
inode_size: usize,
|
||||
inodes_per_group: usize,
|
||||
inodes_count: usize,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<S: SectorSize, V: Volume<u8, S>> Iterator for Inodes<S, V> {
|
||||
type Item = Inode<S, V>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.index < self.inodes_count {
|
||||
let block_group = (self.index - 1) / self.inodes_per_group;
|
||||
let index = (self.index - 1) % self.inodes_per_group;
|
||||
self.index += 1;
|
||||
|
||||
let fs = self.fs.inner();
|
||||
|
||||
let inodes_block =
|
||||
fs.block_groups.inner[block_group].inode_table_block;
|
||||
|
||||
let offset = Address::with_block_size(
|
||||
inodes_block,
|
||||
(index * self.inode_size) as i32,
|
||||
self.log_block_size,
|
||||
);
|
||||
let raw = unsafe {
|
||||
RawInode::find_inode(&fs.volume, offset, self.inode_size).ok()
|
||||
};
|
||||
raw.map(|(raw, offset)| {
|
||||
Inode::new(
|
||||
self.fs.clone(),
|
||||
raw,
|
||||
offset,
|
||||
(self.index - 1) as u32,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// A single inode in an ext2 filesystem.
|
||||
pub struct Inode<S: SectorSize, V: Volume<u8, S>> {
|
||||
fs: Synced<Ext2<S, V>>,
|
||||
inner: RawInode,
|
||||
addr: Address<S>,
|
||||
num: u32,
|
||||
}
|
||||
|
||||
impl<S: SectorSize, V: Volume<u8, S>> Clone for Inode<S, V> {
|
||||
fn clone(&self) -> Self {
|
||||
Inode {
|
||||
fs: self.fs.clone(),
|
||||
inner: self.inner,
|
||||
addr: self.addr,
|
||||
num: self.num,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SectorSize, V: Volume<u8, S>> Inode<S, V> {
|
||||
///
|
||||
pub fn new(
|
||||
fs: Synced<Ext2<S, V>>,
|
||||
inner: RawInode,
|
||||
addr: Address<S>,
|
||||
num: u32,
|
||||
) -> Inode<S, V> {
|
||||
Inode {
|
||||
fs,
|
||||
inner,
|
||||
addr,
|
||||
num,
|
||||
}
|
||||
}
|
||||
/// Read to the end of a buffer.
|
||||
pub fn read_to_end(&self, buf: &mut Vec<u8>) -> Result<usize, Error> {
|
||||
let total_size = self.size();
|
||||
let capacity = buf.capacity();
|
||||
if capacity < total_size {
|
||||
buf.reserve_exact(total_size - capacity);
|
||||
}
|
||||
unsafe {
|
||||
buf.set_len(total_size);
|
||||
}
|
||||
let size = self.read(&mut buf[..]);
|
||||
size.and_then(|size| {
|
||||
unsafe {
|
||||
buf.set_len(size);
|
||||
}
|
||||
Ok(size)
|
||||
})
|
||||
.or_else(|err| {
|
||||
unsafe {
|
||||
buf.set_len(0);
|
||||
}
|
||||
Err(err)
|
||||
})
|
||||
}
|
||||
/// Return blocks on a sector
|
||||
pub fn blocks(&self) -> InodeBlocks<S, V> {
|
||||
InodeBlocks {
|
||||
inode: self.clone(),
|
||||
index: 0,
|
||||
}
|
||||
}
|
||||
/// return a directory iterator
|
||||
pub fn directory(&self) -> Option<Directory<S, V>> {
|
||||
if self.is_dir() {
|
||||
Some(Directory {
|
||||
blocks: self.blocks(),
|
||||
offset: 0,
|
||||
buffer: None,
|
||||
block_size: {
|
||||
let fs = self.fs.inner();
|
||||
fs.block_size()
|
||||
},
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
/// Determine if an inode is a directory
|
||||
pub fn is_dir(&self) -> bool {
|
||||
use sys::inode::TypePerm;
|
||||
|
||||
{ self.inner.type_perm }.contains(TypePerm::DIRECTORY)
|
||||
// self.inner.type_perm.contains(TypePerm::DIRECTORY)
|
||||
}
|
||||
///
|
||||
pub fn block(&self, index: usize) -> Option<NonZeroU32> {
|
||||
self.try_block(index).ok().and_then(|block| block)
|
||||
}
|
||||
/// Try to get a block
|
||||
pub fn try_block(
|
||||
&self,
|
||||
mut index: usize,
|
||||
) -> Result<Option<NonZeroU32>, Error> {
|
||||
// number of blocks in direct table: 12
|
||||
// number of blocks in indirect table: block_size/4
|
||||
// why?
|
||||
// - a block is n bytes long
|
||||
// - a block address occupies 32 bits, or 4 bytes
|
||||
// - thus, n/4
|
||||
// number of blocks in doubly table: (block_size/4)^2
|
||||
// why?
|
||||
// - every entry in the doubly table points to another block
|
||||
// - that's n/4 blocks, where n is the block size
|
||||
// - every block contains n/4 block pointers
|
||||
// - that's n/4 blocks with n/4 pointers each = (n/4)^2
|
||||
// number of blocks in triply table: (block_size/4)^3
|
||||
|
||||
fn block_index<S: SectorSize, V: Volume<u8, S>>(
|
||||
volume: &V,
|
||||
block: u32,
|
||||
index: usize,
|
||||
log_block_size: u32,
|
||||
) -> Result<Option<NonZeroU32>, Error> {
|
||||
let offset = (index * 4) as i32;
|
||||
let end = offset + 4;
|
||||
let addr = Address::with_block_size(block, offset, log_block_size);
|
||||
let end = Address::with_block_size(block, end, log_block_size);
|
||||
let block = volume.slice(addr..end);
|
||||
match block {
|
||||
Ok(block) => unsafe {
|
||||
Ok(NonZeroU32::new(block.dynamic_cast::<u32>().0))
|
||||
},
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
}
|
||||
|
||||
let fs = self.fs.inner();
|
||||
|
||||
let bs4 = fs.block_size() / 4;
|
||||
let log_block_size = fs.log_block_size();
|
||||
|
||||
if index < 12 {
|
||||
return Ok(NonZeroU32::new(self.inner.direct_pointer[index]));
|
||||
}
|
||||
|
||||
index -= 12;
|
||||
|
||||
if index < bs4 {
|
||||
let block = self.inner.indirect_pointer;
|
||||
return block_index(&fs.volume, block, index, log_block_size);
|
||||
}
|
||||
|
||||
index -= bs4;
|
||||
|
||||
if index < bs4 * bs4 {
|
||||
let indirect_index = index >> (log_block_size + 2);
|
||||
let block = match block_index(
|
||||
&fs.volume,
|
||||
self.inner.doubly_indirect,
|
||||
indirect_index,
|
||||
log_block_size,
|
||||
) {
|
||||
Ok(Some(block)) => block.get(),
|
||||
Ok(None) => return Ok(None),
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
return block_index(
|
||||
&fs.volume,
|
||||
block,
|
||||
index & (bs4 - 1),
|
||||
log_block_size,
|
||||
);
|
||||
}
|
||||
|
||||
index -= bs4 * bs4;
|
||||
|
||||
if index < bs4 * bs4 * bs4 {
|
||||
let doubly_index = index >> (2 * log_block_size + 4);
|
||||
let indirect = match block_index(
|
||||
&fs.volume,
|
||||
self.inner.triply_indirect,
|
||||
doubly_index,
|
||||
log_block_size,
|
||||
) {
|
||||
Ok(Some(block)) => block.get(),
|
||||
Ok(None) => return Ok(None),
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
let indirect_index = (index >> (log_block_size + 2)) & (bs4 - 1);
|
||||
let block = match block_index(
|
||||
&fs.volume,
|
||||
indirect as u32,
|
||||
indirect_index,
|
||||
log_block_size,
|
||||
) {
|
||||
Ok(Some(block)) => block.get(),
|
||||
Ok(None) => return Ok(None),
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
return block_index(
|
||||
&fs.volume,
|
||||
block,
|
||||
index & (bs4 - 1),
|
||||
log_block_size,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
///
|
||||
pub fn in_use(&self) -> bool {
|
||||
self.inner.hard_links > 0
|
||||
}
|
||||
/// return the uid
|
||||
pub fn uid(&self) -> u16 {
|
||||
self.inner.uid
|
||||
}
|
||||
///
|
||||
pub fn sectors(&self) -> usize {
|
||||
self.inner.sectors_count as usize
|
||||
}
|
||||
///
|
||||
pub fn size32(&self) -> u32 {
|
||||
self.inner.size_low
|
||||
}
|
||||
///
|
||||
pub fn size64(&self) -> u64 {
|
||||
self.inner.size_low as u64 | (self.inner.size_high as u64) << 32
|
||||
}
|
||||
///
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
#[inline]
|
||||
pub fn size(&self) -> usize {
|
||||
self.size64() as usize
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
#[inline]
|
||||
pub fn size(&self) -> usize {
|
||||
self.size32() as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SectorSize, V: Volume<u8, S>> File for Inode<S, V> {
|
||||
type Error = Error;
|
||||
|
||||
fn read(&self, buf: &mut [u8]) -> Result<usize, Error> {
|
||||
let total_size = self.size();
|
||||
let block_size = {
|
||||
let fs = self.fs.inner();
|
||||
fs.block_size()
|
||||
};
|
||||
let mut offset = 0;
|
||||
|
||||
for block in self.blocks() {
|
||||
match block {
|
||||
Ok((data, _)) => {
|
||||
let data_size = block_size
|
||||
.min(total_size - offset)
|
||||
.min(buf.len() - offset);
|
||||
let end = offset + data_size;
|
||||
buf[offset..end].copy_from_slice(&data[..data_size]);
|
||||
offset += data_size;
|
||||
}
|
||||
Err(err) => return Err(err.into()),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(offset)
|
||||
}
|
||||
|
||||
fn write(&mut self, _buf: &[u8]) -> Result<usize, Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> Result<(), Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn seek(&mut self, _pos: SeekFrom) -> Result<u64, Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct InodeBlocks<S: SectorSize, V: Volume<u8, S>> {
|
||||
inode: Inode<S, V>,
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl<S: SectorSize, V: Volume<u8, S>> Iterator for InodeBlocks<S, V> {
|
||||
type Item = Result<(Vec<u8>, Address<S>), Error>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let block = self.inode.try_block(self.index);
|
||||
let block = match block {
|
||||
Ok(Some(ok)) => ok,
|
||||
Ok(None) => return None,
|
||||
Err(err) => return Some(Err(err)),
|
||||
};
|
||||
|
||||
self.index += 1;
|
||||
let fs = self.inode.fs.inner();
|
||||
|
||||
let block = block.get();
|
||||
let log_block_size = fs.log_block_size();
|
||||
let offset = Address::with_block_size(block, 0, log_block_size);
|
||||
let end = Address::with_block_size(block + 1, 0, log_block_size);
|
||||
|
||||
let slice = fs
|
||||
.volume
|
||||
.slice(offset..end)
|
||||
.map(|slice| (slice.to_vec(), offset))
|
||||
.map_err(|err| err.into());
|
||||
Some(slice)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// A directory structure
|
||||
pub struct Directory<S: SectorSize, V: Volume<u8, S>> {
|
||||
blocks: InodeBlocks<S, V>,
|
||||
offset: usize,
|
||||
buffer: Option<Vec<u8>>,
|
||||
block_size: usize,
|
||||
}
|
||||
|
||||
impl<S: SectorSize, V: Volume<u8, S>> Dir<DirectoryEntry, Error>
|
||||
for Directory<S, V>
|
||||
{
|
||||
}
|
||||
|
||||
impl<S: SectorSize, V: Volume<u8, S>> Iterator for Directory<S, V> {
|
||||
type Item = Result<DirectoryEntry, Error>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.buffer.is_none() || self.offset >= self.block_size {
|
||||
self.buffer = match self.blocks.next() {
|
||||
None => return None,
|
||||
Some(Ok((block, _))) => Some(block),
|
||||
Some(Err(err)) => return Some(Err(err)),
|
||||
};
|
||||
|
||||
self.offset = 0;
|
||||
}
|
||||
|
||||
let buffer = &self.buffer.as_ref().unwrap()[self.offset..];
|
||||
|
||||
let inode = buffer[0] as u32
|
||||
| (buffer[1] as u32) << 8
|
||||
| (buffer[2] as u32) << 16
|
||||
| (buffer[3] as u32) << 24;
|
||||
if inode == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let size = buffer[4] as u16 | (buffer[5] as u16) << 8;
|
||||
let len = buffer[6];
|
||||
let ty = buffer[7];
|
||||
|
||||
let name = buffer[8..8 + len as usize].to_vec();
|
||||
|
||||
self.offset += size as usize;
|
||||
|
||||
Some(Ok(DirectoryEntry {
|
||||
name: name,
|
||||
inode: inode as usize,
|
||||
ty: ty,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
/// A directory entry
|
||||
pub struct DirectoryEntry {
|
||||
/// The name of the entry
|
||||
pub name: Vec<u8>,
|
||||
/// The inode of the entry
|
||||
pub inode: usize,
|
||||
///
|
||||
pub ty: u8,
|
||||
}
|
||||
|
||||
impl DirEntry for DirectoryEntry {
|
||||
type Path = [u8];
|
||||
type PathOwned = Vec<u8>;
|
||||
type Metadata = (); // TODO
|
||||
type FileType = u8; // TODO: enum FileType
|
||||
type Error = Error;
|
||||
|
||||
fn path(&self) -> Self::PathOwned {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn metadata(&self) -> Result<Self::Metadata, Self::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn file_type(&self) -> Result<Self::FileType, Self::Error> {
|
||||
Ok(self.ty)
|
||||
}
|
||||
|
||||
fn file_name(&self) -> &Self::Path {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::cell::RefCell;
|
||||
use std::fs::File;
|
||||
|
||||
use genfs::{File as GenFile, Fs, OpenOptions};
|
||||
|
||||
use sector::{SectorSize, Size512};
|
||||
use volume::Volume;
|
||||
|
||||
use super::{Ext2, Inode, Synced};
|
||||
|
||||
#[test]
|
||||
fn file() {
|
||||
let file = RefCell::new(File::open("ext2.img").unwrap());
|
||||
let fs = Synced::<Ext2<Size512, _>>::new(file);
|
||||
|
||||
assert!(
|
||||
fs.is_ok(),
|
||||
"Err({:?})",
|
||||
fs.err().unwrap_or_else(|| unreachable!()),
|
||||
);
|
||||
|
||||
let fs = fs.unwrap();
|
||||
let inner = fs.inner();
|
||||
|
||||
let vers = inner.version();
|
||||
println!("version: {}.{}", vers.0, vers.1);
|
||||
assert_eq!(128, inner.inode_size());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inodes() {
|
||||
let file = RefCell::new(File::open("ext2.img").unwrap());
|
||||
let fs = Synced::<Ext2<Size512, _>>::new(file);
|
||||
|
||||
assert!(
|
||||
fs.is_ok(),
|
||||
"Err({:?})",
|
||||
fs.err().unwrap_or_else(|| unreachable!()),
|
||||
);
|
||||
|
||||
let fs = fs.unwrap();
|
||||
|
||||
let inodes = fs.inodes().filter(|inode| inode.in_use());
|
||||
for inode in inodes {
|
||||
println!("{:?}", inode);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inode_blocks() {
|
||||
use std::str;
|
||||
let file = RefCell::new(File::open("ext2.img").unwrap());
|
||||
let fs = Synced::<Ext2<Size512, _>>::new(file).unwrap();
|
||||
|
||||
let inodes = fs.inodes().filter(|inode| {
|
||||
inode.in_use() && inode.uid() == 1000 && inode.size() < 1024
|
||||
});
|
||||
for inode in inodes {
|
||||
println!("{:?}", inode);
|
||||
let size = inode.size();
|
||||
for block in inode.blocks() {
|
||||
let (data, _) = block.unwrap();
|
||||
assert_eq!(data.len(), {
|
||||
let fs = fs.inner();
|
||||
fs.block_size()
|
||||
});
|
||||
println!("{:?}", &data[..size]);
|
||||
let _ = str::from_utf8(&data[..size])
|
||||
.map(|string| println!("{}", string));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_inode() {
|
||||
let file = RefCell::new(File::open("ext2.img").unwrap());
|
||||
let fs = Synced::<Ext2<Size512, _>>::new(file).unwrap();
|
||||
|
||||
let inodes = fs.inodes().filter(|inode| {
|
||||
inode.in_use() && inode.uid() == 1000 && inode.size() < 1024
|
||||
});
|
||||
for inode in inodes {
|
||||
let mut buf = Vec::with_capacity(inode.size());
|
||||
unsafe {
|
||||
buf.set_len(inode.size());
|
||||
}
|
||||
let size = inode.read(&mut buf[..]);
|
||||
assert!(size.is_ok());
|
||||
let size = size.unwrap();
|
||||
assert_eq!(size, inode.size());
|
||||
unsafe {
|
||||
buf.set_len(size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_big() {
|
||||
let file = RefCell::new(File::open("ext2.img").unwrap());
|
||||
let fs = Synced::<Ext2<Size512, _>>::new(file).unwrap();
|
||||
|
||||
let inodes = fs.inodes().filter(|inode| {
|
||||
inode.in_use() && inode.uid() == 1000 && inode.size() == 537600
|
||||
});
|
||||
for inode in inodes {
|
||||
let mut buf = Vec::with_capacity(inode.size());
|
||||
unsafe {
|
||||
buf.set_len(inode.size());
|
||||
}
|
||||
let size = inode.read(&mut buf[..]);
|
||||
assert!(size.is_ok());
|
||||
let size = size.unwrap();
|
||||
assert_eq!(size, inode.size());
|
||||
unsafe {
|
||||
buf.set_len(size);
|
||||
}
|
||||
|
||||
for (i, &x) in buf.iter().enumerate() {
|
||||
if i & 1 == 0 {
|
||||
assert_eq!(x, b'u', "{}", i);
|
||||
} else {
|
||||
assert_eq!(x, b'\n', "{}", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn walkdir() {
|
||||
use std::str;
|
||||
|
||||
fn walk<'vol, S: SectorSize, V: Volume<u8, S>>(
|
||||
fs: &'vol Synced<Ext2<S, V>>,
|
||||
inode: Inode<S, V>,
|
||||
name: String,
|
||||
) {
|
||||
inode.directory().map(|dir| {
|
||||
for entry in dir {
|
||||
assert!(entry.is_ok());
|
||||
let entry = entry.unwrap();
|
||||
let entry_name = str::from_utf8(&entry.name).unwrap_or("?");
|
||||
println!("{}/{} => {}", name, entry_name, entry.inode,);
|
||||
if entry_name != "." && entry_name != ".." {
|
||||
walk(
|
||||
fs,
|
||||
fs.inode_nth(entry.inode).unwrap(),
|
||||
format!("{}/{}", name, entry_name),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let file = RefCell::new(File::open("ext2.img").unwrap());
|
||||
let fs = Synced::<Ext2<Size512, _>>::new(file).unwrap();
|
||||
|
||||
let root = fs.root_inode();
|
||||
walk(&fs, root, String::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn find() {
|
||||
use std::str;
|
||||
let file = RefCell::new(File::open("ext2.img").unwrap());
|
||||
let fs = Synced::<Ext2<Size512, _>>::new(file).unwrap();
|
||||
|
||||
let found = fs.open(b"/home/funky/README.md", &OpenOptions::new());
|
||||
|
||||
assert!(found.is_ok());
|
||||
let inode = found.unwrap();
|
||||
let mut vec = Vec::new();
|
||||
assert!(inode.read_to_end(&mut vec).is_ok());
|
||||
println!("{}", str::from_utf8(&vec).unwrap());
|
||||
}
|
||||
}
|
41
ext2-rs/src/lib.rs
Normal file
41
ext2-rs/src/lib.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
//! Ext2 crate for ableOS
|
||||
|
||||
#![deny(missing_docs)]
|
||||
#![feature(
|
||||
min_specialization,
|
||||
const_fn_trait_bound,
|
||||
step_trait,
|
||||
associated_type_defaults
|
||||
)]
|
||||
#![cfg_attr(all(not(test), feature = "no_std"), no_std)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
extern crate genfs;
|
||||
extern crate spin;
|
||||
|
||||
#[cfg(any(test, not(feature = "no_std")))]
|
||||
extern crate core;
|
||||
|
||||
pub mod error;
|
||||
pub mod fs;
|
||||
pub mod sector;
|
||||
pub mod sys;
|
||||
pub mod volume;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use sys::block_group::*;
|
||||
use sys::inode::*;
|
||||
use sys::superblock::*;
|
||||
|
||||
#[test]
|
||||
fn sizes() {
|
||||
use std::mem::size_of;
|
||||
assert_eq!(size_of::<Superblock>(), 1024);
|
||||
assert_eq!(size_of::<BlockGroupDescriptor>(), 32);
|
||||
assert_eq!(size_of::<Inode>(), 128);
|
||||
}
|
||||
}
|
242
ext2-rs/src/sector.rs
Normal file
242
ext2-rs/src/sector.rs
Normal file
|
@ -0,0 +1,242 @@
|
|||
//! Sector data.
|
||||
|
||||
use core::{
|
||||
fmt::{self, Debug, Display, LowerHex},
|
||||
iter::Step,
|
||||
marker::PhantomData,
|
||||
ops::{Add, Sub},
|
||||
};
|
||||
/// Size of a sector in bytes
|
||||
pub trait SectorSize: Clone + Copy + PartialEq + PartialOrd + 'static {
|
||||
/// DOCME: What is this?
|
||||
const LOG_SIZE: u32;
|
||||
/// DOCME: What is this?
|
||||
const SIZE: usize = 1 << Self::LOG_SIZE;
|
||||
/// DOCME: What is this?
|
||||
const OFFSET_MASK: u32 = (Self::SIZE - 1) as u32;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
/// DOCME: What is this?
|
||||
pub struct Size512;
|
||||
impl SectorSize for Size512 {
|
||||
const LOG_SIZE: u32 = 9;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
/// DOCME: What is this?
|
||||
pub struct Size1024;
|
||||
impl SectorSize for Size1024 {
|
||||
const LOG_SIZE: u32 = 10;
|
||||
}
|
||||
/// DOCME: What is this?
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct Size2048;
|
||||
impl SectorSize for Size2048 {
|
||||
const LOG_SIZE: u32 = 11;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
/// DOCME: What is this?
|
||||
pub struct Size4096;
|
||||
impl SectorSize for Size4096 {
|
||||
const LOG_SIZE: u32 = 12;
|
||||
}
|
||||
|
||||
/// Address in a physical sector
|
||||
#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct Address<S: SectorSize> {
|
||||
sector: u32,
|
||||
offset: u32,
|
||||
_phantom: PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<S: SectorSize> Address<S> {
|
||||
///
|
||||
pub unsafe fn new_unchecked(sector: u32, offset: u32) -> Address<S> {
|
||||
assert!((offset as usize) < S::SIZE, "offset out of sector bounds");
|
||||
let _phantom = PhantomData;
|
||||
Address {
|
||||
sector,
|
||||
offset,
|
||||
_phantom,
|
||||
}
|
||||
}
|
||||
///
|
||||
pub fn new(sector: u32, offset: i32) -> Address<S> {
|
||||
let sector = (sector as i32 + (offset >> S::LOG_SIZE)) as u32;
|
||||
let offset = offset.abs() as u32 & S::OFFSET_MASK;
|
||||
unsafe { Address::new_unchecked(sector, offset) }
|
||||
}
|
||||
///
|
||||
pub fn with_block_size(
|
||||
block: u32,
|
||||
offset: i32,
|
||||
log_block_size: u32,
|
||||
) -> Address<S> {
|
||||
let block = (block as i32 + (offset >> log_block_size)) as u32;
|
||||
let offset = offset.abs() as u32 & ((1 << log_block_size) - 1);
|
||||
|
||||
let log_diff = log_block_size as i32 - S::LOG_SIZE as i32;
|
||||
let top_offset = offset >> S::LOG_SIZE;
|
||||
let offset = offset & ((1 << S::LOG_SIZE) - 1);
|
||||
let sector = block << log_diff | top_offset;
|
||||
unsafe { Address::new_unchecked(sector, offset) }
|
||||
}
|
||||
///
|
||||
pub fn into_index(&self) -> u64 {
|
||||
((self.sector as u64) << S::LOG_SIZE) + self.offset as u64
|
||||
}
|
||||
/// Get the size of the sector
|
||||
pub const fn sector_size(&self) -> usize {
|
||||
S::SIZE
|
||||
}
|
||||
/// DOCME: What is this?
|
||||
pub const fn log_sector_size(&self) -> u32 {
|
||||
S::LOG_SIZE
|
||||
}
|
||||
/// Return the sector number
|
||||
pub fn sector(&self) -> u32 {
|
||||
self.sector
|
||||
}
|
||||
/// Return the offset in the sector
|
||||
pub fn offset(&self) -> u32 {
|
||||
self.offset
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SectorSize> Step for Address<S> {
|
||||
fn steps_between(start: &Self, end: &Self) -> Option<usize> {
|
||||
if end.sector >= start.sector {
|
||||
Some(end.sector as usize - start.sector as usize)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
fn replace_one(&mut self) -> Self {
|
||||
mem::replace(self, Address::new(1, 0))
|
||||
}
|
||||
|
||||
fn replace_zero(&mut self) -> Self {
|
||||
mem::replace(self, Address::new(0, 0))
|
||||
}
|
||||
|
||||
fn add_one(&self) -> Self {
|
||||
Address::new(self.sector + 1, 0)
|
||||
}
|
||||
|
||||
fn sub_one(&self) -> Self {
|
||||
Address::new(self.sector - 1, 0)
|
||||
}
|
||||
|
||||
fn add_usize(&self, n: usize) -> Option<Self> {
|
||||
self.sector
|
||||
.checked_add(n as u32)
|
||||
.map(|sector| Address::new(sector, 0))
|
||||
}
|
||||
*/
|
||||
|
||||
fn forward_checked(_start: Self, count: usize) -> Option<Self> {
|
||||
todo!("forward_checked: count: {}", count);
|
||||
}
|
||||
|
||||
fn backward_checked(_start: Self, count: usize) -> Option<Self> {
|
||||
todo!("backward_checked count: {}", count);
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SectorSize> Display for Address<S> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}:{}", self.sector, self.offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SectorSize> LowerHex for Address<S> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:x}:{:x}", self.sector, self.offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SectorSize> From<u64> for Address<S> {
|
||||
fn from(idx: u64) -> Address<S> {
|
||||
let sector = idx >> S::LOG_SIZE;
|
||||
let offset = idx & S::OFFSET_MASK as u64;
|
||||
Address::new(sector as u32, offset as i32)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SectorSize> From<usize> for Address<S> {
|
||||
fn from(idx: usize) -> Address<S> {
|
||||
let sector = idx >> S::LOG_SIZE;
|
||||
let offset = idx & S::OFFSET_MASK as usize;
|
||||
Address::new(sector as u32, offset as i32)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SectorSize> Add for Address<S> {
|
||||
type Output = Address<S>;
|
||||
fn add(self, rhs: Address<S>) -> Address<S> {
|
||||
Address::new(
|
||||
self.sector + rhs.sector,
|
||||
(self.offset + rhs.offset) as i32,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SectorSize> Sub for Address<S> {
|
||||
type Output = Address<S>;
|
||||
fn sub(self, rhs: Address<S>) -> Address<S> {
|
||||
Address::new(
|
||||
self.sector - rhs.sector,
|
||||
self.offset as i32 - rhs.offset as i32,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn conv() {
|
||||
assert_eq!(Address::<Size512>::new(0, 1024).into_index(), 1024);
|
||||
assert_eq!(Address::<Size512>::from(1024_u64).into_index(), 1024);
|
||||
assert_eq!(
|
||||
Address::<Size512>::with_block_size(1, 256, 10).into_index(),
|
||||
1024 + 256
|
||||
);
|
||||
assert_eq!(
|
||||
Address::<Size512>::with_block_size(2, 0, 10).into_index(),
|
||||
2048
|
||||
);
|
||||
assert_eq!(
|
||||
Address::<Size512>::with_block_size(0, 1792, 10).into_index(),
|
||||
1792
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arithmetic() {
|
||||
assert_eq!(
|
||||
Address::<Size512>::new(0, 512),
|
||||
Address::<Size512>::new(1, 0),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Address::<Size512>::new(2, -256),
|
||||
Address::<Size512>::new(1, 256),
|
||||
);
|
||||
|
||||
let a = Address::<Size2048>::new(0, 1024);
|
||||
let b = Address::<Size2048>::new(0, 1024);
|
||||
assert_eq!(a + b, Address::<Size2048>::new(1, 0));
|
||||
assert_eq!((a + b).into_index(), 2048);
|
||||
|
||||
let a = Address::<Size512>::new(0, 2048);
|
||||
let b = Address::<Size512>::new(0, 256);
|
||||
assert_eq!(a - b, Address::<Size512>::new(3, 256));
|
||||
assert_eq!((a - b).into_index(), 1792);
|
||||
}
|
||||
}
|
116
ext2-rs/src/sys/block_group.rs
Normal file
116
ext2-rs/src/sys/block_group.rs
Normal file
|
@ -0,0 +1,116 @@
|
|||
//!
|
||||
|
||||
use {
|
||||
alloc::vec::Vec,
|
||||
core::{fmt::Debug, mem},
|
||||
error::Error,
|
||||
sector::{Address, SectorSize},
|
||||
volume::Volume,
|
||||
};
|
||||
|
||||
/// The Block Group Descriptor Table contains a descriptor for each block group
|
||||
/// within the file system. The number of block groups within the file system,
|
||||
/// and correspondingly, the number of entries in the Block Group Descriptor
|
||||
/// Table, is described above. Each descriptor contains information regarding
|
||||
/// where important data structures for that group are located.
|
||||
///
|
||||
/// The (`BlockGroupDescriptor`) table is located in the block immediately
|
||||
/// following the Superblock. So if the block size (determined from a field in
|
||||
/// the superblock) is 1024 bytes per block, the Block Group Descriptor Table
|
||||
/// will begin at block 2. For any other block size, it will begin at block 1.
|
||||
/// Remember that blocks are numbered starting at 0, and that block numbers
|
||||
/// don't usually correspond to physical block addresses.
|
||||
#[repr(C, packed)]
|
||||
#[derive(Clone, Debug, Copy)]
|
||||
pub struct BlockGroupDescriptor {
|
||||
/// Block address of block usage bitmap
|
||||
pub block_usage_addr: u32,
|
||||
/// Block address of inode usage bitmap
|
||||
pub inode_usage_addr: u32,
|
||||
/// Starting block address of inode table
|
||||
pub inode_table_block: u32,
|
||||
/// Number of unallocated blocks in group
|
||||
pub free_blocks_count: u16,
|
||||
/// Number of unallocated inodes in group
|
||||
pub free_inodes_count: u16,
|
||||
/// Number of directories in group
|
||||
pub dirs_count: u16,
|
||||
#[doc(hidden)]
|
||||
_reserved: [u8; 14],
|
||||
}
|
||||
|
||||
impl BlockGroupDescriptor {
|
||||
/// Find a descriptor in a descriptor table
|
||||
pub unsafe fn find_descriptor<S: SectorSize, V: Volume<u8, S>>(
|
||||
haystack: &V,
|
||||
offset: Address<S>,
|
||||
) -> Result<(BlockGroupDescriptor, Address<S>), Error> {
|
||||
let end =
|
||||
offset + Address::from(mem::size_of::<BlockGroupDescriptor>());
|
||||
if haystack.size() < end {
|
||||
return Err(Error::AddressOutOfBounds {
|
||||
sector: end.sector(),
|
||||
offset: end.offset(),
|
||||
size: end.sector_size(),
|
||||
});
|
||||
}
|
||||
|
||||
let descr = haystack
|
||||
.slice_unchecked(offset..end)
|
||||
.dynamic_cast::<BlockGroupDescriptor>();
|
||||
|
||||
Ok(descr)
|
||||
}
|
||||
/// find a descriptor table
|
||||
pub unsafe fn find_descriptor_table<S: SectorSize, V: Volume<u8, S>>(
|
||||
haystack: &V,
|
||||
offset: Address<S>,
|
||||
count: usize,
|
||||
) -> Result<(Vec<BlockGroupDescriptor>, Address<S>), Error> {
|
||||
let end = offset
|
||||
+ Address::from(count * mem::size_of::<BlockGroupDescriptor>());
|
||||
if haystack.size() < end {
|
||||
return Err(Error::AddressOutOfBounds {
|
||||
sector: end.sector(),
|
||||
offset: end.offset(),
|
||||
size: end.sector_size(),
|
||||
});
|
||||
}
|
||||
|
||||
let mut vec = Vec::with_capacity(count);
|
||||
for i in 0..count {
|
||||
let offset = offset
|
||||
+ Address::from(i * mem::size_of::<BlockGroupDescriptor>());
|
||||
vec.push({
|
||||
BlockGroupDescriptor::find_descriptor(haystack, offset)?.0
|
||||
});
|
||||
}
|
||||
|
||||
Ok((vec, offset))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use sector::{Address, Size512};
|
||||
|
||||
#[test]
|
||||
fn find() {
|
||||
let volume = vec![0_u8; 4096];
|
||||
let table = unsafe {
|
||||
BlockGroupDescriptor::find_descriptor_table(
|
||||
&volume,
|
||||
Address::<Size512>::new(4, 0),
|
||||
8,
|
||||
)
|
||||
};
|
||||
assert!(
|
||||
table.is_ok(),
|
||||
"Err({:?})",
|
||||
table.err().unwrap_or_else(|| unreachable!()),
|
||||
);
|
||||
let table = table.unwrap_or_else(|_| unreachable!());
|
||||
assert_eq!(table.0.len(), 8);
|
||||
}
|
||||
}
|
188
ext2-rs/src/sys/inode.rs
Normal file
188
ext2-rs/src/sys/inode.rs
Normal file
|
@ -0,0 +1,188 @@
|
|||
//!
|
||||
use {
|
||||
core::{fmt::Debug, mem},
|
||||
error::Error,
|
||||
sector::{Address, SectorSize},
|
||||
volume::Volume,
|
||||
};
|
||||
|
||||
/// An inode is a structure on the disk that represents a file, directory,
|
||||
/// symbolic link, etc. Inodes do not contain the data of the file / directory /
|
||||
/// etc. that they represent. Instead, they link to the blocks that actually
|
||||
/// contain the data. This lets the inodes themselves have a well-defined size
|
||||
/// which lets them be placed in easily indexed arrays. Each block group has an
|
||||
/// array of inodes it is responsible for, and conversely every inode within a
|
||||
/// file system belongs to one of such tables (and one of such block groups).
|
||||
#[repr(C, packed)]
|
||||
#[derive(Clone, Debug, Copy)]
|
||||
pub struct Inode {
|
||||
/// Type and Permissions (see below)
|
||||
pub type_perm: TypePerm,
|
||||
/// User ID
|
||||
pub uid: u16,
|
||||
/// Lower 32 bits of size in bytes
|
||||
pub size_low: u32,
|
||||
/// Last Access Time (in POSIX time)
|
||||
pub atime: u32,
|
||||
/// Creation Time (in POSIX time)
|
||||
pub ctime: u32,
|
||||
/// Last Modification time (in POSIX time)
|
||||
pub mtime: u32,
|
||||
/// Deletion time (in POSIX time)
|
||||
pub dtime: u32,
|
||||
/// Group ID
|
||||
pub gid: u16,
|
||||
/// Count of hard links (directory entries) to this inode. When this
|
||||
/// reaches 0, the data blocks are marked as unallocated.
|
||||
pub hard_links: u16,
|
||||
/// Count of disk sectors (not Ext2 blocks) in use by this inode, not
|
||||
/// counting the actual inode structure nor directory entries linking
|
||||
/// to the inode.
|
||||
pub sectors_count: u32,
|
||||
/// Flags
|
||||
pub flags: Flags,
|
||||
/// Operating System Specific value #1
|
||||
pub _os_specific_1: [u8; 4],
|
||||
/// Direct block pointers
|
||||
pub direct_pointer: [u32; 12],
|
||||
/// Singly Indirect Block Pointer (Points to a block that is a list of
|
||||
/// block pointers to data)
|
||||
pub indirect_pointer: u32,
|
||||
/// Doubly Indirect Block Pointer (Points to a block that is a list of
|
||||
/// block pointers to Singly Indirect Blocks)
|
||||
pub doubly_indirect: u32,
|
||||
/// Triply Indirect Block Pointer (Points to a block that is a list of
|
||||
/// block pointers to Doubly Indirect Blocks)
|
||||
pub triply_indirect: u32,
|
||||
/// Generation number (Primarily used for NFS)
|
||||
pub gen_number: u32,
|
||||
/// In Ext2 version 0, this field is reserved. In version >= 1,
|
||||
/// Extended attribute block (File ACL).
|
||||
pub ext_attribute_block: u32,
|
||||
/// In Ext2 version 0, this field is reserved. In version >= 1, Upper
|
||||
/// 32 bits of file size (if feature bit set) if it's a file,
|
||||
/// Directory ACL if it's a directory
|
||||
pub size_high: u32,
|
||||
/// Block address of fragment
|
||||
pub frag_block_addr: u32,
|
||||
/// Operating System Specific Value #2
|
||||
pub _os_specific_2: [u8; 12],
|
||||
}
|
||||
|
||||
impl Inode {
|
||||
/// Discover the inode location on the disk.
|
||||
pub unsafe fn find_inode<S: SectorSize, V: Volume<u8, S>>(
|
||||
haystack: &V,
|
||||
offset: Address<S>,
|
||||
size: usize,
|
||||
) -> Result<(Inode, Address<S>), Error> {
|
||||
if size != mem::size_of::<Inode>() {
|
||||
unimplemented!("inodes with a size != 128");
|
||||
}
|
||||
|
||||
let end = offset + Address::from(size);
|
||||
if haystack.size() < end {
|
||||
return Err(Error::AddressOutOfBounds {
|
||||
sector: end.sector(),
|
||||
offset: end.offset(),
|
||||
size: end.sector_size(),
|
||||
});
|
||||
}
|
||||
|
||||
let inode = haystack
|
||||
.slice_unchecked(offset..end)
|
||||
.dynamic_cast::<Inode>();
|
||||
|
||||
Ok(inode)
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
///
|
||||
// #[derive(Copy)]
|
||||
pub struct TypePerm: u16 {
|
||||
/// FIFO
|
||||
const FIFO = 0x1000;
|
||||
/// Character device
|
||||
const CHAR_DEVICE = 0x2000;
|
||||
/// Directory
|
||||
const DIRECTORY = 0x4000;
|
||||
/// Block device
|
||||
const BLOCK_DEVICE = 0x6000;
|
||||
/// Regular file
|
||||
const FILE = 0x8000;
|
||||
/// Symbolic link
|
||||
const SYMLINK = 0xA000;
|
||||
/// Unix socket
|
||||
const SOCKET = 0xC000;
|
||||
/// Other—execute permission
|
||||
const O_EXEC = 0x001;
|
||||
/// Other—write permission
|
||||
const O_WRITE = 0x002;
|
||||
/// Other—read permission
|
||||
const O_READ = 0x004;
|
||||
/// Group—execute permission
|
||||
const G_EXEC = 0x008;
|
||||
/// Group—write permission
|
||||
const G_WRITE = 0x010;
|
||||
/// Group—read permission
|
||||
const G_READ = 0x020;
|
||||
/// User—execute permission
|
||||
const U_EXEC = 0x040;
|
||||
/// User—write permission
|
||||
const U_WRITE = 0x080;
|
||||
/// User—read permission
|
||||
const U_READ = 0x100;
|
||||
/// Sticky Bit
|
||||
const STICKY = 0x200;
|
||||
/// Set group ID
|
||||
const SET_GID = 0x400;
|
||||
/// Set user ID
|
||||
const SET_UID = 0x800;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Flags
|
||||
pub struct Flags: u32 {
|
||||
/// Secure deletion (not used)
|
||||
const SECURE_DEL = 0x00000001;
|
||||
/// Keep a copy of data when deleted (not used)
|
||||
const KEEP_COPY = 0x00000002;
|
||||
/// File compression (not used)
|
||||
const COMPRESSION = 0x00000004;
|
||||
/// Synchronous updates—new data is written immediately to disk
|
||||
const SYNC_UPDATE = 0x00000008;
|
||||
/// Immutable file (content cannot be changed)
|
||||
const IMMUTABLE = 0x00000010;
|
||||
/// Append only
|
||||
const APPEND_ONLY = 0x00000020;
|
||||
/// File is not included in 'dump' command
|
||||
const NODUMP = 0x00000040;
|
||||
/// Last accessed time should not updated
|
||||
const DONT_ATIME = 0x00000080;
|
||||
/// Hash indexed directory
|
||||
const HASH_DIR = 0x00010000;
|
||||
/// AFS directory
|
||||
const AFS_DIR = 0x00020000;
|
||||
/// Journal file data
|
||||
const JOURNAL_DATA = 0x00040000;
|
||||
}
|
||||
}
|
||||
|
||||
/// Unknown entry type
|
||||
pub const UNKNOWN: u8 = 0;
|
||||
/// FIFO entry type
|
||||
pub const FIFO: u8 = 1;
|
||||
/// Character device entry type
|
||||
pub const CHAR_DEVICE: u8 = 2;
|
||||
/// Directory entry type
|
||||
pub const DIRECTORY: u8 = 3;
|
||||
/// Block device entry type
|
||||
pub const BLOCK_DEVICE: u8 = 4;
|
||||
/// Regular file entry type
|
||||
pub const FILE: u8 = 5;
|
||||
/// Symbolic link entry type
|
||||
pub const SYMLINK: u8 = 6;
|
||||
/// Unix socket entry type
|
||||
pub const SOCKET: u8 = 7;
|
5
ext2-rs/src/sys/mod.rs
Normal file
5
ext2-rs/src/sys/mod.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
//!
|
||||
|
||||
pub mod block_group;
|
||||
pub mod inode;
|
||||
pub mod superblock;
|
284
ext2-rs/src/sys/superblock.rs
Normal file
284
ext2-rs/src/sys/superblock.rs
Normal file
|
@ -0,0 +1,284 @@
|
|||
//! Superblock information
|
||||
|
||||
use {
|
||||
core::{fmt::Debug, mem},
|
||||
error::Error,
|
||||
sector::{Address, SectorSize},
|
||||
volume::Volume,
|
||||
};
|
||||
|
||||
/// Ext2 signature (0xef53), used to help confirm the presence of Ext2 on a
|
||||
/// volume
|
||||
pub const EXT2_MAGIC: u16 = 0xef53;
|
||||
|
||||
/// Filesystem is free of errors
|
||||
pub const FS_CLEAN: u16 = 1;
|
||||
/// Filesystem has errors
|
||||
pub const FS_ERR: u16 = 2;
|
||||
|
||||
/// Ignore errors
|
||||
pub const ERR_IGNORE: u16 = 1;
|
||||
/// Remount as read-only on error
|
||||
pub const ERR_RONLY: u16 = 2;
|
||||
/// Panic on error
|
||||
pub const ERR_PANIC: u16 = 3;
|
||||
|
||||
/// Creator OS is Linux
|
||||
pub const OS_LINUX: u32 = 0;
|
||||
/// Creator OS is Hurd
|
||||
pub const OS_HURD: u32 = 1;
|
||||
/// Creator OS is Masix
|
||||
pub const OS_MASIX: u32 = 2;
|
||||
/// Creator OS is FreeBSD
|
||||
pub const OS_FREEBSD: u32 = 3;
|
||||
/// Creator OS is a BSD4.4-Lite derivative
|
||||
pub const OS_LITE: u32 = 4;
|
||||
|
||||
/// The Superblock contains all information about the layout of the file system
|
||||
/// and possibly contains other important information like what optional
|
||||
/// features were used to create the file system.
|
||||
///
|
||||
/// The Superblock is always located at byte 1024 from the beginning of the
|
||||
/// volume and is exactly 1024 bytes in length. For example, if the disk uses
|
||||
/// 512 byte sectors, the Superblock will begin at LBA 2 and will occupy all of
|
||||
/// sector 2 and 3.
|
||||
#[repr(C, packed)]
|
||||
#[derive(Clone, Debug, Copy)]
|
||||
pub struct Superblock {
|
||||
// taken from https://wiki.osdev.org/Ext2
|
||||
/// Total number of inodes in file system
|
||||
pub inodes_count: u32,
|
||||
/// Total number of blocks in file system
|
||||
pub blocks_count: u32,
|
||||
/// Number of blocks reserved for superuser (see offset 80)
|
||||
pub r_blocks_count: u32,
|
||||
/// Total number of unallocated blocks
|
||||
pub free_blocks_count: u32,
|
||||
/// Total number of unallocated inodes
|
||||
pub free_inodes_count: u32,
|
||||
/// Block number of the block containing the superblock
|
||||
pub first_data_block: u32,
|
||||
/// log2 (block size) - 10. (In other words, the number to shift 1,024
|
||||
/// to the left by to obtain the block size)
|
||||
pub log_block_size: u32,
|
||||
/// log2 (fragment size) - 10. (In other words, the number to shift
|
||||
/// 1,024 to the left by to obtain the fragment size)
|
||||
pub log_frag_size: i32,
|
||||
/// Number of blocks in each block group
|
||||
pub blocks_per_group: u32,
|
||||
/// Number of fragments in each block group
|
||||
pub frags_per_group: u32,
|
||||
/// Number of inodes in each block group
|
||||
pub inodes_per_group: u32,
|
||||
/// Last mount time (in POSIX time)
|
||||
pub mtime: u32,
|
||||
/// Last written time (in POSIX time)
|
||||
pub wtime: u32,
|
||||
/// Number of times the volume has been mounted since its last
|
||||
/// consistency check (fsck)
|
||||
pub mnt_count: u16,
|
||||
/// Number of mounts allowed before a consistency check (fsck) must be
|
||||
/// done
|
||||
pub max_mnt_count: i16,
|
||||
/// Ext2 signature (0xef53), used to help confirm the presence of Ext2
|
||||
/// on a volume
|
||||
pub magic: u16,
|
||||
/// File system state (see `FS_CLEAN` and `FS_ERR`)
|
||||
pub state: u16,
|
||||
/// What to do when an error is detected (see `ERR_IGNORE`, `ERR_RONLY` and
|
||||
/// `ERR_PANIC`)
|
||||
pub errors: u16,
|
||||
/// Minor portion of version (combine with Major portion below to
|
||||
/// construct full version field)
|
||||
pub rev_minor: u16,
|
||||
/// POSIX time of last consistency check (fsck)
|
||||
pub lastcheck: u32,
|
||||
/// Interval (in POSIX time) between forced consistency checks (fsck)
|
||||
pub checkinterval: u32,
|
||||
/// Operating system ID from which the filesystem on this volume was
|
||||
/// created
|
||||
pub creator_os: u32,
|
||||
/// Major portion of version (combine with Minor portion above to
|
||||
/// construct full version field)
|
||||
pub rev_major: u32,
|
||||
/// User ID that can use reserved blocks
|
||||
pub block_uid: u16,
|
||||
/// Group ID that can use reserved blocks
|
||||
pub block_gid: u16,
|
||||
|
||||
/// First non-reserved inode in file system.
|
||||
pub first_inode: u32,
|
||||
/// SectorSize of each inode structure in bytes.
|
||||
pub inode_size: u16,
|
||||
/// Block group that this superblock is part of (if backup copy)
|
||||
pub block_group: u16,
|
||||
/// Optional features present (features that are not required to read
|
||||
/// or write, but usually result in a performance increase)
|
||||
pub features_opt: FeaturesOptional,
|
||||
/// Required features present (features that are required to be
|
||||
/// supported to read or write)
|
||||
pub features_req: FeaturesRequired,
|
||||
/// Features that if not supported, the volume must be mounted
|
||||
/// read-only)
|
||||
pub features_ronly: FeaturesROnly,
|
||||
/// File system ID (what is output by blkid)
|
||||
pub fs_id: [u8; 16],
|
||||
/// Volume name (C-style string: characters terminated by a 0 byte)
|
||||
pub volume_name: [u8; 16],
|
||||
/// Path volume was last mounted to (C-style string: characters
|
||||
/// terminated by a 0 byte)
|
||||
pub last_mnt_path: [u8; 64],
|
||||
/// Compression algorithms used (see Required features above)
|
||||
pub compression: u32,
|
||||
/// Number of blocks to preallocate for files
|
||||
pub prealloc_blocks_files: u8,
|
||||
/// Number of blocks to preallocate for directories
|
||||
pub prealloc_blocks_dirs: u8,
|
||||
#[doc(hidden)]
|
||||
_unused: [u8; 2],
|
||||
/// Journal ID (same style as the File system ID above)
|
||||
pub journal_id: [u8; 16],
|
||||
/// Journal inode
|
||||
pub journal_inode: u32,
|
||||
/// Journal device
|
||||
pub journal_dev: u32,
|
||||
/// Head of orphan inode list
|
||||
pub journal_orphan_head: u32,
|
||||
#[doc(hidden)]
|
||||
_reserved: [u8; 788],
|
||||
}
|
||||
impl Superblock {
|
||||
/// Discover the location of the superblock in the given block device.
|
||||
pub unsafe fn find<S: SectorSize, V: Volume<u8, S>>(
|
||||
haystack: &V,
|
||||
) -> Result<(Superblock, Address<S>), Error> {
|
||||
let offset = Address::from(1024_usize);
|
||||
let end = offset + Address::from(mem::size_of::<Superblock>());
|
||||
if haystack.size() < end {
|
||||
return Err(Error::AddressOutOfBounds {
|
||||
sector: end.sector(),
|
||||
offset: end.offset(),
|
||||
size: end.sector_size(),
|
||||
});
|
||||
}
|
||||
|
||||
let superblock = {
|
||||
haystack
|
||||
.slice_unchecked(offset..end)
|
||||
.dynamic_cast::<Superblock>()
|
||||
};
|
||||
|
||||
if superblock.0.magic != EXT2_MAGIC {
|
||||
Err(Error::BadMagic {
|
||||
magic: superblock.0.magic,
|
||||
})
|
||||
} else {
|
||||
Ok(superblock)
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Return the block size
|
||||
pub fn block_size(&self) -> usize {
|
||||
1024 << self.log_block_size
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// Return the fragment size
|
||||
pub fn frag_size(&self) -> usize {
|
||||
1024 << self.log_frag_size
|
||||
}
|
||||
/// Return the number of blocks per group
|
||||
pub fn block_group_count(&self) -> Result<u32, (u32, u32)> {
|
||||
let blocks_mod = self.blocks_count % self.blocks_per_group;
|
||||
let inodes_mod = self.inodes_count % self.inodes_per_group;
|
||||
let blocks_inc = if blocks_mod == 0 { 0 } else { 1 };
|
||||
let inodes_inc = if inodes_mod == 0 { 0 } else { 1 };
|
||||
let by_blocks = self.blocks_count / self.blocks_per_group + blocks_inc;
|
||||
let by_inodes = self.inodes_count / self.inodes_per_group + inodes_inc;
|
||||
if by_blocks == by_inodes {
|
||||
Ok(by_blocks)
|
||||
} else {
|
||||
Err((by_blocks, by_inodes))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Optional features
|
||||
pub struct FeaturesOptional: u32 {
|
||||
/// Preallocate some number of (contiguous?) blocks (see
|
||||
/// `Superblock::prealloc_blocks_dirs`) to a directory when creating a new one
|
||||
const PREALLOCATE = 0x0001;
|
||||
/// AFS server inodes exist
|
||||
const AFS = 0x0002;
|
||||
/// File system has a journal (Ext3)
|
||||
const JOURNAL = 0x0004;
|
||||
/// Inodes have extended attributes
|
||||
const EXTENDED_INODE = 0x0008;
|
||||
/// File system can resize itself for larger partitions
|
||||
const SELF_RESIZE = 0x0010;
|
||||
/// Directories use hash index
|
||||
const HASH_INDEX = 0x0020;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Required features. If these are not supported; can't mount
|
||||
pub struct FeaturesRequired: u32 {
|
||||
/// Compression is used
|
||||
const REQ_COMPRESSION = 0x0001;
|
||||
/// Directory entries contain a type field
|
||||
const REQ_DIRECTORY_TYPE = 0x0002;
|
||||
/// File system needs to replay its journal
|
||||
const REQ_REPLAY_JOURNAL = 0x0004;
|
||||
/// File system uses a journal device
|
||||
const REQ_JOURNAL_DEVICE = 0x0008;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// ROnly features. If these are not supported; remount as read-only
|
||||
pub struct FeaturesROnly: u32 {
|
||||
/// Sparse superblocks and group descriptor tables
|
||||
const RONLY_SPARSE = 0x0001;
|
||||
/// File system uses a 64-bit file size
|
||||
const RONLY_FILE_SIZE_64 = 0x0002;
|
||||
/// Directory contents are stored in the form of a Binary Tree
|
||||
const RONLY_BTREE_DIRECTORY = 0x0004;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use sector::Size512;
|
||||
|
||||
#[test]
|
||||
fn find() {
|
||||
let mut volume = vec![0_u8; 4096];
|
||||
// magic
|
||||
volume[1024 + 56] = EXT2_MAGIC as u8;
|
||||
volume[1024 + 57] = (EXT2_MAGIC >> 8) as u8;
|
||||
let superblock = unsafe { Superblock::find::<Size512, _>(&volume) };
|
||||
assert!(
|
||||
superblock.is_ok(),
|
||||
"Err({:?})",
|
||||
superblock.err().unwrap_or_else(|| unreachable!()),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn superblock() {
|
||||
use std::cell::RefCell;
|
||||
use std::fs::File;
|
||||
|
||||
let file = RefCell::new(File::open("ext2.img").unwrap());
|
||||
let superblock = unsafe { Superblock::find::<Size512, _>(&file) };
|
||||
assert!(
|
||||
superblock.is_ok(),
|
||||
"Err({:?})",
|
||||
superblock.err().unwrap_or_else(|| unreachable!()),
|
||||
);
|
||||
}
|
||||
}
|
371
ext2-rs/src/volume/mod.rs
Normal file
371
ext2-rs/src/volume/mod.rs
Normal file
|
@ -0,0 +1,371 @@
|
|||
#![allow(missing_docs)]
|
||||
use {
|
||||
alloc::{
|
||||
borrow::{Cow, ToOwned},
|
||||
boxed::Box,
|
||||
vec::Vec,
|
||||
},
|
||||
core::{
|
||||
mem,
|
||||
ops::{Deref, DerefMut, Range},
|
||||
slice,
|
||||
},
|
||||
error::Error,
|
||||
sector::{Address, SectorSize},
|
||||
};
|
||||
|
||||
pub mod size;
|
||||
use self::size::Size;
|
||||
|
||||
pub trait Volume<T: Clone, S: SectorSize> {
|
||||
type Error: Into<Error>;
|
||||
|
||||
fn size(&self) -> Size<S>;
|
||||
fn commit(
|
||||
&mut self,
|
||||
slice: Option<VolumeCommit<T, S>>,
|
||||
) -> Result<(), Self::Error>;
|
||||
unsafe fn slice_unchecked<'a>(
|
||||
&'a self,
|
||||
range: Range<Address<S>>,
|
||||
) -> VolumeSlice<'a, T, S>;
|
||||
|
||||
fn slice<'a>(
|
||||
&'a self,
|
||||
range: Range<Address<S>>,
|
||||
) -> Result<VolumeSlice<'a, T, S>, Self::Error>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||
pub struct VolumeSlice<'a, T: 'a + Clone, S: SectorSize> {
|
||||
inner: Cow<'a, [T]>,
|
||||
index: Address<S>,
|
||||
}
|
||||
|
||||
impl<T: Clone, S: SectorSize> VolumeSlice<'static, T, S> {
|
||||
pub fn with_static(inner: &'static [T]) -> VolumeSlice<'static, T, S> {
|
||||
VolumeSlice {
|
||||
inner: Cow::Borrowed(inner),
|
||||
index: Address::new(0, 0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_owned(
|
||||
inner: <[T] as ToOwned>::Owned,
|
||||
index: Address<S>,
|
||||
) -> VolumeSlice<'static, T, S> {
|
||||
VolumeSlice {
|
||||
inner: Cow::Owned(inner),
|
||||
index,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Clone, S: SectorSize> VolumeSlice<'a, T, S> {
|
||||
pub fn new(inner: &'a [T], index: Address<S>) -> VolumeSlice<'a, T, S> {
|
||||
VolumeSlice {
|
||||
inner: Cow::Borrowed(inner),
|
||||
index,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_mutated(&self) -> bool {
|
||||
match self.inner {
|
||||
Cow::Borrowed(_) => false,
|
||||
Cow::Owned(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn address(&self) -> Address<S> {
|
||||
self.index
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S: SectorSize> VolumeSlice<'a, u8, S> {
|
||||
pub unsafe fn dynamic_cast<T: Copy>(&self) -> (T, Address<S>) {
|
||||
assert!(self.inner.len() >= mem::size_of::<T>());
|
||||
let index = self.index;
|
||||
let cast = self.inner.as_ptr().cast::<T>().read_unaligned();
|
||||
|
||||
// mem::transmute_copy(self.inner.as_ptr().as_ref().unwrap());
|
||||
(cast, index)
|
||||
}
|
||||
|
||||
pub fn from_cast<T: Copy>(
|
||||
cast: &'a T,
|
||||
index: Address<S>,
|
||||
) -> VolumeSlice<'a, u8, S> {
|
||||
let len = mem::size_of::<T>();
|
||||
let ptr = cast as *const T as *const u8;
|
||||
let slice = unsafe { slice::from_raw_parts(ptr, len) };
|
||||
VolumeSlice::new(slice, index)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Clone, S: SectorSize> VolumeSlice<'a, T, S> {
|
||||
pub fn commit(self) -> Option<VolumeCommit<T, S>> {
|
||||
if self.is_mutated() {
|
||||
Some(VolumeCommit::new(self.inner.into_owned(), self.index))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Clone, S: SectorSize> AsRef<[T]> for VolumeSlice<'a, T, S> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.inner.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Clone, S: SectorSize> AsMut<[T]> for VolumeSlice<'a, T, S> {
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.inner.to_mut().as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Clone, S: SectorSize> Deref for VolumeSlice<'a, T, S> {
|
||||
type Target = [T];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Clone, S: SectorSize> DerefMut for VolumeSlice<'a, T, S> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VolumeCommit<T, S: SectorSize> {
|
||||
inner: Vec<T>,
|
||||
index: Address<S>,
|
||||
}
|
||||
|
||||
impl<T: Clone, S: SectorSize> VolumeCommit<T, S> {
|
||||
pub fn with_vec(inner: Vec<T>) -> VolumeCommit<T, S> {
|
||||
VolumeCommit {
|
||||
inner,
|
||||
index: Address::new(0, 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, S: SectorSize> VolumeCommit<T, S> {
|
||||
pub fn new(inner: Vec<T>, index: Address<S>) -> VolumeCommit<T, S> {
|
||||
VolumeCommit { inner, index }
|
||||
}
|
||||
|
||||
pub fn into_inner(self) -> Vec<T> {
|
||||
self.inner
|
||||
}
|
||||
|
||||
pub fn address(&self) -> Address<S> {
|
||||
self.index
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, S: SectorSize> AsRef<[T]> for VolumeCommit<T, S> {
|
||||
fn as_ref(&self) -> &[T] {
|
||||
self.inner.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, S: SectorSize> AsMut<[T]> for VolumeCommit<T, S> {
|
||||
fn as_mut(&mut self) -> &mut [T] {
|
||||
self.inner.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, S: SectorSize> Deref for VolumeCommit<T, S> {
|
||||
type Target = [T];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone, S: SectorSize> DerefMut for VolumeCommit<T, S> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_slice {
|
||||
(@inner $volume:ty $( , $lt:lifetime )* ) => {
|
||||
impl<$( $lt, )* T: Clone, S: SectorSize> Volume<T, S>
|
||||
for $volume
|
||||
{
|
||||
type Error = Error;
|
||||
|
||||
fn size(&self) -> Size<S> {
|
||||
Size::Bounded(
|
||||
Address::from(<Self as AsRef<[T]>>::as_ref(self).len())
|
||||
)
|
||||
}
|
||||
|
||||
fn commit(
|
||||
&mut self,
|
||||
slice: Option<VolumeCommit<T, S>>,
|
||||
) -> Result<(), Self::Error> {
|
||||
slice.map(|slice| {
|
||||
let index = slice.address().into_index() as usize;
|
||||
let end = index + slice.as_ref().len();
|
||||
// XXX: it would be much better to drop the contents of dst
|
||||
// and move the contents of slice instead of cloning
|
||||
let dst =
|
||||
&mut <Self as AsMut<[T]>>::as_mut(self)[index..end];
|
||||
dst.clone_from_slice(slice.as_ref());
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn slice_unchecked<'a>(
|
||||
&'a self,
|
||||
range: Range<Address<S>>,
|
||||
) -> VolumeSlice<'a, T, S> {
|
||||
let index = range.start;
|
||||
let range = range.start.into_index() as usize
|
||||
..range.end.into_index() as usize;
|
||||
VolumeSlice::new(
|
||||
<Self as AsRef<[T]>>::as_ref(self).get_unchecked(range),
|
||||
index,
|
||||
)
|
||||
}
|
||||
|
||||
fn slice<'a>(
|
||||
&'a self,
|
||||
range: Range<Address<S>>,
|
||||
) -> Result<VolumeSlice<'a, T, S>, Self::Error> {
|
||||
if self.size() >= range.end {
|
||||
unsafe { Ok(self.slice_unchecked(range)) }
|
||||
} else {
|
||||
Err(Error::AddressOutOfBounds {
|
||||
sector: range.end.sector(),
|
||||
offset: range.end.offset(),
|
||||
size: range.end.sector_size()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
($volume:ty) => {
|
||||
impl_slice!(@inner $volume);
|
||||
};
|
||||
($volume:ty $( , $lt:lifetime )* ) => {
|
||||
impl_slice!(@inner $volume $( , $lt )* );
|
||||
};
|
||||
}
|
||||
|
||||
impl_slice!(&'b mut [T], 'b);
|
||||
impl_slice!(Vec<T>);
|
||||
impl_slice!(Box<[T]>);
|
||||
|
||||
#[cfg(any(test, not(feature = "no_std")))]
|
||||
mod file {
|
||||
use std::cell::RefCell;
|
||||
use std::fs::File;
|
||||
use std::io::{self, Read, Seek, SeekFrom, Write};
|
||||
use std::ops::Range;
|
||||
|
||||
use sector::{Address, SectorSize};
|
||||
|
||||
use super::size::Size;
|
||||
use super::{Volume, VolumeCommit, VolumeSlice};
|
||||
|
||||
impl<S: SectorSize> Volume<u8, S> for RefCell<File> {
|
||||
type Error = io::Error;
|
||||
|
||||
fn size(&self) -> Size<S> {
|
||||
Size::Bounded(
|
||||
self.borrow()
|
||||
.metadata()
|
||||
.map(|data| Address::from(data.len()))
|
||||
.unwrap_or(Address::new(0, 0)),
|
||||
)
|
||||
}
|
||||
|
||||
fn commit(
|
||||
&mut self,
|
||||
slice: Option<VolumeCommit<u8, S>>,
|
||||
) -> Result<(), Self::Error> {
|
||||
slice
|
||||
.map(|slice| {
|
||||
let index = slice.address();
|
||||
let mut refmut = self.borrow_mut();
|
||||
refmut
|
||||
.seek(SeekFrom::Start(index.into_index()))
|
||||
.and_then(|_| refmut.write(slice.as_ref()))
|
||||
.map(|_| ())
|
||||
})
|
||||
.unwrap_or(Ok(()))
|
||||
}
|
||||
|
||||
unsafe fn slice_unchecked<'a>(
|
||||
&'a self,
|
||||
range: Range<Address<S>>,
|
||||
) -> VolumeSlice<'a, u8, S> {
|
||||
let index = range.start;
|
||||
let len = range.end - range.start;
|
||||
let mut vec = Vec::with_capacity(len.into_index() as usize);
|
||||
vec.set_len(len.into_index() as usize);
|
||||
let mut refmut = self.borrow_mut();
|
||||
refmut
|
||||
.seek(SeekFrom::Start(index.into_index()))
|
||||
.and_then(|_| refmut.read_exact(&mut vec[..]))
|
||||
.unwrap_or_else(|err| {
|
||||
panic!("could't read from File Volume: {:?}", err)
|
||||
});
|
||||
VolumeSlice::new_owned(vec, index)
|
||||
}
|
||||
|
||||
fn slice<'a>(
|
||||
&'a self,
|
||||
range: Range<Address<S>>,
|
||||
) -> Result<VolumeSlice<'a, u8, S>, Self::Error> {
|
||||
let index = range.start;
|
||||
let mut vec = Vec::with_capacity(
|
||||
(range.end - range.start).into_index() as usize,
|
||||
);
|
||||
unsafe {
|
||||
vec.set_len((range.end - range.start).into_index() as usize);
|
||||
}
|
||||
let mut refmut = self.borrow_mut();
|
||||
refmut
|
||||
.seek(SeekFrom::Start(index.into_index()))
|
||||
.and_then(|_| refmut.read_exact(&mut vec[..]))
|
||||
.map(move |_| VolumeSlice::new_owned(vec, index))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use sector::{Address, Size512};
|
||||
|
||||
#[test]
|
||||
fn volume() {
|
||||
let mut volume = vec![0; 1024];
|
||||
let commit = {
|
||||
let mut slice = volume
|
||||
.slice(
|
||||
Address::<Size512>::from(256_u64)
|
||||
..Address::<Size512>::from(512_u64),
|
||||
)
|
||||
.unwrap();
|
||||
slice.iter_mut().for_each(|x| *x = 1);
|
||||
slice.commit()
|
||||
};
|
||||
assert!(volume.commit(commit).is_ok());
|
||||
|
||||
for (i, &x) in volume.iter().enumerate() {
|
||||
if i < 256 || i >= 512 {
|
||||
assert_eq!(x, 0);
|
||||
} else {
|
||||
assert_eq!(x, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
115
ext2-rs/src/volume/size.rs
Normal file
115
ext2-rs/src/volume/size.rs
Normal file
|
@ -0,0 +1,115 @@
|
|||
//!
|
||||
|
||||
use {
|
||||
core::{
|
||||
cmp::Ordering,
|
||||
fmt::{self, Display},
|
||||
},
|
||||
sector::{Address, SectorSize},
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Hash)]
|
||||
/// A size
|
||||
pub enum Size<S: SectorSize> {
|
||||
/// An unbounded size
|
||||
Unbounded,
|
||||
/// A bounded size
|
||||
Bounded(Address<S>),
|
||||
}
|
||||
|
||||
impl<S: SectorSize> Size<S> {
|
||||
/// Try to get the length of the sector
|
||||
pub fn try_len(&self) -> Option<Address<S>> {
|
||||
match *self {
|
||||
Size::Unbounded => None,
|
||||
Size::Bounded(n) => Some(n),
|
||||
}
|
||||
}
|
||||
/// Get the length of the sector unsafely
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function is unsafe because it does not check that the size is
|
||||
/// bounded.
|
||||
pub unsafe fn len(&self) -> Address<S> {
|
||||
match *self {
|
||||
Size::Unbounded => panic!(
|
||||
"attempt to convert `Size::Unbounded` to a concrete length"
|
||||
),
|
||||
Size::Bounded(n) => n,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SectorSize> Size<S> {
|
||||
/// Check if the size is unbounded
|
||||
pub fn is_bounded(&self) -> bool {
|
||||
match *self {
|
||||
Size::Unbounded => false,
|
||||
Size::Bounded(_) => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SectorSize> Display for Size<S> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
Size::Unbounded => write!(f, "Unbounded"),
|
||||
Size::Bounded(n) => write!(f, "Bounded({})", n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SectorSize> PartialEq for Size<S> {
|
||||
fn eq(&self, rhs: &Self) -> bool {
|
||||
match (self, rhs) {
|
||||
(&Size::Unbounded, _) => false,
|
||||
(_, &Size::Unbounded) => false,
|
||||
(&Size::Bounded(ref a), &Size::Bounded(ref b)) => a.eq(b),
|
||||
}
|
||||
}
|
||||
|
||||
fn ne(&self, rhs: &Self) -> bool {
|
||||
match (self, rhs) {
|
||||
(&Size::Unbounded, _) => false,
|
||||
(_, &Size::Unbounded) => false,
|
||||
(&Size::Bounded(ref a), &Size::Bounded(ref b)) => a.ne(b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SectorSize> PartialEq<Address<S>> for Size<S> {
|
||||
fn eq(&self, rhs: &Address<S>) -> bool {
|
||||
match *self {
|
||||
Size::Unbounded => false,
|
||||
Size::Bounded(ref n) => n.eq(rhs),
|
||||
}
|
||||
}
|
||||
|
||||
fn ne(&self, rhs: &Address<S>) -> bool {
|
||||
match *self {
|
||||
Size::Unbounded => false,
|
||||
Size::Bounded(ref n) => n.eq(rhs),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SectorSize> PartialOrd for Size<S> {
|
||||
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
|
||||
match (self, rhs) {
|
||||
(&Size::Unbounded, &Size::Unbounded) => None,
|
||||
(&Size::Unbounded, _) => Some(Ordering::Greater),
|
||||
(_, &Size::Unbounded) => Some(Ordering::Less),
|
||||
(&Size::Bounded(ref a), &Size::Bounded(ref b)) => a.partial_cmp(b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: SectorSize> PartialOrd<Address<S>> for Size<S> {
|
||||
fn partial_cmp(&self, rhs: &Address<S>) -> Option<Ordering> {
|
||||
match *self {
|
||||
Size::Unbounded => Some(Ordering::Greater),
|
||||
Size::Bounded(ref n) => n.partial_cmp(rhs),
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue