Gentoo Archives: gentoo-portage-dev

From: Zac Medico <zmedico@g.o>
To: gentoo-portage-dev@l.g.o
Cc: Zac Medico <zmedico@g.o>
Subject: [gentoo-portage-dev] [PATCH v3] unpack: use chmod-lite helper for bug 554084
Date: Fri, 02 Oct 2015 19:12:03
Message-Id: 1443813104-1566-1-git-send-email-zmedico@gentoo.org
In Reply to: [gentoo-portage-dev] [PATCH v2] unpack: use chmod-lite helper for bug 554084 by Zac Medico
1 Use the apply_recursive_permissions function to minimize the number of
2 chmod calls.
3
4 Also, fix an UnboundLocalError triggered in portage.data._get_global
5 by chmod-lite.
6
7 X-Gentoo-Bug: 554084
8 X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=554084
9 ---
10 [PATCH v3] fixes apply_recursive_permissions to properly separate modes
11 and masks for files and directories
12
13 bin/chmod-lite | 10 +++++
14 bin/chmod-lite.py | 30 +++++++++++++++
15 bin/phase-helpers.sh | 2 +-
16 pym/portage/data.py | 2 +-
17 pym/portage/util/__init__.py | 88 ++++++++++++++++++++++----------------------
18 5 files changed, 87 insertions(+), 45 deletions(-)
19 create mode 100755 bin/chmod-lite
20 create mode 100755 bin/chmod-lite.py
21
22 diff --git a/bin/chmod-lite b/bin/chmod-lite
23 new file mode 100755
24 index 0000000..ffa8d4d
25 --- /dev/null
26 +++ b/bin/chmod-lite
27 @@ -0,0 +1,10 @@
28 +#!/bin/bash
29 +# Copyright 2015 Gentoo Foundation
30 +# Distributed under the terms of the GNU General Public License v2
31 +
32 +export __PORTAGE_HELPER_CWD=${PWD}
33 +
34 +# Use safe cwd, avoiding unsafe import for bug #469338.
35 +cd "${PORTAGE_PYM_PATH}" || exit 1
36 +PYTHONPATH=${PORTAGE_PYTHONPATH:-${PORTAGE_PYM_PATH}} \
37 + exec "${PORTAGE_PYTHON:-/usr/bin/python}" "$PORTAGE_BIN_PATH/chmod-lite.py" "$@"
38 diff --git a/bin/chmod-lite.py b/bin/chmod-lite.py
39 new file mode 100755
40 index 0000000..a552b25
41 --- /dev/null
42 +++ b/bin/chmod-lite.py
43 @@ -0,0 +1,30 @@
44 +#!/usr/bin/python -b
45 +# Copyright 2015 Gentoo Foundation
46 +# Distributed under the terms of the GNU General Public License v2
47 +
48 +import os
49 +import sys
50 +
51 +from portage.util import apply_recursive_permissions
52 +
53 +# Change back to original cwd _after_ all imports (bug #469338).
54 +os.chdir(os.environ["__PORTAGE_HELPER_CWD"])
55 +
56 +def main(files):
57 +
58 + if sys.hexversion >= 0x3000000:
59 + # We can't trust that the filesystem encoding (locale dependent)
60 + # correctly matches the arguments, so use surrogateescape to
61 + # pass through the original argv bytes for Python 3.
62 + fs_encoding = sys.getfilesystemencoding()
63 + files = [x.encode(fs_encoding, 'surrogateescape') for x in files]
64 +
65 + for filename in files:
66 + # Emulate 'chmod -fR a+rX,u+w,g-w,o-w' with mininal chmod calls.
67 + apply_recursive_permissions(filename, filemode=0o644,
68 + filemask=0o022, dirmode=0o755, dirmask=0o022)
69 +
70 + return os.EX_OK
71 +
72 +if __name__ == "__main__":
73 + sys.exit(main(sys.argv[1:]))
74 diff --git a/bin/phase-helpers.sh b/bin/phase-helpers.sh
75 index efd2cfa..0c25ffe 100644
76 --- a/bin/phase-helpers.sh
77 +++ b/bin/phase-helpers.sh
78 @@ -532,7 +532,7 @@ unpack() {
79 # Do not chmod '.' since it's probably ${WORKDIR} and PORTAGE_WORKDIR_MODE
80 # should be preserved.
81 find . -mindepth 1 -maxdepth 1 ! -type l -print0 | \
82 - ${XARGS} -0 chmod -fR a+rX,u+w,g-w,o-w
83 + ${XARGS} -0 "${PORTAGE_BIN_PATH}/chmod-lite"
84 }
85
86 econf() {
87 diff --git a/pym/portage/data.py b/pym/portage/data.py
88 index 2fd287d..2c99548 100644
89 --- a/pym/portage/data.py
90 +++ b/pym/portage/data.py
91 @@ -139,7 +139,7 @@ def _get_global(k):
92 v = 2
93 elif unprivileged:
94 v = 2
95 - elif portage_gid in os.getgroups():
96 + elif _get_global('portage_gid') in os.getgroups():
97 v = 1
98
99 elif k in ('portage_gid', 'portage_uid'):
100 diff --git a/pym/portage/util/__init__.py b/pym/portage/util/__init__.py
101 index c0b509b..38780a7 100644
102 --- a/pym/portage/util/__init__.py
103 +++ b/pym/portage/util/__init__.py
104 @@ -17,9 +17,9 @@ from copy import deepcopy
105 import errno
106 import io
107 try:
108 - from itertools import filterfalse
109 + from itertools import chain, filterfalse
110 except ImportError:
111 - from itertools import ifilterfalse as filterfalse
112 + from itertools import chain, ifilterfalse as filterfalse
113 import logging
114 import re
115 import shlex
116 @@ -1041,6 +1041,23 @@ def unique_everseen(iterable, key=None):
117 seen_add(k)
118 yield element
119
120 +def _do_stat(filename, follow_links=True):
121 + try:
122 + if follow_links:
123 + return os.stat(filename)
124 + else:
125 + return os.lstat(filename)
126 + except OSError as oe:
127 + func_call = "stat('%s')" % filename
128 + if oe.errno == errno.EPERM:
129 + raise OperationNotPermitted(func_call)
130 + elif oe.errno == errno.EACCES:
131 + raise PermissionDenied(func_call)
132 + elif oe.errno == errno.ENOENT:
133 + raise FileNotFound(filename)
134 + else:
135 + raise
136 +
137 def apply_permissions(filename, uid=-1, gid=-1, mode=-1, mask=-1,
138 stat_cached=None, follow_links=True):
139 """Apply user, group, and mode bits to a file if the existing bits do not
140 @@ -1058,21 +1075,7 @@ def apply_permissions(filename, uid=-1, gid=-1, mode=-1, mask=-1,
141 gid = int(gid)
142
143 if stat_cached is None:
144 - try:
145 - if follow_links:
146 - stat_cached = os.stat(filename)
147 - else:
148 - stat_cached = os.lstat(filename)
149 - except OSError as oe:
150 - func_call = "stat('%s')" % filename
151 - if oe.errno == errno.EPERM:
152 - raise OperationNotPermitted(func_call)
153 - elif oe.errno == errno.EACCES:
154 - raise PermissionDenied(func_call)
155 - elif oe.errno == errno.ENOENT:
156 - raise FileNotFound(filename)
157 - else:
158 - raise
159 + stat_cached = _do_stat(filename, follow_links=follow_links)
160
161 if (uid != -1 and uid != stat_cached.st_uid) or \
162 (gid != -1 and gid != stat_cached.st_gid):
163 @@ -1177,22 +1180,35 @@ def apply_recursive_permissions(top, uid=-1, gid=-1,
164 else:
165 raise
166
167 + # For bug 554084, always apply permissions to a directory before
168 + # that directory is traversed.
169 all_applied = True
170 - for dirpath, dirnames, filenames in os.walk(top):
171 - try:
172 - applied = apply_secpass_permissions(dirpath,
173 - uid=uid, gid=gid, mode=dirmode, mask=dirmask,
174 - follow_links=follow_links)
175 - if not applied:
176 - all_applied = False
177 - except PortageException as e:
178 +
179 + stat_cached = _do_stat(top, follow_links=follow_links)
180 + if stat.S_ISDIR(stat_cached.st_mode):
181 + mode = dirmode
182 + mask = dirmask
183 + else:
184 + mode = filemode
185 + mask = filemask
186 +
187 + try:
188 + applied = apply_secpass_permissions(top,
189 + uid=uid, gid=gid, mode=mode, mask=mask,
190 + stat_cached=stat_cached, follow_links=follow_links)
191 + if not applied:
192 all_applied = False
193 - onerror(e)
194 + except PortageException as e:
195 + all_applied = False
196 + onerror(e)
197
198 - for name in filenames:
199 + for dirpath, dirnames, filenames in os.walk(top):
200 + for name, mode, mask in chain(
201 + ((x, filemode, filemask) for x in filenames),
202 + ((x, dirmode, dirmask) for x in dirnames)):
203 try:
204 applied = apply_secpass_permissions(os.path.join(dirpath, name),
205 - uid=uid, gid=gid, mode=filemode, mask=filemask,
206 + uid=uid, gid=gid, mode=mode, mask=mask,
207 follow_links=follow_links)
208 if not applied:
209 all_applied = False
210 @@ -1216,21 +1232,7 @@ def apply_secpass_permissions(filename, uid=-1, gid=-1, mode=-1, mask=-1,
211 unapplied."""
212
213 if stat_cached is None:
214 - try:
215 - if follow_links:
216 - stat_cached = os.stat(filename)
217 - else:
218 - stat_cached = os.lstat(filename)
219 - except OSError as oe:
220 - func_call = "stat('%s')" % filename
221 - if oe.errno == errno.EPERM:
222 - raise OperationNotPermitted(func_call)
223 - elif oe.errno == errno.EACCES:
224 - raise PermissionDenied(func_call)
225 - elif oe.errno == errno.ENOENT:
226 - raise FileNotFound(filename)
227 - else:
228 - raise
229 + stat_cached = _do_stat(filename, follow_links=follow_links)
230
231 all_applied = True
232
233 --
234 2.4.6