Skip to content

Commit

Permalink
compose: rewrite rpmdb timestamps if SOURCE_DATE_EPOCH is set
Browse files Browse the repository at this point in the history
RPM package headers may contain several values that are either
timestamps or derived from timestamps. These introduce variation
into the RPM database.

This patch looks for the SOURCE_DATE_EPOCH environment variable
and, if that is present, rewrites these values to match the
value it contains.
  • Loading branch information
jeamland committed Oct 21, 2021
1 parent 622dbbd commit 9547627
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 2 deletions.
5 changes: 5 additions & 0 deletions rust/src/composepost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::bwrap::Bubblewrap;
use crate::cxxrsutil::*;
use crate::ffi::BubblewrapMutability;
use crate::ffiutil::ffi_view_openat_dir;
use crate::normalization;
use crate::passwd::PasswdDB;
use crate::treefile::Treefile;
use crate::{bwrap, importer};
Expand Down Expand Up @@ -980,6 +981,10 @@ fn rewrite_rpmdb_for_target_inner(rootfs_dfd: &openat::Dir) -> Result<()> {
let mut dbfd = Rc::try_unwrap(dbfd).unwrap();
dbfd.seek(std::io::SeekFrom::Start(0))?;

// In the interests of build stability, rewrite the INSTALLTIME and INSTALLTID tags
// to match SOURCE_DATE_EPOCH if it's present
normalization::rewrite_rpmdb_timestamps(&mut dbfd)?;

// Fork the target rpmdb to write the content from memory to disk
let mut bwrap = Bubblewrap::new_with_mutability(rootfs_dfd, BubblewrapMutability::RoFiles)?;
bwrap.append_child_argv(&["rpmdb", dbpath_arg.as_str(), "--importdb"]);
Expand Down
70 changes: 68 additions & 2 deletions rust/src/normalization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT

use crate::nameservice::shadow::parse_shadow_content;
use anyhow::Result;
use anyhow::{anyhow, Result};
use fn_error_context::context;
use lazy_static::lazy_static;
use std::io::{BufReader, Seek, SeekFrom};
use std::convert::TryInto;
use std::io::{BufReader, Read, Seek, SeekFrom, Write};

lazy_static! {
static ref SOURCE_DATE_EPOCH_RAW: Option<String> = std::env::var("SOURCE_DATE_EPOCH").ok();
Expand Down Expand Up @@ -45,3 +46,68 @@ pub(crate) fn normalize_etc_shadow(rootfs: &openat::Dir) -> Result<()> {

Ok(())
}

const RPM_HEADER_MAGIC: [u8; 8] = [0x8E, 0xAD, 0xE8, 0x01, 0x00, 0x00, 0x00, 0x00];
const RPMTAG_INSTALLTIME: u32 = 1008;
const RPMTAG_INSTALLTID: u32 = 1128;

#[context("Rewriting rpmdb timestamps for build stability")]
pub(crate) fn rewrite_rpmdb_timestamps<F: Read + Write + Seek>(rpmdb: &mut F) -> Result<()> {
let mut buffer: [u8; 16] = [0; 16];

let source_date_epoch = if let Some(source_date_epoch) = *SOURCE_DATE_EPOCH {
source_date_epoch as u32
} else {
return Ok(());
};

loop {
// Read in a header record
let bytes = rpmdb.read(&mut buffer)?;
if bytes == 0 {
break;
}

// Make sure things are sane
if buffer[..8] != RPM_HEADER_MAGIC {
return Err(anyhow!("Bad RPM header magic in RPM database"));
}

// Grab the count of index records and the size of the data blob
let record_count = u32::from_be_bytes(buffer[8..12].try_into()?);
let data_size = u32::from_be_bytes(buffer[12..].try_into()?);

// Loop through the records looking for ones that point at things
// that are, or are derived from, timestamps
let mut offsets = Vec::new();
for _ in 0..record_count {
let bytes = rpmdb.read(&mut buffer)?;
if bytes == 0 {
return Err(anyhow!("Incomplete RPM header in RPM database"));
}

let tag = u32::from_be_bytes(buffer[..4].try_into()?);
if tag == RPMTAG_INSTALLTIME || tag == RPMTAG_INSTALLTID {
offsets.push(u32::from_be_bytes(buffer[8..12].try_into()?));
}
}

// Work through the data blob replacing the timestamp-derived values
// with the timestamp we want
offsets.sort_unstable();
let mut offset = 0;
for value_offset in offsets {
rpmdb.seek(std::io::SeekFrom::Current((value_offset - offset) as i64))?;
rpmdb.write_all(&source_date_epoch.to_be_bytes())?;
offset = value_offset + std::mem::size_of::<u32>() as u32;
}

// Move to the next record
rpmdb.seek(std::io::SeekFrom::Current((data_size - offset) as i64))?;
}

// Seek back to the start
rpmdb.seek(std::io::SeekFrom::Start(0))?;

Ok(())
}

0 comments on commit 9547627

Please sign in to comment.