Add resizing support
This commit is contained in:
Generated
+23
@@ -295,6 +295,15 @@ dependencies = [
|
||||
"wasi 0.14.7+wasi-0.2.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humansize"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7"
|
||||
dependencies = [
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.4"
|
||||
@@ -340,6 +349,12 @@ version = "0.2.176"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.10"
|
||||
@@ -397,6 +412,12 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parse-size"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "487f2ccd1e17ce8c1bfab3a65c89525af41cfad4c8659021a1e9a2aacd73b89b"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.32"
|
||||
@@ -525,10 +546,12 @@ dependencies = [
|
||||
"env_logger",
|
||||
"fuser",
|
||||
"getrandom 0.2.16",
|
||||
"humansize",
|
||||
"libc",
|
||||
"libredox",
|
||||
"log",
|
||||
"lz4_flex",
|
||||
"parse-size",
|
||||
"range-tree",
|
||||
"redox-path",
|
||||
"redox-scheme",
|
||||
|
||||
+10
@@ -36,6 +36,12 @@ path = "src/bin/mkfs.rs"
|
||||
doc = false
|
||||
required-features = ["std"]
|
||||
|
||||
[[bin]]
|
||||
name = "redoxfs-resize"
|
||||
path = "src/bin/resize.rs"
|
||||
doc = false
|
||||
required-features = ["std"]
|
||||
|
||||
[dependencies]
|
||||
aes = { version = "0.8", default-features = false }
|
||||
argon2 = { version = "0.4", default-features = false, features = ["alloc"] }
|
||||
@@ -44,10 +50,12 @@ bitflags = "2"
|
||||
endian-num = "0.1"
|
||||
env_logger = { version = "0.11", optional = true }
|
||||
getrandom = { version = "0.2.5", optional = true }
|
||||
humansize = { version = "2", 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"] }
|
||||
parse-size = { version = "1", optional = true }
|
||||
range-tree = { version = "0.1", optional = true }
|
||||
redox-path = "0.3.0"
|
||||
redox-scheme = { version = "0.7.0", optional = true }
|
||||
@@ -63,8 +71,10 @@ std = [
|
||||
"env_logger",
|
||||
"fuser",
|
||||
"getrandom",
|
||||
"humansize",
|
||||
"libc",
|
||||
"libredox",
|
||||
"parse-size",
|
||||
"range-tree",
|
||||
"termion",
|
||||
"time",
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
use std::{env, process};
|
||||
|
||||
use humansize::{format_size, BINARY, DECIMAL};
|
||||
use redoxfs::{BlockAddr, BlockMeta, Disk, DiskFile, FileSystem};
|
||||
use uuid::Uuid;
|
||||
|
||||
fn resize<D: Disk>(fs: &mut FileSystem<D>, size_arg: String) -> Result<(), String> {
|
||||
let disk_size = fs
|
||||
.disk
|
||||
.size()
|
||||
.map_err(|err| format!("failed to read disk size: {}", err))?;
|
||||
|
||||
// Find contiguous free region
|
||||
//TODO: better error management
|
||||
let mut last_free = None;
|
||||
let mut last_end = 0;
|
||||
fs.tx(|tx| {
|
||||
let mut alloc_ptr = tx.header.alloc;
|
||||
while !alloc_ptr.is_null() {
|
||||
let alloc = tx.read_block(alloc_ptr)?;
|
||||
alloc_ptr = alloc.data().prev;
|
||||
for entry in alloc.data().entries.iter() {
|
||||
let count = entry.count();
|
||||
if count <= 0 {
|
||||
continue;
|
||||
}
|
||||
let end = entry.index() + count as u64;
|
||||
if end > last_end {
|
||||
last_free = Some(*entry);
|
||||
last_end = end;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.map_err(|err| format!("failed to read alloc log: {}", err))?;
|
||||
|
||||
let old_size = fs.header.size();
|
||||
let min_size = if let Some(entry) = last_free {
|
||||
entry.index() * redoxfs::BLOCK_SIZE
|
||||
} else {
|
||||
old_size
|
||||
};
|
||||
let max_size = disk_size - (fs.block * redoxfs::BLOCK_SIZE);
|
||||
|
||||
let new_size = match size_arg.to_lowercase().as_str() {
|
||||
"min" | "minimum" => min_size,
|
||||
"" | "max" | "maximum" => max_size,
|
||||
_ => match parse_size::parse_size(&size_arg) {
|
||||
Ok(new_size) => {
|
||||
if new_size < min_size {
|
||||
return Err(format!(
|
||||
"requested size {} is smaller than {} by {}",
|
||||
new_size,
|
||||
min_size,
|
||||
min_size - new_size
|
||||
));
|
||||
}
|
||||
|
||||
if new_size > max_size {
|
||||
return Err(format!(
|
||||
"requested size {} is larger than {} by {}",
|
||||
new_size,
|
||||
max_size,
|
||||
new_size - max_size
|
||||
));
|
||||
}
|
||||
|
||||
new_size
|
||||
}
|
||||
Err(err) => {
|
||||
return Err(format!(
|
||||
"failed to parse size argument {:?}: {}",
|
||||
size_arg, err
|
||||
));
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
println!(
|
||||
"minimum size: {} ({})",
|
||||
format_size(min_size, DECIMAL),
|
||||
format_size(min_size, BINARY)
|
||||
);
|
||||
println!(
|
||||
"maximum size: {} ({})",
|
||||
format_size(max_size, DECIMAL),
|
||||
format_size(max_size, BINARY)
|
||||
);
|
||||
println!(
|
||||
"new size: {} ({})",
|
||||
format_size(new_size, DECIMAL),
|
||||
format_size(new_size, BINARY)
|
||||
);
|
||||
|
||||
let old_blocks = old_size / redoxfs::BLOCK_SIZE;
|
||||
let new_blocks = new_size / redoxfs::BLOCK_SIZE;
|
||||
let (start, end, shrink) = if new_size == old_size {
|
||||
println!("already requested size");
|
||||
return Ok(());
|
||||
} else if new_size < old_size {
|
||||
println!("shrinking by {}", old_size - new_size);
|
||||
(new_blocks, old_blocks, true)
|
||||
} else {
|
||||
println!("growing by {}", new_size - old_size);
|
||||
(old_blocks, new_blocks, false)
|
||||
};
|
||||
|
||||
// Allocate or deallocate blocks as needed
|
||||
unsafe {
|
||||
let allocator = fs.allocator_mut();
|
||||
for index in start..end {
|
||||
if shrink {
|
||||
//TODO: replace assert with error?
|
||||
let addr = BlockAddr::new(index as u64, BlockMeta::default());
|
||||
assert_eq!(allocator.allocate_exact(addr), Some(addr));
|
||||
} else {
|
||||
let addr = BlockAddr::new(index as u64, BlockMeta::default());
|
||||
allocator.deallocate(addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fs.tx(|tx| {
|
||||
// Update header
|
||||
tx.header.size = new_size.into();
|
||||
tx.header_changed = true;
|
||||
|
||||
// Sync with squash
|
||||
tx.sync(true)?;
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.map_err(|err| format!("transaction failed: {}", err))
|
||||
}
|
||||
|
||||
fn main() {
|
||||
env_logger::init();
|
||||
|
||||
let mut args = env::args().skip(1);
|
||||
|
||||
let disk_path = if let Some(path) = args.next() {
|
||||
path
|
||||
} else {
|
||||
eprintln!("redoxfs-resize: no new disk image provided");
|
||||
eprintln!("redoxfs-resize NEW-DISK [SIZE]");
|
||||
process::exit(1);
|
||||
};
|
||||
|
||||
let size_arg = args.next().unwrap_or_default();
|
||||
|
||||
let disk = match DiskFile::open(&disk_path) {
|
||||
Ok(disk) => disk,
|
||||
Err(err) => {
|
||||
eprintln!(
|
||||
"redoxfs-resize: failed to open disk image {}: {}",
|
||||
disk_path, err
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let mut fs = match FileSystem::open(disk, None, None, true) {
|
||||
Ok(fs) => fs,
|
||||
Err(err) => {
|
||||
eprintln!(
|
||||
"redoxfs-resize: failed to open filesystem on {}: {}",
|
||||
disk_path, err
|
||||
);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
match resize(&mut fs, size_arg) {
|
||||
Ok(()) => {}
|
||||
Err(err) => {
|
||||
eprintln!(
|
||||
"redoxfs-resize: failed to resize filesystem on {}: {}",
|
||||
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-resize: resized filesystem on {}", disk_path);
|
||||
println!("\tuuid: {}", uuid.hyphenated());
|
||||
println!(
|
||||
"\tsize: {} ({})",
|
||||
format_size(size, DECIMAL),
|
||||
format_size(size, BINARY)
|
||||
);
|
||||
println!(
|
||||
"\tused: {} ({})",
|
||||
format_size(used, DECIMAL),
|
||||
format_size(used, BINARY)
|
||||
);
|
||||
println!(
|
||||
"\tfree: {} ({})",
|
||||
format_size(free, DECIMAL),
|
||||
format_size(free, BINARY)
|
||||
);
|
||||
}
|
||||
+1
-1
@@ -21,7 +21,7 @@ impl BlockAddr {
|
||||
const LEVEL_MASK: u64 = 0xF;
|
||||
|
||||
// Unsafe because this can create invalid blocks
|
||||
pub(crate) unsafe fn new(index: u64, meta: BlockMeta) -> Self {
|
||||
pub 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");
|
||||
|
||||
@@ -237,6 +237,11 @@ impl<D: Disk> FileSystem<D> {
|
||||
&self.allocator
|
||||
}
|
||||
|
||||
/// Unsafe as it can corrupt the filesystem
|
||||
pub unsafe fn allocator_mut(&mut self) -> &mut Allocator {
|
||||
&mut self.allocator
|
||||
}
|
||||
|
||||
/// Reset allocator to state stored on disk
|
||||
///
|
||||
/// # Safety
|
||||
|
||||
Reference in New Issue
Block a user