diff --git a/libcitadel/src/realmfs/resizer.rs b/libcitadel/src/realmfs/resizer.rs index 6b565ea..e536eea 100644 --- a/libcitadel/src/realmfs/resizer.rs +++ b/libcitadel/src/realmfs/resizer.rs @@ -23,8 +23,8 @@ impl ResizeSize { pub fn gigs(n: usize) -> Self { ResizeSize(BLOCKS_PER_GIG * n) - } + pub fn megs(n: usize) -> Self { ResizeSize(BLOCKS_PER_MEG * n) } @@ -45,8 +45,8 @@ impl ResizeSize { self.0 / BLOCKS_PER_MEG } - /// If the RealmFS needs to be resized to a larger size, returns the - /// recommended size. + /// If the RealmFS has less than `AUTO_RESIZE_MINIMUM_FREE` blocks free then choose a new + /// size to resize the filesystem to and return it. Otherwise, return `None` pub fn auto_resize_size(realmfs: &RealmFS) -> Option { let sb = match Superblock::load(realmfs.path(), 4096) { Ok(sb) => sb, @@ -56,22 +56,37 @@ impl ResizeSize { }, }; - 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)) + if free_blocks >= AUTO_RESIZE_MINIMUM_FREE.nblocks() { + return None; + } + + let metainfo_nblocks = realmfs.metainfo().nblocks(); + + 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 { - 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; + +/// 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]); impl Superblock { diff --git a/libcitadel/src/realmfs/update.rs b/libcitadel/src/realmfs/update.rs index 7f81032..6025804 100644 --- a/libcitadel/src/realmfs/update.rs +++ b/libcitadel/src/realmfs/update.rs @@ -76,8 +76,6 @@ impl <'a> Update<'a> { } self.realmfs.copy_image_file(self.target())?; - self.truncate_verity()?; - self.resize_image_file()?; Ok(()) } @@ -115,9 +113,8 @@ impl <'a> Update<'a> { } // 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 { - self.realmfs.metainfo().nblocks() + 1 + self.realmfs.metainfo().nblocks() } fn unmount_update_image(&mut self) { @@ -159,7 +156,8 @@ impl <'a> Update<'a> { } 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() .write(true) .open(&self.target) @@ -176,7 +174,7 @@ impl <'a> Update<'a> { if self.realmfs.header().has_flag(ImageHeader::FLAG_HASH_TREE) { 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"); } Ok(()) @@ -185,7 +183,7 @@ impl <'a> Update<'a> { // If resize was requested, adjust size of update copy of image file. fn resize_image_file(&self) -> Result<()> { let nblocks = match self.resize { - Some(rs) => rs.nblocks() + 1, + Some(rs) => rs.nblocks(), None => return Ok(()), }; @@ -224,7 +222,7 @@ impl <'a> Update<'a> { fn seal(&mut self) -> Result<()> { let nblocks = match self.resize { Some(rs) => rs.nblocks(), - None => self.metainfo_nblock_size() - 1, + None => self.metainfo_nblock_size(), }; 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()))?; 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()))?; - // 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() .ok_or_else(|| format_err!("no root hash returned from verity format operation"))?; 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"); 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");