Merge branch 'v8' into 'master'
v8 See merge request redox-os/redoxfs!95
This commit is contained in:
Generated
+8
@@ -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
@@ -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 }
|
||||
|
||||
Generated
+40
-19
@@ -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
@@ -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() {
|
||||
|
||||
@@ -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
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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),
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user