aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvatar Fernando J. Pereda <ferdy@ferdyx.org> 2008-01-03 23:34:43 +0000
committerAvatar Fernando J. Pereda <ferdy@ferdyx.org> 2008-01-03 23:34:43 +0000
commit3102f3c540e1cdac69ffdcf8f9ad37fe60ab312c (patch)
tree4be7035ed19dcedaf9436e863e6c681a72ce74a0
parentd4a6d90e2cbd92e404757d330fa573aacf0d474b (diff)
downloadpaludis-3102f3c540e1cdac69ffdcf8f9ad37fe60ab312c.tar.gz
paludis-3102f3c540e1cdac69ffdcf8f9ad37fe60ab312c.tar.xz
Merge by rename and merge to temporary for robustness.
Also use rename for really fast and cheap dir-over-nothing merges.
-rw-r--r--NEWS4
-rw-r--r--paludis/merger.cc157
-rw-r--r--paludis/merger.hh2
-rw-r--r--paludis/selinux/security_context.cc4
-rw-r--r--paludis/selinux/security_context.hh6
-rw-r--r--paludis/util/fs_entry.cc14
-rw-r--r--paludis/util/fs_entry.hh12
7 files changed, 166 insertions, 33 deletions
diff --git a/NEWS b/NEWS
index bfd6aeb..381845e 100644
--- a/NEWS
+++ b/NEWS
@@ -16,6 +16,10 @@ trunk/:
downloaded for a given package to be installed. Two new metadata keys,
size_of_download_required and size_of_all_distfiles, have been added.
+ * Some merger improvements were implemented: try to merge by renaming a
+ parent directory or merging each file separately. If a copy is needed,
+ merge to a temporary file and then rename to the real destination.
+
0.26.0_alpha4:
* STILL BROKEN, wait for 0.26.0 if you use these: CRAN, Ruby bindings for
dep specs.
diff --git a/paludis/merger.cc b/paludis/merger.cc
index df54264..59ecebb 100644
--- a/paludis/merger.cc
+++ b/paludis/merger.cc
@@ -2,6 +2,7 @@
/*
* Copyright (c) 2007 Ciaran McCreesh
+ * Copyright (c) 2008 Fernando J. Pereda
*
* 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
@@ -619,33 +620,56 @@ Merger::install_file(const FSEntry & src, const FSEntry & dst_dir, const std::st
Log::get_instance()->message(ll_warning, lc_context,
"Merge of '" + stringify(src) + "' to '" + stringify(dst_dir) + "' pre hooks returned non-zero");
- FSCreateCon createcon(MatchPathCon::get_instance()->match(stringify(dst_dir/dst_name), src.permissions()));
- FDHolder input_fd(::open(stringify(src).c_str(), O_RDONLY), false);
- if (-1 == input_fd)
- throw MergerError("Cannot read '" + stringify(src) + "': " + stringify(::strerror(errno)));
+ FSEntry dst(dst_dir / (stringify(dst_name) + "|paludis-midmerge"));
+ FSEntry dst_real(dst_dir / dst_name);
- FDHolder output_fd(::open(stringify(dst_dir / dst_name).c_str(), O_WRONLY | O_CREAT,
- src.permissions()), false);
- if (-1 == output_fd)
- throw MergerError("Cannot write '" + stringify(dst_dir / dst_name) + "': " + stringify(::strerror(errno)));
+ tr1::shared_ptr<const SecurityContext> secctx(MatchPathCon::get_instance()->match(stringify(dst_real), src.permissions()));
+ FSCreateCon createcon(secctx);
+ if (0 != paludis::setfilecon(src, secctx))
+ throw MergerError("Could not set SELinux context on '"
+ + stringify(src) + "': " + stringify(::strerror(errno)));
- if (! _options.no_chown)
- if (0 != ::fchown(output_fd,
- src.owner() == _options.environment->reduced_uid() ? 0 : src.owner(),
- src.group() == _options.environment->reduced_gid() ? 0 : src.group()))
- throw MergerError("Cannot fchown '" + stringify(dst_dir / dst_name) + "': " + stringify(::strerror(errno)));
-
- /* set*id bits */
- if (0 != ::fchmod(output_fd, src.permissions()))
- throw MergerError("Cannot fchmod '" + stringify(dst_dir / dst_name) + "': " + stringify(::strerror(errno)));
-
- char buf[4096];
- ssize_t count;
- while ((count = read(input_fd, buf, 4096)) > 0)
- if (-1 == write(output_fd, buf, count))
- throw MergerError("write failed: " + stringify(::strerror(errno)));
- if (-1 == count)
- throw MergerError("read failed: " + stringify(::strerror(errno)));
+ if (0 == ::rename(stringify(src).c_str(), stringify(dst_real).c_str()))
+ {
+ if (! dst_real.utime())
+ throw MergerError("utime(" + stringify(dst_real) + ", 0) failed: " + stringify(::strerror(errno)));
+ }
+ else
+ {
+ Log::get_instance()->message(ll_debug, lc_context,
+ "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)
+ throw MergerError("Cannot read '" + stringify(src) + "': " + stringify(::strerror(errno)));
+
+ FDHolder output_fd(::open(stringify(dst).c_str(), O_WRONLY | O_CREAT,
+ src.permissions()), false);
+ if (-1 == output_fd)
+ throw MergerError("Cannot write '" + stringify(dst) + "': " + stringify(::strerror(errno)));
+
+ if (! _options.no_chown)
+ if (0 != ::fchown(output_fd,
+ src.owner() == _options.environment->reduced_uid() ? 0 : src.owner(),
+ src.group() == _options.environment->reduced_gid() ? 0 : src.group()))
+ throw MergerError("Cannot fchown '" + stringify(dst) + "': " + stringify(::strerror(errno)));
+
+ /* set*id bits */
+ if (0 != ::fchmod(output_fd, src.permissions()))
+ throw MergerError("Cannot fchmod '" + stringify(dst) + "': " + stringify(::strerror(errno)));
+
+ char buf[4096];
+ ssize_t count;
+ while ((count = read(input_fd, buf, 4096)) > 0)
+ if (-1 == write(output_fd, buf, count))
+ throw MergerError("write failed: " + stringify(::strerror(errno)));
+ if (-1 == count)
+ throw MergerError("read failed: " + stringify(::strerror(errno)));
+
+ if (0 != ::rename(stringify(dst).c_str(), stringify(dst_real).c_str()))
+ throw MergerError(
+ "rename(" + stringify(dst) + ", " + stringify(dst_real) + ") failed: " + stringify(::strerror(errno)));
+ }
if (0 != _options.environment->perform_hook(extend_hook(
Hook("merger_install_file_post")
@@ -656,6 +680,56 @@ Merger::install_file(const FSEntry & src, const FSEntry & dst_dir, const std::st
}
void
+Merger::record_renamed_dir_recursive(const FSEntry & dst)
+{
+ record_install_dir(dst, dst.dirname());
+ for (DirIterator d(dst, false), d_end ; d != d_end ; ++d)
+ {
+ EntryType m(entry_type(*d));
+ switch (m)
+ {
+ case et_sym:
+ record_install_sym(*d, dst);
+ continue;
+
+ case et_file:
+ record_install_file(*d, dst, stringify(d->basename()));
+ continue;
+
+ case et_dir:
+ record_renamed_dir_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) + "'");
+ }
+}
+
+void
+Merger::relabel_dir_recursive(const FSEntry & src, const FSEntry & dst)
+{
+ for (DirIterator d(src, false), d_end ; d != d_end ; ++d)
+ {
+ mode_t mode(d->permissions());
+ tr1::shared_ptr<const SecurityContext> secctx(
+ MatchPathCon::get_instance()->match(stringify(dst / d->basename()), mode));
+ if (0 != paludis::setfilecon(*d, secctx))
+ throw MergerError("Could not set SELinux context on '"
+ + stringify(*d) + "' : " + stringify(::strerror(errno)));
+ if (d->is_directory())
+ relabel_dir_recursive(*d, dst / d->basename());
+ }
+}
+
+void
Merger::install_dir(const FSEntry & src, const FSEntry & dst_dir)
{
Context context("When installing dir '" + stringify(src) + "' to '" + stringify(dst_dir) + "':");
@@ -669,14 +743,31 @@ Merger::install_dir(const FSEntry & src, const FSEntry & dst_dir)
mode_t mode(src.permissions());
FSEntry dst(dst_dir / src.basename());
- FSCreateCon createcon(MatchPathCon::get_instance()->match(stringify(dst), mode));
- dst.mkdir(mode);
- if (! _options.no_chown)
- dst.chown(
- src.owner() == _options.environment->reduced_uid() ? 0 : src.owner(),
- src.group() == _options.environment->reduced_gid() ? 0 : src.group());
- /* pick up set*id bits */
- dst.chmod(src.permissions());
+ tr1::shared_ptr<const SecurityContext> secctx(MatchPathCon::get_instance()->match(stringify(dst), mode));
+ FSCreateCon createcon(secctx);
+ if (0 != paludis::setfilecon(src, secctx))
+ throw MergerError("Could not set SELinux context on '"
+ + stringify(src) + "': " + stringify(::strerror(errno)));
+
+ if (is_selinux_enabled())
+ relabel_dir_recursive(src, dst);
+
+ if (0 == ::rename(stringify(src).c_str(), stringify(dst).c_str()))
+ {
+ record_renamed_dir_recursive(dst);
+ _skip_dir = true;
+ }
+ else
+ {
+ Log::get_instance()->message(ll_debug, lc_context, "rename failed. Falling back to recursive copy.");
+ dst.mkdir(mode);
+ if (! _options.no_chown)
+ dst.chown(
+ src.owner() == _options.environment->reduced_uid() ? 0 : src.owner(),
+ src.group() == _options.environment->reduced_gid() ? 0 : src.group());
+ /* pick up set*id bits */
+ dst.chmod(src.permissions());
+ }
if (0 != _options.environment->perform_hook(extend_hook(
Hook("merger_install_dir_post")
diff --git a/paludis/merger.hh b/paludis/merger.hh
index 5fbf3ee..c396dc1 100644
--- a/paludis/merger.hh
+++ b/paludis/merger.hh
@@ -76,6 +76,8 @@ namespace paludis
MergerOptions _options;
bool _result;
bool _skip_dir;
+ void record_renamed_dir_recursive(const FSEntry &);
+ void relabel_dir_recursive(const FSEntry &, const FSEntry &);
protected:
///\name Basic operations
diff --git a/paludis/selinux/security_context.cc b/paludis/selinux/security_context.cc
index fdb5472..358fd75 100644
--- a/paludis/selinux/security_context.cc
+++ b/paludis/selinux/security_context.cc
@@ -261,3 +261,7 @@ int paludis::setfilecon(const paludis::FSEntry & path, tr1::shared_ptr<const Sec
return libselinux.setfilecon(stringify(path).c_str(), con->_imp->_context);
}
+bool paludis::is_selinux_enabled()
+{
+ return libselinux.is_selinux_enabled() == 1;
+}
diff --git a/paludis/selinux/security_context.hh b/paludis/selinux/security_context.hh
index 5598967..c5f7eb6 100644
--- a/paludis/selinux/security_context.hh
+++ b/paludis/selinux/security_context.hh
@@ -172,6 +172,12 @@ namespace paludis
*/
int setfilecon(const FSEntry & file, tr1::shared_ptr<const SecurityContext> con);
+ /**
+ * Whether SELinux is enabled. Ideally, you are not using this function.
+ *
+ * \ingroup grplibpaludisselinux
+ */
+ bool is_selinux_enabled();
}
#endif
diff --git a/paludis/util/fs_entry.cc b/paludis/util/fs_entry.cc
index 8e04c4c..3c04949 100644
--- a/paludis/util/fs_entry.cc
+++ b/paludis/util/fs_entry.cc
@@ -33,6 +33,7 @@
#include <sys/stat.h>
#include <sys/types.h>
+#include <utime.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
@@ -575,6 +576,19 @@ FSEntry::rmdir()
throw FSError("rmdir '" + _imp->path + "' failed: " + ::strerror(e));
}
+bool
+FSEntry::utime(const struct ::utimbuf * buf)
+{
+ if (0 == ::utime(_imp->path.c_str(), buf))
+ return true;
+
+ int e(errno);
+ if (e == ENOENT)
+ return false;
+ else
+ throw FSError("utime '" + _imp->path + "' failed: " + ::strerror(e));
+}
+
std::string
FSEntry::readlink() const
{
diff --git a/paludis/util/fs_entry.hh b/paludis/util/fs_entry.hh
index 0055928..3d3ae45 100644
--- a/paludis/util/fs_entry.hh
+++ b/paludis/util/fs_entry.hh
@@ -40,6 +40,7 @@
*/
struct stat;
+struct utimbuf;
namespace paludis
{
@@ -339,6 +340,17 @@ namespace paludis
bool rmdir();
/**
+ * Try to set atime and mtime
+ *
+ * \return True, if we succeeded, and false if we don't exist
+ * already.
+ *
+ * \exception FSError If an error other than us already not
+ * existing ocurrs.
+ */
+ bool utime(const struct ::utimbuf * buf = 0);
+
+ /**
* Change our permissions.
*
* \exception FSError If the chown failed.