1 |
When case-insensitive-fs is enabled in FEATURES, the dblink.isowner |
2 |
method, _owners_db class, and ConfigProtect class will be |
3 |
case-insensitive. This causes the collision-protect and unmerge code |
4 |
to behave correctly for a case-insensitive file system. If the file |
5 |
system is case-insensitive but case-preserving, then case is preserved |
6 |
in the CONTENTS data of installed packages. |
7 |
|
8 |
X-Gentoo-Bug: 524236 |
9 |
X-Gentoo-Url: https://bugs.gentoo.org/show_bug.cgi?id=524236 |
10 |
--- |
11 |
bin/dispatch-conf | 8 ++++++- |
12 |
bin/etc-update | 10 ++++++-- |
13 |
bin/portageq | 7 +++--- |
14 |
bin/quickpkg | 4 +++- |
15 |
man/make.conf.5 | 6 +++++ |
16 |
pym/_emerge/depgraph.py | 4 +++- |
17 |
pym/portage/_global_updates.py | 4 +++- |
18 |
pym/portage/const.py | 1 + |
19 |
pym/portage/dbapi/vartree.py | 53 +++++++++++++++++++++++++++--------------- |
20 |
pym/portage/update.py | 6 +++-- |
21 |
pym/portage/util/__init__.py | 8 ++++++- |
22 |
11 files changed, 80 insertions(+), 31 deletions(-) |
23 |
|
24 |
diff --git a/bin/dispatch-conf b/bin/dispatch-conf |
25 |
index 8058d6f..b679910 100755 |
26 |
--- a/bin/dispatch-conf |
27 |
+++ b/bin/dispatch-conf |
28 |
@@ -35,6 +35,10 @@ from portage.process import find_binary, spawn |
29 |
FIND_EXTANT_CONFIGS = "find '%s' %s -name '._cfg????_%s' ! -name '.*~' ! -iname '.*.bak' -print" |
30 |
DIFF_CONTENTS = "diff -Nu '%s' '%s'" |
31 |
|
32 |
+if "case-insensitive-fs" in portage.settings.features: |
33 |
+ FIND_EXTANT_CONFIGS = FIND_EXTANT_CONFIGS.replace( |
34 |
+ "-name '._cfg", "-iname '._cfg") |
35 |
+ |
36 |
# We need a secure scratch dir and python does silly verbose errors on the use of tempnam |
37 |
oldmask = os.umask(0o077) |
38 |
SCRATCH_DIR = None |
39 |
@@ -152,7 +156,9 @@ class dispatch: |
40 |
protect_obj = portage.util.ConfigProtect( |
41 |
config_root, config_paths, |
42 |
portage.util.shlex_split( |
43 |
- portage.settings.get('CONFIG_PROTECT_MASK', ''))) |
44 |
+ portage.settings.get('CONFIG_PROTECT_MASK', '')), |
45 |
+ case_insensitive=("case-insensitive-fs" |
46 |
+ in portage.settings.features)) |
47 |
|
48 |
# |
49 |
# Remove new configs identical to current |
50 |
diff --git a/bin/etc-update b/bin/etc-update |
51 |
index 0307688..e0f7224 100755 |
52 |
--- a/bin/etc-update |
53 |
+++ b/bin/etc-update |
54 |
@@ -113,12 +113,15 @@ scan() { |
55 |
[[ -d ${path%/*} ]] || continue |
56 |
local name_opt=$(get_basename_find_opt "${path##*/}") |
57 |
path="${path%/*}" |
58 |
- find_opts=( -maxdepth 1 -name "$name_opt" ) |
59 |
+ find_opts=( -maxdepth 1 ) |
60 |
else |
61 |
# Do not traverse hidden directories such as .svn or .git. |
62 |
local name_opt=$(get_basename_find_opt '*') |
63 |
- find_opts=( -name '.*' -type d -prune -o -name "$name_opt" ) |
64 |
+ find_opts=( -name '.*' -type d -prune -o ) |
65 |
fi |
66 |
+ ${case_insensitive} && \ |
67 |
+ find_opts+=( -iname ) || find_opts+=( -name ) |
68 |
+ find_opts+=( "$name_opt" ) |
69 |
find_opts+=( ! -name '.*~' ! -iname '.*.bak' -print ) |
70 |
|
71 |
if [ ! -w "${path}" ] ; then |
72 |
@@ -743,6 +746,7 @@ fi |
73 |
|
74 |
portage_vars=( |
75 |
CONFIG_PROTECT{,_MASK} |
76 |
+ FEATURES |
77 |
PORTAGE_CONFIGROOT |
78 |
PORTAGE_INST_{G,U}ID |
79 |
PORTAGE_TMPDIR |
80 |
@@ -759,6 +763,8 @@ fi |
81 |
|
82 |
export PORTAGE_TMPDIR |
83 |
SCAN_PATHS=${*:-${CONFIG_PROTECT}} |
84 |
+[[ " ${FEATURES} " == *" case-insensitive-fs "* ]] && \ |
85 |
+ case_insensitive=true || case_insensitive=false |
86 |
|
87 |
TMP="${PORTAGE_TMPDIR}/etc-update-$$" |
88 |
trap "die terminated" SIGTERM |
89 |
diff --git a/bin/portageq b/bin/portageq |
90 |
index ef565d1..1618b44 100755 |
91 |
--- a/bin/portageq |
92 |
+++ b/bin/portageq |
93 |
@@ -379,8 +379,8 @@ def is_protected(argv): |
94 |
protect = portage.util.shlex_split(settings.get("CONFIG_PROTECT", "")) |
95 |
protect_mask = portage.util.shlex_split( |
96 |
settings.get("CONFIG_PROTECT_MASK", "")) |
97 |
- protect_obj = ConfigProtect(root, protect, protect_mask) |
98 |
- |
99 |
+ protect_obj = ConfigProtect(root, protect, protect_mask, |
100 |
+ case_insensitive=("case-insensitive-fs" in settings.features)) |
101 |
if protect_obj.isprotected(f): |
102 |
return 0 |
103 |
return 1 |
104 |
@@ -414,7 +414,8 @@ def filter_protected(argv): |
105 |
protect = portage.util.shlex_split(settings.get("CONFIG_PROTECT", "")) |
106 |
protect_mask = portage.util.shlex_split( |
107 |
settings.get("CONFIG_PROTECT_MASK", "")) |
108 |
- protect_obj = ConfigProtect(root, protect, protect_mask) |
109 |
+ protect_obj = ConfigProtect(root, protect, protect_mask, |
110 |
+ case_insensitive=("case-insensitive-fs" in settings.features)) |
111 |
|
112 |
errors = 0 |
113 |
|
114 |
diff --git a/bin/quickpkg b/bin/quickpkg |
115 |
index cf75791..2c69a69 100755 |
116 |
--- a/bin/quickpkg |
117 |
+++ b/bin/quickpkg |
118 |
@@ -102,7 +102,9 @@ def quickpkg_atom(options, infos, arg, eout): |
119 |
if not include_config: |
120 |
confprot = ConfigProtect(eroot, |
121 |
shlex_split(settings.get("CONFIG_PROTECT", "")), |
122 |
- shlex_split(settings.get("CONFIG_PROTECT_MASK", ""))) |
123 |
+ shlex_split(settings.get("CONFIG_PROTECT_MASK", "")), |
124 |
+ case_insensitive=("case-insensitive-fs" |
125 |
+ in settings.features)) |
126 |
def protect(filename): |
127 |
if not confprot.isprotected(filename): |
128 |
return False |
129 |
diff --git a/man/make.conf.5 b/man/make.conf.5 |
130 |
index 84e894b..69d95fc 100644 |
131 |
--- a/man/make.conf.5 |
132 |
+++ b/man/make.conf.5 |
133 |
@@ -265,6 +265,12 @@ Build binary packages for just packages in the system set. |
134 |
Enable a special progress indicator when \fBemerge\fR(1) is calculating |
135 |
dependencies. |
136 |
.TP |
137 |
+.B case\-insensitive\-fs |
138 |
+Use case\-insensitive file name comparisions when merging and unmerging |
139 |
+files. Most users should not enable this feature, since most filesystems |
140 |
+are case\-sensitive. You should only enable this feature if you are |
141 |
+using portage to install files to a case\-insensitive filesystem. |
142 |
+.TP |
143 |
.B ccache |
144 |
Enable portage support for the ccache package. If the ccache dir is not |
145 |
present in the user's environment, then portage will default to |
146 |
diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py |
147 |
index 94eaed8..6bf2be5 100644 |
148 |
--- a/pym/_emerge/depgraph.py |
149 |
+++ b/pym/_emerge/depgraph.py |
150 |
@@ -7805,7 +7805,9 @@ class depgraph(object): |
151 |
settings = self._frozen_config.roots[root].settings |
152 |
protect_obj[root] = ConfigProtect(settings["EROOT"], \ |
153 |
shlex_split(settings.get("CONFIG_PROTECT", "")), |
154 |
- shlex_split(settings.get("CONFIG_PROTECT_MASK", ""))) |
155 |
+ shlex_split(settings.get("CONFIG_PROTECT_MASK", "")), |
156 |
+ case_insensitive=("case-insensitive-fs" |
157 |
+ in settings.features)) |
158 |
|
159 |
def write_changes(root, changes, file_to_write_to): |
160 |
file_contents = None |
161 |
diff --git a/pym/portage/_global_updates.py b/pym/portage/_global_updates.py |
162 |
index 17dc080..87085de 100644 |
163 |
--- a/pym/portage/_global_updates.py |
164 |
+++ b/pym/portage/_global_updates.py |
165 |
@@ -208,7 +208,9 @@ def _do_global_updates(trees, prev_mtimes, quiet=False, if_mtime_changed=True): |
166 |
update_config_files(root, |
167 |
shlex_split(mysettings.get("CONFIG_PROTECT", "")), |
168 |
shlex_split(mysettings.get("CONFIG_PROTECT_MASK", "")), |
169 |
- repo_map, match_callback=_config_repo_match) |
170 |
+ repo_map, match_callback = _config_repo_match, |
171 |
+ case_insensitive="case-insensitive-fs" |
172 |
+ in mysettings.features) |
173 |
|
174 |
# The above global updates proceed quickly, so they |
175 |
# are considered a single mtimedb transaction. |
176 |
diff --git a/pym/portage/const.py b/pym/portage/const.py |
177 |
index d472075..febdb4a 100644 |
178 |
--- a/pym/portage/const.py |
179 |
+++ b/pym/portage/const.py |
180 |
@@ -125,6 +125,7 @@ SUPPORTED_FEATURES = frozenset([ |
181 |
"buildpkg", |
182 |
"buildsyspkg", |
183 |
"candy", |
184 |
+ "case-insensitive-fs", |
185 |
"ccache", |
186 |
"cgroup", |
187 |
"chflags", |
188 |
diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py |
189 |
index 2d1003f..bf7985a 100644 |
190 |
--- a/pym/portage/dbapi/vartree.py |
191 |
+++ b/pym/portage/dbapi/vartree.py |
192 |
@@ -1051,13 +1051,13 @@ class vardbapi(dbapi): |
193 |
|
194 |
def add(self, cpv): |
195 |
eroot_len = len(self._vardb._eroot) |
196 |
- contents = self._vardb._dblink(cpv).getcontents() |
197 |
pkg_hash = self._hash_pkg(cpv) |
198 |
- if not contents: |
199 |
+ db = self._vardb._dblink(cpv) |
200 |
+ if not db.getcontents(): |
201 |
# Empty path is a code used to represent empty contents. |
202 |
self._add_path("", pkg_hash) |
203 |
|
204 |
- for x in contents: |
205 |
+ for x in db._contents_iter(): |
206 |
self._add_path(x[eroot_len:], pkg_hash) |
207 |
|
208 |
self._vardb._aux_cache["modified"].add(cpv) |
209 |
@@ -1189,6 +1189,8 @@ class vardbapi(dbapi): |
210 |
hash_pkg = owners_cache._hash_pkg |
211 |
hash_str = owners_cache._hash_str |
212 |
base_names = self._vardb._aux_cache["owners"]["base_names"] |
213 |
+ case_insensitive = "case-insensitive-fs" \ |
214 |
+ in vardb.settings.features |
215 |
|
216 |
dblink_cache = {} |
217 |
|
218 |
@@ -1205,6 +1207,8 @@ class vardbapi(dbapi): |
219 |
while path_iter: |
220 |
|
221 |
path = path_iter.pop() |
222 |
+ if case_insensitive: |
223 |
+ path = path.lower() |
224 |
is_basename = os.sep != path[:1] |
225 |
if is_basename: |
226 |
name = path |
227 |
@@ -1235,7 +1239,7 @@ class vardbapi(dbapi): |
228 |
continue |
229 |
|
230 |
if is_basename: |
231 |
- for p in dblink(cpv).getcontents(): |
232 |
+ for p in dblink(cpv)._contents_iter(): |
233 |
if os.path.basename(p) == name: |
234 |
owners.append((cpv, p[len(root):])) |
235 |
else: |
236 |
@@ -1265,8 +1269,12 @@ class vardbapi(dbapi): |
237 |
if not path_list: |
238 |
return |
239 |
|
240 |
+ case_insensitive = "case-insensitive-fs" \ |
241 |
+ in self._vardb.settings.features |
242 |
path_info_list = [] |
243 |
for path in path_list: |
244 |
+ if case_insensitive: |
245 |
+ path = path.lower() |
246 |
is_basename = os.sep != path[:1] |
247 |
if is_basename: |
248 |
name = path |
249 |
@@ -1284,9 +1292,11 @@ class vardbapi(dbapi): |
250 |
dblnk = self._vardb._dblink(cpv) |
251 |
for path, name, is_basename in path_info_list: |
252 |
if is_basename: |
253 |
- for p in dblnk.getcontents(): |
254 |
+ for p in dblnk._contents_iter(): |
255 |
if os.path.basename(p) == name: |
256 |
- search_pkg.results.append((dblnk, p[len(root):])) |
257 |
+ search_pkg.results.append((dblnk, |
258 |
+ dblnk._contents_case_reverse_map( |
259 |
+ p)[len(root):])) |
260 |
else: |
261 |
if dblnk.isowner(path): |
262 |
search_pkg.results.append((dblnk, path)) |
263 |
@@ -1551,7 +1561,9 @@ class dblink(object): |
264 |
portage.util.shlex_split( |
265 |
self.settings.get("CONFIG_PROTECT", "")), |
266 |
portage.util.shlex_split( |
267 |
- self.settings.get("CONFIG_PROTECT_MASK", ""))) |
268 |
+ self.settings.get("CONFIG_PROTECT_MASK", "")), |
269 |
+ case_insensitive=("case-insensitive-fs" |
270 |
+ in self.settings.features)) |
271 |
|
272 |
return self._protect_obj |
273 |
|
274 |
@@ -2804,15 +2816,18 @@ class dblink(object): |
275 |
os_filename_arg.path.join(destroot, |
276 |
filename.lstrip(os_filename_arg.path.sep))) |
277 |
|
278 |
- pkgfiles = self.getcontents() |
279 |
- if pkgfiles and destfile in pkgfiles: |
280 |
- return destfile |
281 |
- if pkgfiles: |
282 |
+ if "case-insensitive-fs" in self.settings.features: |
283 |
+ destfile = destfile.lower() |
284 |
+ |
285 |
+ if self._contents_contains(destfile): |
286 |
+ return self._contents_key(destfile) |
287 |
+ |
288 |
+ if self.getcontents(): |
289 |
basename = os_filename_arg.path.basename(destfile) |
290 |
if self._contents_basenames is None: |
291 |
|
292 |
try: |
293 |
- for x in pkgfiles: |
294 |
+ for x in self._contents_iter(): |
295 |
_unicode_encode(x, |
296 |
encoding=_encodings['merge'], |
297 |
errors='strict') |
298 |
@@ -2821,7 +2836,7 @@ class dblink(object): |
299 |
# different value of sys.getfilesystemencoding(), |
300 |
# so fall back to utf_8 if appropriate. |
301 |
try: |
302 |
- for x in pkgfiles: |
303 |
+ for x in self._contents_iter(): |
304 |
_unicode_encode(x, |
305 |
encoding=_encodings['fs'], |
306 |
errors='strict') |
307 |
@@ -2831,7 +2846,7 @@ class dblink(object): |
308 |
os = portage.os |
309 |
|
310 |
self._contents_basenames = set( |
311 |
- os.path.basename(x) for x in pkgfiles) |
312 |
+ os.path.basename(x) for x in self._contents_iter()) |
313 |
if basename not in self._contents_basenames: |
314 |
# This is a shortcut that, in most cases, allows us to |
315 |
# eliminate this package as an owner without the need |
316 |
@@ -2852,7 +2867,7 @@ class dblink(object): |
317 |
|
318 |
if os is _os_merge: |
319 |
try: |
320 |
- for x in pkgfiles: |
321 |
+ for x in self._contents_iter(): |
322 |
_unicode_encode(x, |
323 |
encoding=_encodings['merge'], |
324 |
errors='strict') |
325 |
@@ -2861,7 +2876,7 @@ class dblink(object): |
326 |
# different value of sys.getfilesystemencoding(), |
327 |
# so fall back to utf_8 if appropriate. |
328 |
try: |
329 |
- for x in pkgfiles: |
330 |
+ for x in self._contents_iter(): |
331 |
_unicode_encode(x, |
332 |
encoding=_encodings['fs'], |
333 |
errors='strict') |
334 |
@@ -2872,7 +2887,7 @@ class dblink(object): |
335 |
|
336 |
self._contents_inodes = {} |
337 |
parent_paths = set() |
338 |
- for x in pkgfiles: |
339 |
+ for x in self._contents_iter(): |
340 |
p_path = os.path.dirname(x) |
341 |
if p_path in parent_paths: |
342 |
continue |
343 |
@@ -2897,8 +2912,8 @@ class dblink(object): |
344 |
if p_path_list: |
345 |
for p_path in p_path_list: |
346 |
x = os_filename_arg.path.join(p_path, basename) |
347 |
- if x in pkgfiles: |
348 |
- return x |
349 |
+ if self._contents_contains(x): |
350 |
+ return self._contents_key(x) |
351 |
|
352 |
return False |
353 |
|
354 |
diff --git a/pym/portage/update.py b/pym/portage/update.py |
355 |
index df4e11b..83fc3d2 100644 |
356 |
--- a/pym/portage/update.py |
357 |
+++ b/pym/portage/update.py |
358 |
@@ -282,7 +282,8 @@ def parse_updates(mycontent): |
359 |
myupd.append(mysplit) |
360 |
return myupd, errors |
361 |
|
362 |
-def update_config_files(config_root, protect, protect_mask, update_iter, match_callback = None): |
363 |
+def update_config_files(config_root, protect, protect_mask, update_iter, |
364 |
+ match_callback=None, case_insensitive=False): |
365 |
"""Perform global updates on /etc/portage/package.*, /etc/portage/profile/package.*, |
366 |
/etc/portage/profile/packages and /etc/portage/sets. |
367 |
config_root - location of files to update |
368 |
@@ -406,7 +407,8 @@ def update_config_files(config_root, protect, protect_mask, update_iter, match_c |
369 |
sys.stdout.flush() |
370 |
|
371 |
protect_obj = ConfigProtect( |
372 |
- config_root, protect, protect_mask) |
373 |
+ config_root, protect, protect_mask, |
374 |
+ case_insensitive=case_insensitive) |
375 |
for x in update_files: |
376 |
updating_file = os.path.join(abs_user_config, x) |
377 |
if protect_obj.isprotected(updating_file): |
378 |
diff --git a/pym/portage/util/__init__.py b/pym/portage/util/__init__.py |
379 |
index ad3a351..d0cca5b 100644 |
380 |
--- a/pym/portage/util/__init__.py |
381 |
+++ b/pym/portage/util/__init__.py |
382 |
@@ -1555,10 +1555,12 @@ class LazyItemsDict(UserDict): |
383 |
return result |
384 |
|
385 |
class ConfigProtect(object): |
386 |
- def __init__(self, myroot, protect_list, mask_list): |
387 |
+ def __init__(self, myroot, protect_list, mask_list, |
388 |
+ case_insensitive=False): |
389 |
self.myroot = myroot |
390 |
self.protect_list = protect_list |
391 |
self.mask_list = mask_list |
392 |
+ self.case_insensitive = case_insensitive |
393 |
self.updateprotect() |
394 |
|
395 |
def updateprotect(self): |
396 |
@@ -1586,6 +1588,8 @@ class ConfigProtect(object): |
397 |
for x in self.mask_list: |
398 |
ppath = normalize_path( |
399 |
os.path.join(self.myroot, x.lstrip(os.path.sep))) |
400 |
+ if self.case_insensitive: |
401 |
+ ppath = ppath.lower() |
402 |
try: |
403 |
"""Use lstat so that anything, even a broken symlink can be |
404 |
protected.""" |
405 |
@@ -1606,6 +1610,8 @@ class ConfigProtect(object): |
406 |
masked = 0 |
407 |
protected = 0 |
408 |
sep = os.path.sep |
409 |
+ if self.case_insensitive: |
410 |
+ obj = obj.lower() |
411 |
for ppath in self.protect: |
412 |
if len(ppath) > masked and obj.startswith(ppath): |
413 |
if ppath in self._dirs: |
414 |
-- |
415 |
2.0.4 |