From f1d0edfcfa19ad7b14201be299fe2fe33d737eb0 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Thu, 31 Jul 2008 03:46:10 +0000 Subject: Fixes in portage.fetch() for bugs #233303 and #94133: * Totally skip $DISTDIR creation if the fetch_to_ro feature is enabled. * Don't touch $DISTDIR permissions unless unless usepriv and/or userfetch are enabled. * When usepriv and/or userfetch are enabled, test whether or not a process that has dropped privileges is able to create a file in the directory, and only adjust permissions if the test fails. * Completely svn path=/main/trunk/; revision=11290 --- pym/portage/__init__.py | 50 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) (limited to 'pym') diff --git a/pym/portage/__init__.py b/pym/portage/__init__.py index 5dae685d..94f818af 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): -- cgit v1.2.3-65-gdbad