diff --git a/ipxe-xcat/README b/ipxe-xcat/README new file mode 100644 index 0000000..193e830 --- /dev/null +++ b/ipxe-xcat/README @@ -0,0 +1,70 @@ +ipxe-xcat +========= + +This package installs the ipxeboot.tar.gz tree of the iPXE v2.0.0 release +under /tftpboot/xcat/ipxe, unchanged. Nothing is rebuilt. The x86_64-sb +and arm64-sb builds carry their Secure Boot signatures inside the files, and +the shim finds snponly.efi and ipxe.efi by name in its own directory, so the +package keeps every name, symlink and byte of the release tree. + +Files +----- + +ipxeboot-2.0.0.tar.gz + The ipxeboot.tar.gz asset of + https://github.com/ipxe/ipxe/releases/tag/v2.0.0, renamed. Its SHA-256, + 01a526d4cc791fc30362259c609d6c506cc64a7bdff51b9a5eb788354e17eee1, is the + digest that GitHub publishes for the asset. + +ipxe-2.0.0-source.tar.gz + The source archive of tag v2.0.0, commit + 12798ec29aa8a64d8675c4378b99f5fe28447afb, from + https://github.com/ipxe/ipxe/archive/refs/tags/v2.0.0.tar.gz. Its content + equals "git archive --prefix=ipxe-2.0.0/ v2.0.0". snpnet.c and undinet.c + are GPL2_OR_LATER without the UBDL, so snponly.efi and undionly.kpxe are + GPLv2+ as a whole. The package installs this archive with the binaries. + +SHA256SUMS + The SHA-256 of both archives. Both builders check it before the build. + +payload.sha256 + One line for each directory, file and symlink of the release tree, with + the SHA-256 of each file and the target of each symlink. After the build, + both builders unpack the RPM or deb and compare its /tftpboot/xcat/ipxe + with this list, entry for entry, with verify-payload.pl. A difference + fails the build. + +licenses/ + ipxe/COPYING, COPYING.GPLv2 and COPYING.UBDL are from iPXE tag v2.0.0. + shim/COPYRIGHT is from ipxe/shim tag ipxe-16.1. + shim/openssl/LICENSE is from openssl/openssl tag OpenSSL_1_0_2k, the + OpenSSL version that shim 16.1 carries in Cryptlib. + shim/gnu-efi/README.efilib is from rhboot/gnu-efi commit dc7fd96, the + gnu-efi commit that tag ipxe-16.1 pins. + +The shim +-------- + +x86_64-sb/shimx64.efi and arm64-sb/shimaa64.efi report shim 16.1, and +shimx64.efi is signed by the Microsoft Corporation UEFI CA 2011 only. The +ipxe/shim ipxe-16.1 release assets were replaced on 2026-05-27 with a build +signed by both the UEFI CA 2011 and the UEFI CA 2023. The files in this +release tree are the earlier 16.1 build. + +Update to a new release +----------------------- + +1. Download ipxeboot.tar.gz from the release, and compare its SHA-256 with + the digest on the release page. +2. Download the source archive of the tag, and compare its content with + "git archive" of the tag. +3. Replace both archives, and write SHA256SUMS with sha256sum. +4. Write payload.sha256: + + mkdir tree + tar -xzf ipxeboot-.tar.gz --strip-components=1 -C tree + ./verify-payload.pl --generate tree > payload.sha256 + +5. Update licenses/ when the release changes its licence texts or its shim. +6. Set the version in ipxe-xcat.spec and debian/changelog, and the ipxe-xcat + pins in packages-manifest.conf and debs-manifest.conf. diff --git a/ipxe-xcat/debian/changelog b/ipxe-xcat/debian/changelog new file mode 100644 index 0000000..0eddf8e --- /dev/null +++ b/ipxe-xcat/debian/changelog @@ -0,0 +1,5 @@ +ipxe-xcat (2.0.0-1) unstable; urgency=medium + + * Package the ipxeboot.tar.gz tree of the iPXE v2.0.0 release. + + -- xCAT Fri, 25 Sep 2026 12:00:00 +0000 diff --git a/ipxe-xcat/debian/compat b/ipxe-xcat/debian/compat new file mode 100644 index 0000000..48082f7 --- /dev/null +++ b/ipxe-xcat/debian/compat @@ -0,0 +1 @@ +12 diff --git a/ipxe-xcat/debian/control b/ipxe-xcat/debian/control new file mode 100644 index 0000000..4cafab9 --- /dev/null +++ b/ipxe-xcat/debian/control @@ -0,0 +1,16 @@ +Source: ipxe-xcat +Section: admin +Priority: optional +Maintainer: xCAT +Build-Depends: debhelper (>= 12) +Standards-Version: 4.5.0 +Homepage: https://ipxe.org/ + +Package: ipxe-xcat +Architecture: all +Depends: ${misc:Depends} +Description: iPXE network boot binaries from the upstream release + The ipxeboot.tar.gz tree of the iPXE 2.0.0 release, installed unchanged + under /tftpboot/xcat/ipxe. It carries the signed Secure Boot builds and + their shim. The source archive of the release tag is installed with the + documentation. diff --git a/ipxe-xcat/debian/copyright b/ipxe-xcat/debian/copyright new file mode 100644 index 0000000..4be3d40 --- /dev/null +++ b/ipxe-xcat/debian/copyright @@ -0,0 +1,37 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: iPXE +Source: https://github.com/ipxe/ipxe/releases/tag/v2.0.0 +Comment: ipxeboot-2.0.0.tar.gz is the ipxeboot.tar.gz asset of the iPXE v2.0.0 + release, unchanged. ipxe-2.0.0-source.tar.gz is the source archive of tag + v2.0.0, installed as /usr/share/doc/ipxe-xcat/ipxe-2.0.0-source.tar.gz. + . + The files x86_64-sb/shimx64.efi and arm64-sb/shimaa64.efi in the release tree + are shim 16.1, built by the iPXE project from ipxe/shim tag ipxe-16.1. Shim is + under the BSD licence in licenses/shim/COPYRIGHT and includes OpenSSL 1.0.2k + (licenses/shim/openssl/LICENSE) and gnu-efi (licenses/shim/gnu-efi/README.efilib). + . + The licence texts are installed in /usr/share/doc/ipxe-xcat/licenses/. + +Files: * +Copyright: Michael Brown and the iPXE contributors +License: GPL-2+ + iPXE files are licensed under the GNU General Public License, version 2 or + (at your option) any later version, unless the file states another licence. + Some files are licensed under version 2 only, some under BSD or MIT terms, + and most may also be used under the Unmodified Binary Distribution Licence + (licenses/ipxe/COPYING.UBDL). Each file in the source archive states its + own licence. + . + On Debian systems, the complete text of the GNU General Public License + version 2 can be found in /usr/share/common-licenses/GPL-2. + +Files: debian/* +Copyright: xCAT contributors +License: GPL-2+ + This packaging is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + . + On Debian systems, the complete text of the GNU General Public License + version 2 can be found in /usr/share/common-licenses/GPL-2. diff --git a/ipxe-xcat/debian/rules b/ipxe-xcat/debian/rules new file mode 100755 index 0000000..9e64b75 --- /dev/null +++ b/ipxe-xcat/debian/rules @@ -0,0 +1,36 @@ +#!/usr/bin/make -f +# The payload is the upstream release tree, byte for byte: nothing strips it, and dh_compress and +# dh_fixperms leave /tftpboot and the licence texts alone. + +VERSION := $(shell dpkg-parsechangelog -S Version | sed 's/-[^-]*$$//') +DEST := debian/ipxe-xcat +DOC := $(DEST)/usr/share/doc/ipxe-xcat + +build build-arch build-indep: + +clean: + dh_testdir + dh_clean + +binary-arch: + +binary-indep: + dh_testdir + dh_testroot + dh_prep + install -d $(DEST)/tftpboot/xcat/ipxe $(DOC) + tar -xzf ipxeboot-$(VERSION).tar.gz --no-same-owner --strip-components=1 -C $(DEST)/tftpboot/xcat/ipxe + install -m 0644 ipxe-$(VERSION)-source.tar.gz $(DOC)/ + cp -R licenses $(DOC)/licenses + dh_installdocs + dh_installchangelogs + dh_compress -Xlicenses/ + dh_fixperms -Xtftpboot/ + dh_installdeb + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch + +.PHONY: build build-arch build-indep clean binary-arch binary-indep binary diff --git a/ipxe-xcat/debian/source/format b/ipxe-xcat/debian/source/format new file mode 100644 index 0000000..89ae9db --- /dev/null +++ b/ipxe-xcat/debian/source/format @@ -0,0 +1 @@ +3.0 (native) diff --git a/ipxe-xcat/ipxe-xcat.spec b/ipxe-xcat/ipxe-xcat.spec new file mode 100644 index 0000000..8b80e77 --- /dev/null +++ b/ipxe-xcat/ipxe-xcat.spec @@ -0,0 +1,53 @@ +# The payload is the upstream release, byte for byte: signed EFI files must not be stripped or +# otherwise touched by the build-root policy scripts. +%global debug_package %{nil} +%global __os_install_post %{nil} + +Name: ipxe-xcat +Version: 2.0.0 +Release: 1 +Summary: iPXE network boot binaries from the upstream release +License: GPL-2.0-only AND GPL-2.0-or-later AND BSD-2-Clause AND BSD-2-Clause-Patent AND BSD-3-Clause AND MIT AND OpenSSL +URL: https://ipxe.org/ +BuildArch: noarch + +Source0: ipxeboot-%{version}.tar.gz +Source1: ipxe-%{version}-source.tar.gz +Source2: licenses/ipxe/COPYING +Source3: licenses/ipxe/COPYING.GPLv2 +Source4: licenses/ipxe/COPYING.UBDL +Source5: licenses/shim/COPYRIGHT +Source6: licenses/shim/openssl/LICENSE +Source7: licenses/shim/gnu-efi/README.efilib + +%description +The ipxeboot.tar.gz tree of the iPXE %{version} release, installed unchanged +under /tftpboot/xcat/ipxe. It carries the signed Secure Boot builds and +their shim. The source archive of the release tag is installed with the +documentation. + +%prep +%setup -q -c -T +install -D -m 0644 %{SOURCE2} licenses/ipxe/COPYING +install -D -m 0644 %{SOURCE3} licenses/ipxe/COPYING.GPLv2 +install -D -m 0644 %{SOURCE4} licenses/ipxe/COPYING.UBDL +install -D -m 0644 %{SOURCE5} licenses/shim/COPYRIGHT +install -D -m 0644 %{SOURCE6} licenses/shim/openssl/LICENSE +install -D -m 0644 %{SOURCE7} licenses/shim/gnu-efi/README.efilib + +%build + +%install +mkdir -p %{buildroot}/tftpboot/xcat/ipxe +tar -xzf %{SOURCE0} --no-same-owner --strip-components=1 -C %{buildroot}/tftpboot/xcat/ipxe +install -D -m 0644 %{SOURCE1} %{buildroot}%{_pkgdocdir}/ipxe-%{version}-source.tar.gz + +%files +/tftpboot/xcat/ipxe +%license licenses/ipxe licenses/shim +%dir %{_pkgdocdir} +%doc %{_pkgdocdir}/ipxe-%{version}-source.tar.gz + +%changelog +* Fri Sep 25 2026 xCAT - 2.0.0-1 +- Package the ipxeboot.tar.gz tree of the iPXE v2.0.0 release diff --git a/ipxe-xcat/mockbuild.pl b/ipxe-xcat/mockbuild.pl new file mode 100755 index 0000000..fd674ff --- /dev/null +++ b/ipxe-xcat/mockbuild.pl @@ -0,0 +1,172 @@ +#!/usr/bin/perl +# ipxe-xcat/mockbuild.pl -- build the ipxe-xcat noarch RPM with mock from the committed release +# archives. The archives are checked against SHA256SUMS before the build, and the built RPM payload +# against payload.sha256 before anything is copied to --result-dir. +use strict; +use warnings; +use Cwd qw(abs_path); +use File::Basename qw(basename); +use File::Copy qw(copy); +use File::Path qw(make_path remove_tree); +use FindBin qw($RealBin); +use Getopt::Long qw(GetOptions); +use lib "$RealBin/..", "$RealBin/../lib"; +use MockBuildUtils qw(resolve_mock_cfg); +use XCAT::BuildUtils qw(capture_command digest_file print_step require_command run_command shell_quote); + +my $pkg_dir = abs_path($RealBin); +my $repo_root = abs_path("$pkg_dir/.."); +my $spec_file = "$pkg_dir/ipxe-xcat.spec"; + +my $work_dir = '/tmp/ipxe-xcat-mockbuild'; +my $mock_cfg = ''; +my $mock_uniqueext = ''; +my $result_dir = "$repo_root/build-output/list3/ipxe-xcat"; +my $log_dir = "$repo_root/build-logs/list3/ipxe-xcat"; +my $build_timestamp; + +GetOptions( + 'work-dir=s' => \$work_dir, + 'mock-cfg=s' => \$mock_cfg, + 'mock-uniqueext=s' => \$mock_uniqueext, + 'result-dir=s' => \$result_dir, + 'log-dir=s' => \$log_dir, + 'build-timestamp=i' => \$build_timestamp, +) or die usage(); + +die "Run as root (current uid=$>)\n" if $> != 0; +require_command($_) for qw(mock rpm rpm2cpio cpio bash sha256sum); + +my ($version, @sources) = spec_sources($spec_file); +die "Could not parse Version from $spec_file\n" if !$version; + +if (!$mock_cfg) { + my $os_id = capture_command('bash', '-c', 'source /etc/os-release; echo $ID'); + my $arch = capture_command('uname', '-m'); + $mock_cfg = resolve_mock_cfg($os_id, 10, $arch); +} +my @uniqueext = $mock_uniqueext ne '' ? ('--uniqueext', $mock_uniqueext) : (); + +my $epoch = $build_timestamp; +if (!defined $epoch) { + $epoch = `git -C \Q$repo_root\E log -1 --format=%ct HEAD 2>/dev/null` // ''; + chomp $epoch; + $epoch = time() if $epoch !~ /^\d+$/; +} +$ENV{SOURCE_DATE_EPOCH} = $epoch; + +print_step('Configuration'); +print "pkg_dir: $pkg_dir\n"; +print "version: $version\n"; +print "work_dir: $work_dir\n"; +print "result_dir: $result_dir\n"; +print "log_dir: $log_dir\n"; +print "mock_cfg: $mock_cfg\n"; + +print_step('Check the release archives'); +run_command('bash', '-c', 'cd ' . shell_quote($pkg_dir) . ' && sha256sum --check --strict SHA256SUMS'); + +print_step('Stage the sources'); +remove_tree($work_dir) if -d $work_dir; +my $sources_dir = "$work_dir/sources"; +make_path($sources_dir, $result_dir, $log_dir); +my %staged; +for my $source (@sources) { + my $name = basename($source); + die "Two Source files share the name $name\n" if $staged{$name}++; + copy("$pkg_dir/$source", "$sources_dir/$name") + or die "Failed to copy $pkg_dir/$source: $!\n"; +} +run_command('bash', '-c', 'cd ' . shell_quote($sources_dir) + . ' && sha256sum --check --strict ' . shell_quote("$pkg_dir/SHA256SUMS")); + +my $det_cfg = "$work_dir/mock-deterministic.cfg"; +open(my $cfg_fh, '>', $det_cfg) or die "Cannot write $det_cfg: $!\n"; +print {$cfg_fh} "include('/etc/mock/${mock_cfg}.cfg')\n"; +print {$cfg_fh} "config_opts['environment']['SOURCE_DATE_EPOCH'] = '$epoch'\n"; +close($cfg_fh) or die "Cannot write $det_cfg: $!\n"; +my @defines = map { ('--define', $_) } + ('use_source_date_epoch_as_buildtime 1', 'clamp_mtime_to_source_date_epoch 1', '_buildhost xcat-build'); + +print_step('Build the SRPM with mock'); +my $srpm_out = "$work_dir/srpm"; +make_path($srpm_out); +run_mock('mock', '-r', $det_cfg, @uniqueext, '--buildsrpm', '--spec', $spec_file, + '--sources', $sources_dir, '--resultdir', $srpm_out, @defines); +my @srpms = glob("$srpm_out/ipxe-xcat-*.src.rpm"); +die "Expected one SRPM in $srpm_out, found " . scalar(@srpms) . "\n" if @srpms != 1; + +print_step('Rebuild the RPM with mock'); +my $rpm_out = "$work_dir/rpm"; +make_path($rpm_out); +run_mock('mock', '-r', $det_cfg, @uniqueext, '--rebuild', $srpms[0], '--resultdir', $rpm_out, @defines); +my @rpms = glob("$rpm_out/ipxe-xcat-$version-*.noarch.rpm"); +die "Expected one ipxe-xcat noarch RPM in $rpm_out, found " . scalar(@rpms) . "\n" if @rpms != 1; +my $rpm = $rpms[0]; + +print_step('Check the RPM payload'); +my $payload = "$work_dir/payload"; +make_path($payload); +run_command('bash', '-o', 'pipefail', '-c', 'cd ' . shell_quote($payload) + . ' && rpm2cpio ' . shell_quote($rpm) . ' | cpio -idm --quiet'); +run_command('perl', "$pkg_dir/verify-payload.pl", "$payload/tftpboot/xcat/ipxe", + "$pkg_dir/payload.sha256"); +check_installed("$payload/usr/share/doc/ipxe-xcat/ipxe-$version-source.tar.gz", + "$pkg_dir/ipxe-$version-source.tar.gz"); +for my $licence (grep { m{^licenses/} } @sources) { + (my $installed = $licence) =~ s{^licenses/}{}; + check_installed("$payload/usr/share/licenses/ipxe-xcat/$installed", "$pkg_dir/$licence"); +} + +print_step('Collect the results'); +for my $file ($rpm, $srpms[0]) { + copy($file, $result_dir) or die "Failed to copy $file to $result_dir: $!\n"; + print "Copied: $result_dir/" . basename($file) . "\n"; +} +for my $log (qw(build.log root.log state.log)) { + copy("$rpm_out/$log", "$log_dir/$log") if -f "$rpm_out/$log"; + copy("$srpm_out/$log", "$log_dir/srpm-$log") if -f "$srpm_out/$log"; +} +print_step('Completed'); +exit 0; + +sub usage { + return <<"USAGE"; +Usage: $0 [options] + --work-dir PATH Temporary work directory (default: $work_dir) + --mock-cfg NAME Mock config (default: the EL10 config of this host) + --mock-uniqueext TEXT mock --uniqueext suffix for concurrent builds + --result-dir PATH Output directory for the RPM and SRPM + --log-dir PATH Output directory for the mock logs + --build-timestamp EPOCH SOURCE_DATE_EPOCH for a reproducible build +USAGE +} + +sub spec_sources { + my ($path) = @_; + open(my $fh, '<', $path) or die "Cannot read $path: $!\n"; + my ($version, @sources) = (''); + while (my $line = <$fh>) { + $version = $1 if $line =~ /^Version:\s*(\S+)/; + push @sources, $1 if $line =~ /^Source\d*:\s*(\S+)/; + } + close($fh); + s/%\{version\}/$version/g for @sources; + return ($version, @sources); +} + +sub check_installed { + my ($installed, $committed) = @_; + die "Missing from the RPM payload: $installed\n" if !-f $installed || -l $installed; + die "The RPM payload changed $installed\n" if digest_file($installed) ne digest_file($committed); +} + +# mock exits 30 when its package manager failed, most often a transient mirror error: retry once. +sub run_mock { + my (@command) = @_; + my $ok = eval { run_command(@command) }; + return 1 if $ok; + die $@ if $@ !~ /\(rc=30\)/; + print "mock failed with rc=30 (package manager); retrying once\n"; + return run_command(@command); +} diff --git a/ipxe-xcat/payload.sha256 b/ipxe-xcat/payload.sha256 new file mode 100644 index 0000000..75f953f --- /dev/null +++ b/ipxe-xcat/payload.sha256 @@ -0,0 +1,54 @@ +# ipxe-xcat payload manifest, written by verify-payload.pl --generate +dir - arm32 +file 2a8d50db2b3b5f02302302b3161931c1142301f42d52c64c0005974b602b7092 arm32/ipxe-legacy.efi +file fa58061710e6fab23fc27c6d200d510453c3f14f3bc0715fa6804111f1e5fc74 arm32/ipxe.efi +file 206310f30306569ed2c6e0ef015b3ad24230b303a1ad1171e19c117c914a65d6 arm32/snponly.efi +dir - arm64 +dir - arm64-sb +link shimaa64.efi arm64-sb/ipxe-shim.efi +file 01b2438e6dc354c343590c308f1d4f000bc098be4d2237540432023abdbddcb8 arm64-sb/ipxe.efi +file 8c13ca9078279c4012dc26dbd095bbebd54553db88af0b67a76b1641cf7b1a87 arm64-sb/shimaa64.efi +link shimaa64.efi arm64-sb/snponly-shim.efi +file bed8da8639b45eeec55d41627349863efa5085a2113bb1193838b8a463e5f49e arm64-sb/snponly.efi +file cff2abf51b7b491d7c5e2ef68c45003a0b4c5e866cb5b20f162569746d53390d arm64/ipxe-legacy.efi +file a9cb6df506a68f3afa4bb94cf6cb8e3862a5a2ff3bf1d8027fea83f7e1d49217 arm64/ipxe.efi +file 03665b4184e4a0b8b9a889de0c7fb833fc48b02385e3fa40a4460db1122f1b36 arm64/snponly.efi +dir - i386 +file 9dc4aee199f582fb8f55cf8bfb4622998c5e71c2dde149d5bbdfe33121c925d8 i386/ipxe-legacy.efi +file df9bab29487d20af3fe9dda46afc775009ba1c089d2216098b9a6e82d906939b i386/ipxe-legacy.pxe +file ea63c6d3745e751e8db4f9c8d4c97efa883fb6d0fc57c697b3d13ad98c1bfe0c i386/ipxe.efi +file a0f144a5f7e10e567d189606c6c2320c1f43a228f9fb6148e7e52f5009fd789f i386/ipxe.pxe +file 81a086995758b889fba7f13d7c5255dc7eb87ed38350b8c03112b9f56556de98 i386/snponly.efi +file ef6ff013f988c52feb3e543dccf0bdc50cd1d68b8a4ab84b8975cef33f64e848 i386/undionly.kpxe +link x86_64/ipxe-legacy.efi ipxe-legacy.efi +link x86_64/ipxe-legacy.pxe ipxe-legacy.pxe +link x86_64/ipxe.efi ipxe.efi +link x86_64/ipxe.pxe ipxe.pxe +dir - loong64 +file 03b078b2ba00e97427b5d99b14035bdb7abdb48631aba8759cf95c24a2620b7e loong64/ipxe-legacy.efi +file c326c8bf54d8fc40fa6730907ee07cb44e8151ecc1397e1ddd2c0edef7cb2872 loong64/ipxe.efi +file fd7cb2df195ad5cc3a09f41fab23713afa23624ff664f83de0e7ca0fd5776fb7 loong64/snponly.efi +dir - riscv32 +file 41f76da3efc3293887a701f35cac389c04a3a8110743673e37451e3c6e24e507 riscv32/ipxe-legacy.efi +file 4afd51329213e50a4aaf24b397e8aba67910ca1e94ea3a6fab0e089d91ac69b8 riscv32/ipxe.efi +file 0a901453bde1392051e53bb1e75584a4eb7cc9c75a27a2b34d7b26ad21734f8f riscv32/snponly.efi +dir - riscv64 +file a72923e2ff600bd94628ef5f97a7ddd5c23fabdc9736fdaa2c22cfb7f0fe908d riscv64/ipxe-legacy.efi +file 53c926c45043c1f2ad03aa70aa22921accc3202865c791cc6023fa17d92f8f24 riscv64/ipxe.efi +file 95b9f6466e0d88fafe642c46d3d6024e4107cac24d2197664b62610f21a48585 riscv64/snponly.efi +link x86_64-sb sb +link x86_64/snponly.efi snponly.efi +link x86_64/undionly.kpxe undionly.kpxe +dir - x86_64 +dir - x86_64-sb +link shimx64.efi x86_64-sb/ipxe-shim.efi +file 6558e37887516b246d6a97122e8d18bedfe4197b7ba7f67bf1bf102a16678d33 x86_64-sb/ipxe.efi +file 83ad71c7d4f2cf328b75b653d09bf3bea5f29bee2e67ca058f37d83c07133885 x86_64-sb/shimx64.efi +link shimx64.efi x86_64-sb/snponly-shim.efi +file b1e67c3e4a1e8708ddfd0079ad4505e3a02245acb55ee9a95437ab3c507be82a x86_64-sb/snponly.efi +file 1d567f2e89a3c0cf0a184549918a3e799a47dc542df385c56e6b464e4765fae2 x86_64/ipxe-legacy.efi +file fc37ef1e4a4bd5d22f8db3c84e1718ac8b03fe20156cf2fc4b9e2ae5e4a348db x86_64/ipxe-legacy.pxe +file 868aa34057ff416ebf2fdfb5781de035e2c540477c04039198a9f8a9c6130034 x86_64/ipxe.efi +file 0fb21c695c82fd70d4f89732ddcd285063776e2bbd71de9a2c39e9cfe4b180b8 x86_64/ipxe.pxe +file f61c2ce34e05d7d857633df2e512d547df75b6aa18b2da152a7c9af222cfe28f x86_64/snponly.efi +file 4186562d21ff54e970d905751c9f36d628e73a51a94afe4a6a42f925b0df448c x86_64/undionly.kpxe diff --git a/ipxe-xcat/sbuild.pl b/ipxe-xcat/sbuild.pl new file mode 100755 index 0000000..c8a79c9 --- /dev/null +++ b/ipxe-xcat/sbuild.pl @@ -0,0 +1,55 @@ +#!/usr/bin/env perl +# ipxe-xcat/sbuild.pl -- per-package Ubuntu/Debian builder for ipxe-xcat, the apt analogue of +# ipxe-xcat/mockbuild.pl. Invoked by sbuild-all.pl per (codename,arch); also runnable standalone. +# The build runs on a copy of the package tree inside the --sbuild chroot, and it +# checks the archives before dpkg-buildpackage and the built payload after it, so a deb that +# differs from the release never reaches --result-dir. +use strict; +use warnings; +use Cwd qw(abs_path); +use File::Basename qw(basename); +use Getopt::Long qw(GetOptions); +use FindBin qw($RealBin); +use lib "$RealBin/.."; +use BuildUtils qw(chroot_name build_deb_in_chroot); + +my $pkg_dir = abs_path($RealBin); +my $pkg = basename($pkg_dir); +my ($codename, $arch, $chroot, $result_dir, $log_dir) = ('', '', '', '', ''); +my ($build_timestamp, $build_number, $skip_install) = (undef, undef, 0); +# --log-dir, --build-number and --skip-install keep the command line sbuild-all.pl passes to every +# builder; this package has no use for them. +GetOptions( + 'codename=s' => \$codename, 'arch=s' => \$arch, 'chroot=s' => \$chroot, + 'result-dir=s' => \$result_dir, 'log-dir=s' => \$log_dir, + 'build-timestamp=i' => \$build_timestamp, 'build-number=i' => \$build_number, + 'skip-install!' => \$skip_install, +) or die "bad options\n"; +$arch ||= `dpkg --print-architecture 2>/dev/null`; chomp $arch; $arch ||= 'amd64'; +die "FATAL: --codename required\n" unless $codename; +$chroot ||= chroot_name($codename, $arch); +$result_dir ||= "$pkg_dir/../build-output/sbuild/$codename/$arch"; +$build_timestamp = time() unless defined $build_timestamp; + +# ipxe-xcat is Architecture:all and is built once on amd64 (see debs-manifest.conf). +my $build = <<'BUILD'; +set -e +sha256sum --check --strict SHA256SUMS +dpkg-buildpackage -uc -us -b +version=$(dpkg-parsechangelog -S Version) +payload=$(mktemp -d) +dpkg-deb -x "../ipxe-xcat_${version}_all.deb" "$payload" +perl ./verify-payload.pl "$payload/tftpboot/xcat/ipxe" payload.sha256 +source_archive="ipxe-${version%-*}-source.tar.gz" +grep -F " $source_archive" SHA256SUMS \ + | (cd "$payload/usr/share/doc/ipxe-xcat" && sha256sum --check --strict -) +for licence in $(cd licenses && find . -type f); do + cmp "licenses/$licence" "$payload/usr/share/doc/ipxe-xcat/licenses/$licence" +done +rm -rf "$payload" +BUILD + +build_deb_in_chroot( + pkg => $pkg, chroot => $chroot, pkg_dir => $pkg_dir, result_dir => $result_dir, + build_timestamp => $build_timestamp, build => $build, +); diff --git a/ipxe-xcat/verify-payload.pl b/ipxe-xcat/verify-payload.pl new file mode 100755 index 0000000..fd5f759 --- /dev/null +++ b/ipxe-xcat/verify-payload.pl @@ -0,0 +1,118 @@ +#!/usr/bin/perl +# verify-payload.pl -- compare an unpacked ipxe-xcat tree with payload.sha256. +# +# verify-payload.pl --generate > payload.sha256 +# verify-payload.pl +# +# Each manifest line is "\t\t", sorted by path: "file" with the SHA-256 of the +# content, "link" with the symlink target, "dir" with "-". Paths are relative to , and +# symlinks are never followed. The check exits 0 when the tree matches the manifest entry for +# entry, 1 when it differs, and 2 on a usage or read error. +use strict; +use warnings; +use Digest::SHA (); +use File::Find (); +use Getopt::Long qw(GetOptions); + +my $generate = 0; +GetOptions('generate' => \$generate) or usage(); + +if ($generate) { + usage() if @ARGV != 1; + my $tree = scan_tree($ARGV[0]); + print "# ipxe-xcat payload manifest, written by verify-payload.pl --generate\n"; + for my $path (sort keys %{$tree}) { + print join("\t", @{ $tree->{$path} }, $path), "\n"; + } + exit 0; +} + +usage() if @ARGV != 2; +my ($root, $manifest_file) = @ARGV; +my $expected = read_manifest($manifest_file); +my $found = scan_tree($root); + +my @problems; +for my $path (sort keys %{$expected}) { + my ($type, $value) = @{ $expected->{$path} }; + if (!exists $found->{$path}) { + push @problems, "missing: $path"; + next; + } + my ($found_type, $found_value) = @{ $found->{$path} }; + if ($found_type ne $type) { + push @problems, "type changed: $path (expected $type, found $found_type)"; + } elsif ($type eq 'file' && $found_value ne $value) { + push @problems, "content changed: $path"; + } elsif ($type eq 'link' && $found_value ne $value) { + push @problems, "link target changed: $path (expected $value, found $found_value)"; + } +} +push @problems, map { "unexpected: $_" } grep { !exists $expected->{$_} } sort keys %{$found}; + +if (@problems) { + print STDERR "$_\n" for @problems; + print STDERR "payload does not match $manifest_file: " . scalar(@problems) . " difference(s)\n"; + exit 1; +} +print "payload matches $manifest_file: " . scalar(keys %{$expected}) . " entries\n"; +exit 0; + +sub usage { + print STDERR "Usage: $0 --generate \n $0 \n"; + exit 2; +} + +sub fail { + my ($message) = @_; + print STDERR "$0: $message\n"; + exit 2; +} + +sub scan_tree { + my ($dir) = @_; + $dir =~ s{/+\z}{} if $dir ne '/'; + fail("not a directory: $dir") if -l $dir || !-d $dir; + my %entries; + File::Find::find({ + no_chdir => 1, + wanted => sub { + my $path = $File::Find::name; + return if $path eq $dir; + my $relative = substr($path, length($dir) + 1); + lstat($path) or fail("cannot stat $path: $!"); + if (-l _) { + my $target = readlink($path); + fail("cannot read link $path: $!") if !defined $target; + $entries{$relative} = ['link', $target]; + } elsif (-d _) { + $entries{$relative} = ['dir', '-']; + } elsif (-f _) { + my $sha = Digest::SHA->new(256); + eval { $sha->addfile($path, 'b'); 1 } or fail("cannot read $path: $@"); + $entries{$relative} = ['file', $sha->hexdigest]; + } else { + $entries{$relative} = ['other', '-']; + } + }, + }, $dir); + return \%entries; +} + +sub read_manifest { + my ($file) = @_; + open(my $fh, '<', $file) or fail("cannot read $file: $!"); + my %entries; + while (my $line = <$fh>) { + chomp $line; + next if $line =~ /^\s*(?:#|$)/; + my ($type, $value, $path) = split(/\t/, $line, 3); + fail("$file line $.: malformed entry") + if !defined $path || $path eq '' || $type !~ /^(?:file|link|dir)$/ + || ($type eq 'file' && $value !~ /^[0-9a-f]{64}$/); + fail("$file line $.: duplicate path $path") if exists $entries{$path}; + $entries{$path} = [$type, $value]; + } + close($fh); + return \%entries; +}