[prev in list] [next in list] [prev in thread] [next in thread] 

List:       gentoo-portage-dev
Subject:    [gentoo-portage-dev] [PATCH] Support unprivileged mode for bug #433453.
From:       Zac Medico <zmedico () gentoo ! org>
Date:       2014-09-29 18:23:00
Message-ID: 5429A384.8020008 () gentoo ! org
[Download RAW message or body]

From 313604d52e6764ba360cd938fa6bf480895e793c Mon Sep 17 00:00:00 2001
From: Zac Medico <zmedico@gentoo.org>
Date: Mon, 29 Sep 2014 11:18:42 -0700
Subject: [PATCH] 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 0bb83ae..6e39804 100644
--- a/pym/portage/tests/emerge/test_simple.py
+++ b/pym/portage/tests/emerge/test_simple.py
@@ -357,7 +357,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")
-- 
1.8.5.5

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic