Gentoo Archives: gentoo-portage-dev

From: Zac Medico <zmedico@g.o>
To: gentoo-portage-dev@l.g.o
Subject: [gentoo-portage-dev] [PATCH] Support unprivileged mode for bug #433453.
Date: Mon, 29 Sep 2014 18:23:05
Message-Id: 5429A384.8020008@gentoo.org
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

Replies