Fixes in portage.fetch() for bugs #233303 and #94133:
authorZac Medico <zmedico@gentoo.org>
Thu, 31 Jul 2008 03:46:10 +0000 (03:46 -0000)
committerZac Medico <zmedico@gentoo.org>
Thu, 31 Jul 2008 03:46:10 +0000 (03:46 -0000)
* 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

index 5dae685d6750d1f65084a5bf309162e85b245b48..94f818af05d0172a8cc05eddb7dfe7c12419041a 100644 (file)
@@ -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):