Merge branch 'v8' into 'master'

v8

See merge request redox-os/redoxfs!95
This commit is contained in:
Jeremy Soller
2025-09-26 15:44:29 -06:00
17 changed files with 1031 additions and 319 deletions
Generated
+8
View File
@@ -357,6 +357,12 @@ version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "lz4_flex"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08ab2867e3eeeca90e844d1940eab391c9dc5228783db2ed999acbc0a9ed375a"
[[package]]
name = "memchr"
version = "2.7.5"
@@ -514,6 +520,7 @@ dependencies = [
"argon2",
"assert_cmd",
"base64ct",
"bitflags",
"endian-num",
"env_logger",
"fuser",
@@ -521,6 +528,7 @@ dependencies = [
"libc",
"libredox",
"log",
"lz4_flex",
"range-tree",
"redox-path",
"redox-scheme",
+9 -1
View File
@@ -24,6 +24,12 @@ path = "src/bin/ar.rs"
doc = false
required-features = ["std"]
[[bin]]
name = "redoxfs-clone"
path = "src/bin/clone.rs"
doc = false
required-features = ["std"]
[[bin]]
name = "redoxfs-mkfs"
path = "src/bin/mkfs.rs"
@@ -34,16 +40,18 @@ required-features = ["std"]
aes = { version = "0.8", default-features = false }
argon2 = { version = "0.4", default-features = false, features = ["alloc"] }
base64ct = { version = "1", default-features = false }
bitflags = "2"
endian-num = "0.1"
env_logger = { version = "0.11", optional = true }
getrandom = { version = "0.2.5", optional = true }
libc = "0.2"
libredox = { version = "0.1.6", features = ["call"], optional = true }
log = { version = "0.4.14", default-features = false, optional = true }
lz4_flex = { version = "0.11", default-features = false, features = ["checked-decode"] }
range-tree = { version = "0.1", optional = true }
redox-path = "0.3.0"
redox-scheme = { version = "0.7.0", optional = true }
redox_syscall = { version = "0.5.15", optional = true }
redox_syscall = "0.5.15"
seahash = { version = "4.1.0", default-features = false }
termion = { version = "4", optional = true }
uuid = { version = "1.4", default-features = false }
+40 -19
View File
@@ -4,14 +4,13 @@ version = 4
[[package]]
name = "aes"
version = "0.7.5"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
"opaque-debug",
]
[[package]]
@@ -159,11 +158,12 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "cipher"
version = "0.3.0"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"generic-array",
"crypto-common",
"inout",
]
[[package]]
@@ -303,6 +303,15 @@ dependencies = [
"wasi",
]
[[package]]
name = "inout"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
dependencies = [
"generic-array",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
@@ -361,9 +370,9 @@ dependencies = [
[[package]]
name = "libredox"
version = "0.1.3"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3"
dependencies = [
"bitflags",
"libc",
@@ -382,6 +391,12 @@ version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "lz4_flex"
version = "0.11.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08ab2867e3eeeca90e844d1940eab391c9dc5228783db2ed999acbc0a9ed375a"
[[package]]
name = "memchr"
version = "2.7.4"
@@ -418,12 +433,6 @@ version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "opaque-debug"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
[[package]]
name = "page_size"
version = "0.6.0"
@@ -493,9 +502,9 @@ checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717"
[[package]]
name = "redox-scheme"
version = "0.2.4"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6143c4d307e1c99ac14f60b5b07b2dccaf9d17137f7cee4e4e29977dd8014a1b"
checksum = "4da6a0251965958189cdfd5ebb66f99754db4aa165394300aa2b958525d94b64"
dependencies = [
"libredox",
"redox_syscall",
@@ -503,9 +512,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.5.11"
version = "0.5.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3"
checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
dependencies = [
"bitflags",
]
@@ -518,7 +527,7 @@ checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb"
[[package]]
name = "redoxfs"
version = "0.6.10"
version = "0.7.1"
dependencies = [
"aes",
"argon2",
@@ -530,6 +539,7 @@ dependencies = [
"libc",
"libredox",
"log",
"lz4_flex",
"range-tree",
"redox-path",
"redox-scheme",
@@ -538,6 +548,7 @@ dependencies = [
"termion",
"time",
"uuid",
"xts-mode",
]
[[package]]
@@ -830,6 +841,16 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "xts-mode"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cbddb7545ca0b9ffa7bdc653e8743303e1712687a6918ced25f2cdbed42520"
dependencies = [
"byteorder",
"cipher",
]
[[package]]
name = "zerocopy"
version = "0.7.35"
+17 -17
View File
@@ -2,7 +2,7 @@ use alloc::vec::Vec;
use core::{fmt, mem, ops, slice};
use endian_num::Le;
use crate::{BlockAddr, BlockLevel, BlockPtr, BlockTrait, BLOCK_SIZE};
use crate::{BlockAddr, BlockLevel, BlockMeta, BlockPtr, BlockTrait, BLOCK_SIZE};
pub const ALLOC_LIST_ENTRIES: usize =
(BLOCK_SIZE as usize - mem::size_of::<BlockPtr<AllocList>>()) / mem::size_of::<AllocEntry>();
@@ -52,10 +52,10 @@ impl Allocator {
/// 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> {
pub fn allocate(&mut self, meta: BlockMeta) -> Option<BlockAddr> {
// First, find the lowest level with a free block
let mut index_opt = None;
let mut level = block_level.0;
let mut level = meta.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() {
@@ -68,13 +68,13 @@ impl Allocator {
// 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 {
while level > meta.level.0 {
level -= 1;
let level_size = 1 << level;
self.levels[level].push(index + level_size);
}
Some(unsafe { BlockAddr::new(index, block_level) })
Some(unsafe { BlockAddr::new(index, meta) })
}
/// Try to allocate the exact block specified, making all necessary splits.
@@ -112,7 +112,7 @@ impl Allocator {
}
}
Some(unsafe { BlockAddr::new(index_opt?, exact_addr.level()) })
Some(unsafe { BlockAddr::new(index_opt?, exact_addr.meta()) })
}
/// Deallocate the given block, marking it "free" so that it can be re-used later.
@@ -191,11 +191,11 @@ impl AllocEntry {
}
pub fn allocate(addr: BlockAddr) -> Self {
Self::new(addr.index(), -addr.level().blocks())
Self::new(addr.index(), -addr.level().blocks::<i64>())
}
pub fn deallocate(addr: BlockAddr) -> Self {
Self::new(addr.index(), addr.level().blocks())
Self::new(addr.index(), addr.level().blocks::<i64>())
}
pub fn index(&self) -> u64 {
@@ -282,17 +282,17 @@ fn alloc_node_size_test() {
fn allocator_test() {
let mut alloc = Allocator::default();
assert_eq!(alloc.allocate(BlockLevel::default()), None);
assert_eq!(alloc.allocate(BlockMeta::default()), None);
alloc.deallocate(unsafe { BlockAddr::new(1, BlockLevel::default()) });
alloc.deallocate(unsafe { BlockAddr::new(1, BlockMeta::default()) });
assert_eq!(
alloc.allocate(BlockLevel::default()),
Some(unsafe { BlockAddr::new(1, BlockLevel::default()) })
alloc.allocate(BlockMeta::default()),
Some(unsafe { BlockAddr::new(1, BlockMeta::default()) })
);
assert_eq!(alloc.allocate(BlockLevel::default()), None);
assert_eq!(alloc.allocate(BlockMeta::default()), None);
for addr in 1023..2048 {
alloc.deallocate(unsafe { BlockAddr::new(addr, BlockLevel::default()) });
alloc.deallocate(unsafe { BlockAddr::new(addr, BlockMeta::default()) });
}
assert_eq!(alloc.levels.len(), 11);
@@ -308,11 +308,11 @@ fn allocator_test() {
for addr in 1023..2048 {
assert_eq!(
alloc.allocate(BlockLevel::default()),
Some(unsafe { BlockAddr::new(addr, BlockLevel::default()) })
alloc.allocate(BlockMeta::default()),
Some(unsafe { BlockAddr::new(addr, BlockMeta::default()) })
);
}
assert_eq!(alloc.allocate(BlockLevel::default()), None);
assert_eq!(alloc.allocate(BlockMeta::default()), None);
assert_eq!(alloc.levels.len(), 11);
for level in 0..alloc.levels.len() {
+153
View File
@@ -0,0 +1,153 @@
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::{clone, DiskFile, FileSystem};
use uuid::Uuid;
fn main() {
env_logger::init();
let mut args = env::args().skip(1);
let disk_path_old = if let Some(path) = args.next() {
path
} else {
println!("redoxfs-clone: no old disk image provided");
println!("redoxfs-clone NEW-DISK OLD-DISK [BOOTLOADER]");
process::exit(1);
};
let disk_path = if let Some(path) = args.next() {
path
} else {
println!("redoxfs-clone: no new disk image provided");
println!("redoxfs-clone NEW-DISK OLD-DISK [BOOTLOADER]");
process::exit(1);
};
let bootloader_path_opt = args.next();
// Open old disk in readonly mode
let disk_old = match fs::OpenOptions::new()
.read(true)
.write(false)
.open(&disk_path_old)
.map(DiskFile::from)
{
Ok(disk) => disk,
Err(err) => {
println!(
"redoxfs-clone: failed to open old disk image {}: {}",
disk_path_old, err
);
process::exit(1);
}
};
let mut fs_old = match FileSystem::open(disk_old, None, None, false) {
Ok(fs) => fs,
Err(err) => {
println!(
"redoxfs-clone: failed to open filesystem on {}: {}",
disk_path_old, err
);
process::exit(1);
}
};
let disk = match DiskFile::open(&disk_path) {
Ok(disk) => disk,
Err(err) => {
println!(
"redoxfs-clone: failed to open new disk 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-clone: failed to read bootloader {}: {}",
bootloader_path, err
);
process::exit(1);
}
},
Err(err) => {
println!(
"redoxfs-clone: failed to open bootloader {}: {}",
bootloader_path, err
);
process::exit(1);
}
}
};
let ctime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let mut fs = match FileSystem::create_reserved(
disk,
None,
&bootloader,
ctime.as_secs(),
ctime.subsec_nanos(),
) {
Ok(fs) => fs,
Err(err) => {
println!(
"redoxfs-clone: failed to create filesystem on {}: {}",
disk_path, err
);
process::exit(1);
}
};
let size_old = fs_old.header.size();
let free_old = fs_old.allocator().free() * redoxfs::BLOCK_SIZE;
let used_old = size_old - free_old;
let mut last_percent = 0;
let clone_res = clone(&mut fs_old, &mut fs, move |used| {
let percent = (used * 100) / used_old;
if percent != last_percent {
eprint!(
"\r{}%: {} MB/{} MB",
percent,
used / 1000 / 1000,
used_old / 1000 / 1000
);
last_percent = percent;
}
});
eprintln!();
match clone_res {
Ok(()) => (),
Err(err) => {
println!(
"redoxfs-clone: failed to clone {} to {}: {}",
disk_path_old, disk_path, err
);
process::exit(1);
}
}
let uuid = Uuid::from_bytes(fs.header.uuid());
let size = fs.header.size();
let free = fs.allocator().free() * redoxfs::BLOCK_SIZE;
let used = size - free;
println!("redoxfs-clone: created filesystem on {}", disk_path,);
println!("\treserved: {} blocks", fs.block);
println!("\tuuid: {}", uuid.hyphenated());
println!("\tsize: {} MB", size / 1000 / 1000);
println!("\tused: {} MB", used / 1000 / 1000);
println!("\tfree: {} MB", free / 1000 / 1000);
}
+67 -16
View File
@@ -9,38 +9,67 @@ const BLOCK_LIST_ENTRIES: usize = BLOCK_SIZE as usize / mem::size_of::<BlockPtr<
///
/// This encodes a block's position _and_ [`BlockLevel`]:
/// the first four bits of this `u64` encode the block's level,
/// the next four bits indicates decompression level,
/// the rest encode its index.
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct BlockAddr(u64);
impl BlockAddr {
const INDEX_SHIFT: u64 = 8;
const DECOMP_LEVEL_MASK: u64 = 0xF0;
const DECOMP_LEVEL_SHIFT: u64 = 4;
const LEVEL_MASK: u64 = 0xF;
// 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");
pub(crate) unsafe fn new(index: u64, meta: BlockMeta) -> Self {
// Level must fit within LEVEL_MASK
if meta.level.0 > Self::LEVEL_MASK as usize {
panic!("block level too large");
}
// Index must not use the highest four bits
// Decomp level must fit within DECOMP_LEVEL_MASK
let decomp_level = meta.decomp_level.unwrap_or_default();
if (decomp_level.0 << Self::DECOMP_LEVEL_SHIFT) > Self::DECOMP_LEVEL_MASK as usize {
panic!("decompressed block level too large");
}
// Index must not use the metadata bits
let inner = index
.checked_shl(4)
.expect("block index used highest four bits")
| (level.0 as u64);
.checked_shl(Self::INDEX_SHIFT as u32)
.expect("block index too large")
| ((decomp_level.0 as u64) << Self::DECOMP_LEVEL_SHIFT)
| (meta.level.0 as u64);
Self(inner)
}
pub fn null(level: BlockLevel) -> Self {
unsafe { Self::new(0, level) }
pub fn null(meta: BlockMeta) -> Self {
unsafe { Self::new(0, meta) }
}
pub fn index(&self) -> u64 {
// The first four bits store the level
self.0 >> 4
self.0 >> Self::INDEX_SHIFT
}
pub fn level(&self) -> BlockLevel {
// The first four bits store the level
BlockLevel((self.0 & 0xF) as usize)
BlockLevel((self.0 & Self::LEVEL_MASK) as usize)
}
pub fn decomp_level(&self) -> Option<BlockLevel> {
let value = (self.0 & Self::DECOMP_LEVEL_MASK) >> Self::DECOMP_LEVEL_SHIFT;
if value != 0 {
Some(BlockLevel(value as usize))
} else {
None
}
}
pub fn meta(&self) -> BlockMeta {
BlockMeta {
level: self.level(),
decomp_level: self.decomp_level(),
}
}
pub fn is_null(&self) -> bool {
@@ -48,6 +77,28 @@ impl BlockAddr {
}
}
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
pub struct BlockMeta {
pub(crate) level: BlockLevel,
pub(crate) decomp_level: Option<BlockLevel>,
}
impl BlockMeta {
pub fn new(level: BlockLevel) -> Self {
Self {
level,
decomp_level: None,
}
}
pub fn new_compressed(level: BlockLevel, decomp_level: BlockLevel) -> Self {
Self {
level,
decomp_level: Some(decomp_level),
}
}
}
/// The size of a block.
///
/// Level 0 blocks are blocks of [`BLOCK_SIZE`] bytes.
@@ -74,8 +125,8 @@ impl BlockLevel {
/// 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
pub fn blocks<T: From<u32>>(self) -> T {
T::from(1u32 << self.0)
}
/// The number of bytes in a block of this level
@@ -211,9 +262,9 @@ pub struct BlockPtr<T> {
}
impl<T> BlockPtr<T> {
pub fn null(level: BlockLevel) -> Self {
pub fn null(meta: BlockMeta) -> Self {
Self {
addr: BlockAddr::null(level).0.into(),
addr: BlockAddr::null(meta).0.into(),
hash: 0.into(),
phantom: PhantomData,
}
+99
View File
@@ -0,0 +1,99 @@
use std::fs;
use std::io;
use std::os::unix::ffi::OsStrExt;
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)
}
fn tx_progress<D: Disk, F: FnMut(u64)>(tx: &mut Transaction<D>, progress: &mut F) {
let size = tx.header.size();
let free = tx.allocator.free() * BLOCK_SIZE;
progress(size - free);
}
//TODO: handle hard links
fn clone_at<D: Disk, E: Disk, F: FnMut(u64)>(
tx_old: &mut Transaction<D>,
parent_ptr_old: TreePtr<Node>,
tx: &mut Transaction<E>,
parent_ptr: TreePtr<Node>,
buf: &mut [u8],
progress: &mut F,
) -> syscall::Result<()> {
let mut entries = Vec::new();
tx_old.child_nodes(parent_ptr_old, &mut entries)?;
for entry in entries {
//TODO: return error instead?
let Some(name) = entry.name() else {
continue;
};
let node_ptr_old = entry.node_ptr();
let node_old = tx_old.read_tree(node_ptr_old)?;
//TODO: this slows down the clone, but Redox has issues without this (Linux is fine)
if tx.write_cache.len() > 64 {
tx.sync(false)?;
}
let node_ptr = {
let mode = node_old.data().mode();
let (ctime, ctime_nsec) = node_old.data().ctime();
let (mtime, mtime_nsec) = node_old.data().mtime();
let mut node = tx.create_node(parent_ptr, &name, mode, ctime, ctime_nsec)?;
node.data_mut().set_uid(node_old.data().uid());
node.data_mut().set_gid(node_old.data().gid());
node.data_mut().set_mtime(mtime, mtime_nsec);
if !node_old.data().is_dir() {
let mut offset = 0;
loop {
let count = tx_old.read_node_inner(&node_old, offset, buf)?;
if count == 0 {
break;
}
tx.write_node_inner(&mut node, &mut offset, &buf[..count])?;
}
}
let node_ptr = node.ptr();
tx.sync_tree(node)?;
node_ptr
};
tx_progress(tx, progress);
if node_old.data().is_dir() {
clone_at(tx_old, node_ptr_old, tx, node_ptr, buf, progress)?;
}
}
Ok(())
}
pub fn clone<D: Disk, E: Disk, F: FnMut(u64)>(
fs_old: &mut FileSystem<D>,
fs: &mut FileSystem<E>,
mut progress: F,
) -> syscall::Result<()> {
fs_old.tx(|tx_old| {
let mut tx = Transaction::new(fs);
// Clone at root node
let mut buf = vec![0; 4 * 1024 * 1024];
clone_at(
tx_old,
TreePtr::root(),
&mut tx,
TreePtr::root(),
&mut buf,
&mut progress,
)?;
// Commit and squash alloc log
tx.commit(true)
})
}
+6
View File
@@ -76,3 +76,9 @@ impl Disk for DiskFile {
self.file.seek(SeekFrom::End(0)).or_eio()
}
}
impl From<File> for DiskFile {
fn from(file: File) -> Self {
Self { file }
}
}
+17 -7
View File
@@ -1,11 +1,18 @@
use aes::Aes128;
use alloc::collections::VecDeque;
use alloc::{boxed::Box, collections::VecDeque, vec};
use syscall::error::{Error, Result, EKEYREJECTED, ENOENT, ENOKEY};
use xts_mode::{get_tweak_default, Xts128};
#[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};
use crate::{
Allocator, BlockAddr, BlockLevel, BlockMeta, Disk, Header, Transaction, BLOCK_SIZE,
HEADER_RING, RECORD_SIZE,
};
fn compress_cache() -> Box<[u8]> {
vec![0; lz4_flex::block::get_maximum_output_size(RECORD_SIZE as usize)].into_boxed_slice()
}
/// A file system
pub struct FileSystem<D: Disk> {
@@ -17,6 +24,7 @@ pub struct FileSystem<D: Disk> {
pub header: Header,
pub(crate) allocator: Allocator,
pub(crate) cipher_opt: Option<Xts128<Aes128>>,
pub(crate) compress_cache: Box<[u8]>,
}
impl<D: Disk> FileSystem<D> {
@@ -81,6 +89,7 @@ impl<D: Disk> FileSystem<D> {
header,
allocator: Allocator::default(),
cipher_opt,
compress_cache: compress_cache(),
};
unsafe { fs.reset_allocator()? };
@@ -160,6 +169,7 @@ impl<D: Disk> FileSystem<D> {
header,
allocator: Allocator::default(),
cipher_opt,
compress_cache: compress_cache(),
};
// Write header generation zero
@@ -174,12 +184,12 @@ impl<D: Disk> FileSystem<D> {
// 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()),
BlockAddr::new(HEADER_RING + 1, BlockMeta::default()),
TreeList::empty(BlockLevel::default()).unwrap(),
);
let mut alloc = BlockData::new(
BlockAddr::new(HEADER_RING + 2, BlockLevel::default()),
BlockAddr::new(HEADER_RING + 2, BlockMeta::default()),
AllocList::empty(BlockLevel::default()).unwrap(),
);
@@ -199,7 +209,7 @@ impl<D: Disk> FileSystem<D> {
fs.tx(|tx| unsafe {
let mut root = BlockData::new(
BlockAddr::new(HEADER_RING + 3, BlockLevel::default()),
BlockAddr::new(HEADER_RING + 3, BlockMeta::default()),
Node::new(Node::MODE_DIR | 0o755, 0, 0, ctime, ctime_nsec),
);
root.data_mut().set_links(1);
@@ -254,12 +264,12 @@ impl<D: Disk> FileSystem<D> {
if count < 0 {
for i in 0..-count {
//TODO: replace assert with error?
let addr = BlockAddr::new(index + i as u64, BlockLevel::default());
let addr = BlockAddr::new(index + i as u64, BlockMeta::default());
assert_eq!(self.allocator.allocate_exact(addr), Some(addr));
}
} else {
for i in 0..count {
let addr = BlockAddr::new(index + i as u64, BlockLevel::default());
let addr = BlockAddr::new(index + i as u64, BlockMeta::default());
self.allocator.deallocate(addr);
}
}
+7 -3
View File
@@ -16,7 +16,7 @@ pub const BLOCK_SIZE: u64 = 4096;
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 = 7;
pub const VERSION: u64 = 8;
pub const DIR_ENTRY_MAX_LENGTH: usize = 252;
pub static IS_UMT: AtomicUsize = AtomicUsize::new(0);
@@ -25,8 +25,10 @@ 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,
BlockAddr, BlockData, BlockLevel, BlockList, BlockMeta, BlockPtr, BlockRaw, BlockTrait,
};
#[cfg(feature = "std")]
pub use self::clone::clone;
pub use self::dir::{DirEntry, DirList};
pub use self::disk::*;
pub use self::filesystem::FileSystem;
@@ -34,7 +36,7 @@ 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::node::{Node, NodeFlags, NodeLevel, NodeLevelData};
pub use self::record::RecordRaw;
pub use self::transaction::Transaction;
pub use self::tree::{Tree, TreeData, TreeList, TreePtr};
@@ -45,6 +47,8 @@ mod allocator;
#[cfg(feature = "std")]
mod archive;
mod block;
#[cfg(feature = "std")]
mod clone;
mod dir;
mod disk;
mod filesystem;
+1 -1
View File
@@ -77,7 +77,7 @@ fn node_attr(node: &TreeData<Node>) -> 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().div_ceil(BLOCK_SIZE) * (BLOCK_SIZE / 512),
blocks: node.data().blocks() * (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),
+4 -2
View File
@@ -14,7 +14,7 @@ use syscall::flag::{
};
use syscall::{EBADFD, PAGE_SIZE};
use crate::{Disk, Node, Transaction, TreePtr};
use crate::{Disk, Node, Transaction, TreePtr, BLOCK_SIZE};
pub type Fmaps = BTreeMap<u32, FileMmapInfo>;
@@ -117,13 +117,15 @@ pub trait Resource<D: Disk> {
st_uid: node.data().uid(),
st_gid: node.data().gid(),
st_size: node.data().size(),
st_blksize: 512,
// Blocks is in 512 byte blocks, not in our block size
st_blocks: node.data().blocks() * (BLOCK_SIZE / 512),
st_mtime: mtime.0,
st_mtime_nsec: mtime.1,
st_atime: atime.0,
st_atime_nsec: atime.1,
st_ctime: ctime.0,
st_ctime_nsec: ctime.1,
..Default::default()
};
Ok(())
+180 -43
View File
@@ -3,6 +3,12 @@ use endian_num::Le;
use crate::{BlockLevel, BlockList, BlockPtr, BlockTrait, RecordRaw, BLOCK_SIZE, RECORD_LEVEL};
bitflags::bitflags! {
pub struct NodeFlags: u32 {
const INLINE_DATA = 0x1;
}
}
/// An index into a [`Node`]'s block table.
pub enum NodeLevel {
L0(usize),
@@ -85,40 +91,8 @@ type BlockListL2 = BlockList<BlockListL1>;
type BlockListL3 = BlockList<BlockListL2>;
type BlockListL4 = BlockList<BlockListL3>;
/// A file/folder node
#[repr(C, packed)]
pub struct Node {
/// This node's type & permissions.
/// - four most significant bits are the node's type
/// - next four bits are permissions for the node's user
/// - next four bits are permissions for the node's group
/// - four least significant bits are permissions for everyone else
pub mode: Le<u16>,
/// The uid that owns this file
pub uid: Le<u32>,
/// The gid that owns this file
pub gid: Le<u32>,
/// The number of links to this file
/// (directory entries, symlinks, etc)
pub links: Le<u32>,
/// The length of this file, in bytes
pub size: Le<u64>,
pub ctime: Le<u64>,
pub ctime_nsec: Le<u32>,
pub mtime: Le<u64>,
pub mtime_nsec: Le<u32>,
pub atime: Le<u64>,
pub atime_nsec: Le<u32>,
pub record_level: Le<u32>,
pub padding: [u8; BLOCK_SIZE as usize - 4094],
pub struct NodeLevelData {
/// The first 128 blocks of this file.
///
/// Total size: 128 * RECORD_SIZE (16 MiB, 128 KiB each)
@@ -143,11 +117,72 @@ pub struct Node {
/// Total size: 16 * 256 * 256 * 256 * RECORD_SIZE (32 TiB, 2 TiB each)
pub level3: [BlockPtr<BlockListL3>; 16],
/// The next 12 * 256 * 256 * 256 * 256 blocks of this file,
/// stored behind 12 level four tables.
/// The next 8 * 256 * 256 * 256 * 256 blocks of this file,
/// stored behind 8 level four tables.
///
/// Total size: 12 * 256 * 256 * 256 * 256 * RECORD_SIZE (6 PiB, 512 TiB each)
pub level4: [BlockPtr<BlockListL4>; 12],
/// Total size: 8 * 256 * 256 * 256 * 256 * RECORD_SIZE (4 PiB, 512 TiB each)
pub level4: [BlockPtr<BlockListL4>; 8],
}
impl Default for NodeLevelData {
fn default() -> Self {
Self {
level0: [BlockPtr::default(); 128],
level1: [BlockPtr::default(); 64],
level2: [BlockPtr::default(); 32],
level3: [BlockPtr::default(); 16],
level4: [BlockPtr::default(); 8],
}
}
}
/// A file/folder node
#[repr(C, packed)]
pub struct Node {
/// This node's type & permissions.
/// - four most significant bits are the node's type
/// - next four bits are permissions for the node's user
/// - next four bits are permissions for the node's group
/// - four least significant bits are permissions for everyone else
pub mode: Le<u16>,
/// The uid that owns this file
pub uid: Le<u32>,
/// The gid that owns this file
pub gid: Le<u32>,
/// The number of hard links to this file
pub links: Le<u32>,
/// The length of this file, in bytes
pub size: Le<u64>,
/// The disk usage of this file, in blocks
pub blocks: Le<u64>,
/// Creation time
pub ctime: Le<u64>,
pub ctime_nsec: Le<u32>,
/// Modification time
pub mtime: Le<u64>,
pub mtime_nsec: Le<u32>,
/// Access time
pub atime: Le<u64>,
pub atime_nsec: Le<u32>,
/// Record level
pub record_level: Le<u32>,
/// Flags
pub flags: Le<u32>,
/// Padding
pub padding: [u8; BLOCK_SIZE as usize - 4042],
/// Level data, should not be used directly so inline data can be supported
pub(crate) level_data: NodeLevelData,
}
unsafe impl BlockTrait for Node {
@@ -168,6 +203,8 @@ impl Default for Node {
gid: 0.into(),
links: 0.into(),
size: 0.into(),
// This node counts as a block
blocks: 1.into(),
ctime: 0.into(),
ctime_nsec: 0.into(),
mtime: 0.into(),
@@ -175,12 +212,9 @@ impl Default for Node {
atime: 0.into(),
atime_nsec: 0.into(),
record_level: 0.into(),
padding: [0; BLOCK_SIZE as usize - 4094],
level0: [BlockPtr::default(); 128],
level1: [BlockPtr::default(); 64],
level2: [BlockPtr::default(); 32],
level3: [BlockPtr::default(); 16],
level4: [BlockPtr::default(); 12],
flags: 0.into(),
padding: [0; BLOCK_SIZE as usize - 4042],
level_data: NodeLevelData::default(),
}
}
}
@@ -219,6 +253,14 @@ impl Node {
0
}
.into(),
flags: if mode & Self::MODE_TYPE == Self::MODE_DIR {
// Directories must not use inline data (until h-tree supports it)
NodeFlags::empty()
} else {
NodeFlags::INLINE_DATA
}
.bits()
.into(),
..Default::default()
}
}
@@ -253,6 +295,11 @@ impl Node {
self.size.to_ne()
}
/// The disk usage of this file, in blocks.
pub fn blocks(&self) -> u64 {
self.blocks.to_ne()
}
pub fn ctime(&self) -> (u64, u32) {
(self.ctime.to_ne(), self.ctime_nsec.to_ne())
}
@@ -269,6 +316,10 @@ impl Node {
BlockLevel(self.record_level.to_ne() as usize)
}
pub fn flags(&self) -> NodeFlags {
NodeFlags::from_bits_retain(self.flags.to_ne())
}
pub fn set_mode(&mut self, mode: u16) {
self.mode = mode.into();
}
@@ -289,6 +340,10 @@ impl Node {
self.size = size.into();
}
pub fn set_blocks(&mut self, blocks: u64) {
self.blocks = blocks.into();
}
pub fn set_mtime(&mut self, mtime: u64, mtime_nsec: u32) {
self.mtime = mtime.into();
self.mtime_nsec = mtime_nsec.into();
@@ -299,6 +354,56 @@ impl Node {
self.atime_nsec = atime_nsec.into();
}
pub fn set_flags(&mut self, flags: NodeFlags) {
self.flags = flags.bits().into();
}
pub fn has_inline_data(&self) -> bool {
self.flags().contains(NodeFlags::INLINE_DATA)
}
pub fn inline_data(&self) -> Option<&[u8]> {
if self.has_inline_data() {
Some(unsafe {
slice::from_raw_parts(
&self.level_data as *const NodeLevelData as *const u8,
mem::size_of::<NodeLevelData>(),
)
})
} else {
None
}
}
pub fn inline_data_mut(&mut self) -> Option<&mut [u8]> {
if self.has_inline_data() {
Some(unsafe {
slice::from_raw_parts_mut(
&mut self.level_data as *mut NodeLevelData as *mut u8,
mem::size_of::<NodeLevelData>(),
)
})
} else {
None
}
}
pub fn level_data(&self) -> Option<&NodeLevelData> {
if !self.has_inline_data() {
Some(&self.level_data)
} else {
None
}
}
pub fn level_data_mut(&mut self) -> Option<&mut NodeLevelData> {
if !self.has_inline_data() {
Some(&mut self.level_data)
} else {
None
}
}
pub fn is_dir(&self) -> bool {
self.mode() & Self::MODE_TYPE == Self::MODE_DIR
}
@@ -348,6 +453,7 @@ impl fmt::Debug for Node {
let gid = self.gid;
let links = self.links;
let size = self.size;
let blocks = self.blocks;
let ctime = self.ctime;
let ctime_nsec = self.ctime_nsec;
let mtime = self.mtime;
@@ -360,6 +466,7 @@ impl fmt::Debug for Node {
.field("gid", &gid)
.field("links", &links)
.field("size", &size)
.field("blocks", &blocks)
.field("ctime", &ctime)
.field("ctime_nsec", &ctime_nsec)
.field("mtime", &mtime)
@@ -395,6 +502,36 @@ fn node_size_test() {
assert_eq!(mem::size_of::<Node>(), crate::BLOCK_SIZE as usize);
}
#[test]
fn node_inline_data_test() {
let mut node = Node::default();
assert!(!node.has_inline_data());
assert!(node.inline_data().is_none());
assert!(node.inline_data_mut().is_none());
assert!(node.level_data().is_some());
assert!(node.level_data_mut().is_some());
node.set_flags(NodeFlags::INLINE_DATA);
assert!(node.has_inline_data());
assert!(node.level_data().is_none());
assert!(node.level_data_mut().is_none());
let node_addr = &node as *const Node as usize;
let meta_size = 128;
{
let inline_data = node.inline_data().unwrap();
let inline_data_addr = inline_data.as_ptr() as usize;
assert_eq!(node_addr + meta_size, inline_data_addr);
assert_eq!(inline_data.len(), (crate::BLOCK_SIZE as usize) - meta_size);
}
{
let inline_data = node.inline_data_mut().unwrap();
let inline_data_addr = inline_data.as_ptr() as usize;
assert_eq!(node_addr + meta_size, inline_data_addr);
assert_eq!(inline_data.len(), (crate::BLOCK_SIZE as usize) - meta_size);
}
}
#[cfg(kani)]
#[kani::proof]
fn check_node_level() {
+1 -1
View File
@@ -5,7 +5,7 @@ use crate::{BlockLevel, BlockTrait, RECORD_LEVEL};
//TODO: this is a box to prevent stack overflows
#[derive(Clone)]
pub struct RecordRaw(Box<[u8]>);
pub struct RecordRaw(pub(crate) Box<[u8]>);
unsafe impl BlockTrait for RecordRaw {
fn empty(level: BlockLevel) -> Option<Self> {
+58 -55
View File
@@ -1,6 +1,7 @@
use crate::htree::{HTreeHash, HTreeNode, HTreePtr, HTREE_IDX_ENTRIES};
use crate::{
BlockAddr, BlockData, BlockLevel, BlockPtr, DirEntry, DirList, DiskMemory, DiskSparse,
transaction::{level_data, level_data_mut, FsCtx},
BlockAddr, BlockData, BlockMeta, BlockPtr, DirEntry, DirList, DiskMemory, DiskSparse,
FileSystem, Node, TreePtr, ALLOC_GC_THRESHOLD, BLOCK_SIZE,
};
use std::sync::atomic::AtomicUsize;
@@ -260,7 +261,7 @@ fn create_minimal_l2_htree(
let mut parent = tx.read_tree(parent_ptr).unwrap();
let child1_block_data = BlockData::new(
unsafe { tx.allocate(BlockLevel::default()) }.unwrap(),
unsafe { tx.allocate(&mut FsCtx, BlockMeta::default()) }.unwrap(),
Node::new(
Node::MODE_FILE,
parent.data().uid(),
@@ -276,20 +277,20 @@ fn create_minimal_l2_htree(
let mut dir_list = BlockData::<DirList>::empty(BlockAddr::default()).unwrap();
dir_list.data_mut().append(&child1_dir_entry);
let dir_ptr = tx.sync_block(dir_list).unwrap();
let dir_ptr = tx.sync_block(&mut parent, dir_list).unwrap();
let mut l1 = BlockData::<HTreeNode<DirList>>::empty(BlockAddr::default()).unwrap();
l1.data_mut().ptrs[0] = HTreePtr::new(child1_htree_hash, dir_ptr);
let l1_ptr = tx.sync_block(l1).unwrap();
let l1_ptr = tx.sync_block(&mut parent, l1).unwrap();
let mut l2 =
BlockData::<HTreeNode<HTreeNode<DirList>>>::empty(BlockAddr::default()).unwrap();
l2.data_mut().ptrs[0] = HTreePtr::new(child1_htree_hash, l1_ptr);
let l2_ptr = tx.sync_block(l2).unwrap();
let l2_ptr = tx.sync_block(&mut parent, l2).unwrap();
let l2_ptr = unsafe { l2_ptr.cast() };
parent.data_mut().level0[0] = BlockPtr::marker(2);
parent.data_mut().level0[1] = l2_ptr;
level_data_mut(&mut parent)?.level0[0] = BlockPtr::marker(2);
level_data_mut(&mut parent)?.level0[1] = l2_ptr;
let size = parent.data().size() + BLOCK_SIZE * 4;
parent.data_mut().size = size.into();
tx.sync_tree(parent).unwrap();
@@ -320,10 +321,10 @@ fn insert_dir_entry_without_hash_change() {
// THEN the child node is added, but the H-Tree retains its structure, and the updated nodes retain
// the old HTreeHash value
let parent = tx.read_tree(parent_ptr).unwrap();
assert!(parent.data().level0[0].is_marker());
assert_eq!(parent.data().level0[0].addr().level().0, 2);
assert!(level_data(&parent)?.level0[0].is_marker());
assert_eq!(level_data(&parent)?.level0[0].addr().level().0, 2);
let l2_ptr = unsafe { parent.data().level0[1].cast() };
let l2_ptr = unsafe { level_data(&parent)?.level0[1].cast() };
let l2: BlockData<HTreeNode<HTreeNode<DirList>>> = tx.read_block(l2_ptr).unwrap();
let l1_ptr = l2.data().ptrs[0];
@@ -368,10 +369,10 @@ fn insert_dir_entry_without_hash_change() {
// THEN the child node is removed, the H-Tree retains its structure, and the updated nodes retain
// the old HTreeHash value
let parent = tx.read_tree(parent_ptr).unwrap();
assert!(parent.data().level0[0].is_marker());
assert_eq!(parent.data().level0[0].addr().level().0, 2);
assert!(level_data(&parent)?.level0[0].is_marker());
assert_eq!(level_data(&parent)?.level0[0].addr().level().0, 2);
let l2_ptr = unsafe { parent.data().level0[1].cast() };
let l2_ptr = unsafe { level_data(&parent)?.level0[1].cast() };
let l2: BlockData<HTreeNode<HTreeNode<DirList>>> = tx.read_block(l2_ptr).unwrap();
let l1_ptr = l2.data().ptrs[0];
@@ -432,10 +433,10 @@ fn insert_dir_entry_with_hash_change() {
// the new HTreeHash value
let child2_htree_hash = HTreeHash::from_name(child2_name);
let parent = tx.read_tree(parent_ptr).unwrap();
assert!(parent.data().level0[0].is_marker());
assert_eq!(parent.data().level0[0].addr().level().0, 2);
assert!(level_data(&parent)?.level0[0].is_marker());
assert_eq!(level_data(&parent)?.level0[0].addr().level().0, 2);
let l2_ptr = unsafe { parent.data().level0[1].cast() };
let l2_ptr = unsafe { level_data(&parent)?.level0[1].cast() };
let l2: BlockData<HTreeNode<HTreeNode<DirList>>> = tx.read_block(l2_ptr).unwrap();
let l1_ptr = l2.data().ptrs[0];
@@ -481,10 +482,10 @@ fn insert_dir_entry_with_hash_change() {
// to child1's HTreeHash value
let child1_htree_hash = HTreeHash::from_name(child1_name);
let parent = tx.read_tree(parent_ptr).unwrap();
assert!(parent.data().level0[0].is_marker());
assert_eq!(parent.data().level0[0].addr().level().0, 2);
assert!(level_data(&parent)?.level0[0].is_marker());
assert_eq!(level_data(&parent)?.level0[0].addr().level().0, 2);
let l2_ptr = unsafe { parent.data().level0[1].cast() };
let l2_ptr = unsafe { level_data(&parent)?.level0[1].cast() };
let l2: BlockData<HTreeNode<HTreeNode<DirList>>> = tx.read_block(l2_ptr).unwrap();
let l1_ptr = l2.data().ptrs[0];
@@ -545,8 +546,8 @@ fn delete_to_empty() {
);
let parent = tx.read_tree(parent_ptr).unwrap();
assert!(!parent.data().level0[0].is_marker());
assert!(parent.data().level0[0].addr().is_null());
assert!(!level_data(&parent)?.level0[0].is_marker());
assert!(level_data(&parent)?.level0[0].addr().is_null());
Ok(())
})
@@ -569,11 +570,11 @@ fn split_htree_level0_to_level1() {
// Confirm preconditions: the level 0 is full of the expected entries.
let parent = tx.read_tree(parent_ptr).unwrap();
assert!(parent.data().level0[0].is_marker());
assert_eq!(parent.data().level0[0].addr().level().0, 0);
assert!(!parent.data().level0[0].addr().is_null());
assert!(level_data(&parent)?.level0[0].is_marker());
assert_eq!(level_data(&parent)?.level0[0].addr().level().0, 0);
assert!(!level_data(&parent)?.level0[0].addr().is_null());
let dir_ptr: BlockPtr<DirList> = unsafe { parent.data().level0[1].cast() };
let dir_ptr: BlockPtr<DirList> = unsafe { level_data(&parent)?.level0[1].cast() };
let dir_list = tx.read_block(dir_ptr).unwrap();
for (i, entry) in dir_list.data().entries().enumerate() {
assert_eq!(entry.name().unwrap(), format!("child__{i:0243}"));
@@ -598,11 +599,12 @@ fn split_htree_level0_to_level1() {
// THEN the level is increased and the DirList is split
fs.tx(|tx| {
let parent = tx.read_tree(parent_ptr).unwrap();
assert!(parent.data().level0[0].is_marker());
assert_eq!(parent.data().level0[0].addr().level().0, 1);
assert!(!parent.data().level0[1].addr().is_null());
assert!(level_data(&parent)?.level0[0].is_marker());
assert_eq!(level_data(&parent)?.level0[0].addr().level().0, 1);
assert!(!level_data(&parent)?.level0[1].addr().is_null());
let htree_ptr: BlockPtr<HTreeNode<DirList>> = unsafe { parent.data().level0[1].cast() };
let htree_ptr: BlockPtr<HTreeNode<DirList>> =
unsafe { level_data(&parent)?.level0[1].cast() };
let htree_node = tx.read_block(htree_ptr).unwrap();
assert!(!htree_node.data().ptrs[0].is_null());
assert_eq!(
@@ -653,11 +655,12 @@ fn split_htree_level0_to_level1() {
// THEN only the other split remains
fs.tx(|tx| {
let parent = tx.read_tree(parent_ptr).unwrap();
assert!(parent.data().level0[0].is_marker());
assert_eq!(parent.data().level0[0].addr().level().0, 1);
assert!(!parent.data().level0[1].addr().is_null());
assert!(level_data(&parent)?.level0[0].is_marker());
assert_eq!(level_data(&parent)?.level0[0].addr().level().0, 1);
assert!(!level_data(&parent)?.level0[1].addr().is_null());
let htree_ptr: BlockPtr<HTreeNode<DirList>> = unsafe { parent.data().level0[1].cast() };
let htree_ptr: BlockPtr<HTreeNode<DirList>> =
unsafe { level_data(&parent)?.level0[1].cast() };
let htree_node = tx.read_block(htree_ptr).unwrap();
assert!(!htree_node.data().ptrs[0].is_null());
assert_eq!(
@@ -691,8 +694,8 @@ fn split_htree_level0_to_level1() {
// THEN the level1 is collapsed back to an empty state
fs.tx(|tx| {
let parent = tx.read_tree(parent_ptr).unwrap();
assert!(!parent.data().level0[0].is_marker());
assert!(parent.data().level0[1].is_null());
assert!(!level_data(&parent)?.level0[0].is_marker());
assert!(level_data(&parent)?.level0[1].is_null());
Ok(())
})
.unwrap();
@@ -716,11 +719,11 @@ fn split_htree_with_multiple_levels() {
// Confirm preconditions: the level 0 is full of the expected entries.
let mut parent = tx.read_tree(parent_ptr).unwrap();
assert!(parent.data().level0[0].is_marker());
assert_eq!(parent.data().level0[0].addr().level().0, 2);
assert!(level_data(&parent)?.level0[0].is_marker());
assert_eq!(level_data(&parent)?.level0[0].addr().level().0, 2);
let l2_ptr: BlockPtr<HTreeNode<HTreeNode<DirList>>> =
unsafe { parent.data().level0[1].cast() };
unsafe { level_data(&parent)?.level0[1].cast() };
let mut l2_node = tx.read_block(l2_ptr).unwrap();
for i in 0..HTREE_IDX_ENTRIES {
if i == 0 {
@@ -744,7 +747,7 @@ fn split_htree_with_multiple_levels() {
l2_node.data_mut().ptrs[0].ptr = unsafe { tx.write_block(l1_node) }.unwrap();
let l2_record_ptr = unsafe { tx.write_block(l2_node) }.unwrap();
parent.data_mut().level0[1] = unsafe { l2_record_ptr.cast() };
level_data_mut(&mut parent)?.level0[1] = unsafe { l2_record_ptr.cast() };
tx.sync_tree(parent).unwrap();
Ok(())
@@ -766,12 +769,12 @@ fn split_htree_with_multiple_levels() {
// THEN the branch splits all the way to the root, increasing the level
fs.tx(|tx| {
let parent = tx.read_tree(parent_ptr).unwrap();
assert!(parent.data().level0[0].is_marker());
assert_eq!(parent.data().level0[0].addr().level().0, 3);
assert!(!parent.data().level0[1].addr().is_null());
assert!(level_data(&parent)?.level0[0].is_marker());
assert_eq!(level_data(&parent)?.level0[0].addr().level().0, 3);
assert!(!level_data(&parent)?.level0[1].addr().is_null());
let htree_ptr: BlockPtr<HTreeNode<HTreeNode<HTreeNode<DirList>>>> =
unsafe { parent.data().level0[1].cast() };
unsafe { level_data(&parent)?.level0[1].cast() };
let htree_node = tx.read_block(htree_ptr).unwrap();
// Note that while a split tries to evenly divide the H-tree entries between the new two sibling nodes,
@@ -816,7 +819,7 @@ fn split_htree_with_multiple_levels() {
fs.tx(|tx| {
let parent = tx.read_tree(parent_ptr).unwrap();
let htree_ptr: BlockPtr<HTreeNode<HTreeNode<HTreeNode<DirList>>>> =
unsafe { parent.data().level0[1].cast() };
unsafe { level_data(&parent)?.level0[1].cast() };
let htree_node = tx.read_block(htree_ptr).unwrap();
assert!(!htree_node.data().ptrs[0].is_null());
@@ -863,7 +866,7 @@ fn split_htree_with_multiple_levels() {
fs.tx(|tx| {
let parent = tx.read_tree(parent_ptr).unwrap();
let htree_ptr: BlockPtr<HTreeNode<HTreeNode<HTreeNode<DirList>>>> =
unsafe { parent.data().level0[1].cast() };
unsafe { level_data(&parent)?.level0[1].cast() };
let htree_node = tx.read_block(htree_ptr).unwrap();
assert!(!htree_node.data().ptrs[0].is_null());
@@ -913,7 +916,7 @@ fn split_htree_with_multiple_levels() {
fs.tx(|tx| {
let parent = tx.read_tree(parent_ptr).unwrap();
let htree_ptr: BlockPtr<HTreeNode<HTreeNode<HTreeNode<DirList>>>> =
unsafe { parent.data().level0[1].cast() };
unsafe { level_data(&parent)?.level0[1].cast() };
let htree_node = tx.read_block(htree_ptr).unwrap();
assert!(!htree_node.data().ptrs[0].is_null());
@@ -944,11 +947,11 @@ fn split_htree_with_multiple_levels_using_duplicates() {
// Confirm preconditions: the level 0 is full of the expected entries.
let mut parent = tx.read_tree(parent_ptr).unwrap();
assert!(parent.data().level0[0].is_marker());
assert_eq!(parent.data().level0[0].addr().level().0, 2);
assert!(level_data(&parent)?.level0[0].is_marker());
assert_eq!(level_data(&parent)?.level0[0].addr().level().0, 2);
let l2_ptr: BlockPtr<HTreeNode<HTreeNode<DirList>>> =
unsafe { parent.data().level0[1].cast() };
unsafe { level_data(&parent)?.level0[1].cast() };
let mut l2_node = tx.read_block(l2_ptr).unwrap();
for i in 0..HTREE_IDX_ENTRIES {
if i == 0 {
@@ -972,7 +975,7 @@ fn split_htree_with_multiple_levels_using_duplicates() {
l2_node.data_mut().ptrs[0].ptr = unsafe { tx.write_block(l1_node) }.unwrap();
let l2_record_ptr = unsafe { tx.write_block(l2_node) }.unwrap();
parent.data_mut().level0[1] = unsafe { l2_record_ptr.cast() };
level_data_mut(&mut parent)?.level0[1] = unsafe { l2_record_ptr.cast() };
tx.sync_tree(parent).unwrap();
Ok(())
@@ -986,12 +989,12 @@ fn split_htree_with_multiple_levels_using_duplicates() {
// THEN the branch splits all the way to the root, increasing the level
fs.tx(|tx| {
let parent = tx.read_tree(parent_ptr).unwrap();
assert!(parent.data().level0[0].is_marker());
assert_eq!(parent.data().level0[0].addr().level().0, 3);
assert!(!parent.data().level0[1].addr().is_null());
assert!(level_data(&parent)?.level0[0].is_marker());
assert_eq!(level_data(&parent)?.level0[0].addr().level().0, 3);
assert!(!level_data(&parent)?.level0[1].addr().is_null());
let htree_ptr: BlockPtr<HTreeNode<HTreeNode<HTreeNode<DirList>>>> =
unsafe { parent.data().level0[1].cast() };
unsafe { level_data(&parent)?.level0[1].cast() };
let htree_node = tx.read_block(htree_ptr).unwrap();
// Note that while a split tries to evenly divide the H-tree entries between the new two sibling nodes,
@@ -1039,7 +1042,7 @@ fn split_htree_with_multiple_levels_using_duplicates() {
fs.tx(|tx| {
let parent = tx.read_tree(parent_ptr).unwrap();
let htree_ptr: BlockPtr<HTreeNode<HTreeNode<HTreeNode<DirList>>>> =
unsafe { parent.data().level0[1].cast() };
unsafe { level_data(&parent)?.level0[1].cast() };
let htree_node = tx.read_block(htree_ptr).unwrap();
assert!(!htree_node.data().ptrs[0].is_null());
+362 -152
View File
@@ -14,21 +14,66 @@ use syscall::error::{
use crate::{
htree::{self, HTreeHash, HTreeNode, HTreePtr},
AllocEntry, AllocList, Allocator, BlockAddr, BlockData, BlockLevel, BlockPtr, BlockTrait,
DirEntry, DirList, Disk, FileSystem, Header, Node, NodeLevel, RecordRaw, TreeData, TreePtr,
ALLOC_GC_THRESHOLD, ALLOC_LIST_ENTRIES, DIR_ENTRY_MAX_LENGTH, HEADER_RING,
AllocEntry, AllocList, Allocator, BlockAddr, BlockData, BlockLevel, BlockMeta, BlockPtr,
BlockTrait, DirEntry, DirList, Disk, FileSystem, Header, Node, NodeFlags, NodeLevel,
NodeLevelData, RecordRaw, TreeData, TreePtr, ALLOC_GC_THRESHOLD, ALLOC_LIST_ENTRIES,
DIR_ENTRY_MAX_LENGTH, HEADER_RING,
};
pub(crate) fn level_data(node: &TreeData<Node>) -> Result<&NodeLevelData> {
node.data().level_data().ok_or_else(|| {
#[cfg(feature = "log")]
log::error!("LEVEL_DATA: NODE HAS INLINE DATA");
Error::new(EIO)
})
}
pub(crate) fn level_data_mut(node: &mut TreeData<Node>) -> Result<&mut NodeLevelData> {
node.data_mut().level_data_mut().ok_or_else(|| {
#[cfg(feature = "log")]
log::error!("LEVEL_DATA_MUT: NODE HAS INLINE DATA");
Error::new(EIO)
})
}
pub trait AllocCtx {
fn allocate(&mut self, _addr: BlockAddr) {}
fn deallocate(&mut self, _addr: BlockAddr) {}
}
pub struct FsCtx;
impl AllocCtx for FsCtx {}
impl AllocCtx for TreeData<Node> {
fn allocate(&mut self, addr: BlockAddr) {
let blocks = self.data().blocks();
self.data_mut().set_blocks(
blocks
.checked_add(addr.level().blocks::<u64>())
.expect("node block count overflow"),
);
}
fn deallocate(&mut self, addr: BlockAddr) {
let blocks = self.data().blocks();
self.data_mut().set_blocks(
blocks
.checked_sub(addr.level().blocks::<u64>())
.expect("node block count underflow"),
);
}
}
pub struct Transaction<'a, D: Disk> {
fs: &'a mut FileSystem<D>,
//TODO: make private
pub header: Header,
//TODO: make private
pub header_changed: bool,
allocator: Allocator,
pub(crate) allocator: Allocator,
allocator_log: VecDeque<AllocEntry>,
deallocate: Vec<BlockAddr>,
write_cache: BTreeMap<BlockAddr, Box<[u8]>>,
pub(crate) write_cache: BTreeMap<BlockAddr, Box<[u8]>>,
}
impl<'a, D: Disk> Transaction<'a, D> {
@@ -57,13 +102,18 @@ impl<'a, D: Disk> Transaction<'a, D> {
// MARK: block operations
//
/// Allocate a new block of size `level`, returning its address.
/// Allocate a new block of size defined by `meta`, returning its address.
/// - returns `Err(ENOSPC)` if a block of this size could not be alloated.
/// - unsafe because order must be done carefully and changes must be flushed to disk
pub(crate) unsafe fn allocate(&mut self, level: BlockLevel) -> Result<BlockAddr> {
match self.allocator.allocate(level) {
pub(crate) unsafe fn allocate(
&mut self,
ctx: &mut dyn AllocCtx,
meta: BlockMeta,
) -> Result<BlockAddr> {
match self.allocator.allocate(meta) {
Some(addr) => {
self.allocator_log.push_back(AllocEntry::allocate(addr));
ctx.allocate(addr);
Ok(addr)
}
None => Err(Error::new(ENOSPC)),
@@ -72,7 +122,7 @@ impl<'a, D: Disk> Transaction<'a, D> {
/// Deallocate the given block.
/// - unsafe because order must be done carefully and changes must be flushed to disk
pub(crate) unsafe fn deallocate(&mut self, addr: BlockAddr) {
pub(crate) unsafe fn deallocate(&mut self, ctx: &mut dyn AllocCtx, addr: BlockAddr) {
//TODO: should we use some sort of not-null abstraction?
assert!(!addr.is_null());
@@ -84,7 +134,7 @@ impl<'a, D: Disk> Transaction<'a, D> {
let mut found = false;
for i in (0..self.allocator_log.len()).rev() {
let entry = self.allocator_log[i];
if entry.index() == addr.index() && entry.count() == -addr.level().blocks() {
if entry.index() == addr.index() && entry.count() == -addr.level().blocks::<i64>() {
found = true;
self.allocator_log.remove(i);
break;
@@ -98,13 +148,19 @@ impl<'a, D: Disk> Transaction<'a, D> {
// Deallocate later when syncing filesystem, to avoid re-use
self.deallocate.push(addr);
}
ctx.deallocate(addr);
}
fn deallocate_block<T: BlockTrait>(&mut self, ptr: BlockPtr<T>) {
unsafe fn deallocate_block<T: BlockTrait>(
&mut self,
ctx: &mut dyn AllocCtx,
ptr: BlockPtr<T>,
) -> bool {
if !ptr.is_null() {
unsafe {
self.deallocate(ptr.addr());
}
self.deallocate(ctx, ptr.addr());
true
} else {
false
}
}
@@ -180,7 +236,7 @@ impl<'a, D: Disk> Transaction<'a, D> {
while new_blocks.len() * ALLOC_LIST_ENTRIES
<= self.allocator_log.len() + self.deallocate.len()
{
new_blocks.push(unsafe { self.allocate(BlockLevel::default())? });
new_blocks.push(unsafe { self.allocate(&mut FsCtx, BlockMeta::default())? });
}
// De-allocate old blocks (after allocation to prevent re-use)
@@ -321,8 +377,9 @@ impl<'a, D: Disk> Transaction<'a, D> {
ptr: BlockPtr<T>,
) -> Result<BlockData<T>> {
if ptr.is_null() {
match T::empty(ptr.addr().level()) {
Some(empty) => Ok(BlockData::new(BlockAddr::default(), empty)),
let addr = ptr.addr();
match T::empty(addr.level()) {
Some(empty) => Ok(BlockData::new(addr, empty)),
None => {
#[cfg(feature = "log")]
log::error!("READ_BLOCK_OR_EMPTY: INVALID BLOCK LEVEL FOR TYPE");
@@ -336,12 +393,41 @@ impl<'a, D: Disk> Transaction<'a, D> {
unsafe fn read_record<T: BlockTrait + DerefMut<Target = [u8]>>(
&mut self,
ptr: BlockPtr<T>,
mut ptr: BlockPtr<T>,
level: BlockLevel,
) -> Result<BlockData<T>> {
let record = unsafe { self.read_block_or_empty(ptr)? };
// Set null pointers to correct size (reduces number of copies below)
if ptr.is_null() {
ptr = BlockPtr::<T>::null(BlockMeta::new(level));
}
// Read record from disk, or construct empty one for null pointers
let mut record = unsafe { self.read_block_or_empty(ptr)? };
// Attempt to decompress if address metadata indicates compression
if let Some(decomp_level) = record.addr().decomp_level() {
// First 2 bytes store compressed data length
// This means only compressed record sizes up to 64 KiB are supported
let mut decomp = match T::empty(decomp_level) {
Some(empty) => empty,
None => {
#[cfg(feature = "log")]
log::error!("READ_RECORD: INVALID DECOMPRESSED BLOCK LEVEL FOR TYPE");
return Err(Error::new(ENOENT));
}
};
let comp_len = record.data()[0] as usize | ((record.data()[1] as usize) << 8);
let total_len = comp_len + 2;
if let Err(err) = lz4_flex::decompress_into(&record.data()[2..total_len], &mut decomp) {
#[cfg(feature = "log")]
log::error!("READ_RECORD: FAILED TO DECOMPRESS: {:?}", err);
return Err(Error::new(EIO));
}
record = BlockData::new(BlockAddr::null(BlockMeta::new(decomp_level)), decomp);
}
// Return record if it is larger than or equal to requested level
if record.addr().level() >= level {
// Return record if it is larger than or equal to requested level
return Ok(record);
}
@@ -359,22 +445,24 @@ impl<'a, D: Disk> Transaction<'a, D> {
};
let len = min(raw.len(), old_raw.len());
raw[..len].copy_from_slice(&old_raw[..len]);
Ok(BlockData::new(BlockAddr::null(level), raw))
Ok(BlockData::new(BlockAddr::null(BlockMeta::new(level)), raw))
}
/// Write block data to a new address, returning new address
pub fn sync_block<T: BlockTrait + Deref<Target = [u8]>>(
&mut self,
ctx: &mut dyn AllocCtx,
mut block: BlockData<T>,
) -> Result<BlockPtr<T>> {
// Swap block to new address
let level = block.addr().level();
let old_addr = block.swap_addr(unsafe { self.allocate(level)? });
let meta = block.addr().meta();
let old_addr = block.swap_addr(unsafe { self.allocate(ctx, meta)? });
// Deallocate old address (will only take effect after sync_allocator, which helps to
// prevent re-use before a new header is written
if !old_addr.is_null() {
unsafe {
self.deallocate(old_addr);
self.deallocate(ctx, old_addr);
}
}
// Write new block
@@ -498,14 +586,14 @@ impl<'a, D: Disk> Transaction<'a, D> {
l0.data_mut().ptrs[i0] = block_ptr.cast();
l1.data_mut()
.set_branch_full(i1, l0.data().tree_list_is_full());
l1.data_mut().ptrs[i1] = self.sync_block(l0)?;
l1.data_mut().ptrs[i1] = self.sync_block(&mut FsCtx, l0)?;
l2.data_mut()
.set_branch_full(i2, l1.data().tree_list_is_full());
l2.data_mut().ptrs[i2] = self.sync_block(l1)?;
l2.data_mut().ptrs[i2] = self.sync_block(&mut FsCtx, l1)?;
l3.data_mut()
.set_branch_full(i3, l2.data().tree_list_is_full());
l3.data_mut().ptrs[i3] = self.sync_block(l2)?;
self.header.tree = self.sync_block(l3)?;
l3.data_mut().ptrs[i3] = self.sync_block(&mut FsCtx, l2)?;
self.header.tree = self.sync_block(&mut FsCtx, l3)?;
self.header_changed = true;
return Ok(tree_ptr);
@@ -542,37 +630,37 @@ impl<'a, D: Disk> Transaction<'a, D> {
l0.data_mut().set_branch_full(i0, false);
l0.data_mut().ptrs[i0] = BlockPtr::default();
let l0_ptr = if l0.data().tree_list_is_empty() {
unsafe { self.deallocate(l0.addr()) };
unsafe { self.deallocate(&mut FsCtx, l0.addr()) };
BlockPtr::default()
} else {
self.sync_block(l0)?
self.sync_block(&mut FsCtx, l0)?
};
l1.data_mut().set_branch_full(i1, false);
l1.data_mut().ptrs[i1] = l0_ptr;
let l1_ptr = if l1.data().tree_list_is_empty() {
unsafe { self.deallocate(l1.addr()) };
unsafe { self.deallocate(&mut FsCtx, l1.addr()) };
BlockPtr::default()
} else {
self.sync_block(l1)?
self.sync_block(&mut FsCtx, l1)?
};
l2.data_mut().set_branch_full(i2, false);
l2.data_mut().ptrs[i2] = l1_ptr;
let l2_ptr = if l2.data().tree_list_is_empty() {
unsafe { self.deallocate(l2.addr()) };
unsafe { self.deallocate(&mut FsCtx, l2.addr()) };
BlockPtr::default()
} else {
self.sync_block(l2)?
self.sync_block(&mut FsCtx, l2)?
};
l3.data_mut().set_branch_full(i3, false);
l3.data_mut().ptrs[i3] = l2_ptr;
let l3_ptr = if l3.data().tree_list_is_empty() {
unsafe { self.deallocate(l3.addr()) };
unsafe { self.deallocate(&mut FsCtx, l3.addr()) };
BlockPtr::default()
} else {
self.sync_block(l3)?
self.sync_block(&mut FsCtx, l3)?
};
self.header.tree = l3_ptr;
@@ -608,11 +696,11 @@ impl<'a, D: Disk> Transaction<'a, D> {
raw.data_mut().copy_from_slice(node.data());
// Write updates to newly allocated blocks
l0.data_mut().ptrs[i0] = self.sync_block(raw)?;
l1.data_mut().ptrs[i1] = self.sync_block(l0)?;
l2.data_mut().ptrs[i2] = self.sync_block(l1)?;
l3.data_mut().ptrs[i3] = self.sync_block(l2)?;
self.header.tree = self.sync_block(l3)?;
l0.data_mut().ptrs[i0] = self.sync_block(&mut FsCtx, raw)?;
l1.data_mut().ptrs[i1] = self.sync_block(&mut FsCtx, l0)?;
l2.data_mut().ptrs[i2] = self.sync_block(&mut FsCtx, l1)?;
l3.data_mut().ptrs[i3] = self.sync_block(&mut FsCtx, l2)?;
self.header.tree = self.sync_block(&mut FsCtx, l3)?;
self.header_changed = true;
}
@@ -635,18 +723,18 @@ impl<'a, D: Disk> Transaction<'a, D> {
children: &mut Vec<DirEntry>,
) -> Result<()> {
let parent = self.read_tree(parent_ptr)?;
if parent.data().level0[0].is_marker() {
let htree_levels = parent.data().level0[0].addr().level().0;
if level_data(&parent)?.level0[0].is_marker() {
let htree_levels = level_data(&parent)?.level0[0].addr().level().0;
let htree_root = if htree_levels == 0 {
// Create a fake root to satisfy the recursive child_nodes_inner function signature
let mut fake_htree_node =
BlockData::<HTreeNode<RecordRaw>>::empty(BlockAddr::default()).unwrap();
let dir_ptr = parent.data().level0[1];
let dir_ptr = level_data(&parent)?.level0[1];
let htree_ptr = HTreePtr::new(HTreeHash::MAX, dir_ptr);
fake_htree_node.data_mut().ptrs[0] = htree_ptr;
fake_htree_node
} else {
let htree_record_ptr = parent.data().level0[1];
let htree_record_ptr = level_data(&parent)?.level0[1];
let htree_ptr: BlockPtr<HTreeNode<RecordRaw>> = unsafe { htree_record_ptr.cast() };
self.read_block(htree_ptr)?
};
@@ -685,23 +773,23 @@ impl<'a, D: Disk> Transaction<'a, D> {
/// Returns ENOENT if this node is not found.
pub fn find_node(&mut self, parent_ptr: TreePtr<Node>, name: &str) -> Result<TreeData<Node>> {
let parent = self.read_tree(parent_ptr)?;
if !parent.data().level0[0].is_marker() {
if !level_data(&parent)?.level0[0].is_marker() {
return Err(Error::new(ENOENT));
}
let htree_levels = parent.data().level0[0].addr().level().0;
let htree_levels = level_data(&parent)?.level0[0].addr().level().0;
let root_htree_node = if htree_levels == 0 {
// Create a fake root to satisfy the recursive inner_find_node function signature
let mut fake_htree_node =
BlockData::<HTreeNode<RecordRaw>>::empty(BlockAddr::default()).unwrap();
let dir_ptr = parent.data().level0[1];
let dir_ptr = level_data(&parent)?.level0[1];
let htree_ptr = HTreePtr::new(HTreeHash::MAX, dir_ptr);
fake_htree_node.data_mut().ptrs[0] = htree_ptr;
fake_htree_node
} else {
let root_htree_ptr: BlockPtr<HTreeNode<RecordRaw>> =
unsafe { parent.data().level0[1].cast() };
unsafe { level_data(&parent)?.level0[1].cast() };
self.read_block(root_htree_ptr)?
};
@@ -773,7 +861,7 @@ impl<'a, D: Disk> Transaction<'a, D> {
unsafe {
let parent = self.read_tree(parent_ptr)?;
let node_block_data = BlockData::new(
self.allocate(BlockLevel::default())?,
self.allocate(&mut FsCtx, BlockMeta::default())?,
Node::new(
mode,
parent.data().uid(),
@@ -810,37 +898,37 @@ impl<'a, D: Disk> Transaction<'a, D> {
let record_byte_size = parent.data().record_level().bytes();
// If this is a brand new directory, create the first DirList block
if !parent.data().level0[0].is_marker() {
if !level_data(&parent)?.level0[0].is_marker() {
let marker: BlockPtr<RecordRaw> = BlockPtr::marker(0);
assert!(marker.is_marker());
parent.data_mut().level0[0] = BlockPtr::marker(0);
assert!(parent.data().level0[0].is_marker());
level_data_mut(&mut parent)?.level0[0] = BlockPtr::marker(0);
assert!(level_data(&parent)?.level0[0].is_marker());
// Create the first DirList block
let dir = BlockData::<DirList>::empty(BlockAddr::default()).unwrap();
let dir_ptr = self.sync_block(dir)?;
let dir_ptr = self.sync_block(&mut parent, dir)?;
// Add the DirList directly to the parent directory
parent.data_mut().level0[1] = unsafe { dir_ptr.cast() };
level_data_mut(&mut parent)?.level0[1] = unsafe { dir_ptr.cast() };
let size = parent.data().size() + record_byte_size;
parent.data_mut().set_size(size);
}
let mut htree_levels = parent.data().level0[0].addr().level().0;
let mut htree_levels = level_data(&parent)?.level0[0].addr().level().0;
let mut htree_root = if htree_levels == 0 {
// If we have no H-tree root, create a fake one to satisfy the recurisve inner_link_node function
let mut fake_htree_node =
BlockData::<HTreeNode<RecordRaw>>::empty(BlockAddr::default()).unwrap();
let dir_ptr = parent.data().level0[1];
let dir_ptr = level_data(&parent)?.level0[1];
let htree_ptr = HTreePtr::new(HTreeHash::MAX, dir_ptr);
fake_htree_node.data_mut().ptrs[0] = htree_ptr;
fake_htree_node
} else {
// Otherwise get the real H-tree root
let htree_root_ptr: BlockPtr<HTreeNode<RecordRaw>> =
unsafe { parent.data().level0[1].cast() };
unsafe { level_data(&parent)?.level0[1].cast() };
self.read_block(htree_root_ptr)?
};
@@ -855,7 +943,7 @@ impl<'a, D: Disk> Transaction<'a, D> {
// If we used a fake root, and we grew beyond a single DirList block, we need to create a real root
if htree_levels == 0 && !htree_root.data().ptrs[1].is_null() {
htree_levels = 1;
parent.data_mut().level0[0] = BlockPtr::marker(1);
level_data_mut(&mut parent)?.level0[0] = BlockPtr::marker(1);
let size = parent.data().size() + record_byte_size;
parent.data_mut().set_size(size);
}
@@ -868,7 +956,7 @@ impl<'a, D: Disk> Transaction<'a, D> {
let mut sibling =
BlockData::<HTreeNode<RecordRaw>>::empty(BlockAddr::default()).unwrap();
let _ = mem::replace(sibling.data_mut(), unallocated_sibling);
let sibling_block_ptr = self.sync_block(sibling)?;
let sibling_block_ptr = self.sync_block(&mut parent, sibling)?;
let sibling_htree_ptr = HTreePtr::new(sibling_htree_hash, sibling_block_ptr);
let sibling_record_ptr: HTreePtr<RecordRaw> = unsafe { sibling_htree_ptr.cast() };
@@ -877,7 +965,7 @@ impl<'a, D: Disk> Transaction<'a, D> {
.data()
.find_max_htree_hash()
.ok_or(Error::new(EIO))?;
let root_block_ptr = self.sync_block(htree_root)?;
let root_block_ptr = self.sync_block(&mut parent, htree_root)?;
let root_htree_ptr = HTreePtr::new(root_htree_hash, root_block_ptr);
let root_record_ptr: HTreePtr<RecordRaw> = unsafe { root_htree_ptr.cast() };
@@ -887,20 +975,20 @@ impl<'a, D: Disk> Transaction<'a, D> {
new_root.data_mut().ptrs[0] = sibling_record_ptr;
let unexpected_sibling = htree::add_inner_node(new_root.data_mut(), root_record_ptr)?;
assert!(unexpected_sibling.is_none());
let new_root_ptr = self.sync_block(new_root)?;
let new_root_ptr = self.sync_block(&mut parent, new_root)?;
// Add the parent node pointer, increase the level, and increase one block size per allocated block
parent.data_mut().level0[0] = BlockPtr::marker(htree_levels as u8 + 1);
parent.data_mut().level0[1] = unsafe { new_root_ptr.cast() };
level_data_mut(&mut parent)?.level0[0] = BlockPtr::marker(htree_levels as u8 + 1);
level_data_mut(&mut parent)?.level0[1] = unsafe { new_root_ptr.cast() };
let size = parent.data().size() + 2 * record_byte_size;
parent.data_mut().set_size(size);
} else if htree_levels > 0 {
// Update the parent node with the new root pointer
let root_block_ptr = self.sync_block(htree_root)?;
parent.data_mut().level0[1] = unsafe { root_block_ptr.cast() };
let root_block_ptr = self.sync_block(&mut parent, htree_root)?;
level_data_mut(&mut parent)?.level0[1] = unsafe { root_block_ptr.cast() };
} else {
// Update the parent with the DirList block, ignoring the fake htree_root
parent.data_mut().level0[1] = htree_root.data().ptrs[0].ptr;
level_data_mut(&mut parent)?.level0[1] = htree_root.data().ptrs[0].ptr;
}
self.sync_trees(&[parent, node])?;
Ok(())
@@ -938,7 +1026,7 @@ impl<'a, D: Disk> Transaction<'a, D> {
let mut dir = self.read_block(dir_ptr)?;
let unallocated_sibling =
htree::add_dir_entry(dir.data_mut(), &mut htree_ptr.htree_hash, dir_entry)?;
let dir_record_ptr = unsafe { self.sync_block(dir)?.cast() };
let dir_record_ptr = unsafe { self.sync_block(parent_dir_node, dir)?.cast() };
parent_htree_node.ptrs[htree_ptr_idx] =
HTreePtr::new(htree_ptr.htree_hash, dir_record_ptr);
@@ -946,7 +1034,7 @@ impl<'a, D: Disk> Transaction<'a, D> {
// The DirList block was split, so we need to add it to the h-tree
let mut dir = BlockData::<DirList>::empty(BlockAddr::default()).unwrap();
let _ = mem::replace(dir.data_mut(), new_unallocated_dir);
let dir_ptr = self.sync_block(dir)?;
let dir_ptr = self.sync_block(parent_dir_node, dir)?;
let dir_htree_ptr = HTreePtr::new(new_hash, dir_ptr);
let dir_record_ptr: HTreePtr<RecordRaw> = unsafe { dir_htree_ptr.cast() };
let size = parent_dir_node.data().size() + record_byte_size;
@@ -971,7 +1059,7 @@ impl<'a, D: Disk> Transaction<'a, D> {
// Write the muteated H-tree block back to disk and update the parent node's pointer
let htree_hash = htree_block.data().find_max_htree_hash().unwrap();
let htree_block_ptr = self.sync_block(htree_block)?;
let htree_block_ptr = self.sync_block(parent_dir_node, htree_block)?;
let htree_record_ptr: BlockPtr<RecordRaw> = unsafe { htree_block_ptr.cast() };
parent_htree_node.ptrs[htree_ptr_idx] = HTreePtr::new(htree_hash, htree_record_ptr);
@@ -980,7 +1068,7 @@ impl<'a, D: Disk> Transaction<'a, D> {
let mut sibling =
BlockData::<HTreeNode<RecordRaw>>::empty(BlockAddr::default()).unwrap();
let _ = mem::replace(sibling.data_mut(), new_unallocated_sibling);
let sibling_ptr = self.sync_block(sibling)?;
let sibling_ptr = self.sync_block(parent_dir_node, sibling)?;
let sibling_htree_ptr = HTreePtr::new(new_hash, sibling_ptr);
let sibling_record_ptr: HTreePtr<RecordRaw> = unsafe { sibling_htree_ptr.cast() };
let size = parent_dir_node.data().size() + record_byte_size;
@@ -1008,26 +1096,26 @@ impl<'a, D: Disk> Transaction<'a, D> {
);
let mut parent = self.read_tree(parent_ptr)?;
if !parent.data().level0[0].is_marker() {
if !level_data(&parent)?.level0[0].is_marker() {
#[cfg(feature = "log")]
log::error!("REMOVE_NODE: Parent has no htree marker set (not a directory or empty)");
return Err(Error::new(ENOENT));
}
let htree_levels = parent.data().level0[0].addr().level().0;
let htree_levels = level_data(&parent)?.level0[0].addr().level().0;
let name_hash = HTreeHash::from_name(name);
let mut htree_root = if htree_levels == 0 {
// If we have no H-tree root, create a fake one to satisfy the recurisve inner_link_node function
let mut fake_htree_node =
BlockData::<HTreeNode<RecordRaw>>::empty(BlockAddr::default()).unwrap();
let dir_ptr = parent.data().level0[1];
let dir_ptr = level_data(&parent)?.level0[1];
let htree_ptr = HTreePtr::new(HTreeHash::MAX, dir_ptr);
fake_htree_node.data_mut().ptrs[0] = htree_ptr;
fake_htree_node
} else {
// Otherwise get the real H-tree root
let htree_root_record_ptr = parent.data().level0[1];
let htree_root_record_ptr = level_data(&parent)?.level0[1];
let htree_root_ptr: BlockPtr<HTreeNode<RecordRaw>> =
unsafe { htree_root_record_ptr.cast() };
self.read_block(htree_root_ptr)?
@@ -1084,29 +1172,29 @@ impl<'a, D: Disk> Transaction<'a, D> {
// Dealocate the htree_root only if it was a real root node in the H-tree
if htree_levels > 0 {
unsafe {
self.deallocate(htree_root.addr());
self.deallocate(&mut parent, htree_root.addr());
}
let record_byte_size = parent.data().record_level().bytes();
let size = parent.data().size() - record_byte_size;
parent.data_mut().set_size(size);
}
parent.data_mut().level0[0] = BlockPtr::default();
parent.data_mut().level0[1] = BlockPtr::default();
level_data_mut(&mut parent)?.level0[0] = BlockPtr::default();
level_data_mut(&mut parent)?.level0[1] = BlockPtr::default();
} else if htree_levels > 0 {
// Update the real htree_root and update the ptr in the parent
let htree_root_block_ptr = self.sync_block(htree_root)?;
parent.data_mut().level0[1] = unsafe { htree_root_block_ptr.cast() };
let htree_root_block_ptr = self.sync_block(&mut parent, htree_root)?;
level_data_mut(&mut parent)?.level0[1] = unsafe { htree_root_block_ptr.cast() };
} else {
// The htree_root is fake, so update the parent with the ptr to the one and only directory list
let dir_list_block_ptr = htree_root.data().ptrs[0].ptr;
parent.data_mut().level0[1] = unsafe { dir_list_block_ptr.cast() };
level_data_mut(&mut parent)?.level0[1] = unsafe { dir_list_block_ptr.cast() };
}
if remove_node {
self.sync_tree(parent)?;
self.remove_tree(node.ptr())?;
unsafe {
self.deallocate(node_addr);
self.deallocate(&mut FsCtx, node_addr);
}
Ok(Some(node_id))
@@ -1155,14 +1243,14 @@ impl<'a, D: Disk> Transaction<'a, D> {
if let Some(new_tree_hash) = new_htree_hash {
// The entry_ptr needs to be updated in the parent_htree_node
let dir_block_ptr = self.sync_block(dir_list)?;
let dir_block_ptr = self.sync_block(parent_dir_node, dir_list)?;
let dir_record_ptr: BlockPtr<RecordRaw> = unsafe { dir_block_ptr.cast() };
parent_htree_node.ptrs[entry_idx] =
HTreePtr::new(new_tree_hash, dir_record_ptr);
} else {
// The entry needs to be removed from the parent_htree_noce
parent_htree_node.ptrs[entry_idx] = HTreePtr::default();
unsafe { self.deallocate(dir_list.addr()) };
unsafe { self.deallocate(parent_dir_node, dir_list.addr()) };
let size = parent_dir_node.data().size() - record_byte_size;
parent_dir_node.data_mut().set_size(size);
}
@@ -1195,14 +1283,14 @@ impl<'a, D: Disk> Transaction<'a, D> {
if let Some(new_htree_hash) = htree_node.data().find_max_htree_hash() {
// The entry_ptr needs to be updated in the parent_htree_node
let htree_block_ptr = self.sync_block(htree_node)?;
let htree_block_ptr = self.sync_block(parent_dir_node, htree_node)?;
let htree_record_ptr: BlockPtr<RecordRaw> = unsafe { htree_block_ptr.cast() };
parent_htree_node.ptrs[entry_idx] =
HTreePtr::new(new_htree_hash, htree_record_ptr);
} else {
// The htree_node is now empty, so remove it
parent_htree_node.ptrs[entry_idx] = HTreePtr::default();
unsafe { self.deallocate(htree_node.addr()) };
unsafe { self.deallocate(parent_dir_node, htree_node.addr()) };
let size = parent_dir_node.data().size() - record_byte_size;
parent_dir_node.data_mut().set_size(size);
}
@@ -1277,24 +1365,24 @@ impl<'a, D: Disk> Transaction<'a, D> {
) -> Result<BlockPtr<RecordRaw>> {
unsafe {
match NodeLevel::new(record_offset).ok_or(Error::new(ERANGE))? {
NodeLevel::L0(i0) => Ok(node.data().level0[i0]),
NodeLevel::L0(i0) => Ok(level_data(node)?.level0[i0]),
NodeLevel::L1(i1, i0) => {
let l0 = self.read_block_or_empty(node.data().level1[i1])?;
let l0 = self.read_block_or_empty(level_data(node)?.level1[i1])?;
Ok(l0.data().ptrs[i0])
}
NodeLevel::L2(i2, i1, i0) => {
let l1 = self.read_block_or_empty(node.data().level2[i2])?;
let l1 = self.read_block_or_empty(level_data(node)?.level2[i2])?;
let l0 = self.read_block_or_empty(l1.data().ptrs[i1])?;
Ok(l0.data().ptrs[i0])
}
NodeLevel::L3(i3, i2, i1, i0) => {
let l2 = self.read_block_or_empty(node.data().level3[i3])?;
let l2 = self.read_block_or_empty(level_data(node)?.level3[i3])?;
let l1 = self.read_block_or_empty(l2.data().ptrs[i2])?;
let l0 = self.read_block_or_empty(l1.data().ptrs[i1])?;
Ok(l0.data().ptrs[i0])
}
NodeLevel::L4(i4, i3, i2, i1, i0) => {
let l3 = self.read_block_or_empty(node.data().level4[i4])?;
let l3 = self.read_block_or_empty(level_data(node)?.level4[i4])?;
let l2 = self.read_block_or_empty(l3.data().ptrs[i3])?;
let l1 = self.read_block_or_empty(l2.data().ptrs[i2])?;
let l0 = self.read_block_or_empty(l1.data().ptrs[i1])?;
@@ -1312,78 +1400,83 @@ impl<'a, D: Disk> Transaction<'a, D> {
unsafe {
match NodeLevel::new(record_offset).ok_or(Error::new(ERANGE))? {
NodeLevel::L0(i0) => {
self.deallocate_block(node.data_mut().level0[i0].clear());
let ptr = level_data_mut(node)?.level0[i0].clear();
self.deallocate_block(node, ptr);
}
NodeLevel::L1(i1, i0) => {
let mut l0 = self.read_block_or_empty(node.data().level1[i1])?;
self.deallocate_block(l0.data_mut().ptrs[i0].clear());
let mut l0 = self.read_block_or_empty(level_data(node)?.level1[i1])?;
self.deallocate_block(node, l0.data_mut().ptrs[i0].clear());
if l0.data().is_empty() {
self.deallocate_block(node.data_mut().level1[i1].clear());
let ptr = level_data_mut(node)?.level1[i1].clear();
self.deallocate_block(node, ptr);
} else {
node.data_mut().level1[i1] = self.sync_block(l0)?;
level_data_mut(node)?.level1[i1] = self.sync_block(node, l0)?;
}
}
NodeLevel::L2(i2, i1, i0) => {
let mut l1 = self.read_block_or_empty(node.data().level2[i2])?;
let mut l1 = self.read_block_or_empty(level_data(node)?.level2[i2])?;
let mut l0 = self.read_block_or_empty(l1.data().ptrs[i1])?;
self.deallocate_block(l0.data_mut().ptrs[i0].clear());
self.deallocate_block(node, l0.data_mut().ptrs[i0].clear());
if l0.data().is_empty() {
self.deallocate_block(l1.data_mut().ptrs[i1].clear());
self.deallocate_block(node, l1.data_mut().ptrs[i1].clear());
} else {
l1.data_mut().ptrs[i1] = self.sync_block(l0)?;
l1.data_mut().ptrs[i1] = self.sync_block(node, l0)?;
}
if l1.data().is_empty() {
self.deallocate_block(node.data_mut().level2[i2].clear());
let ptr = level_data_mut(node)?.level2[i2].clear();
self.deallocate_block(node, ptr);
} else {
node.data_mut().level2[i2] = self.sync_block(l1)?;
level_data_mut(node)?.level2[i2] = self.sync_block(node, l1)?;
}
}
NodeLevel::L3(i3, i2, i1, i0) => {
let mut l2 = self.read_block_or_empty(node.data().level3[i3])?;
let mut l2 = self.read_block_or_empty(level_data(node)?.level3[i3])?;
let mut l1 = self.read_block_or_empty(l2.data().ptrs[i2])?;
let mut l0 = self.read_block_or_empty(l1.data().ptrs[i1])?;
self.deallocate_block(l0.data_mut().ptrs[i0].clear());
self.deallocate_block(node, l0.data_mut().ptrs[i0].clear());
if l0.data().is_empty() {
self.deallocate_block(l1.data_mut().ptrs[i1].clear());
self.deallocate_block(node, l1.data_mut().ptrs[i1].clear());
} else {
l1.data_mut().ptrs[i1] = self.sync_block(l0)?;
l1.data_mut().ptrs[i1] = self.sync_block(node, l0)?;
}
if l1.data().is_empty() {
self.deallocate_block(l2.data_mut().ptrs[i2].clear());
self.deallocate_block(node, l2.data_mut().ptrs[i2].clear());
} else {
l2.data_mut().ptrs[i2] = self.sync_block(l1)?;
l2.data_mut().ptrs[i2] = self.sync_block(node, l1)?;
}
if l2.data().is_empty() {
self.deallocate_block(node.data_mut().level3[i3].clear());
let ptr = level_data_mut(node)?.level3[i3].clear();
self.deallocate_block(node, ptr);
} else {
node.data_mut().level3[i3] = self.sync_block(l2)?;
level_data_mut(node)?.level3[i3] = self.sync_block(node, l2)?;
}
}
NodeLevel::L4(i4, i3, i2, i1, i0) => {
let mut l3 = self.read_block_or_empty(node.data().level4[i4])?;
let mut l3 = self.read_block_or_empty(level_data(node)?.level4[i4])?;
let mut l2 = self.read_block_or_empty(l3.data().ptrs[i3])?;
let mut l1 = self.read_block_or_empty(l2.data().ptrs[i2])?;
let mut l0 = self.read_block_or_empty(l1.data().ptrs[i1])?;
self.deallocate_block(l0.data_mut().ptrs[i0].clear());
self.deallocate_block(node, l0.data_mut().ptrs[i0].clear());
if l0.data().is_empty() {
self.deallocate_block(l1.data_mut().ptrs[i1].clear());
self.deallocate_block(node, l1.data_mut().ptrs[i1].clear());
} else {
l1.data_mut().ptrs[i1] = self.sync_block(l0)?;
l1.data_mut().ptrs[i1] = self.sync_block(node, l0)?;
}
if l1.data().is_empty() {
self.deallocate_block(l2.data_mut().ptrs[i2].clear());
self.deallocate_block(node, l2.data_mut().ptrs[i2].clear());
} else {
l2.data_mut().ptrs[i2] = self.sync_block(l1)?;
l2.data_mut().ptrs[i2] = self.sync_block(node, l1)?;
}
if l2.data().is_empty() {
self.deallocate_block(l3.data_mut().ptrs[i3].clear());
self.deallocate_block(node, l3.data_mut().ptrs[i3].clear());
} else {
l3.data_mut().ptrs[i3] = self.sync_block(l2)?;
l3.data_mut().ptrs[i3] = self.sync_block(node, l2)?;
}
if l3.data().is_empty() {
self.deallocate_block(node.data_mut().level4[i4].clear());
let ptr = level_data_mut(node)?.level4[i4].clear();
self.deallocate_block(node, ptr);
} else {
node.data_mut().level4[i4] = self.sync_block(l3)?;
level_data_mut(node)?.level4[i4] = self.sync_block(node, l3)?;
}
}
}
@@ -1402,43 +1495,43 @@ impl<'a, D: Disk> Transaction<'a, D> {
unsafe {
match NodeLevel::new(record_offset).ok_or(Error::new(ERANGE))? {
NodeLevel::L0(i0) => {
node.data_mut().level0[i0] = ptr;
level_data_mut(node)?.level0[i0] = ptr;
}
NodeLevel::L1(i1, i0) => {
let mut l0 = self.read_block_or_empty(node.data().level1[i1])?;
let mut l0 = self.read_block_or_empty(level_data(node)?.level1[i1])?;
l0.data_mut().ptrs[i0] = ptr;
node.data_mut().level1[i1] = self.sync_block(l0)?;
level_data_mut(node)?.level1[i1] = self.sync_block(node, l0)?;
}
NodeLevel::L2(i2, i1, i0) => {
let mut l1 = self.read_block_or_empty(node.data().level2[i2])?;
let mut l1 = self.read_block_or_empty(level_data(node)?.level2[i2])?;
let mut l0 = self.read_block_or_empty(l1.data().ptrs[i1])?;
l0.data_mut().ptrs[i0] = ptr;
l1.data_mut().ptrs[i1] = self.sync_block(l0)?;
node.data_mut().level2[i2] = self.sync_block(l1)?;
l1.data_mut().ptrs[i1] = self.sync_block(node, l0)?;
level_data_mut(node)?.level2[i2] = self.sync_block(node, l1)?;
}
NodeLevel::L3(i3, i2, i1, i0) => {
let mut l2 = self.read_block_or_empty(node.data().level3[i3])?;
let mut l2 = self.read_block_or_empty(level_data(node)?.level3[i3])?;
let mut l1 = self.read_block_or_empty(l2.data().ptrs[i2])?;
let mut l0 = self.read_block_or_empty(l1.data().ptrs[i1])?;
l0.data_mut().ptrs[i0] = ptr;
l1.data_mut().ptrs[i1] = self.sync_block(l0)?;
l2.data_mut().ptrs[i2] = self.sync_block(l1)?;
node.data_mut().level3[i3] = self.sync_block(l2)?;
l1.data_mut().ptrs[i1] = self.sync_block(node, l0)?;
l2.data_mut().ptrs[i2] = self.sync_block(node, l1)?;
level_data_mut(node)?.level3[i3] = self.sync_block(node, l2)?;
}
NodeLevel::L4(i4, i3, i2, i1, i0) => {
let mut l3 = self.read_block_or_empty(node.data().level4[i4])?;
let mut l3 = self.read_block_or_empty(level_data(node)?.level4[i4])?;
let mut l2 = self.read_block_or_empty(l3.data().ptrs[i3])?;
let mut l1 = self.read_block_or_empty(l2.data().ptrs[i2])?;
let mut l0 = self.read_block_or_empty(l1.data().ptrs[i1])?;
l0.data_mut().ptrs[i0] = ptr;
l1.data_mut().ptrs[i1] = self.sync_block(l0)?;
l2.data_mut().ptrs[i2] = self.sync_block(l1)?;
l3.data_mut().ptrs[i3] = self.sync_block(l2)?;
node.data_mut().level4[i4] = self.sync_block(l3)?;
l1.data_mut().ptrs[i1] = self.sync_block(node, l0)?;
l2.data_mut().ptrs[i2] = self.sync_block(node, l1)?;
l3.data_mut().ptrs[i3] = self.sync_block(node, l2)?;
level_data_mut(node)?.level4[i4] = self.sync_block(node, l3)?;
}
}
}
@@ -1453,6 +1546,36 @@ impl<'a, D: Disk> Transaction<'a, D> {
buf: &mut [u8],
) -> Result<usize> {
let node_size = node.data().size();
// Try reading from inline data
if let Some(inline_data) = node.data().inline_data() {
if offset >= node_size {
return Ok(0);
}
// Read as much as possible from inline data
let mut i = 0;
if offset < inline_data.len() as u64 {
let len = min(
buf.len() as u64,
min(node_size - offset, inline_data.len() as u64 - offset),
);
buf[i..len as usize]
.copy_from_slice(&inline_data[offset as usize..(offset + len) as usize]);
i += len as usize;
offset += len;
}
// Handle sparse data (outside of inline data)
while i < buf.len() && offset < node_size {
buf[i] = 0;
i += 1;
offset += 1;
}
return Ok(i);
}
let record_level = node.data().record_level();
let mut bytes_read = 0;
@@ -1546,7 +1669,7 @@ impl<'a, D: Disk> Transaction<'a, D> {
self.write_node_inner(node, &mut offset, &zeroes[start as usize..end as usize])?;
}
assert_eq!(offset, size);
} else {
} else if !node.data().has_inline_data() {
// Deallocate records
for record in
(size.div_ceil(record_level.bytes())..old_size / record_level.bytes()).rev()
@@ -1585,7 +1708,7 @@ impl<'a, D: Disk> Transaction<'a, D> {
Ok(())
}
pub fn write_node_inner(
fn write_node_inner_records(
&mut self,
node: &mut TreeData<Node>,
offset: &mut u64,
@@ -1594,7 +1717,8 @@ impl<'a, D: Disk> Transaction<'a, D> {
let mut node_changed = false;
let record_level = node.data().record_level();
let node_records = node.data().size().div_ceil(record_level.bytes());
let node_size = node.data().size();
let node_records = node_size.div_ceil(record_level.bytes());
let mut i = 0;
while i < buf.len() {
@@ -1605,30 +1729,73 @@ impl<'a, D: Disk> Transaction<'a, D> {
let mut record_ptr = if node_records > (*offset / record_level.bytes()) {
self.node_record_ptr(node, *offset / record_level.bytes())?
} else {
BlockPtr::null(level)
BlockPtr::null(BlockMeta::new(level))
};
let mut record = unsafe { self.read_record(record_ptr, level)? };
// If record has changed
if buf[i..i + len] != record.data()[j..j + len] {
unsafe {
// CoW record using its current level
let mut old_addr = record.swap_addr(self.allocate(record.addr().level())?);
// Update record in memory
record.data_mut()[j..j + len].copy_from_slice(&buf[i..i + len]);
// If the record was resized we need to dealloc the original ptr
if old_addr.is_null() {
old_addr = record_ptr.addr();
}
record.data_mut()[j..j + len].copy_from_slice(&buf[i..i + len]);
record_ptr = self.write_block(record)?;
if !old_addr.is_null() {
self.deallocate(old_addr);
// Handle record compression, if record is larger than one block
let decomp_level = record.addr().level();
if decomp_level.0 > 0 {
assert_eq!(decomp_level.bytes(), record.data().len() as u64);
match lz4_flex::compress_into(record.data(), &mut self.fs.compress_cache) {
Ok(comp_len) => {
let total_len = comp_len + 2;
// Maximum compressed record size is 64 KiB
if total_len <= 64 * 1024 {
let comp_level = BlockLevel::for_bytes(total_len as u64);
// Replace record with compressed record, if it saves space
if comp_level < decomp_level {
if let Some(mut comp) = RecordRaw::empty(comp_level) {
// First two bytes store compressed data length
comp[0] = comp_len as u8;
comp[1] = (comp_len >> 8) as u8;
comp[2..total_len]
.copy_from_slice(&self.fs.compress_cache[..comp_len]);
record = BlockData::new(
BlockAddr::null(BlockMeta::new_compressed(
comp_level,
decomp_level,
)),
comp,
);
}
}
}
}
Err(_err) => {
// Failures to compress can be ignored, with the original record data used
}
}
}
// CoW record using its current level
let new_addr = unsafe { self.allocate(node, record.addr().meta())? };
let mut old_addr = record.swap_addr(new_addr);
// If the record was resized we need to dealloc the original ptr
if old_addr.is_null() {
old_addr = record_ptr.addr();
}
// Write record to disk
//TODO: deallocate new_addr on failure?
record_ptr = unsafe { self.write_block(record)? };
// Update record pointer
self.sync_node_record_ptr(node, *offset / record_level.bytes(), record_ptr)?;
node_changed = true;
// Deallocate old record
if !old_addr.is_null() {
unsafe {
self.deallocate(node, old_addr);
}
}
}
i += len;
@@ -1643,6 +1810,49 @@ impl<'a, D: Disk> Transaction<'a, D> {
Ok(node_changed)
}
pub fn write_node_inner(
&mut self,
node: &mut TreeData<Node>,
offset: &mut u64,
buf: &[u8],
) -> Result<bool> {
let mut node_changed = false;
// Try writing to inline data
let node_size = node.data().size();
let convert_inline = if let Some(inline_data) = node.data_mut().inline_data_mut() {
let end = *offset + (buf.len() as u64);
if end < inline_data.len() as u64 {
inline_data[*offset as usize..end as usize].copy_from_slice(buf);
*offset += buf.len() as u64;
if node.data().size() < *offset {
node.data_mut().set_size(*offset);
}
return Ok(true);
} else {
Some(inline_data[..node_size as usize].to_vec())
}
} else {
None
};
if let Some(inline_data) = convert_inline {
// If inline data cannot fit, convert to records
let mut flags = node.data().flags();
flags.remove(NodeFlags::INLINE_DATA);
node.data_mut().set_flags(flags);
node.data_mut().level_data = NodeLevelData::default();
self.write_node_inner_records(node, &mut 0, &inline_data)?;
node_changed = true;
}
if self.write_node_inner_records(node, offset, buf)? {
node_changed = true;
}
Ok(node_changed)
}
/// Write the bytes at `buf` to `node` starting at `offset`.
pub fn write_node(
&mut self,
+2 -2
View File
@@ -224,7 +224,7 @@ impl<T> Default for TreePtr<T> {
#[cfg(test)]
mod tests {
use crate::{BlockAddr, BlockData};
use crate::{BlockAddr, BlockData, BlockMeta};
use super::*;
@@ -257,7 +257,7 @@ mod tests {
}
fn mock_block(addr: u64) -> BlockPtr<BlockRaw> {
let block_addr = unsafe { BlockAddr::new(addr, BlockLevel::default()) };
let block_addr = unsafe { BlockAddr::new(addr, BlockMeta::default()) };
BlockData::empty(block_addr).unwrap().create_ptr()
}