Use less memory when decompressing images for install

1. Delete source image before decompressing temporary file
2. Decompress the images serially instead of spawning a thread for each
   one.
This commit is contained in:
Bruce Leidl 2022-05-27 18:06:52 -04:00
parent 0832ddfa9f
commit 3dbfda2c40
6 changed files with 33 additions and 16 deletions

View File

@ -20,7 +20,7 @@ pub fn live_rootfs() -> Result<()> {
} }
pub fn live_setup() -> Result<()> { pub fn live_setup() -> Result<()> {
decompress_images()?; decompress_images(true)?;
info!("Starting live setup"); info!("Starting live setup");
let live = Installer::new_livesetup(); let live = Installer::new_livesetup();
live.run() live.run()
@ -131,14 +131,21 @@ fn find_rootfs_image() -> Result<ResourceImage> {
bail!("unable to find rootfs resource image in {}", IMAGE_DIRECTORY) bail!("unable to find rootfs resource image in {}", IMAGE_DIRECTORY)
} }
fn decompress_images() -> Result<()> { fn decompress_images(sync: bool) -> Result<()> {
info!("Decompressing images"); info!("Decompressing images");
let mut threads = Vec::new(); let mut threads = Vec::new();
util::read_directory("/run/citadel/images", |dent| { util::read_directory("/run/citadel/images", |dent| {
if dent.path().extension() == Some(OsStr::new("img")) { if dent.path().extension() == Some(OsStr::new("img")) {
if let Ok(image) = ResourceImage::from_path(&dent.path()) { if let Ok(image) = ResourceImage::from_path(&dent.path()) {
if image.is_compressed() { if image.is_compressed() {
threads.push(decompress_one_image(image)); if sync {
if let Err(err) = decompress_one_image_sync(image) {
warn!("Error: {}", err);
}
} else {
threads.push(decompress_one_image(image));
}
} }
} }
} }
@ -146,22 +153,28 @@ fn decompress_images() -> Result<()> {
})?; })?;
for t in threads { for t in threads {
t.join().unwrap()?; if let Err(err) = t.join().unwrap() {
warn!("Error: {}", err);
}
} }
info!("Finished decompressing images"); info!("Finished decompressing images");
Ok(()) Ok(())
} }
fn decompress_one_image(image: ResourceImage) -> JoinHandle<Result<()>> { fn decompress_one_image_sync(image: ResourceImage) -> Result<()> {
thread::spawn(move || {
let start = Instant::now(); let start = Instant::now();
info!("Decompressing {}", image.path().display()); info!("Decompressing {}", image.path().display());
image.decompress()?; image.decompress(true)
.map_err(|e| format_err!("Failed to decompress image file {}: {}", image.path().display(), e))?;
cmd!("/usr/bin/du", "-h {}", image.path().display())?; cmd!("/usr/bin/du", "-h {}", image.path().display())?;
info!("Decompress {:?} finished in {} seconds", info!("Decompress {:?} finished in {} seconds",
image.path().file_name().unwrap(), image.path().file_name().unwrap(),
start.elapsed().as_secs()); start.elapsed().as_secs());
Ok(()) Ok(())
}
fn decompress_one_image(image: ResourceImage) -> JoinHandle<Result<()>> {
thread::spawn(move || {
decompress_one_image_sync(image)
}) })
} }

View File

@ -23,7 +23,7 @@ pub fn setup_rootfs_resource(rootfs: &ResourceImage) -> Result<()> {
fn setup_resource_unverified(img: &ResourceImage) -> Result<()> { fn setup_resource_unverified(img: &ResourceImage) -> Result<()> {
if img.is_compressed() { if img.is_compressed() {
img.decompress()?; img.decompress(false)?;
} }
let loopdev = LoopDevice::create(img.path(), Some(4096), true)?; let loopdev = LoopDevice::create(img.path(), Some(4096), true)?;
info!("Loop device created: {}", loopdev); info!("Loop device created: {}", loopdev);

View File

@ -275,7 +275,7 @@ fn decompress(arg_matches: &ArgMatches) -> Result<()> {
if !img.is_compressed() { if !img.is_compressed() {
info!("Image is not compressed, not decompressing."); info!("Image is not compressed, not decompressing.");
} else { } else {
img.decompress()?; img.decompress(false)?;
} }
Ok(()) Ok(())
} }

View File

@ -122,7 +122,7 @@ fn install_image(path: &Path, flags: u32) -> Result<()> {
// dmverity hash tree. // dmverity hash tree.
fn prepare_image(image: &ResourceImage, flags: u32) -> Result<()> { fn prepare_image(image: &ResourceImage, flags: u32) -> Result<()> {
if image.is_compressed() { if image.is_compressed() {
image.decompress()?; image.decompress(false)?;
} }
if flags & FLAG_SKIP_SHA == 0 { if flags & FLAG_SKIP_SHA == 0 {

View File

@ -54,7 +54,7 @@ impl Exec {
.map_err(context!("failed to execute command {}", self.cmd_name))?; .map_err(context!("failed to execute command {}", self.cmd_name))?;
for line in BufReader::new(result.stderr.as_slice()).lines() { for line in BufReader::new(result.stderr.as_slice()).lines() {
verbose!(" {}", line.unwrap()); warn!(" {}", line.unwrap());
} }
self.check_cmd_status(result.status) self.check_cmd_status(result.status)
} }

View File

@ -138,7 +138,7 @@ impl ResourceImage {
self.header.has_flag(ImageHeader::FLAG_HASH_TREE) self.header.has_flag(ImageHeader::FLAG_HASH_TREE)
} }
pub fn decompress(&self) -> Result<()> { pub fn decompress(&self, early_remove: bool) -> Result<()> {
if !self.is_compressed() { if !self.is_compressed() {
return Ok(()) return Ok(())
} }
@ -154,6 +154,10 @@ impl ResourceImage {
io::copy(&mut reader, &mut out) io::copy(&mut reader, &mut out)
.map_err(context!("error copying image file {:?} to temporary file", self.path()))?; .map_err(context!("error copying image file {:?} to temporary file", self.path()))?;
if early_remove {
util::remove_file(self.path())?;
}
util::xz_decompress(xzfile)?; util::xz_decompress(xzfile)?;
let tmpfile = self.path.with_extension("tmp"); let tmpfile = self.path.with_extension("tmp");
util::rename(&tmpfile, self.path())?; util::rename(&tmpfile, self.path())?;
@ -218,7 +222,7 @@ impl ResourceImage {
return Ok(()) return Ok(())
} }
if self.is_compressed() { if self.is_compressed() {
self.decompress()?; self.decompress(false)?;
} }
info!("Generating dm-verity hash tree for image {}", self.path.display()); info!("Generating dm-verity hash tree for image {}", self.path.display());
let verity = self.verity()?; let verity = self.verity()?;
@ -239,7 +243,7 @@ impl ResourceImage {
pub fn generate_shasum(&self) -> Result<String> { pub fn generate_shasum(&self) -> Result<String> {
if self.is_compressed() { if self.is_compressed() {
self.decompress()?; self.decompress(false)?;
} }
info!("Calculating sha256 of image"); info!("Calculating sha256 of image");
let output = util::exec_cmdline_pipe_input("sha256sum", "-", self.path(), util::FileRange::Range{offset: 4096, len: self.metainfo().nblocks() * 4096}) let output = util::exec_cmdline_pipe_input("sha256sum", "-", self.path(), util::FileRange::Range{offset: 4096, len: self.metainfo().nblocks() * 4096})
@ -256,7 +260,7 @@ impl ResourceImage {
info!("loop mounting image to {} (noverity)", self.mount_path().display()); info!("loop mounting image to {} (noverity)", self.mount_path().display());
if self.is_compressed() { if self.is_compressed() {
self.decompress()?; self.decompress(false)?;
} }
let loopdev = LoopDevice::create(self.path(), Some(4096), true)?; let loopdev = LoopDevice::create(self.path(), Some(4096), true)?;