aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvatar Fernando J. Pereda <ferdy@ferdyx.org> 2008-03-07 15:23:25 +0000
committerAvatar Fernando J. Pereda <ferdy@ferdyx.org> 2008-03-07 15:23:25 +0000
commit661dc16397d97014aca5f1584af069880d29f1b2 (patch)
tree6d979c16dbccb9dff5169db42b6cc42b15030fcc
parentcf5089fb6c74024ff6203430b6fb2feb8cd03e33 (diff)
downloadpaludis-661dc16397d97014aca5f1584af069880d29f1b2.tar.gz
paludis-661dc16397d97014aca5f1584af069880d29f1b2.tar.xz
Merge hardlinks as sanely as possible
-rw-r--r--NEWS3
-rw-r--r--paludis/hashed_containers.cc7
-rw-r--r--paludis/hashed_containers.hh28
-rw-r--r--paludis/merger.cc66
-rw-r--r--paludis/merger.hh5
-rw-r--r--paludis/merger.se1
-rw-r--r--paludis/ndbam_merger.cc4
-rw-r--r--paludis/repositories/e/vdb_merger.cc4
-rw-r--r--paludis/util/fs_entry.cc10
-rw-r--r--paludis/util/fs_entry.hh7
10 files changed, 128 insertions, 7 deletions
diff --git a/NEWS b/NEWS
index be73f02..b5525a3 100644
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,9 @@ News for Paludis
This file lists the major changes between versions. For a more detailed list
of every change, see the ChangeLog.
+trunk/:
+ * The merger is able to detect hardlinks and tries to merge them as such.
+
0.26.0_alpha11:
* paludis --owner now shows matching files when --full-match is not
specified.
diff --git a/paludis/hashed_containers.cc b/paludis/hashed_containers.cc
index 7e104da..a7f96de 100644
--- a/paludis/hashed_containers.cc
+++ b/paludis/hashed_containers.cc
@@ -99,6 +99,13 @@ CRCHash<std::pair<QualifiedPackageName, VersionSpec> >::operator() (
}
std::size_t
+CRCHash<std::pair<dev_t, ino_t> >::operator() (
+ const std::pair<dev_t, ino_t> & val) const
+{
+ return val.first ^ val.second;
+}
+
+std::size_t
CRCHash<PackageID>::operator() (const PackageID & val) const
{
return
diff --git a/paludis/hashed_containers.hh b/paludis/hashed_containers.hh
index 076562d..78f2a90 100644
--- a/paludis/hashed_containers.hh
+++ b/paludis/hashed_containers.hh
@@ -337,6 +337,34 @@ namespace paludis
};
/**
+ * Hash, for a dev_t + ino_t pair.
+ *
+ * \ingroup g_data_structures
+ */
+ template <>
+ class PALUDIS_VISIBLE CRCHash<std::pair<dev_t, ino_t> > :
+ public std::unary_function<const std::pair<dev_t, ino_t>, std::size_t>,
+ protected hashed_containers_internals::CRCHashBase
+ {
+ public:
+ /// Hash function
+ std::size_t operator() (const std::pair<dev_t, ino_t> & val) const;
+
+#if (! defined(PALUDIS_HASH_IS_STD_TR1_UNORDERED)) && (! defined(PALUDIS_HASH_IS_GNU_CXX_HASH))
+ enum
+ {
+ min_buckets = 32,
+ bucket_size = 4
+ };
+
+ bool operator() (const std::pair<dev_t, ino_t> & i1, const std::pair<dev_t, ino_t> & i2) const
+ {
+ return i1 < i2;
+ }
+#endif
+ };
+
+ /**
* Hash, for a shared pointer.
*
* \ingroup g_data_structures
diff --git a/paludis/merger.cc b/paludis/merger.cc
index 31302a1..9abaa18 100644
--- a/paludis/merger.cc
+++ b/paludis/merger.cc
@@ -650,6 +650,8 @@ Merger::install_file(const FSEntry & src, const FSEntry & dst_dir, const std::st
dest_gid = new_gid == 0 ? 0 : dest_gid;
}
+ bool do_copy(false);
+
if (0 == ::rename(stringify(src).c_str(), stringify(dst_real).c_str()))
{
result += msi_rename;
@@ -658,11 +660,32 @@ Merger::install_file(const FSEntry & src, const FSEntry & dst_dir, const std::st
/* set*id bits get partially clobbered on a rename on linux */
dst_real.chmod(src_perms);
+
+ _merged_ids.insert(make_pair(src.lowlevel_id(), stringify(dst_real)));
}
else
{
+ do_copy = true;
+ std::pair<MergedMap::const_iterator, MergedMap::const_iterator> ii(
+ _merged_ids.equal_range(src.lowlevel_id()));
+ for (MergedMap::const_iterator i = ii.first ; i != ii.second ; ++i)
+ {
+ if (0 == ::link(i->second.c_str(), stringify(dst_real).c_str()))
+ {
+ do_copy = false;
+ result += msi_as_hardlink;
+ break;
+ }
+ Log::get_instance()->message(ll_debug, lc_context,
+ "link(" + i->second + ", " + stringify(dst_real) + ") failed: "
+ + stringify(::strerror(errno)));
+ }
+ }
+
+ if (do_copy)
+ {
Log::get_instance()->message(ll_debug, lc_context,
- "link failed: " + stringify(::strerror(errno))
+ "rename/link failed: " + stringify(::strerror(errno))
+ ". Falling back to regular read/write copy");
FDHolder input_fd(::open(stringify(src).c_str(), O_RDONLY), false);
if (-1 == input_fd)
@@ -691,6 +714,8 @@ Merger::install_file(const FSEntry & src, const FSEntry & dst_dir, const std::st
if (0 != ::rename(stringify(dst).c_str(), stringify(dst_real).c_str()))
throw MergerError(
"rename(" + stringify(dst) + ", " + stringify(dst_real) + ") failed: " + stringify(::strerror(errno)));
+
+ _merged_ids.insert(make_pair(src.lowlevel_id(), stringify(dst_real)));
}
if (0 != _params[k::environment()]->perform_hook(extend_hook(
@@ -764,10 +789,12 @@ Merger::record_renamed_dir_recursive(const FSEntry & dst)
case et_sym:
rewrite_symlink_as_needed(*d, dst);
record_install_sym(*d, dst, MergeStatusFlags() + msi_parent_rename);
+ _merged_ids.insert(make_pair(d->lowlevel_id(), stringify(*d)));
continue;
case et_file:
record_install_file(*d, dst, stringify(d->basename()), MergeStatusFlags() + msi_parent_rename);
+ _merged_ids.insert(make_pair(d->lowlevel_id(), stringify(*d)));
continue;
case et_dir:
@@ -887,10 +914,12 @@ Merger::install_sym(const FSEntry & src, const FSEntry & dst_dir)
MergeStatusFlags result;
+ FSEntry dst(dst_dir / src.basename());
+
if (0 != _params[k::environment()]->perform_hook(extend_hook(
Hook("merger_install_sym_pre")
("INSTALL_SOURCE", stringify(src))
- ("INSTALL_DESTINATION", stringify(dst_dir / src.basename())))).max_exit_status)
+ ("INSTALL_DESTINATION", stringify(dst)))).max_exit_status)
Log::get_instance()->message(ll_warning, lc_context,
"Merge of '" + stringify(src) + "' to '" + stringify(dst_dir) + "' pre hooks returned non-zero");
@@ -900,27 +929,50 @@ Merger::install_sym(const FSEntry & src, const FSEntry & dst_dir)
if (0 != (src.permissions() & (S_ISVTX | S_ISUID | S_ISGID)))
result += msi_setid_bits;
+ bool do_sym(false);
+
if (symlink_needs_rewriting(src))
rewrite_symlink_as_needed(src, dst_dir);
else
{
- FSCreateCon createcon(MatchPathCon::get_instance()->match(stringify(dst_dir / src.basename()), S_IFLNK));
- if (0 != ::symlink(stringify(src.readlink()).c_str(), stringify(dst_dir / src.basename()).c_str()))
- throw MergerError("Couldn't create symlink at '" + stringify(dst_dir / src.basename()) + "': "
+ do_sym = true;
+ FSCreateCon createcon(MatchPathCon::get_instance()->match(stringify(dst), S_IFLNK));
+ std::pair<MergedMap::const_iterator, MergedMap::const_iterator> ii(
+ _merged_ids.equal_range(src.lowlevel_id()));
+ for (MergedMap::const_iterator i = ii.first ; i != ii.second ; ++i)
+ {
+ if (0 == ::link(i->second.c_str(), stringify(dst).c_str()))
+ {
+ do_sym = false;
+ result += msi_as_hardlink;
+ break;
+ }
+ Log::get_instance()->message(ll_debug, lc_context,
+ "link(" + i->second + ", " + stringify(dst) + ") failed: "
+ + stringify(::strerror(errno)));
+ }
+ }
+
+ if (do_sym)
+ {
+ FSCreateCon createcon(MatchPathCon::get_instance()->match(stringify(dst), S_IFLNK));
+ if (0 != ::symlink(stringify(src.readlink()).c_str(), stringify(dst).c_str()))
+ throw MergerError("Couldn't create symlink at '" + stringify(dst) + "': "
+ stringify(::strerror(errno)));
+ _merged_ids.insert(make_pair(src.lowlevel_id(), stringify(dst)));
}
if (! _params[k::no_chown()])
{
if (src.owner() != dest_uid || src.group() != dest_gid)
result += msi_fixed_ownership;
- FSEntry(dst_dir / src.basename()).lchown(dest_uid, dest_gid);
+ dst.lchown(dest_uid, dest_gid);
}
if (0 != _params[k::environment()]->perform_hook(extend_hook(
Hook("merger_install_sym_post")
("INSTALL_SOURCE", stringify(src))
- ("INSTALL_DESTINATION", stringify(dst_dir / src.basename())))).max_exit_status)
+ ("INSTALL_DESTINATION", stringify(dst)))).max_exit_status)
Log::get_instance()->message(ll_warning, lc_context,
"Merge of '" + stringify(src) + "' to '" + stringify(dst_dir) + "' post hooks returned non-zero");
diff --git a/paludis/merger.hh b/paludis/merger.hh
index d1892fd..2e0125b 100644
--- a/paludis/merger.hh
+++ b/paludis/merger.hh
@@ -27,6 +27,9 @@
#include <paludis/util/kc.hh>
#include <paludis/merger_entry_type.hh>
#include <iosfwd>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <paludis/hashed_containers.hh>
/** \file
* Declarations for the Merger class, which can be used by Repository
@@ -73,6 +76,8 @@ namespace paludis
class PALUDIS_VISIBLE Merger
{
private:
+ typedef MakeHashedMultiMap<std::pair<dev_t, ino_t>, std::string>::Type MergedMap;
+ MergedMap _merged_ids;
MergerParams _params;
bool _result;
bool _skip_dir;
diff --git a/paludis/merger.se b/paludis/merger.se
index 8bfd247..9f7b904 100644
--- a/paludis/merger.se
+++ b/paludis/merger.se
@@ -11,6 +11,7 @@ make_enum_MergeStatusFlag()
key msi_used_existing "We used the existing entry (e.g. dir over dir)"
key msi_fixed_ownership "We fixed owner or group from the reduced id"
key msi_setid_bits "The source file had set*id bits"
+ key msi_as_hardlink "We detected a hardlink and merged it as such"
doxygen_comment << "END"
/**
diff --git a/paludis/ndbam_merger.cc b/paludis/ndbam_merger.cc
index af92eba..39f9a5f 100644
--- a/paludis/ndbam_merger.cc
+++ b/paludis/ndbam_merger.cc
@@ -291,6 +291,10 @@ NDBAMMerger::make_arrows(const MergeStatusFlags & flags) const
result[1] = '-';
continue;
+ case msi_as_hardlink:
+ result[1] = '&';
+ continue;
+
case msi_fixed_ownership:
result[2] = '~';
continue;
diff --git a/paludis/repositories/e/vdb_merger.cc b/paludis/repositories/e/vdb_merger.cc
index b2af472..9c10cc7 100644
--- a/paludis/repositories/e/vdb_merger.cc
+++ b/paludis/repositories/e/vdb_merger.cc
@@ -322,6 +322,10 @@ VDBMerger::make_arrows(const MergeStatusFlags & flags) const
result[1] = '-';
continue;
+ case msi_as_hardlink:
+ result[1] = '&';
+ continue;
+
case msi_fixed_ownership:
result[2] = '~';
continue;
diff --git a/paludis/util/fs_entry.cc b/paludis/util/fs_entry.cc
index b03e902..a83f712 100644
--- a/paludis/util/fs_entry.cc
+++ b/paludis/util/fs_entry.cc
@@ -693,3 +693,13 @@ FSEntry::rename(const FSEntry & new_name)
::strerror(errno));
}
+std::pair<dev_t, ino_t>
+FSEntry::lowlevel_id() const
+{
+ _stat();
+
+ if (! _imp->exists)
+ throw FSError("Filesystem entry '" + _imp->path + "' does not exist");
+
+ return std::make_pair(_imp->stat_info->st_dev, _imp->stat_info->st_ino);
+}
diff --git a/paludis/util/fs_entry.hh b/paludis/util/fs_entry.hh
index 87b9bd0..9b38d26 100644
--- a/paludis/util/fs_entry.hh
+++ b/paludis/util/fs_entry.hh
@@ -29,6 +29,7 @@
#include <iosfwd>
#include <paludis/util/tr1_memory.hh>
#include <sys/types.h>
+#include <sys/stat.h>
/** \file
* Declarations for FSEntry.
@@ -294,6 +295,12 @@ namespace paludis
static FSEntry cwd()
PALUDIS_ATTRIBUTE((warn_unused_result));
+ /**
+ * Return an unique low-level id for this entry
+ */
+ std::pair<dev_t, ino_t> lowlevel_id() const
+ PALUDIS_ATTRIBUTE((warn_unused_result));
+
///\}
///\name Filesystem operations