From 34956c8a6cc6a2305058ec1dcce39f9378417c09 Mon Sep 17 00:00:00 2001 From: Daniel Alley Date: Sun, 7 Jan 2024 01:08:44 -0500 Subject: [PATCH] temp --- src/rpm/headers/types.rs | 45 +++- tests/assets/SPECS/rpm-rich-deps.spec | 69 +++++ .../SPECS/rpm-triggers-and-scriptlets.spec | 44 ++++ tests/building.rs | 238 ++++++++++++++++++ 4 files changed, 395 insertions(+), 1 deletion(-) create mode 100644 tests/assets/SPECS/rpm-rich-deps.spec create mode 100644 tests/assets/SPECS/rpm-triggers-and-scriptlets.spec diff --git a/src/rpm/headers/types.rs b/src/rpm/headers/types.rs index ff7323a0..fe22a214 100644 --- a/src/rpm/headers/types.rs +++ b/src/rpm/headers/types.rs @@ -52,7 +52,50 @@ pub const REGULAR_FILE_TYPE: u16 = 0o100000; // bit representation = "100000000 pub const DIR_FILE_TYPE: u16 = 0o040000; // bit representation = "0100000000000000" pub const SYMBOLIC_LINK_FILE_TYPE: u16 = 0o120000; // bit representation = "1010000000000000" -// @todo: +use bitflags::bitflags; + +// typedef enum rpmFileTypes_e { +// = 1, /*!< pipe/fifo */ +// CDEV = 2, /*!< character device */ +// XDIR = 4, /*!< directory */ +// BDEV = 6, /*!< block device */ +// REG = 8, /*!< regular file */ +// LINK = 10, /*!< hard link */ +// SOCK = 12 /*!< socket */ +// } rpmFileTypes; + +bitflags! { + #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] + pub struct FileModeFlags: u16 { + const PERMISSIONS_BIT_MASK = 0b0000111111111111; // 0o007777 + + const SUID_BIT_MASK = 0b0000100000000000; // 0o007777 + const SGID_BIT_MASK = 0b0000010000000000; // 0o007777 + const STICKY_BIT_MASK = 0b0000001000000000; // 0o007777 + + const USER_PERM_BIT_MASK = 0b0000000111000000; // 0o007777 + const GROUP_PERM_BIT_MASK = 0b0000000000111000; // 0o007777 + const OTHER_PERM_BIT_MASK = 0b0000000000000111; + +// The set-user-ID bit (setuid bit). + +// On execution, set the process’s effective user ID to that of the file. For directories on a few systems, give files created in the directory the same owner as the directory, no matter who creates them, and set the set-user-ID bit of newly-created subdirectories. +// The set-group-ID bit (setgid bit). + +// On execution, set the process’s effective group ID to that of the file. For directories on most systems, give files created in the directory the same group as the directory, no matter what group the user who creates them is in, and set the set-group-ID bit of newly-created subdirectories. +// The restricted deletion flag or sticky bit. + +// Prevent unprivileged users from removing or renaming a file in a directory unless they own the file or the directory; this is commonly found on world-writable directories like /tmp. For regular files on some older systems, save the program’s text image on the swap device so it will load more quickly when run, so that the image is “sticky”. + + const FILE_TYPE_BIT_MASK = 0b1111000000000000; // 0o170000 + const REGULAR_FILE_TYPE = 0b1000000000000000; // 0o100000 + const DIR_FILE_TYPE = 0b0100000000000000; // 0o040000 + const SYMBOLIC_LINK_FILE_TYPE = 0b1010000000000000; // 0o120000 + } +} + + + impl From for FileMode { fn from(raw_mode: u16) -> Self { // example diff --git a/tests/assets/SPECS/rpm-rich-deps.spec b/tests/assets/SPECS/rpm-rich-deps.spec new file mode 100644 index 00000000..4d8d9d2b --- /dev/null +++ b/tests/assets/SPECS/rpm-rich-deps.spec @@ -0,0 +1,69 @@ +Name: rpm-rich-deps +Epoch: 1 +Version: 2.3.4 +Release: 5.el8 +License: MPL-2.0 +Summary: A package for testing rich dependencies + +Group: Development/Tools +Url: http://bobloblaw.com +Packager: Michael Bluth +Vendor: Bluth Company +Vcs: https://github.com/rpm-rs/rpm + +BuildArch: noarch +BuildRequires: file-devel + +Requires: (fur <= 2 or arson >= 1.0.0-1) +Requires: staircar <= 99.1-3 +Requires: /usr/bin/bash + +Requires(pre): zstd +Requires(pre): glibc > 2.16 +Requires(post): gzip + +Provides: laughter = 33 +Provides: narration(ronhoward) +Provides: /usr/bin/ls + +Obsoletes: cornballer < 444 +Obsoletes: bluemangroup < 32.1-0 + +Conflicts: foxnetwork > 5555 + +Recommends: yacht > 9:11.0-0 +Recommends: GeneParmesan(PI) +Recommends: ((hiding and attic) if light-treason) + +Suggests: (job or money > 9000) +Suggests: (dove and return) +Suggests: (bobloblaw >= 1.1 if maritimelaw else anyone < 0.5.1-2) + +Supplements: comedy = 0:11.1-4 +Supplements: ((hiding and illusion) unless alliance-of-magicians) + +Enhances: (bananas or magic) + +%description +This package tests rich dependencies. + +%build +echo "Now the story of a wealthy man who lost everything, and the one son who had no choice but to keep them all together." > README +echo OK + +%install + +%clean + +%files +%doc README + +%changelog +* Mon Jun 14 2021 George Bluth - 3.3.3-3 +- There’s always money in the banana stand + +* Sun Apr 25 2021 Job Bluth - 2.2.2-2 +- I've made a huge mistake + +* Wed Mar 31 2021 Lucille Bluth - 1.1.1-1 +- It's a banana, Michael. How much could it cost, $10? diff --git a/tests/assets/SPECS/rpm-triggers-and-scriptlets.spec b/tests/assets/SPECS/rpm-triggers-and-scriptlets.spec new file mode 100644 index 00000000..3762da3b --- /dev/null +++ b/tests/assets/SPECS/rpm-triggers-and-scriptlets.spec @@ -0,0 +1,44 @@ +Name: triggers +Version: 1.0 +Release: %{rel} +Summary: Testing trigger behavior +Group: Testing +License: GPL +BuildArch: noarch + +%description +%{summary} + +%files +%defattr(-,root,root,-) + +%triggerprein -- %{trigpkg} +echo %{name}-%{version}-%{release} TRIGGERPREIN $* + +%triggerin -- %{trigpkg} +echo %{name}-%{version}-%{release} TRIGGERIN $* + +%triggerun -- %{trigpkg} +echo %{name}-%{version}-%{release} TRIGGERUN $* + +%triggerpostun -- %{trigpkg} +echo %{name}-%{version}-%{release} TRIGGERPOSTUN $* + + +%pre + +%post + +%preun + +%postun + +%pretrans + +%posttrans + +%preuntrans + +%postuntrans + +%verifyscript diff --git a/tests/building.rs b/tests/building.rs index 6dec3915..778b1a8a 100644 --- a/tests/building.rs +++ b/tests/building.rs @@ -15,6 +15,10 @@ fn test_empty_package_equivalent() -> Result<(), Box> { .compression(CompressionType::None) .build()?; + // Current issues: + // =============== + // Lead: archnum is set to zero + // @TODO: currently failing due to missing tags, different checksums, offsets etc. // empty_rpm.canonicalize()?; // built_empty_rpm.canonicalize()?; @@ -51,6 +55,240 @@ fn test_empty_source_package_equivalent() -> Result<(), Box Result<(), Box> { + let original_rpm = Package::open(common::rpm_basic_pkg_path())?; + let built_rpm = PackageBuilder::new( + "rpm-basic", + "2.3.4", + "MPL-2.0", + "noarch", + "A package for exercising basic features of RPM", + ) + .epoch(1) + .release("5") + .description("This package attempts to exercise basic features of RPM packages.") + .vendor("Los Pollos Hermanos") + .url("http://www.savewalterwhite.com/") + .vcs("https://github.com/rpm-rs/rpm") + .group("Development/Tools") + .packager("Walter White") + .compression(CompressionType::None) + .build_host("localhost") + .source_date(1681068559) + .provides(Dependency::any("/usr/bin/ls")) + .provides(Dependency::any("aaronpaul")) + .provides(Dependency::any("breaking(bad)")) + .provides(Dependency::config("rpm-basic", "1:2.3.4-5.el9")) + .provides(Dependency::eq("rpm-basic", "1:2.3.4-5.el9")) + .provides(Dependency::eq("shock", "33")) + .requires(Dependency::script_pre("/usr/sbin/ego")) + .requires(Dependency::config("rpm-basic", "1:2.3.4-5.el9")) + .requires(Dependency::greater_eq("methylamine", "1.0.0-1")) + .requires(Dependency::less_eq("morality", "2")) + .requires(Dependency::script_post("regret")) + .conflicts(Dependency::greater("hank", "35")) + .obsoletes(Dependency::less("gusfring", "32.1-0")) + .obsoletes(Dependency::less("tucosalamanca", "444")) + .supplements(Dependency::eq("comedy", "0:11.1-4")) + .suggests(Dependency::any("chilipowder")) + .enhances(Dependency::greater("purity", "9000")) + .recommends(Dependency::any("SaulGoodman(CriminalLawyer)")) + .recommends(Dependency::greater("huel", "9:11.0-0")) + .with_file( + "./tests/assets/SOURCES/example_config.toml", + FileOptions::new("/etc/rpm-basic/example_config.toml").is_config(), + )? + .with_file( + "./tests/assets/SOURCES/multiplication_tables.py", + FileOptions::new("/usr/bin/rpm-basic"), + )? + .with_file( + "", + FileOptions::new("/usr/lib/rpm-basic").mode(FileMode::Dir { permissions: 0o644 }), + )? + .with_file( + "", + FileOptions::new("/usr/lib/rpm-basic/module").mode(FileMode::Dir { permissions: 0o755 }), + )? + .with_file( + "./tests/assets/SOURCES/module/__init__.py", + FileOptions::new("/usr/lib/rpm-basic/module/__init__.py"), + )? + .with_file( + "./tests/assets/SOURCES/module/hello.py", + FileOptions::new("/usr/lib/rpm-basic/module/hello.py"), + )? + .with_file( + "", + FileOptions::new("/usr/lib/rpm-basic/module").mode(FileMode::Dir { permissions: 0o755 }), + )? + .with_file( + "", + FileOptions::new("/usr/share/doc/rpm-basic").mode(FileMode::Regular { permissions: 0o644 }), + )? + .with_file( + "", + FileOptions::new("/usr/share/doc/rpm-basic/README").is_doc(), + )? + .with_file( + "./tests/assets/SOURCES/example_data.xml", + FileOptions::new("/usr/share/rpm-basic/example_data.xml"), + )? + .with_file( + "", + FileOptions::new("/var/log/rpm-basic/basic.log").is_ghost(), + )? + .with_file( + "", + FileOptions::new("/var/tmp/rpm-basic").mode(FileMode::Dir { permissions: 0o755 }), + )? + .add_changelog_entry( + "Walter White - 3.3.3-3", + "- I'm not in the meth business. I'm in the empire business.", + 1623672000, + ) + .add_changelog_entry( + "Gustavo Fring - 2.2.2-2", + "- Never Make The Same Mistake Twice.", + 1619352000, + ) + .add_changelog_entry( + "Mike Ehrmantraut - 1.1.1-1", + "- Just because you shot Jesse James, don't make you Jesse James.", + 1619352000, + ) + .build()?; + + // @TODO: currently failing due to missing tags, different checksums, offsets etc. + // empty_rpm.canonicalize()?; + // built_empty_rpm.canonicalize()?; + // pretty_assertions::assert_eq!(empty_rpm.metadata, built_empty_rpm.metadata); + + // Test that the payload generated is equivalent + pretty_assertions::assert_str_eq!( + format!("{:?}", &original_rpm.content.as_bstr()), + format!("{:?}", &built_rpm.content.as_bstr()), + ); + + Ok(()) +} + +/// Build a package with all different kinds of file attrs and compare it to one built by rpmbuild (using rpm-file-attrs.spec) +#[ignore] +#[test] +fn test_file_attrs_package_equivalent() -> Result<(), Box> { + let empty_rpm = Package::open(common::rpm_empty_path())?; + let built_empty_rpm = PackageBuilder::new( + "rpm-file-attrs", + "1.0", + "MIT", + "noarch", + "Test RPM file attributes", + ) + .release("1") + .description("Test RPM file attributes") + .compression(CompressionType::None) + .build_host("localhost") + .source_date(1681068559) + .with_file( + "", + FileOptions::new("/opt/rpm-file-attrs").mode(FileMode::Dir { permissions: 0o755 }), + )? + .with_file_contents( + "artifact", + FileOptions::new("/opt/rpm-file-attrs/artifact").is_artifact(), + )? + .with_file_contents( + "config", + FileOptions::new("/opt/rpm-file-attrs/config").is_config(), + )? + .with_file_contents( + "config_noreplace", + FileOptions::new("/opt/rpm-file-attrs/config_noreplace").is_config_noreplace(), + )? + .with_file_contents( + "different-owner-and-group", + FileOptions::new("/opt/rpm-file-attrs/different-owner-and-group") + .user("jane") + .group("bob") + .mode(FileMode::Regular { permissions: 0o655 }), + )? + .with_file( + "", + FileOptions::new("/opt/rpm-file-attrs/dir").mode(FileMode::Dir { permissions: 0o755 }), + )? + .with_file_contents("normal", FileOptions::new("/opt/rpm-file-attrs/dir/normal"))? + .with_file_contents("doc", FileOptions::new("/opt/rpm-file-attrs/doc").is_doc())? + .with_file_contents( + "empty_caps", + FileOptions::new("/opt/rpm-file-attrs/empty_caps").caps("=")?, + )? + .with_file_contents( + "empty_caps2", + FileOptions::new("/opt/rpm-file-attrs/empty_caps2").caps("")?, + )? + .with_file_contents( + "example-binary", + FileOptions::new("/opt/rpm-file-attrs/example-binary") + .mode(FileMode::Regular { permissions: 0o644 }), + )? + .with_file_contents( + "example-confidential-file", + FileOptions::new("/opt/rpm-file-attrs/example-confidential-file") + .mode(FileMode::Regular { permissions: 0o600 }), + )? + .with_file_contents("ghost", FileOptions::new("/opt/rpm-file-attrs/ghost").is_ghost())? + .with_file_contents( + "license", + FileOptions::new("/opt/rpm-file-attrs/license").is_license(), + )? + .with_file_contents( + "missingok", + FileOptions::new("/opt/rpm-file-attrs/missingok").is_missingok(), + )? + .with_file("normal", FileOptions::new("/opt/rpm-file-attrs/normal"))? + .with_file( + "readme", + FileOptions::new("/opt/rpm-file-attrs/readme").is_readme(), + )? + .with_file( + "", + FileOptions::new("/opt/rpm-file-attrs/symlink") + .symlink("normal") + .mode(FileMode::SymbolicLink { permissions: 0o777 }), + )? + .with_file( + "", + FileOptions::new("/opt/rpm-file-attrs/symlink_dir") + .mode(FileMode::Dir { permissions: 0o755 }), + )? + .with_file("", FileOptions::new("/opt/rpm-file-attrs/symlink_dir/dir"))? + .with_file_contents( + "with_caps", + FileOptions::new("/opt/rpm-file-attrs/with_caps") + .symlink("../dir") + .mode(FileMode::Dir { permissions: 0o755 }) + .caps("cap_sys_ptrace,cap_sys_admin=ep")?, + )? + .build()?; + + // @TODO: currently failing due to missing tags, different checksums, offsets etc. + // empty_rpm.canonicalize()?; + // built_empty_rpm.canonicalize()?; + // pretty_assertions::assert_eq!(empty_rpm.metadata, built_empty_rpm.metadata); + + // Test that the payload generated is equivalent + pretty_assertions::assert_str_eq!( + format!("{:?}", &empty_rpm.content.as_bstr()), + format!("{:?}", &built_empty_rpm.content.as_bstr()), + ); + + Ok(()) +} + #[test] fn test_rpm_builder() -> Result<(), Box> { let mut buff = std::io::Cursor::new(Vec::::new());