aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2006-06-28 20:19:14 +0000
committerAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2006-06-28 20:19:14 +0000
commita7c0c9d36e1989a52b7e4bef7827c1c03a1a9ad2 (patch)
tree02ec03ffa87586cb42e6b8cc71815b6b1a387c8b
parent4aa5fcbe170fcb488e1c6d321906534f038f5520 (diff)
downloadpaludis-a7c0c9d36e1989a52b7e4bef7827c1c03a1a9ad2.tar.gz
paludis-a7c0c9d36e1989a52b7e4bef7827c1c03a1a9ad2.tar.xz
Fix set*id handling. Re-enable config protect support
-rw-r--r--ebuild/builtin_merge.bash3
-rw-r--r--ebuild/utils/merge.cc143
2 files changed, 128 insertions, 18 deletions
diff --git a/ebuild/builtin_merge.bash b/ebuild/builtin_merge.bash
index d9e5b07..27732ae 100644
--- a/ebuild/builtin_merge.bash
+++ b/ebuild/builtin_merge.bash
@@ -45,6 +45,9 @@ builtin_merge()
CONFIG_PROTECT_MASK=${PALUDIS_EBUILD_OVERRIDE_CONFIG_PROTECT_MASK}
fi
+ export CONFIG_PROTECT="${CONFIG_PROTECT}"
+ export CONFIG_PROTECT_MASK="${CONFIG_PROTECT_MASK}"
+
[[ -f "${EBUILD}" ]] && cp "${EBUILD}" ${dbdir}/
local x e ee=
for e in ${ECLASSDIRS:-${ECLASSDIR}} ; do
diff --git a/ebuild/utils/merge.cc b/ebuild/utils/merge.cc
index c3d07d0..9ee9f18 100644
--- a/ebuild/utils/merge.cc
+++ b/ebuild/utils/merge.cc
@@ -24,6 +24,7 @@
#include <paludis/util/stringify.hh>
#include <paludis/util/strip.hh>
#include <paludis/util/system.hh>
+#include <paludis/util/tokeniser.hh>
#include <algorithm>
#include <fstream>
@@ -31,10 +32,12 @@
#include <iostream>
#include <iterator>
#include <string>
+#include <vector>
#include <cstdlib>
#include <errno.h>
#include <fcntl.h>
+#include <fnmatch.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -44,11 +47,96 @@ using std::cerr;
using std::endl;
using std::ifstream;
using std::ofstream;
+using std::istreambuf_iterator;
+using std::ostreambuf_iterator;
+
namespace
{
int exit_status;
+ struct Failure
+ {
+ std::string message;
+
+ Failure(const std::string & m) :
+ message(m)
+ {
+ };
+ };
+
+ std::vector<std::string>
+ get_config_var(const std::string & var)
+ {
+ std::vector<std::string> result;
+ WhitespaceTokeniser::get_instance()->tokenise(getenv_with_default(var, ""),
+ std::back_inserter(result));
+ return result;
+ }
+
+ bool
+ is_config_protected(const FSEntry & root, const FSEntry & file)
+ {
+ static std::vector<std::string> cfg_pro(get_config_var("CONFIG_PROTECT")),
+ cfg_pro_mask(get_config_var("CONFIG_PROTECT_MASK"));
+
+ std::string file_str(stringify(file)), root_str(stringify(root));
+ if (0 != file_str.compare(0, root_str.length(), root_str))
+ throw Failure("is_config_protected confused: '" + root_str + "' '" + file_str + "'");
+ file_str.erase(0, root_str.length());
+ if (file_str.empty())
+ file_str = "/";
+
+ bool result(false);
+ for (std::vector<std::string>::const_iterator c(cfg_pro.begin()),
+ c_end(cfg_pro.end()) ; c != c_end && ! result ; ++c)
+ if (0 == fnmatch((*c + "/*").c_str(), file_str.c_str(), 0))
+ result = true;
+
+ for (std::vector<std::string>::const_iterator c(cfg_pro_mask.begin()),
+ c_end(cfg_pro_mask.end()) ; c != c_end && result ; ++c)
+ if (0 == fnmatch((*c + "/*").c_str(), file_str.c_str(), 0))
+ result = false;
+
+ return result;
+ }
+
+ FSEntry
+ make_config_protect_name(const FSEntry & name, const FSEntry & file_to_install)
+ {
+ int n(0);
+
+ PStream our_md5command(getenv_or_error("PALUDIS_EBUILD_DIR") +
+ "/digests/domd5 '" + stringify(file_to_install) + "'");
+ std::string our_md5((istreambuf_iterator<char>(our_md5command)),
+ istreambuf_iterator<char>());
+ if (0 != our_md5command.exit_status())
+ throw Failure("Could not md5sum '" + stringify(name) + "'");
+
+ FSEntry result(name);
+ while (true)
+ {
+ if (! result.exists())
+ return result;
+ else if (result.is_regular_file())
+ {
+ PStream other_md5command(getenv_or_error("PALUDIS_EBUILD_DIR") +
+ "/digests/domd5 '" + stringify(result) + "'");
+ std::string other_md5((istreambuf_iterator<char>(other_md5command)),
+ istreambuf_iterator<char>());
+ if (0 != other_md5command.exit_status())
+ throw Failure("Could not md5sum '" + stringify(result) + "'");
+ if (other_md5 == our_md5)
+ return result;
+ }
+
+ std::stringstream s;
+ s << std::setw(4) << std::setfill('0') << std::right << n++;
+ result = FSEntry(stringify(name.dirname() / ("._cfg" + s.str() + "_"
+ + name.basename())));
+ }
+ }
+
/**
* Output stream buffer class that's opened via an FD, so that we can avoid race
* conditions that would be present on the non-Unix-specific standard file
@@ -148,16 +236,6 @@ namespace
}
};
- struct Failure
- {
- std::string message;
-
- Failure(const std::string & m) :
- message(m)
- {
- };
- };
-
void
do_dir(const FSEntry & root, const FSEntry & src_dir,
const FSEntry & dst_dir, ofstream * const contents)
@@ -185,6 +263,8 @@ namespace
mode_t mode(src_dir.permissions());
FSEntry(dst_dir).mkdir(mode);
FSEntry(stringify(dst_dir)).chown(src_dir.owner(), src_dir.group());
+ /* the chmod is needed to pick up fancy set*id bits */
+ FSEntry(stringify(dst_dir)).chmod(src_dir.permissions());
}
*contents << "dir " << dst_dir_str.substr(root_str.length()) << "/" <<
@@ -198,36 +278,58 @@ namespace
Context context("Installing object in root '" + stringify(root) + "' from '"
+ stringify(src) + "' to '" + stringify(dst) + "':");
- using std::istreambuf_iterator;
- using std::ostreambuf_iterator;
-
std::string root_str(stringify(root)), dst_dir_str(stringify(dst.dirname()));
if (0 != dst_dir_str.compare(0, root_str.length(), root_str))
throw Failure("do_obj confused: '" + root_str + "' '" + dst_dir_str + "'");
cout << ">>> " << std::setw(5) << std::left << "[obj]" << " " <<
- dst_dir_str.substr(root_str.length()) << "/" << dst.basename() << endl;
+ dst_dir_str.substr(root_str.length()) << "/" << dst.basename();
if (dst.is_directory())
throw Failure("Cannot overwrite directory '" + stringify(dst) + "' with a file");
else
{
+ FSEntry real_dst(dst);
+
ifstream input_file(stringify(src).c_str());
if (! input_file)
throw Failure("Cannot read '" + stringify(src) + "'");
if (dst.exists())
- FSEntry(dst).unlink();
+ {
+ if (is_config_protected(root, dst))
+ {
+ real_dst = make_config_protect_name(dst, src);
+ if (dst != real_dst)
+ cout << " -> " << real_dst << endl;
+ else
+ cout << endl;
+ }
+ else
+ {
+ FSEntry(dst).unlink();
+ cout << endl;
+ }
+ }
+ else
+ cout << endl;
/* FDHolder must be destroyed before we do the md5 thing, or the
* disk write may not have synced. */
{
- FDHolder fd(::open(stringify(dst).c_str(), O_WRONLY | O_CREAT, src.permissions()));
+ FDHolder fd(::open(stringify(real_dst).c_str(), O_WRONLY | O_CREAT, src.permissions()));
if (-1 == fd)
- throw Failure("Cannot open '" + stringify(dst) + "' for write");
+ throw Failure("Cannot open '" + stringify(real_dst) + "' for write");
if (0 != ::fchown(fd, src.owner(), src.group()))
- throw Failure("Cannot fchown '" + stringify(dst) + "': " + stringify(::strerror(errno)));
+ throw Failure("Cannot fchown '" + stringify(real_dst) + "': " +
+ stringify(::strerror(errno)));
+
+ /* the chmod is needed for set*id bits, which are dropped by
+ * umask in the ::open */
+ if (0 != ::fchmod(fd, src.permissions()))
+ throw Failure("Cannot fchmod '" + stringify(real_dst) + "': " +
+ stringify(::strerror(errno)));
FDOutputStream output_file(fd);
if (! output_file)
@@ -338,6 +440,11 @@ main(int argc, char * argv[])
else
throw Failure("bad value for log level");
+ Log::get_instance()->message(ll_debug, "CONFIG_PROTECT is " + getenv_with_default(
+ "CONFIG_PROTECT", "(unset)"));
+ Log::get_instance()->message(ll_debug, "CONFIG_PROTECT_MASK is " + getenv_with_default(
+ "CONFIG_PROTECT_MASK", "(unset)"));
+
FSEntry src(argv[1]), dst(argv[2]), contents(argv[3]);
if (! ((src = src.realpath())).is_directory())