aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-01-24 14:46:19 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-01-30 12:32:32 +0200
commit67a0e8d70f0caf8b85e0cf2031333236b2a3dcdf (patch)
tree3a7d71cd1553338cccb19190dc47bfceafd52499
parentcf86194e91d55464f9c137258e4157415d196697 (diff)
Add checksum verification
-rw-r--r--bpkg/buildfile1
-rw-r--r--bpkg/cfg-fetch.cxx57
-rw-r--r--bpkg/checksum32
-rw-r--r--bpkg/checksum.cxx385
-rw-r--r--bpkg/common.cli26
-rw-r--r--bpkg/fetch9
-rw-r--r--bpkg/fetch.cxx71
-rw-r--r--bpkg/package7
-rw-r--r--bpkg/package.xml1
-rw-r--r--bpkg/pkg-fetch.cxx16
-rw-r--r--bpkg/rep-create.cxx45
-rw-r--r--bpkg/rep-info.cxx14
-rw-r--r--bpkg/types12
-rwxr-xr-xtests/test.sh3
14 files changed, 631 insertions, 48 deletions
diff --git a/bpkg/buildfile b/bpkg/buildfile
index e4f5756..4effcaf 100644
--- a/bpkg/buildfile
+++ b/bpkg/buildfile
@@ -13,6 +13,7 @@ exe{bpkg}: \
{hxx cxx}{ cfg-add } {hxx ixx cxx}{ cfg-add-options } \
{hxx cxx}{ cfg-create } {hxx ixx cxx}{ cfg-create-options } \
{hxx cxx}{ cfg-fetch } {hxx ixx cxx}{ cfg-fetch-options } \
+{hxx cxx}{ checksum } \
{hxx ixx cxx}{ common-options } \
{hxx ixx cxx}{ configuration-options } \
{hxx cxx}{ database } \
diff --git a/bpkg/cfg-fetch.cxx b/bpkg/cfg-fetch.cxx
index c261863..d90ab93 100644
--- a/bpkg/cfg-fetch.cxx
+++ b/bpkg/cfg-fetch.cxx
@@ -58,10 +58,26 @@ namespace bpkg
r->fetched = true; // Mark as being fetched.
- // Load the 'repositories' file and use it to populate the
- // prerequisite and complement repository sets.
+ // Load the 'packages' file. We do this first so that we can get and
+ // verify the checksum of the 'repositories' file which below.
//
- repository_manifests rms (fetch_repositories (co, rl, true));
+ package_manifests pms (fetch_packages (co, rl, true));
+
+ // Load the 'repositories' file and use it to populate the prerequisite and
+ // complement repository sets.
+ //
+ repository_manifests rms;
+
+ try
+ {
+ rms = fetch_repositories (co, rl, pms.sha256sum, true);
+ }
+ catch (const checksum_mismatch&)
+ {
+ fail << "repository files checksum mismatch for "
+ << rl.canonical_name () <<
+ info << "try again";
+ }
for (repository_manifest& rm: rms)
{
@@ -150,13 +166,6 @@ namespace bpkg
}
}
- // Load the 'packages' file.
- //
- // @@ We need to check that that 'repositories' file hasn't
- // changed since.
- //
- package_manifests pms (fetch_packages (co, rl, true));
-
// "Suspend" session while persisting packages to reduce memory
// consumption.
//
@@ -178,6 +187,27 @@ namespace bpkg
p = make_shared<available_package> (move (pm));
persist = true;
}
+ else
+ {
+ // Make sure this is the same package.
+ //
+ assert (p->sha256sum && !p->locations.empty ()); // Can't be transient.
+
+ if (*pm.sha256sum != *p->sha256sum)
+ {
+ // All the previous repositories that contain this package have the
+ // same checksum (since they passed this test), so we can pick any
+ // to show to the user.
+ //
+ const string& r1 (rl.canonical_name ());
+ const string& r2 (p->locations[0].repository.object_id ());
+
+ fail << "checksum mismatch for " << pm.name << " " << pm.version <<
+ info << r1 << " has " << *pm.sha256sum <<
+ info << r2 << " has " << *p->sha256sum <<
+ info << "consider reporting this to the repository maintainers";
+ }
+ }
// This repository shouldn't already be in the location set since
// that would mean it has already been loaded and we shouldn't be
@@ -251,7 +281,12 @@ namespace bpkg
// their packages.
//
for (const lazy_shared_ptr<repository>& lp: ua)
- cfg_fetch (o, t, lp.load (), root, ""); // No reason (user-added).
+ {
+ shared_ptr<repository> r (lp.load ());
+
+ if (!r->fetched) // Can already be loaded as a prerequisite/complement.
+ cfg_fetch (o, t, r, root, ""); // No reason (user-added).
+ }
size_t rcount, pcount;
if (verb)
diff --git a/bpkg/checksum b/bpkg/checksum
new file mode 100644
index 0000000..3ef35cc
--- /dev/null
+++ b/bpkg/checksum
@@ -0,0 +1,32 @@
+// file : bpkg/checksum -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BPKG_CHECKSUM
+#define BPKG_CHECKSUM
+
+#include <bpkg/types>
+#include <bpkg/utility>
+
+#include <bpkg/common-options>
+
+namespace bpkg
+{
+ // Calculate SHA256 sum of the specified memory buffer in binary mode. Issue
+ // diagnostics and throw failed if anything goes wrong.
+ //
+ string
+ sha256 (const common_options&, const char* buf, size_t n);
+
+ // The same but for a stream (if ifstream, open in binary mode).
+ //
+ string
+ sha256 (const common_options&, istream&);
+
+ // The same but for a file.
+ //
+ string
+ sha256 (const common_options&, const path& file);
+}
+
+#endif // BPKG_CHECKSUM
diff --git a/bpkg/checksum.cxx b/bpkg/checksum.cxx
new file mode 100644
index 0000000..95f2d80
--- /dev/null
+++ b/bpkg/checksum.cxx
@@ -0,0 +1,385 @@
+// file : bpkg/checksum.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <bpkg/checksum>
+
+#include <fstream>
+#include <streambuf>
+
+#include <butl/process>
+#include <butl/fdstream>
+#include <butl/filesystem>
+
+#include <bpkg/diagnostics>
+
+using namespace std;
+using namespace butl;
+
+namespace bpkg
+{
+ // sha256
+ //
+ static bool
+ check_sha256 (const path& prog)
+ {
+ // This one doesn't have --version or --help. Running it without any
+ // arguments causes it to calculate the sum of STDIN. But we can ask
+ // it to calculate a sum of an empty string.
+ //
+ const char* args[] = {prog.string ().c_str (), "-q", "-s", "", nullptr};
+
+ if (verb >= 3)
+ print_process (args);
+
+ try
+ {
+ process pr (args, 0, -1, 1); // Redirect STDOUT and STDERR to a pipe.
+
+ ifdstream is (pr.in_ofd);
+ string l;
+ getline (is, l);
+
+ return
+ l == "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
+ && pr.wait ();
+ }
+ catch (const process_error& e)
+ {
+ if (e.child ())
+ exit (1);
+
+ return false;
+ }
+ }
+
+ static process
+ start_sha256 (const path& prog, const strings& ops)
+ {
+ cstrings args {prog.string ().c_str (), "-q"};
+
+ for (const string& o: ops)
+ args.push_back (o.c_str ());
+
+ args.push_back (nullptr);
+
+ if (verb >= 2)
+ print_process (args);
+
+ // Pipe both STDIN and STDOUT. Process exceptions must be handled by
+ // the caller.
+ //
+ return process (args.data (), -1, -1);
+ }
+
+ // sha256sum
+ //
+ static bool
+ check_sha256sum (const path& prog)
+ {
+ // sha256sum --version prints the version to STDOUT and exits with 0
+ // status. The first line starts with "sha256sum (GNU coreutils) 8.21".
+ //
+ const char* args[] = {prog.string ().c_str (), "--version", nullptr};
+
+ if (verb >= 3)
+ print_process (args);
+
+ try
+ {
+ process pr (args, 0, -1); // Redirect STDOUT to a pipe.
+
+ ifdstream is (pr.in_ofd);
+ string l;
+ getline (is, l);
+
+ return l.compare (0, 9, "sha256sum") == 0 && pr.wait ();
+ }
+ catch (const process_error& e)
+ {
+ if (e.child ())
+ exit (1);
+
+ return false;
+ }
+ }
+
+ static process
+ start_sha256sum (const path& prog, const strings& ops)
+ {
+ cstrings args {prog.string ().c_str (), "-b"};
+
+ for (const string& o: ops)
+ args.push_back (o.c_str ());
+
+ args.push_back (nullptr);
+
+ if (verb >= 2)
+ print_process (args);
+
+ // Pipe both STDIN and STDOUT. Process exceptions must be handled by
+ // the caller.
+ //
+ return process (args.data (), -1, -1);
+ }
+
+ // shasum
+ //
+ static bool
+ check_shasum (const path& prog)
+ {
+ // shasum --version prints just the version to STDOUT and exits with 0
+ // status. The output looks like "5.84".
+ //
+ const char* args[] = {prog.string ().c_str (), "--version", nullptr};
+
+ if (verb >= 3)
+ print_process (args);
+
+ try
+ {
+ process pr (args, 0, -1); // Redirect STDOUT to a pipe.
+
+ ifdstream is (pr.in_ofd);
+ string l;
+ getline (is, l);
+
+ return l.size () != 0 && l[0] >= '0' && l[0] <= '9' && pr.wait ();
+ }
+ catch (const process_error& e)
+ {
+ if (e.child ())
+ exit (1);
+
+ return false;
+ }
+ }
+
+ static process
+ start_shasum (const path& prog, const strings& ops)
+ {
+ cstrings args {prog.string ().c_str (), "-a", "256", "-b"};
+
+ for (const string& o: ops)
+ args.push_back (o.c_str ());
+
+ args.push_back (nullptr);
+
+ if (verb >= 2)
+ print_process (args);
+
+ // Pipe both STDIN and STDOUT. Process exceptions must be handled by
+ // the caller.
+ //
+ return process (args.data (), -1, -1);
+ }
+
+ // The dispatcher.
+ //
+ // Cache the result of finding/testing the sha256 program. Sometimes
+ // a simple global variable is really the right solution...
+ //
+ enum class kind {sha256, sha256sum, shasum};
+
+ static path sha256_path;
+ static kind sha256_kind;
+
+ static kind
+ check (const common_options& o)
+ {
+ if (!sha256_path.empty ())
+ return sha256_kind; // Cached.
+
+ if (o.sha256_specified ())
+ {
+ const path& p (sha256_path = o.sha256 ());
+
+ // Figure out which one it is.
+ //
+ const path& n (p.leaf ());
+ const string& s (n.string ());
+
+ if (s.find ("sha256sum") != string::npos)
+ {
+ if (!check_sha256sum (p))
+ fail << p << " does not appear to be the 'sha256sum' program";
+
+ sha256_kind = kind::sha256sum;
+ }
+ else if (s.find ("shasum") != string::npos)
+ {
+ if (!check_shasum (p))
+ fail << p << " does not appear to be the 'shasum' program";
+
+ sha256_kind = kind::shasum;
+ }
+ else if (s.find ("sha256") != string::npos)
+ {
+ if (!check_sha256 (p))
+ fail << p << " does not appear to be the 'sha256' program";
+
+ sha256_kind = kind::sha256;
+ }
+ else
+ fail << "unknown sha256 program " << p;
+ }
+ else
+ {
+ // See if any is available. The preference order is:
+ //
+ // sha256 (FreeBSD)
+ // sha256sum (Linux coreutils)
+ // shasum (Perl tool, Mac OS)
+ //
+ if (check_sha256 (sha256_path = path ("sha256")))
+ {
+ sha256_kind = kind::sha256;
+ }
+ else if (check_sha256sum (sha256_path = path ("sha256sum")))
+ {
+ sha256_kind = kind::sha256sum;
+ }
+ else if (check_shasum (sha256_path = path ("shasum")))
+ {
+ sha256_kind = kind::shasum;
+ }
+ else
+ fail << "unable to find 'sha256', 'sha256sum', or 'shasum'" <<
+ info << "use --sha256 to specify the sha256 program location";
+
+ if (verb > 1)
+ info << "using '" << sha256_path << "' as the sha256 program, "
+ << "use --sha256 to override";
+ }
+
+ return sha256_kind;
+ }
+
+ static process
+ start (const common_options& o)
+ {
+ process (*f) (const path&, const strings&) = nullptr;
+
+ switch (check (o))
+ {
+ case kind::sha256: f = &start_sha256; break;
+ case kind::sha256sum: f = &start_sha256sum; break;
+ case kind::shasum: f = &start_shasum; break;
+ }
+
+ try
+ {
+ return f (sha256_path, o.sha256_option ());
+ }
+ catch (const process_error& e)
+ {
+ error << "unable to execute " << sha256_path << ": " << e.what ();
+
+ if (e.child ())
+ exit (1);
+
+ throw failed ();
+ }
+ }
+
+ static string
+ sha256 (const common_options& o, streambuf& sb)
+ {
+ process pr (start (o));
+
+ try
+ {
+ ofdstream os (pr.out_fd);
+ os.exceptions (ofdstream::badbit | ofdstream::failbit);
+
+ ifdstream is (pr.in_ofd);
+ is.exceptions (ifdstream::badbit | ifdstream::failbit);
+
+ os << &sb;
+ os.close ();
+
+ // All three tools output the sum as the first word.
+ //
+ string s;
+ is >> s;
+ is.close ();
+
+ if (pr.wait ())
+ {
+ if (s.size () != 64)
+ fail << "'" << s << "' doesn't appear to be a SHA256 sum" <<
+ info << "produced by '" << sha256_path << "'; "
+ << "use --sha256 to override";
+
+ return s;
+ }
+
+ // Child existed with an error, fall through.
+ }
+ // Ignore these exceptions if the child process exited with an error status
+ // since that's the source of the failure.
+ //
+ catch (const iostream::failure&)
+ {
+ if (pr.wait ())
+ fail << "unable to read/write '" << sha256_path << "' output/input";
+ }
+
+ // We should only get here if the child exited with an error status.
+ //
+ assert (!pr.wait ());
+
+ // While it is reasonable to assuming the child process issued diagnostics,
+ // issue something just in case.
+ //
+ error << "unable to calculate SHA256 sum using '" << sha256_path << "'" <<
+ info << "re-run with -v for more information";
+
+ throw failed ();
+ }
+
+ struct memstreambuf: streambuf
+ {
+ memstreambuf (const char* buf, size_t n)
+ {
+ char* b (const_cast<char*> (buf));
+ setg (b, b, b + n);
+ }
+ };
+
+ string
+ sha256 (const common_options& o, const char* buf, size_t n)
+ {
+ memstreambuf msb (buf, n);
+ return sha256 (o, msb);
+ }
+
+ string
+ sha256 (const common_options& o, istream& is)
+ {
+ return sha256 (o, *is.rdbuf ());
+ }
+
+ string
+ sha256 (const common_options& o, const path& f)
+ {
+ if (!exists (f))
+ fail << "file " << f << " does not exist";
+
+ try
+ {
+ ifstream ifs (f.string (), ios::binary);
+ if (!ifs.is_open ())
+ fail << "unable to open " << f << " in read mode";
+
+ ifs.exceptions (ofstream::badbit | ofstream::failbit);
+
+ return sha256 (o, ifs);
+ }
+ catch (const iostream::failure&)
+ {
+ error << "unable read " << f;
+ throw failed ();
+ }
+ }
+}
diff --git a/bpkg/common.cli b/bpkg/common.cli
index 60692c3..4776e73 100644
--- a/bpkg/common.cli
+++ b/bpkg/common.cli
@@ -101,7 +101,7 @@ namespace bpkg
to the fetch program with \cb{--fetch-option}.
If the fetch program is not specified, then \cb{bpkg} will try to
- discover if one of the above program is available and use that.
+ discover if one of the above programs is available and use that.
Currently, \cb{bpkg} has the following preference order: \cb{wget}
1.16 or higher (supports \cb{--show-progress}), \cb{curl},
\cb{wget}, and \cb{fetch}."
@@ -115,6 +115,30 @@ namespace bpkg
specify multiple fetch options."
}
+ path --sha256
+ {
+ "<path>",
+ "The sha256 program to be used to calculate SHA256 sums. Currently,
+ \cb{bpkg} recognizes \cb{sha256}, \cb{sha256sum}, and \cb{shasum}.
+ Note that the last component of <path> must contain one of these names
+ as a substring in order for \cb{bpkg} to recognize which program is
+ being used. You can also specify additional options that should be
+ passed to the sha256 program with \cb{--sha256-option}.
+
+ If the sha256 program is not specified, then \cb{bpkg} will try to
+ discover if one of the above programs is available and use that.
+ Currently, \cb{bpkg} has the following preference order: \cb{sha256},
+ \cb{sha256sum}, and \cb{shasum}."
+ }
+
+ strings --sha256-option
+ {
+ "<opt>",
+ "Additional option to be passed to the sha256 program. See \cb{--sha256}
+ for more information on the sha256 program. Repeat this option to
+ specify multiple sha256 options."
+ }
+
path --tar = "tar"
{
"<path>",
diff --git a/bpkg/fetch b/bpkg/fetch
index 18dd665..06c2d15 100644
--- a/bpkg/fetch
+++ b/bpkg/fetch
@@ -5,18 +5,27 @@
#ifndef BPKG_FETCH
#define BPKG_FETCH
+#include <exception>
+
#include <bpkg/manifest>
#include <bpkg/types>
#include <bpkg/utility>
+
#include <bpkg/common-options>
namespace bpkg
{
+ class checksum_mismatch: public std::exception {};
+
repository_manifests fetch_repositories (const dir_path&,
bool ignore_unknown);
+
+ // Verify the checksum and throw checksum_mismatch if it doesn't match.
+ //
repository_manifests fetch_repositories (const common_options&,
const repository_location&,
+ const string& sha256sum,
bool ignore_unknown);
package_manifests fetch_packages (const dir_path&,
diff --git a/bpkg/fetch.cxx b/bpkg/fetch.cxx
index c47fb55..257d903 100644
--- a/bpkg/fetch.cxx
+++ b/bpkg/fetch.cxx
@@ -5,7 +5,7 @@
#include <bpkg/fetch>
#include <fstream>
-#include <cstdint> // uint16_t
+#include <sstream>
#include <butl/process>
#include <butl/fdstream>
@@ -13,6 +13,7 @@
#include <bpkg/manifest-parser>
+#include <bpkg/checksum>
#include <bpkg/diagnostics>
#include <bpkg/bpkg-version>
@@ -354,7 +355,7 @@ namespace bpkg
static path fetch_path;
static kind fetch_kind;
- kind
+ static kind
check (const common_options& o)
{
if (!fetch_path.empty ())
@@ -523,6 +524,8 @@ namespace bpkg
return r;
}
+ // If sha256sum is empty, then don't verify.
+ //
template <typename M>
static M
fetch_manifest (const common_options& o,
@@ -530,6 +533,7 @@ namespace bpkg
const string& host,
uint16_t port,
const path& f,
+ const string& sha256sum,
bool ignore_unknown)
{
string url (to_url (proto, host, port, f));
@@ -540,9 +544,32 @@ namespace bpkg
ifdstream is (pr.in_ofd);
is.exceptions (ifdstream::badbit | ifdstream::failbit);
- manifest_parser mp (is, url);
- M m (mp, ignore_unknown);
- is.close ();
+ M m;
+ if (sha256sum.empty ())
+ {
+ manifest_parser mp (is, url);
+ m = M (mp, ignore_unknown);
+ is.close ();
+ }
+ else
+ {
+ // Unfortunately we cannot rewind STDOUT as we do below for files.
+ // There doesn't seem to be anything better than reading the entire
+ // file into memory and then streaming it twice, once to calculate
+ // the checksum and the second time to actually parse.
+ //
+ stringstream ss;
+ ss << is.rdbuf ();
+ is.close ();
+
+ if (sha256sum != sha256 (o, ss))
+ throw checksum_mismatch ();
+
+ ss.clear (); ss.seekg (0); // Rewind.
+
+ manifest_parser mp (ss, url);
+ m = M (mp, ignore_unknown);
+ }
if (pr.wait ())
return m;
@@ -618,9 +645,14 @@ namespace bpkg
return r;
}
+ // If sha256sum is empty, then don't verify.
+ //
template <typename M>
static M
- fetch_manifest (const path& f, bool ignore_unknown)
+ fetch_manifest (const common_options* o,
+ const path& f,
+ const string& sha256sum,
+ bool ignore_unknown)
{
if (!exists (f))
fail << "file " << f << " does not exist";
@@ -631,6 +663,16 @@ namespace bpkg
ifs.exceptions (ofstream::badbit | ofstream::failbit);
ifs.open (f.string ());
+ if (!sha256sum.empty ())
+ {
+ assert (o != nullptr);
+
+ if (sha256sum != sha256 (*o, ifs))
+ throw checksum_mismatch ();
+
+ ifs.seekg (0); // Rewind the file stream.
+ }
+
manifest_parser mp (ifs, f.string ());
return M (mp, ignore_unknown);
}
@@ -651,12 +693,17 @@ namespace bpkg
repository_manifests
fetch_repositories (const dir_path& d, bool iu)
{
- return fetch_manifest<repository_manifests> (d / repositories, iu);
+ return fetch_manifest<repository_manifests> (
+ nullptr,
+ d / repositories,
+ "", // No checksum verification.
+ iu);
}
repository_manifests
fetch_repositories (const common_options& o,
const repository_location& rl,
+ const string& sha256sum,
bool iu)
{
assert (rl.remote () || rl.absolute ());
@@ -665,8 +712,8 @@ namespace bpkg
return rl.remote ()
? fetch_manifest<repository_manifests> (
- o, rl.proto (), rl.host (), rl.port (), f, iu)
- : fetch_manifest<repository_manifests> (f, iu);
+ o, rl.proto (), rl.host (), rl.port (), f, sha256sum, iu)
+ : fetch_manifest<repository_manifests> (&o, f, sha256sum, iu);
}
static const path packages ("packages");
@@ -674,7 +721,7 @@ namespace bpkg
package_manifests
fetch_packages (const dir_path& d, bool iu)
{
- return fetch_manifest<package_manifests> (d / packages, iu);
+ return fetch_manifest<package_manifests> (nullptr, d / packages, "", iu);
}
package_manifests
@@ -688,8 +735,8 @@ namespace bpkg
return rl.remote ()
? fetch_manifest<package_manifests> (
- o, rl.proto (), rl.host (), rl.port (), f, iu)
- : fetch_manifest<package_manifests> (f, iu);
+ o, rl.proto (), rl.host (), rl.port (), f, "", iu)
+ : fetch_manifest<package_manifests> (&o, f, "", iu);
}
path
diff --git a/bpkg/package b/bpkg/package
index 3e8c989..da19def 100644
--- a/bpkg/package
+++ b/bpkg/package
@@ -302,11 +302,16 @@ namespace bpkg
dependencies_type dependencies;
+ // Present for non-transient objects only.
+ //
+ optional<string> sha256sum;
+
public:
available_package (package_manifest&& m)
: id (move (m.name), m.version),
version (move (m.version)),
- dependencies (move (m.dependencies)) {}
+ dependencies (move (m.dependencies)),
+ sha256sum (move (m.sha256sum)) {}
// Database mapping.
//
diff --git a/bpkg/package.xml b/bpkg/package.xml
index 80a4aa7..0e0872d 100644
--- a/bpkg/package.xml
+++ b/bpkg/package.xml
@@ -53,6 +53,7 @@
<column name="version_revision" type="INTEGER" null="true"/>
<column name="version_upstream" type="TEXT" null="true"/>
<column name="version_release" type="TEXT" null="true"/>
+ <column name="sha256sum" type="TEXT" null="true"/>
<primary-key>
<column name="name"/>
<column name="version_epoch"/>
diff --git a/bpkg/pkg-fetch.cxx b/bpkg/pkg-fetch.cxx
index 3032eda..a57059d 100644
--- a/bpkg/pkg-fetch.cxx
+++ b/bpkg/pkg-fetch.cxx
@@ -11,6 +11,7 @@
#include <bpkg/package>
#include <bpkg/package-odb>
#include <bpkg/utility>
+#include <bpkg/checksum>
#include <bpkg/database>
#include <bpkg/diagnostics>
#include <bpkg/manifest-utility>
@@ -23,7 +24,6 @@ using namespace butl;
namespace bpkg
{
-
static shared_ptr<selected_package>
pkg_fetch (dir_path c,
transaction& t,
@@ -218,6 +218,20 @@ namespace bpkg
path a (fetch_archive (co, pl->repository->location, pl->location, c));
auto_rm arm (a);
+ // We can't be fetching an archive for a transient object.
+ //
+ assert (ap->sha256sum);
+
+ const string& sha256sum (sha256 (co, a));
+ if (sha256sum != *ap->sha256sum)
+ {
+ fail << "checksum mismatch for " << n << " " << v <<
+ info << pl->repository->name << " has " << *ap->sha256sum <<
+ info << "fetched archive has " << sha256sum <<
+ info << "try again, if problem persists, consider reporting this to "
+ << "the repository maintainer";
+ }
+
shared_ptr<selected_package> p (
pkg_fetch (c,
t,
diff --git a/bpkg/rep-create.cxx b/bpkg/rep-create.cxx
index c6d07ec..cc2ade1 100644
--- a/bpkg/rep-create.cxx
+++ b/bpkg/rep-create.cxx
@@ -14,9 +14,11 @@
#include <bpkg/manifest>
#include <bpkg/manifest-serializer>
-#include <bpkg/fetch>
#include <bpkg/types>
#include <bpkg/utility>
+
+#include <bpkg/checksum>
+#include <bpkg/fetch>
#include <bpkg/diagnostics>
#include <bpkg/pkg-verify>
@@ -51,6 +53,9 @@ namespace bpkg
using package_map = map<package_key, package_data>;
+ static const path repositories ("repositories");
+ static const path packages ("packages");
+
static void
collect (const rep_create_options& o,
package_map& map,
@@ -89,8 +94,7 @@ namespace bpkg
//
if (d == root)
{
- if (p == path ("repositories") ||
- p == path ("packages"))
+ if (p == repositories || p == packages)
continue;
}
@@ -99,7 +103,12 @@ namespace bpkg
path a (d / p);
package_manifest m (pkg_verify (o, a, o.ignore_unknown ()));
- level4 ([&]{trace << m.name << " " << m.version << " in " << a;});
+ // Calculate its checksum.
+ //
+ m.sha256sum = sha256 (o, a);
+
+ level4 ([&]{trace << m.name << " " << m.version << " in " << a
+ << " sha256sum " << *m.sha256sum;});
// Add package archive location relative to the repository root.
//
@@ -156,9 +165,22 @@ namespace bpkg
package_map pm;
collect (o, pm, d, d);
+ package_manifests manifests;
+ manifests.sha256sum = sha256 (o, path (d / repositories));
+
+ for (auto& p: pm)
+ {
+ package_manifest& m (p.second.manifest);
+
+ if (verb)
+ text << "adding " << m.name << " " << m.version;
+
+ manifests.emplace_back (move (m));
+ }
+
// Serialize.
//
- path p (d / path ("packages"));
+ path p (d / packages);
try
{
@@ -167,18 +189,7 @@ namespace bpkg
ofs.open (p.string ());
manifest_serializer s (ofs, p.string ());
-
- for (const auto& p: pm)
- {
- const package_manifest& m (p.second.manifest);
-
- if (verb)
- text << "adding " << m.name << " " << m.version;
-
- m.serialize (s);
- }
-
- s.next ("", ""); // The end.
+ manifests.serialize (s);
}
catch (const manifest_serialization& e)
{
diff --git a/bpkg/rep-info.cxx b/bpkg/rep-info.cxx
index 2205a5b..241f04b 100644
--- a/bpkg/rep-info.cxx
+++ b/bpkg/rep-info.cxx
@@ -35,9 +35,21 @@ namespace bpkg
// Fetch everything we will need before printing anything. Ignore
// unknown manifest entries unless we are dumping them.
//
- repository_manifests rms (fetch_repositories (o, rl, !o.manifest ()));
package_manifests pms (fetch_packages (o, rl, !o.manifest ()));
+ repository_manifests rms;
+
+ try
+ {
+ rms = fetch_repositories (o, rl, pms.sha256sum, !o.manifest ());
+ }
+ catch (const checksum_mismatch&)
+ {
+ fail << "repository files checksum mismatch for "
+ << rl.canonical_name () <<
+ info << "try again";
+ }
+
// Now print.
//
bool all (!o.name () && !o.repositories () && !o.packages ());
diff --git a/bpkg/types b/bpkg/types
index e667fc0..b0db478 100644
--- a/bpkg/types
+++ b/bpkg/types
@@ -10,6 +10,7 @@
#include <memory> // shared_ptr, unique_ptr
#include <cstddef> // size_t
#include <cstdint> // uint{8,16,32,64}_t
+#include <istream>
#include <ostream>
#include <odb/lazy-ptr.hxx>
@@ -33,6 +34,9 @@ namespace bpkg
using strings = vector<string>;
using cstrings = vector<const char*>;
+ using std::istream;
+ using std::ostream;
+
using butl::optional;
using butl::nullopt;
@@ -56,11 +60,11 @@ namespace bpkg
using paths = std::vector<path>;
using dir_paths = std::vector<dir_path>;
- inline std::ostream&
- operator<< (std::ostream& os, const path& p) {return os << p.string ();}
+ inline ostream&
+ operator<< (ostream& os, const path& p) {return os << p.string ();}
- inline std::ostream&
- operator<< (std::ostream& os, const dir_path& p)
+ inline ostream&
+ operator<< (ostream& os, const dir_path& p)
{
const string& s (p.string ());
os << s;
diff --git a/tests/test.sh b/tests/test.sh
index e2ff4d0..e68b7eb 100755
--- a/tests/test.sh
+++ b/tests/test.sh
@@ -214,6 +214,8 @@ EOF
test rep-info -m -p $rep/common/bar/unstable <<EOF
: 1
+sha256sum: 3034b727288efbb52b7b6e41fe147b815e7b3aa704e8cef6c2ee8d7421ab5b72
+:
name: libbar
version: 1.1.1
summary: libbar
@@ -222,6 +224,7 @@ url: http://example.org
email: pkg@example.org
depends: libfoo >= 1.1.0
location: libbar-1.1.1.tar.gz
+sha256sum: abb073e9729bbc914f26ed0dedad9f5ead2c73a36cbc1e8f2702d6fde7634ff3
EOF