diff options
author | 2012-03-29 19:38:24 +0100 | |
---|---|---|
committer | 2012-03-30 18:30:59 +0100 | |
commit | ad3a7401391d321ae4664a6e8e630ee3611fd512 (patch) | |
tree | 618d1470328cda05aea845862b4c4d18626aa7d2 | |
parent | 28da33d1363d6b7ff1298da7eccd5f387450fbc3 (diff) | |
download | paludis-ad3a7401391d321ae4664a6e8e630ee3611fd512.tar.gz paludis-ad3a7401391d321ae4664a6e8e630ee3611fd512.tar.xz |
Support for verifying MD5-based flat_hash caches
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 19204ad4c..5096454ca 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 13a2a2bd5..c825469a9 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 e8a61f02e..ee90764f8 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 |