Fix broken realmfs autoresize

This commit is contained in:
Bruce Leidl 2024-09-06 10:02:23 -04:00
parent 2dc8bf2922
commit 24f786cf75
2 changed files with 34 additions and 30 deletions

View File

@ -23,8 +23,8 @@ impl ResizeSize {
pub fn gigs(n: usize) -> Self { pub fn gigs(n: usize) -> Self {
ResizeSize(BLOCKS_PER_GIG * n) ResizeSize(BLOCKS_PER_GIG * n)
} }
pub fn megs(n: usize) -> Self { pub fn megs(n: usize) -> Self {
ResizeSize(BLOCKS_PER_MEG * n) ResizeSize(BLOCKS_PER_MEG * n)
} }
@ -45,8 +45,8 @@ impl ResizeSize {
self.0 / BLOCKS_PER_MEG self.0 / BLOCKS_PER_MEG
} }
/// If the RealmFS needs to be resized to a larger size, returns the /// If the RealmFS has less than `AUTO_RESIZE_MINIMUM_FREE` blocks free then choose a new
/// recommended size. /// size to resize the filesystem to and return it. Otherwise, return `None`
pub fn auto_resize_size(realmfs: &RealmFS) -> Option<ResizeSize> { pub fn auto_resize_size(realmfs: &RealmFS) -> Option<ResizeSize> {
let sb = match Superblock::load(realmfs.path(), 4096) { let sb = match Superblock::load(realmfs.path(), 4096) {
Ok(sb) => sb, Ok(sb) => sb,
@ -56,22 +56,37 @@ impl ResizeSize {
}, },
}; };
sb.free_block_count();
let free_blocks = sb.free_block_count() as usize; let free_blocks = sb.free_block_count() as usize;
if free_blocks < AUTO_RESIZE_MINIMUM_FREE.nblocks() { if free_blocks >= AUTO_RESIZE_MINIMUM_FREE.nblocks() {
let metainfo_nblocks = realmfs.metainfo().nblocks() + 1; return None;
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 metainfo_nblocks = realmfs.metainfo().nblocks();
let grow_blocks = (free_blocks + mask) & !mask;
Some(ResizeSize::blocks(grow_blocks)) if metainfo_nblocks >= AUTO_RESIZE_INCREASE_SIZE.nblocks() {
return Some(ResizeSize::blocks(metainfo_nblocks + AUTO_RESIZE_INCREASE_SIZE.nblocks()))
}
// If current size is under 4GB (AUTO_RESIZE_INCREASE_SIZE) and raising size to 4GB will create more than the
// minimum free space (1GB) then just do that.
if free_blocks + (AUTO_RESIZE_INCREASE_SIZE.nblocks() - metainfo_nblocks) >= AUTO_RESIZE_MINIMUM_FREE.nblocks() {
Some(AUTO_RESIZE_INCREASE_SIZE)
} else { } else {
None // Otherwise for original size under 4GB, since raising to 4GB is not enough,
// raise size to 8GB
Some(ResizeSize::blocks(AUTO_RESIZE_INCREASE_SIZE.nblocks() * 2))
} }
} }
} }
const SUPERBLOCK_SIZE: usize = 1024; const SUPERBLOCK_SIZE: usize = 1024;
/// An EXT4 superblock structure.
///
/// A class for reading the first superblock from an EXT4 filesystem
/// and parsing the Free Block Count field. No other fields are parsed
/// since this is the only information needed for the resize operation.
///
pub struct Superblock([u8; SUPERBLOCK_SIZE]); pub struct Superblock([u8; SUPERBLOCK_SIZE]);
impl Superblock { impl Superblock {

View File

@ -76,8 +76,6 @@ impl <'a> Update<'a> {
} }
self.realmfs.copy_image_file(self.target())?; self.realmfs.copy_image_file(self.target())?;
self.truncate_verity()?;
self.resize_image_file()?;
Ok(()) Ok(())
} }
@ -115,9 +113,8 @@ impl <'a> Update<'a> {
} }
// Return size of image file in blocks based on metainfo `nblocks` field. // Return size of image file in blocks based on metainfo `nblocks` field.
// Include header block in count so add one block
fn metainfo_nblock_size(&self) -> usize { fn metainfo_nblock_size(&self) -> usize {
self.realmfs.metainfo().nblocks() + 1 self.realmfs.metainfo().nblocks()
} }
fn unmount_update_image(&mut self) { fn unmount_update_image(&mut self) {
@ -159,7 +156,8 @@ impl <'a> Update<'a> {
} }
fn set_target_len(&self, nblocks: usize) -> Result<()> { fn set_target_len(&self, nblocks: usize) -> Result<()> {
let len = (nblocks * BLOCK_SIZE) as u64; // add one block for header block
let len = ((nblocks + 1) * BLOCK_SIZE) as u64;
let f = fs::OpenOptions::new() let f = fs::OpenOptions::new()
.write(true) .write(true)
.open(&self.target) .open(&self.target)
@ -176,7 +174,7 @@ impl <'a> Update<'a> {
if self.realmfs.header().has_flag(ImageHeader::FLAG_HASH_TREE) { if self.realmfs.header().has_flag(ImageHeader::FLAG_HASH_TREE) {
self.set_target_len(metainfo_nblocks)?; self.set_target_len(metainfo_nblocks)?;
} else if file_nblocks > metainfo_nblocks { } else if file_nblocks > (metainfo_nblocks + 1) {
warn!("RealmFS image size was greater than length indicated by metainfo.nblocks but FLAG_HASH_TREE not set"); warn!("RealmFS image size was greater than length indicated by metainfo.nblocks but FLAG_HASH_TREE not set");
} }
Ok(()) Ok(())
@ -185,7 +183,7 @@ impl <'a> Update<'a> {
// If resize was requested, adjust size of update copy of image file. // If resize was requested, adjust size of update copy of image file.
fn resize_image_file(&self) -> Result<()> { fn resize_image_file(&self) -> Result<()> {
let nblocks = match self.resize { let nblocks = match self.resize {
Some(rs) => rs.nblocks() + 1, Some(rs) => rs.nblocks(),
None => return Ok(()), None => return Ok(()),
}; };
@ -224,7 +222,7 @@ impl <'a> Update<'a> {
fn seal(&mut self) -> Result<()> { fn seal(&mut self) -> Result<()> {
let nblocks = match self.resize { let nblocks = match self.resize {
Some(rs) => rs.nblocks(), Some(rs) => rs.nblocks(),
None => self.metainfo_nblock_size() - 1, None => self.metainfo_nblock_size(),
}; };
let salt = hex::encode(randombytes(32)); let salt = hex::encode(randombytes(32));
@ -232,20 +230,11 @@ impl <'a> Update<'a> {
.map_err(context!("failed to create verity context for realmfs update image {:?}", self.target()))?; .map_err(context!("failed to create verity context for realmfs update image {:?}", self.target()))?;
let output = verity.generate_image_hashtree_with_salt(&salt, nblocks) let output = verity.generate_image_hashtree_with_salt(&salt, nblocks)
.map_err(context!("failed to generate dm-verity hashtree for realmfs update image {:?}", self.target()))?; .map_err(context!("failed to generate dm-verity hashtree for realmfs update image {:?}", self.target()))?;
// XXX passes metainfo for nblocks
//let output = Verity::new(&self.target).generate_image_hashtree_with_salt(&self.realmfs.metainfo(), &salt)?;
let root_hash = output.root_hash() let root_hash = output.root_hash()
.ok_or_else(|| format_err!("no root hash returned from verity format operation"))?; .ok_or_else(|| format_err!("no root hash returned from verity format operation"))?;
info!("root hash is {}", output.root_hash().unwrap()); info!("root hash is {}", output.root_hash().unwrap());
/*
let nblocks = match self.resize {
Some(rs) => rs.nblocks(),
None => self.metainfo_nblock_size() - 1,
};
*/
info!("Signing new image with user realmfs keys"); info!("Signing new image with user realmfs keys");
let metainfo_bytes = RealmFS::generate_metainfo(self.realmfs.name(), nblocks, salt.as_str(), root_hash); let metainfo_bytes = RealmFS::generate_metainfo(self.realmfs.name(), nblocks, salt.as_str(), root_hash);
let keys = self.realmfs.sealing_keys().expect("No sealing keys"); let keys = self.realmfs.sealing_keys().expect("No sealing keys");