commit:     1364fcd89384c9f60e6d72d7057dc00d8caba175
Author:     Zac Medico <zmedico <AT> gentoo <DOT> org>
AuthorDate: Mon Sep 29 18:18:42 2014 +0000
Commit:     Zac Medico <zmedico <AT> gentoo <DOT> org>
CommitDate: Wed Oct 22 23:11:58 2014 +0000
URL:        
http://sources.gentoo.org/gitweb/?p=proj/portage.git;a=commit;h=1364fcd8

Support unprivileged mode for bug #433453.

This takes the existing unprivileged prefix support and enables it to
work when EPREFIX is empty. This "unprivileged" mode is automatically
enabled if the current user is not root, but has write access to the
EROOT directory (not due to the 0002 bit). For use in conditional logic
(for example, see doebuild.py diff), "unprivileged" is automatically
added to the FEATURES variable.

X-Gentoo-Bug: 433453
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=433453

---
 pym/portage/const.py                    |  1 +
 pym/portage/data.py                     | 95 ++++++++++++++++++++++-----------
 pym/portage/package/ebuild/config.py    | 21 +++++---
 pym/portage/package/ebuild/doebuild.py  |  4 +-
 pym/portage/tests/emerge/test_simple.py |  2 +-
 5 files changed, 83 insertions(+), 40 deletions(-)

diff --git a/pym/portage/const.py b/pym/portage/const.py
index acb90f9..d472075 100644
--- a/pym/portage/const.py
+++ b/pym/portage/const.py
@@ -189,6 +189,7 @@ SUPPORTED_FEATURES       = frozenset([
        "unmerge-backup",
        "unmerge-logs",
        "unmerge-orphans",
+       "unprivileged",
        "userfetch",
        "userpriv",
        "usersandbox",

diff --git a/pym/portage/data.py b/pym/portage/data.py
index 54e3a8d..3d91e48 100644
--- a/pym/portage/data.py
+++ b/pym/portage/data.py
@@ -59,6 +59,15 @@ def portage_group_warning():
 # If the "wheel" group does not exist then wheelgid falls back to 0.
 # If the "portage" group does not exist then portage_uid falls back to 
wheelgid.
 
+# If the current user is not root, but has write access to the
+# EROOT directory (not due to the 0002 bit), then use "unprivileged"
+# mode which sets secpass = 2 and uses the UID and GID of the EROOT
+# directory to generate default PORTAGE_INST_GID, PORTAGE_INST_UID,
+# PORTAGE_USERNAME, and PORTAGE_GRPNAME settings.
+def _unprivileged_mode(eroot, eroot_st):
+       return os.getuid() != 0 and os.access(eroot, os.W_OK) and \
+               not eroot_st.st_mode & 0o0002
+
 uid = os.getuid()
 wheelgid = 0
 try:
@@ -77,13 +86,33 @@ def _get_global(k):
        if k in _initialized_globals:
                return globals()[k]
 
-       if k in ('portage_gid', 'portage_uid', 'secpass'):
-               global portage_gid, portage_uid, secpass
-               secpass = 0
+       if k == 'secpass':
+
+               unprivileged = False
+               if hasattr(portage, 'settings'):
+                       unprivileged = "unprivileged" in 
portage.settings.features
+               else:
+                       # The config class has equivalent code, but we also 
need to
+                       # do it here if _disable_legacy_globals() has been 
called.
+                       eroot = os.path.join(os.environ.get('ROOT', os.sep),
+                               portage.const.EPREFIX.lstrip(os.sep))
+                       try:
+                               eroot_st = os.stat(eroot)
+                       except OSError:
+                               pass
+                       else:
+                               unprivileged = _unprivileged_mode(eroot, 
eroot_st)
+
+               v = 0
                if uid == 0:
-                       secpass = 2
-               elif portage.const.EPREFIX:
-                       secpass = 2
+                       v = 2
+               elif unprivileged:
+                       v = 2
+               elif portage_gid in os.getgroups():
+                       v = 1
+
+       elif k in ('portage_gid', 'portage_uid'):
+
                #Discover the uid and gid of the portage user/group
                keyerror = False
                try:
@@ -98,9 +127,6 @@ def _get_global(k):
                        keyerror = True
                        portage_gid = 0
 
-               if secpass < 1 and portage_gid in os.getgroups():
-                       secpass = 1
-
                # Suppress this error message if both PORTAGE_GRPNAME and
                # PORTAGE_USERNAME are set to "root", for things like
                # Android (see bug #454060).
@@ -118,16 +144,15 @@ def _get_global(k):
                                noiselevel=-1)
                        portage_group_warning()
 
+               globals()['portage_gid'] = portage_gid
                _initialized_globals.add('portage_gid')
+               globals()['portage_uid'] = portage_uid
                _initialized_globals.add('portage_uid')
-               _initialized_globals.add('secpass')
 
                if k == 'portage_gid':
                        return portage_gid
                elif k == 'portage_uid':
                        return portage_uid
-               elif k == 'secpass':
-                       return secpass
                else:
                        raise AssertionError('unknown name: %s' % k)
 
@@ -178,11 +203,9 @@ def _get_global(k):
                        v = os.environ[env_key]
                elif hasattr(portage, 'settings'):
                        v = portage.settings.get(env_key)
-               elif portage.const.EPREFIX:
-                       # For prefix environments, default to the UID and GID of
-                       # the top-level EROOT directory. The config class has
-                       # equivalent code, but we also need to do it here if
-                       # _disable_legacy_globals() has been called.
+               else:
+                       # The config class has equivalent code, but we also 
need to
+                       # do it here if _disable_legacy_globals() has been 
called.
                        eroot = os.path.join(os.environ.get('ROOT', os.sep),
                                portage.const.EPREFIX.lstrip(os.sep))
                        try:
@@ -190,20 +213,21 @@ def _get_global(k):
                        except OSError:
                                pass
                        else:
-                               if k == '_portage_grpname':
-                                       try:
-                                               grp_struct = 
grp.getgrgid(eroot_st.st_gid)
-                                       except KeyError:
-                                               pass
+                               if _unprivileged_mode(eroot, eroot_st):
+                                       if k == '_portage_grpname':
+                                               try:
+                                                       grp_struct = 
grp.getgrgid(eroot_st.st_gid)
+                                               except KeyError:
+                                                       pass
+                                               else:
+                                                       v = grp_struct.gr_name
                                        else:
-                                               v = grp_struct.gr_name
-                               else:
-                                       try:
-                                               pwd_struct = 
pwd.getpwuid(eroot_st.st_uid)
-                                       except KeyError:
-                                               pass
-                                       else:
-                                               v = pwd_struct.pw_name
+                                               try:
+                                                       pwd_struct = 
pwd.getpwuid(eroot_st.st_uid)
+                                               except KeyError:
+                                                       pass
+                                               else:
+                                                       v = pwd_struct.pw_name
 
                if v is None:
                        v = 'portage'
@@ -254,3 +278,14 @@ def _init(settings):
                        v = portage._native_string(v)
                globals()['_portage_username'] = v
                _initialized_globals.add('_portage_username')
+
+       if 'secpass' not in _initialized_globals:
+               v = 0
+               if uid == 0:
+                       v = 2
+               elif "unprivileged" in settings.features:
+                       v = 2
+               elif portage_gid in os.getgroups():
+                       v = 1
+               globals()['secpass'] = v
+               _initialized_globals.add('secpass')

diff --git a/pym/portage/package/ebuild/config.py 
b/pym/portage/package/ebuild/config.py
index 264ed8e..1087443 100644
--- a/pym/portage/package/ebuild/config.py
+++ b/pym/portage/package/ebuild/config.py
@@ -834,14 +834,16 @@ class config(object):
                                "PORTAGE_INST_UID": "0",
                        }
 
-                       if eprefix:
-                               # For prefix environments, default to the UID 
and GID of
-                               # the top-level EROOT directory.
-                               try:
-                                       eroot_st = os.stat(eroot)
-                               except OSError:
-                                       pass
-                               else:
+                       unprivileged = False
+                       try:
+                               eroot_st = os.stat(eroot)
+                       except OSError:
+                               pass
+                       else:
+
+                               if portage.data._unprivileged_mode(eroot, 
eroot_st):
+                                       unprivileged = True
+
                                        default_inst_ids["PORTAGE_INST_GID"] = 
str(eroot_st.st_gid)
                                        default_inst_ids["PORTAGE_INST_UID"] = 
str(eroot_st.st_uid)
 
@@ -880,6 +882,9 @@ class config(object):
                        # initialize self.features
                        self.regenerate()
 
+                       if unprivileged:
+                               self.features.add('unprivileged')
+
                        if bsd_chflags:
                                self.features.add('chflags')
 

diff --git a/pym/portage/package/ebuild/doebuild.py 
b/pym/portage/package/ebuild/doebuild.py
index d3e3f5a..9516173 100644
--- a/pym/portage/package/ebuild/doebuild.py
+++ b/pym/portage/package/ebuild/doebuild.py
@@ -200,7 +200,9 @@ def _doebuild_path(settings, eapi=None):
        if "xattr" in settings.features:
                path.append(os.path.join(portage_bin_path, "ebuild-helpers", 
"xattr"))
 
-       if eprefix and uid != 0 and "fakeroot" not in settings.features:
+       if uid != 0 and \
+               "unprivileged" in settings.features and \
+               "fakeroot" not in settings.features:
                path.append(os.path.join(portage_bin_path,
                        "ebuild-helpers", "unprivileged"))
 

diff --git a/pym/portage/tests/emerge/test_simple.py 
b/pym/portage/tests/emerge/test_simple.py
index 6fc81ab..6c20a07 100644
--- a/pym/portage/tests/emerge/test_simple.py
+++ b/pym/portage/tests/emerge/test_simple.py
@@ -372,7 +372,7 @@ pkg_preinst() {
                                os.environ["__PORTAGE_TEST_HARDLINK_LOCKS"]
 
                updates_dir = os.path.join(test_repo_location, "profiles", 
"updates")
-               dirs = [cachedir, cachedir_pregen, distdir, fake_bin,
+               dirs = [cachedir, cachedir_pregen, cross_prefix, distdir, 
fake_bin,
                        portage_tmpdir, updates_dir,
                        user_config_dir, var_cache_edb]
                etc_symlinks = ("dispatch-conf.conf", "etc-update.conf")

Reply via email to