1 |
From 313604d52e6764ba360cd938fa6bf480895e793c Mon Sep 17 00:00:00 2001 |
2 |
From: Zac Medico <zmedico@g.o> |
3 |
Date: Mon, 29 Sep 2014 11:18:42 -0700 |
4 |
Subject: [PATCH] Support unprivileged mode for bug #433453. |
5 |
|
6 |
This takes the existing unprivileged prefix support and enables it to |
7 |
work when EPREFIX is empty. This "unprivileged" mode is automatically |
8 |
enabled if the current user is not root, but has write access to the |
9 |
EROOT directory (not due to the 0002 bit). For use in conditional logic |
10 |
(for example, see doebuild.py diff), "unprivileged" is automatically |
11 |
added to the FEATURES variable. |
12 |
|
13 |
X-Gentoo-Bug: 433453 |
14 |
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=433453 |
15 |
--- |
16 |
pym/portage/const.py | 1 + |
17 |
pym/portage/data.py | 95 ++++++++++++++++++++++----------- |
18 |
pym/portage/package/ebuild/config.py | 21 +++++--- |
19 |
pym/portage/package/ebuild/doebuild.py | 4 +- |
20 |
pym/portage/tests/emerge/test_simple.py | 2 +- |
21 |
5 files changed, 83 insertions(+), 40 deletions(-) |
22 |
|
23 |
diff --git a/pym/portage/const.py b/pym/portage/const.py |
24 |
index acb90f9..d472075 100644 |
25 |
--- a/pym/portage/const.py |
26 |
+++ b/pym/portage/const.py |
27 |
@@ -189,6 +189,7 @@ SUPPORTED_FEATURES = frozenset([ |
28 |
"unmerge-backup", |
29 |
"unmerge-logs", |
30 |
"unmerge-orphans", |
31 |
+ "unprivileged", |
32 |
"userfetch", |
33 |
"userpriv", |
34 |
"usersandbox", |
35 |
diff --git a/pym/portage/data.py b/pym/portage/data.py |
36 |
index 54e3a8d..3d91e48 100644 |
37 |
--- a/pym/portage/data.py |
38 |
+++ b/pym/portage/data.py |
39 |
@@ -59,6 +59,15 @@ def portage_group_warning(): |
40 |
# If the "wheel" group does not exist then wheelgid falls back to 0. |
41 |
# If the "portage" group does not exist then portage_uid falls back to wheelgid. |
42 |
|
43 |
+# If the current user is not root, but has write access to the |
44 |
+# EROOT directory (not due to the 0002 bit), then use "unprivileged" |
45 |
+# mode which sets secpass = 2 and uses the UID and GID of the EROOT |
46 |
+# directory to generate default PORTAGE_INST_GID, PORTAGE_INST_UID, |
47 |
+# PORTAGE_USERNAME, and PORTAGE_GRPNAME settings. |
48 |
+def _unprivileged_mode(eroot, eroot_st): |
49 |
+ return os.getuid() != 0 and os.access(eroot, os.W_OK) and \ |
50 |
+ not eroot_st.st_mode & 0o0002 |
51 |
+ |
52 |
uid = os.getuid() |
53 |
wheelgid = 0 |
54 |
try: |
55 |
@@ -77,13 +86,33 @@ def _get_global(k): |
56 |
if k in _initialized_globals: |
57 |
return globals()[k] |
58 |
|
59 |
- if k in ('portage_gid', 'portage_uid', 'secpass'): |
60 |
- global portage_gid, portage_uid, secpass |
61 |
- secpass = 0 |
62 |
+ if k == 'secpass': |
63 |
+ |
64 |
+ unprivileged = False |
65 |
+ if hasattr(portage, 'settings'): |
66 |
+ unprivileged = "unprivileged" in portage.settings.features |
67 |
+ else: |
68 |
+ # The config class has equivalent code, but we also need to |
69 |
+ # do it here if _disable_legacy_globals() has been called. |
70 |
+ eroot = os.path.join(os.environ.get('ROOT', os.sep), |
71 |
+ portage.const.EPREFIX.lstrip(os.sep)) |
72 |
+ try: |
73 |
+ eroot_st = os.stat(eroot) |
74 |
+ except OSError: |
75 |
+ pass |
76 |
+ else: |
77 |
+ unprivileged = _unprivileged_mode(eroot, eroot_st) |
78 |
+ |
79 |
+ v = 0 |
80 |
if uid == 0: |
81 |
- secpass = 2 |
82 |
- elif portage.const.EPREFIX: |
83 |
- secpass = 2 |
84 |
+ v = 2 |
85 |
+ elif unprivileged: |
86 |
+ v = 2 |
87 |
+ elif portage_gid in os.getgroups(): |
88 |
+ v = 1 |
89 |
+ |
90 |
+ elif k in ('portage_gid', 'portage_uid'): |
91 |
+ |
92 |
#Discover the uid and gid of the portage user/group |
93 |
keyerror = False |
94 |
try: |
95 |
@@ -98,9 +127,6 @@ def _get_global(k): |
96 |
keyerror = True |
97 |
portage_gid = 0 |
98 |
|
99 |
- if secpass < 1 and portage_gid in os.getgroups(): |
100 |
- secpass = 1 |
101 |
- |
102 |
# Suppress this error message if both PORTAGE_GRPNAME and |
103 |
# PORTAGE_USERNAME are set to "root", for things like |
104 |
# Android (see bug #454060). |
105 |
@@ -118,16 +144,15 @@ def _get_global(k): |
106 |
noiselevel=-1) |
107 |
portage_group_warning() |
108 |
|
109 |
+ globals()['portage_gid'] = portage_gid |
110 |
_initialized_globals.add('portage_gid') |
111 |
+ globals()['portage_uid'] = portage_uid |
112 |
_initialized_globals.add('portage_uid') |
113 |
- _initialized_globals.add('secpass') |
114 |
|
115 |
if k == 'portage_gid': |
116 |
return portage_gid |
117 |
elif k == 'portage_uid': |
118 |
return portage_uid |
119 |
- elif k == 'secpass': |
120 |
- return secpass |
121 |
else: |
122 |
raise AssertionError('unknown name: %s' % k) |
123 |
|
124 |
@@ -178,11 +203,9 @@ def _get_global(k): |
125 |
v = os.environ[env_key] |
126 |
elif hasattr(portage, 'settings'): |
127 |
v = portage.settings.get(env_key) |
128 |
- elif portage.const.EPREFIX: |
129 |
- # For prefix environments, default to the UID and GID of |
130 |
- # the top-level EROOT directory. The config class has |
131 |
- # equivalent code, but we also need to do it here if |
132 |
- # _disable_legacy_globals() has been called. |
133 |
+ else: |
134 |
+ # The config class has equivalent code, but we also need to |
135 |
+ # do it here if _disable_legacy_globals() has been called. |
136 |
eroot = os.path.join(os.environ.get('ROOT', os.sep), |
137 |
portage.const.EPREFIX.lstrip(os.sep)) |
138 |
try: |
139 |
@@ -190,20 +213,21 @@ def _get_global(k): |
140 |
except OSError: |
141 |
pass |
142 |
else: |
143 |
- if k == '_portage_grpname': |
144 |
- try: |
145 |
- grp_struct = grp.getgrgid(eroot_st.st_gid) |
146 |
- except KeyError: |
147 |
- pass |
148 |
+ if _unprivileged_mode(eroot, eroot_st): |
149 |
+ if k == '_portage_grpname': |
150 |
+ try: |
151 |
+ grp_struct = grp.getgrgid(eroot_st.st_gid) |
152 |
+ except KeyError: |
153 |
+ pass |
154 |
+ else: |
155 |
+ v = grp_struct.gr_name |
156 |
else: |
157 |
- v = grp_struct.gr_name |
158 |
- else: |
159 |
- try: |
160 |
- pwd_struct = pwd.getpwuid(eroot_st.st_uid) |
161 |
- except KeyError: |
162 |
- pass |
163 |
- else: |
164 |
- v = pwd_struct.pw_name |
165 |
+ try: |
166 |
+ pwd_struct = pwd.getpwuid(eroot_st.st_uid) |
167 |
+ except KeyError: |
168 |
+ pass |
169 |
+ else: |
170 |
+ v = pwd_struct.pw_name |
171 |
|
172 |
if v is None: |
173 |
v = 'portage' |
174 |
@@ -254,3 +278,14 @@ def _init(settings): |
175 |
v = portage._native_string(v) |
176 |
globals()['_portage_username'] = v |
177 |
_initialized_globals.add('_portage_username') |
178 |
+ |
179 |
+ if 'secpass' not in _initialized_globals: |
180 |
+ v = 0 |
181 |
+ if uid == 0: |
182 |
+ v = 2 |
183 |
+ elif "unprivileged" in settings.features: |
184 |
+ v = 2 |
185 |
+ elif portage_gid in os.getgroups(): |
186 |
+ v = 1 |
187 |
+ globals()['secpass'] = v |
188 |
+ _initialized_globals.add('secpass') |
189 |
diff --git a/pym/portage/package/ebuild/config.py b/pym/portage/package/ebuild/config.py |
190 |
index 264ed8e..1087443 100644 |
191 |
--- a/pym/portage/package/ebuild/config.py |
192 |
+++ b/pym/portage/package/ebuild/config.py |
193 |
@@ -834,14 +834,16 @@ class config(object): |
194 |
"PORTAGE_INST_UID": "0", |
195 |
} |
196 |
|
197 |
- if eprefix: |
198 |
- # For prefix environments, default to the UID and GID of |
199 |
- # the top-level EROOT directory. |
200 |
- try: |
201 |
- eroot_st = os.stat(eroot) |
202 |
- except OSError: |
203 |
- pass |
204 |
- else: |
205 |
+ unprivileged = False |
206 |
+ try: |
207 |
+ eroot_st = os.stat(eroot) |
208 |
+ except OSError: |
209 |
+ pass |
210 |
+ else: |
211 |
+ |
212 |
+ if portage.data._unprivileged_mode(eroot, eroot_st): |
213 |
+ unprivileged = True |
214 |
+ |
215 |
default_inst_ids["PORTAGE_INST_GID"] = str(eroot_st.st_gid) |
216 |
default_inst_ids["PORTAGE_INST_UID"] = str(eroot_st.st_uid) |
217 |
|
218 |
@@ -880,6 +882,9 @@ class config(object): |
219 |
# initialize self.features |
220 |
self.regenerate() |
221 |
|
222 |
+ if unprivileged: |
223 |
+ self.features.add('unprivileged') |
224 |
+ |
225 |
if bsd_chflags: |
226 |
self.features.add('chflags') |
227 |
|
228 |
diff --git a/pym/portage/package/ebuild/doebuild.py b/pym/portage/package/ebuild/doebuild.py |
229 |
index d3e3f5a..9516173 100644 |
230 |
--- a/pym/portage/package/ebuild/doebuild.py |
231 |
+++ b/pym/portage/package/ebuild/doebuild.py |
232 |
@@ -200,7 +200,9 @@ def _doebuild_path(settings, eapi=None): |
233 |
if "xattr" in settings.features: |
234 |
path.append(os.path.join(portage_bin_path, "ebuild-helpers", "xattr")) |
235 |
|
236 |
- if eprefix and uid != 0 and "fakeroot" not in settings.features: |
237 |
+ if uid != 0 and \ |
238 |
+ "unprivileged" in settings.features and \ |
239 |
+ "fakeroot" not in settings.features: |
240 |
path.append(os.path.join(portage_bin_path, |
241 |
"ebuild-helpers", "unprivileged")) |
242 |
|
243 |
diff --git a/pym/portage/tests/emerge/test_simple.py b/pym/portage/tests/emerge/test_simple.py |
244 |
index 0bb83ae..6e39804 100644 |
245 |
--- a/pym/portage/tests/emerge/test_simple.py |
246 |
+++ b/pym/portage/tests/emerge/test_simple.py |
247 |
@@ -357,7 +357,7 @@ pkg_preinst() { |
248 |
os.environ["__PORTAGE_TEST_HARDLINK_LOCKS"] |
249 |
|
250 |
updates_dir = os.path.join(test_repo_location, "profiles", "updates") |
251 |
- dirs = [cachedir, cachedir_pregen, distdir, fake_bin, |
252 |
+ dirs = [cachedir, cachedir_pregen, cross_prefix, distdir, fake_bin, |
253 |
portage_tmpdir, updates_dir, |
254 |
user_config_dir, var_cache_edb] |
255 |
etc_symlinks = ("dispatch-conf.conf", "etc-update.conf") |
256 |
-- |
257 |
1.8.5.5 |