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 | 57 +++++++++++++++++++++++++++--------------- |
20 |
pym/portage/update.py | 6 +++-- |
21 |
pym/portage/util/__init__.py | 8 +++++- |
22 |
11 files changed, 83 insertions(+), 32 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 2a839d0..6f1910d 100644 |
148 |
--- a/pym/_emerge/depgraph.py |
149 |
+++ b/pym/_emerge/depgraph.py |
150 |
@@ -7813,7 +7813,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..81ee484 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 22f41d0..faee773 100644 |
190 |
--- a/pym/portage/dbapi/vartree.py |
191 |
+++ b/pym/portage/dbapi/vartree.py |
192 |
@@ -1052,13 +1052,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_keys(): |
206 |
self._add_path(x[eroot_len:], pkg_hash) |
207 |
|
208 |
self._vardb._aux_cache["modified"].add(cpv) |
209 |
@@ -1190,6 +1190,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 |
@@ -1206,6 +1208,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 |
@@ -1236,9 +1240,11 @@ class vardbapi(dbapi): |
228 |
continue |
229 |
|
230 |
if is_basename: |
231 |
- for p in dblink(cpv).getcontents(): |
232 |
+ for p in dblink(cpv)._contents_keys(): |
233 |
if os.path.basename(p) == name: |
234 |
- owners.append((cpv, p[len(root):])) |
235 |
+ owners.append((cpv, |
236 |
+ dblink(cpv)._contents_unmap_key( |
237 |
+ p)[len(root):])) |
238 |
else: |
239 |
if dblink(cpv).isowner(path): |
240 |
owners.append((cpv, path)) |
241 |
@@ -1266,8 +1272,12 @@ class vardbapi(dbapi): |
242 |
if not path_list: |
243 |
return |
244 |
|
245 |
+ case_insensitive = "case-insensitive-fs" \ |
246 |
+ in self._vardb.settings.features |
247 |
path_info_list = [] |
248 |
for path in path_list: |
249 |
+ if case_insensitive: |
250 |
+ path = path.lower() |
251 |
is_basename = os.sep != path[:1] |
252 |
if is_basename: |
253 |
name = path |
254 |
@@ -1285,9 +1295,11 @@ class vardbapi(dbapi): |
255 |
dblnk = self._vardb._dblink(cpv) |
256 |
for path, name, is_basename in path_info_list: |
257 |
if is_basename: |
258 |
- for p in dblnk.getcontents(): |
259 |
+ for p in dblnk._contents_keys(): |
260 |
if os.path.basename(p) == name: |
261 |
- search_pkg.results.append((dblnk, p[len(root):])) |
262 |
+ search_pkg.results.append((dblnk, |
263 |
+ dblnk._contents_unmap_key( |
264 |
+ p)[len(root):])) |
265 |
else: |
266 |
if dblnk.isowner(path): |
267 |
search_pkg.results.append((dblnk, path)) |
268 |
@@ -1547,7 +1559,9 @@ class dblink(object): |
269 |
portage.util.shlex_split( |
270 |
self.settings.get("CONFIG_PROTECT", "")), |
271 |
portage.util.shlex_split( |
272 |
- self.settings.get("CONFIG_PROTECT_MASK", ""))) |
273 |
+ self.settings.get("CONFIG_PROTECT_MASK", "")), |
274 |
+ case_insensitive=("case-insensitive-fs" |
275 |
+ in self.settings.features)) |
276 |
|
277 |
return self._protect_obj |
278 |
|
279 |
@@ -2769,15 +2783,18 @@ class dblink(object): |
280 |
os_filename_arg.path.join(destroot, |
281 |
filename.lstrip(os_filename_arg.path.sep))) |
282 |
|
283 |
- pkgfiles = self.getcontents() |
284 |
- if pkgfiles and destfile in pkgfiles: |
285 |
- return destfile |
286 |
- if pkgfiles: |
287 |
+ if "case-insensitive-fs" in self.settings.features: |
288 |
+ destfile = destfile.lower() |
289 |
+ |
290 |
+ if self._contents_contains(destfile): |
291 |
+ return self._contents_unmap_key(destfile) |
292 |
+ |
293 |
+ if self.getcontents(): |
294 |
basename = os_filename_arg.path.basename(destfile) |
295 |
if self._contents_basenames is None: |
296 |
|
297 |
try: |
298 |
- for x in pkgfiles: |
299 |
+ for x in self._contents_keys(): |
300 |
_unicode_encode(x, |
301 |
encoding=_encodings['merge'], |
302 |
errors='strict') |
303 |
@@ -2786,7 +2803,7 @@ class dblink(object): |
304 |
# different value of sys.getfilesystemencoding(), |
305 |
# so fall back to utf_8 if appropriate. |
306 |
try: |
307 |
- for x in pkgfiles: |
308 |
+ for x in self._contents_keys(): |
309 |
_unicode_encode(x, |
310 |
encoding=_encodings['fs'], |
311 |
errors='strict') |
312 |
@@ -2796,7 +2813,7 @@ class dblink(object): |
313 |
os = portage.os |
314 |
|
315 |
self._contents_basenames = set( |
316 |
- os.path.basename(x) for x in pkgfiles) |
317 |
+ os.path.basename(x) for x in self._contents_keys()) |
318 |
if basename not in self._contents_basenames: |
319 |
# This is a shortcut that, in most cases, allows us to |
320 |
# eliminate this package as an owner without the need |
321 |
@@ -2817,7 +2834,7 @@ class dblink(object): |
322 |
|
323 |
if os is _os_merge: |
324 |
try: |
325 |
- for x in pkgfiles: |
326 |
+ for x in self._contents_keys(): |
327 |
_unicode_encode(x, |
328 |
encoding=_encodings['merge'], |
329 |
errors='strict') |
330 |
@@ -2826,7 +2843,7 @@ class dblink(object): |
331 |
# different value of sys.getfilesystemencoding(), |
332 |
# so fall back to utf_8 if appropriate. |
333 |
try: |
334 |
- for x in pkgfiles: |
335 |
+ for x in self._contents_keys(): |
336 |
_unicode_encode(x, |
337 |
encoding=_encodings['fs'], |
338 |
errors='strict') |
339 |
@@ -2837,7 +2854,7 @@ class dblink(object): |
340 |
|
341 |
self._contents_inodes = {} |
342 |
parent_paths = set() |
343 |
- for x in pkgfiles: |
344 |
+ for x in self._contents_keys(): |
345 |
p_path = os.path.dirname(x) |
346 |
if p_path in parent_paths: |
347 |
continue |
348 |
@@ -2862,8 +2879,8 @@ class dblink(object): |
349 |
if p_path_list: |
350 |
for p_path in p_path_list: |
351 |
x = os_filename_arg.path.join(p_path, basename) |
352 |
- if x in pkgfiles: |
353 |
- return x |
354 |
+ if self._contents_contains(x): |
355 |
+ return self._contents_unmap_key(x) |
356 |
|
357 |
return False |
358 |
|
359 |
diff --git a/pym/portage/update.py b/pym/portage/update.py |
360 |
index df4e11b..83fc3d2 100644 |
361 |
--- a/pym/portage/update.py |
362 |
+++ b/pym/portage/update.py |
363 |
@@ -282,7 +282,8 @@ def parse_updates(mycontent): |
364 |
myupd.append(mysplit) |
365 |
return myupd, errors |
366 |
|
367 |
-def update_config_files(config_root, protect, protect_mask, update_iter, match_callback = None): |
368 |
+def update_config_files(config_root, protect, protect_mask, update_iter, |
369 |
+ match_callback=None, case_insensitive=False): |
370 |
"""Perform global updates on /etc/portage/package.*, /etc/portage/profile/package.*, |
371 |
/etc/portage/profile/packages and /etc/portage/sets. |
372 |
config_root - location of files to update |
373 |
@@ -406,7 +407,8 @@ def update_config_files(config_root, protect, protect_mask, update_iter, match_c |
374 |
sys.stdout.flush() |
375 |
|
376 |
protect_obj = ConfigProtect( |
377 |
- config_root, protect, protect_mask) |
378 |
+ config_root, protect, protect_mask, |
379 |
+ case_insensitive=case_insensitive) |
380 |
for x in update_files: |
381 |
updating_file = os.path.join(abs_user_config, x) |
382 |
if protect_obj.isprotected(updating_file): |
383 |
diff --git a/pym/portage/util/__init__.py b/pym/portage/util/__init__.py |
384 |
index ad3a351..d0cca5b 100644 |
385 |
--- a/pym/portage/util/__init__.py |
386 |
+++ b/pym/portage/util/__init__.py |
387 |
@@ -1555,10 +1555,12 @@ class LazyItemsDict(UserDict): |
388 |
return result |
389 |
|
390 |
class ConfigProtect(object): |
391 |
- def __init__(self, myroot, protect_list, mask_list): |
392 |
+ def __init__(self, myroot, protect_list, mask_list, |
393 |
+ case_insensitive=False): |
394 |
self.myroot = myroot |
395 |
self.protect_list = protect_list |
396 |
self.mask_list = mask_list |
397 |
+ self.case_insensitive = case_insensitive |
398 |
self.updateprotect() |
399 |
|
400 |
def updateprotect(self): |
401 |
@@ -1586,6 +1588,8 @@ class ConfigProtect(object): |
402 |
for x in self.mask_list: |
403 |
ppath = normalize_path( |
404 |
os.path.join(self.myroot, x.lstrip(os.path.sep))) |
405 |
+ if self.case_insensitive: |
406 |
+ ppath = ppath.lower() |
407 |
try: |
408 |
"""Use lstat so that anything, even a broken symlink can be |
409 |
protected.""" |
410 |
@@ -1606,6 +1610,8 @@ class ConfigProtect(object): |
411 |
masked = 0 |
412 |
protected = 0 |
413 |
sep = os.path.sep |
414 |
+ if self.case_insensitive: |
415 |
+ obj = obj.lower() |
416 |
for ppath in self.protect: |
417 |
if len(ppath) > masked and obj.startswith(ppath): |
418 |
if ppath in self._dirs: |
419 |
-- |
420 |
2.0.4 |