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] FEATURES=case-insensitive-fs for bug #524236
Date: Thu, 13 Nov 2014 01:22:46
Message-Id: 1415841756-8430-1-git-send-email-zmedico@gentoo.org
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

Replies