Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • redox-os/redoxfs
  • jD91mZM2/redoxfs
  • microcolonel/redoxfs
  • rm-dr/redoxfs
  • deepaksirone/redoxfs
  • sevenEng/redoxfs
  • mortona/redoxfs
  • potatogim/redoxfs
  • 4lDO2/redoxfs
  • malandrakisgeo/redoxfs
  • ssd/redoxfs
  • dahc/redoxfs
  • ashton/redoxfs
  • usapmz/redoxfs
  • vadorovsky/redoxfs
  • bjorn3/redoxfs
  • rw_van/redoxfs
  • mkroening/redoxfs
  • emaxx-g/redoxfs
  • CILP/redoxfs
  • AnandSMain/redoxfs
  • aaronjanse/redoxfs
  • liamnprg/redoxfs
  • coolreader18/redoxfs
  • freewilll/redoxfs
  • adi-g15/redoxfs
  • andrey.turkin/redoxfs
  • matlik/redoxfs
28 results
Show changes
use alloc::vec::Vec;
use core::{fmt, mem, ops, slice};
use endian_num::Le;
use crate::{BlockAddr, BlockLevel, BlockPtr, BlockTrait, BLOCK_SIZE};
pub const ALLOC_LIST_ENTRIES: usize =
(BLOCK_SIZE as usize - mem::size_of::<BlockPtr<AllocList>>()) / mem::size_of::<AllocEntry>();
/// The RedoxFS block allocator. This struct manages all "data" blocks in RedoxFS
/// (i.e, all blocks that aren't reserved or part of the header chain).
///
/// [`Allocator`] can allocate blocks of many "levels"---that is, it can
/// allocate multiple consecutive [`BLOCK_SIZE`] blocks in one operation.
///
/// This reduces the amount of memory that the [`Allocator`] uses:
/// Instead of storing the index of each free [`BLOCK_SIZE`] block,
/// the `levels` array can keep track of higher-level blocks, splitting
/// them when a smaller block is requested.
///
/// Higher-level blocks also allow us to more efficiently allocate memory
/// for large files.
#[derive(Clone, Default)]
pub struct Allocator {
/// This array keeps track of all free blocks of each level,
/// and is initialized using the AllocList chain when we open the filesystem.
///
/// Every element of the outer array represents a block level:
/// - item 0: free level 0 blocks (with size [`BLOCK_SIZE`])
/// - item 1: free level 1 blocks (with size 2*[`BLOCK_SIZE`])
/// - item 2: free level 2 blocks (with size 4*[`BLOCK_SIZE`])
/// ...and so on.
///
/// Each inner array contains a list of free block indices,
levels: Vec<Vec<u64>>,
}
impl Allocator {
pub fn levels(&self) -> &Vec<Vec<u64>> {
&self.levels
}
/// Count the number of free [`BLOCK_SIZE`] available to this [`Allocator`].
pub fn free(&self) -> u64 {
let mut free = 0;
for level in 0..self.levels.len() {
let level_size = 1 << level;
free += self.levels[level].len() as u64 * level_size;
}
free
}
/// Find a free block of the given level, mark it as "used", and return its address.
/// Returns [`None`] if there are no free blocks with this level.
pub fn allocate(&mut self, block_level: BlockLevel) -> Option<BlockAddr> {
// First, find the lowest level with a free block
let mut index_opt = None;
let mut level = block_level.0;
// Start searching at the level we want. Smaller levels are too small!
while level < self.levels.len() {
if !self.levels[level].is_empty() {
index_opt = self.levels[level].pop();
break;
}
level += 1;
}
// If a free block was found, split it until we find a usable block of the right level.
// The left side of the split block is kept free, and the right side is allocated.
let index = index_opt?;
while level > block_level.0 {
level -= 1;
let level_size = 1 << level;
self.levels[level].push(index + level_size);
}
Some(unsafe { BlockAddr::new(index, block_level) })
}
/// Try to allocate the exact block specified, making all necessary splits.
/// Returns [`None`] if this some (or all) of this block is already allocated.
///
/// Note that [`BlockAddr`] encodes the blocks location _and_ level.
pub fn allocate_exact(&mut self, exact_addr: BlockAddr) -> Option<BlockAddr> {
// This function only supports level 0 right now
assert_eq!(exact_addr.level().0, 0);
let exact_index = exact_addr.index();
let mut index_opt = None;
// Go from the highest to the lowest level
for level in (0..self.levels.len()).rev() {
let level_size = 1 << level;
// Split higher block if found
if let Some(index) = index_opt.take() {
self.levels[level].push(index);
self.levels[level].push(index + level_size);
}
// Look for matching block and remove it
for i in 0..self.levels[level].len() {
let start = self.levels[level][i];
if start <= exact_index {
let end = start + level_size;
if end > exact_index {
self.levels[level].remove(i);
index_opt = Some(start);
break;
}
}
}
}
Some(unsafe { BlockAddr::new(index_opt?, exact_addr.level()) })
}
/// Deallocate the given block, marking it "free" so that it can be re-used later.
pub fn deallocate(&mut self, addr: BlockAddr) {
// When we deallocate, we check if block we're deallocating has a free sibling.
// If it does, we join the two to create one free block in the next (higher) level.
//
// We repeat this until we no longer have a sibling to join.
let mut index = addr.index();
let mut level = addr.level().0;
loop {
while level >= self.levels.len() {
self.levels.push(Vec::new());
}
let level_size = 1 << level;
let next_size = level_size << 1;
let mut found = false;
let mut i = 0;
// look at all free blocks in the current level...
while i < self.levels[level].len() {
// index of the second block we're looking at
let level_index = self.levels[level][i];
// - the block we just freed aligns with the next largest block, and
// - the second block we're looking at is the right sibling of this block
if index % next_size == 0 && index + level_size == level_index {
// "alloc" the next highest block, repeat deallocation process.
self.levels[level].remove(i);
found = true;
break;
// - the index of this block doesn't align with the next largest block, and
// - the block we're looking at is the left neighbor of this block
} else if level_index % next_size == 0 && level_index + level_size == index {
// "alloc" the next highest block, repeat deallocation process.
self.levels[level].remove(i);
index = level_index; // index moves to left block
found = true;
break;
}
i += 1;
}
// We couldn't find a higher block,
// deallocate this one and finish
if !found {
self.levels[level].push(index);
return;
}
// repeat deallocation process on the
// higher-level block we just created.
level += 1;
}
}
}
#[repr(C, packed)]
pub struct AllocEntry {
/// The index of the first block this [`AllocEntry`] refers to
index: Le<u64>,
/// The number of blocks after (and including) `index` that are are free or used.
/// If negative, they are used; if positive, they are free.
count: Le<i64>,
}
impl AllocEntry {
pub fn new(index: u64, count: i64) -> Self {
Self {
index: index.into(),
count: count.into(),
}
}
pub fn allocate(addr: BlockAddr) -> Self {
Self::new(addr.index(), -addr.level().blocks())
}
pub fn deallocate(addr: BlockAddr) -> Self {
Self::new(addr.index(), addr.level().blocks())
}
pub fn index(&self) -> u64 {
self.index.to_ne()
}
pub fn count(&self) -> i64 {
self.count.to_ne()
}
pub fn is_null(&self) -> bool {
self.count() == 0
}
}
impl Clone for AllocEntry {
fn clone(&self) -> Self {
*self
}
}
impl Copy for AllocEntry {}
impl Default for AllocEntry {
fn default() -> Self {
Self {
index: 0.into(),
count: 0.into(),
}
}
}
impl fmt::Debug for AllocEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let index = self.index();
let count = self.count();
f.debug_struct("AllocEntry")
.field("index", &index)
.field("count", &count)
.finish()
}
}
/// A node in the allocation chain.
#[repr(C, packed)]
pub struct AllocList {
/// A pointer to the previous AllocList.
/// If this is the null pointer, this is the first element of the chain.
pub prev: BlockPtr<AllocList>,
/// Allocation entries.
pub entries: [AllocEntry; ALLOC_LIST_ENTRIES],
}
unsafe impl BlockTrait for AllocList {
fn empty(level: BlockLevel) -> Option<Self> {
if level.0 == 0 {
Some(Self {
prev: BlockPtr::default(),
entries: [AllocEntry::default(); ALLOC_LIST_ENTRIES],
})
} else {
None
}
}
}
impl fmt::Debug for AllocList {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let prev = self.prev;
let entries: Vec<&AllocEntry> = self
.entries
.iter()
.filter(|entry| entry.count() > 0)
.collect();
f.debug_struct("AllocList")
.field("prev", &prev)
.field("entries", &entries)
.finish()
}
}
impl ops::Deref for AllocList {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const AllocList as *const u8,
mem::size_of::<AllocList>(),
) as &[u8]
}
}
}
impl ops::DerefMut for AllocList {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut AllocList as *mut u8,
mem::size_of::<AllocList>(),
) as &mut [u8]
}
}
}
#[test]
fn alloc_node_size_test() {
assert_eq!(mem::size_of::<AllocList>(), crate::BLOCK_SIZE as usize);
}
#[test]
fn allocator_test() {
let mut alloc = Allocator::default();
assert_eq!(alloc.allocate(BlockLevel::default()), None);
alloc.deallocate(unsafe { BlockAddr::new(1, BlockLevel::default()) });
assert_eq!(
alloc.allocate(BlockLevel::default()),
Some(unsafe { BlockAddr::new(1, BlockLevel::default()) })
);
assert_eq!(alloc.allocate(BlockLevel::default()), None);
for addr in 1023..2048 {
alloc.deallocate(unsafe { BlockAddr::new(addr, BlockLevel::default()) });
}
assert_eq!(alloc.levels.len(), 11);
for level in 0..alloc.levels.len() {
if level == 0 {
assert_eq!(alloc.levels[level], [1023]);
} else if level == 10 {
assert_eq!(alloc.levels[level], [1024]);
} else {
assert_eq!(alloc.levels[level], []);
}
}
for addr in 1023..2048 {
assert_eq!(
alloc.allocate(BlockLevel::default()),
Some(unsafe { BlockAddr::new(addr, BlockLevel::default()) })
);
}
assert_eq!(alloc.allocate(BlockLevel::default()), None);
assert_eq!(alloc.levels.len(), 11);
for level in 0..alloc.levels.len() {
assert_eq!(alloc.levels[level], []);
}
}
use std::fs;
use std::io;
use std::os::unix::ffi::OsStrExt;
use std::os::unix::fs::MetadataExt;
use std::path::Path;
use crate::{Disk, FileSystem, Node, Transaction, TreePtr, BLOCK_SIZE};
fn syscall_err(err: syscall::Error) -> io::Error {
io::Error::from_raw_os_error(err.errno)
}
pub fn archive_at<D: Disk, P: AsRef<Path>>(
tx: &mut Transaction<D>,
parent_path: P,
parent_ptr: TreePtr<Node>,
) -> io::Result<()> {
for entry_res in fs::read_dir(parent_path)? {
let entry = entry_res?;
let metadata = entry.metadata()?;
let file_type = metadata.file_type();
let name = entry.file_name().into_string().map_err(|_| {
io::Error::new(io::ErrorKind::InvalidData, "filename is not valid UTF-8")
})?;
let mode_type = if file_type.is_dir() {
Node::MODE_DIR
} else if file_type.is_file() {
Node::MODE_FILE
} else if file_type.is_symlink() {
Node::MODE_SYMLINK
} else {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("Does not support parsing {:?}", file_type),
));
};
let node_ptr;
{
let mode = mode_type | (metadata.mode() as u16 & Node::MODE_PERM);
let mut node = tx
.create_node(
parent_ptr,
&name,
mode,
metadata.ctime() as u64,
metadata.ctime_nsec() as u32,
)
.map_err(syscall_err)?;
node_ptr = node.ptr();
if node.data().uid() != metadata.uid() || node.data().gid() != metadata.gid() {
node.data_mut().set_uid(metadata.uid());
node.data_mut().set_gid(metadata.gid());
tx.sync_tree(node).map_err(syscall_err)?;
}
}
let path = entry.path();
if file_type.is_dir() {
archive_at(tx, path, node_ptr)?;
} else if file_type.is_file() {
let data = fs::read(path)?;
let count = tx
.write_node(
node_ptr,
0,
&data,
metadata.mtime() as u64,
metadata.mtime_nsec() as u32,
)
.map_err(syscall_err)?;
if count != data.len() {
panic!("file write count {} != {}", count, data.len());
}
} else if file_type.is_symlink() {
let destination = fs::read_link(path)?;
let data = destination.as_os_str().as_bytes();
let count = tx
.write_node(
node_ptr,
0,
data,
metadata.mtime() as u64,
metadata.mtime_nsec() as u32,
)
.map_err(syscall_err)?;
if count != data.len() {
panic!("symlink write count {} != {}", count, data.len());
}
} else {
return Err(io::Error::new(
io::ErrorKind::Other,
format!("Does not support creating {:?}", file_type),
));
}
}
Ok(())
}
pub fn archive<D: Disk, P: AsRef<Path>>(fs: &mut FileSystem<D>, parent_path: P) -> io::Result<u64> {
let end_block = fs
.tx(|tx| {
// Archive_at root node
archive_at(tx, parent_path, TreePtr::root())
.map_err(|err| syscall::Error::new(err.raw_os_error().unwrap()))?;
// Squash alloc log
tx.sync(true)?;
let end_block = tx.header.size() / BLOCK_SIZE;
/* TODO: Cut off any free blocks at the end of the filesystem
let mut end_changed = true;
while end_changed {
end_changed = false;
let allocator = fs.allocator();
let levels = allocator.levels();
for level in 0..levels.len() {
let level_size = 1 << level;
for &block in levels[level].iter() {
if block < end_block && block + level_size >= end_block {
end_block = block;
end_changed = true;
}
}
}
}
*/
// Update header
tx.header.size = (end_block * BLOCK_SIZE).into();
tx.header_changed = true;
tx.sync(false)?;
Ok(end_block)
})
.map_err(syscall_err)?;
Ok((fs.block + end_block) * BLOCK_SIZE)
}
extern crate redoxfs;
extern crate syscall;
extern crate uuid;
use std::io::Read;
use std::time::{SystemTime, UNIX_EPOCH};
use std::{env, fs, process};
use redoxfs::{archive, DiskFile, FileSystem};
use uuid::Uuid;
fn main() {
env_logger::init();
let mut args = env::args().skip(1);
let disk_path = if let Some(path) = args.next() {
path
} else {
println!("redoxfs-ar: no disk image provided");
println!("redoxfs-ar DISK FOLDER [BOOTLOADER]");
process::exit(1);
};
let folder_path = if let Some(path) = args.next() {
path
} else {
println!("redoxfs-ar: no folder provided");
println!("redoxfs-ar DISK FOLDER [BOOTLOADER]");
process::exit(1);
};
let bootloader_path_opt = args.next();
let disk = match DiskFile::open(&disk_path) {
Ok(disk) => disk,
Err(err) => {
println!("redoxfs-ar: failed to open image {}: {}", disk_path, err);
process::exit(1);
}
};
let mut bootloader = vec![];
if let Some(bootloader_path) = bootloader_path_opt {
match fs::File::open(&bootloader_path) {
Ok(mut file) => match file.read_to_end(&mut bootloader) {
Ok(_) => (),
Err(err) => {
println!(
"redoxfs-ar: failed to read bootloader {}: {}",
bootloader_path, err
);
process::exit(1);
}
},
Err(err) => {
println!(
"redoxfs-ar: failed to open bootloader {}: {}",
bootloader_path, err
);
process::exit(1);
}
}
};
let ctime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
match FileSystem::create_reserved(
disk,
None,
&bootloader,
ctime.as_secs(),
ctime.subsec_nanos(),
) {
Ok(mut fs) => {
let size = match archive(&mut fs, &folder_path) {
Ok(ok) => ok,
Err(err) => {
println!("redoxfs-ar: failed to archive {}: {}", folder_path, err);
process::exit(1);
}
};
if let Err(err) = fs.disk.file.set_len(size) {
println!(
"redoxfs-ar: failed to truncate {} to {}: {}",
disk_path, size, err
);
process::exit(1);
}
let uuid = Uuid::from_bytes(fs.header.uuid());
println!(
"redoxfs-ar: created filesystem on {}, reserved {} blocks, size {} MB, uuid {}",
disk_path,
fs.block,
fs.header.size() / 1000 / 1000,
uuid.hyphenated()
);
}
Err(err) => {
println!(
"redoxfs-ar: failed to create filesystem on {}: {}",
disk_path, err
);
process::exit(1);
}
};
}
extern crate redoxfs;
extern crate uuid;
use std::io::Read;
use std::{env, fs, io, process, time};
use redoxfs::{DiskFile, FileSystem};
use termion::input::TermRead;
use uuid::Uuid;
fn usage() -> ! {
eprintln!("redoxfs-mkfs [--encrypt] DISK [BOOTLOADER]");
process::exit(1);
}
fn main() {
env_logger::init();
let mut encrypt = false;
let mut disk_path_opt = None;
let mut bootloader_path_opt = None;
for arg in env::args().skip(1) {
if arg == "--encrypt" {
encrypt = true;
} else if disk_path_opt.is_none() {
disk_path_opt = Some(arg);
} else if bootloader_path_opt.is_none() {
bootloader_path_opt = Some(arg);
} else {
eprintln!("redoxfs-mkfs: too many arguments provided");
usage();
}
}
let disk_path = if let Some(path) = disk_path_opt {
path
} else {
eprintln!("redoxfs-mkfs: no disk image provided");
usage();
};
let disk = match DiskFile::open(&disk_path) {
Ok(disk) => disk,
Err(err) => {
eprintln!("redoxfs-mkfs: failed to open image {}: {}", disk_path, err);
process::exit(1);
}
};
let mut bootloader = vec![];
if let Some(bootloader_path) = bootloader_path_opt {
match fs::File::open(&bootloader_path) {
Ok(mut file) => match file.read_to_end(&mut bootloader) {
Ok(_) => (),
Err(err) => {
eprintln!(
"redoxfs-mkfs: failed to read bootloader {}: {}",
bootloader_path, err
);
process::exit(1);
}
},
Err(err) => {
eprintln!(
"redoxfs-mkfs: failed to open bootloader {}: {}",
bootloader_path, err
);
process::exit(1);
}
}
};
let password_opt = if encrypt {
eprint!("redoxfs-mkfs: password: ");
let password = io::stdin()
.read_passwd(&mut io::stderr())
.unwrap()
.unwrap_or_default();
eprintln!();
if password.is_empty() {
eprintln!("redoxfs-mkfs: empty password, giving up");
process::exit(1);
}
Some(password)
} else {
None
};
let ctime = time::SystemTime::now()
.duration_since(time::UNIX_EPOCH)
.unwrap();
match FileSystem::create_reserved(
disk,
password_opt.as_ref().map(|x| x.as_bytes()),
&bootloader,
ctime.as_secs(),
ctime.subsec_nanos(),
) {
Ok(filesystem) => {
let uuid = Uuid::from_bytes(filesystem.header.uuid());
eprintln!(
"redoxfs-mkfs: created filesystem on {}, reserved {} blocks, size {} MB, uuid {}",
disk_path,
filesystem.block,
filesystem.header.size() / 1000 / 1000,
uuid.hyphenated()
);
}
Err(err) => {
eprintln!(
"redoxfs-mkfs: failed to create filesystem on {}: {}",
disk_path, err
);
process::exit(1);
}
}
}
extern crate libc;
extern crate redoxfs;
#[cfg(target_os = "redox")]
extern crate syscall;
extern crate uuid;
use std::env;
use std::fs::File;
use std::io::{self, Read, Write};
use std::os::unix::io::{FromRawFd, RawFd};
use std::process;
#[cfg(target_os = "redox")]
use std::{mem::MaybeUninit, ptr::addr_of_mut, sync::atomic::Ordering};
use redoxfs::{mount, DiskCache, DiskFile, FileSystem};
use termion::input::TermRead;
use uuid::Uuid;
#[cfg(target_os = "redox")]
extern "C" fn unmount_handler(_s: usize) {
redoxfs::IS_UMT.store(1, Ordering::SeqCst);
}
#[cfg(target_os = "redox")]
//set up a signal handler on redox, this implements unmounting. I have no idea what sa_flags is
//for, so I put 2. I don't think 0,0 is a valid sa_mask. I don't know what i'm doing here. When u
//send it a sigkill, it shuts off the filesystem
fn setsig() {
// TODO: High-level wrapper like the nix crate?
unsafe {
let mut action = MaybeUninit::<libc::sigaction>::uninit();
assert_eq!(
libc::sigemptyset(addr_of_mut!((*action.as_mut_ptr()).sa_mask)),
0
);
addr_of_mut!((*action.as_mut_ptr()).sa_flags).write(0);
addr_of_mut!((*action.as_mut_ptr()).sa_sigaction).write(unmount_handler as usize);
assert_eq!(
libc::sigaction(libc::SIGTERM, action.as_ptr(), core::ptr::null_mut()),
0
);
}
}
#[cfg(not(target_os = "redox"))]
// on linux, this is implemented properly, so no need for this unscrupulous nonsense!
fn setsig() {}
fn fork() -> isize {
unsafe { libc::fork() as isize }
}
fn pipe(pipes: &mut [i32; 2]) -> isize {
unsafe { libc::pipe(pipes.as_mut_ptr()) as isize }
}
#[cfg(not(target_os = "redox"))]
fn capability_mode() {}
#[cfg(not(target_os = "redox"))]
fn bootloader_password() -> Option<Vec<u8>> {
None
}
#[cfg(target_os = "redox")]
fn capability_mode() {
libredox::call::setrens(0, 0).expect("redoxfs: failed to enter null namespace");
}
#[cfg(target_os = "redox")]
fn bootloader_password() -> Option<Vec<u8>> {
use libredox::call::MmapArgs;
let addr_env = env::var_os("REDOXFS_PASSWORD_ADDR")?;
let size_env = env::var_os("REDOXFS_PASSWORD_SIZE")?;
let addr = usize::from_str_radix(
addr_env.to_str().expect("REDOXFS_PASSWORD_ADDR not valid"),
16,
)
.expect("failed to parse REDOXFS_PASSWORD_ADDR");
let size = usize::from_str_radix(
size_env.to_str().expect("REDOXFS_PASSWORD_SIZE not valid"),
16,
)
.expect("failed to parse REDOXFS_PASSWORD_SIZE");
let mut password = Vec::with_capacity(size);
unsafe {
let aligned_size = size.next_multiple_of(syscall::PAGE_SIZE);
let fd = libredox::Fd::open("memory:physical", libredox::flag::O_CLOEXEC, 0)
.expect("failed to open physical memory file");
let password_map = libredox::call::mmap(MmapArgs {
addr: core::ptr::null_mut(),
length: aligned_size,
prot: libredox::flag::PROT_READ,
flags: libredox::flag::MAP_SHARED,
fd: fd.raw(),
offset: addr as u64,
})
.expect("failed to map REDOXFS_PASSWORD")
.cast::<u8>();
for i in 0..size {
password.push(password_map.add(i).read());
}
let _ = libredox::call::munmap(password_map.cast(), aligned_size);
}
Some(password)
}
fn print_err_exit(err: impl AsRef<str>) -> ! {
eprintln!("{}", err.as_ref());
usage();
process::exit(1)
}
fn print_usage_exit() -> ! {
usage();
process::exit(1)
}
fn usage() {
println!("redoxfs --no-daemon [-d] [--uuid] [disk or uuid] [mountpoint] [block in hex]");
}
enum DiskId {
Path(String),
Uuid(Uuid),
}
fn filesystem_by_path(
path: &str,
block_opt: Option<u64>,
) -> Option<(String, FileSystem<DiskCache<DiskFile>>)> {
println!("redoxfs: opening {}", path);
let attempts = 10;
for attempt in 0..=attempts {
let password_opt = if attempt > 0 {
eprint!("redoxfs: password: ");
let password = io::stdin()
.read_passwd(&mut io::stderr())
.unwrap()
.unwrap_or_default();
eprintln!();
if password.is_empty() {
eprintln!("redoxfs: empty password, giving up");
// Password is empty, exit loop
break;
}
Some(password.into_bytes())
} else {
bootloader_password()
};
match DiskFile::open(path).map(DiskCache::new) {
Ok(disk) => {
match redoxfs::FileSystem::open(disk, password_opt.as_deref(), block_opt, true) {
Ok(filesystem) => {
println!(
"redoxfs: opened filesystem on {} with uuid {}",
path,
Uuid::from_bytes(filesystem.header.uuid()).hyphenated()
);
return Some((path.to_string(), filesystem));
}
Err(err) => match err.errno {
syscall::ENOKEY => {
if password_opt.is_some() {
println!("redoxfs: incorrect password ({}/{})", attempt, attempts);
}
}
_ => {
println!("redoxfs: failed to open filesystem {}: {}", path, err);
break;
}
},
}
}
Err(err) => {
println!("redoxfs: failed to open image {}: {}", path, err);
break;
}
}
}
None
}
#[cfg(not(target_os = "redox"))]
fn filesystem_by_uuid(
_uuid: &Uuid,
_block_opt: Option<u64>,
) -> Option<(String, FileSystem<DiskCache<DiskFile>>)> {
None
}
#[cfg(target_os = "redox")]
fn filesystem_by_uuid(
uuid: &Uuid,
block_opt: Option<u64>,
) -> Option<(String, FileSystem<DiskCache<DiskFile>>)> {
use std::fs;
use redox_path::RedoxPath;
match fs::read_dir("/scheme") {
Ok(entries) => {
for entry_res in entries {
if let Ok(entry) = entry_res {
if let Some(disk) = entry.path().to_str() {
if RedoxPath::from_absolute(disk)
.unwrap_or(RedoxPath::from_absolute("/")?)
.is_scheme_category("disk")
{
println!("redoxfs: found scheme {}", disk);
match fs::read_dir(disk) {
Ok(entries) => {
for entry_res in entries {
if let Ok(entry) = entry_res {
if let Ok(path) =
entry.path().into_os_string().into_string()
{
println!("redoxfs: found path {}", path);
if let Some((path, filesystem)) =
filesystem_by_path(&path, block_opt)
{
if &filesystem.header.uuid() == uuid.as_bytes()
{
println!(
"redoxfs: filesystem on {} matches uuid {}",
path,
uuid.hyphenated()
);
return Some((path, filesystem));
} else {
println!(
"redoxfs: filesystem on {} does not match uuid {}",
path,
uuid.hyphenated()
);
}
}
}
}
}
}
Err(err) => {
println!("redoxfs: failed to list '{}': {}", disk, err);
}
}
}
}
}
}
}
Err(err) => {
println!("redoxfs: failed to list schemes: {}", err);
}
}
None
}
fn daemon(
disk_id: &DiskId,
mountpoint: &str,
block_opt: Option<u64>,
mut write: Option<File>,
) -> ! {
setsig();
let filesystem_opt = match *disk_id {
DiskId::Path(ref path) => filesystem_by_path(path, block_opt),
DiskId::Uuid(ref uuid) => filesystem_by_uuid(uuid, block_opt),
};
if let Some((path, filesystem)) = filesystem_opt {
match mount(filesystem, mountpoint, |mounted_path| {
capability_mode();
println!(
"redoxfs: mounted filesystem on {} to {}",
path,
mounted_path.display()
);
if let Some(ref mut write) = write {
let _ = write.write(&[0]);
}
}) {
Ok(()) => {
process::exit(0);
}
Err(err) => {
println!(
"redoxfs: failed to mount {} to {}: {}",
path, mountpoint, err
);
}
}
}
match *disk_id {
DiskId::Path(ref path) => {
println!("redoxfs: not able to mount path {}", path);
}
DiskId::Uuid(ref uuid) => {
println!("redoxfs: not able to mount uuid {}", uuid.hyphenated());
}
}
if let Some(ref mut write) = write {
let _ = write.write(&[1]);
}
process::exit(1);
}
fn main() {
env_logger::init();
let mut args = env::args().skip(1);
let mut daemonise = true;
let mut disk_id: Option<DiskId> = None;
let mut mountpoint: Option<String> = None;
let mut block_opt: Option<u64> = None;
while let Some(arg) = args.next() {
match arg.as_str() {
"--no-daemon" | "-d" => daemonise = false,
"--uuid" if disk_id.is_none() => {
disk_id = Some(DiskId::Uuid(
match args.next().as_deref().map(Uuid::parse_str) {
Some(Ok(uuid)) => uuid,
Some(Err(err)) => {
print_err_exit(format!("redoxfs: invalid uuid '{}': {}", arg, err))
}
None => print_err_exit("redoxfs: no uuid provided"),
},
));
}
disk if disk_id.is_none() => disk_id = Some(DiskId::Path(disk.to_owned())),
mnt if disk_id.is_some() && mountpoint.is_none() => mountpoint = Some(mnt.to_owned()),
opts if mountpoint.is_some() => match u64::from_str_radix(opts, 16) {
Ok(block) => block_opt = Some(block),
Err(err) => print_err_exit(format!("redoxfs: invalid block '{}': {}", opts, err)),
},
_ => print_usage_exit(),
}
}
let Some(disk_id) = disk_id else {
print_err_exit("redoxfs: no disk provided");
};
let Some(mountpoint) = mountpoint else {
print_err_exit("redoxfs: no mountpoint provided");
};
if daemonise {
let mut pipes = [0; 2];
if pipe(&mut pipes) == 0 {
let mut read = unsafe { File::from_raw_fd(pipes[0] as RawFd) };
let write = unsafe { File::from_raw_fd(pipes[1] as RawFd) };
let pid = fork();
if pid == 0 {
drop(read);
daemon(&disk_id, &mountpoint, block_opt, Some(write));
} else if pid > 0 {
drop(write);
let mut res = [0];
read.read_exact(&mut res).unwrap();
process::exit(res[0] as i32);
} else {
panic!("redoxfs: failed to fork");
}
} else {
panic!("redoxfs: failed to create pipe");
}
} else {
println!("redoxfs: running in foreground");
daemon(&disk_id, &mountpoint, block_opt, None);
}
}
use core::{fmt, marker::PhantomData, mem, ops, slice};
use endian_num::Le;
use crate::BLOCK_SIZE;
const BLOCK_LIST_ENTRIES: usize = BLOCK_SIZE as usize / mem::size_of::<BlockPtr<BlockRaw>>();
/// An address of a data block.
///
/// This encodes a block's position _and_ [`BlockLevel`]:
/// the first four bits of this `u64` encode the block's level,
/// the rest encode its index.
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct BlockAddr(u64);
impl BlockAddr {
// Unsafe because this can create invalid blocks
pub(crate) unsafe fn new(index: u64, level: BlockLevel) -> Self {
// Level must only use the lowest four bits
if level.0 > 0xF {
panic!("block level used more than four bits");
}
// Index must not use the highest four bits
let inner = index
.checked_shl(4)
.expect("block index used highest four bits")
| (level.0 as u64);
Self(inner)
}
pub fn null(level: BlockLevel) -> Self {
unsafe { Self::new(0, level) }
}
pub fn index(&self) -> u64 {
// The first four bits store the level
self.0 >> 4
}
pub fn level(&self) -> BlockLevel {
// The first four bits store the level
BlockLevel((self.0 & 0xF) as usize)
}
pub fn is_null(&self) -> bool {
self.index() == 0
}
}
/// The size of a block.
///
/// Level 0 blocks are blocks of [`BLOCK_SIZE`] bytes.
/// A level 1 block consists of two consecutive level 0 blocks.
/// A level n block consists of two consecutive level n-1 blocks.
///
/// See [`crate::Allocator`] docs for more details.
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct BlockLevel(pub(crate) usize);
impl BlockLevel {
/// Returns the smallest block level that can contain
/// the given number of bytes.
pub(crate) fn for_bytes(bytes: u64) -> Self {
if bytes == 0 {
return BlockLevel(0);
}
let level = bytes
.div_ceil(BLOCK_SIZE)
.next_power_of_two()
.trailing_zeros() as usize;
BlockLevel(level)
}
/// The number of [`BLOCK_SIZE`] blocks (i.e, level 0 blocks)
/// in a block of this level
pub fn blocks(self) -> i64 {
1 << self.0
}
/// The number of bytes in a block of this level
pub fn bytes(self) -> u64 {
BLOCK_SIZE << self.0
}
}
pub unsafe trait BlockTrait {
/// Create an empty block of this type.
fn empty(level: BlockLevel) -> Option<Self>
where
Self: Sized;
}
/// A [`BlockAddr`] and the data it points to.
#[derive(Clone, Copy, Debug, Default)]
pub struct BlockData<T> {
addr: BlockAddr,
data: T,
}
impl<T> BlockData<T> {
pub fn new(addr: BlockAddr, data: T) -> Self {
Self { addr, data }
}
pub fn addr(&self) -> BlockAddr {
self.addr
}
pub fn data(&self) -> &T {
&self.data
}
pub fn data_mut(&mut self) -> &mut T {
&mut self.data
}
pub(crate) unsafe fn into_parts(self) -> (BlockAddr, T) {
(self.addr, self.data)
}
/// Set the address of this [`BlockData`] to `addr`, returning this
/// block's old address. This method does not update block data.
///
/// `addr` must point to a block with the same level as this block.
#[must_use = "don't forget to de-allocate old block address"]
pub fn swap_addr(&mut self, addr: BlockAddr) -> BlockAddr {
// Address levels must match
assert_eq!(self.addr.level(), addr.level());
let old = self.addr;
self.addr = addr;
old
}
}
impl<T: BlockTrait> BlockData<T> {
pub fn empty(addr: BlockAddr) -> Option<Self> {
let empty = T::empty(addr.level())?;
Some(Self::new(addr, empty))
}
}
impl<T: ops::Deref<Target = [u8]>> BlockData<T> {
pub fn create_ptr(&self) -> BlockPtr<T> {
BlockPtr {
addr: self.addr.0.into(),
hash: seahash::hash(self.data.deref()).into(),
phantom: PhantomData,
}
}
}
#[repr(C, packed)]
pub struct BlockList<T> {
pub ptrs: [BlockPtr<T>; BLOCK_LIST_ENTRIES],
}
unsafe impl<T> BlockTrait for BlockList<T> {
fn empty(level: BlockLevel) -> Option<Self> {
if level.0 == 0 {
Some(Self {
ptrs: [BlockPtr::default(); BLOCK_LIST_ENTRIES],
})
} else {
None
}
}
}
impl<T> BlockList<T> {
pub fn is_empty(&self) -> bool {
for ptr in self.ptrs.iter() {
if !ptr.is_null() {
return false;
}
}
true
}
}
impl<T> ops::Deref for BlockList<T> {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const BlockList<T> as *const u8,
mem::size_of::<BlockList<T>>(),
) as &[u8]
}
}
}
impl<T> ops::DerefMut for BlockList<T> {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self as *mut BlockList<T> as *mut u8,
mem::size_of::<BlockList<T>>(),
) as &mut [u8]
}
}
}
/// An address of a data block, along with a checksum of its data.
///
/// This encodes a block's position _and_ [`BlockLevel`].
/// the first four bits of `addr` encode the block's level,
/// the rest encode its index.
///
/// Also see [`BlockAddr`].
#[repr(C, packed)]
pub struct BlockPtr<T> {
addr: Le<u64>,
hash: Le<u64>,
phantom: PhantomData<T>,
}
impl<T> BlockPtr<T> {
pub fn null(level: BlockLevel) -> Self {
Self {
addr: BlockAddr::null(level).0.into(),
hash: 0.into(),
phantom: PhantomData,
}
}
pub fn addr(&self) -> BlockAddr {
BlockAddr(self.addr.to_ne())
}
pub fn hash(&self) -> u64 {
self.hash.to_ne()
}
pub fn is_null(&self) -> bool {
self.addr().is_null()
}
/// Cast BlockPtr to another type
///
/// # Safety
/// Unsafe because it can be used to transmute types
pub unsafe fn cast<U>(self) -> BlockPtr<U> {
BlockPtr {
addr: self.addr,
hash: self.hash,
phantom: PhantomData,
}
}
#[must_use = "the returned pointer should usually be deallocated"]
pub fn clear(&mut self) -> BlockPtr<T> {
let mut ptr = Self::default();
mem::swap(self, &mut ptr);
ptr
}
}
impl<T> Clone for BlockPtr<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for BlockPtr<T> {}
impl<T> Default for BlockPtr<T> {
fn default() -> Self {
Self {
addr: 0.into(),
hash: 0.into(),
phantom: PhantomData,
}
}
}
impl<T> fmt::Debug for BlockPtr<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let addr = self.addr();
let hash = self.hash();
f.debug_struct("BlockPtr")
.field("addr", &addr)
.field("hash", &hash)
.finish()
}
}
#[repr(C, packed)]
pub struct BlockRaw([u8; BLOCK_SIZE as usize]);
unsafe impl BlockTrait for BlockRaw {
fn empty(level: BlockLevel) -> Option<Self> {
if level.0 == 0 {
Some(Self([0; BLOCK_SIZE as usize]))
} else {
None
}
}
}
impl Clone for BlockRaw {
fn clone(&self) -> Self {
Self(self.0)
}
}
impl ops::Deref for BlockRaw {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.0
}
}
impl ops::DerefMut for BlockRaw {
fn deref_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
#[test]
fn block_list_size_test() {
assert_eq!(mem::size_of::<BlockList<BlockRaw>>(), BLOCK_SIZE as usize);
}
#[test]
fn block_raw_size_test() {
assert_eq!(mem::size_of::<BlockRaw>(), BLOCK_SIZE as usize);
}
use alloc::{boxed::Box, vec};
use core::{mem, ops, slice, str};
use crate::{BlockLevel, BlockTrait, Node, TreePtr, DIR_ENTRY_MAX_LENGTH, RECORD_LEVEL};
#[repr(C, packed)]
pub struct DirEntry {
node_ptr: TreePtr<Node>,
name: [u8; DIR_ENTRY_MAX_LENGTH],
}
impl DirEntry {
pub fn new(node_ptr: TreePtr<Node>, name: &str) -> DirEntry {
let mut entry = DirEntry {
node_ptr,
..Default::default()
};
entry.name[..name.len()].copy_from_slice(name.as_bytes());
entry
}
pub fn node_ptr(&self) -> TreePtr<Node> {
self.node_ptr
}
pub fn name(&self) -> Option<&str> {
let mut len = 0;
while len < self.name.len() {
if self.name[len] == 0 {
break;
}
len += 1;
}
//TODO: report utf8 error?
str::from_utf8(&self.name[..len]).ok()
}
}
impl Clone for DirEntry {
fn clone(&self) -> Self {
*self
}
}
impl Copy for DirEntry {}
impl Default for DirEntry {
fn default() -> Self {
Self {
node_ptr: TreePtr::default(),
name: [0; DIR_ENTRY_MAX_LENGTH],
}
}
}
//TODO: this is a box to prevent stack overflows
pub struct DirList {
pub entries: Box<[DirEntry]>,
}
unsafe impl BlockTrait for DirList {
fn empty(level: BlockLevel) -> Option<Self> {
if level.0 <= RECORD_LEVEL {
let entries = level.bytes() as usize / mem::size_of::<DirEntry>();
Some(Self {
entries: vec![DirEntry::default(); entries].into_boxed_slice(),
})
} else {
None
}
}
}
impl DirList {
pub fn is_empty(&self) -> bool {
for entry in self.entries.iter() {
if !entry.node_ptr().is_null() {
return false;
}
}
true
}
}
impl ops::Deref for DirList {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self.entries.as_ptr() as *const u8,
self.entries.len() * mem::size_of::<DirEntry>(),
) as &[u8]
}
}
}
impl ops::DerefMut for DirList {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(
self.entries.as_mut_ptr() as *mut u8,
self.entries.len() * mem::size_of::<DirEntry>(),
) as &mut [u8]
}
}
}
#[test]
fn dir_list_size_test() {
use core::ops::Deref;
for level_i in 0..RECORD_LEVEL {
let level = BlockLevel(level_i);
assert_eq!(
DirList::empty(level).unwrap().deref().len(),
level.bytes() as usize
);
}
}
use syscall::error::Result;
/// A disk
pub trait Disk {
fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize>;
fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result<usize>;
fn size(&mut self) -> Result<u64>;
}
use std::collections::{HashMap, VecDeque};
use std::{cmp, ptr};
use redoxfs::Disk;
use syscall::error::Result;
use self::lru_cache::LruCache;
mod linked_hash_map;
mod lru_cache;
use crate::disk::Disk;
use crate::BLOCK_SIZE;
fn copy_memory(src: &[u8], dest: &mut [u8]) -> usize {
let len = cmp::min(src.len(), dest.len());
......@@ -15,36 +11,52 @@ fn copy_memory(src: &[u8], dest: &mut [u8]) -> usize {
len
}
pub struct Cache<T> {
pub struct DiskCache<T> {
inner: T,
cache: LruCache<u64, [u8; 512]>,
cache: HashMap<u64, [u8; BLOCK_SIZE as usize]>,
order: VecDeque<u64>,
size: usize,
}
impl<T: Disk> Cache<T> {
impl<T: Disk> DiskCache<T> {
pub fn new(inner: T) -> Self {
Cache {
inner: inner,
cache: LruCache::new(65536) // 32 MB cache
// 16 MB cache
let size = 16 * 1024 * 1024 / BLOCK_SIZE as usize;
DiskCache {
inner,
cache: HashMap::with_capacity(size),
order: VecDeque::with_capacity(size),
size,
}
}
fn insert(&mut self, i: u64, data: [u8; BLOCK_SIZE as usize]) {
while self.order.len() >= self.size {
let removed = self.order.pop_front().unwrap();
self.cache.remove(&removed);
}
self.cache.insert(i, data);
self.order.push_back(i);
}
}
impl<T: Disk> Disk for Cache<T> {
fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
impl<T: Disk> Disk for DiskCache<T> {
unsafe fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
// println!("Cache read at {}", block);
let mut read = 0;
let mut failed = false;
for i in 0..(buffer.len() + 511)/512 {
for i in 0..(buffer.len() + BLOCK_SIZE as usize - 1) / (BLOCK_SIZE as usize) {
let block_i = block + i as u64;
let buffer_i = i * 512;
let buffer_j = cmp::min(buffer_i + 512, buffer.len());
let buffer_slice = &mut buffer[buffer_i .. buffer_j];
let buffer_i = i * BLOCK_SIZE as usize;
let buffer_j = cmp::min(buffer_i + BLOCK_SIZE as usize, buffer.len());
let buffer_slice = &mut buffer[buffer_i..buffer_j];
if let Some(cache_buf) = self.cache.get_mut(&block_i) {
read += copy_memory(cache_buf, buffer_slice);
}else{
} else {
failed = true;
break;
}
......@@ -54,38 +66,39 @@ impl<T: Disk> Disk for Cache<T> {
self.inner.read_at(block, buffer)?;
read = 0;
for i in 0..(buffer.len() + 511)/512 {
for i in 0..(buffer.len() + BLOCK_SIZE as usize - 1) / (BLOCK_SIZE as usize) {
let block_i = block + i as u64;
let buffer_i = i * 512;
let buffer_j = cmp::min(buffer_i + 512, buffer.len());
let buffer_slice = &buffer[buffer_i .. buffer_j];
let buffer_i = i * BLOCK_SIZE as usize;
let buffer_j = cmp::min(buffer_i + BLOCK_SIZE as usize, buffer.len());
let buffer_slice = &buffer[buffer_i..buffer_j];
let mut cache_buf = [0; 512];
let mut cache_buf = [0; BLOCK_SIZE as usize];
read += copy_memory(buffer_slice, &mut cache_buf);
self.cache.insert(block_i, cache_buf);
self.insert(block_i, cache_buf);
}
}
Ok(read)
}
fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result<usize> {
unsafe fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result<usize> {
//TODO: Write only blocks that have changed
// println!("Cache write at {}", block);
self.inner.write_at(block, buffer)?;
let mut written = 0;
for i in 0..(buffer.len() + 511)/512 {
for i in 0..(buffer.len() + BLOCK_SIZE as usize - 1) / (BLOCK_SIZE as usize) {
let block_i = block + i as u64;
let buffer_i = i * 512;
let buffer_j = cmp::min(buffer_i + 512, buffer.len());
let buffer_slice = &buffer[buffer_i .. buffer_j];
let buffer_i = i * BLOCK_SIZE as usize;
let buffer_j = cmp::min(buffer_i + BLOCK_SIZE as usize, buffer.len());
let buffer_slice = &buffer[buffer_i..buffer_j];
let mut cache_buf = [0; 512];
let mut cache_buf = [0; BLOCK_SIZE as usize];
written += copy_memory(buffer_slice, &mut cache_buf);
self.cache.insert(block_i, cache_buf);
self.insert(block_i, cache_buf);
}
Ok(written)
......
use std::fs::{File, OpenOptions};
use std::io::{Seek, SeekFrom};
use std::os::unix::fs::FileExt;
use std::path::Path;
use syscall::error::{Error, Result, EIO};
use crate::disk::Disk;
use crate::BLOCK_SIZE;
pub struct DiskFile {
pub file: File,
}
trait ResultExt {
type T;
fn or_eio(self) -> Result<Self::T>;
}
impl<T> ResultExt for Result<T> {
type T = T;
fn or_eio(self) -> Result<Self::T> {
match self {
Ok(t) => Ok(t),
Err(err) => {
eprintln!("RedoxFS: IO ERROR: {err}");
Err(Error::new(EIO))
}
}
}
}
impl<T> ResultExt for std::io::Result<T> {
type T = T;
fn or_eio(self) -> Result<Self::T> {
match self {
Ok(t) => Ok(t),
Err(err) => {
eprintln!("RedoxFS: IO ERROR: {err}");
Err(Error::new(EIO))
}
}
}
}
impl DiskFile {
pub fn open(path: impl AsRef<Path>) -> Result<DiskFile> {
let file = OpenOptions::new()
.read(true)
.write(true)
.open(path)
.or_eio()?;
Ok(DiskFile { file })
}
pub fn create(path: impl AsRef<Path>, size: u64) -> Result<DiskFile> {
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(path)
.or_eio()?;
file.set_len(size).or_eio()?;
Ok(DiskFile { file })
}
}
impl Disk for DiskFile {
unsafe fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
self.file.read_at(buffer, block * BLOCK_SIZE).or_eio()
}
unsafe fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result<usize> {
self.file.write_at(buffer, block * BLOCK_SIZE).or_eio()
}
fn size(&mut self) -> Result<u64> {
self.file.seek(SeekFrom::End(0)).or_eio()
}
}
use std::io::{Read, Seek, SeekFrom, Write};
use syscall::error::{Error, Result, EIO};
use crate::disk::Disk;
use crate::BLOCK_SIZE;
macro_rules! try_disk {
($expr:expr) => {
match $expr {
Ok(val) => val,
Err(err) => {
eprintln!("Disk I/O Error: {}", err);
return Err(Error::new(EIO));
}
}
};
}
pub struct DiskIo<T>(pub T);
impl<T: Read + Write + Seek> Disk for DiskIo<T> {
unsafe fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
try_disk!(self.0.seek(SeekFrom::Start(block * BLOCK_SIZE)));
let count = try_disk!(self.0.read(buffer));
Ok(count)
}
unsafe fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result<usize> {
try_disk!(self.0.seek(SeekFrom::Start(block * BLOCK_SIZE)));
let count = try_disk!(self.0.write(buffer));
Ok(count)
}
fn size(&mut self) -> Result<u64> {
let size = try_disk!(self.0.seek(SeekFrom::End(0)));
Ok(size)
}
}
use syscall::error::Result;
#[cfg(feature = "std")]
pub use self::cache::DiskCache;
#[cfg(feature = "std")]
pub use self::file::DiskFile;
#[cfg(feature = "std")]
pub use self::io::DiskIo;
#[cfg(feature = "std")]
pub use self::sparse::DiskSparse;
#[cfg(feature = "std")]
mod cache;
#[cfg(feature = "std")]
mod file;
#[cfg(feature = "std")]
mod io;
#[cfg(feature = "std")]
mod sparse;
/// A disk
pub trait Disk {
/// Read blocks from disk
///
/// # Safety
/// Unsafe to discourage use, use filesystem wrappers instead
unsafe fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize>;
/// Write blocks from disk
///
/// # Safety
/// Unsafe to discourage use, use filesystem wrappers instead
unsafe fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result<usize>;
/// Get size of disk in bytes
fn size(&mut self) -> Result<u64>;
}
use std::fs::{File, OpenOptions};
use std::io::{Read, Seek, SeekFrom, Write};
use std::path::Path;
use std::u64;
use syscall::error::{Error, Result, EIO};
use crate::disk::Disk;
use crate::BLOCK_SIZE;
macro_rules! try_disk {
($expr:expr) => {
match $expr {
Ok(val) => val,
Err(err) => {
eprintln!("Disk I/O Error: {}", err);
return Err(Error::new(EIO));
}
}
};
}
pub struct DiskSparse {
pub file: File,
pub max_size: u64,
}
impl DiskSparse {
pub fn create<P: AsRef<Path>>(path: P, max_size: u64) -> Result<DiskSparse> {
let file = try_disk!(OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(path));
Ok(DiskSparse { file, max_size })
}
}
impl Disk for DiskSparse {
unsafe fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
try_disk!(self.file.seek(SeekFrom::Start(block * BLOCK_SIZE)));
let count = try_disk!(self.file.read(buffer));
Ok(count)
}
unsafe fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result<usize> {
try_disk!(self.file.seek(SeekFrom::Start(block * BLOCK_SIZE)));
let count = try_disk!(self.file.write(buffer));
Ok(count)
}
fn size(&mut self) -> Result<u64> {
Ok(self.max_size)
}
}
use std::{fmt, mem, ops, slice};
use super::Extent;
/// An extra node
#[repr(packed)]
pub struct ExNode {
pub prev: u64,
pub next: u64,
pub extents: [Extent; 31],
}
impl ExNode {
pub fn default() -> ExNode {
ExNode {
prev: 0,
next: 0,
extents: [Extent::default(); 31],
}
}
pub fn size(&self) -> u64 {
self.extents.iter().fold(0, |size, extent| size + extent.length)
}
}
impl fmt::Debug for ExNode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let extents: Vec<&Extent> = self.extents.iter().filter(|extent| -> bool { extent.length > 0 }).collect();
f.debug_struct("ExNode")
.field("prev", &self.prev)
.field("next", &self.next)
.field("extents", &extents)
.finish()
}
}
impl ops::Deref for ExNode {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(self as *const ExNode as *const u8, mem::size_of::<ExNode>()) as &[u8]
}
}
}
impl ops::DerefMut for ExNode {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(self as *mut ExNode as *mut u8, mem::size_of::<ExNode>()) as &mut [u8]
}
}
}
#[test]
fn ex_node_size_test() {
assert_eq!(mem::size_of::<ExNode>(), 512);
}
use std::cmp::min;
pub struct BlockIter {
block: u64,
length: u64,
i: u64
}
impl Iterator<> for BlockIter {
type Item = (u64, usize);
fn next(&mut self) -> Option<Self::Item> {
if self.i < (self.length + 511)/512 {
let ret = Some((self.block + self.i, min(512, self.length - self.i * 512) as usize));
self.i += 1;
ret
} else {
None
}
}
}
/// A disk extent
#[derive(Copy, Clone, Debug, Default)]
#[repr(packed)]
pub struct Extent {
pub block: u64,
pub length: u64,
}
impl Extent {
pub fn default() -> Extent {
Extent {
block: 0,
length: 0
}
}
pub fn new(block: u64, length: u64) -> Extent {
Extent {
block: block,
length: length
}
}
pub fn blocks(&self) -> BlockIter {
BlockIter {
block: self.block,
length: self.length,
i: 0
}
}
}
use std::cmp::min;
use aes::{Aes128, BlockDecrypt, BlockEncrypt};
use alloc::{collections::VecDeque, vec::Vec};
use syscall::error::{Error, Result, EKEYREJECTED, ENOENT, ENOKEY};
use syscall::error::{Result, Error, EEXIST, EISDIR, ENOENT, ENOSPC, ENOTDIR, ENOTEMPTY};
use super::{Disk, ExNode, Extent, Header, Node};
#[cfg(feature = "std")]
use crate::{AllocEntry, AllocList, BlockData, BlockTrait, Key, KeySlot, Node, Salt, TreeList};
use crate::{Allocator, BlockAddr, BlockLevel, Disk, Header, Transaction, BLOCK_SIZE, HEADER_RING};
/// A file system
pub struct FileSystem {
pub disk: Box<Disk>,
pub struct FileSystem<D: Disk> {
//TODO: make private
pub disk: D,
//TODO: make private
pub block: u64,
pub header: (u64, Header)
//TODO: make private
pub header: Header,
pub(crate) allocator: Allocator,
pub(crate) aes_opt: Option<Aes128>,
aes_blocks: Vec<aes::Block>,
}
impl FileSystem {
impl<D: Disk> FileSystem<D> {
/// Open a file system on a disk
pub fn open(mut disk: Box<Disk>) -> Result<Self> {
for block in 0..65536 {
let mut header = (0, Header::default());
disk.read_at(block + header.0, &mut header.1)?;
if header.1.valid() {
let mut root = (header.1.root, Node::default());
disk.read_at(block + root.0, &mut root.1)?;
let mut free = (header.1.free, Node::default());
disk.read_at(block + free.0, &mut free.1)?;
return Ok(FileSystem {
disk: disk,
block: block,
header: header
});
pub fn open(
mut disk: D,
password_opt: Option<&[u8]>,
block_opt: Option<u64>,
squash: bool,
) -> Result<Self> {
for ring_block in block_opt.map_or(0..65536, |x| x..x + 1) {
let mut header = Header::default();
unsafe { disk.read_at(ring_block, &mut header)? };
// Skip invalid headers
if !header.valid() {
continue;
}
}
Err(Error::new(ENOENT))
}
/// Create a file system on a disk
pub fn create(mut disk: Box<Disk>, ctime: u64, ctime_nsec: u32) -> Result<Self> {
let size = disk.size()?;
if size >= 4 * 512 {
let mut free = (2, Node::new(Node::MODE_FILE, "free", 0, ctime, ctime_nsec));
free.1.extents[0] = Extent::new(4, size - 4 * 512);
disk.write_at(free.0, &free.1)?;
let root = (1, Node::new(Node::MODE_DIR | 0o755, "root", 0, ctime, ctime_nsec));
disk.write_at(root.0, &root.1)?;
let header = (0, Header::new(size, root.0, free.0));
disk.write_at(header.0, &header.1)?;
let block = ring_block - (header.generation() % HEADER_RING);
for i in 0..HEADER_RING {
let mut other_header = Header::default();
unsafe { disk.read_at(block + i, &mut other_header)? };
Ok(FileSystem {
disk: disk,
block: 0,
header: header
})
} else {
Err(Error::new(ENOSPC))
}
}
pub fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
self.disk.read_at(self.block + block, buffer)
}
pub fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result<usize> {
self.disk.write_at(self.block + block, buffer)
}
pub fn allocate(&mut self, length: u64) -> Result<u64> {
//TODO: traverse next pointer
let free_block = self.header.1.free;
let mut free = self.node(free_block)?;
let mut block_option = None;
for mut extent in free.1.extents.iter_mut() {
if extent.length/512 >= length {
block_option = Some(extent.block);
extent.length -= length * 512;
extent.block += length;
break;
}
}
if let Some(block) = block_option {
self.write_at(free.0, &free.1)?;
Ok(block)
} else {
Err(Error::new(ENOSPC))
}
}
pub fn deallocate(&mut self, block: u64, length: u64) -> Result<()> {
let free_block = self.header.1.free;
self.insert_blocks(block, length, free_block)
}
pub fn node(&mut self, block: u64) -> Result<(u64, Node)> {
let mut node = Node::default();
self.read_at(block, &mut node)?;
Ok((block, node))
}
pub fn ex_node(&mut self, block: u64) -> Result<(u64, ExNode)> {
let mut node = ExNode::default();
self.read_at(block, &mut node)?;
Ok((block, node))
}
pub fn child_nodes(&mut self, children: &mut Vec<(u64, Node)>, parent_block: u64) -> Result<()> {
if parent_block == 0 {
return Ok(());
}
// Skip invalid headers
if !other_header.valid() {
continue;
}
let parent = self.node(parent_block)?;
for extent in parent.1.extents.iter() {
for (block, size) in extent.blocks() {
if size >= 512 {
children.push(self.node(block)?);
// If this is a newer header, use it
if other_header.generation() > header.generation() {
header = other_header;
}
}
}
self.child_nodes(children, parent.1.next)
}
pub fn find_node(&mut self, name: &str, parent_block: u64) -> Result<(u64, Node)> {
if parent_block == 0 {
return Err(Error::new(ENOENT));
}
let parent = self.node(parent_block)?;
for extent in parent.1.extents.iter() {
for (block, size) in extent.blocks() {
if size >= 512 {
let child = self.node(block)?;
let mut matches = false;
if let Ok(child_name) = child.1.name() {
if child_name == name {
matches = true;
let aes_opt = match password_opt {
Some(password) => {
if !header.encrypted() {
// Header not encrypted but password provided
return Err(Error::new(EKEYREJECTED));
}
match header.aes(password) {
Some(aes) => Some(aes),
None => {
// Header encrypted with a different password
return Err(Error::new(ENOKEY));
}
}
if matches {
return Ok(child);
}
None => {
if header.encrypted() {
// Header encrypted but no password provided
return Err(Error::new(ENOKEY));
}
None
}
}
}
};
self.find_node(name, parent.1.next)
}
let mut fs = FileSystem {
disk,
block,
header,
allocator: Allocator::default(),
aes_opt,
aes_blocks: Vec::with_capacity(BLOCK_SIZE as usize / aes::BLOCK_SIZE),
};
fn insert_blocks(&mut self, block: u64, length: u64, parent_block: u64) -> Result<()> {
if parent_block == 0 {
return Err(Error::new(ENOSPC));
}
unsafe { fs.reset_allocator()? };
let mut inserted = false;
let mut parent = self.node(parent_block)?;
for mut extent in parent.1.extents.iter_mut() {
if extent.length == 0 {
//New extent
inserted = true;
extent.block = block;
extent.length = length;
break;
} else if length % 512 == 0 && extent.block == block + length/512 {
//At beginning
inserted = true;
extent.block = block;
extent.length += length;
break;
} else if extent.length % 512 == 0 && extent.block + extent.length/512 == block {
//At end
inserted = true;
extent.length += length;
break;
}
}
// Squash allocations and sync
Transaction::new(&mut fs).commit(squash)?;
if inserted {
self.write_at(parent.0, &parent.1)?;
Ok(())
} else {
if parent.1.next == 0 {
let next = self.allocate(1)?;
// Could be mutated by self.allocate if free block
if parent.0 == self.header.1.free {
self.read_at(parent.0, &mut parent.1)?;
}
parent.1.next = next;
self.write_at(parent.0, &parent.1)?;
self.write_at(parent.1.next, &Node::default())?;
}
self.insert_blocks(block, length, parent.1.next)
return Ok(fs);
}
}
pub fn create_node(&mut self, mode: u16, name: &str, parent_block: u64, ctime: u64, ctime_nsec: u32) -> Result<(u64, Node)> {
if self.find_node(name, parent_block).is_ok() {
Err(Error::new(EEXIST))
} else {
let node = (self.allocate(1)?, Node::new(mode, name, parent_block, ctime, ctime_nsec));
self.write_at(node.0, &node.1)?;
self.insert_blocks(node.0, 512, parent_block)?;
Ok(node)
}
Err(Error::new(ENOENT))
}
fn remove_blocks(&mut self, block: u64, length: u64, parent_block: u64) -> Result<()> {
if parent_block == 0 {
return Err(Error::new(ENOENT));
}
let mut removed = false;
let mut replace_option = None;
let mut parent = self.node(parent_block)?;
for mut extent in parent.1.extents.iter_mut() {
if block >= extent.block && block + length <= extent.block + extent.length/512 {
//Inside
removed = true;
let left = Extent::new(extent.block, (block - extent.block) * 512);
let right = Extent::new(block + length, ((extent.block + extent.length/512) - (block + length)) * 512);
if left.length > 0 {
*extent = left;
/// Create a file system on a disk
#[cfg(feature = "std")]
pub fn create(
disk: D,
password_opt: Option<&[u8]>,
ctime: u64,
ctime_nsec: u32,
) -> Result<Self> {
Self::create_reserved(disk, password_opt, &[], ctime, ctime_nsec)
}
if right.length > 0 {
replace_option = Some(right);
}
} else if right.length > 0 {
*extent = right;
} else {
*extent = Extent::default();
}
/// Create a file system on a disk, with reserved data at the beginning
/// Reserved data will be zero padded up to the nearest block
/// We need to pass ctime and ctime_nsec in order to initialize the unix timestamps
#[cfg(feature = "std")]
pub fn create_reserved(
mut disk: D,
password_opt: Option<&[u8]>,
reserved: &[u8],
ctime: u64,
ctime_nsec: u32,
) -> Result<Self> {
let size = disk.size()?;
let block_offset = (reserved.len() as u64 + BLOCK_SIZE - 1) / BLOCK_SIZE;
break;
}
if size < (block_offset + HEADER_RING + 4) * BLOCK_SIZE {
return Err(Error::new(syscall::error::ENOSPC));
}
if removed {
self.write_at(parent.0, &parent.1)?;
// Fill reserved data, pad with zeroes
for block in 0..block_offset as usize {
let mut data = [0; BLOCK_SIZE as usize];
if let Some(replace) = replace_option {
self.insert_blocks(replace.block, replace.length, parent_block)?;
let mut i = 0;
while i < data.len() && block * BLOCK_SIZE as usize + i < reserved.len() {
data[i] = reserved[block * BLOCK_SIZE as usize + i];
i += 1;
}
self.deallocate(block, 512)?;
Ok(())
} else {
self.remove_blocks(block, length, parent.1.next)
}
}
pub fn remove_node(&mut self, mode: u16, name: &str, parent_block: u64) -> Result<()> {
let node = self.find_node(name, parent_block)?;
if node.1.mode & Node::MODE_TYPE == mode {
if node.1.is_dir() {
let mut children = Vec::new();
self.child_nodes(&mut children, node.0)?;
if ! children.is_empty() {
return Err(Error::new(ENOTEMPTY));
}
unsafe {
disk.write_at(block as u64, &data)?;
}
self.node_set_len(node.0, 0)?;
self.remove_blocks(node.0, 1, parent_block)?;
self.write_at(node.0, &Node::default())?;
Ok(())
} else if node.1.is_dir() {
Err(Error::new(EISDIR))
} else {
Err(Error::new(ENOTDIR))
}
}
// TODO: modification time
fn node_ensure_len(&mut self, block: u64, mut length: u64) -> Result<()> {
if block == 0 {
return Err(Error::new(ENOENT));
}
let mut header = Header::new(size);
let mut changed = false;
let mut node = self.node(block)?;
for mut extent in node.1.extents.iter_mut() {
if extent.length >= length {
length = 0;
break;
} else {
changed = true;
let allocated = ((extent.length + 511)/512) * 512;
if allocated >= length {
extent.length = length;
length = 0;
break;
} else {
extent.length = allocated;
length -= allocated;
}
let aes_opt = match password_opt {
Some(password) => {
//TODO: handle errors
header.key_slots[0] =
KeySlot::new(password, Salt::new().unwrap(), Key::new().unwrap()).unwrap();
Some(header.key_slots[0].key(password).unwrap().into_aes())
}
}
if changed {
self.write_at(node.0, &node.1)?;
}
None => None,
};
let mut fs = FileSystem {
disk,
block: block_offset,
header,
allocator: Allocator::default(),
aes_opt,
aes_blocks: Vec::with_capacity(BLOCK_SIZE as usize / aes::BLOCK_SIZE),
};
// Write header generation zero
let count = unsafe { fs.disk.write_at(fs.block, &fs.header)? };
if count != core::mem::size_of_val(&fs.header) {
// Wrote wrong number of bytes
#[cfg(feature = "log")]
log::error!("CREATE: WRONG NUMBER OF BYTES");
return Err(Error::new(syscall::error::EIO));
}
// Set tree and alloc pointers and write header generation one
fs.tx(|tx| unsafe {
let tree = BlockData::new(
BlockAddr::new(HEADER_RING + 1, BlockLevel::default()),
TreeList::empty(BlockLevel::default()).unwrap(),
);
let mut alloc = BlockData::new(
BlockAddr::new(HEADER_RING + 2, BlockLevel::default()),
AllocList::empty(BlockLevel::default()).unwrap(),
);
let alloc_free = size / BLOCK_SIZE - (block_offset + HEADER_RING + 4);
alloc.data_mut().entries[0] = AllocEntry::new(HEADER_RING + 4, alloc_free as i64);
tx.header.tree = tx.write_block(tree)?;
tx.header.alloc = tx.write_block(alloc)?;
tx.header_changed = true;
if length > 0 {
if node.1.next > 0 {
self.node_ensure_len(node.1.next, length)
} else {
let new_block = self.allocate((length + 511)/512)?;
self.insert_blocks(new_block, length, block)?;
Ok(())
}
} else {
Ok(())
}
}
})?;
//TODO: modification time
pub fn node_set_len(&mut self, block: u64, mut length: u64) -> Result<()> {
if block == 0 {
return Err(Error::new(ENOENT));
unsafe {
fs.reset_allocator()?;
}
let mut changed = false;
fs.tx(|tx| unsafe {
let mut root = BlockData::new(
BlockAddr::new(HEADER_RING + 3, BlockLevel::default()),
Node::new(Node::MODE_DIR | 0o755, 0, 0, ctime, ctime_nsec),
);
root.data_mut().set_links(1);
let root_ptr = tx.write_block(root)?;
assert_eq!(tx.insert_tree(root_ptr)?.id(), 1);
Ok(())
})?;
let mut node = self.node(block)?;
for mut extent in node.1.extents.iter_mut() {
if extent.length > length {
let start = (length + 511)/512;
let end = (extent.length + 511)/512;
if end > start {
self.deallocate(extent.block + start, (end - start) * 512)?;
}
extent.length = length;
changed = true;
length = 0;
} else {
length -= extent.length;
}
}
// Make sure everything is synced and squash allocations
Transaction::new(&mut fs).commit(true)?;
if changed {
self.write_at(node.0, &node.1)?;
}
Ok(fs)
}
if node.1.next > 0 {
self.node_set_len(node.1.next, length)
} else {
Ok(())
}
/// start a filesystem transaction, required for making any changes
pub fn tx<F: FnOnce(&mut Transaction<D>) -> Result<T>, T>(&mut self, f: F) -> Result<T> {
let mut tx = Transaction::new(self);
let t = f(&mut tx)?;
tx.commit(false)?;
Ok(t)
}
fn node_extents(&mut self, block: u64, mut offset: u64, mut len: usize, extents: &mut Vec<Extent>) -> Result<()> {
if block == 0 {
return Ok(());
}
pub fn allocator(&self) -> &Allocator {
&self.allocator
}
let node = self.node(block)?;
for extent in node.1.extents.iter() {
let mut push_extent = Extent::default();
for (block, size) in extent.blocks() {
if offset == 0 {
if push_extent.block == 0 {
push_extent.block = block;
}
if len >= size {
push_extent.length += size as u64;
len -= size;
} else if len > 0 {
push_extent.length += len as u64;
len = 0;
break;
} else {
break;
/// Reset allocator to state stored on disk
///
/// # Safety
/// Unsafe, it must only be called when opening the filesystem
unsafe fn reset_allocator(&mut self) -> Result<()> {
self.allocator = Allocator::default();
// To avoid having to update all prior alloc blocks, there is only a previous pointer
// This means we need to roll back all allocations. Currently we do this by reading the
// alloc log into a buffer to reverse it.
let mut allocs = VecDeque::new();
self.tx(|tx| {
let mut alloc_ptr = tx.header.alloc;
while !alloc_ptr.is_null() {
let alloc = tx.read_block(alloc_ptr)?;
alloc_ptr = alloc.data().prev;
allocs.push_front(alloc);
}
Ok(())
})?;
for alloc in allocs {
for entry in alloc.data().entries.iter() {
let index = entry.index();
let count = entry.count();
if count < 0 {
for i in 0..-count {
//TODO: replace assert with error?
let addr = BlockAddr::new(index + i as u64, BlockLevel::default());
assert_eq!(self.allocator.allocate_exact(addr), Some(addr));
}
} else {
offset -= 1;
for i in 0..count {
let addr = BlockAddr::new(index + i as u64, BlockLevel::default());
self.allocator.deallocate(addr);
}
}
}
if push_extent.length > 0 {
extents.push(push_extent);
}
if len == 0 {
break;
}
}
if len > 0 {
self.node_extents(node.1.next, offset, len, extents)
} else {
Ok(())
}
Ok(())
}
pub fn read_node(&mut self, block: u64, offset: u64, buf: &mut [u8]) -> Result<usize> {
let block_offset = offset / 512;
let mut byte_offset = (offset % 512) as usize;
let mut extents = Vec::new();
self.node_extents(block, block_offset, byte_offset + buf.len(), &mut extents)?;
let mut i = 0;
for extent in extents.iter() {
let mut block = extent.block;
let mut length = extent.length;
if byte_offset > 0 && length > 0 {
let mut sector = [0; 512];
self.read_at(block, &mut sector)?;
let sector_size = min(sector.len() as u64, length) as usize;
for (s_b, mut b) in sector[byte_offset..sector_size].iter().zip(buf[i..].iter_mut()) {
*b = *s_b;
i += 1;
}
block += 1;
length -= sector_size as u64;
byte_offset = 0;
}
let length_aligned = ((min(length, (buf.len() - i) as u64)/512) * 512) as usize;
if length_aligned > 0 {
let extent_buf = &mut buf[i..i + length_aligned];
self.read_at(block, extent_buf)?;
i += length_aligned;
block += (length_aligned as u64)/512;
length -= length_aligned as u64;
}
pub(crate) fn decrypt(&mut self, data: &mut [u8]) -> bool {
let aes = if let Some(ref aes) = self.aes_opt {
aes
} else {
// Do nothing if encryption is disabled
return false;
};
if length > 0 {
let mut sector = [0; 512];
self.read_at(block, &mut sector)?;
assert_eq!(data.len() % aes::BLOCK_SIZE, 0);
let sector_size = min(sector.len() as u64, length) as usize;
for (s_b, mut b) in sector[..sector_size].iter().zip(buf[i..].iter_mut()) {
*b = *s_b;
i += 1;
}
self.aes_blocks.clear();
for i in 0..data.len() / aes::BLOCK_SIZE {
self.aes_blocks.push(aes::Block::clone_from_slice(
&data[i * aes::BLOCK_SIZE..(i + 1) * aes::BLOCK_SIZE],
));
}
block += 1;
length -= sector_size as u64;
}
aes.decrypt_blocks(&mut self.aes_blocks);
assert_eq!(length, 0);
assert_eq!(block, extent.block + (extent.length + 511)/512);
for i in 0..data.len() / aes::BLOCK_SIZE {
data[i * aes::BLOCK_SIZE..(i + 1) * aes::BLOCK_SIZE]
.copy_from_slice(&self.aes_blocks[i]);
}
self.aes_blocks.clear();
Ok(i)
true
}
pub fn write_node(&mut self, block: u64, offset: u64, buf: &[u8], mtime: u64, mtime_nsec: u32) -> Result<usize> {
let block_offset = offset / 512;
let mut byte_offset = (offset % 512) as usize;
self.node_ensure_len(block, block_offset as u64 * 512 + (byte_offset + buf.len()) as u64)?;
let mut extents = Vec::new();
self.node_extents(block, block_offset, byte_offset + buf.len(), &mut extents)?;
let mut i = 0;
for extent in extents.iter() {
let mut block = extent.block;
let mut length = extent.length;
if byte_offset > 0 && length > 0 {
let mut sector = [0; 512];
self.read_at(block, &mut sector)?;
let sector_size = min(sector.len() as u64, length) as usize;
for (mut s_b, b) in sector[byte_offset..sector_size].iter_mut().zip(buf[i..].iter()) {
*s_b = *b;
i += 1;
}
self.write_at(block, &sector)?;
block += 1;
length -= sector_size as u64;
byte_offset = 0;
}
let length_aligned = ((min(length, (buf.len() - i) as u64)/512) * 512) as usize;
if length_aligned > 0 {
let extent_buf = &buf[i..i + length_aligned];
self.write_at(block, extent_buf)?;
i += length_aligned;
block += (length_aligned as u64)/512;
length -= length_aligned as u64;
}
if length > 0 {
let mut sector = [0; 512];
self.read_at(block, &mut sector)?;
let sector_size = min(sector.len() as u64, length) as usize;
for (mut s_b, b) in sector[..sector_size].iter_mut().zip(buf[i..].iter()) {
*s_b = *b;
i += 1;
}
self.write_at(block, &sector)?;
block += 1;
length -= sector_size as u64;
}
assert_eq!(length, 0);
assert_eq!(block, extent.block + (extent.length + 511)/512);
}
if i > 0 {
let mut node = self.node(block)?;
if mtime > node.1.mtime || (mtime == node.1.mtime && mtime_nsec > node.1.mtime_nsec) {
node.1.mtime = mtime;
node.1.mtime_nsec = mtime_nsec;
self.write_at(node.0, &node.1)?;
}
}
pub(crate) fn encrypt(&mut self, data: &mut [u8]) -> bool {
let aes = if let Some(ref aes) = self.aes_opt {
aes
} else {
// Do nothing if encryption is disabled
return false;
};
Ok(i)
}
assert_eq!(data.len() % aes::BLOCK_SIZE, 0);
pub fn node_len(&mut self, block: u64) -> Result<u64> {
if block == 0 {
return Err(Error::new(ENOENT));
self.aes_blocks.clear();
for i in 0..data.len() / aes::BLOCK_SIZE {
self.aes_blocks.push(aes::Block::clone_from_slice(
&data[i * aes::BLOCK_SIZE..(i + 1) * aes::BLOCK_SIZE],
));
}
let mut size = 0;
aes.encrypt_blocks(&mut self.aes_blocks);
let node = self.node(block)?;
for extent in node.1.extents.iter() {
size += extent.length;
for i in 0..data.len() / aes::BLOCK_SIZE {
data[i * aes::BLOCK_SIZE..(i + 1) * aes::BLOCK_SIZE]
.copy_from_slice(&self.aes_blocks[i]);
}
self.aes_blocks.clear();
if node.1.next > 0 {
size += self.node_len(node.1.next)?;
Ok(size)
} else {
Ok(size)
}
true
}
}
use std::{fmt, mem, slice};
use std::ops::{Deref, DerefMut};
use core::ops::{Deref, DerefMut};
use core::{fmt, mem, slice};
use endian_num::Le;
use aes::{Aes128, BlockDecrypt, BlockEncrypt};
use crate::{AllocList, BlockPtr, KeySlot, Tree, BLOCK_SIZE, SIGNATURE, VERSION};
pub const HEADER_RING: u64 = 256;
/// The header of the filesystem
#[repr(packed)]
#[derive(Clone, Copy)]
#[repr(C, packed)]
pub struct Header {
/// Signature, should be b"RedoxFS\0"
/// Signature, should be SIGNATURE
pub signature: [u8; 8],
/// Version, should be 1
pub version: u64,
/// Version, should be VERSION
pub version: Le<u64>,
/// Disk ID, a 128-bit unique identifier
pub uuid: [u8; 16],
/// Disk size, in 512-byte sectors
pub size: u64,
/// Block of root node
pub root: u64,
/// Block of free space node
pub free: u64,
/// Disk size, in number of BLOCK_SIZE sectors
pub size: Le<u64>,
/// Generation of header
pub generation: Le<u64>,
/// Block of first tree node
pub tree: BlockPtr<Tree>,
/// Block of last alloc node
pub alloc: BlockPtr<AllocList>,
/// Key slots
pub key_slots: [KeySlot; 64],
/// Padding
pub padding: [u8; 456]
pub padding: [u8; BLOCK_SIZE as usize - 2152],
/// encrypted hash of header data without hash, set to hash and padded if disk is not encrypted
pub encrypted_hash: [u8; 16],
/// hash of header data without hash
pub hash: Le<u64>,
}
impl Header {
pub const SIGNATURE: &'static [u8; 8] = b"RedoxFS\0";
pub const VERSION: u64 = 2;
#[cfg(feature = "std")]
pub fn new(size: u64) -> Header {
let uuid = uuid::Uuid::new_v4();
let mut header = Header {
signature: *SIGNATURE,
version: VERSION.into(),
uuid: *uuid.as_bytes(),
size: size.into(),
..Default::default()
};
header.update_hash(None);
header
}
pub fn default() -> Header {
Header {
signature: [0; 8],
version: 0,
uuid: [0; 16],
size: 0,
root: 0,
free: 0,
padding: [0; 456]
pub fn valid(&self) -> bool {
if &self.signature != SIGNATURE {
// Signature does not match
return false;
}
if self.version.to_ne() != VERSION {
// Version does not match
return false;
}
if self.hash.to_ne() != self.create_hash() {
// Hash does not match
return false;
}
// All tests passed, header is valid
true
}
pub fn new(size: u64, root: u64, free: u64) -> Header {
Header {
signature: *Header::SIGNATURE,
version: Header::VERSION,
uuid: [0; 16],
size: size,
root: root,
free: free,
padding: [0; 456]
pub fn uuid(&self) -> [u8; 16] {
self.uuid
}
pub fn size(&self) -> u64 {
self.size.to_ne()
}
pub fn generation(&self) -> u64 {
self.generation.to_ne()
}
fn create_hash(&self) -> u64 {
// Calculate part of header to hash (everything before the hashes)
let end = mem::size_of_val(self)
- mem::size_of_val(&{ self.hash })
- mem::size_of_val(&{ self.encrypted_hash });
seahash::hash(&self[..end])
}
fn create_encrypted_hash(&self, aes_opt: Option<&Aes128>) -> [u8; 16] {
let mut encrypted_hash = [0; 16];
for (i, b) in self.hash.to_le_bytes().iter().enumerate() {
encrypted_hash[i] = *b;
}
if let Some(aes) = aes_opt {
let mut block = aes::Block::from(encrypted_hash);
aes.encrypt_block(&mut block);
encrypted_hash = block.into();
}
encrypted_hash
}
pub fn valid(&self) -> bool {
&self.signature == Header::SIGNATURE && self.version == Header::VERSION
pub fn encrypted(&self) -> bool {
(self.encrypted_hash) != self.create_encrypted_hash(None)
}
pub fn aes(&self, password: &[u8]) -> Option<Aes128> {
let hash = self.create_encrypted_hash(None);
for slot in self.key_slots.iter() {
//TODO: handle errors
let aes = slot.key(password).unwrap().into_aes();
let mut block = aes::Block::from(self.encrypted_hash);
aes.decrypt_block(&mut block);
if block == aes::Block::from(hash) {
return Some(aes);
}
}
None
}
fn update_hash(&mut self, aes_opt: Option<&Aes128>) {
self.hash = self.create_hash().into();
// Make sure to do this second, it relies on the hash being up to date
self.encrypted_hash = self.create_encrypted_hash(aes_opt);
}
pub fn update(&mut self, aes_opt: Option<&Aes128>) -> u64 {
let mut generation = self.generation();
generation += 1;
self.generation = generation.into();
self.update_hash(aes_opt);
generation
}
}
impl Default for Header {
fn default() -> Self {
Self {
signature: [0; 8],
version: 0.into(),
uuid: [0; 16],
size: 0.into(),
generation: 0.into(),
tree: BlockPtr::<Tree>::default(),
alloc: BlockPtr::<AllocList>::default(),
key_slots: [KeySlot::default(); 64],
padding: [0; BLOCK_SIZE as usize - 2152],
encrypted_hash: [0; 16],
hash: 0.into(),
}
}
}
impl fmt::Debug for Header {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let signature = self.signature;
let version = self.version;
let uuid = self.uuid;
let size = self.size;
let generation = self.generation;
let tree = self.tree;
let alloc = self.alloc;
let hash = self.hash;
f.debug_struct("Header")
.field("signature", &self.signature)
.field("version", &self.version)
.field("uuid", &self.uuid)
.field("size", &self.size)
.field("root", &self.root)
.field("free", &self.free)
.field("signature", &signature)
.field("version", &version)
.field("uuid", &uuid)
.field("size", &size)
.field("generation", &generation)
.field("tree", &tree)
.field("alloc", &alloc)
.field("hash", &hash)
.finish()
}
}
......@@ -70,7 +181,8 @@ impl Deref for Header {
type Target = [u8];
fn deref(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(self as *const Header as *const u8, mem::size_of::<Header>()) as &[u8]
slice::from_raw_parts(self as *const Header as *const u8, mem::size_of::<Header>())
as &[u8]
}
}
}
......@@ -78,12 +190,36 @@ impl Deref for Header {
impl DerefMut for Header {
fn deref_mut(&mut self) -> &mut [u8] {
unsafe {
slice::from_raw_parts_mut(self as *mut Header as *mut u8, mem::size_of::<Header>()) as &mut [u8]
slice::from_raw_parts_mut(self as *mut Header as *mut u8, mem::size_of::<Header>())
as &mut [u8]
}
}
}
#[test]
fn header_not_valid_test() {
assert_eq!(Header::default().valid(), false);
}
#[test]
fn header_size_test() {
assert_eq!(mem::size_of::<Header>(), 512);
assert_eq!(mem::size_of::<Header>(), BLOCK_SIZE as usize);
}
#[test]
fn header_hash_test() {
let mut header = Header::default();
assert_eq!(header.create_hash(), 0xe81ffcb86026ff96);
header.update_hash(None);
assert_eq!(header.hash.to_ne(), 0xe81ffcb86026ff96);
assert_eq!(
header.encrypted_hash,
[0x96, 0xff, 0x26, 0x60, 0xb8, 0xfc, 0x1f, 0xe8, 0, 0, 0, 0, 0, 0, 0, 0]
);
}
#[cfg(feature = "std")]
#[test]
fn header_valid_test() {
assert_eq!(Header::new(0).valid(), true);
}
use aes::{Aes128, BlockDecrypt, BlockEncrypt, NewBlockCipher};
// The raw key, keep secret!
#[repr(transparent)]
pub struct Key([u8; 16]);
impl Key {
/// Generate a random key
#[cfg(feature = "std")]
pub fn new() -> Result<Self, getrandom::Error> {
let mut bytes = [0; 16];
getrandom::getrandom(&mut bytes)?;
Ok(Self(bytes))
}
pub fn into_aes(self) -> Aes128 {
Aes128::new(&aes::Block::from(self.0))
}
}
/// The encrypted key, encrypted with AES using the salt and password
#[derive(Clone, Copy, Default)]
#[repr(transparent)]
pub struct EncryptedKey([u8; 16]);
/// Salt used to prevent rainbow table attacks on the encryption password
#[derive(Clone, Copy, Default)]
#[repr(transparent)]
pub struct Salt([u8; 16]);
impl Salt {
/// Generate a random salt
#[cfg(feature = "std")]
pub fn new() -> Result<Self, getrandom::Error> {
let mut bytes = [0; 16];
getrandom::getrandom(&mut bytes)?;
Ok(Self(bytes))
}
}
/// The key slot, containing the salt and encrypted key that are used with one password
#[derive(Clone, Copy, Default)]
#[repr(C, packed)]
pub struct KeySlot {
salt: Salt,
encrypted_key: EncryptedKey,
}
impl KeySlot {
/// Get the password AES key (generated from the password and salt, encrypts the real key)
pub fn password_aes(password: &[u8], salt: &Salt) -> Result<Aes128, argon2::Error> {
let mut key = Key([0; 16]);
let mut params_builder = argon2::ParamsBuilder::new();
params_builder.output_len(key.0.len())?;
let argon2 = argon2::Argon2::new(
argon2::Algorithm::Argon2id,
argon2::Version::V0x13,
params_builder.params()?,
);
argon2.hash_password_into(password, &salt.0, &mut key.0)?;
Ok(key.into_aes())
}
/// Create a new key slot from a password, salt, and encryption key
pub fn new(password: &[u8], salt: Salt, key: Key) -> Result<Self, argon2::Error> {
let password_aes = Self::password_aes(password, &salt)?;
// Encrypt the real AES key
let mut block = aes::Block::from(key.0);
password_aes.encrypt_block(&mut block);
Ok(Self {
salt,
encrypted_key: EncryptedKey(block.into()),
})
}
/// Get the encryption key from this key slot
pub fn key(&self, password: &[u8]) -> Result<Key, argon2::Error> {
let password_aes = Self::password_aes(password, &self.salt)?;
// Decrypt the real AES key
let mut block = aes::Block::from(self.encrypted_key.0);
password_aes.decrypt_block(&mut block);
Ok(Key(block.into()))
}
}
#![crate_name="redoxfs"]
#![crate_type="lib"]
#![crate_name = "redoxfs"]
#![crate_type = "lib"]
#![cfg_attr(not(feature = "std"), no_std)]
// Used often in generating redox_syscall errors
#![allow(clippy::or_fun_call)]
#![allow(unexpected_cfgs)]
#![deny(warnings)]
extern crate alloc;
extern crate syscall;
use core::sync::atomic::AtomicUsize;
pub use self::disk::Disk;
pub use self::ex_node::ExNode;
pub use self::extent::Extent;
// The alloc log grows by 1 block about every 21 generations
pub const ALLOC_GC_THRESHOLD: u64 = 1024;
pub const BLOCK_SIZE: u64 = 4096;
// A record is 4KiB << 5 = 128KiB
pub const RECORD_LEVEL: usize = 5;
pub const RECORD_SIZE: u64 = BLOCK_SIZE << RECORD_LEVEL;
pub const SIGNATURE: &[u8; 8] = b"RedoxFS\0";
pub const VERSION: u64 = 6;
pub const DIR_ENTRY_MAX_LENGTH: usize = 252;
pub static IS_UMT: AtomicUsize = AtomicUsize::new(0);
pub use self::allocator::{AllocEntry, AllocList, Allocator, ALLOC_LIST_ENTRIES};
#[cfg(feature = "std")]
pub use self::archive::{archive, archive_at};
pub use self::block::{
BlockAddr, BlockData, BlockLevel, BlockList, BlockPtr, BlockRaw, BlockTrait,
};
pub use self::dir::{DirEntry, DirList};
pub use self::disk::*;
pub use self::filesystem::FileSystem;
pub use self::header::Header;
pub use self::node::Node;
pub use self::header::{Header, HEADER_RING};
pub use self::key::{Key, KeySlot, Salt};
#[cfg(feature = "std")]
pub use self::mount::mount;
pub use self::node::{Node, NodeLevel};
pub use self::record::RecordRaw;
pub use self::transaction::Transaction;
pub use self::tree::{Tree, TreeData, TreeList, TreePtr};
#[cfg(feature = "std")]
pub use self::unmount::unmount_path;
mod allocator;
#[cfg(feature = "std")]
mod archive;
mod block;
mod dir;
mod disk;
mod filesystem;
mod header;
mod key;
#[cfg(all(feature = "std", not(fuzzing)))]
mod mount;
#[cfg(all(feature = "std", fuzzing))]
pub mod mount;
mod node;
mod record;
mod transaction;
mod tree;
#[cfg(feature = "std")]
mod unmount;
pub mod disk;
pub mod ex_node;
pub mod extent;
pub mod filesystem;
pub mod header;
pub mod node;
#[cfg(all(feature = "std", test))]
mod tests;
extern crate fuser;
use std::cmp;
use std::ffi::OsStr;
use std::io;
use std::os::unix::ffi::OsStrExt;
use std::path::Path;
use std::time::{SystemTime, UNIX_EPOCH};
use self::fuser::MountOption;
use self::fuser::TimeOrNow;
use crate::mount::fuse::TimeOrNow::Now;
use crate::mount::fuse::TimeOrNow::SpecificTime;
use crate::{filesystem, Disk, Node, Transaction, TreeData, TreePtr, BLOCK_SIZE};
use self::fuser::{
FileAttr, FileType, Filesystem, ReplyAttr, ReplyCreate, ReplyData, ReplyDirectory, ReplyEmpty,
ReplyEntry, ReplyStatfs, ReplyWrite, Request, Session,
};
use std::time::Duration;
const TTL: Duration = Duration::new(1, 0); // 1 second
const NULL_TIME: Duration = Duration::new(0, 0);
pub fn mount<D, P, T, F>(
mut filesystem: filesystem::FileSystem<D>,
mountpoint: P,
callback: F,
) -> io::Result<T>
where
D: Disk,
P: AsRef<Path>,
F: FnOnce(&Path) -> T,
{
let mountpoint = mountpoint.as_ref();
// One of the uses of this redoxfs fuse wrapper is to populate a filesystem
// while building the Redox OS kernel. This means that we need to write on
// a filesystem that belongs to `root`, which in turn means that we need to
// be `root`, thus that we need to allow `root` to have access.
let defer_permissions = [MountOption::CUSTOM("defer_permissions".to_owned())];
let res = {
let mut session = Session::new(
Fuse {
fs: &mut filesystem,
},
mountpoint,
if cfg!(target_os = "macos") {
&defer_permissions
} else {
&[]
},
)?;
let res = callback(mountpoint);
session.run()?;
res
};
// Squash allocations and sync on unmount
let _ = Transaction::new(&mut filesystem).commit(true);
Ok(res)
}
pub struct Fuse<'f, D: Disk> {
pub fs: &'f mut filesystem::FileSystem<D>,
}
fn node_attr(node: &TreeData<Node>) -> FileAttr {
FileAttr {
ino: node.id() as u64,
size: node.data().size(),
// Blocks is in 512 byte blocks, not in our block size
blocks: (node.data().size() + BLOCK_SIZE - 1) / BLOCK_SIZE * (BLOCK_SIZE / 512),
blksize: 512,
atime: SystemTime::UNIX_EPOCH + Duration::new(node.data().atime().0, node.data().atime().1),
mtime: SystemTime::UNIX_EPOCH + Duration::new(node.data().mtime().0, node.data().mtime().1),
ctime: SystemTime::UNIX_EPOCH + Duration::new(node.data().ctime().0, node.data().ctime().1),
crtime: UNIX_EPOCH + NULL_TIME,
kind: if node.data().is_dir() {
FileType::Directory
} else if node.data().is_symlink() {
FileType::Symlink
} else {
FileType::RegularFile
},
perm: node.data().mode() & Node::MODE_PERM,
nlink: node.data().links(),
uid: node.data().uid(),
gid: node.data().gid(),
rdev: 0,
flags: 0,
}
}
impl<'f, D: Disk> Filesystem for Fuse<'f, D> {
fn lookup(&mut self, _req: &Request, parent_id: u64, name: &OsStr, reply: ReplyEntry) {
let parent_ptr = TreePtr::new(parent_id as u32);
match self
.fs
.tx(|tx| tx.find_node(parent_ptr, name.to_str().unwrap()))
{
Ok(node) => {
reply.entry(&TTL, &node_attr(&node), 0);
}
Err(err) => {
reply.error(err.errno);
}
}
}
fn getattr(&mut self, _req: &Request, node_id: u64, reply: ReplyAttr) {
let node_ptr = TreePtr::<Node>::new(node_id as u32);
match self.fs.tx(|tx| tx.read_tree(node_ptr)) {
Ok(node) => {
reply.attr(&TTL, &node_attr(&node));
}
Err(err) => {
reply.error(err.errno);
}
}
}
fn setattr(
&mut self,
_req: &Request,
node_id: u64,
mode: Option<u32>,
uid: Option<u32>,
gid: Option<u32>,
size: Option<u64>,
atime: Option<TimeOrNow>,
mtime: Option<TimeOrNow>,
_ctime: Option<SystemTime>,
_fh: Option<u64>,
_crtime: Option<SystemTime>,
_chgtime: Option<SystemTime>,
_bkuptime: Option<SystemTime>,
_flags: Option<u32>,
reply: ReplyAttr,
) {
let node_ptr = TreePtr::<Node>::new(node_id as u32);
let mut node = match self.fs.tx(|tx| tx.read_tree(node_ptr)) {
Ok(ok) => ok,
Err(err) => {
reply.error(err.errno);
return;
}
};
let mut node_changed = false;
if let Some(mode) = mode {
if node.data().mode() & Node::MODE_PERM != mode as u16 & Node::MODE_PERM {
let new_mode =
(node.data().mode() & Node::MODE_TYPE) | (mode as u16 & Node::MODE_PERM);
node.data_mut().set_mode(new_mode);
node_changed = true;
}
}
if let Some(uid) = uid {
if node.data().uid() != uid {
node.data_mut().set_uid(uid);
node_changed = true;
}
}
if let Some(gid) = gid {
if node.data().gid() != gid {
node.data_mut().set_gid(gid);
node_changed = true;
}
}
if let Some(atime) = atime {
let atime_c = match atime {
SpecificTime(st) => st.duration_since(UNIX_EPOCH).unwrap(),
Now => SystemTime::now().duration_since(UNIX_EPOCH).unwrap(),
};
node.data_mut()
.set_atime(atime_c.as_secs(), atime_c.subsec_nanos());
node_changed = true;
}
if let Some(mtime) = mtime {
let mtime_c = match mtime {
SpecificTime(st) => st.duration_since(UNIX_EPOCH).unwrap(),
Now => SystemTime::now().duration_since(UNIX_EPOCH).unwrap(),
};
node.data_mut()
.set_mtime(mtime_c.as_secs(), mtime_c.subsec_nanos());
node_changed = true;
}
if let Some(size) = size {
match self.fs.tx(|tx| tx.truncate_node_inner(&mut node, size)) {
Ok(ok) => {
if ok {
node_changed = true;
}
}
Err(err) => {
reply.error(err.errno);
return;
}
}
}
let attr = node_attr(&node);
if node_changed {
if let Err(err) = self.fs.tx(|tx| tx.sync_tree(node)) {
reply.error(err.errno);
return;
}
}
reply.attr(&TTL, &attr);
}
fn read(
&mut self,
_req: &Request,
node_id: u64,
_fh: u64,
offset: i64,
size: u32,
_flags: i32,
_lock_owner: Option<u64>,
reply: ReplyData,
) {
let node_ptr = TreePtr::<Node>::new(node_id as u32);
let atime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let mut data = vec![0; size as usize];
match self.fs.tx(|tx| {
tx.read_node(
node_ptr,
cmp::max(0, offset) as u64,
&mut data,
atime.as_secs(),
atime.subsec_nanos(),
)
}) {
Ok(count) => {
reply.data(&data[..count]);
}
Err(err) => {
reply.error(err.errno);
}
}
}
fn write(
&mut self,
_req: &Request,
node_id: u64,
_fh: u64,
offset: i64,
data: &[u8],
_write_flags: u32,
_flags: i32,
_lock_owner: Option<u64>,
reply: ReplyWrite,
) {
let node_ptr = TreePtr::<Node>::new(node_id as u32);
let mtime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
match self.fs.tx(|tx| {
tx.write_node(
node_ptr,
cmp::max(0, offset) as u64,
data,
mtime.as_secs(),
mtime.subsec_nanos(),
)
}) {
Ok(count) => {
reply.written(count as u32);
}
Err(err) => {
reply.error(err.errno);
}
}
}
fn flush(&mut self, _req: &Request, _ino: u64, _fh: u64, _lock_owner: u64, reply: ReplyEmpty) {
reply.ok();
}
fn fsync(&mut self, _req: &Request, _ino: u64, _fh: u64, _datasync: bool, reply: ReplyEmpty) {
reply.ok();
}
fn readdir(
&mut self,
_req: &Request,
parent_id: u64,
_fh: u64,
offset: i64,
mut reply: ReplyDirectory,
) {
let parent_ptr = TreePtr::new(parent_id as u32);
let mut children = Vec::new();
match self.fs.tx(|tx| tx.child_nodes(parent_ptr, &mut children)) {
Ok(()) => {
let mut i;
let skip;
if offset == 0 {
skip = 0;
i = 0;
let _full = reply.add(parent_id, i, FileType::Directory, ".");
i += 1;
let _full = reply.add(
//TODO: get parent?
parent_id,
i,
FileType::Directory,
"..",
);
i += 1;
} else {
i = offset + 1;
skip = offset as usize - 1;
}
for child in children.iter().skip(skip) {
//TODO: make it possible to get file type from directory entry
let node = match self.fs.tx(|tx| tx.read_tree(child.node_ptr())) {
Ok(ok) => ok,
Err(err) => {
reply.error(err.errno);
return;
}
};
let full = reply.add(
child.node_ptr().id() as u64,
i,
if node.data().is_dir() {
FileType::Directory
} else {
FileType::RegularFile
},
child.name().unwrap(),
);
if full {
break;
}
i += 1;
}
reply.ok();
}
Err(err) => {
reply.error(err.errno);
}
}
}
fn create(
&mut self,
_req: &Request,
parent_id: u64,
name: &OsStr,
mode: u32,
_umask: u32,
_flags: i32,
reply: ReplyCreate,
) {
let parent_ptr = TreePtr::<Node>::new(parent_id as u32);
let ctime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
match self.fs.tx(|tx| {
tx.create_node(
parent_ptr,
name.to_str().unwrap(),
Node::MODE_FILE | (mode as u16 & Node::MODE_PERM),
ctime.as_secs(),
ctime.subsec_nanos(),
)
}) {
Ok(node) => {
// println!("Create {:?}:{:o}:{:o}", node.1.name(), node.1.mode, mode);
reply.created(&TTL, &node_attr(&node), 0, 0, 0);
}
Err(error) => {
reply.error(error.errno);
}
}
}
fn mkdir(
&mut self,
_req: &Request,
parent_id: u64,
name: &OsStr,
mode: u32,
_umask: u32,
reply: ReplyEntry,
) {
let parent_ptr = TreePtr::<Node>::new(parent_id as u32);
let ctime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
match self.fs.tx(|tx| {
tx.create_node(
parent_ptr,
name.to_str().unwrap(),
Node::MODE_DIR | (mode as u16 & Node::MODE_PERM),
ctime.as_secs(),
ctime.subsec_nanos(),
)
}) {
Ok(node) => {
// println!("Mkdir {:?}:{:o}:{:o}", node.1.name(), node.1.mode, mode);
reply.entry(&TTL, &node_attr(&node), 0);
}
Err(error) => {
reply.error(error.errno);
}
}
}
fn rmdir(&mut self, _req: &Request, parent_id: u64, name: &OsStr, reply: ReplyEmpty) {
let parent_ptr = TreePtr::<Node>::new(parent_id as u32);
match self
.fs
.tx(|tx| tx.remove_node(parent_ptr, name.to_str().unwrap(), Node::MODE_DIR))
{
Ok(()) => {
reply.ok();
}
Err(err) => {
reply.error(err.errno);
}
}
}
fn unlink(&mut self, _req: &Request, parent_id: u64, name: &OsStr, reply: ReplyEmpty) {
let parent_ptr = TreePtr::<Node>::new(parent_id as u32);
match self
.fs
.tx(|tx| tx.remove_node(parent_ptr, name.to_str().unwrap(), Node::MODE_FILE))
{
Ok(()) => {
reply.ok();
}
Err(err) => {
reply.error(err.errno);
}
}
}
fn statfs(&mut self, _req: &Request, _ino: u64, reply: ReplyStatfs) {
let bsize = BLOCK_SIZE;
let blocks = self.fs.header.size() / bsize;
let bfree = self.fs.allocator().free();
reply.statfs(blocks, bfree, bfree, 0, 0, bsize as u32, 256, 0);
}
fn symlink(
&mut self,
_req: &Request,
parent_id: u64,
name: &OsStr,
link: &Path,
reply: ReplyEntry,
) {
let parent_ptr = TreePtr::<Node>::new(parent_id as u32);
let ctime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
match self.fs.tx(|tx| {
let node = tx.create_node(
parent_ptr,
name.to_str().unwrap(),
Node::MODE_SYMLINK | 0o777,
ctime.as_secs(),
ctime.subsec_nanos(),
)?;
let mtime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
tx.write_node(
node.ptr(),
0,
link.as_os_str().as_bytes(),
mtime.as_secs(),
mtime.subsec_nanos(),
)?;
Ok(node)
}) {
Ok(node) => {
reply.entry(&TTL, &node_attr(&node), 0);
}
Err(error) => {
reply.error(error.errno);
}
}
}
fn readlink(&mut self, _req: &Request, node_id: u64, reply: ReplyData) {
let node_ptr = TreePtr::<Node>::new(node_id as u32);
let atime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let mut data = vec![0; 4096];
match self.fs.tx(|tx| {
tx.read_node(
node_ptr,
0,
&mut data,
atime.as_secs(),
atime.subsec_nanos(),
)
}) {
Ok(count) => {
reply.data(&data[..count]);
}
Err(err) => {
reply.error(err.errno);
}
}
}
fn rename(
&mut self,
_req: &Request,
orig_parent: u64,
orig_name: &OsStr,
new_parent: u64,
new_name: &OsStr,
_flags: u32,
reply: ReplyEmpty,
) {
let orig_parent_ptr = TreePtr::<Node>::new(orig_parent as u32);
let orig_name = orig_name.to_str().expect("name is not utf-8");
let new_parent_ptr = TreePtr::<Node>::new(new_parent as u32);
let new_name = new_name.to_str().expect("name is not utf-8");
// TODO: improve performance
match self
.fs
.tx(|tx| tx.rename_node(orig_parent_ptr, orig_name, new_parent_ptr, new_name))
{
Ok(()) => reply.ok(),
Err(err) => reply.error(err.errno),
}
}
}