summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--pym/portage/__init__.py50
1 files changed, 48 insertions, 2 deletions
diff --git a/pym/portage/__init__.py b/pym/portage/__init__.py
index 5dae685d6..94f818af0 100644
--- a/pym/portage/__init__.py
+++ b/pym/portage/__init__.py
@@ -3184,7 +3184,8 @@ def _spawn_fetch(settings, args, **kwargs):
con = con.replace(settings["PORTAGE_T"], settings["PORTAGE_FETCH_T"])
selinux.setexec(con)
# bash is an allowed entrypoint, while most binaries are not
- args = [BASH_BINARY, "-c", "exec \"$@\"", args[0]] + args
+ if args[0] != BASH_BINARY:
+ args = [BASH_BINARY, "-c", "exec \"$@\"", args[0]] + args
rval = portage.process.spawn(args,
env=dict(settings.iteritems()), **kwargs)
@@ -3195,6 +3196,35 @@ def _spawn_fetch(settings, args, **kwargs):
return rval
+_userpriv_test_write_file_cache = {}
+_userpriv_test_write_cmd_script = "> %(file_path)s ; rval=$? ; " + \
+ "rm -f %(file_path)s ; exit $rval"
+
+def _userpriv_test_write_file(settings, file_path):
+ """
+ Drop privileges and try to open a file for writing. The file may or
+ may not exist, and the parent directory is assumed to exist. The file
+ is removed before returning.
+
+ @param settings: A config instance which is passed to _spawn_fetch()
+ @param file_path: A file path to open and write.
+ @return: True if write succeeds, False otherwise.
+ """
+
+ global _userpriv_test_write_file_cache, _userpriv_test_write_cmd_script
+ rval = _userpriv_test_write_file_cache.get(file_path)
+ if rval is not None:
+ return rval
+
+ args = [BASH_BINARY, "-c", _userpriv_test_write_cmd_script % \
+ {"file_path" : _shell_quote(file_path)}]
+
+ returncode = _spawn_fetch(settings, args)
+
+ rval = returncode == os.EX_OK
+ _userpriv_test_write_file_cache[file_path] = rval
+ return rval
+
def _checksum_failure_temp_file(distdir, basename):
"""
First try to find a duplicate temp file with the same checksum and return
@@ -3302,6 +3332,11 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0, locks_in_subdir=".locks",
features = mysettings.features
restrict = mysettings.get("PORTAGE_RESTRICT","").split()
+
+ from portage.data import secpass
+ userfetch = secpass >= 2 and "userfetch" in features
+ userpriv = secpass >= 2 and "userpriv" in features
+
# 'nomirror' is bad/negative logic. You Restrict mirroring, not no-mirroring.
if "mirror" in restrict or \
"nomirror" in restrict:
@@ -3501,7 +3536,8 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0, locks_in_subdir=".locks",
if not mysettings.get(var_name, None):
can_fetch = False
- if can_fetch:
+ if can_fetch and not fetch_to_ro:
+ global _userpriv_test_write_file_cache
dirmode = 02070
filemode = 060
modemask = 02
@@ -3519,7 +3555,17 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0, locks_in_subdir=".locks",
for x in distdir_dirs:
mydir = os.path.join(mysettings["DISTDIR"], x)
+ write_test_file = os.path.join(
+ mydir, ".__portage_test_write__")
+
+ if os.path.isdir(mydir):
+ if not (userfetch or userpriv):
+ continue
+ if _userpriv_test_write_file(mysettings, write_test_file):
+ continue
+
if portage.util.ensure_dirs(mydir, gid=dir_gid, mode=dirmode, mask=modemask):
+ _userpriv_test_write_file_cache.pop(write_test_file, None)
writemsg("Adjusting permissions recursively: '%s'\n" % mydir,
noiselevel=-1)
def onerror(e):