aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2010-07-28 21:23:11 +0100
committerAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2010-07-31 11:36:04 +0100
commit07adeeb6f48bab15107e2e92c9dd37c9168789a5 (patch)
tree5497eb7d37a56f8cfc75da68afa2c8a66439c7dd
parenta3c6fb2529026c256987712e5068efb617a9e44f (diff)
downloadpaludis-07adeeb6f48bab15107e2e92c9dd37c9168789a5.tar.gz
paludis-07adeeb6f48bab15107e2e92c9dd37c9168789a5.tar.xz
Start basic TarMerger
-rw-r--r--.gitignore1
-rw-r--r--configure.ac13
-rw-r--r--paludis/Makefile.am.m49
-rw-r--r--paludis/files.m41
-rw-r--r--paludis/fs_merger.cc133
-rw-r--r--paludis/fs_merger.hh12
-rw-r--r--paludis/merger.cc103
-rw-r--r--paludis/merger.hh19
-rw-r--r--paludis/tar_extras.cc79
-rw-r--r--paludis/tar_extras.hh32
-rw-r--r--paludis/tar_merger-fwd.hh28
-rw-r--r--paludis/tar_merger.cc150
-rw-r--r--paludis/tar_merger.hh86
-rw-r--r--paludis/tar_merger_TEST.cc120
-rwxr-xr-xpaludis/tar_merger_TEST_cleanup.sh9
-rwxr-xr-xpaludis/tar_merger_TEST_setup.sh11
16 files changed, 688 insertions, 118 deletions
diff --git a/.gitignore b/.gitignore
index c96e68d..26097ac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -421,6 +421,7 @@ paludis-*.*.*.tar.bz2
/paludis/syncers/dotar+ftp
/paludis/syncers/dotar+http
/paludis/syncers/dotar+https
+/paludis/tar_merger_TEST
/paludis/uninstall_list_TEST
/paludis/user_dep_spec_TEST
/paludis/util/Makefile.am
diff --git a/configure.ac b/configure.ac
index 242081e..dea6463 100644
--- a/configure.ac
+++ b/configure.ac
@@ -91,6 +91,7 @@ need_xml_check=
need_pcrecpp_check=
need_syck_check=
need_gem_check=
+need_libarchive_check=
need_resolver=
dnl {{{ we can use abi::__cxa_demangle
@@ -1466,6 +1467,7 @@ AC_SUBST([ENABLE_VISIBILITY])
AM_CONDITIONAL([ENABLE_PBINS], test "x$enable_pbins" = "xyes")
if test x"$ENABLE_PBINS" = "xyes" ; then
AC_DEFINE([ENABLE_PBINS], [1], [Build pbin support])
+ need_libarchive_check=yes
fi
dnl }}}
@@ -1500,6 +1502,17 @@ if test "x$need_pcrecpp_check" = "xyes" ; then
fi
dnl }}}
+dnl {{{ libarchive
+if test "x$need_libarchive_check" = "xyes" ; then
+ AC_MSG_CHECKING([for libarchive])
+ AC_PREPROC_IFELSE([
+#include <archive.h>
+],
+ [AC_MSG_RESULT([yes])],
+ [AC_MSG_ERROR([libarchive is required for pbins])])
+fi
+dnl }}}
+
dnl {{{ syck check
if test "x$need_syck_check" = "xyes" ; then
AC_MSG_CHECKING([for syck])
diff --git a/paludis/Makefile.am.m4 b/paludis/Makefile.am.m4
index 5ce272f..2558b7a 100644
--- a/paludis/Makefile.am.m4
+++ b/paludis/Makefile.am.m4
@@ -163,6 +163,15 @@ if ENABLE_PYTHON_HOOKS
lib_LTLIBRARIES += libpaludispythonhooks_@PALUDIS_PC_SLOT@.la
endif
+if ENABLE_PBINS
+lib_LTLIBRARIES += libpaludistarextras_@PALUDIS_PC_SLOT@.la
+endif
+
+libpaludistarextras_@PALUDIS_PC_SLOT@_la_SOURCES = tar_extras.cc tar_extras.hh
+libpaludistarextras_@PALUDIS_PC_SLOT@_la_CXXFLAGS = $(AM_CXXFLAGS)
+libpaludistarextras_@PALUDIS_PC_SLOT@_la_LIBADD = -larchive
+libpaludistarextras_@PALUDIS_PC_SLOT@_la_LDFLAGS = -version-info @VERSION_LIB_CURRENT@:@VERSION_LIB_REVISION@:0
+
paludis_includedir = $(includedir)/paludis-$(PALUDIS_PC_SLOT)/paludis/
paludis_include_HEADERS = headerlist seheaderlist
diff --git a/paludis/files.m4 b/paludis/files.m4
index db296a1..10f32e0 100644
--- a/paludis/files.m4
+++ b/paludis/files.m4
@@ -99,6 +99,7 @@ add(`stringify_formatter', `hh', `cc', `fwd', `impl', `test')
add(`stripper', `hh', `cc', `fwd', `test', `testscript')
add(`syncer', `hh', `cc')
add(`sync_task', `hh', `cc')
+add(`tar_merger', `hh', `cc', `fwd', `test', `testscript')
add(`tasks_exceptions', `hh', `cc')
add(`tee_output_manager', `hh', `cc', `fwd')
add(`unchoices_key', `hh', `cc', `fwd')
diff --git a/paludis/fs_merger.cc b/paludis/fs_merger.cc
index 1f98456..58c0320 100644
--- a/paludis/fs_merger.cc
+++ b/paludis/fs_merger.cc
@@ -59,7 +59,6 @@ namespace paludis
template <>
struct Imp<FSMerger>
{
- std::set<FSEntry> fixed_entries;
MergedMap merged_ids;
FSMergerParams params;
@@ -79,9 +78,11 @@ FSMerger::FSMerger(const FSMergerParams & p) :
Pimp<FSMerger>(p),
Merger(make_named_values<MergerParams>(
n::environment() = p.environment(),
+ n::get_new_ids_or_minus_one() = p.get_new_ids_or_minus_one(),
n::image() = p.image(),
n::install_under() = p.install_under(),
n::merged_entries() = p.merged_entries(),
+ n::no_chown() = p.no_chown(),
n::options() = p.options(),
n::root() = p.root()
)),
@@ -96,9 +97,6 @@ FSMerger::~FSMerger()
void
FSMerger::merge()
{
- Context context("When performing merge from '" + stringify(_imp->params.image()) + "' to '"
- + stringify(_imp->params.root()) + "':");
-
struct SaveUmask
{
mode_t m;
@@ -114,43 +112,27 @@ FSMerger::merge()
}
} old_umask(::umask(0000));
- if (0 != _imp->params.environment()->perform_hook(extend_hook(
- Hook("merger_install_pre")
- ("INSTALL_SOURCE", stringify(_imp->params.image()))
- ("INSTALL_DESTINATION", stringify(_imp->params.root())))).max_exit_status())
- Log::get_instance()->message("merger.pre_hooks.failure", ll_warning, lc_context) <<
- "Merge of '" << _imp->params.image() << "' to '" << _imp->params.root() << "' pre hooks returned non-zero";
-
- /* special handling for install_under */
- {
- Context local_context("When preparing install_under directory '" + stringify(_imp->params.install_under()) + "' under root '"
- + stringify(_imp->params.root()) + "':");
-
- std::list<FSEntry> dd;
- for (FSEntry d(_imp->params.root().realpath() / _imp->params.install_under()), d_end(_imp->params.root().realpath()) ;
- d != d_end ; d = d.dirname())
- dd.push_front(d);
- for (std::list<FSEntry>::iterator d(dd.begin()), d_end(dd.end()) ; d != d_end ; ++d)
- if (! d->exists())
- {
- d->mkdir();
- track_install_under_dir(*d, FSMergerStatusFlags());
- }
- else
- track_install_under_dir(*d, FSMergerStatusFlags() + msi_used_existing);
- }
-
- if (! _imp->params.no_chown())
- do_ownership_fixes_recursive(_imp->params.image());
+ Merger::merge();
+}
- do_dir_recursive(false, _imp->params.image(), (_imp->params.root() / _imp->params.install_under()).realpath());
+void
+FSMerger::prepare_install_under()
+{
+ Context context("When preparing install_under directory '" + stringify(_imp->params.install_under()) + "' under root '"
+ + stringify(_imp->params.root()) + "':");
- if (0 != _imp->params.environment()->perform_hook(extend_hook(
- Hook("merger_install_post")
- ("INSTALL_SOURCE", stringify(_imp->params.image()))
- ("INSTALL_DESTINATION", stringify(_imp->params.root())))).max_exit_status())
- Log::get_instance()->message("merger.post_hooks.failure", ll_warning, lc_context) <<
- "Merge of '" << _imp->params.image() << "' to '" << _imp->params.root() << "' post hooks returned non-zero";
+ std::list<FSEntry> dd;
+ for (FSEntry d(_imp->params.root().realpath() / _imp->params.install_under()), d_end(_imp->params.root().realpath()) ;
+ d != d_end ; d = d.dirname())
+ dd.push_front(d);
+ for (std::list<FSEntry>::iterator d(dd.begin()), d_end(dd.end()) ; d != d_end ; ++d)
+ if (! d->exists())
+ {
+ d->mkdir();
+ track_install_under_dir(*d, FSMergerStatusFlags());
+ }
+ else
+ track_install_under_dir(*d, FSMergerStatusFlags() + msi_used_existing);
}
void
@@ -308,59 +290,6 @@ FSMerger::on_sym_over_misc(bool is_check, const FSEntry & src, const FSEntry & d
track_install_sym(src, dst, install_sym(src, dst) + msi_unlinked_first);
}
-void
-FSMerger::do_ownership_fixes_recursive(const FSEntry & dir)
-{
- for (DirIterator d(dir, { dio_include_dotfiles, dio_inode_sort }), d_end ; d != d_end ; ++d)
- {
- std::pair<uid_t, gid_t> new_ids(_imp->params.get_new_ids_or_minus_one()(*d));
- if (uid_t(-1) != new_ids.first || gid_t(-1) != new_ids.second)
- {
- FSEntry f(*d);
- f.lchown(new_ids.first, new_ids.second);
-
- if (et_sym != entry_type(*d))
- {
- mode_t mode(f.permissions());
-
- if (et_dir == entry_type(*d))
- {
- if (uid_t(-1) != new_ids.first)
- mode &= ~S_ISUID;
- if (gid_t(-1) != new_ids.second)
- mode &= ~S_ISGID;
- }
-
- f.chmod(mode); /* set*id */
- }
-
- _imp->fixed_entries.insert(f);
- }
-
- EntryType m(entry_type(*d));
- switch (m)
- {
- case et_sym:
- case et_file:
- continue;
-
- case et_dir:
- do_ownership_fixes_recursive(*d);
- continue;
-
- case et_misc:
- throw FSMergerError("Unexpected 'et_misc' entry found at: " + stringify(*d));
- continue;
-
- case et_nothing:
- case last_et:
- ;
- }
-
- throw InternalError(PALUDIS_HERE, "Unexpected entry_type '" + stringify(m) + "'");
- }
-}
-
FSMergerStatusFlags
FSMerger::install_file(const FSEntry & src, const FSEntry & dst_dir, const std::string & dst_name)
{
@@ -492,7 +421,7 @@ FSMerger::install_file(const FSEntry & src, const FSEntry & dst_dir, const std::
_imp->merged_ids.insert(make_pair(src.lowlevel_id(), stringify(dst_real)));
}
- if (_imp->fixed_entries.end() != _imp->fixed_entries.find(src))
+ if (fixed_ownership_for(src))
result += msi_fixed_ownership;
if (0 != _imp->params.environment()->perform_hook(extend_hook(
@@ -532,7 +461,7 @@ FSMerger::track_renamed_dir_recursive(const FSEntry & dst)
for (DirIterator d(dst, { dio_include_dotfiles, dio_inode_sort }), d_end ; d != d_end ; ++d)
{
FSMergerStatusFlags merged_how;
- if (_imp->fixed_entries.end() != _imp->fixed_entries.find(_imp->params.image() / *d))
+ if (fixed_ownership_for(_imp->params.image() / *d))
merged_how += msi_fixed_ownership;
EntryType m(entry_type(*d));
switch (m)
@@ -659,7 +588,7 @@ FSMerger::install_dir(const FSEntry & src, const FSEntry & dst_dir)
try_to_copy_xattrs(src, dst_fd, result);
}
- if (_imp->fixed_entries.end() != _imp->fixed_entries.find(src))
+ if (fixed_ownership_for(src))
result += msi_fixed_ownership;
if (0 != _imp->params.environment()->perform_hook(extend_hook(
@@ -727,7 +656,7 @@ FSMerger::install_sym(const FSEntry & src, const FSEntry & dst_dir)
if (! _imp->params.no_chown())
{
dst.lchown(src.owner(), src.group());
- if (_imp->fixed_entries.end() != _imp->fixed_entries.find(src))
+ if (fixed_ownership_for(src))
result += msi_fixed_ownership;
}
@@ -938,8 +867,10 @@ FSMerger::track_install_sym(const FSEntry & src, const FSEntry & dst_dir, const
}
void
-FSMerger::on_file_main(bool is_check, const EntryType m, const FSEntry & src, const FSEntry & dst)
+FSMerger::on_file_main(bool is_check, const FSEntry & src, const FSEntry & dst)
{
+ EntryType m(entry_type(dst / src.basename()));
+
do
{
switch (m)
@@ -973,8 +904,10 @@ FSMerger::on_file_main(bool is_check, const EntryType m, const FSEntry & src, co
}
void
-FSMerger::on_dir_main(bool is_check, const EntryType m, const FSEntry & src, const FSEntry & dst)
+FSMerger::on_dir_main(bool is_check, const FSEntry & src, const FSEntry & dst)
{
+ EntryType m(entry_type(dst / src.basename()));
+
do
{
switch (m)
@@ -1009,8 +942,10 @@ FSMerger::on_dir_main(bool is_check, const EntryType m, const FSEntry & src, con
}
void
-FSMerger::on_sym_main(bool is_check, const EntryType m, const FSEntry & src, const FSEntry & dst)
+FSMerger::on_sym_main(bool is_check, const FSEntry & src, const FSEntry & dst)
{
+ EntryType m(entry_type(dst / src.basename()));
+
do
{
switch (m)
diff --git a/paludis/fs_merger.hh b/paludis/fs_merger.hh
index 768ecb0..36420a1 100644
--- a/paludis/fs_merger.hh
+++ b/paludis/fs_merger.hh
@@ -133,7 +133,6 @@ 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 &);
- void do_ownership_fixes_recursive(const FSEntry &);
Pimp<FSMerger>::ImpPtr & _imp;
@@ -160,7 +159,7 @@ namespace paludis
///\name Handle filesystem entry things
///\{
- virtual void on_file_main(bool is_check, const EntryType, const FSEntry & src, const FSEntry & dst);
+ virtual void on_file_main(bool is_check, 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 &);
@@ -171,7 +170,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_main(bool is_check, const EntryType, const FSEntry & src, const FSEntry & dst);
+ virtual void on_dir_main(bool is_check, 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 &);
@@ -183,7 +182,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_main(bool is_check, const EntryType, const FSEntry & src, const FSEntry & dst);
+ virtual void on_sym_main(bool is_check, 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 &);
@@ -196,6 +195,8 @@ namespace paludis
virtual void unlink_misc(FSEntry);
+ virtual void prepare_install_under();
+
///\}
///\name Configuration protection
@@ -217,9 +218,6 @@ namespace paludis
///\}
- /**
- * Perform the merge.
- */
virtual void merge();
};
diff --git a/paludis/merger.cc b/paludis/merger.cc
index e4dc3f1..6a79c48 100644
--- a/paludis/merger.cc
+++ b/paludis/merger.cc
@@ -25,6 +25,7 @@
#include <paludis/util/pimp-impl.hh>
#include <paludis/environment.hh>
#include <paludis/hook.hh>
+#include <set>
#include <istream>
#include <ostream>
@@ -44,6 +45,8 @@ namespace paludis
bool result;
bool skip_dir;
+ std::set<FSEntry> fixed_entries;
+
Imp(const MergerParams & p) :
params(p),
result(true),
@@ -92,6 +95,35 @@ Merger::check()
}
void
+Merger::merge()
+{
+ Context context("When performing merge from '" + stringify(_imp->params.image()) + "' to '"
+ + stringify(_imp->params.root()) + "':");
+
+ if (0 != _imp->params.environment()->perform_hook(extend_hook(
+ Hook("merger_install_pre")
+ ("INSTALL_SOURCE", stringify(_imp->params.image()))
+ ("INSTALL_DESTINATION", stringify(_imp->params.root())))).max_exit_status())
+ Log::get_instance()->message("merger.pre_hooks.failure", ll_warning, lc_context) <<
+ "Merge of '" << _imp->params.image() << "' to '" << _imp->params.root() << "' pre hooks returned non-zero";
+
+ prepare_install_under();
+
+ if (! _imp->params.no_chown())
+ do_ownership_fixes_recursive(_imp->params.image());
+
+ do_dir_recursive(false, _imp->params.image(), (_imp->params.root() / _imp->params.install_under()).realpath());
+
+ if (0 != _imp->params.environment()->perform_hook(extend_hook(
+ Hook("merger_install_post")
+ ("INSTALL_SOURCE", stringify(_imp->params.image()))
+ ("INSTALL_DESTINATION", stringify(_imp->params.root())))).max_exit_status())
+ Log::get_instance()->message("merger.post_hooks.failure", ll_warning, lc_context) <<
+ "Merge of '" << _imp->params.image() << "' to '" << _imp->params.root() << "' post hooks returned non-zero";
+}
+
+
+void
Merger::make_check_fail()
{
_imp->result = false;
@@ -196,8 +228,6 @@ 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")
@@ -224,7 +254,7 @@ Merger::on_file(bool is_check, const FSEntry & src, const FSEntry & dst)
}
}
- on_file_main(is_check, m, src, dst);
+ on_file_main(is_check, src, dst);
if (is_check &&
0 != _imp->params.environment()->perform_hook(extend_hook(
@@ -239,8 +269,6 @@ 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")
@@ -268,7 +296,7 @@ Merger::on_dir(bool is_check, const FSEntry & src, const FSEntry & dst)
}
}
- on_dir_main(is_check, m, src, dst);
+ on_dir_main(is_check, src, dst);
if (is_check &&
0 != _imp->params.environment()->perform_hook(extend_hook(
@@ -283,8 +311,6 @@ 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")
@@ -316,7 +342,7 @@ Merger::on_sym(bool is_check, const FSEntry & src, const FSEntry & dst)
on_error(is_check, "Symlink to image detected at: " + stringify(src) + " (" + src.readlink() + ")");
}
- on_sym_main(is_check, m, src, dst);
+ on_sym_main(is_check, src, dst);
if (is_check &&
0 != _imp->params.environment()->perform_hook(extend_hook(
@@ -350,3 +376,62 @@ Merger::set_skipped_dir(const bool value)
_imp->skip_dir = value;
}
+void
+Merger::do_ownership_fixes_recursive(const FSEntry & dir)
+{
+ for (DirIterator d(dir, { dio_include_dotfiles, dio_inode_sort }), d_end ; d != d_end ; ++d)
+ {
+ std::pair<uid_t, gid_t> new_ids(_imp->params.get_new_ids_or_minus_one()(*d));
+ if (uid_t(-1) != new_ids.first || gid_t(-1) != new_ids.second)
+ {
+ FSEntry f(*d);
+ f.lchown(new_ids.first, new_ids.second);
+
+ if (et_sym != entry_type(*d))
+ {
+ mode_t mode(f.permissions());
+
+ if (et_dir == entry_type(*d))
+ {
+ if (uid_t(-1) != new_ids.first)
+ mode &= ~S_ISUID;
+ if (gid_t(-1) != new_ids.second)
+ mode &= ~S_ISGID;
+ }
+
+ f.chmod(mode); /* set*id */
+ }
+
+ _imp->fixed_entries.insert(f);
+ }
+
+ EntryType m(entry_type(*d));
+ switch (m)
+ {
+ case et_sym:
+ case et_file:
+ continue;
+
+ case et_dir:
+ do_ownership_fixes_recursive(*d);
+ continue;
+
+ case et_misc:
+ throw MergerError("Unexpected 'et_misc' entry found at: " + stringify(*d));
+ continue;
+
+ case et_nothing:
+ case last_et:
+ ;
+ }
+
+ throw InternalError(PALUDIS_HERE, "Unexpected entry_type '" + stringify(m) + "'");
+ }
+}
+
+bool
+Merger::fixed_ownership_for(const FSEntry & f)
+{
+ return _imp->fixed_entries.end() != _imp->fixed_entries.find(f);
+}
+
diff --git a/paludis/merger.hh b/paludis/merger.hh
index a291d4c..826e083 100644
--- a/paludis/merger.hh
+++ b/paludis/merger.hh
@@ -33,9 +33,11 @@ namespace paludis
namespace n
{
typedef Name<struct environment_name> environment;
+ typedef Name<struct get_new_ids_or_minus_one_name> get_new_ids_or_minus_one;
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 no_chown_name> no_chown;
typedef Name<struct options_name> options;
typedef Name<struct root_name> root;
}
@@ -51,9 +53,11 @@ namespace paludis
struct MergerParams
{
NamedValue<n::environment, Environment *> environment;
+ NamedValue<n::get_new_ids_or_minus_one, std::function<std::pair<uid_t, gid_t> (const FSEntry &)> > get_new_ids_or_minus_one;
NamedValue<n::image, FSEntry> image;
NamedValue<n::install_under, FSEntry> install_under;
NamedValue<n::merged_entries, std::shared_ptr<FSEntrySet> > merged_entries;
+ NamedValue<n::no_chown, bool> no_chown;
NamedValue<n::options, MergerOptions> options;
NamedValue<n::root, FSEntry> root;
};
@@ -71,6 +75,8 @@ namespace paludis
protected:
bool symlink_needs_rewriting(const FSEntry &);
void set_skipped_dir(const bool);
+ void do_ownership_fixes_recursive(const FSEntry &);
+ bool fixed_ownership_for(const FSEntry &);
/**
* Allows subclasses to extend hook calls.
@@ -119,9 +125,11 @@ namespace paludis
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;
+ virtual void on_file_main(bool is_check, const FSEntry &, const FSEntry &) = 0;
+ virtual void on_dir_main(bool is_check, const FSEntry &, const FSEntry &) = 0;
+ virtual void on_sym_main(bool is_check, const FSEntry &, const FSEntry &) = 0;
+
+ virtual void prepare_install_under() = 0;
public:
explicit Merger(const MergerParams &);
@@ -134,6 +142,11 @@ 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/tar_extras.cc b/paludis/tar_extras.cc
new file mode 100644
index 0000000..4e7199b
--- /dev/null
+++ b/paludis/tar_extras.cc
@@ -0,0 +1,79 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2010 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/tar_extras.hh>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <archive.h>
+#include <archive_entry.h>
+
+struct PaludisTarExtras
+{
+ struct archive * archive;
+};
+
+extern "C"
+PaludisTarExtras *
+paludis_tar_extras_init(const std::string & f)
+{
+ auto extras(new PaludisTarExtras);
+ extras->archive = archive_write_new();
+ archive_write_set_compression_none(extras->archive);
+ archive_write_set_format_pax(extras->archive);
+ archive_write_open_filename(extras->archive, f.c_str());
+ return extras;
+}
+
+extern "C"
+void
+paludis_tar_extras_add_file(PaludisTarExtras * const extras, const std::string & from, const std::string & path)
+{
+ struct archive * disk_archive(archive_read_disk_new());
+ archive_read_disk_set_standard_lookup(disk_archive);
+
+ struct archive_entry * entry(archive_entry_new());
+
+ int fd(open(from.c_str(), O_RDONLY));
+
+ archive_entry_copy_pathname(entry, path.c_str());
+ archive_read_disk_entry_from_file(disk_archive, entry, fd, 0);
+ archive_write_header(extras->archive, entry);
+
+ int bytes_read;
+ char buf[4096];
+ while ((bytes_read = read(fd, buf, sizeof(buf))) > 0)
+ archive_write_data(extras->archive, buf, bytes_read);
+
+ close(fd);
+
+ archive_write_finish_entry(extras->archive);
+ archive_read_finish(disk_archive);
+ archive_entry_free(entry);
+}
+
+extern "C"
+void
+paludis_tar_extras_cleanup(PaludisTarExtras * const extras)
+{
+ archive_write_close(extras->archive);
+ archive_write_finish(extras->archive);
+ delete extras;
+}
+
diff --git a/paludis/tar_extras.hh b/paludis/tar_extras.hh
new file mode 100644
index 0000000..0643be1
--- /dev/null
+++ b/paludis/tar_extras.hh
@@ -0,0 +1,32 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2010 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
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_TAR_EXTRAS_HH
+#define PALUDIS_GUARD_PALUDIS_TAR_EXTRAS_HH 1
+
+#include <paludis/util/attributes.hh>
+#include <string>
+
+struct PaludisTarExtras;
+
+extern "C" PaludisTarExtras * paludis_tar_extras_init(const std::string &) PALUDIS_VISIBLE PALUDIS_ATTRIBUTE((warn_unused_result));
+extern "C" void paludis_tar_extras_add_file(PaludisTarExtras * const, const std::string &, const std::string &) PALUDIS_VISIBLE;
+extern "C" void paludis_tar_extras_cleanup(PaludisTarExtras * const) PALUDIS_VISIBLE;
+
+#endif
diff --git a/paludis/tar_merger-fwd.hh b/paludis/tar_merger-fwd.hh
new file mode 100644
index 0000000..66605a1
--- /dev/null
+++ b/paludis/tar_merger-fwd.hh
@@ -0,0 +1,28 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2010 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
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_TAR_MERGER_FWD_HH
+#define PALUDIS_GUARD_PALUDIS_TAR_MERGER_FWD_HH 1
+
+namespace paludis
+{
+ class TarMerger;
+}
+
+#endif
diff --git a/paludis/tar_merger.cc b/paludis/tar_merger.cc
new file mode 100644
index 0000000..9fd30d6
--- /dev/null
+++ b/paludis/tar_merger.cc
@@ -0,0 +1,150 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2010 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/tar_merger.hh>
+#include <paludis/tar_extras.hh>
+#include <paludis/util/pimp-impl.hh>
+#include <paludis/util/make_named_values.hh>
+#include <paludis/util/singleton-impl.hh>
+#include <paludis/util/strip.hh>
+#include <paludis/about.hh>
+
+#include <dlfcn.h>
+#include <stdint.h>
+
+#define STUPID_CAST(type, val) reinterpret_cast<type>(reinterpret_cast<uintptr_t>(val))
+
+using namespace paludis;
+
+namespace
+{
+ struct TarMergerHandle :
+ Singleton<TarMergerHandle>
+ {
+ typedef PaludisTarExtras * (* InitPtr) (const std::string &);
+ typedef void (* AddFilePtr) (PaludisTarExtras * const, const std::string &, const std::string &);
+ typedef void (* CleanupPtr) (PaludisTarExtras * const);
+
+ void * handle;
+ InitPtr init;
+ AddFilePtr add_file;
+ CleanupPtr cleanup;
+
+ TarMergerHandle()
+ {
+ handle = ::dlopen(("libpaludistarextras_" + stringify(PALUDIS_PC_SLOT) + ".so").c_str(), RTLD_NOW | RTLD_GLOBAL);
+ if (! handle)
+ throw MergerError("Unable to open libpaludistarextras due to error '" + stringify(::dlerror()) + "' from dlopen");
+
+ init = STUPID_CAST(InitPtr, ::dlsym(handle, "paludis_tar_extras_init"));
+ if (! init)
+ throw MergerError("Unable to init from libpaludistarextras due to error '" + stringify(::dlerror()) + "' from dlsym");
+
+ add_file = STUPID_CAST(AddFilePtr, ::dlsym(handle, "paludis_tar_extras_add_file"));
+ if (! add_file)
+ throw MergerError("Unable to add_file from libpaludistarextras due to error '" + stringify(::dlerror()) + "' from dlsym");
+
+ cleanup = STUPID_CAST(CleanupPtr, ::dlsym(handle, "paludis_tar_extras_cleanup"));
+ if (! cleanup)
+ throw MergerError("Unable to cleanup from libpaludistarextras due to error '" + stringify(::dlerror()) + "' from dlsym");
+ }
+
+ ~TarMergerHandle()
+ {
+ if (handle)
+ ::dlclose(handle);
+ }
+ };
+}
+
+namespace paludis
+{
+ template <>
+ struct Imp<TarMerger>
+ {
+ TarMergerParams params;
+ PaludisTarExtras * tar;
+
+ Imp(const TarMergerParams & p) :
+ params(p)
+ {
+ }
+ };
+}
+
+TarMerger::TarMerger(const TarMergerParams & p) :
+ Pimp<TarMerger>(p),
+ Merger(make_named_values<MergerParams>(
+ n::environment() = p.environment(),
+ n::get_new_ids_or_minus_one() = p.get_new_ids_or_minus_one(),
+ n::image() = p.image(),
+ n::install_under() = FSEntry("/"),
+ n::merged_entries() = p.merged_entries(),
+ n::no_chown() = p.no_chown(),
+ n::options() = p.options(),
+ n::root() = p.root()
+ )),
+ _imp(Pimp<TarMerger>::_imp)
+{
+}
+
+TarMerger::~TarMerger() = default;
+
+void
+TarMerger::on_file_main(bool is_check, const FSEntry & src, const FSEntry & dst)
+{
+ if (is_check)
+ return;
+
+ (*TarMergerHandle::get_instance()->add_file)(_imp->tar, stringify(src), strip_leading(stringify(dst / src.basename()), "/"));
+}
+
+void
+TarMerger::on_dir_main(bool, const FSEntry &, const FSEntry &)
+{
+}
+
+void
+TarMerger::on_sym_main(bool, const FSEntry &, const FSEntry &)
+{
+}
+
+void
+TarMerger::prepare_install_under()
+{
+}
+
+void
+TarMerger::merge()
+{
+ _imp->tar = (*TarMergerHandle::get_instance()->init)(stringify(_imp->params.tar_file()));
+
+ try
+ {
+ Merger::merge();
+
+ (*TarMergerHandle::get_instance()->cleanup)(_imp->tar);
+ }
+ catch (...)
+ {
+ (*TarMergerHandle::get_instance()->cleanup)(_imp->tar);
+ throw;
+ }
+}
+
diff --git a/paludis/tar_merger.hh b/paludis/tar_merger.hh
new file mode 100644
index 0000000..09716ea
--- /dev/null
+++ b/paludis/tar_merger.hh
@@ -0,0 +1,86 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2010 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
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_TAR_MERGER_HH
+#define PALUDIS_GUARD_PALUDIS_TAR_MERGER_HH 1
+
+#include <paludis/tar_merger-fwd.hh>
+#include <paludis/util/timestamp.hh>
+#include <paludis/merger.hh>
+
+namespace paludis
+{
+ namespace n
+ {
+ typedef Name<struct environment_name> environment;
+ typedef Name<struct fix_mtimes_before_name> fix_mtimes_before;
+ typedef Name<struct get_new_ids_or_minus_one_name> get_new_ids_or_minus_one;
+ 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 no_chown_name> no_chown;
+ typedef Name<struct options_name> options;
+ typedef Name<struct root_name> root;
+ typedef Name<struct tar_file_name> tar_file;
+ }
+
+ /**
+ * Parameters for a basic TarMerger.
+ *
+ * \see Merger
+ * \ingroup g_repository
+ * \nosubgrouping
+ * \since 0.51
+ */
+ struct TarMergerParams
+ {
+ NamedValue<n::environment, Environment *> environment;
+ NamedValue<n::fix_mtimes_before, Timestamp> fix_mtimes_before;
+ NamedValue<n::get_new_ids_or_minus_one, std::function<std::pair<uid_t, gid_t> (const FSEntry &)> > get_new_ids_or_minus_one;
+ NamedValue<n::image, FSEntry> image;
+ NamedValue<n::install_under, FSEntry> install_under;
+ NamedValue<n::merged_entries, std::shared_ptr<FSEntrySet> > merged_entries;
+ NamedValue<n::no_chown, bool> no_chown;
+ NamedValue<n::options, MergerOptions> options;
+ NamedValue<n::root, FSEntry> root;
+ NamedValue<n::tar_file, FSEntry> tar_file;
+ };
+
+ class PALUDIS_VISIBLE TarMerger :
+ private Pimp<TarMerger>,
+ public Merger
+ {
+ private:
+ Pimp<TarMerger>::ImpPtr & _imp;
+
+ public:
+ TarMerger(const TarMergerParams &);
+ ~TarMerger();
+
+ virtual void on_file_main(bool is_check, const FSEntry &, const FSEntry &);
+ virtual void on_dir_main(bool is_check, const FSEntry &, const FSEntry &);
+ virtual void on_sym_main(bool is_check, const FSEntry &, const FSEntry &);
+
+ virtual void prepare_install_under();
+
+ virtual void merge();
+ };
+}
+
+#endif
diff --git a/paludis/tar_merger_TEST.cc b/paludis/tar_merger_TEST.cc
new file mode 100644
index 0000000..65660c5
--- /dev/null
+++ b/paludis/tar_merger_TEST.cc
@@ -0,0 +1,120 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2010 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/tar_merger.hh>
+#include <paludis/environments/test/test_environment.hh>
+#include <paludis/hooker.hh>
+#include <paludis/util/wrapped_forward_iterator.hh>
+#include <paludis/util/make_named_values.hh>
+#include <paludis/util/timestamp.hh>
+#include <paludis/util/set.hh>
+#include <paludis/util/system.hh>
+#include <paludis/util/stringify.hh>
+#include <paludis/hook.hh>
+#include <test/test_framework.hh>
+#include <test/test_runner.hh>
+
+using namespace paludis;
+using namespace test;
+
+namespace
+{
+ std::pair<uid_t, gid_t>
+ get_new_ids_or_minus_one(const FSEntry &)
+ {
+ return std::make_pair(-1, -1);
+ }
+}
+
+namespace
+{
+ struct TestTarMerger :
+ TarMerger
+ {
+ TestTarMerger(const TarMergerParams & p) :
+ TarMerger(p)
+ {
+ }
+
+ virtual void on_error(bool is_check, const std::string & s)
+ {
+ if (is_check)
+ make_check_fail();
+ else
+ throw MergerError(s);
+ }
+
+ void on_warn(bool, const std::string &)
+ {
+ }
+
+ void display_override(const std::string &) const
+ {
+ }
+ };
+}
+
+namespace test_cases
+{
+ struct SimpleTarMergerTest : TestCase
+ {
+ SimpleTarMergerTest() : TestCase("simple tar merge") { }
+
+ bool repeatable() const
+ {
+ return false;
+ }
+
+ void run()
+ {
+ auto output(FSEntry("tar_merger_TEST_dir") / "simple.tar");
+
+ TestEnvironment env;
+ TestTarMerger merger(make_named_values<TarMergerParams>(
+ n::environment() = &env,
+ n::fix_mtimes_before() = Timestamp(0, 0),
+ n::get_new_ids_or_minus_one() = &get_new_ids_or_minus_one,
+ n::image() = FSEntry("tar_merger_TEST_dir") / "simple",
+ n::install_under() = FSEntry("/"),
+ n::merged_entries() = std::make_shared<FSEntrySet>(),
+ n::no_chown() = true,
+ n::options() = MergerOptions(),
+ n::root() = FSEntry("/"),
+ n::tar_file() = output
+ ));
+
+ TEST_CHECK(! output.is_regular_file());
+
+ merger.merge();
+ output = FSEntry(stringify(output));
+
+ TEST_CHECK(output.is_regular_file());
+ TEST_CHECK(output.file_size() > 100);
+
+ Command cmd("tar xf ../simple.tar 2>&1");
+ cmd.with_chdir(FSEntry("tar_merger_TEST_dir/simple_extract"));
+ TEST_CHECK_EQUAL(0, run_command(cmd));
+
+ TEST_CHECK((FSEntry("tar_merger_TEST_dir") / "simple_extract" / "file").is_regular_file());
+ TEST_CHECK_EQUAL((FSEntry("tar_merger_TEST_dir") / "simple_extract" / "file").file_size(),
+ (FSEntry("tar_merger_TEST_dir") / "simple" / "file").file_size());
+ }
+ } test_simple_tar_merger;
+}
+
diff --git a/paludis/tar_merger_TEST_cleanup.sh b/paludis/tar_merger_TEST_cleanup.sh
new file mode 100755
index 0000000..9368b0d
--- /dev/null
+++ b/paludis/tar_merger_TEST_cleanup.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+if [ -d tar_merger_TEST_dir ] ; then
+ rm -fr tar_merger_TEST_dir
+else
+ true
+fi
+
diff --git a/paludis/tar_merger_TEST_setup.sh b/paludis/tar_merger_TEST_setup.sh
new file mode 100755
index 0000000..ae4d3ca
--- /dev/null
+++ b/paludis/tar_merger_TEST_setup.sh
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+# vim: set ft=sh sw=4 sts=4 et :
+
+mkdir tar_merger_TEST_dir || exit 2
+cd tar_merger_TEST_dir || exit 3
+
+mkdir -p simple simple_extract
+cat <<END > simple/file
+This is the file.
+END
+