aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvatar David Leverton <levertond@googlemail.com> 2012-03-29 19:38:24 +0100
committerAvatar David Leverton <levertond@googlemail.com> 2012-03-30 18:30:59 +0100
commitad3a7401391d321ae4664a6e8e630ee3611fd512 (patch)
tree618d1470328cda05aea845862b4c4d18626aa7d2
parent28da33d1363d6b7ff1298da7eccd5f387450fbc3 (diff)
downloadpaludis-ad3a7401391d321ae4664a6e8e630ee3611fd512.tar.gz
paludis-ad3a7401391d321ae4664a6e8e630ee3611fd512.tar.xz
Support for verifying MD5-based flat_hash caches
-rw-r--r--paludis/repositories/e/ebuild_flat_metadata_cache.cc97
-rw-r--r--paludis/repositories/e/ebuild_flat_metadata_cache_TEST.cc132
-rwxr-xr-xpaludis/repositories/e/ebuild_flat_metadata_cache_TEST_setup.sh166
3 files changed, 370 insertions, 25 deletions
diff --git a/paludis/repositories/e/ebuild_flat_metadata_cache.cc b/paludis/repositories/e/ebuild_flat_metadata_cache.cc
index 19204ad..5096454 100644
--- a/paludis/repositories/e/ebuild_flat_metadata_cache.cc
+++ b/paludis/repositories/e/ebuild_flat_metadata_cache.cc
@@ -35,6 +35,7 @@
#include <paludis/util/fs_stat.hh>
#include <paludis/util/fs_error.hh>
#include <paludis/util/pimp-impl.hh>
+#include <paludis/util/md5.hh>
#include <paludis/environment.hh>
#include <paludis/unformatted_pretty_printer.hh>
@@ -376,14 +377,36 @@ EbuildFlatMetadataCache::load(const std::shared_ptr<const EbuildID> & id, const
std::vector<std::string> inherited;
{
- std::map<std::string, std::string>::const_iterator mtime_it(keys.find("_mtime_"));
- std::time_t cache_time(keys.end() == mtime_it ? _imp->filename_stat.mtim().seconds() : destringify<std::time_t>(mtime_it->second));
- bool ok(_imp->ebuild_stat.mtim().seconds() == cache_time);
- if (! ok)
- Log::get_instance()->message("e.cache.flat_hash.mtime", ll_debug, lc_context)
- << "ebuild has mtime " << _imp->ebuild_stat.mtim().seconds() << ", but expected " << cache_time;
+ bool ok(true), is_md5(false);
+
+ std::map<std::string, std::string>::const_iterator md5_it(keys.find("_md5_"));
+ if (keys.end() != md5_it)
+ {
+ is_md5 = true;
+ SafeIFStream s(_imp->ebuild);
+ MD5 md5(s);
+ if (md5.hexsum() != md5_it->second)
+ {
+ Log::get_instance()->message("e.cache.flat_hash.md5", ll_debug, lc_context)
+ << "ebuild has MD5 '" << md5.hexsum() << "', but expected '" << md5_it->second << "'";
+ ok = false;
+ }
+ }
- if (ok) {
+ else
+ {
+ std::map<std::string, std::string>::const_iterator mtime_it(keys.find("_mtime_"));
+ std::time_t cache_time(keys.end() == mtime_it ? _imp->filename_stat.mtim().seconds() : destringify<std::time_t>(mtime_it->second));
+ if (_imp->ebuild_stat.mtim().seconds() != cache_time)
+ {
+ Log::get_instance()->message("e.cache.flat_hash.mtime", ll_debug, lc_context)
+ << "ebuild has mtime " << _imp->ebuild_stat.mtim().seconds() << ", but expected " << cache_time;
+ ok = false;
+ }
+ }
+
+ if (ok)
+ {
std::string cache_guessed(keys["_guessed_eapi_"]);
if (cache_guessed.empty())
cache_guessed = "0";
@@ -412,14 +435,6 @@ EbuildFlatMetadataCache::load(const std::shared_ptr<const EbuildID> & id, const
<< "_eclasses_ entry is incomplete";
return false;
}
- FSPath eclass_dir(std::string::npos != it->find('/') ? FSPath(*it++) : eclassdir);
- if (eclasses.end() == it)
- {
- Log::get_instance()->message("e.cache.flat_hash.eclass.truncated", ll_warning, lc_context)
- << "_eclasses_ entry is incomplete";
- return false;
- }
- std::time_t eclass_mtime(destringify<std::time_t>(*it));
auto eclass(_imp->eclass_mtimes->eclass(eclass_name));
if (eclass)
@@ -433,22 +448,47 @@ EbuildFlatMetadataCache::load(const std::shared_ptr<const EbuildID> & id, const
ok = false;
}
- else if (eclass->first.dirname() != eclass_dir)
+ else if (is_md5)
{
- Log::get_instance()->message("e.cache.flat_hash.eclass.wrong_location", ll_debug, lc_context)
- << "Cache-requested eclass '" << eclass_name << "' was found at '"
- << eclass->first.dirname() << "', but expected '" << eclass_dir << "'";
- ok = false;
+ std::string cache_md5(*it), actual_md5(_imp->eclass_mtimes->md5(eclass->first));
+ if (actual_md5 != cache_md5)
+ {
+ Log::get_instance()->message("e.cache.flat_hash.eclass.wrong_md5", ll_debug, lc_context)
+ << "Cache-requested eclass '" << eclass_name << "' has MD5 '"
+ << actual_md5 << "', but expected '" << cache_md5 << "'";
+ ok = false;
+ }
}
- else if (eclass->second.mtim().seconds() != eclass_mtime)
+ else
{
- Log::get_instance()->message("e.cache.flat_hash.eclass.wrong_mtime", ll_debug, lc_context)
- << "Cache-requested eclass '" << eclass_name << "' has mtime "
- << eclass->second.mtim().seconds() << ", but expected " << eclass_mtime;
- ok = false;
+ FSPath eclass_dir(std::string::npos != it->find('/') ? FSPath(*it++) : eclassdir);
+ if (eclasses.end() == it)
+ {
+ Log::get_instance()->message("e.cache.flat_hash.eclass.truncated", ll_warning, lc_context)
+ << "_eclasses_ entry is incomplete";
+ return false;
+ }
+ std::time_t eclass_mtime(destringify<std::time_t>(*it));
+
+ if (eclass->first.dirname() != eclass_dir)
+ {
+ Log::get_instance()->message("e.cache.flat_hash.eclass.wrong_location", ll_debug, lc_context)
+ << "Cache-requested eclass '" << eclass_name << "' was found at '"
+ << eclass->first.dirname() << "', but expected '" << eclass_dir << "'";
+ ok = false;
+ }
+
+ else if (eclass->second.mtim().seconds() != eclass_mtime)
+ {
+ Log::get_instance()->message("e.cache.flat_hash.eclass.wrong_mtime", ll_debug, lc_context)
+ << "Cache-requested eclass '" << eclass_name << "' has mtime "
+ << eclass->second.mtim().seconds() << ", but expected " << eclass_mtime;
+ ok = false;
+ }
}
+
if (! ok)
break;
}
@@ -461,6 +501,13 @@ EbuildFlatMetadataCache::load(const std::shared_ptr<const EbuildID> & id, const
for (std::vector<std::string>::const_iterator it(exlibs.begin()),
it_end(exlibs.end()); it_end != it; ++it)
{
+ if (is_md5)
+ {
+ Log::get_instance()->message("e.cache.flat_hash.exlib.md5.unimplemented", ll_warning, lc_context)
+ << "Verifying _exlibs_ using MD5 is not yet implemented";
+ return false;
+ }
+
std::string exlib_name(*it);
inherited.push_back(exlib_name);
if (exlibs.end() == ++it)
diff --git a/paludis/repositories/e/ebuild_flat_metadata_cache_TEST.cc b/paludis/repositories/e/ebuild_flat_metadata_cache_TEST.cc
index 13a2a2b..c825469 100644
--- a/paludis/repositories/e/ebuild_flat_metadata_cache_TEST.cc
+++ b/paludis/repositories/e/ebuild_flat_metadata_cache_TEST.cc
@@ -446,6 +446,50 @@ TEST(EbuildFlatMetadataCache, HashBadMtime)
EXPECT_EQ("The Generated Description flat_hash-bad-mtime", id1->short_description_key()->parse_value());
}
+TEST(EbuildFlatMetadataCache, HashMD5)
+{
+ TestEnvironment env;
+ std::shared_ptr<Map<std::string, std::string> > keys(std::make_shared<Map<std::string, std::string>>());
+ keys->insert("format", "e");
+ keys->insert("names_cache", "/var/empty");
+ keys->insert("location", stringify(FSPath::cwd() / "ebuild_flat_metadata_cache_TEST_dir/repo"));
+ keys->insert("profiles", stringify(FSPath::cwd() / "ebuild_flat_metadata_cache_TEST_dir/repo/profiles/profile"));
+ keys->insert("eclassdirs", "ebuild_flat_metadata_cache_TEST_dir/repo/eclass ebuild_flat_metadata_cache_TEST_dir/extra_eclasses");
+ keys->insert("builddir", stringify(FSPath::cwd() / "ebuild_flat_metadata_cache_TEST_dir" / "build"));
+ std::shared_ptr<Repository> repo(ERepository::repository_factory_create(&env,
+ std::bind(from_keys, keys, std::placeholders::_1)));
+ env.add_repository(1, repo);
+
+ const std::shared_ptr<const PackageID> id1(*env[selection::RequireExactlyOne(generator::Matches(
+ PackageDepSpec(parse_user_package_dep_spec("=cat/flat_hash-md5-1",
+ &env, { })), make_null_shared_ptr(), { }))]->begin());
+
+ ASSERT_TRUE(bool(id1->short_description_key()));
+ EXPECT_EQ("The Description flat_hash-md5", id1->short_description_key()->parse_value());
+}
+
+TEST(EbuildFlatMetadataCache, HashBadMD5)
+{
+ TestEnvironment env;
+ std::shared_ptr<Map<std::string, std::string> > keys(std::make_shared<Map<std::string, std::string>>());
+ keys->insert("format", "e");
+ keys->insert("names_cache", "/var/empty");
+ keys->insert("location", stringify(FSPath::cwd() / "ebuild_flat_metadata_cache_TEST_dir/repo"));
+ keys->insert("profiles", stringify(FSPath::cwd() / "ebuild_flat_metadata_cache_TEST_dir/repo/profiles/profile"));
+ keys->insert("eclassdirs", "ebuild_flat_metadata_cache_TEST_dir/repo/eclass ebuild_flat_metadata_cache_TEST_dir/extra_eclasses");
+ keys->insert("builddir", stringify(FSPath::cwd() / "ebuild_flat_metadata_cache_TEST_dir" / "build"));
+ std::shared_ptr<Repository> repo(ERepository::repository_factory_create(&env,
+ std::bind(from_keys, keys, std::placeholders::_1)));
+ env.add_repository(1, repo);
+
+ const std::shared_ptr<const PackageID> id1(*env[selection::RequireExactlyOne(generator::Matches(
+ PackageDepSpec(parse_user_package_dep_spec("=cat/flat_hash-bad-md5-1",
+ &env, { })), make_null_shared_ptr(), { }))]->begin());
+
+ ASSERT_TRUE(bool(id1->short_description_key()));
+ EXPECT_EQ("The Generated Description flat_hash-bad-md5", id1->short_description_key()->parse_value());
+}
+
TEST(EbuildFlatMetadataCache, HashNoEAPI)
{
TestEnvironment env;
@@ -766,6 +810,94 @@ TEST(EbuildFlatMetadataCache, HashEclassSpaces)
EXPECT_EQ("The Generated Description flat_hash-eclasses-spaces", id->short_description_key()->parse_value());
}
+TEST(EbuildFlatMetadataCache, HashEclassMD5)
+{
+ TestEnvironment env;
+ std::shared_ptr<Map<std::string, std::string> > keys(std::make_shared<Map<std::string, std::string>>());
+ keys->insert("format", "e");
+ keys->insert("names_cache", "/var/empty");
+ keys->insert("location", stringify(FSPath::cwd() / "ebuild_flat_metadata_cache_TEST_dir/repo"));
+ keys->insert("profiles", stringify(FSPath::cwd() / "ebuild_flat_metadata_cache_TEST_dir/repo/profiles/profile"));
+ keys->insert("eclassdirs", "ebuild_flat_metadata_cache_TEST_dir/repo/eclass ebuild_flat_metadata_cache_TEST_dir/extra_eclasses");
+ keys->insert("builddir", stringify(FSPath::cwd() / "ebuild_flat_metadata_cache_TEST_dir" / "build"));
+ std::shared_ptr<Repository> repo(ERepository::repository_factory_create(&env,
+ std::bind(from_keys, keys, std::placeholders::_1)));
+ env.add_repository(1, repo);
+
+ std::shared_ptr<const PackageID> id(*env[selection::RequireExactlyOne(generator::Matches(
+ PackageDepSpec(parse_user_package_dep_spec("=cat/flat_hash-eclass-md5-1",
+ &env, { })), make_null_shared_ptr(), { }))]->begin());
+
+ ASSERT_TRUE(bool(id->short_description_key()));
+ EXPECT_EQ("The Description flat_hash-eclass-md5", id->short_description_key()->parse_value());
+}
+
+TEST(EbuildFlatMetadataCache, HashEclassBadMD5)
+{
+ TestEnvironment env;
+ std::shared_ptr<Map<std::string, std::string> > keys(std::make_shared<Map<std::string, std::string>>());
+ keys->insert("format", "e");
+ keys->insert("names_cache", "/var/empty");
+ keys->insert("location", stringify(FSPath::cwd() / "ebuild_flat_metadata_cache_TEST_dir/repo"));
+ keys->insert("profiles", stringify(FSPath::cwd() / "ebuild_flat_metadata_cache_TEST_dir/repo/profiles/profile"));
+ keys->insert("eclassdirs", "ebuild_flat_metadata_cache_TEST_dir/repo/eclass ebuild_flat_metadata_cache_TEST_dir/extra_eclasses");
+ keys->insert("builddir", stringify(FSPath::cwd() / "ebuild_flat_metadata_cache_TEST_dir" / "build"));
+ std::shared_ptr<Repository> repo(ERepository::repository_factory_create(&env,
+ std::bind(from_keys, keys, std::placeholders::_1)));
+ env.add_repository(1, repo);
+
+ std::shared_ptr<const PackageID> id(*env[selection::RequireExactlyOne(generator::Matches(
+ PackageDepSpec(parse_user_package_dep_spec("=cat/flat_hash-eclass-bad-md5-1",
+ &env, { })), make_null_shared_ptr(), { }))]->begin());
+
+ ASSERT_TRUE(bool(id->short_description_key()));
+ EXPECT_EQ("The Generated Description flat_hash-eclass-bad-md5", id->short_description_key()->parse_value());
+}
+
+TEST(EbuildFlatMetadataCache, HashEclassTruncatedMD5)
+{
+ TestEnvironment env;
+ std::shared_ptr<Map<std::string, std::string> > keys(std::make_shared<Map<std::string, std::string>>());
+ keys->insert("format", "e");
+ keys->insert("names_cache", "/var/empty");
+ keys->insert("location", stringify(FSPath::cwd() / "ebuild_flat_metadata_cache_TEST_dir/repo"));
+ keys->insert("profiles", stringify(FSPath::cwd() / "ebuild_flat_metadata_cache_TEST_dir/repo/profiles/profile"));
+ keys->insert("eclassdirs", "ebuild_flat_metadata_cache_TEST_dir/repo/eclass ebuild_flat_metadata_cache_TEST_dir/extra_eclasses");
+ keys->insert("builddir", stringify(FSPath::cwd() / "ebuild_flat_metadata_cache_TEST_dir" / "build"));
+ std::shared_ptr<Repository> repo(ERepository::repository_factory_create(&env,
+ std::bind(from_keys, keys, std::placeholders::_1)));
+ env.add_repository(1, repo);
+
+ std::shared_ptr<const PackageID> id(*env[selection::RequireExactlyOne(generator::Matches(
+ PackageDepSpec(parse_user_package_dep_spec("=cat/flat_hash-eclass-truncated-md5-1",
+ &env, { })), make_null_shared_ptr(), { }))]->begin());
+
+ ASSERT_TRUE(bool(id->short_description_key()));
+ EXPECT_EQ("The Generated Description flat_hash-eclass-truncated-md5", id->short_description_key()->parse_value());
+}
+
+TEST(EbuildFlatMetadataCache, HashEclassGoneMD5)
+{
+ TestEnvironment env;
+ std::shared_ptr<Map<std::string, std::string> > keys(std::make_shared<Map<std::string, std::string>>());
+ keys->insert("format", "e");
+ keys->insert("names_cache", "/var/empty");
+ keys->insert("location", stringify(FSPath::cwd() / "ebuild_flat_metadata_cache_TEST_dir/repo"));
+ keys->insert("profiles", stringify(FSPath::cwd() / "ebuild_flat_metadata_cache_TEST_dir/repo/profiles/profile"));
+ keys->insert("eclassdirs", "ebuild_flat_metadata_cache_TEST_dir/repo/eclass ebuild_flat_metadata_cache_TEST_dir/extra_eclasses");
+ keys->insert("builddir", stringify(FSPath::cwd() / "ebuild_flat_metadata_cache_TEST_dir" / "build"));
+ std::shared_ptr<Repository> repo(ERepository::repository_factory_create(&env,
+ std::bind(from_keys, keys, std::placeholders::_1)));
+ env.add_repository(1, repo);
+
+ std::shared_ptr<const PackageID> id(*env[selection::RequireExactlyOne(generator::Matches(
+ PackageDepSpec(parse_user_package_dep_spec("=cat/flat_hash-eclass-gone-md5-1",
+ &env, { })), make_null_shared_ptr(), { }))]->begin());
+
+ ASSERT_TRUE(bool(id->short_description_key()));
+ EXPECT_EQ("The Generated Description flat_hash-eclass-gone-md5", id->short_description_key()->parse_value());
+}
+
TEST(EbuildFlatMetadataCache, HashExlib)
{
TestEnvironment env;
diff --git a/paludis/repositories/e/ebuild_flat_metadata_cache_TEST_setup.sh b/paludis/repositories/e/ebuild_flat_metadata_cache_TEST_setup.sh
index e8a61f0..ee90764 100755
--- a/paludis/repositories/e/ebuild_flat_metadata_cache_TEST_setup.sh
+++ b/paludis/repositories/e/ebuild_flat_metadata_cache_TEST_setup.sh
@@ -477,6 +477,57 @@ END
TZ=UTC touch -t 197001010001 cat/flat_hash-bad-mtime/flat_hash-bad-mtime-1 || exit 2
TZ=UTC touch -t 197001010001 metadata/cache/cat/flat_hash-bad-mtime-1 || exit 2
+mkdir cat/flat_hash-md5
+cat <<END > cat/flat_hash-md5/flat_hash-md5-1.ebuild || exit 1
+END
+cat <<END > metadata/cache/cat/flat_hash-md5-1 || exit 1
+_md5_=d41d8cd98f00b204e9800998ecf8427e
+_guessed_eapi_=0
+DEPEND=the/depend
+RDEPEND=the/rdepend
+SLOT=the-slot
+SRC_URI=the-src-uri
+RESTRICT=the-restrict
+HOMEPAGE=the-homepage
+LICENSE=the-license
+DESCRIPTION=The Description flat_hash-md5
+KEYWORDS=the-keywords
+IUSE=the-iuse
+PDEPEND=the/pdepend
+EAPI=0
+END
+TZ=UTC touch -t 197001010001 metadata/cache/cat/flat_hash-md5-1 || exit 2
+
+mkdir cat/flat_hash-bad-md5
+cat <<END > cat/flat_hash-bad-md5/flat_hash-bad-md5-1.ebuild || exit 1
+DESCRIPTION="The Generated Description flat_hash-bad-md5"
+HOMEPAGE="http://example.com/"
+SRC_URI=""
+SLOT="0"
+IUSE=""
+LICENSE="GPL-2"
+KEYWORDS="test"
+DEPEND=""
+END
+cat <<END > metadata/cache/cat/flat_hash-bad-md5-1 || exit 1
+_md5_=d41d8cd98f00b204e9800998ecf8427e
+_guessed_eapi_=0
+DEPEND=the/depend
+RDEPEND=the/rdepend
+SLOT=the-slot
+SRC_URI=the-src-uri
+RESTRICT=the-restrict
+HOMEPAGE=the-homepage
+LICENSE=the-license
+DESCRIPTION=The Stale Description
+KEYWORDS=the-keywords
+IUSE=the-iuse
+PDEPEND=the/pdepend
+EAPI=0
+END
+TZ=UTC touch -t 197001010001 cat/flat_hash-bad-md5/flat_hash-bad-md5-1.ebuild || exit 2
+TZ=UTC touch -t 197001010001 metadata/cache/cat/flat_hash-bad-md5-1 || exit 2
+
mkdir cat/flat_hash-no-eapi
cat <<END > cat/flat_hash-no-eapi/flat_hash-no-eapi-1.ebuild || exit 1
DESCRIPTION="The Generated Description flat_hash-no-eapi"
@@ -913,6 +964,121 @@ PROPERTIES=the-properties
END
TZ=UTC touch -t 197001010001 cat/flat_hash-eclasses-spaces/flat_hash-eclasses-spaces-1.ebuild || exit 2
+mkdir cat/flat_hash-eclass-md5
+cat <<END > cat/flat_hash-eclass-md5/flat_hash-eclass-md5-1.ebuild || exit 1
+END
+cat <<END > metadata/cache/cat/flat_hash-eclass-md5-1 || exit 1
+_md5_=d41d8cd98f00b204e9800998ecf8427e
+_guessed_eapi_=0
+_eclasses_=foo 0a4d9ad1a63a829bc31879a68024ef78 bar d41d8cd98f00b204e9800998ecf8427e
+DEPEND=the/depend
+RDEPEND=the/rdepend
+SLOT=the-slot
+SRC_URI=the-src-uri
+RESTRICT=the-restrict
+HOMEPAGE=the-homepage
+LICENSE=the-license
+DESCRIPTION=The Description flat_hash-eclass-md5
+KEYWORDS=the-keywords
+IUSE=the-iuse
+PDEPEND=the/pdepend
+EAPI=0
+END
+TZ=UTC touch -t 197001010001 metadata/cache/cat/flat_hash-eclass-md5-1 || exit 2
+
+mkdir cat/flat_hash-eclass-bad-md5
+cat <<END > cat/flat_hash-eclass-bad-md5/flat_hash-eclass-bad-md5-1.ebuild || exit 1
+DESCRIPTION="The Generated Description flat_hash-eclass-bad-md5"
+HOMEPAGE="http://example.com/"
+SRC_URI=""
+SLOT="0"
+IUSE=""
+LICENSE="GPL-2"
+KEYWORDS="test"
+DEPEND=""
+END
+cat <<END > metadata/cache/cat/flat_hash-eclass-bad-md5-1 || exit 1
+_md5_=d3472291afcdc827fa4bff7215be1bfa
+_guessed_eapi_=0
+_eclasses_=foo 0a4d9ad1a63a829bc31879a68024ef79 bar d41d8cd98f00b204e9800998ecf8427e
+DEPEND=the/depend
+RDEPEND=the/rdepend
+SLOT=the-slot
+SRC_URI=the-src-uri
+RESTRICT=the-restrict
+HOMEPAGE=the-homepage
+LICENSE=the-license
+DESCRIPTION=The Stale Description
+KEYWORDS=the-keywords
+IUSE=the-iuse
+PDEPEND=the/pdepend
+EAPI=0
+END
+TZ=UTC touch -t 197001010001 cat/flat_hash-eclass-bad-md5/flat_hash-eclass-bad-md5-1.ebuild || exit 2
+TZ=UTC touch -t 197001010001 metadata/cache/cat/flat_hash-eclass-bad-md5-1 || exit 2
+
+mkdir cat/flat_hash-eclass-truncated-md5
+cat <<END > cat/flat_hash-eclass-truncated-md5/flat_hash-eclass-truncated-md5-1.ebuild || exit 1
+DESCRIPTION="The Generated Description flat_hash-eclass-truncated-md5"
+HOMEPAGE="http://example.com/"
+SRC_URI=""
+SLOT="0"
+IUSE=""
+LICENSE="GPL-2"
+KEYWORDS="test"
+DEPEND=""
+END
+cat <<END > metadata/cache/cat/flat_hash-eclass-truncated-md5-1 || exit 1
+_md5_=088607e2812c483ecc9a16b69fd1b71f
+_guessed_eapi_=0
+_eclasses_=foo 0a4d9ad1a63a829bc31879a68024ef78 bar
+DEPEND=the/depend
+RDEPEND=the/rdepend
+SLOT=the-slot
+SRC_URI=the-src-uri
+RESTRICT=the-restrict
+HOMEPAGE=the-homepage
+LICENSE=the-license
+DESCRIPTION=The Stale Description
+KEYWORDS=the-keywords
+IUSE=the-iuse
+PDEPEND=the/pdepend
+EAPI=0
+END
+TZ=UTC touch -t 197001010001 cat/flat_hash-eclass-truncated-md5/flat_hash-eclass-truncated-md5-1.ebuild || exit 2
+TZ=UTC touch -t 197001010001 metadata/cache/cat/flat_hash-eclass-truncated-md5-1 || exit 2
+
+mkdir cat/flat_hash-eclass-gone-md5
+cat <<END > cat/flat_hash-eclass-gone-md5/flat_hash-eclass-gone-md5-1.ebuild || exit 1
+DESCRIPTION="The Generated Description flat_hash-eclass-gone-md5"
+HOMEPAGE="http://example.com/"
+SRC_URI=""
+SLOT="0"
+IUSE=""
+LICENSE="GPL-2"
+KEYWORDS="test"
+DEPEND=""
+END
+cat <<END > metadata/cache/cat/flat_hash-eclass-gone-md5-1 || exit 1
+_md5_=7a0546fdebe4320feb9008ea76ca3ace
+_guessed_eapi_=0
+_eclasses_=foo 0a4d9ad1a63a829bc31879a68024ef78 bat d41d8cd98f00b204e9800998ecf8427e
+DEPEND=the/depend
+RDEPEND=the/rdepend
+SLOT=the-slot
+SRC_URI=the-src-uri
+RESTRICT=the-restrict
+HOMEPAGE=the-homepage
+LICENSE=the-license
+DESCRIPTION=The Stale Description
+KEYWORDS=the-keywords
+IUSE=the-iuse
+PDEPEND=the/pdepend
+EAPI=0
+END
+TZ=UTC touch -t 197001010001 cat/flat_hash-eclass-gone-md5/flat_hash-eclass-gone-md5-1.ebuild || exit 2
+TZ=UTC touch -t 197001010001 metadata/cache/cat/flat_hash-eclass-gone-md5-1 || exit 2
+
mkdir cat/flat_hash-exlib
cat <<END > cat/flat_hash-exlib/flat_hash-exlib-1.ebuild || exit 1
END