Exheredludis/paludis/repositories/e/vdb_merger.cc
Saleem Abdulrasool 64ba7d5be8 modernize: use default method synthesis
Convert a number of destructors to default synthesized functions.  Try to
inline a few instances into the header.  It should be possible to inline all of
them, however, gcc seems to emit a number of warnings.  Furthermore, some of the
destructors are pure-virtualed, but provide an implementation.  Placing the
definition into the header causes ODR violations.
2016-08-06 11:58:04 -07:00

350 lines
12 KiB
C++

/* vim: set sw=4 sts=4 et foldmethod=syntax : */
/*
* Copyright (c) 2007, 2008, 2009, 2010, 2011, 2013 Ciaran McCreesh
*
* This file is part of the Paludis package manager. Paludis is free software;
* you can redistribute it and/or modify it under the terms of the GNU General
* Public License version 2, as published by the Free Software Foundation.
*
* Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <paludis/repositories/e/vdb_merger.hh>
#include <paludis/util/log.hh>
#include <paludis/util/tokeniser.hh>
#include <paludis/util/system.hh>
#include <paludis/util/wrapped_forward_iterator.hh>
#include <paludis/util/join.hh>
#include <paludis/util/pimp-impl.hh>
#include <paludis/util/sequence.hh>
#include <paludis/util/fs_stat.hh>
#include <paludis/util/strip.hh>
#include <paludis/util/options.hh>
#include <paludis/util/make_named_values.hh>
#include <paludis/util/enum_iterator.hh>
#include <paludis/util/timestamp.hh>
#include <paludis/util/env_var_names.hh>
#include <paludis/util/safe_ofstream.hh>
#include <paludis/util/safe_ifstream.hh>
#include <paludis/util/env_var_names.hh>
#include <paludis/util/md5.hh>
#include <paludis/output_manager.hh>
#include <paludis/hook.hh>
#include <paludis/package_id.hh>
#include <paludis/environment.hh>
#include <paludis/metadata_key.hh>
#include <paludis/version_spec.hh>
#include <paludis/slot.hh>
#include <iomanip>
#include <list>
using namespace paludis;
namespace paludis
{
template<>
struct Imp<VDBMerger>
{
VDBMergerParams params;
FSPath realroot;
std::shared_ptr<SafeOFStream> contents_file;
std::list<std::string> config_protect;
std::list<std::string> config_protect_mask;
Imp(const VDBMergerParams & p) :
params(p),
realroot(params.root().realpath())
{
tokenise_whitespace(params.config_protect(), std::back_inserter(config_protect));
tokenise_whitespace(params.config_protect_mask(), std::back_inserter(config_protect_mask));
}
};
}
namespace
{
std::pair<uid_t, gid_t>
get_new_ids_or_minus_one(const Environment * const env, const FSPath & f)
{
FSStat f_stat(f.stat());
uid_t uid = (f_stat.owner() == env->reduced_uid()) ? 0 : -1;
gid_t gid = (f_stat.group() == env->reduced_gid()) ? 0 : -1;
return std::make_pair(uid, gid);
}
}
VDBMerger::VDBMerger(const VDBMergerParams & p) :
FSMerger(make_named_values<FSMergerParams>(
n::environment() = p.environment(),
n::fix_mtimes_before() = p.fix_mtimes_before(),
n::fs_merger_options() = p.fs_merger_options(),
n::get_new_ids_or_minus_one() = std::bind(&get_new_ids_or_minus_one, p.environment(), std::placeholders::_1),
n::image() = p.image(),
n::install_under() = FSPath("/"),
n::maybe_output_manager() = p.output_manager(),
n::merged_entries() = p.merged_entries(),
n::no_chown() = ! getenv_with_default(env_vars::no_chown, "").empty(),
n::options() = p.options(),
n::parts() = nullptr,
n::permit_destination() = p.permit_destination(),
n::root() = p.root(),
n::should_merge() = nullptr
)),
_imp(p)
{
}
VDBMerger::~VDBMerger() = default;
Hook
VDBMerger::extend_hook(const Hook & h)
{
std::shared_ptr<const FSPathSequence> bashrc_files(_imp->params.environment()->bashrc_files());
if (_imp->params.package_id())
{
std::string cat(stringify(_imp->params.package_id()->name().category()));
std::string pn(stringify(_imp->params.package_id()->name().package()));
std::string pvr(stringify(_imp->params.package_id()->version()));
std::string pv(stringify(_imp->params.package_id()->version().remove_revision()));
std::string slot(_imp->params.package_id()->slot_key() ? stringify(_imp->params.package_id()->slot_key()->parse_value().raw_value()) : "");
return FSMerger::extend_hook(h)
("P", pn + "-" + pv)
("PNV", pn + "-" + pv)
("PN", pn)
("CATEGORY", cat)
("PR", _imp->params.package_id()->version().revision_only())
("PV", pv)
("PVR", pvr)
("PF", pn + "-" + pvr)
("PNVR", pn + "-" + pvr)
("SLOT", slot)
("CONFIG_PROTECT", _imp->params.config_protect())
("CONFIG_PROTECT_MASK", _imp->params.config_protect_mask())
("PALUDIS_BASHRC_FILES", join(bashrc_files->begin(), bashrc_files->end(), " "));
}
else
return FSMerger::extend_hook(h)
("CONFIG_PROTECT", _imp->params.config_protect())
("CONFIG_PROTECT_MASK", _imp->params.config_protect_mask())
("PALUDIS_BASHRC_FILES", join(bashrc_files->begin(), bashrc_files->end(), " "));
}
void
VDBMerger::record_install_file(const FSPath & src, const FSPath & dst_dir, const std::string & dst_name, const FSMergerStatusFlags & flags)
{
const auto file(dst_dir / src.basename());
const auto renamed_file(dst_dir / dst_name);
const std::string tidy(stringify(renamed_file.strip_leading(_imp->realroot))),
tidy_real(stringify(file.strip_leading(_imp->realroot)));
const Timestamp timestamp(renamed_file.stat().mtim());
SafeIFStream infile(renamed_file);
if (! infile)
throw FSMergerError("Cannot read '" + stringify(renamed_file) + "'");
MD5 md5(infile);
display_merge(et_file, renamed_file, flags,
src.basename() == dst_name ? "" : dst_name);
*_imp->contents_file << "obj " << tidy_real << " " << md5.hexsum() << " " << timestamp.seconds() << std::endl;
}
void
VDBMerger::record_install_dir(const FSPath & src, const FSPath & dst_dir, const FSMergerStatusFlags & flags)
{
const auto dir(dst_dir / src.basename());
const std::string tidy(stringify(dir.strip_leading(_imp->realroot)));
display_merge(et_dir, dir, flags);
*_imp->contents_file << "dir " << tidy << std::endl;
}
void
VDBMerger::record_install_under_dir(const FSPath & dst_dir, const FSMergerStatusFlags & flags)
{
const std::string tidy(stringify(dst_dir.strip_leading(_imp->realroot)));
display_merge(et_dir, dst_dir, flags);
*_imp->contents_file << "dir " << tidy << std::endl;
}
void
VDBMerger::record_install_sym(const FSPath & src, const FSPath & dst_dir, const FSMergerStatusFlags & flags)
{
const auto sym(dst_dir / src.basename());
const std::string tidy(stringify(sym.strip_leading(_imp->realroot)));
const std::string target(sym.readlink());
const Timestamp timestamp(sym.stat().mtim());
display_merge(et_sym, sym, flags);
*_imp->contents_file << "sym " << tidy << " -> " << target << " " << timestamp.seconds() << std::endl;
}
void
VDBMerger::on_error(bool is_check, const std::string & s)
{
make_check_fail();
if (is_check)
_imp->params.output_manager()->stdout_stream() << "." << std::endl << "!!! " << s << std::endl;
else
throw FSMergerError(s);
}
void
VDBMerger::on_warn(bool is_check, const std::string & s)
{
if (is_check)
Log::get_instance()->message("e.vdb_merger.warning", ll_warning, lc_context) << s;
}
bool
VDBMerger::config_protected(const FSPath & src, const FSPath & dst_dir)
{
std::string tidy(stringify((dst_dir / src.basename()).strip_leading(_imp->realroot)));
bool result(false);
for (std::list<std::string>::const_iterator c(_imp->config_protect.begin()),
c_end(_imp->config_protect.end()) ; c != c_end && ! result ; ++c)
{
std::string cc(strip_trailing(*c, "/") + "/");
if (tidy == *c || 0 == tidy.compare(0, cc.length(), cc))
result = true;
}
if (result)
for (std::list<std::string>::const_iterator c(_imp->config_protect_mask.begin()),
c_end(_imp->config_protect_mask.end()) ; c != c_end && result ; ++c)
{
std::string cc(strip_trailing(*c, "/") + "/");
if (tidy == *c || 0 == tidy.compare(0, cc.length(), cc))
result = false;
}
return result;
}
std::string
VDBMerger::make_config_protect_name(const FSPath & src, const FSPath & dst)
{
std::string result_name(src.basename());
int n(0);
SafeIFStream our_md5_file(src);
if (! our_md5_file)
throw FSMergerError("Could not get md5 for '" + stringify((dst / src.basename()).strip_leading(_imp->realroot)) + "'");
MD5 our_md5(our_md5_file);
while (true)
{
FSPath dst_result_name(dst / result_name);
FSStat dst_result_name_stat(dst_result_name);
if (! dst_result_name_stat.exists())
break;
if (dst_result_name_stat.is_regular_file_or_symlink_to_regular_file())
{
try
{
SafeIFStream other_md5_file(dst / result_name);
MD5 other_md5(other_md5_file);
if (our_md5.hexsum() == other_md5.hexsum())
break;
}
catch (const SafeIFStreamError &)
{
}
}
std::stringstream s;
s << std::setw(4) << std::setfill('0') << std::right << n++;
result_name = "._cfg" + s.str() + "_" + src.basename();
}
return result_name;
}
void
VDBMerger::merge()
{
display_override(">>> Merging to " + stringify(_imp->params.root()));
_imp->contents_file = std::make_shared<SafeOFStream>(_imp->params.contents_file(), -1, false);
FSMerger::merge();
}
bool
VDBMerger::check()
{
_imp->params.output_manager()->stdout_stream() << ">>> Checking whether we can merge to " << _imp->params.root() << " ";
bool result(FSMerger::check());
_imp->params.output_manager()->stdout_stream() << std::endl;
return result;
}
void
VDBMerger::on_enter_dir(bool is_check, const FSPath)
{
if (! is_check)
return;
_imp->params.output_manager()->stdout_stream() << "." << std::flush;
}
void
VDBMerger::on_file(bool is_check, const FSPath & src, const FSPath & dst)
{
if (is_check && std::string::npos != src.basename().find('\n'))
throw FSMergerError("File '" + stringify(src) + "' contains a newline in its name, which cannot be stored by VDB");
FSMerger::on_file(is_check, src, dst);
}
void
VDBMerger::on_dir(bool is_check, const FSPath & src, const FSPath & dst)
{
if (is_check && std::string::npos != src.basename().find('\n'))
throw FSMergerError("Directory '" + stringify(src) + "' contains a newline in its name, which cannot be stored by VDB");
FSMerger::on_dir(is_check, src, dst);
}
void
VDBMerger::on_sym(bool is_check, const FSPath & src, const FSPath & dst)
{
if (is_check)
{
if (std::string::npos != src.basename().find('\n'))
throw FSMergerError("Symlink '" + stringify(src) + "' contains a newline in its name, which cannot be stored by VDB");
if (std::string::npos != src.readlink().find('\n'))
throw FSMergerError("Symlink '" + stringify(src) + "' contains a newline in its target '" +
src.readlink() + "', which cannot be stored by VDB");
if (std::string::npos != stringify(src).find(" -> "))
throw FSMergerError("Symlink '" + stringify(src) + "' contains a ' -> ' in its name, which cannot be stored by VDB");
}
FSMerger::on_sym(is_check, src, dst);
}
void
VDBMerger::display_override(const std::string & message) const
{
_imp->params.output_manager()->stdout_stream() << message << std::endl;
}