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 |
Note that this patch has already been merged into the prefix branch. |
12 |
|
13 |
bin/dispatch-conf | 8 +++++++- |
14 |
bin/etc-update | 10 ++++++++-- |
15 |
bin/portageq | 7 ++++--- |
16 |
bin/quickpkg | 4 +++- |
17 |
man/make.conf.5 | 4 ++++ |
18 |
pym/_emerge/depgraph.py | 4 +++- |
19 |
pym/portage/_global_updates.py | 4 +++- |
20 |
pym/portage/const.py | 1 + |
21 |
pym/portage/dbapi/vartree.py | 32 +++++++++++++++++++++++++++++++- |
22 |
pym/portage/update.py | 6 ++++-- |
23 |
pym/portage/util/__init__.py | 8 +++++++- |
24 |
11 files changed, 75 insertions(+), 13 deletions(-) |
25 |
|
26 |
diff --git a/bin/dispatch-conf b/bin/dispatch-conf |
27 |
index 8058d6f..83fbe93 100755 |
28 |
--- a/bin/dispatch-conf |
29 |
+++ b/bin/dispatch-conf |
30 |
@@ -35,6 +35,10 @@ from portage.process import find_binary, spawn |
31 |
FIND_EXTANT_CONFIGS = "find '%s' %s -name '._cfg????_%s' ! -name '.*~' ! -iname '.*.bak' -print" |
32 |
DIFF_CONTENTS = "diff -Nu '%s' '%s'" |
33 |
|
34 |
+if "case-insensitive-fs" in portage.settings.features: |
35 |
+ FIND_EXTANT_CONFIGS = \ |
36 |
+ FIND_EXTANT_CONFIGS.replace("-name '._cfg", "-iname '._cfg") |
37 |
+ |
38 |
# We need a secure scratch dir and python does silly verbose errors on the use of tempnam |
39 |
oldmask = os.umask(0o077) |
40 |
SCRATCH_DIR = None |
41 |
@@ -152,7 +156,9 @@ class dispatch: |
42 |
protect_obj = portage.util.ConfigProtect( |
43 |
config_root, config_paths, |
44 |
portage.util.shlex_split( |
45 |
- portage.settings.get('CONFIG_PROTECT_MASK', ''))) |
46 |
+ portage.settings.get('CONFIG_PROTECT_MASK', '')), |
47 |
+ case_insensitive=("case-insensitive-fs" |
48 |
+ in portage.settings.features)) |
49 |
|
50 |
# |
51 |
# Remove new configs identical to current |
52 |
diff --git a/bin/etc-update b/bin/etc-update |
53 |
index 0307688..e0f7224 100755 |
54 |
--- a/bin/etc-update |
55 |
+++ b/bin/etc-update |
56 |
@@ -113,12 +113,15 @@ scan() { |
57 |
[[ -d ${path%/*} ]] || continue |
58 |
local name_opt=$(get_basename_find_opt "${path##*/}") |
59 |
path="${path%/*}" |
60 |
- find_opts=( -maxdepth 1 -name "$name_opt" ) |
61 |
+ find_opts=( -maxdepth 1 ) |
62 |
else |
63 |
# Do not traverse hidden directories such as .svn or .git. |
64 |
local name_opt=$(get_basename_find_opt '*') |
65 |
- find_opts=( -name '.*' -type d -prune -o -name "$name_opt" ) |
66 |
+ find_opts=( -name '.*' -type d -prune -o ) |
67 |
fi |
68 |
+ ${case_insensitive} && \ |
69 |
+ find_opts+=( -iname ) || find_opts+=( -name ) |
70 |
+ find_opts+=( "$name_opt" ) |
71 |
find_opts+=( ! -name '.*~' ! -iname '.*.bak' -print ) |
72 |
|
73 |
if [ ! -w "${path}" ] ; then |
74 |
@@ -743,6 +746,7 @@ fi |
75 |
|
76 |
portage_vars=( |
77 |
CONFIG_PROTECT{,_MASK} |
78 |
+ FEATURES |
79 |
PORTAGE_CONFIGROOT |
80 |
PORTAGE_INST_{G,U}ID |
81 |
PORTAGE_TMPDIR |
82 |
@@ -759,6 +763,8 @@ fi |
83 |
|
84 |
export PORTAGE_TMPDIR |
85 |
SCAN_PATHS=${*:-${CONFIG_PROTECT}} |
86 |
+[[ " ${FEATURES} " == *" case-insensitive-fs "* ]] && \ |
87 |
+ case_insensitive=true || case_insensitive=false |
88 |
|
89 |
TMP="${PORTAGE_TMPDIR}/etc-update-$$" |
90 |
trap "die terminated" SIGTERM |
91 |
diff --git a/bin/portageq b/bin/portageq |
92 |
index 009f116..caf88e7 100755 |
93 |
--- a/bin/portageq |
94 |
+++ b/bin/portageq |
95 |
@@ -379,8 +379,8 @@ def is_protected(argv): |
96 |
protect = portage.util.shlex_split(settings.get("CONFIG_PROTECT", "")) |
97 |
protect_mask = portage.util.shlex_split( |
98 |
settings.get("CONFIG_PROTECT_MASK", "")) |
99 |
- protect_obj = ConfigProtect(root, protect, protect_mask) |
100 |
- |
101 |
+ protect_obj = ConfigProtect(root, protect, protect_mask, |
102 |
+ case_insensitive=("case-insensitive-fs" in settings.features)) |
103 |
if protect_obj.isprotected(f): |
104 |
return 0 |
105 |
return 1 |
106 |
@@ -414,7 +414,8 @@ def filter_protected(argv): |
107 |
protect = portage.util.shlex_split(settings.get("CONFIG_PROTECT", "")) |
108 |
protect_mask = portage.util.shlex_split( |
109 |
settings.get("CONFIG_PROTECT_MASK", "")) |
110 |
- protect_obj = ConfigProtect(root, protect, protect_mask) |
111 |
+ protect_obj = ConfigProtect(root, protect, protect_mask, |
112 |
+ case_insensitive=("case-insensitive-fs" in settings.features)) |
113 |
|
114 |
errors = 0 |
115 |
|
116 |
diff --git a/bin/quickpkg b/bin/quickpkg |
117 |
index cf75791..2c69a69 100755 |
118 |
--- a/bin/quickpkg |
119 |
+++ b/bin/quickpkg |
120 |
@@ -102,7 +102,9 @@ def quickpkg_atom(options, infos, arg, eout): |
121 |
if not include_config: |
122 |
confprot = ConfigProtect(eroot, |
123 |
shlex_split(settings.get("CONFIG_PROTECT", "")), |
124 |
- shlex_split(settings.get("CONFIG_PROTECT_MASK", ""))) |
125 |
+ shlex_split(settings.get("CONFIG_PROTECT_MASK", "")), |
126 |
+ case_insensitive=("case-insensitive-fs" |
127 |
+ in settings.features)) |
128 |
def protect(filename): |
129 |
if not confprot.isprotected(filename): |
130 |
return False |
131 |
diff --git a/man/make.conf.5 b/man/make.conf.5 |
132 |
index 84e894b..7b7daa4 100644 |
133 |
--- a/man/make.conf.5 |
134 |
+++ b/man/make.conf.5 |
135 |
@@ -265,6 +265,10 @@ Build binary packages for just packages in the system set. |
136 |
Enable a special progress indicator when \fBemerge\fR(1) is calculating |
137 |
dependencies. |
138 |
.TP |
139 |
+.B case\-insensitive\-fs |
140 |
+Use case\-insensitive file name comparisions when merging and unmerging |
141 |
+files. |
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 8b06f4c..404b2f1 100644 |
190 |
--- a/pym/portage/dbapi/vartree.py |
191 |
+++ b/pym/portage/dbapi/vartree.py |
192 |
@@ -1052,6 +1052,11 @@ class vardbapi(dbapi): |
193 |
def add(self, cpv): |
194 |
eroot_len = len(self._vardb._eroot) |
195 |
contents = self._vardb._dblink(cpv).getcontents() |
196 |
+ |
197 |
+ if "case-insensitive-fs" in self._vardb.settings.features: |
198 |
+ contents = dict((k.lower(), v) |
199 |
+ for k, v in contents.items()) |
200 |
+ |
201 |
pkg_hash = self._hash_pkg(cpv) |
202 |
if not contents: |
203 |
# Empty path is a code used to represent empty contents. |
204 |
@@ -1189,6 +1194,8 @@ class vardbapi(dbapi): |
205 |
hash_pkg = owners_cache._hash_pkg |
206 |
hash_str = owners_cache._hash_str |
207 |
base_names = self._vardb._aux_cache["owners"]["base_names"] |
208 |
+ case_insensitive = "case-insensitive-fs" \ |
209 |
+ in vardb.settings.features |
210 |
|
211 |
dblink_cache = {} |
212 |
|
213 |
@@ -1205,6 +1212,8 @@ class vardbapi(dbapi): |
214 |
while path_iter: |
215 |
|
216 |
path = path_iter.pop() |
217 |
+ if case_insensitive: |
218 |
+ path = path.lower() |
219 |
is_basename = os.sep != path[:1] |
220 |
if is_basename: |
221 |
name = path |
222 |
@@ -1236,6 +1245,8 @@ class vardbapi(dbapi): |
223 |
|
224 |
if is_basename: |
225 |
for p in dblink(cpv).getcontents(): |
226 |
+ if case_insensitive: |
227 |
+ p = p.lower() |
228 |
if os.path.basename(p) == name: |
229 |
owners.append((cpv, p[len(root):])) |
230 |
else: |
231 |
@@ -1265,8 +1276,12 @@ class vardbapi(dbapi): |
232 |
if not path_list: |
233 |
return |
234 |
|
235 |
+ case_insensitive = "case-insensitive-fs" \ |
236 |
+ in self._vardb.settings.features |
237 |
path_info_list = [] |
238 |
for path in path_list: |
239 |
+ if case_insensitive: |
240 |
+ path = path.lower() |
241 |
is_basename = os.sep != path[:1] |
242 |
if is_basename: |
243 |
name = path |
244 |
@@ -1285,6 +1300,8 @@ class vardbapi(dbapi): |
245 |
for path, name, is_basename in path_info_list: |
246 |
if is_basename: |
247 |
for p in dblnk.getcontents(): |
248 |
+ if case_insensitive: |
249 |
+ p = p.lower() |
250 |
if os.path.basename(p) == name: |
251 |
search_pkg.results.append((dblnk, p[len(root):])) |
252 |
else: |
253 |
@@ -1540,7 +1557,9 @@ class dblink(object): |
254 |
portage.util.shlex_split( |
255 |
self.settings.get("CONFIG_PROTECT", "")), |
256 |
portage.util.shlex_split( |
257 |
- self.settings.get("CONFIG_PROTECT_MASK", ""))) |
258 |
+ self.settings.get("CONFIG_PROTECT_MASK", "")), |
259 |
+ case_insensitive=("case-insensitive-fs" |
260 |
+ in self.settings.features)) |
261 |
|
262 |
return self._protect_obj |
263 |
|
264 |
@@ -2762,7 +2781,16 @@ class dblink(object): |
265 |
filename.lstrip(os_filename_arg.path.sep))) |
266 |
|
267 |
pkgfiles = self.getcontents() |
268 |
+ |
269 |
+ preserve_case = None |
270 |
+ if "case-insensitive-fs" in self.settings.features: |
271 |
+ destfile = destfile.lower() |
272 |
+ preserve_case = dict((k.lower(), k) for k in pkgfiles) |
273 |
+ pkgfiles = dict((k.lower(), v) for k, v in pkgfiles.items()) |
274 |
+ |
275 |
if pkgfiles and destfile in pkgfiles: |
276 |
+ if preserve_case is not None: |
277 |
+ return preserve_case[destfile] |
278 |
return destfile |
279 |
if pkgfiles: |
280 |
basename = os_filename_arg.path.basename(destfile) |
281 |
@@ -2855,6 +2883,8 @@ class dblink(object): |
282 |
for p_path in p_path_list: |
283 |
x = os_filename_arg.path.join(p_path, basename) |
284 |
if x in pkgfiles: |
285 |
+ if preserve_case is not None: |
286 |
+ return preserve_case[x] |
287 |
return x |
288 |
|
289 |
return False |
290 |
diff --git a/pym/portage/update.py b/pym/portage/update.py |
291 |
index df4e11b..83fc3d2 100644 |
292 |
--- a/pym/portage/update.py |
293 |
+++ b/pym/portage/update.py |
294 |
@@ -282,7 +282,8 @@ def parse_updates(mycontent): |
295 |
myupd.append(mysplit) |
296 |
return myupd, errors |
297 |
|
298 |
-def update_config_files(config_root, protect, protect_mask, update_iter, match_callback = None): |
299 |
+def update_config_files(config_root, protect, protect_mask, update_iter, |
300 |
+ match_callback=None, case_insensitive=False): |
301 |
"""Perform global updates on /etc/portage/package.*, /etc/portage/profile/package.*, |
302 |
/etc/portage/profile/packages and /etc/portage/sets. |
303 |
config_root - location of files to update |
304 |
@@ -406,7 +407,8 @@ def update_config_files(config_root, protect, protect_mask, update_iter, match_c |
305 |
sys.stdout.flush() |
306 |
|
307 |
protect_obj = ConfigProtect( |
308 |
- config_root, protect, protect_mask) |
309 |
+ config_root, protect, protect_mask, |
310 |
+ case_insensitive=case_insensitive) |
311 |
for x in update_files: |
312 |
updating_file = os.path.join(abs_user_config, x) |
313 |
if protect_obj.isprotected(updating_file): |
314 |
diff --git a/pym/portage/util/__init__.py b/pym/portage/util/__init__.py |
315 |
index ad3a351..d0cca5b 100644 |
316 |
--- a/pym/portage/util/__init__.py |
317 |
+++ b/pym/portage/util/__init__.py |
318 |
@@ -1555,10 +1555,12 @@ class LazyItemsDict(UserDict): |
319 |
return result |
320 |
|
321 |
class ConfigProtect(object): |
322 |
- def __init__(self, myroot, protect_list, mask_list): |
323 |
+ def __init__(self, myroot, protect_list, mask_list, |
324 |
+ case_insensitive=False): |
325 |
self.myroot = myroot |
326 |
self.protect_list = protect_list |
327 |
self.mask_list = mask_list |
328 |
+ self.case_insensitive = case_insensitive |
329 |
self.updateprotect() |
330 |
|
331 |
def updateprotect(self): |
332 |
@@ -1586,6 +1588,8 @@ class ConfigProtect(object): |
333 |
for x in self.mask_list: |
334 |
ppath = normalize_path( |
335 |
os.path.join(self.myroot, x.lstrip(os.path.sep))) |
336 |
+ if self.case_insensitive: |
337 |
+ ppath = ppath.lower() |
338 |
try: |
339 |
"""Use lstat so that anything, even a broken symlink can be |
340 |
protected.""" |
341 |
@@ -1606,6 +1610,8 @@ class ConfigProtect(object): |
342 |
masked = 0 |
343 |
protected = 0 |
344 |
sep = os.path.sep |
345 |
+ if self.case_insensitive: |
346 |
+ obj = obj.lower() |
347 |
for ppath in self.protect: |
348 |
if len(ppath) > masked and obj.startswith(ppath): |
349 |
if ppath in self._dirs: |
350 |
-- |
351 |
2.0.4 |