aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2010-07-28 15:38:59 +0100
committerAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2010-07-28 15:38:59 +0100
commit416ca35e601c5a3995e3982803400bcb65d4fc9d (patch)
tree66d7f18f8c14e66f7c5b2c444146b2276a5ff5c7
parentef952e15c6447acfeff93c88809f7c10355818d0 (diff)
downloadpaludis-416ca35e601c5a3995e3982803400bcb65d4fc9d.tar.gz
paludis-416ca35e601c5a3995e3982803400bcb65d4fc9d.tar.xz
Move some code into Merger
-rw-r--r--paludis/fs_merger.cc492
-rw-r--r--paludis/fs_merger.hh59
-rw-r--r--paludis/merger.cc314
-rw-r--r--paludis/merger.hh101
4 files changed, 540 insertions, 426 deletions
diff --git a/paludis/fs_merger.cc b/paludis/fs_merger.cc
index f3cde6c..1f98456 100644
--- a/paludis/fs_merger.cc
+++ b/paludis/fs_merger.cc
@@ -28,6 +28,7 @@
#include <paludis/util/pimp-impl.hh>
#include <paludis/util/set.hh>
#include <paludis/util/timestamp.hh>
+#include <paludis/util/make_named_values.hh>
#include <paludis/selinux/security_context.hh>
#include <paludis/environment.hh>
#include <paludis/hook.hh>
@@ -61,25 +62,30 @@ namespace paludis
std::set<FSEntry> fixed_entries;
MergedMap merged_ids;
FSMergerParams params;
- bool result;
- bool skip_dir;
Imp(const FSMergerParams & p) :
- params(p),
- result(true),
- skip_dir(false)
+ params(p)
{
}
};
}
FSMergerError::FSMergerError(const std::string & s) throw () :
- Exception(s)
+ MergerError(s)
{
}
FSMerger::FSMerger(const FSMergerParams & p) :
- Pimp<FSMerger>(p)
+ Pimp<FSMerger>(p),
+ Merger(make_named_values<MergerParams>(
+ n::environment() = p.environment(),
+ n::image() = p.image(),
+ n::install_under() = p.install_under(),
+ n::merged_entries() = p.merged_entries(),
+ n::options() = p.options(),
+ n::root() = p.root()
+ )),
+ _imp(Pimp<FSMerger>::_imp)
{
}
@@ -87,35 +93,6 @@ FSMerger::~FSMerger()
{
}
-bool
-FSMerger::check()
-{
- Context context("When checking merge from '" + stringify(_imp->params.image()) + "' to '"
- + stringify(_imp->params.root()) + "':");
-
- if (0 != _imp->params.environment()->perform_hook(extend_hook(
- Hook("merger_check_pre")
- ("INSTALL_SOURCE", stringify(_imp->params.image()))
- ("INSTALL_DESTINATION", stringify(_imp->params.root())))).max_exit_status())
- make_check_fail();
-
- do_dir_recursive(true, _imp->params.image(), _imp->params.root() / _imp->params.install_under());
-
- if (0 != _imp->params.environment()->perform_hook(extend_hook(
- Hook("merger_check_post")
- ("INSTALL_SOURCE", stringify(_imp->params.image()))
- ("INSTALL_DESTINATION", stringify(_imp->params.root())))).max_exit_status())
- make_check_fail();
-
- return _imp->result;
-}
-
-void
-FSMerger::make_check_fail()
-{
- _imp->result = false;
-}
-
void
FSMerger::merge()
{
@@ -176,332 +153,6 @@ FSMerger::merge()
"Merge of '" << _imp->params.image() << "' to '" << _imp->params.root() << "' post hooks returned non-zero";
}
-EntryType
-FSMerger::entry_type(const FSEntry & f)
-{
- Context context("When checking type of '" + stringify(f) + "':");
-
- if (! f.exists())
- return et_nothing;
-
- if (f.is_symbolic_link())
- return et_sym;
-
- if (f.is_regular_file())
- return et_file;
-
- if (f.is_directory())
- return et_dir;
-
- return et_misc;
-}
-
-void
-FSMerger::do_dir_recursive(bool is_check, const FSEntry & src, const FSEntry & dst)
-{
- Context context("When " + stringify(is_check ? "checking" : "performing") + " merge from '" +
- stringify(src) + "' to '" + stringify(dst) + "':");
-
- if (! src.is_directory())
- throw FSMergerError("Source directory '" + stringify(src) + "' is not a directory");
- if ((! is_check) && (! dst.is_directory()))
- throw FSMergerError("Destination directory '" + stringify(dst) + "' is not a directory");
-
- on_enter_dir(is_check, src);
-
- DirIterator d(src, { dio_include_dotfiles, dio_inode_sort }), d_end;
-
- if (is_check && d == d_end && dst != _imp->params.root().realpath())
- {
- if (_imp->params.options()[mo_allow_empty_dirs])
- Log::get_instance()->message("merger.empty_directory", ll_warning, lc_context) << "Installing empty directory '"
- << stringify(dst) << "'";
- else
- on_error(is_check, "Attempted to install empty directory '" + stringify(dst) + "'");
- }
-
- for ( ; d != d_end ; ++d)
- {
- EntryType m(entry_type(*d));
- switch (m)
- {
- case et_sym:
- on_sym(is_check, *d, dst);
- continue;
-
- case et_file:
- on_file(is_check, *d, dst);
- continue;
-
- case et_dir:
- on_dir(is_check, *d, dst);
- if (_imp->result)
- {
- if (! _imp->skip_dir)
- do_dir_recursive(is_check, *d,
- is_check ? (dst / d->basename()) : (dst / d->basename()).realpath());
- else
- _imp->skip_dir = false;
- }
- continue;
-
- case et_misc:
- on_misc(is_check, *d, dst);
- continue;
-
- case et_nothing:
- case last_et:
- ;
- }
-
- throw InternalError(PALUDIS_HERE, "Unexpected entry_type '" + stringify(m) + "'");
- }
-
- on_leave_dir(is_check, src);
-}
-
-void
-FSMerger::on_file(bool is_check, const FSEntry & src, const FSEntry & dst)
-{
- Context context("When handling file '" + stringify(src) + "' to '" + stringify(dst) + "':");
-
- EntryType m(entry_type(dst / src.basename()));
-
- if (is_check &&
- 0 != _imp->params.environment()->perform_hook(extend_hook(
- Hook("merger_check_file_pre")
- ("INSTALL_SOURCE", stringify(src))
- ("INSTALL_DESTINATION", stringify(dst / src.basename())))).max_exit_status())
- make_check_fail();
-
- if (! is_check)
- {
- HookResult hr(_imp->params.environment()->perform_hook(extend_hook(
- Hook("merger_install_file_override")
- ("INSTALL_SOURCE", stringify(src))
- ("INSTALL_DESTINATION", stringify(dst / src.basename()))
- .grab_output(Hook::AllowedOutputValues()("skip")))));
-
- if (hr.max_exit_status() != 0)
- Log::get_instance()->message("merger.file.skip_hooks.failure", ll_warning, lc_context) << "Merge of '"
- << stringify(src) << "' to '" << stringify(dst) << "' skip hooks returned non-zero";
- else if (hr.output() == "skip")
- {
- std::string tidy(stringify((dst / src.basename()).strip_leading(_imp->params.root().realpath())));
- display_override("--- [skp] " + tidy);
- return;
- }
- }
-
- do
- {
- switch (m)
- {
- case et_nothing:
- on_file_over_nothing(is_check, src, dst);
- continue;
-
- case et_sym:
- on_file_over_sym(is_check, src, dst);
- continue;
-
- case et_dir:
- on_file_over_dir(is_check, src, dst);
- continue;
-
- case et_misc:
- on_file_over_misc(is_check, src, dst);
- continue;
-
- case et_file:
- on_file_over_file(is_check, src, dst);
- continue;
-
- case last_et:
- ;
- }
-
- throw InternalError(PALUDIS_HERE, "Unexpected entry_type '" + stringify(m) + "'");
- } while (false);
-
- if (is_check &&
- 0 != _imp->params.environment()->perform_hook(extend_hook(
- Hook("merger_check_file_post")
- ("INSTALL_SOURCE", stringify(src))
- ("INSTALL_DESTINATION", stringify(dst / src.basename())))).max_exit_status())
- make_check_fail();
-}
-
-void
-FSMerger::on_dir(bool is_check, const FSEntry & src, const FSEntry & dst)
-{
- Context context("When handling dir '" + stringify(src) + "' to '" + stringify(dst) + "':");
-
- EntryType m(entry_type(dst / src.basename()));
-
- if (is_check &&
- 0 != _imp->params.environment()->perform_hook(extend_hook(
- Hook("merger_check_dir_pre")
- ("INSTALL_SOURCE", stringify(src))
- ("INSTALL_DESTINATION", stringify(dst / src.basename())))).max_exit_status())
- make_check_fail();
-
- if (! is_check)
- {
- HookResult hr(_imp->params.environment()->perform_hook(extend_hook(
- Hook("merger_install_dir_override")
- ("INSTALL_SOURCE", stringify(src))
- ("INSTALL_DESTINATION", stringify(dst / src.basename()))
- .grab_output(Hook::AllowedOutputValues()("skip")))));
-
- if (hr.max_exit_status() != 0)
- Log::get_instance()->message("merger.dir.skip_hooks.failure", ll_warning, lc_context) << "Merge of '"
- << stringify(src) << "' to '" << stringify(dst) << "' skip hooks returned non-zero";
- else if (hr.output() == "skip")
- {
- std::string tidy(stringify((dst / src.basename()).strip_leading(_imp->params.root().realpath())));
- display_override("--- [skp] " + tidy);
- _imp->skip_dir = true;
- return;
- }
- }
-
- do
- {
- switch (m)
- {
- case et_nothing:
- on_dir_over_nothing(is_check, src, dst);
- continue;
-
- case et_sym:
- on_dir_over_sym(is_check, src, dst);
- continue;
-
- case et_dir:
- on_dir_over_dir(is_check, src, dst);
- continue;
-
- case et_misc:
- on_dir_over_misc(is_check, src, dst);
- continue;
-
- case et_file:
- on_dir_over_file(is_check, src, dst);
- continue;
-
- case last_et:
- ;
- }
-
- throw InternalError(PALUDIS_HERE, "Unexpected entry_type '" + stringify(m) + "'");
-
- } while (false);
-
- if (is_check &&
- 0 != _imp->params.environment()->perform_hook(extend_hook(
- Hook("merger_check_dir_post")
- ("INSTALL_SOURCE", stringify(src))
- ("INSTALL_DESTINATION", stringify(dst / src.basename())))).max_exit_status())
- make_check_fail();
-}
-
-void
-FSMerger::on_sym(bool is_check, const FSEntry & src, const FSEntry & dst)
-{
- Context context("When handling sym '" + stringify(src) + "' to '" + stringify(dst) + "':");
-
- EntryType m(entry_type(dst / src.basename()));
-
- if (is_check &&
- 0 != _imp->params.environment()->perform_hook(extend_hook(
- Hook("merger_check_sym_post")
- ("INSTALL_SOURCE", stringify(src))
- ("INSTALL_DESTINATION", stringify(dst / src.basename())))).max_exit_status())
- make_check_fail();
-
- if (! is_check)
- {
- HookResult hr(_imp->params.environment()->perform_hook(extend_hook(
- Hook("merger_install_sym_override")
- ("INSTALL_SOURCE", stringify(src))
- ("INSTALL_DESTINATION", stringify(dst / src.basename()))
- .grab_output(Hook::AllowedOutputValues()("skip")))));
-
- if (hr.max_exit_status() != 0)
- Log::get_instance()->message("merger.sym.skip_hooks.failure", ll_warning, lc_context) << "Merge of '"
- << stringify(src) << "' to '" << stringify(dst) << "' skip hooks returned non-zero";
- else if (hr.output() == "skip")
- {
- std::string tidy(stringify((dst / src.basename()).strip_leading(_imp->params.root().realpath())));
- display_override("--- [skp] " + tidy);
- return;
- }
- }
- else
- {
- if (symlink_needs_rewriting(src) && ! _imp->params.options()[mo_rewrite_symlinks])
- on_error(is_check, "Symlink to image detected at: " + stringify(src) + " (" + src.readlink() + ")");
- }
-
- do
- {
- switch (m)
- {
- case et_nothing:
- on_sym_over_nothing(is_check, src, dst);
- continue;
-
- case et_sym:
- on_sym_over_sym(is_check, src, dst);
- continue;
-
- case et_dir:
- on_sym_over_dir(is_check, src, dst);
- continue;
-
- case et_misc:
- on_sym_over_misc(is_check, src, dst);
- continue;
-
- case et_file:
- on_sym_over_file(is_check, src, dst);
- continue;
-
- case last_et:
- ;
- }
-
- throw InternalError(PALUDIS_HERE, "Unexpected entry_type '" + stringify(m) + "'");
- } while (false);
-
- if (is_check &&
- 0 != _imp->params.environment()->perform_hook(extend_hook(
- Hook("merger_check_sym_post")
- ("INSTALL_SOURCE", stringify(src))
- ("INSTALL_DESTINATION", stringify(dst / src.basename())))).max_exit_status())
- make_check_fail();
-}
-
-void
-FSMerger::on_misc(bool is_check, const FSEntry & src, const FSEntry & dst)
-{
- Context context("When handling misc '" + stringify(src) + "' to '" + stringify(dst) + "':");
-
- on_error(is_check, "Cannot write '" + stringify(src) + "' to '" + stringify(dst) +
- "' because it is not a recognised file type");
-}
-
-void
-FSMerger::on_enter_dir(bool, const FSEntry)
-{
-}
-
-void
-FSMerger::on_leave_dir(bool, const FSEntry)
-{
-}
-
void
FSMerger::on_file_over_nothing(bool is_check, const FSEntry & src, const FSEntry & dst)
{
@@ -855,15 +506,6 @@ FSMerger::install_file(const FSEntry & src, const FSEntry & dst_dir, const std::
return result;
}
-bool
-FSMerger::symlink_needs_rewriting(const FSEntry & sym)
-{
- std::string target(sym.readlink());
- std::string real_image(stringify(_imp->params.image().realpath()));
-
- return (0 == target.compare(0, real_image.length(), real_image));
-}
-
void
FSMerger::rewrite_symlink_as_needed(const FSEntry & src, const FSEntry & dst_dir)
{
@@ -990,7 +632,7 @@ FSMerger::install_dir(const FSEntry & src, const FSEntry & dst_dir)
{
result += msi_rename;
track_renamed_dir_recursive(dst);
- _imp->skip_dir = true;
+ set_skipped_dir(true);
}
else
{
@@ -1295,3 +937,109 @@ FSMerger::track_install_sym(const FSEntry & src, const FSEntry & dst_dir, const
record_install_sym(src, dst_dir, flags);
}
+void
+FSMerger::on_file_main(bool is_check, const EntryType m, const FSEntry & src, const FSEntry & dst)
+{
+ do
+ {
+ switch (m)
+ {
+ case et_nothing:
+ on_file_over_nothing(is_check, src, dst);
+ continue;
+
+ case et_sym:
+ on_file_over_sym(is_check, src, dst);
+ continue;
+
+ case et_dir:
+ on_file_over_dir(is_check, src, dst);
+ continue;
+
+ case et_misc:
+ on_file_over_misc(is_check, src, dst);
+ continue;
+
+ case et_file:
+ on_file_over_file(is_check, src, dst);
+ continue;
+
+ case last_et:
+ ;
+ }
+
+ throw InternalError(PALUDIS_HERE, "Unexpected entry_type '" + stringify(m) + "'");
+ } while (false);
+}
+
+void
+FSMerger::on_dir_main(bool is_check, const EntryType m, const FSEntry & src, const FSEntry & dst)
+{
+ do
+ {
+ switch (m)
+ {
+ case et_nothing:
+ on_dir_over_nothing(is_check, src, dst);
+ continue;
+
+ case et_sym:
+ on_dir_over_sym(is_check, src, dst);
+ continue;
+
+ case et_dir:
+ on_dir_over_dir(is_check, src, dst);
+ continue;
+
+ case et_misc:
+ on_dir_over_misc(is_check, src, dst);
+ continue;
+
+ case et_file:
+ on_dir_over_file(is_check, src, dst);
+ continue;
+
+ case last_et:
+ ;
+ }
+
+ throw InternalError(PALUDIS_HERE, "Unexpected entry_type '" + stringify(m) + "'");
+
+ } while (false);
+}
+
+void
+FSMerger::on_sym_main(bool is_check, const EntryType m, const FSEntry & src, const FSEntry & dst)
+{
+ do
+ {
+ switch (m)
+ {
+ case et_nothing:
+ on_sym_over_nothing(is_check, src, dst);
+ continue;
+
+ case et_sym:
+ on_sym_over_sym(is_check, src, dst);
+ continue;
+
+ case et_dir:
+ on_sym_over_dir(is_check, src, dst);
+ continue;
+
+ case et_misc:
+ on_sym_over_misc(is_check, src, dst);
+ continue;
+
+ case et_file:
+ on_sym_over_file(is_check, src, dst);
+ continue;
+
+ case last_et:
+ ;
+ }
+
+ throw InternalError(PALUDIS_HERE, "Unexpected entry_type '" + stringify(m) + "'");
+ } while (false);
+}
+
diff --git a/paludis/fs_merger.hh b/paludis/fs_merger.hh
index d1cb0b3..768ecb0 100644
--- a/paludis/fs_merger.hh
+++ b/paludis/fs_merger.hh
@@ -28,6 +28,8 @@
#include <paludis/util/timestamp.hh>
#include <paludis/merger_entry_type.hh>
#include <paludis/merger.hh>
+#include <paludis/environment-fwd.hh>
+#include <paludis/hook-fwd.hh>
#include <iosfwd>
#include <sys/stat.h>
#include <sys/types.h>
@@ -45,9 +47,6 @@
namespace paludis
{
- class Environment;
- class Hook;
-
namespace n
{
typedef Name<struct environment_name> environment;
@@ -106,7 +105,7 @@ namespace paludis
* \nosubgrouping
*/
class PALUDIS_VISIBLE FSMergerError :
- public Exception
+ public MergerError
{
public:
///\name Basic operations
@@ -134,9 +133,10 @@ namespace paludis
void relabel_dir_recursive(const FSEntry &, const FSEntry &);
void rewrite_symlink_as_needed(const FSEntry &, const FSEntry &);
void try_to_copy_xattrs(const FSEntry &, int, FSMergerStatusFlags &);
- bool symlink_needs_rewriting(const FSEntry &);
void do_ownership_fixes_recursive(const FSEntry &);
+ Pimp<FSMerger>::ImpPtr & _imp;
+
protected:
///\name Basic operations
///\{
@@ -145,33 +145,8 @@ namespace paludis
///\}
- /**
- * When called, makes check()'s result a failure.
- */
- void make_check_fail();
-
virtual Hook extend_hook(const Hook &);
- /**
- * Determine the entry type of a filesystem entry.
- */
- virtual EntryType entry_type(const FSEntry &);
-
- /**
- * Handle a directory, recursively.
- */
- virtual void do_dir_recursive(bool is_check, const FSEntry &, const FSEntry &);
-
- /**
- * Allows subclasses to perform behaviour when entering a directory.
- */
- virtual void on_enter_dir(bool is_check, const FSEntry);
-
- /**
- * Allows subclasses to perform behaviour when leaving a directory.
- */
- virtual void on_leave_dir(bool is_check, const FSEntry);
-
///\name Track and record merges
///\{
@@ -185,7 +160,7 @@ namespace paludis
///\name Handle filesystem entry things
///\{
- virtual void on_file(bool is_check, const FSEntry &, const FSEntry &);
+ virtual void on_file_main(bool is_check, const EntryType, const FSEntry & src, const FSEntry & dst);
virtual void on_file_over_nothing(bool is_check, const FSEntry &, const FSEntry &);
virtual void on_file_over_file(bool is_check, const FSEntry &, const FSEntry &);
virtual void on_file_over_dir(bool is_check, const FSEntry &, const FSEntry &);
@@ -196,7 +171,7 @@ namespace paludis
virtual void unlink_file(FSEntry);
virtual void record_install_file(const FSEntry &, const FSEntry &, const std::string &, const FSMergerStatusFlags &) = 0;
- virtual void on_dir(bool is_check, const FSEntry &, const FSEntry &);
+ virtual void on_dir_main(bool is_check, const EntryType, const FSEntry & src, const FSEntry & dst);
virtual void on_dir_over_nothing(bool is_check, const FSEntry &, const FSEntry &);
virtual void on_dir_over_file(bool is_check, const FSEntry &, const FSEntry &);
virtual void on_dir_over_dir(bool is_check, const FSEntry &, const FSEntry &);
@@ -208,7 +183,7 @@ namespace paludis
virtual void record_install_dir(const FSEntry &, const FSEntry &, const FSMergerStatusFlags &) = 0;
virtual void record_install_under_dir(const FSEntry &, const FSMergerStatusFlags &) = 0;
- virtual void on_sym(bool is_check, const FSEntry &, const FSEntry &);
+ virtual void on_sym_main(bool is_check, const EntryType, const FSEntry & src, const FSEntry & dst);
virtual void on_sym_over_nothing(bool is_check, const FSEntry &, const FSEntry &);
virtual void on_sym_over_file(bool is_check, const FSEntry &, const FSEntry &);
virtual void on_sym_over_dir(bool is_check, const FSEntry &, const FSEntry &);
@@ -220,22 +195,9 @@ namespace paludis
virtual void record_install_sym(const FSEntry &, const FSEntry &, const FSMergerStatusFlags &) = 0;
virtual void unlink_misc(FSEntry);
- virtual void on_misc(bool is_check, const FSEntry &, const FSEntry &);
///\}
- /**
- * What to do when an error occurs.
- */
- virtual void on_error(bool is_check, const std::string &) = 0;
-
- /**
- * What to do when a warning occurs.
- */
- virtual void on_warn(bool is_check, const std::string &) = 0;
-
- virtual void display_override(const std::string &) const = 0;
-
///\name Configuration protection
///\{
@@ -256,11 +218,6 @@ namespace paludis
///\}
/**
- * Check a merge, return whether no errors were encountered.
- */
- virtual bool check() PALUDIS_ATTRIBUTE((warn_unused_result));
-
- /**
* Perform the merge.
*/
virtual void merge();
diff --git a/paludis/merger.cc b/paludis/merger.cc
index 70c2036..e4dc3f1 100644
--- a/paludis/merger.cc
+++ b/paludis/merger.cc
@@ -20,21 +20,333 @@
#include <paludis/merger.hh>
#include <paludis/util/stringify.hh>
#include <paludis/util/exception.hh>
+#include <paludis/util/dir_iterator.hh>
+#include <paludis/util/log.hh>
+#include <paludis/util/pimp-impl.hh>
+#include <paludis/environment.hh>
#include <paludis/hook.hh>
#include <istream>
#include <ostream>
using namespace paludis;
+MergerError::MergerError(const std::string & m) throw () :
+ Exception(m)
+{
+}
+
+namespace paludis
+{
+ template <>
+ struct Imp<Merger>
+ {
+ MergerParams params;
+ bool result;
+ bool skip_dir;
+
+ Imp(const MergerParams & p) :
+ params(p),
+ result(true),
+ skip_dir(false)
+ {
+ }
+ };
+}
+
#include <paludis/merger-se.cc>
-Merger::Merger()
+Merger::Merger(const MergerParams & p) :
+ Pimp<Merger>(p)
{
}
+Merger::~Merger() = default;
+
Hook
Merger::extend_hook(const Hook & h)
{
return h;
}
+bool
+Merger::check()
+{
+ Context context("When checking merge from '" + stringify(_imp->params.image()) + "' to '"
+ + stringify(_imp->params.root()) + "':");
+
+ if (0 != _imp->params.environment()->perform_hook(extend_hook(
+ Hook("merger_check_pre")
+ ("INSTALL_SOURCE", stringify(_imp->params.image()))
+ ("INSTALL_DESTINATION", stringify(_imp->params.root())))).max_exit_status())
+ make_check_fail();
+
+ do_dir_recursive(true, _imp->params.image(), _imp->params.root() / _imp->params.install_under());
+
+ if (0 != _imp->params.environment()->perform_hook(extend_hook(
+ Hook("merger_check_post")
+ ("INSTALL_SOURCE", stringify(_imp->params.image()))
+ ("INSTALL_DESTINATION", stringify(_imp->params.root())))).max_exit_status())
+ make_check_fail();
+
+ return _imp->result;
+}
+
+void
+Merger::make_check_fail()
+{
+ _imp->result = false;
+}
+
+void
+Merger::do_dir_recursive(bool is_check, const FSEntry & src, const FSEntry & dst)
+{
+ Context context("When " + stringify(is_check ? "checking" : "performing") + " merge from '" +
+ stringify(src) + "' to '" + stringify(dst) + "':");
+
+ if (! src.is_directory())
+ throw MergerError("Source directory '" + stringify(src) + "' is not a directory");
+ if ((! is_check) && (! dst.is_directory()))
+ throw MergerError("Destination directory '" + stringify(dst) + "' is not a directory");
+
+ on_enter_dir(is_check, src);
+
+ DirIterator d(src, { dio_include_dotfiles, dio_inode_sort }), d_end;
+
+ if (is_check && d == d_end && dst != _imp->params.root().realpath())
+ {
+ if (_imp->params.options()[mo_allow_empty_dirs])
+ Log::get_instance()->message("merger.empty_directory", ll_warning, lc_context) << "Installing empty directory '"
+ << stringify(dst) << "'";
+ else
+ on_error(is_check, "Attempted to install empty directory '" + stringify(dst) + "'");
+ }
+
+ for ( ; d != d_end ; ++d)
+ {
+ EntryType m(entry_type(*d));
+ switch (m)
+ {
+ case et_sym:
+ on_sym(is_check, *d, dst);
+ continue;
+
+ case et_file:
+ on_file(is_check, *d, dst);
+ continue;
+
+ case et_dir:
+ on_dir(is_check, *d, dst);
+ if (_imp->result)
+ {
+ if (! _imp->skip_dir)
+ do_dir_recursive(is_check, *d,
+ is_check ? (dst / d->basename()) : (dst / d->basename()).realpath());
+ else
+ _imp->skip_dir = false;
+ }
+ continue;
+
+ case et_misc:
+ on_misc(is_check, *d, dst);
+ continue;
+
+ case et_nothing:
+ case last_et:
+ ;
+ }
+
+ throw InternalError(PALUDIS_HERE, "Unexpected entry_type '" + stringify(m) + "'");
+ }
+
+ on_leave_dir(is_check, src);
+}
+
+void
+Merger::on_enter_dir(bool, const FSEntry)
+{
+}
+
+void
+Merger::on_leave_dir(bool, const FSEntry)
+{
+}
+
+EntryType
+Merger::entry_type(const FSEntry & f)
+{
+ Context context("When checking type of '" + stringify(f) + "':");
+
+ if (! f.exists())
+ return et_nothing;
+
+ if (f.is_symbolic_link())
+ return et_sym;
+
+ if (f.is_regular_file())
+ return et_file;
+
+ if (f.is_directory())
+ return et_dir;
+
+ return et_misc;
+}
+
+void
+Merger::on_file(bool is_check, const FSEntry & src, const FSEntry & dst)
+{
+ Context context("When handling file '" + stringify(src) + "' to '" + stringify(dst) + "':");
+
+ EntryType m(entry_type(dst / src.basename()));
+
+ if (is_check &&
+ 0 != _imp->params.environment()->perform_hook(extend_hook(
+ Hook("merger_check_file_pre")
+ ("INSTALL_SOURCE", stringify(src))
+ ("INSTALL_DESTINATION", stringify(dst / src.basename())))).max_exit_status())
+ make_check_fail();
+
+ if (! is_check)
+ {
+ HookResult hr(_imp->params.environment()->perform_hook(extend_hook(
+ Hook("merger_install_file_override")
+ ("INSTALL_SOURCE", stringify(src))
+ ("INSTALL_DESTINATION", stringify(dst / src.basename()))
+ .grab_output(Hook::AllowedOutputValues()("skip")))));
+
+ if (hr.max_exit_status() != 0)
+ Log::get_instance()->message("merger.file.skip_hooks.failure", ll_warning, lc_context) << "Merge of '"
+ << stringify(src) << "' to '" << stringify(dst) << "' skip hooks returned non-zero";
+ else if (hr.output() == "skip")
+ {
+ std::string tidy(stringify((dst / src.basename()).strip_leading(_imp->params.root().realpath())));
+ display_override("--- [skp] " + tidy);
+ return;
+ }
+ }
+
+ on_file_main(is_check, m, src, dst);
+
+ if (is_check &&
+ 0 != _imp->params.environment()->perform_hook(extend_hook(
+ Hook("merger_check_file_post")
+ ("INSTALL_SOURCE", stringify(src))
+ ("INSTALL_DESTINATION", stringify(dst / src.basename())))).max_exit_status())
+ make_check_fail();
+}
+
+void
+Merger::on_dir(bool is_check, const FSEntry & src, const FSEntry & dst)
+{
+ Context context("When handling dir '" + stringify(src) + "' to '" + stringify(dst) + "':");
+
+ EntryType m(entry_type(dst / src.basename()));
+
+ if (is_check &&
+ 0 != _imp->params.environment()->perform_hook(extend_hook(
+ Hook("merger_check_dir_pre")
+ ("INSTALL_SOURCE", stringify(src))
+ ("INSTALL_DESTINATION", stringify(dst / src.basename())))).max_exit_status())
+ make_check_fail();
+
+ if (! is_check)
+ {
+ HookResult hr(_imp->params.environment()->perform_hook(extend_hook(
+ Hook("merger_install_dir_override")
+ ("INSTALL_SOURCE", stringify(src))
+ ("INSTALL_DESTINATION", stringify(dst / src.basename()))
+ .grab_output(Hook::AllowedOutputValues()("skip")))));
+
+ if (hr.max_exit_status() != 0)
+ Log::get_instance()->message("merger.dir.skip_hooks.failure", ll_warning, lc_context) << "Merge of '"
+ << stringify(src) << "' to '" << stringify(dst) << "' skip hooks returned non-zero";
+ else if (hr.output() == "skip")
+ {
+ std::string tidy(stringify((dst / src.basename()).strip_leading(_imp->params.root().realpath())));
+ display_override("--- [skp] " + tidy);
+ _imp->skip_dir = true;
+ return;
+ }
+ }
+
+ on_dir_main(is_check, m, src, dst);
+
+ if (is_check &&
+ 0 != _imp->params.environment()->perform_hook(extend_hook(
+ Hook("merger_check_dir_post")
+ ("INSTALL_SOURCE", stringify(src))
+ ("INSTALL_DESTINATION", stringify(dst / src.basename())))).max_exit_status())
+ make_check_fail();
+}
+
+void
+Merger::on_sym(bool is_check, const FSEntry & src, const FSEntry & dst)
+{
+ Context context("When handling sym '" + stringify(src) + "' to '" + stringify(dst) + "':");
+
+ EntryType m(entry_type(dst / src.basename()));
+
+ if (is_check &&
+ 0 != _imp->params.environment()->perform_hook(extend_hook(
+ Hook("merger_check_sym_post")
+ ("INSTALL_SOURCE", stringify(src))
+ ("INSTALL_DESTINATION", stringify(dst / src.basename())))).max_exit_status())
+ make_check_fail();
+
+ if (! is_check)
+ {
+ HookResult hr(_imp->params.environment()->perform_hook(extend_hook(
+ Hook("merger_install_sym_override")
+ ("INSTALL_SOURCE", stringify(src))
+ ("INSTALL_DESTINATION", stringify(dst / src.basename()))
+ .grab_output(Hook::AllowedOutputValues()("skip")))));
+
+ if (hr.max_exit_status() != 0)
+ Log::get_instance()->message("merger.sym.skip_hooks.failure", ll_warning, lc_context) << "Merge of '"
+ << stringify(src) << "' to '" << stringify(dst) << "' skip hooks returned non-zero";
+ else if (hr.output() == "skip")
+ {
+ std::string tidy(stringify((dst / src.basename()).strip_leading(_imp->params.root().realpath())));
+ display_override("--- [skp] " + tidy);
+ return;
+ }
+ }
+ else
+ {
+ if (symlink_needs_rewriting(src) && ! _imp->params.options()[mo_rewrite_symlinks])
+ on_error(is_check, "Symlink to image detected at: " + stringify(src) + " (" + src.readlink() + ")");
+ }
+
+ on_sym_main(is_check, m, src, dst);
+
+ if (is_check &&
+ 0 != _imp->params.environment()->perform_hook(extend_hook(
+ Hook("merger_check_sym_post")
+ ("INSTALL_SOURCE", stringify(src))
+ ("INSTALL_DESTINATION", stringify(dst / src.basename())))).max_exit_status())
+ make_check_fail();
+}
+
+void
+Merger::on_misc(bool is_check, const FSEntry & src, const FSEntry & dst)
+{
+ Context context("When handling misc '" + stringify(src) + "' to '" + stringify(dst) + "':");
+
+ on_error(is_check, "Cannot write '" + stringify(src) + "' to '" + stringify(dst) +
+ "' because it is not a recognised file type");
+}
+
+bool
+Merger::symlink_needs_rewriting(const FSEntry & sym)
+{
+ std::string target(sym.readlink());
+ std::string real_image(stringify(_imp->params.image().realpath()));
+
+ return (0 == target.compare(0, real_image.length(), real_image));
+}
+
+void
+Merger::set_skipped_dir(const bool value)
+{
+ _imp->skip_dir = value;
+}
+
diff --git a/paludis/merger.hh b/paludis/merger.hh
index d160e79..a291d4c 100644
--- a/paludis/merger.hh
+++ b/paludis/merger.hh
@@ -21,22 +21,119 @@
#define PALUDIS_GUARD_PALUDIS_MERGER_HH 1
#include <paludis/merger-fwd.hh>
+#include <paludis/util/named_value.hh>
+#include <paludis/util/fs_entry.hh>
+#include <paludis/util/options.hh>
#include <paludis/hook-fwd.hh>
+#include <paludis/environment-fwd.hh>
+#include <paludis/merger_entry_type.hh>
namespace paludis
{
- class PALUDIS_VISIBLE Merger
+ namespace n
+ {
+ typedef Name<struct environment_name> environment;
+ typedef Name<struct image_name> image;
+ typedef Name<struct install_under_name> install_under;
+ typedef Name<struct merged_entries_name> merged_entries;
+ typedef Name<struct options_name> options;
+ typedef Name<struct root_name> root;
+ }
+
+ /**
+ * Parameters for a basic Merger.
+ *
+ * \see Merger
+ * \ingroup g_repository
+ * \nosubgrouping
+ * \since 0.30
+ */
+ struct MergerParams
+ {
+ NamedValue<n::environment, Environment *> environment;
+ NamedValue<n::image, FSEntry> image;
+ NamedValue<n::install_under, FSEntry> install_under;
+ NamedValue<n::merged_entries, std::shared_ptr<FSEntrySet> > merged_entries;
+ NamedValue<n::options, MergerOptions> options;
+ NamedValue<n::root, FSEntry> root;
+ };
+
+ class PALUDIS_VISIBLE MergerError :
+ public Exception
+ {
+ public:
+ MergerError(const std::string &) throw ();
+ };
+
+ class PALUDIS_VISIBLE Merger :
+ private Pimp<Merger>
{
protected:
+ bool symlink_needs_rewriting(const FSEntry &);
+ void set_skipped_dir(const bool);
+
/**
* Allows subclasses to extend hook calls.
*/
virtual Hook extend_hook(const Hook &);
+ /**
+ * When called, makes check()'s result a failure.
+ */
+ void make_check_fail();
+
+ /**
+ * Handle a directory, recursively.
+ */
+ virtual void do_dir_recursive(bool is_check, const FSEntry &, const FSEntry &);
+
+ /**
+ * Determine the entry type of a filesystem entry.
+ */
+ virtual EntryType entry_type(const FSEntry &);
+
+ /**
+ * Allows subclasses to perform behaviour when entering a directory.
+ */
+ virtual void on_enter_dir(bool is_check, const FSEntry);
+
+ /**
+ * Allows subclasses to perform behaviour when leaving a directory.
+ */
+ virtual void on_leave_dir(bool is_check, const FSEntry);
+
+ /**
+ * What to do when an error occurs.
+ */
+ virtual void on_error(bool is_check, const std::string &) = 0;
+
+ /**
+ * What to do when a warning occurs.
+ */
+ virtual void on_warn(bool is_check, const std::string &) = 0;
+
+ virtual void display_override(const std::string &) const = 0;
+
+ virtual void on_misc(bool is_check, const FSEntry &, const FSEntry &);
+ virtual void on_file(bool is_check, const FSEntry &, const FSEntry &);
+ virtual void on_dir(bool is_check, const FSEntry &, const FSEntry &);
+ virtual void on_sym(bool is_check, const FSEntry &, const FSEntry &);
+
+ virtual void on_file_main(bool is_check, const EntryType, const FSEntry &, const FSEntry &) = 0;
+ virtual void on_dir_main(bool is_check, const EntryType, const FSEntry &, const FSEntry &) = 0;
+ virtual void on_sym_main(bool is_check, const EntryType, const FSEntry &, const FSEntry &) = 0;
+
public:
- Merger();
+ explicit Merger(const MergerParams &);
+ ~Merger();
+
Merger(const Merger &) = delete;
Merger & operator= (const Merger &) = delete;
+
+ /**
+ * Check a merge, return whether no errors were encountered.
+ */
+ virtual bool check() PALUDIS_ATTRIBUTE((warn_unused_result));
};
}