Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up and expand tests #115

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
out
**/*.rpm
Cargo.lock
!test_assets/*
!tests/assets/**
dnf-cache
.idea
.idea
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `Dependency::config()`, `Dependency::user()`, `Dependency::group()`
- `PackageBuilder::verify_script()`
- `PackageBuilder::group()` and `PackageBuilder::packager()`
- `PackageBuilder::with_file_contents()`
- Added support for the automatic user/group creation feature in rpm 4.19

### Changed
Expand All @@ -29,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed

- Using file capabilities now adds the appropriate rpmlib() dependency
- RPM packages that use large files (>4gb) now correctly declare rpmlib() dependency

## 0.13.1

Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ serial_test = "3.0"
pretty_assertions = "1.3.0"
gethostname = "0.4"
hex-literal = "0.4"
bstr = "1.4.0"

[features]
default = ["signature-pgp"]
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,24 @@ This library does not build software like rpmbuild. It is meant for finished art
```rust
use rpm::signature::pgp::{Signer, Verifier};

let raw_secret_key = std::fs::read("./test_assets/secret_key.asc")?;
let raw_secret_key = std::fs::read("./tests/assets/signing_keys/secret_ed25519.asc")?;
// It's recommended to use timestamp of last commit in your VCS
let source_date = 1_600_000_000;
let pkg = rpm::PackageBuilder::new("test", "1.0.0", "MIT", "x86_64", "some awesome package")
.compression(rpm::CompressionType::Gzip)
.with_file(
"./test_assets/awesome.toml",
"./tests/assets/SOURCES/example_config.toml",
rpm::FileOptions::new("/etc/awesome/config.toml")
.is_config()
.is_no_replace(),
)?
// file mode is inherited from source file
.with_file(
"./test_assets/awesome.py",
"./tests/assets/SOURCES/multiplication_tables.py",
rpm::FileOptions::new("/usr/bin/awesome"),
)?
.with_file(
"./test_assets/awesome.toml",
"./tests/assets/SOURCES/example_config.toml",
// you can set a custom mode and custom user too
rpm::FileOptions::new("/etc/awesome/second.toml")
.mode(rpm::FileMode::regular(0o644))
Expand Down Expand Up @@ -88,7 +88,7 @@ pkg.write_file("./awesome.rpm")?;

// reading
let raw_pub_key = std::fs::read("/path/to/gpg.key.pub")?;
let pkg = rpm::Package::open("test_assets/389-ds-base-devel-1.3.8.4-15.el7.x86_64.rpm")?;
let pkg = rpm::Package::open("tests/assets/RPMS/signed/noarch/rpm-basic-with-ed25519-2.3.4-5.el9.noarch.rpm")?;

let name = pkg.metadata.get_name()?;
let version = pkg.metadata.get_version()?;
Expand Down
2 changes: 1 addition & 1 deletion src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,7 @@ bitflags! {
const DONOTUSE = 1 << 2; // %%donotuse
const MISSINGOK = 1 << 3; // %%config(missingok)
const NOREPLACE = 1 << 4; // %%config(noreplace)
// const SPECFILE = 1 << 5; // first file in SRPM?
const SPECFILE = 1 << 5; // specfile, which is the first file in a source RPM
const GHOST = 1 << 6; // %%ghost
const LICENSE = 1 << 7; // %%license
const README = 1 << 8; // %%readme
Expand Down
13 changes: 5 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,22 @@
//! # let _ = env_logger::Builder::new().filter_level(log::LevelFilter::Trace).is_test(true).try_init();
//! # #[cfg(feature = "signature-pgp")]
//! # {
//! let raw_secret_key = std::fs::read("./test_assets/secret_key.asc")?;
//! let raw_secret_key = std::fs::read("./tests/assets/signing_keys/secret_rsa4096.asc")?;
//! // It's recommended to use timestamp of last commit in your VCS
//! let source_date = 1_600_000_000;
//! let pkg = rpm::PackageBuilder::new("test", "1.0.0", "MIT", "x86_64", "some awesome package")
//! .compression(rpm::CompressionType::Gzip)
//! .with_file(
//! "./test_assets/awesome.toml",
//! "./tests/assets/SOURCES/example_config.toml",
//! rpm::FileOptions::new("/etc/awesome/config.toml").is_config(),
//! )?
//! // file mode is inherited from source file
//! .with_file(
//! "./test_assets/awesome.py",
//! "./tests/assets/SOURCES/multiplication_tables.py",
//! rpm::FileOptions::new("/usr/bin/awesome"),
//! )?
//! .with_file(
//! "./test_assets/awesome.toml",
//! "./tests/assets/SOURCES/example_config.toml",
//! // you can set a custom mode and custom user too
//! rpm::FileOptions::new("/etc/awesome/second.toml")
//! .mode(rpm::FileMode::regular(0o644))
Expand Down Expand Up @@ -66,7 +66,7 @@
//! pkg.write(&mut f)?;
//!
//! // reading
//! let raw_pub_key = std::fs::read("test_assets/public_key.asc")?;
//! let raw_pub_key = std::fs::read("tests/assets/signing_keys/public_rsa4096.asc")?;
//! let pkg = rpm::Package::open("/tmp/awesome.rpm")?;
//! // verifying
//! pkg.verify_signature(Verifier::load_from_asc_bytes(&raw_pub_key)?)?;
Expand All @@ -85,6 +85,3 @@ pub use crate::constants::*;

mod rpm;
pub use crate::rpm::*;

#[cfg(test)]
mod tests;
71 changes: 56 additions & 15 deletions src/rpm/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,40 @@ impl PackageBuilder {
Ok(self)
}

/// Add a file to the package without needing an existing file.
///
/// Helpful if files are being generated on-demand, and you don't want to write them to disk.
///
/// ```
/// # fn foo() -> Result<(), Box<dyn std::error::Error>> {
///
/// let pkg = rpm::PackageBuilder::new("foo", "1.0.0", "Apache-2.0", "x86_64", "some baz package")
/// .with_file_contents(
/// "
/// [check]
/// date = true
/// time = true
/// ",
/// rpm::FileOptions::new("/etc/awesome/config.toml").is_config(),
/// )?
/// .with_file_contents(
/// "./awesome-config.toml",
/// // you can set a custom mode, capabilities and custom user too
/// rpm::FileOptions::new("/etc/awesome/second.toml").mode(0o100744).caps("cap_sys_admin=pe")?.user("hugo"),
/// )?
/// .build()?;
/// # Ok(())
/// # }
/// ```
pub fn with_file_contents(
mut self,
content: impl Into<Vec<u8>>,
options: impl Into<FileOptions>,
) -> Result<Self, Error> {
self.add_data(content.into(), Timestamp::now(), options.into())?;
Ok(self)
}

fn add_data(
&mut self,
content: Vec<u8>,
Expand Down Expand Up @@ -783,6 +817,8 @@ impl PackageBuilder {
}
cpio::newc::trailer(&mut archive)?;

let large_package = combined_file_sizes > u32::MAX.into();

self.provides
.push(Dependency::eq(self.name.clone(), self.version.clone()));
self.provides.push(Dependency::eq(
Expand All @@ -808,6 +844,12 @@ impl PackageBuilder {
self.requires
.push(Dependency::rpmlib("FileCaps", "4.6.1-1".to_owned()));
}

if large_package {
self.requires
.push(Dependency::rpmlib("LargeFiles", "4.12.0-1".to_owned()));
}

// TODO: as per https://rpm-software-management.github.io/rpm/manual/users_and_groups.html,
// at some point in the future this might make sense as hard requirements, but since it's a new feature,
// they have to be weak requirements to avoid breaking things.
Expand Down Expand Up @@ -900,7 +942,6 @@ impl PackageBuilder {
}

let offset = 0;
let small_package = combined_file_sizes <= u32::MAX.into();

let mut actual_records = vec![
// Existence of this tag is how rpm decides whether or not a package is a source rpm or binary rpm
Expand Down Expand Up @@ -951,7 +992,13 @@ impl PackageBuilder {
offset,
IndexData::I18NString(vec![self.summary]),
),
if small_package {
if large_package {
IndexEntry::new(
IndexTag::RPMTAG_LONGSIZE,
offset,
IndexData::Int64(vec![combined_file_sizes]),
)
} else {
let combined_file_sizes = combined_file_sizes
.try_into()
.expect("combined_file_sizes should be smaller than 4 GiB");
Expand All @@ -960,12 +1007,6 @@ impl PackageBuilder {
offset,
IndexData::Int32(vec![combined_file_sizes]),
)
} else {
IndexEntry::new(
IndexTag::RPMTAG_LONGSIZE,
offset,
IndexData::Int64(vec![combined_file_sizes]),
)
},
IndexEntry::new(
IndexTag::RPMTAG_LICENSE,
Expand Down Expand Up @@ -1022,7 +1063,13 @@ impl PackageBuilder {

// if we have an empty RPM, we have to leave out all file related index entries.
if !self.files.is_empty() {
let size_entry = if small_package {
let size_entry = if large_package {
IndexEntry::new(
IndexTag::RPMTAG_LONGFILESIZES,
offset,
IndexData::Int64(file_sizes),
)
} else {
let file_sizes = file_sizes
.into_iter()
.map(u32::try_from)
Expand All @@ -1036,12 +1083,6 @@ impl PackageBuilder {
offset,
IndexData::Int32(file_sizes),
)
} else {
IndexEntry::new(
IndexTag::RPMTAG_LONGFILESIZES,
offset,
IndexData::Int64(file_sizes),
)
};
actual_records.extend([
size_entry,
Expand Down
12 changes: 6 additions & 6 deletions src/rpm/headers/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use std::{
use super::*;
use crate::{constants::*, errors::*, Timestamp};

#[derive(Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub struct Header<T: Tag> {
pub(crate) index_header: IndexHeader,
pub(crate) index_entries: Vec<IndexEntry<T>>,
Expand Down Expand Up @@ -384,8 +384,8 @@ pub struct FileOwnership {

#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct FileDigest {
digest: String,
algo: DigestAlgorithm,
pub digest: String,
pub algo: DigestAlgorithm,
}

impl FileDigest {
Expand Down Expand Up @@ -480,7 +480,7 @@ where
}

/// A header keeping track of all other header records.
#[derive(Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq)]
pub(crate) struct IndexHeader {
/// rpm specific magic header
pub(crate) magic: [u8; 3],
Expand Down Expand Up @@ -547,7 +547,7 @@ impl IndexHeader {
}

/// A single entry within the [`IndexHeader`](self::IndexHeader)
#[derive(PartialEq)]
#[derive(Clone, PartialEq, Eq)]
pub(crate) struct IndexEntry<T: num::FromPrimitive> {
pub(crate) tag: u32,
pub(crate) data: IndexData,
Expand Down Expand Up @@ -650,7 +650,7 @@ impl<T: Tag> fmt::Display for IndexEntry<T> {
}

/// Data as present in a [`IndexEntry`](self::IndexEntry) .
#[derive(Debug, PartialEq, Eq)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum IndexData {
Null,
Char(Vec<u8>),
Expand Down
2 changes: 1 addition & 1 deletion src/rpm/headers/lead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::errors::*;
/// Used to contain valid data, now only a very limited subset is used
/// and the remaining data is set to fixed values such that compatibility is kept.
/// Only the "magic number" is still relevant as it is used to detect rpm files.
#[derive(Eq)]
#[derive(Clone, Eq)]
pub struct Lead {
magic: [u8; 4],
major: u8,
Expand Down
1 change: 0 additions & 1 deletion src/rpm/headers/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,6 @@ impl<W: std::io::Write> std::io::Write for Sha256Writer<W> {
}

/// Type-alias for a tuple containing index tags for a scriptlet type,
///
pub(crate) type ScriptletIndexTags = (IndexTag, IndexTag, IndexTag);

/// Description of a scriptlet as present in a RPM header record
Expand Down
19 changes: 8 additions & 11 deletions src/rpm/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub struct Digests {
///
/// Can either be created using the [`PackageBuilder`](crate::PackageBuilder)
/// or used with [`parse`](`self::Package::parse`) to obtain from a file.
#[derive(Debug)]
#[derive(Clone, Debug, PartialEq)]
pub struct Package {
/// Header and metadata structures.
///
Expand Down Expand Up @@ -117,8 +117,8 @@ impl Package {
/// # Examples
/// ```
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let mut package = rpm::Package::open("test_assets/ima_signed.rpm")?;
/// let raw_secret_key = std::fs::read("./test_assets/secret_key.asc")?;
/// let mut package = rpm::Package::open("tests/assets/RPMS/noarch/rpm-basic-2.3.4-5.el9.noarch.rpm")?;
/// let raw_secret_key = std::fs::read("./tests/assets/signing_keys/secret_rsa4096.asc")?;
/// let signer = rpm::signature::pgp::Signer::load_from_asc_bytes(&raw_secret_key)?;
/// // It's recommended to use timestamp of last commit in your VCS
/// let source_date = 1_600_000_000;
Expand Down Expand Up @@ -345,7 +345,7 @@ impl Package {
}
}

#[derive(PartialEq, Debug)]
#[derive(Clone, Debug, PartialEq)]
pub struct PackageMetadata {
pub lead: Lead,
pub signature: Header<IndexSignatureTag>,
Expand Down Expand Up @@ -717,7 +717,7 @@ impl PackageMetadata {
///
/// ```
/// # use rpm::Package;
/// # let package = Package::open("test_assets/389-ds-base-devel-1.3.8.4-15.el7.x86_64.rpm").unwrap();
/// # let package = Package::open("tests/assets/RPMS/noarch/rpm-basic-2.3.4-5.el9.noarch.rpm").unwrap();
/// let offsets = package.metadata.get_package_segment_offsets();
/// let lead = offsets.lead..offsets.signature_header;
/// let sig_header = offsets.signature_header..offsets.header;
Expand Down Expand Up @@ -843,8 +843,7 @@ impl PackageMetadata {

/// The digest algorithm used per file.
///
/// Note that this is not necessarily the same as the digest
/// used for headers.
/// Note that this is not necessarily the same as the digest used for headers.
pub fn get_file_digest_algorithm(&self) -> Result<DigestAlgorithm, Error> {
self.header
.get_entry_data_as_u32(IndexTag::RPMTAG_FILEDIGESTALGO)
Expand Down Expand Up @@ -909,6 +908,7 @@ impl PackageMetadata {
Err(Error::TagNotFound(_)) => Ok(None),
Err(e) => return Err(e),
};
// TODO: verify this is correct behavior for links?
let links = self
.header
.get_entry_data_as_string_array(IndexTag::RPMTAG_FILELINKTOS);
Expand All @@ -918,10 +918,7 @@ impl PackageMetadata {
{
Ok(ima_signatures) => Ok(Some(ima_signatures)),
Err(Error::TagNotFound(_)) => Ok(None),
Err(e) => {
println!("{e:?}");
return Err(e);
}
Err(e) => return Err(e),
};

match (
Expand Down
Loading
Loading