forked from brl/citadel-tools
112 lines
3.2 KiB
Rust
112 lines
3.2 KiB
Rust
use std::fs::File;
|
|
use std::io::{Read,Seek,SeekFrom};
|
|
use std::path::Path;
|
|
|
|
use byteorder::{ByteOrder,LittleEndian};
|
|
|
|
use crate::{RealmFS,Result};
|
|
|
|
const BLOCK_SIZE: usize = 4096;
|
|
const BLOCKS_PER_MEG: usize = (1024 * 1024) / BLOCK_SIZE;
|
|
const BLOCKS_PER_GIG: usize = 1024 * BLOCKS_PER_MEG;
|
|
|
|
// If less than 1gb remaining space
|
|
const AUTO_RESIZE_MINIMUM_FREE: ResizeSize = ResizeSize(BLOCKS_PER_GIG);
|
|
// ... add 4gb to size of image
|
|
const AUTO_RESIZE_INCREASE_SIZE: ResizeSize = ResizeSize(4 * BLOCKS_PER_GIG);
|
|
|
|
|
|
#[derive(Copy,Clone)]
|
|
pub struct ResizeSize(usize);
|
|
|
|
impl ResizeSize {
|
|
|
|
pub fn gigs(n: usize) -> Self {
|
|
ResizeSize(BLOCKS_PER_GIG * n)
|
|
|
|
}
|
|
pub fn megs(n: usize) -> Self {
|
|
ResizeSize(BLOCKS_PER_MEG * n)
|
|
}
|
|
|
|
pub fn blocks(n: usize) -> Self {
|
|
ResizeSize(n)
|
|
}
|
|
|
|
pub fn nblocks(&self) -> usize {
|
|
self.0
|
|
}
|
|
|
|
pub fn size_in_gb(&self) -> usize {
|
|
self.0 / BLOCKS_PER_GIG
|
|
}
|
|
|
|
pub fn size_in_mb(&self) -> usize {
|
|
self.0 / BLOCKS_PER_MEG
|
|
}
|
|
|
|
/// If the RealmFS needs to be resized to a larger size, returns the
|
|
/// recommended size.
|
|
pub fn auto_resize_size(realmfs: &RealmFS) -> Option<ResizeSize> {
|
|
let sb = match Superblock::load(realmfs.path(), 4096) {
|
|
Ok(sb) => sb,
|
|
Err(e) => {
|
|
warn!("Error reading superblock from {}: {}", realmfs.path().display(), e);
|
|
return None;
|
|
},
|
|
};
|
|
|
|
sb.free_block_count();
|
|
let free_blocks = sb.free_block_count() as usize;
|
|
if free_blocks < AUTO_RESIZE_MINIMUM_FREE.nblocks() {
|
|
let metainfo_nblocks = realmfs.metainfo().nblocks() + 1;
|
|
let increase_multiple = metainfo_nblocks / AUTO_RESIZE_INCREASE_SIZE.nblocks();
|
|
let grow_size = (increase_multiple + 1) * AUTO_RESIZE_INCREASE_SIZE.nblocks();
|
|
let mask = grow_size - 1;
|
|
let grow_blocks = (free_blocks + mask) & !mask;
|
|
Some(ResizeSize::blocks(grow_blocks))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
const SUPERBLOCK_SIZE: usize = 1024;
|
|
pub struct Superblock([u8; SUPERBLOCK_SIZE]);
|
|
|
|
impl Superblock {
|
|
fn new() -> Self {
|
|
Superblock([0u8; SUPERBLOCK_SIZE])
|
|
}
|
|
|
|
pub fn load(path: impl AsRef<Path>, offset: u64) -> Result<Self> {
|
|
let path = path.as_ref();
|
|
let mut sb = Self::new();
|
|
let mut file = File::open(path)
|
|
.map_err(context!("failed to open image file {:?}", path))?;
|
|
file.seek(SeekFrom::Start(1024 + offset))
|
|
.map_err(context!("failed to seek to offset {} of image file {:?}", 1024 + offset, path))?;
|
|
file.read_exact(&mut sb.0)
|
|
.map_err(context!("error reading superblock from image file {:?}", path))?;
|
|
Ok(sb)
|
|
}
|
|
|
|
pub fn free_block_count(&self) -> u64 {
|
|
self.split_u64(0x0C, 0x158)
|
|
}
|
|
|
|
fn u32(&self, offset: usize) -> u32 {
|
|
LittleEndian::read_u32(self.at(offset))
|
|
}
|
|
|
|
fn split_u64(&self, offset_lo: usize, offset_hi: usize) -> u64 {
|
|
let lo = u64::from(self.u32(offset_lo));
|
|
let hi = u64::from(self.u32(offset_hi));
|
|
(hi << 32) | lo
|
|
}
|
|
|
|
fn at(&self, offset: usize) -> &[u8] {
|
|
&self.0[offset..]
|
|
}
|
|
}
|