* [gentoo-portage-dev] [PATCH] FEATURES=case-insensitive-fs for bug #524236
@ 2014-11-13 1:22 Zac Medico
2014-11-13 10:29 ` Alexander Berntsen
0 siblings, 1 reply; 12+ messages in thread
From: Zac Medico @ 2014-11-13 1:22 UTC (permalink / raw
To: gentoo-portage-dev; +Cc: Zac Medico
When case-insensitive-fs is enabled in FEATURES, the dblink.isowner
method, _owners_db class, and ConfigProtect class will be
case-insensitive. This causes the collision-protect and unmerge code
to behave correctly for a case-insensitive file system. If the file
system is case-insensitive but case-preserving, then case is preserved
in the CONTENTS data of installed packages.
X-Gentoo-Bug: 524236
X-Gentoo-Url: https://bugs.gentoo.org/show_bug.cgi?id=524236
---
Note that this patch has already been merged into the prefix branch.
bin/dispatch-conf | 8 +++++++-
bin/etc-update | 10 ++++++++--
bin/portageq | 7 ++++---
bin/quickpkg | 4 +++-
man/make.conf.5 | 4 ++++
pym/_emerge/depgraph.py | 4 +++-
pym/portage/_global_updates.py | 4 +++-
pym/portage/const.py | 1 +
pym/portage/dbapi/vartree.py | 32 +++++++++++++++++++++++++++++++-
pym/portage/update.py | 6 ++++--
pym/portage/util/__init__.py | 8 +++++++-
11 files changed, 75 insertions(+), 13 deletions(-)
diff --git a/bin/dispatch-conf b/bin/dispatch-conf
index 8058d6f..83fbe93 100755
--- a/bin/dispatch-conf
+++ b/bin/dispatch-conf
@@ -35,6 +35,10 @@ from portage.process import find_binary, spawn
FIND_EXTANT_CONFIGS = "find '%s' %s -name '._cfg????_%s' ! -name '.*~' ! -iname '.*.bak' -print"
DIFF_CONTENTS = "diff -Nu '%s' '%s'"
+if "case-insensitive-fs" in portage.settings.features:
+ FIND_EXTANT_CONFIGS = \
+ FIND_EXTANT_CONFIGS.replace("-name '._cfg", "-iname '._cfg")
+
# We need a secure scratch dir and python does silly verbose errors on the use of tempnam
oldmask = os.umask(0o077)
SCRATCH_DIR = None
@@ -152,7 +156,9 @@ class dispatch:
protect_obj = portage.util.ConfigProtect(
config_root, config_paths,
portage.util.shlex_split(
- portage.settings.get('CONFIG_PROTECT_MASK', '')))
+ portage.settings.get('CONFIG_PROTECT_MASK', '')),
+ case_insensitive=("case-insensitive-fs"
+ in portage.settings.features))
#
# Remove new configs identical to current
diff --git a/bin/etc-update b/bin/etc-update
index 0307688..e0f7224 100755
--- a/bin/etc-update
+++ b/bin/etc-update
@@ -113,12 +113,15 @@ scan() {
[[ -d ${path%/*} ]] || continue
local name_opt=$(get_basename_find_opt "${path##*/}")
path="${path%/*}"
- find_opts=( -maxdepth 1 -name "$name_opt" )
+ find_opts=( -maxdepth 1 )
else
# Do not traverse hidden directories such as .svn or .git.
local name_opt=$(get_basename_find_opt '*')
- find_opts=( -name '.*' -type d -prune -o -name "$name_opt" )
+ find_opts=( -name '.*' -type d -prune -o )
fi
+ ${case_insensitive} && \
+ find_opts+=( -iname ) || find_opts+=( -name )
+ find_opts+=( "$name_opt" )
find_opts+=( ! -name '.*~' ! -iname '.*.bak' -print )
if [ ! -w "${path}" ] ; then
@@ -743,6 +746,7 @@ fi
portage_vars=(
CONFIG_PROTECT{,_MASK}
+ FEATURES
PORTAGE_CONFIGROOT
PORTAGE_INST_{G,U}ID
PORTAGE_TMPDIR
@@ -759,6 +763,8 @@ fi
export PORTAGE_TMPDIR
SCAN_PATHS=${*:-${CONFIG_PROTECT}}
+[[ " ${FEATURES} " == *" case-insensitive-fs "* ]] && \
+ case_insensitive=true || case_insensitive=false
TMP="${PORTAGE_TMPDIR}/etc-update-$$"
trap "die terminated" SIGTERM
diff --git a/bin/portageq b/bin/portageq
index 009f116..caf88e7 100755
--- a/bin/portageq
+++ b/bin/portageq
@@ -379,8 +379,8 @@ def is_protected(argv):
protect = portage.util.shlex_split(settings.get("CONFIG_PROTECT", ""))
protect_mask = portage.util.shlex_split(
settings.get("CONFIG_PROTECT_MASK", ""))
- protect_obj = ConfigProtect(root, protect, protect_mask)
-
+ protect_obj = ConfigProtect(root, protect, protect_mask,
+ case_insensitive=("case-insensitive-fs" in settings.features))
if protect_obj.isprotected(f):
return 0
return 1
@@ -414,7 +414,8 @@ def filter_protected(argv):
protect = portage.util.shlex_split(settings.get("CONFIG_PROTECT", ""))
protect_mask = portage.util.shlex_split(
settings.get("CONFIG_PROTECT_MASK", ""))
- protect_obj = ConfigProtect(root, protect, protect_mask)
+ protect_obj = ConfigProtect(root, protect, protect_mask,
+ case_insensitive=("case-insensitive-fs" in settings.features))
errors = 0
diff --git a/bin/quickpkg b/bin/quickpkg
index cf75791..2c69a69 100755
--- a/bin/quickpkg
+++ b/bin/quickpkg
@@ -102,7 +102,9 @@ def quickpkg_atom(options, infos, arg, eout):
if not include_config:
confprot = ConfigProtect(eroot,
shlex_split(settings.get("CONFIG_PROTECT", "")),
- shlex_split(settings.get("CONFIG_PROTECT_MASK", "")))
+ shlex_split(settings.get("CONFIG_PROTECT_MASK", "")),
+ case_insensitive=("case-insensitive-fs"
+ in settings.features))
def protect(filename):
if not confprot.isprotected(filename):
return False
diff --git a/man/make.conf.5 b/man/make.conf.5
index 84e894b..7b7daa4 100644
--- a/man/make.conf.5
+++ b/man/make.conf.5
@@ -265,6 +265,10 @@ Build binary packages for just packages in the system set.
Enable a special progress indicator when \fBemerge\fR(1) is calculating
dependencies.
.TP
+.B case\-insensitive\-fs
+Use case\-insensitive file name comparisions when merging and unmerging
+files.
+.TP
.B ccache
Enable portage support for the ccache package. If the ccache dir is not
present in the user's environment, then portage will default to
diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py
index 94eaed8..6bf2be5 100644
--- a/pym/_emerge/depgraph.py
+++ b/pym/_emerge/depgraph.py
@@ -7805,7 +7805,9 @@ class depgraph(object):
settings = self._frozen_config.roots[root].settings
protect_obj[root] = ConfigProtect(settings["EROOT"], \
shlex_split(settings.get("CONFIG_PROTECT", "")),
- shlex_split(settings.get("CONFIG_PROTECT_MASK", "")))
+ shlex_split(settings.get("CONFIG_PROTECT_MASK", "")),
+ case_insensitive=("case-insensitive-fs"
+ in settings.features))
def write_changes(root, changes, file_to_write_to):
file_contents = None
diff --git a/pym/portage/_global_updates.py b/pym/portage/_global_updates.py
index 17dc080..87085de 100644
--- a/pym/portage/_global_updates.py
+++ b/pym/portage/_global_updates.py
@@ -208,7 +208,9 @@ def _do_global_updates(trees, prev_mtimes, quiet=False, if_mtime_changed=True):
update_config_files(root,
shlex_split(mysettings.get("CONFIG_PROTECT", "")),
shlex_split(mysettings.get("CONFIG_PROTECT_MASK", "")),
- repo_map, match_callback=_config_repo_match)
+ repo_map, match_callback = _config_repo_match,
+ case_insensitive="case-insensitive-fs"
+ in mysettings.features)
# The above global updates proceed quickly, so they
# are considered a single mtimedb transaction.
diff --git a/pym/portage/const.py b/pym/portage/const.py
index d472075..febdb4a 100644
--- a/pym/portage/const.py
+++ b/pym/portage/const.py
@@ -125,6 +125,7 @@ SUPPORTED_FEATURES = frozenset([
"buildpkg",
"buildsyspkg",
"candy",
+ "case-insensitive-fs",
"ccache",
"cgroup",
"chflags",
diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py
index 8b06f4c..404b2f1 100644
--- a/pym/portage/dbapi/vartree.py
+++ b/pym/portage/dbapi/vartree.py
@@ -1052,6 +1052,11 @@ class vardbapi(dbapi):
def add(self, cpv):
eroot_len = len(self._vardb._eroot)
contents = self._vardb._dblink(cpv).getcontents()
+
+ if "case-insensitive-fs" in self._vardb.settings.features:
+ contents = dict((k.lower(), v)
+ for k, v in contents.items())
+
pkg_hash = self._hash_pkg(cpv)
if not contents:
# Empty path is a code used to represent empty contents.
@@ -1189,6 +1194,8 @@ class vardbapi(dbapi):
hash_pkg = owners_cache._hash_pkg
hash_str = owners_cache._hash_str
base_names = self._vardb._aux_cache["owners"]["base_names"]
+ case_insensitive = "case-insensitive-fs" \
+ in vardb.settings.features
dblink_cache = {}
@@ -1205,6 +1212,8 @@ class vardbapi(dbapi):
while path_iter:
path = path_iter.pop()
+ if case_insensitive:
+ path = path.lower()
is_basename = os.sep != path[:1]
if is_basename:
name = path
@@ -1236,6 +1245,8 @@ class vardbapi(dbapi):
if is_basename:
for p in dblink(cpv).getcontents():
+ if case_insensitive:
+ p = p.lower()
if os.path.basename(p) == name:
owners.append((cpv, p[len(root):]))
else:
@@ -1265,8 +1276,12 @@ class vardbapi(dbapi):
if not path_list:
return
+ case_insensitive = "case-insensitive-fs" \
+ in self._vardb.settings.features
path_info_list = []
for path in path_list:
+ if case_insensitive:
+ path = path.lower()
is_basename = os.sep != path[:1]
if is_basename:
name = path
@@ -1285,6 +1300,8 @@ class vardbapi(dbapi):
for path, name, is_basename in path_info_list:
if is_basename:
for p in dblnk.getcontents():
+ if case_insensitive:
+ p = p.lower()
if os.path.basename(p) == name:
search_pkg.results.append((dblnk, p[len(root):]))
else:
@@ -1540,7 +1557,9 @@ class dblink(object):
portage.util.shlex_split(
self.settings.get("CONFIG_PROTECT", "")),
portage.util.shlex_split(
- self.settings.get("CONFIG_PROTECT_MASK", "")))
+ self.settings.get("CONFIG_PROTECT_MASK", "")),
+ case_insensitive=("case-insensitive-fs"
+ in self.settings.features))
return self._protect_obj
@@ -2762,7 +2781,16 @@ class dblink(object):
filename.lstrip(os_filename_arg.path.sep)))
pkgfiles = self.getcontents()
+
+ preserve_case = None
+ if "case-insensitive-fs" in self.settings.features:
+ destfile = destfile.lower()
+ preserve_case = dict((k.lower(), k) for k in pkgfiles)
+ pkgfiles = dict((k.lower(), v) for k, v in pkgfiles.items())
+
if pkgfiles and destfile in pkgfiles:
+ if preserve_case is not None:
+ return preserve_case[destfile]
return destfile
if pkgfiles:
basename = os_filename_arg.path.basename(destfile)
@@ -2855,6 +2883,8 @@ class dblink(object):
for p_path in p_path_list:
x = os_filename_arg.path.join(p_path, basename)
if x in pkgfiles:
+ if preserve_case is not None:
+ return preserve_case[x]
return x
return False
diff --git a/pym/portage/update.py b/pym/portage/update.py
index df4e11b..83fc3d2 100644
--- a/pym/portage/update.py
+++ b/pym/portage/update.py
@@ -282,7 +282,8 @@ def parse_updates(mycontent):
myupd.append(mysplit)
return myupd, errors
-def update_config_files(config_root, protect, protect_mask, update_iter, match_callback = None):
+def update_config_files(config_root, protect, protect_mask, update_iter,
+ match_callback=None, case_insensitive=False):
"""Perform global updates on /etc/portage/package.*, /etc/portage/profile/package.*,
/etc/portage/profile/packages and /etc/portage/sets.
config_root - location of files to update
@@ -406,7 +407,8 @@ def update_config_files(config_root, protect, protect_mask, update_iter, match_c
sys.stdout.flush()
protect_obj = ConfigProtect(
- config_root, protect, protect_mask)
+ config_root, protect, protect_mask,
+ case_insensitive=case_insensitive)
for x in update_files:
updating_file = os.path.join(abs_user_config, x)
if protect_obj.isprotected(updating_file):
diff --git a/pym/portage/util/__init__.py b/pym/portage/util/__init__.py
index ad3a351..d0cca5b 100644
--- a/pym/portage/util/__init__.py
+++ b/pym/portage/util/__init__.py
@@ -1555,10 +1555,12 @@ class LazyItemsDict(UserDict):
return result
class ConfigProtect(object):
- def __init__(self, myroot, protect_list, mask_list):
+ def __init__(self, myroot, protect_list, mask_list,
+ case_insensitive=False):
self.myroot = myroot
self.protect_list = protect_list
self.mask_list = mask_list
+ self.case_insensitive = case_insensitive
self.updateprotect()
def updateprotect(self):
@@ -1586,6 +1588,8 @@ class ConfigProtect(object):
for x in self.mask_list:
ppath = normalize_path(
os.path.join(self.myroot, x.lstrip(os.path.sep)))
+ if self.case_insensitive:
+ ppath = ppath.lower()
try:
"""Use lstat so that anything, even a broken symlink can be
protected."""
@@ -1606,6 +1610,8 @@ class ConfigProtect(object):
masked = 0
protected = 0
sep = os.path.sep
+ if self.case_insensitive:
+ obj = obj.lower()
for ppath in self.protect:
if len(ppath) > masked and obj.startswith(ppath):
if ppath in self._dirs:
--
2.0.4
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [gentoo-portage-dev] [PATCH] FEATURES=case-insensitive-fs for bug #524236
2014-11-13 1:22 [gentoo-portage-dev] [PATCH] FEATURES=case-insensitive-fs for bug #524236 Zac Medico
@ 2014-11-13 10:29 ` Alexander Berntsen
2014-11-13 17:58 ` Zac Medico
0 siblings, 1 reply; 12+ messages in thread
From: Alexander Berntsen @ 2014-11-13 10:29 UTC (permalink / raw
To: gentoo-portage-dev
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
On 13/11/14 02:22, Zac Medico wrote:
> +if "case-insensitive-fs" in portage.settings.features:
> + FIND_EXTANT_CONFIGS = \
> + FIND_EXTANT_CONFIGS.replace("-name '._cfg", "-iname '._cfg")
> +
Splitting inside the replace will look nicer following PEP indentation
(as you won't need the '\').
> +Use case\-insensitive file name comparisions when merging and unmerging
> +files.
> +.TP
Maybe mention a) that most people can ignore this option, and b) who
it's actually for. Kind of in the kernel option help style.
In general I don't like this patch. It handles a bunch of cases separately by doing lower(), when I think instead it should be handled implicitly. The data should be in a structure such that it knows whether it is supposed to be upper or lowercase, and whatever's dealing with it should deal with it accordingly, rather than checking "is this case insensitive? OK lowercase it before sending it wherever".
But if you think this is the best way, I'm not going to stand in the way of this patch.
- --
Alexander
bernalex@gentoo.org
https://secure.plaimi.net/~alexander
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2
Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/
iF4EAREIAAYFAlRkiB0ACgkQRtClrXBQc7UudAD/V1r4AR5zA54Xz+LHBVNt0bnn
uQ9w+146L8WYK6pDN9gBAJxZbQREeOwxKSDjluZ1lq9XARf1rh/Eqzl58wIc8I4K
=c1Em
-----END PGP SIGNATURE-----
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [gentoo-portage-dev] [PATCH] FEATURES=case-insensitive-fs for bug #524236
2014-11-13 10:29 ` Alexander Berntsen
@ 2014-11-13 17:58 ` Zac Medico
2014-11-16 10:41 ` [gentoo-portage-dev] [PATCH 1/2] dblink: case insensitive support " Zac Medico
0 siblings, 1 reply; 12+ messages in thread
From: Zac Medico @ 2014-11-13 17:58 UTC (permalink / raw
To: gentoo-portage-dev
On 11/13/2014 02:29 AM, Alexander Berntsen wrote:
> On 13/11/14 02:22, Zac Medico wrote:
>> +if "case-insensitive-fs" in portage.settings.features:
>> + FIND_EXTANT_CONFIGS = \
>> + FIND_EXTANT_CONFIGS.replace("-name '._cfg", "-iname '._cfg")
>> +
> Splitting inside the replace will look nicer following PEP indentation
> (as you won't need the '\').
Okay, I'll re-format it as you've suggested.
>> +Use case\-insensitive file name comparisions when merging and unmerging
>> +files.
>> +.TP
> Maybe mention a) that most people can ignore this option, and b) who
> it's actually for. Kind of in the kernel option help style.
Okay, I'll add something about it only being needed for case-insensitive
file systems, which are usually not used.
>
> In general I don't like this patch. It handles a bunch of cases separately
> by doing lower(), when I think instead it should be handled implicitly.
> The data should be in a structure such that it knows whether it is supposed
> to be upper or lowercase, and whatever's dealing with it should deal with
> it accordingly, rather than checking "is this case insensitive? OK
> lowercase it before sending it wherever".
Aside from the ConfigProtect constructor, which has a new
case_insensitive keyword parameter, all affected methods handle case
transformations "implicitly", as far as API consumers are concerned.
However, we could improve efficiency for some usage patterns by
providing an alternative to dblink.getcontents that is oriented toward
case-insensitive handling. For example, every single
dblink._match_contents call currently has to transform all names to
lowercase, and generate a reverse mapping from lowercase back to
preserved case. The dblink._match_contents method would be more
efficient if we created an alternative to dblink.getcontents that
handled the transformations and cached the results. I intentionally did
not implement this optimization yet, since it's probably better to do it
in a separate patch, rather than complicate the current patch.
> But if you think this is the best way, I'm not going to stand in the way of this patch.
As discussed above, think the current approach is pretty reasonable,
though I would like to optimize it with a separate patch.
--
Thanks,
Zac
^ permalink raw reply [flat|nested] 12+ messages in thread
* [gentoo-portage-dev] [PATCH 1/2] dblink: case insensitive support for bug #524236
2014-11-13 17:58 ` Zac Medico
@ 2014-11-16 10:41 ` Zac Medico
2014-11-16 10:41 ` [gentoo-portage-dev] [PATCH 2/2] FEATURES=case-insensitive-fs " Zac Medico
2014-11-16 18:11 ` [gentoo-portage-dev] [PATCH 1/2] dblink: case insensitive support " Brian Dolbec
0 siblings, 2 replies; 12+ messages in thread
From: Zac Medico @ 2014-11-16 10:41 UTC (permalink / raw
To: gentoo-portage-dev; +Cc: Zac Medico
This adds dblink._contents_contains, _contents_iter, and _contents_key
methods that provide an interface for contents operations with "implicit"
case handling.
X-Gentoo-Bug: 524236
X-Gentoo-Url: https://bugs.gentoo.org/show_bug.cgi?id=524236
---
pym/portage/dbapi/vartree.py | 43 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py
index 8b06f4c..2d1003f 100644
--- a/pym/portage/dbapi/vartree.py
+++ b/pym/portage/dbapi/vartree.py
@@ -1515,6 +1515,8 @@ class dblink(object):
self.contentscache = None
self._contents_inodes = None
self._contents_basenames = None
+ self._contents_case_insensitive = None
+ self._contents_case_reverse_map = None
self._linkmap_broken = False
self._device_path_map = {}
self._hardlink_merge_map = {}
@@ -1526,6 +1528,15 @@ class dblink(object):
# compliance with RESTRICT=preserve-libs.
self._preserve_libs = "preserve-libs" in mysettings.features
+ if "case-insensitive-fs" in self.settings.features:
+ self._contents_key = self._contents_key_case_insensitive
+ self._contents_contains = self._contents_contains_case_insensitive
+ self._contents_iter = self._contents_iter_case_insensitive
+ else:
+ self._contents_key = self._contents_key_case_sensitive
+ self._contents_contains = self._contents_contains_case_sensitive
+ self._contents_iter = self._contents_iter_case_sensitive
+
def __hash__(self):
return hash(self._hash_key)
@@ -1612,6 +1623,8 @@ class dblink(object):
self.contentscache = None
self._contents_inodes = None
self._contents_basenames = None
+ self._contents_case_insensitive = None
+ self._contents_case_reverse_map = None
def getcontents(self):
"""
@@ -1716,6 +1729,36 @@ class dblink(object):
self.contentscache = pkgfiles
return pkgfiles
+ def _contents_case_insensitive_init(self):
+ self._contents_case_insensitive = dict(
+ (k.lower(), v) for k, v in self.getcontents().items())
+ self._contents_case_reverse_map = dict(
+ (k.lower(), k) for k in self.getcontents())
+
+ def _contents_iter_case_insensitive(self):
+ if self._contents_case_insensitive is None:
+ self._contents_case_insensitive_init()
+ return iter(self._contents_case_insensitive)
+
+ def _contents_key_case_insensitive(self, key):
+ if self._contents_case_reverse_map is None:
+ self._contents_case_insensitive_init()
+ return self._contents_case_reverse_map[key]
+
+ def _contents_contains_case_insensitive(self, key):
+ if self._contents_case_insensitive is None:
+ self._contents_case_insensitive_init()
+ return key.lower() in self._contents_case_insensitive
+
+ def _contents_key_case_sensitive(self, key):
+ return key
+
+ def _contents_iter_case_sensitive(self):
+ return iter(self.getcontents())
+
+ def _contents_contains_case_sensitive(self, key):
+ return key in self.getcontents()
+
def _prune_plib_registry(self, unmerge=False,
needed=None, preserve_paths=None):
# remove preserved libraries that don't have any consumers left
--
2.0.4
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [gentoo-portage-dev] [PATCH 2/2] FEATURES=case-insensitive-fs for bug #524236
2014-11-16 10:41 ` [gentoo-portage-dev] [PATCH 1/2] dblink: case insensitive support " Zac Medico
@ 2014-11-16 10:41 ` Zac Medico
2014-11-16 18:11 ` [gentoo-portage-dev] [PATCH 1/2] dblink: case insensitive support " Brian Dolbec
1 sibling, 0 replies; 12+ messages in thread
From: Zac Medico @ 2014-11-16 10:41 UTC (permalink / raw
To: gentoo-portage-dev; +Cc: Zac Medico
When case-insensitive-fs is enabled in FEATURES, the dblink.isowner
method, _owners_db class, and ConfigProtect class will be
case-insensitive. This causes the collision-protect and unmerge code
to behave correctly for a case-insensitive file system. If the file
system is case-insensitive but case-preserving, then case is preserved
in the CONTENTS data of installed packages.
X-Gentoo-Bug: 524236
X-Gentoo-Url: https://bugs.gentoo.org/show_bug.cgi?id=524236
---
bin/dispatch-conf | 8 ++++++-
bin/etc-update | 10 ++++++--
bin/portageq | 7 +++---
bin/quickpkg | 4 +++-
man/make.conf.5 | 6 +++++
pym/_emerge/depgraph.py | 4 +++-
pym/portage/_global_updates.py | 4 +++-
pym/portage/const.py | 1 +
pym/portage/dbapi/vartree.py | 53 +++++++++++++++++++++++++++---------------
pym/portage/update.py | 6 +++--
pym/portage/util/__init__.py | 8 ++++++-
11 files changed, 80 insertions(+), 31 deletions(-)
diff --git a/bin/dispatch-conf b/bin/dispatch-conf
index 8058d6f..b679910 100755
--- a/bin/dispatch-conf
+++ b/bin/dispatch-conf
@@ -35,6 +35,10 @@ from portage.process import find_binary, spawn
FIND_EXTANT_CONFIGS = "find '%s' %s -name '._cfg????_%s' ! -name '.*~' ! -iname '.*.bak' -print"
DIFF_CONTENTS = "diff -Nu '%s' '%s'"
+if "case-insensitive-fs" in portage.settings.features:
+ FIND_EXTANT_CONFIGS = FIND_EXTANT_CONFIGS.replace(
+ "-name '._cfg", "-iname '._cfg")
+
# We need a secure scratch dir and python does silly verbose errors on the use of tempnam
oldmask = os.umask(0o077)
SCRATCH_DIR = None
@@ -152,7 +156,9 @@ class dispatch:
protect_obj = portage.util.ConfigProtect(
config_root, config_paths,
portage.util.shlex_split(
- portage.settings.get('CONFIG_PROTECT_MASK', '')))
+ portage.settings.get('CONFIG_PROTECT_MASK', '')),
+ case_insensitive=("case-insensitive-fs"
+ in portage.settings.features))
#
# Remove new configs identical to current
diff --git a/bin/etc-update b/bin/etc-update
index 0307688..e0f7224 100755
--- a/bin/etc-update
+++ b/bin/etc-update
@@ -113,12 +113,15 @@ scan() {
[[ -d ${path%/*} ]] || continue
local name_opt=$(get_basename_find_opt "${path##*/}")
path="${path%/*}"
- find_opts=( -maxdepth 1 -name "$name_opt" )
+ find_opts=( -maxdepth 1 )
else
# Do not traverse hidden directories such as .svn or .git.
local name_opt=$(get_basename_find_opt '*')
- find_opts=( -name '.*' -type d -prune -o -name "$name_opt" )
+ find_opts=( -name '.*' -type d -prune -o )
fi
+ ${case_insensitive} && \
+ find_opts+=( -iname ) || find_opts+=( -name )
+ find_opts+=( "$name_opt" )
find_opts+=( ! -name '.*~' ! -iname '.*.bak' -print )
if [ ! -w "${path}" ] ; then
@@ -743,6 +746,7 @@ fi
portage_vars=(
CONFIG_PROTECT{,_MASK}
+ FEATURES
PORTAGE_CONFIGROOT
PORTAGE_INST_{G,U}ID
PORTAGE_TMPDIR
@@ -759,6 +763,8 @@ fi
export PORTAGE_TMPDIR
SCAN_PATHS=${*:-${CONFIG_PROTECT}}
+[[ " ${FEATURES} " == *" case-insensitive-fs "* ]] && \
+ case_insensitive=true || case_insensitive=false
TMP="${PORTAGE_TMPDIR}/etc-update-$$"
trap "die terminated" SIGTERM
diff --git a/bin/portageq b/bin/portageq
index ef565d1..1618b44 100755
--- a/bin/portageq
+++ b/bin/portageq
@@ -379,8 +379,8 @@ def is_protected(argv):
protect = portage.util.shlex_split(settings.get("CONFIG_PROTECT", ""))
protect_mask = portage.util.shlex_split(
settings.get("CONFIG_PROTECT_MASK", ""))
- protect_obj = ConfigProtect(root, protect, protect_mask)
-
+ protect_obj = ConfigProtect(root, protect, protect_mask,
+ case_insensitive=("case-insensitive-fs" in settings.features))
if protect_obj.isprotected(f):
return 0
return 1
@@ -414,7 +414,8 @@ def filter_protected(argv):
protect = portage.util.shlex_split(settings.get("CONFIG_PROTECT", ""))
protect_mask = portage.util.shlex_split(
settings.get("CONFIG_PROTECT_MASK", ""))
- protect_obj = ConfigProtect(root, protect, protect_mask)
+ protect_obj = ConfigProtect(root, protect, protect_mask,
+ case_insensitive=("case-insensitive-fs" in settings.features))
errors = 0
diff --git a/bin/quickpkg b/bin/quickpkg
index cf75791..2c69a69 100755
--- a/bin/quickpkg
+++ b/bin/quickpkg
@@ -102,7 +102,9 @@ def quickpkg_atom(options, infos, arg, eout):
if not include_config:
confprot = ConfigProtect(eroot,
shlex_split(settings.get("CONFIG_PROTECT", "")),
- shlex_split(settings.get("CONFIG_PROTECT_MASK", "")))
+ shlex_split(settings.get("CONFIG_PROTECT_MASK", "")),
+ case_insensitive=("case-insensitive-fs"
+ in settings.features))
def protect(filename):
if not confprot.isprotected(filename):
return False
diff --git a/man/make.conf.5 b/man/make.conf.5
index 84e894b..69d95fc 100644
--- a/man/make.conf.5
+++ b/man/make.conf.5
@@ -265,6 +265,12 @@ Build binary packages for just packages in the system set.
Enable a special progress indicator when \fBemerge\fR(1) is calculating
dependencies.
.TP
+.B case\-insensitive\-fs
+Use case\-insensitive file name comparisions when merging and unmerging
+files. Most users should not enable this feature, since most filesystems
+are case\-sensitive. You should only enable this feature if you are
+using portage to install files to a case\-insensitive filesystem.
+.TP
.B ccache
Enable portage support for the ccache package. If the ccache dir is not
present in the user's environment, then portage will default to
diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py
index 94eaed8..6bf2be5 100644
--- a/pym/_emerge/depgraph.py
+++ b/pym/_emerge/depgraph.py
@@ -7805,7 +7805,9 @@ class depgraph(object):
settings = self._frozen_config.roots[root].settings
protect_obj[root] = ConfigProtect(settings["EROOT"], \
shlex_split(settings.get("CONFIG_PROTECT", "")),
- shlex_split(settings.get("CONFIG_PROTECT_MASK", "")))
+ shlex_split(settings.get("CONFIG_PROTECT_MASK", "")),
+ case_insensitive=("case-insensitive-fs"
+ in settings.features))
def write_changes(root, changes, file_to_write_to):
file_contents = None
diff --git a/pym/portage/_global_updates.py b/pym/portage/_global_updates.py
index 17dc080..87085de 100644
--- a/pym/portage/_global_updates.py
+++ b/pym/portage/_global_updates.py
@@ -208,7 +208,9 @@ def _do_global_updates(trees, prev_mtimes, quiet=False, if_mtime_changed=True):
update_config_files(root,
shlex_split(mysettings.get("CONFIG_PROTECT", "")),
shlex_split(mysettings.get("CONFIG_PROTECT_MASK", "")),
- repo_map, match_callback=_config_repo_match)
+ repo_map, match_callback = _config_repo_match,
+ case_insensitive="case-insensitive-fs"
+ in mysettings.features)
# The above global updates proceed quickly, so they
# are considered a single mtimedb transaction.
diff --git a/pym/portage/const.py b/pym/portage/const.py
index d472075..febdb4a 100644
--- a/pym/portage/const.py
+++ b/pym/portage/const.py
@@ -125,6 +125,7 @@ SUPPORTED_FEATURES = frozenset([
"buildpkg",
"buildsyspkg",
"candy",
+ "case-insensitive-fs",
"ccache",
"cgroup",
"chflags",
diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py
index 2d1003f..bf7985a 100644
--- a/pym/portage/dbapi/vartree.py
+++ b/pym/portage/dbapi/vartree.py
@@ -1051,13 +1051,13 @@ class vardbapi(dbapi):
def add(self, cpv):
eroot_len = len(self._vardb._eroot)
- contents = self._vardb._dblink(cpv).getcontents()
pkg_hash = self._hash_pkg(cpv)
- if not contents:
+ db = self._vardb._dblink(cpv)
+ if not db.getcontents():
# Empty path is a code used to represent empty contents.
self._add_path("", pkg_hash)
- for x in contents:
+ for x in db._contents_iter():
self._add_path(x[eroot_len:], pkg_hash)
self._vardb._aux_cache["modified"].add(cpv)
@@ -1189,6 +1189,8 @@ class vardbapi(dbapi):
hash_pkg = owners_cache._hash_pkg
hash_str = owners_cache._hash_str
base_names = self._vardb._aux_cache["owners"]["base_names"]
+ case_insensitive = "case-insensitive-fs" \
+ in vardb.settings.features
dblink_cache = {}
@@ -1205,6 +1207,8 @@ class vardbapi(dbapi):
while path_iter:
path = path_iter.pop()
+ if case_insensitive:
+ path = path.lower()
is_basename = os.sep != path[:1]
if is_basename:
name = path
@@ -1235,7 +1239,7 @@ class vardbapi(dbapi):
continue
if is_basename:
- for p in dblink(cpv).getcontents():
+ for p in dblink(cpv)._contents_iter():
if os.path.basename(p) == name:
owners.append((cpv, p[len(root):]))
else:
@@ -1265,8 +1269,12 @@ class vardbapi(dbapi):
if not path_list:
return
+ case_insensitive = "case-insensitive-fs" \
+ in self._vardb.settings.features
path_info_list = []
for path in path_list:
+ if case_insensitive:
+ path = path.lower()
is_basename = os.sep != path[:1]
if is_basename:
name = path
@@ -1284,9 +1292,11 @@ class vardbapi(dbapi):
dblnk = self._vardb._dblink(cpv)
for path, name, is_basename in path_info_list:
if is_basename:
- for p in dblnk.getcontents():
+ for p in dblnk._contents_iter():
if os.path.basename(p) == name:
- search_pkg.results.append((dblnk, p[len(root):]))
+ search_pkg.results.append((dblnk,
+ dblnk._contents_case_reverse_map(
+ p)[len(root):]))
else:
if dblnk.isowner(path):
search_pkg.results.append((dblnk, path))
@@ -1551,7 +1561,9 @@ class dblink(object):
portage.util.shlex_split(
self.settings.get("CONFIG_PROTECT", "")),
portage.util.shlex_split(
- self.settings.get("CONFIG_PROTECT_MASK", "")))
+ self.settings.get("CONFIG_PROTECT_MASK", "")),
+ case_insensitive=("case-insensitive-fs"
+ in self.settings.features))
return self._protect_obj
@@ -2804,15 +2816,18 @@ class dblink(object):
os_filename_arg.path.join(destroot,
filename.lstrip(os_filename_arg.path.sep)))
- pkgfiles = self.getcontents()
- if pkgfiles and destfile in pkgfiles:
- return destfile
- if pkgfiles:
+ if "case-insensitive-fs" in self.settings.features:
+ destfile = destfile.lower()
+
+ if self._contents_contains(destfile):
+ return self._contents_key(destfile)
+
+ if self.getcontents():
basename = os_filename_arg.path.basename(destfile)
if self._contents_basenames is None:
try:
- for x in pkgfiles:
+ for x in self._contents_iter():
_unicode_encode(x,
encoding=_encodings['merge'],
errors='strict')
@@ -2821,7 +2836,7 @@ class dblink(object):
# different value of sys.getfilesystemencoding(),
# so fall back to utf_8 if appropriate.
try:
- for x in pkgfiles:
+ for x in self._contents_iter():
_unicode_encode(x,
encoding=_encodings['fs'],
errors='strict')
@@ -2831,7 +2846,7 @@ class dblink(object):
os = portage.os
self._contents_basenames = set(
- os.path.basename(x) for x in pkgfiles)
+ os.path.basename(x) for x in self._contents_iter())
if basename not in self._contents_basenames:
# This is a shortcut that, in most cases, allows us to
# eliminate this package as an owner without the need
@@ -2852,7 +2867,7 @@ class dblink(object):
if os is _os_merge:
try:
- for x in pkgfiles:
+ for x in self._contents_iter():
_unicode_encode(x,
encoding=_encodings['merge'],
errors='strict')
@@ -2861,7 +2876,7 @@ class dblink(object):
# different value of sys.getfilesystemencoding(),
# so fall back to utf_8 if appropriate.
try:
- for x in pkgfiles:
+ for x in self._contents_iter():
_unicode_encode(x,
encoding=_encodings['fs'],
errors='strict')
@@ -2872,7 +2887,7 @@ class dblink(object):
self._contents_inodes = {}
parent_paths = set()
- for x in pkgfiles:
+ for x in self._contents_iter():
p_path = os.path.dirname(x)
if p_path in parent_paths:
continue
@@ -2897,8 +2912,8 @@ class dblink(object):
if p_path_list:
for p_path in p_path_list:
x = os_filename_arg.path.join(p_path, basename)
- if x in pkgfiles:
- return x
+ if self._contents_contains(x):
+ return self._contents_key(x)
return False
diff --git a/pym/portage/update.py b/pym/portage/update.py
index df4e11b..83fc3d2 100644
--- a/pym/portage/update.py
+++ b/pym/portage/update.py
@@ -282,7 +282,8 @@ def parse_updates(mycontent):
myupd.append(mysplit)
return myupd, errors
-def update_config_files(config_root, protect, protect_mask, update_iter, match_callback = None):
+def update_config_files(config_root, protect, protect_mask, update_iter,
+ match_callback=None, case_insensitive=False):
"""Perform global updates on /etc/portage/package.*, /etc/portage/profile/package.*,
/etc/portage/profile/packages and /etc/portage/sets.
config_root - location of files to update
@@ -406,7 +407,8 @@ def update_config_files(config_root, protect, protect_mask, update_iter, match_c
sys.stdout.flush()
protect_obj = ConfigProtect(
- config_root, protect, protect_mask)
+ config_root, protect, protect_mask,
+ case_insensitive=case_insensitive)
for x in update_files:
updating_file = os.path.join(abs_user_config, x)
if protect_obj.isprotected(updating_file):
diff --git a/pym/portage/util/__init__.py b/pym/portage/util/__init__.py
index ad3a351..d0cca5b 100644
--- a/pym/portage/util/__init__.py
+++ b/pym/portage/util/__init__.py
@@ -1555,10 +1555,12 @@ class LazyItemsDict(UserDict):
return result
class ConfigProtect(object):
- def __init__(self, myroot, protect_list, mask_list):
+ def __init__(self, myroot, protect_list, mask_list,
+ case_insensitive=False):
self.myroot = myroot
self.protect_list = protect_list
self.mask_list = mask_list
+ self.case_insensitive = case_insensitive
self.updateprotect()
def updateprotect(self):
@@ -1586,6 +1588,8 @@ class ConfigProtect(object):
for x in self.mask_list:
ppath = normalize_path(
os.path.join(self.myroot, x.lstrip(os.path.sep)))
+ if self.case_insensitive:
+ ppath = ppath.lower()
try:
"""Use lstat so that anything, even a broken symlink can be
protected."""
@@ -1606,6 +1610,8 @@ class ConfigProtect(object):
masked = 0
protected = 0
sep = os.path.sep
+ if self.case_insensitive:
+ obj = obj.lower()
for ppath in self.protect:
if len(ppath) > masked and obj.startswith(ppath):
if ppath in self._dirs:
--
2.0.4
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [gentoo-portage-dev] [PATCH 1/2] dblink: case insensitive support for bug #524236
2014-11-16 10:41 ` [gentoo-portage-dev] [PATCH 1/2] dblink: case insensitive support " Zac Medico
2014-11-16 10:41 ` [gentoo-portage-dev] [PATCH 2/2] FEATURES=case-insensitive-fs " Zac Medico
@ 2014-11-16 18:11 ` Brian Dolbec
2014-11-17 1:29 ` [gentoo-portage-dev] [PATCH v2 " Zac Medico
1 sibling, 1 reply; 12+ messages in thread
From: Brian Dolbec @ 2014-11-16 18:11 UTC (permalink / raw
To: gentoo-portage-dev
On Sun, 16 Nov 2014 02:41:54 -0800
Zac Medico <zmedico@gentoo.org> wrote:
> This adds dblink._contents_contains, _contents_iter, and
> _contents_key methods that provide an interface for contents
> operations with "implicit" case handling.
>
> X-Gentoo-Bug: 524236
> X-Gentoo-Url: https://bugs.gentoo.org/show_bug.cgi?id=524236
> ---
> pym/portage/dbapi/vartree.py | 43
> +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43
> insertions(+)
>
> diff --git a/pym/portage/dbapi/vartree.py
> b/pym/portage/dbapi/vartree.py index 8b06f4c..2d1003f 100644
> --- a/pym/portage/dbapi/vartree.py
> +++ b/pym/portage/dbapi/vartree.py
> @@ -1515,6 +1515,8 @@ class dblink(object):
> self.contentscache = None
> self._contents_inodes = None
> self._contents_basenames = None
> + self._contents_case_insensitive = None
> + self._contents_case_reverse_map = None
I like that you've prefixed the new variables with _contents_.
I also see there already are a number of _contents_ prefixed variables
and now newly added functions below.
> self._linkmap_broken = False
> self._device_path_map = {}
> self._hardlink_merge_map = {}
> @@ -1526,6 +1528,15 @@ class dblink(object):
> # compliance with RESTRICT=preserve-libs.
> self._preserve_libs = "preserve-libs" in
> mysettings.features
> + if "case-insensitive-fs" in self.settings.features:
> + self._contents_key =
> self._contents_key_case_insensitive
> + self._contents_contains =
> self._contents_contains_case_insensitive
> + self._contents_iter =
> self._contents_iter_case_insensitive
> + else:
> + self._contents_key =
> self._contents_key_case_sensitive
> + self._contents_contains =
> self._contents_contains_case_sensitive
> + self._contents_iter =
> self._contents_iter_case_sensitive +
> def __hash__(self):
> return hash(self._hash_key)
>
> @@ -1612,6 +1623,8 @@ class dblink(object):
> self.contentscache = None
> self._contents_inodes = None
> self._contents_basenames = None
> + self._contents_case_insensitive = None
> + self._contents_case_reverse_map = None
>
> def getcontents(self):
> """
> @@ -1716,6 +1729,36 @@ class dblink(object):
> self.contentscache = pkgfiles
> return pkgfiles
>
> + def _contents_case_insensitive_init(self):
> + self._contents_case_insensitive = dict(
> + (k.lower(), v) for k, v in
> self.getcontents().items())
> + self._contents_case_reverse_map = dict(
> + (k.lower(), k) for k in self.getcontents())
> +
> + def _contents_iter_case_insensitive(self):
> + if self._contents_case_insensitive is None:
> + self._contents_case_insensitive_init()
> + return iter(self._contents_case_insensitive)
> +
> + def _contents_key_case_insensitive(self, key):
> + if self._contents_case_reverse_map is None:
> + self._contents_case_insensitive_init()
> + return self._contents_case_reverse_map[key]
> +
> + def _contents_contains_case_insensitive(self, key):
> + if self._contents_case_insensitive is None:
> + self._contents_case_insensitive_init()
> + return key.lower() in self._contents_case_insensitive
> +
> + def _contents_key_case_sensitive(self, key):
> + return key
> +
> + def _contents_iter_case_sensitive(self):
> + return iter(self.getcontents())
> +
> + def _contents_contains_case_sensitive(self, key):
> + return key in self.getcontents()
> +
> def _prune_plib_registry(self, unmerge=False,
> needed=None, preserve_paths=None):
> # remove preserved libraries that don't have any
> consumers left
These truly have little interaction with the rest of the dblink class.
I would like to see them moved to an independent class. With that move
the names could be shortened without the _contents prefix since they
would be Contents class referenced. It also looks like there should be
additional code from the dblink class moved into this new class.
Primarily the getcontents(), but that is a much larger refactor. That
would also require the cache, re's and probably some others. Then all
the references to these functions in the vardbapi to the class
instance instead.
On that note, I propose we put this new code in a new class (avoiding
adding to the bloat), pass in the getcontents() pointer along with some
others needed. In later commits do the major re-factor moving more
contents specific handling code into the new class. Bare in mind I
have not looked that extensively in dblink and vardbapi, but it does
look doable.
Are you up for the challenge? ;)
I think it would certainly make the code more maintainable, less
daunting and more clearly defined. We could even split vartree.py into
some smaller files or sub-pkg. I dislike multi-thousand lines of code
files and classes. It makes it much more difficult to move around and
keep track of interactions between the code (complexity) and to learn
everything it does.
--
Brian Dolbec <dolsen>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [gentoo-portage-dev] [PATCH v2 1/2] dblink: case insensitive support for bug #524236
2014-11-16 18:11 ` [gentoo-portage-dev] [PATCH 1/2] dblink: case insensitive support " Brian Dolbec
@ 2014-11-17 1:29 ` Zac Medico
2014-11-17 1:29 ` [gentoo-portage-dev] [PATCH v2 2/2] FEATURES=case-insensitive-fs " Zac Medico
2014-11-17 4:58 ` [gentoo-portage-dev] [PATCH v2 1/2] dblink: case insensitive support " Brian Dolbec
0 siblings, 2 replies; 12+ messages in thread
From: Zac Medico @ 2014-11-17 1:29 UTC (permalink / raw
To: gentoo-portage-dev; +Cc: Zac Medico
This adds dblink._contents_contains, _contents_keys, and
_contents_map_key methods that provide an interface for contents
operations with "implicit" case handling. The new methods are
implemented in a separate ContentsCaseSensitivityManager class,
in order to avoid adding more bloat to vartree.py.
X-Gentoo-Bug: 524236
X-Gentoo-Url: https://bugs.gentoo.org/show_bug.cgi?id=524236
---
.../dbapi/_ContentsCaseSensitivityManager.py | 54 ++++++++++++++++++++++
pym/portage/dbapi/vartree.py | 8 ++++
2 files changed, 62 insertions(+)
create mode 100644 pym/portage/dbapi/_ContentsCaseSensitivityManager.py
diff --git a/pym/portage/dbapi/_ContentsCaseSensitivityManager.py b/pym/portage/dbapi/_ContentsCaseSensitivityManager.py
new file mode 100644
index 0000000..6b8301d
--- /dev/null
+++ b/pym/portage/dbapi/_ContentsCaseSensitivityManager.py
@@ -0,0 +1,54 @@
+# Copyright 2014 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+class ContentsCaseSensitivityManager(object):
+
+ def __init__(self, db):
+
+ self.getcontents = db.getcontents
+
+ if "case-insensitive-fs" in db.settings.features:
+ self.unmap_key = self._unmap_key_case_insensitive
+ self.contains = self._contains_case_insensitive
+ self.keys = self._keys_case_insensitive
+ else:
+ self.unmap_key = self._unmap_key_case_sensitive
+ self.contains = self._contains_case_sensitive
+ self.keys = self._keys_case_sensitive
+
+ self._contents_insensitive = None
+ self._reverse_key_map = None
+
+ def clear_cache(self):
+ self._contents_insensitive = None
+ self._reverse_key_map = None
+
+ def _case_insensitive_init(self):
+ self._contents_insensitive = dict(
+ (k.lower(), v) for k, v in self.getcontents().items())
+ self._reverse_key_map = dict(
+ (k.lower(), k) for k in self.getcontents())
+
+ def _keys_case_insensitive(self):
+ if self._contents_insensitive is None:
+ self._case_insensitive_init()
+ return iter(self._contents_insensitive)
+
+ def _contains_case_insensitive(self, key):
+ if self._contents_insensitive is None:
+ self._case_insensitive_init()
+ return key.lower() in self._contents_insensitive
+
+ def _unmap_key_case_insensitive(self, key):
+ if self._reverse_key_map is None:
+ self._case_insensitive_init()
+ return self._reverse_key_map[key]
+
+ def _keys_case_sensitive(self):
+ return iter(self.getcontents())
+
+ def _contains_case_sensitive(self, key):
+ return key in self.getcontents()
+
+ def _unmap_key_case_sensitive(self, key):
+ return key
diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py
index 8b06f4c..22f41d0 100644
--- a/pym/portage/dbapi/vartree.py
+++ b/pym/portage/dbapi/vartree.py
@@ -69,6 +69,7 @@ from _emerge.EbuildPhase import EbuildPhase
from _emerge.emergelog import emergelog
from _emerge.MiscFunctionsProcess import MiscFunctionsProcess
from _emerge.SpawnProcess import SpawnProcess
+from ._ContentsCaseSensitivityManager import ContentsCaseSensitivityManager
import errno
import fnmatch
@@ -1526,6 +1527,12 @@ class dblink(object):
# compliance with RESTRICT=preserve-libs.
self._preserve_libs = "preserve-libs" in mysettings.features
+ manager = ContentsCaseSensitivityManager(self)
+ self._contents_case_sensitivity_manager = manager
+ self._contents_unmap_key = manager.unmap_key
+ self._contents_contains = manager.contains
+ self._contents_keys = manager.keys
+
def __hash__(self):
return hash(self._hash_key)
@@ -1612,6 +1619,7 @@ class dblink(object):
self.contentscache = None
self._contents_inodes = None
self._contents_basenames = None
+ self._contents_case_sensitivity_manager.clear_cache()
def getcontents(self):
"""
--
2.0.4
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [gentoo-portage-dev] [PATCH v2 2/2] FEATURES=case-insensitive-fs for bug #524236
2014-11-17 1:29 ` [gentoo-portage-dev] [PATCH v2 " Zac Medico
@ 2014-11-17 1:29 ` Zac Medico
2014-11-17 4:58 ` [gentoo-portage-dev] [PATCH v2 1/2] dblink: case insensitive support " Brian Dolbec
1 sibling, 0 replies; 12+ messages in thread
From: Zac Medico @ 2014-11-17 1:29 UTC (permalink / raw
To: gentoo-portage-dev; +Cc: Zac Medico
When case-insensitive-fs is enabled in FEATURES, the dblink.isowner
method, _owners_db class, and ConfigProtect class will be
case-insensitive. This causes the collision-protect and unmerge code
to behave correctly for a case-insensitive file system. If the file
system is case-insensitive but case-preserving, then case is preserved
in the CONTENTS data of installed packages.
X-Gentoo-Bug: 524236
X-Gentoo-Url: https://bugs.gentoo.org/show_bug.cgi?id=524236
---
bin/dispatch-conf | 8 +++++-
bin/etc-update | 10 ++++++--
bin/portageq | 7 +++---
bin/quickpkg | 4 ++-
man/make.conf.5 | 6 +++++
pym/_emerge/depgraph.py | 4 ++-
pym/portage/_global_updates.py | 4 ++-
pym/portage/const.py | 1 +
pym/portage/dbapi/vartree.py | 57 +++++++++++++++++++++++++++---------------
pym/portage/update.py | 6 +++--
pym/portage/util/__init__.py | 8 +++++-
11 files changed, 83 insertions(+), 32 deletions(-)
diff --git a/bin/dispatch-conf b/bin/dispatch-conf
index 8058d6f..b679910 100755
--- a/bin/dispatch-conf
+++ b/bin/dispatch-conf
@@ -35,6 +35,10 @@ from portage.process import find_binary, spawn
FIND_EXTANT_CONFIGS = "find '%s' %s -name '._cfg????_%s' ! -name '.*~' ! -iname '.*.bak' -print"
DIFF_CONTENTS = "diff -Nu '%s' '%s'"
+if "case-insensitive-fs" in portage.settings.features:
+ FIND_EXTANT_CONFIGS = FIND_EXTANT_CONFIGS.replace(
+ "-name '._cfg", "-iname '._cfg")
+
# We need a secure scratch dir and python does silly verbose errors on the use of tempnam
oldmask = os.umask(0o077)
SCRATCH_DIR = None
@@ -152,7 +156,9 @@ class dispatch:
protect_obj = portage.util.ConfigProtect(
config_root, config_paths,
portage.util.shlex_split(
- portage.settings.get('CONFIG_PROTECT_MASK', '')))
+ portage.settings.get('CONFIG_PROTECT_MASK', '')),
+ case_insensitive=("case-insensitive-fs"
+ in portage.settings.features))
#
# Remove new configs identical to current
diff --git a/bin/etc-update b/bin/etc-update
index 0307688..e0f7224 100755
--- a/bin/etc-update
+++ b/bin/etc-update
@@ -113,12 +113,15 @@ scan() {
[[ -d ${path%/*} ]] || continue
local name_opt=$(get_basename_find_opt "${path##*/}")
path="${path%/*}"
- find_opts=( -maxdepth 1 -name "$name_opt" )
+ find_opts=( -maxdepth 1 )
else
# Do not traverse hidden directories such as .svn or .git.
local name_opt=$(get_basename_find_opt '*')
- find_opts=( -name '.*' -type d -prune -o -name "$name_opt" )
+ find_opts=( -name '.*' -type d -prune -o )
fi
+ ${case_insensitive} && \
+ find_opts+=( -iname ) || find_opts+=( -name )
+ find_opts+=( "$name_opt" )
find_opts+=( ! -name '.*~' ! -iname '.*.bak' -print )
if [ ! -w "${path}" ] ; then
@@ -743,6 +746,7 @@ fi
portage_vars=(
CONFIG_PROTECT{,_MASK}
+ FEATURES
PORTAGE_CONFIGROOT
PORTAGE_INST_{G,U}ID
PORTAGE_TMPDIR
@@ -759,6 +763,8 @@ fi
export PORTAGE_TMPDIR
SCAN_PATHS=${*:-${CONFIG_PROTECT}}
+[[ " ${FEATURES} " == *" case-insensitive-fs "* ]] && \
+ case_insensitive=true || case_insensitive=false
TMP="${PORTAGE_TMPDIR}/etc-update-$$"
trap "die terminated" SIGTERM
diff --git a/bin/portageq b/bin/portageq
index ef565d1..1618b44 100755
--- a/bin/portageq
+++ b/bin/portageq
@@ -379,8 +379,8 @@ def is_protected(argv):
protect = portage.util.shlex_split(settings.get("CONFIG_PROTECT", ""))
protect_mask = portage.util.shlex_split(
settings.get("CONFIG_PROTECT_MASK", ""))
- protect_obj = ConfigProtect(root, protect, protect_mask)
-
+ protect_obj = ConfigProtect(root, protect, protect_mask,
+ case_insensitive=("case-insensitive-fs" in settings.features))
if protect_obj.isprotected(f):
return 0
return 1
@@ -414,7 +414,8 @@ def filter_protected(argv):
protect = portage.util.shlex_split(settings.get("CONFIG_PROTECT", ""))
protect_mask = portage.util.shlex_split(
settings.get("CONFIG_PROTECT_MASK", ""))
- protect_obj = ConfigProtect(root, protect, protect_mask)
+ protect_obj = ConfigProtect(root, protect, protect_mask,
+ case_insensitive=("case-insensitive-fs" in settings.features))
errors = 0
diff --git a/bin/quickpkg b/bin/quickpkg
index cf75791..2c69a69 100755
--- a/bin/quickpkg
+++ b/bin/quickpkg
@@ -102,7 +102,9 @@ def quickpkg_atom(options, infos, arg, eout):
if not include_config:
confprot = ConfigProtect(eroot,
shlex_split(settings.get("CONFIG_PROTECT", "")),
- shlex_split(settings.get("CONFIG_PROTECT_MASK", "")))
+ shlex_split(settings.get("CONFIG_PROTECT_MASK", "")),
+ case_insensitive=("case-insensitive-fs"
+ in settings.features))
def protect(filename):
if not confprot.isprotected(filename):
return False
diff --git a/man/make.conf.5 b/man/make.conf.5
index 84e894b..69d95fc 100644
--- a/man/make.conf.5
+++ b/man/make.conf.5
@@ -265,6 +265,12 @@ Build binary packages for just packages in the system set.
Enable a special progress indicator when \fBemerge\fR(1) is calculating
dependencies.
.TP
+.B case\-insensitive\-fs
+Use case\-insensitive file name comparisions when merging and unmerging
+files. Most users should not enable this feature, since most filesystems
+are case\-sensitive. You should only enable this feature if you are
+using portage to install files to a case\-insensitive filesystem.
+.TP
.B ccache
Enable portage support for the ccache package. If the ccache dir is not
present in the user's environment, then portage will default to
diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py
index 2a839d0..6f1910d 100644
--- a/pym/_emerge/depgraph.py
+++ b/pym/_emerge/depgraph.py
@@ -7813,7 +7813,9 @@ class depgraph(object):
settings = self._frozen_config.roots[root].settings
protect_obj[root] = ConfigProtect(settings["EROOT"], \
shlex_split(settings.get("CONFIG_PROTECT", "")),
- shlex_split(settings.get("CONFIG_PROTECT_MASK", "")))
+ shlex_split(settings.get("CONFIG_PROTECT_MASK", "")),
+ case_insensitive=("case-insensitive-fs"
+ in settings.features))
def write_changes(root, changes, file_to_write_to):
file_contents = None
diff --git a/pym/portage/_global_updates.py b/pym/portage/_global_updates.py
index 17dc080..81ee484 100644
--- a/pym/portage/_global_updates.py
+++ b/pym/portage/_global_updates.py
@@ -208,7 +208,9 @@ def _do_global_updates(trees, prev_mtimes, quiet=False, if_mtime_changed=True):
update_config_files(root,
shlex_split(mysettings.get("CONFIG_PROTECT", "")),
shlex_split(mysettings.get("CONFIG_PROTECT_MASK", "")),
- repo_map, match_callback=_config_repo_match)
+ repo_map, match_callback=_config_repo_match,
+ case_insensitive="case-insensitive-fs"
+ in mysettings.features)
# The above global updates proceed quickly, so they
# are considered a single mtimedb transaction.
diff --git a/pym/portage/const.py b/pym/portage/const.py
index d472075..febdb4a 100644
--- a/pym/portage/const.py
+++ b/pym/portage/const.py
@@ -125,6 +125,7 @@ SUPPORTED_FEATURES = frozenset([
"buildpkg",
"buildsyspkg",
"candy",
+ "case-insensitive-fs",
"ccache",
"cgroup",
"chflags",
diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py
index 22f41d0..faee773 100644
--- a/pym/portage/dbapi/vartree.py
+++ b/pym/portage/dbapi/vartree.py
@@ -1052,13 +1052,13 @@ class vardbapi(dbapi):
def add(self, cpv):
eroot_len = len(self._vardb._eroot)
- contents = self._vardb._dblink(cpv).getcontents()
pkg_hash = self._hash_pkg(cpv)
- if not contents:
+ db = self._vardb._dblink(cpv)
+ if not db.getcontents():
# Empty path is a code used to represent empty contents.
self._add_path("", pkg_hash)
- for x in contents:
+ for x in db._contents_keys():
self._add_path(x[eroot_len:], pkg_hash)
self._vardb._aux_cache["modified"].add(cpv)
@@ -1190,6 +1190,8 @@ class vardbapi(dbapi):
hash_pkg = owners_cache._hash_pkg
hash_str = owners_cache._hash_str
base_names = self._vardb._aux_cache["owners"]["base_names"]
+ case_insensitive = "case-insensitive-fs" \
+ in vardb.settings.features
dblink_cache = {}
@@ -1206,6 +1208,8 @@ class vardbapi(dbapi):
while path_iter:
path = path_iter.pop()
+ if case_insensitive:
+ path = path.lower()
is_basename = os.sep != path[:1]
if is_basename:
name = path
@@ -1236,9 +1240,11 @@ class vardbapi(dbapi):
continue
if is_basename:
- for p in dblink(cpv).getcontents():
+ for p in dblink(cpv)._contents_keys():
if os.path.basename(p) == name:
- owners.append((cpv, p[len(root):]))
+ owners.append((cpv,
+ dblink(cpv)._contents_unmap_key(
+ p)[len(root):]))
else:
if dblink(cpv).isowner(path):
owners.append((cpv, path))
@@ -1266,8 +1272,12 @@ class vardbapi(dbapi):
if not path_list:
return
+ case_insensitive = "case-insensitive-fs" \
+ in self._vardb.settings.features
path_info_list = []
for path in path_list:
+ if case_insensitive:
+ path = path.lower()
is_basename = os.sep != path[:1]
if is_basename:
name = path
@@ -1285,9 +1295,11 @@ class vardbapi(dbapi):
dblnk = self._vardb._dblink(cpv)
for path, name, is_basename in path_info_list:
if is_basename:
- for p in dblnk.getcontents():
+ for p in dblnk._contents_keys():
if os.path.basename(p) == name:
- search_pkg.results.append((dblnk, p[len(root):]))
+ search_pkg.results.append((dblnk,
+ dblnk._contents_unmap_key(
+ p)[len(root):]))
else:
if dblnk.isowner(path):
search_pkg.results.append((dblnk, path))
@@ -1547,7 +1559,9 @@ class dblink(object):
portage.util.shlex_split(
self.settings.get("CONFIG_PROTECT", "")),
portage.util.shlex_split(
- self.settings.get("CONFIG_PROTECT_MASK", "")))
+ self.settings.get("CONFIG_PROTECT_MASK", "")),
+ case_insensitive=("case-insensitive-fs"
+ in self.settings.features))
return self._protect_obj
@@ -2769,15 +2783,18 @@ class dblink(object):
os_filename_arg.path.join(destroot,
filename.lstrip(os_filename_arg.path.sep)))
- pkgfiles = self.getcontents()
- if pkgfiles and destfile in pkgfiles:
- return destfile
- if pkgfiles:
+ if "case-insensitive-fs" in self.settings.features:
+ destfile = destfile.lower()
+
+ if self._contents_contains(destfile):
+ return self._contents_unmap_key(destfile)
+
+ if self.getcontents():
basename = os_filename_arg.path.basename(destfile)
if self._contents_basenames is None:
try:
- for x in pkgfiles:
+ for x in self._contents_keys():
_unicode_encode(x,
encoding=_encodings['merge'],
errors='strict')
@@ -2786,7 +2803,7 @@ class dblink(object):
# different value of sys.getfilesystemencoding(),
# so fall back to utf_8 if appropriate.
try:
- for x in pkgfiles:
+ for x in self._contents_keys():
_unicode_encode(x,
encoding=_encodings['fs'],
errors='strict')
@@ -2796,7 +2813,7 @@ class dblink(object):
os = portage.os
self._contents_basenames = set(
- os.path.basename(x) for x in pkgfiles)
+ os.path.basename(x) for x in self._contents_keys())
if basename not in self._contents_basenames:
# This is a shortcut that, in most cases, allows us to
# eliminate this package as an owner without the need
@@ -2817,7 +2834,7 @@ class dblink(object):
if os is _os_merge:
try:
- for x in pkgfiles:
+ for x in self._contents_keys():
_unicode_encode(x,
encoding=_encodings['merge'],
errors='strict')
@@ -2826,7 +2843,7 @@ class dblink(object):
# different value of sys.getfilesystemencoding(),
# so fall back to utf_8 if appropriate.
try:
- for x in pkgfiles:
+ for x in self._contents_keys():
_unicode_encode(x,
encoding=_encodings['fs'],
errors='strict')
@@ -2837,7 +2854,7 @@ class dblink(object):
self._contents_inodes = {}
parent_paths = set()
- for x in pkgfiles:
+ for x in self._contents_keys():
p_path = os.path.dirname(x)
if p_path in parent_paths:
continue
@@ -2862,8 +2879,8 @@ class dblink(object):
if p_path_list:
for p_path in p_path_list:
x = os_filename_arg.path.join(p_path, basename)
- if x in pkgfiles:
- return x
+ if self._contents_contains(x):
+ return self._contents_unmap_key(x)
return False
diff --git a/pym/portage/update.py b/pym/portage/update.py
index df4e11b..83fc3d2 100644
--- a/pym/portage/update.py
+++ b/pym/portage/update.py
@@ -282,7 +282,8 @@ def parse_updates(mycontent):
myupd.append(mysplit)
return myupd, errors
-def update_config_files(config_root, protect, protect_mask, update_iter, match_callback = None):
+def update_config_files(config_root, protect, protect_mask, update_iter,
+ match_callback=None, case_insensitive=False):
"""Perform global updates on /etc/portage/package.*, /etc/portage/profile/package.*,
/etc/portage/profile/packages and /etc/portage/sets.
config_root - location of files to update
@@ -406,7 +407,8 @@ def update_config_files(config_root, protect, protect_mask, update_iter, match_c
sys.stdout.flush()
protect_obj = ConfigProtect(
- config_root, protect, protect_mask)
+ config_root, protect, protect_mask,
+ case_insensitive=case_insensitive)
for x in update_files:
updating_file = os.path.join(abs_user_config, x)
if protect_obj.isprotected(updating_file):
diff --git a/pym/portage/util/__init__.py b/pym/portage/util/__init__.py
index ad3a351..d0cca5b 100644
--- a/pym/portage/util/__init__.py
+++ b/pym/portage/util/__init__.py
@@ -1555,10 +1555,12 @@ class LazyItemsDict(UserDict):
return result
class ConfigProtect(object):
- def __init__(self, myroot, protect_list, mask_list):
+ def __init__(self, myroot, protect_list, mask_list,
+ case_insensitive=False):
self.myroot = myroot
self.protect_list = protect_list
self.mask_list = mask_list
+ self.case_insensitive = case_insensitive
self.updateprotect()
def updateprotect(self):
@@ -1586,6 +1588,8 @@ class ConfigProtect(object):
for x in self.mask_list:
ppath = normalize_path(
os.path.join(self.myroot, x.lstrip(os.path.sep)))
+ if self.case_insensitive:
+ ppath = ppath.lower()
try:
"""Use lstat so that anything, even a broken symlink can be
protected."""
@@ -1606,6 +1610,8 @@ class ConfigProtect(object):
masked = 0
protected = 0
sep = os.path.sep
+ if self.case_insensitive:
+ obj = obj.lower()
for ppath in self.protect:
if len(ppath) > masked and obj.startswith(ppath):
if ppath in self._dirs:
--
2.0.4
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [gentoo-portage-dev] [PATCH v2 1/2] dblink: case insensitive support for bug #524236
2014-11-17 1:29 ` [gentoo-portage-dev] [PATCH v2 " Zac Medico
2014-11-17 1:29 ` [gentoo-portage-dev] [PATCH v2 2/2] FEATURES=case-insensitive-fs " Zac Medico
@ 2014-11-17 4:58 ` Brian Dolbec
2014-11-17 20:29 ` [gentoo-portage-dev] [PATCH v3 " Zac Medico
1 sibling, 1 reply; 12+ messages in thread
From: Brian Dolbec @ 2014-11-17 4:58 UTC (permalink / raw
To: gentoo-portage-dev
On Sun, 16 Nov 2014 17:29:26 -0800
Zac Medico <zmedico@gentoo.org> wrote:
> This adds dblink._contents_contains, _contents_keys, and
> _contents_map_key methods that provide an interface for contents
> operations with "implicit" case handling. The new methods are
> implemented in a separate ContentsCaseSensitivityManager class,
> in order to avoid adding more bloat to vartree.py.
>
> X-Gentoo-Bug: 524236
> X-Gentoo-Url: https://bugs.gentoo.org/show_bug.cgi?id=524236
> ---
> .../dbapi/_ContentsCaseSensitivityManager.py | 54
> ++++++++++++++++++++++
> pym/portage/dbapi/vartree.py | 8 ++++ 2 files
> changed, 62 insertions(+) create mode 100644
> pym/portage/dbapi/_ContentsCaseSensitivityManager.py
>
> diff --git a/pym/portage/dbapi/_ContentsCaseSensitivityManager.py
> b/pym/portage/dbapi/_ContentsCaseSensitivityManager.py new file mode
> 100644 index 0000000..6b8301d
> --- /dev/null
> +++ b/pym/portage/dbapi/_ContentsCaseSensitivityManager.py
> @@ -0,0 +1,54 @@
> +# Copyright 2014 Gentoo Foundation
> +# Distributed under the terms of the GNU General Public License v2
> +
> +class ContentsCaseSensitivityManager(object):
> +
> + def __init__(self, db):
> +
> + self.getcontents = db.getcontents
> +
> + if "case-insensitive-fs" in db.settings.features:
> + self.unmap_key =
> self._unmap_key_case_insensitive
> + self.contains =
> self._contains_case_insensitive
> + self.keys = self._keys_case_insensitive
> + else:
> + self.unmap_key =
> self._unmap_key_case_sensitive
> + self.contains = self._contains_case_sensitive
> + self.keys = self._keys_case_sensitive
> +
> + self._contents_insensitive = None
> + self._reverse_key_map = None
> +
> + def clear_cache(self):
> + self._contents_insensitive = None
> + self._reverse_key_map = None
> +
> + def _case_insensitive_init(self):
> + self._contents_insensitive = dict(
> + (k.lower(), v) for k, v in
> self.getcontents().items())
> + self._reverse_key_map = dict(
> + (k.lower(), k) for k in self.getcontents())
> +
> + def _keys_case_insensitive(self):
> + if self._contents_insensitive is None:
> + self._case_insensitive_init()
> + return iter(self._contents_insensitive)
> +
> + def _contains_case_insensitive(self, key):
> + if self._contents_insensitive is None:
> + self._case_insensitive_init()
> + return key.lower() in self._contents_insensitive
> +
> + def _unmap_key_case_insensitive(self, key):
> + if self._reverse_key_map is None:
> + self._case_insensitive_init()
> + return self._reverse_key_map[key]
> +
> + def _keys_case_sensitive(self):
> + return iter(self.getcontents())
> +
> + def _contains_case_sensitive(self, key):
> + return key in self.getcontents()
> +
> + def _unmap_key_case_sensitive(self, key):
> + return key
Very nice, much better this way than adding to the dblink class
directly.
Just one thing left to do... Docstrings please. While normally you
don't NEED them for private functions, in this case they are exported
using the function pointer assignments. They are named quite well to
indicate their function, but document them anyway please.
> diff --git a/pym/portage/dbapi/vartree.py
> b/pym/portage/dbapi/vartree.py index 8b06f4c..22f41d0 100644
> --- a/pym/portage/dbapi/vartree.py
> +++ b/pym/portage/dbapi/vartree.py
> @@ -69,6 +69,7 @@ from _emerge.EbuildPhase import EbuildPhase
> from _emerge.emergelog import emergelog
> from _emerge.MiscFunctionsProcess import MiscFunctionsProcess
> from _emerge.SpawnProcess import SpawnProcess
> +from ._ContentsCaseSensitivityManager import
> ContentsCaseSensitivityManager
> import errno
> import fnmatch
> @@ -1526,6 +1527,12 @@ class dblink(object):
> # compliance with RESTRICT=preserve-libs.
> self._preserve_libs = "preserve-libs" in
> mysettings.features
> + manager = ContentsCaseSensitivityManager(self)
> + self._contents_case_sensitivity_manager = manager
> + self._contents_unmap_key = manager.unmap_key
> + self._contents_contains = manager.contains
> + self._contents_keys = manager.keys
> +
Rather than so many function pointer assignments, why not
+ self._contents = ContentsCaseSensitivityManager(self)
Then later use it like so...
+ self._contents.unmap_key(...)
+ self._contents.contains(...)
+ self._contents.keys()
+
vardbapi calls will just get a similar one additional level of
direction.
<snipped from patch V2 2/2>
- for p in dblink(cpv).getcontents():
+ for p in dblink(cpv)._contents_keys():
to:
- for p in dblink(cpv).getcontents():
+ for p in dblink(cpv)._contents.keys():
It should be less confusing for future maintenance by having to track all the
pointer assignments to know what it is actually pointing at. It is a lot easier to find that _contents is a
ContentsCaseSensitivityManager class instance. Then all function calls directly refer to those functions
or the sensitivity set exported versions of them.
> def __hash__(self):
> return hash(self._hash_key)
>
> @@ -1612,6 +1619,7 @@ class dblink(object):
> self.contentscache = None
> self._contents_inodes = None
> self._contents_basenames = None
> + self._contents_case_sensitivity_manager.clear_cache()
again...
+ self._contents.clear_cache()
>
> def getcontents(self):
> """
--
Brian Dolbec <dolsen>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [gentoo-portage-dev] [PATCH v3 1/2] dblink: case insensitive support for bug #524236
2014-11-17 4:58 ` [gentoo-portage-dev] [PATCH v2 1/2] dblink: case insensitive support " Brian Dolbec
@ 2014-11-17 20:29 ` Zac Medico
2014-11-17 20:29 ` [gentoo-portage-dev] [PATCH v3 2/2] FEATURES=case-insensitive-fs " Zac Medico
2014-11-17 22:09 ` [gentoo-portage-dev] [PATCH v3 1/2] dblink: case insensitive support " Brian Dolbec
0 siblings, 2 replies; 12+ messages in thread
From: Zac Medico @ 2014-11-17 20:29 UTC (permalink / raw
To: gentoo-portage-dev; +Cc: Zac Medico
This adds a dblink._contents attribute with methods that provide
an interface for contents operations with "implicit" case handling.
The new methods are implemented in a separate
ContentsCaseSensitivityManager class, in order to avoid adding more
bloat to vartree.py.
X-Gentoo-Bug: 524236
X-Gentoo-Url: https://bugs.gentoo.org/show_bug.cgi?id=524236
---
.../dbapi/_ContentsCaseSensitivityManager.py | 93 ++++++++++++++++++++++
pym/portage/dbapi/vartree.py | 3 +
2 files changed, 96 insertions(+)
create mode 100644 pym/portage/dbapi/_ContentsCaseSensitivityManager.py
diff --git a/pym/portage/dbapi/_ContentsCaseSensitivityManager.py b/pym/portage/dbapi/_ContentsCaseSensitivityManager.py
new file mode 100644
index 0000000..c479ec9
--- /dev/null
+++ b/pym/portage/dbapi/_ContentsCaseSensitivityManager.py
@@ -0,0 +1,93 @@
+# Copyright 2014 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+class ContentsCaseSensitivityManager(object):
+ """
+ Implicitly handles case transformations that are needed for
+ case-insensitive support.
+ """
+
+ def __init__(self, db):
+ """
+ @param db: A dblink instance
+ @type db: vartree.dblink
+ """
+ self.getcontents = db.getcontents
+
+ if "case-insensitive-fs" in db.settings.features:
+ self.unmap_key = self._unmap_key_case_insensitive
+ self.contains = self._contains_case_insensitive
+ self.keys = self._keys_case_insensitive
+
+ self._contents_insensitive = None
+ self._reverse_key_map = None
+
+ def clear_cache(self):
+ """
+ Clear all cached contents data.
+ """
+ self._contents_insensitive = None
+ self._reverse_key_map = None
+
+ def keys(self):
+ """
+ Iterate over all contents keys, which are transformed to
+ lowercase when appropriate, for use in case-insensitive
+ comparisons.
+ @rtype: iterator
+ @return: An iterator over all the contents keys
+ """
+ return iter(self.getcontents())
+
+ def contains(self, key):
+ """
+ Check if the given key is contained in the contents, using
+ case-insensitive comparison when appropriate.
+ @param key: A filesystem path (including ROOT and EPREFIX)
+ @type key: str
+ @rtype: bool
+ @return: True if the given key is contained in the contents,
+ False otherwise
+ """
+ return key in self.getcontents()
+
+ def unmap_key(self, key):
+ """
+ Map a key (from the keys method) back to its case-preserved
+ form.
+ @param key: A filesystem path (including ROOT and EPREFIX)
+ @type key: str
+ @rtype: str
+ @return: The case-preserved form of key
+ """
+ return key
+
+ def _case_insensitive_init(self):
+ """
+ Initialize data structures for case-insensitive support.
+ """
+ self._contents_insensitive = dict(
+ (k.lower(), v) for k, v in self.getcontents().items())
+ self._reverse_key_map = dict(
+ (k.lower(), k) for k in self.getcontents())
+
+ def _keys_case_insensitive(self):
+ if self._contents_insensitive is None:
+ self._case_insensitive_init()
+ return iter(self._contents_insensitive)
+
+ _keys_case_insensitive.__doc__ = keys.__doc__
+
+ def _contains_case_insensitive(self, key):
+ if self._contents_insensitive is None:
+ self._case_insensitive_init()
+ return key.lower() in self._contents_insensitive
+
+ _contains_case_insensitive.__doc__ = contains.__doc__
+
+ def _unmap_key_case_insensitive(self, key):
+ if self._reverse_key_map is None:
+ self._case_insensitive_init()
+ return self._reverse_key_map[key]
+
+ _unmap_key_case_insensitive.__doc__ = unmap_key.__doc__
diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py
index 8b06f4c..81059b1 100644
--- a/pym/portage/dbapi/vartree.py
+++ b/pym/portage/dbapi/vartree.py
@@ -69,6 +69,7 @@ from _emerge.EbuildPhase import EbuildPhase
from _emerge.emergelog import emergelog
from _emerge.MiscFunctionsProcess import MiscFunctionsProcess
from _emerge.SpawnProcess import SpawnProcess
+from ._ContentsCaseSensitivityManager import ContentsCaseSensitivityManager
import errno
import fnmatch
@@ -1525,6 +1526,7 @@ class dblink(object):
# When necessary, this attribute is modified for
# compliance with RESTRICT=preserve-libs.
self._preserve_libs = "preserve-libs" in mysettings.features
+ self._contents = ContentsCaseSensitivityManager(self)
def __hash__(self):
return hash(self._hash_key)
@@ -1612,6 +1614,7 @@ class dblink(object):
self.contentscache = None
self._contents_inodes = None
self._contents_basenames = None
+ self._contents.clear_cache()
def getcontents(self):
"""
--
2.0.4
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [gentoo-portage-dev] [PATCH v3 2/2] FEATURES=case-insensitive-fs for bug #524236
2014-11-17 20:29 ` [gentoo-portage-dev] [PATCH v3 " Zac Medico
@ 2014-11-17 20:29 ` Zac Medico
2014-11-17 22:09 ` [gentoo-portage-dev] [PATCH v3 1/2] dblink: case insensitive support " Brian Dolbec
1 sibling, 0 replies; 12+ messages in thread
From: Zac Medico @ 2014-11-17 20:29 UTC (permalink / raw
To: gentoo-portage-dev; +Cc: Zac Medico
When case-insensitive-fs is enabled in FEATURES, the dblink.isowner
method, _owners_db class, and ConfigProtect class will be
case-insensitive. This causes the collision-protect and unmerge code
to behave correctly for a case-insensitive file system. If the file
system is case-insensitive but case-preserving, then case is preserved
in the CONTENTS data of installed packages.
X-Gentoo-Bug: 524236
X-Gentoo-Url: https://bugs.gentoo.org/show_bug.cgi?id=524236
---
bin/dispatch-conf | 8 ++++-
bin/etc-update | 10 ++++--
bin/portageq | 7 +++--
bin/quickpkg | 4 ++-
man/make.conf.5 | 6 ++++
pym/_emerge/depgraph.py | 4 ++-
pym/portage/_global_updates.py | 4 ++-
pym/portage/const.py | 1 +
pym/portage/dbapi/vartree.py | 71 +++++++++++++++++++++++++++---------------
pym/portage/update.py | 6 ++--
pym/portage/util/__init__.py | 8 ++++-
11 files changed, 92 insertions(+), 37 deletions(-)
diff --git a/bin/dispatch-conf b/bin/dispatch-conf
index 8058d6f..b679910 100755
--- a/bin/dispatch-conf
+++ b/bin/dispatch-conf
@@ -35,6 +35,10 @@ from portage.process import find_binary, spawn
FIND_EXTANT_CONFIGS = "find '%s' %s -name '._cfg????_%s' ! -name '.*~' ! -iname '.*.bak' -print"
DIFF_CONTENTS = "diff -Nu '%s' '%s'"
+if "case-insensitive-fs" in portage.settings.features:
+ FIND_EXTANT_CONFIGS = FIND_EXTANT_CONFIGS.replace(
+ "-name '._cfg", "-iname '._cfg")
+
# We need a secure scratch dir and python does silly verbose errors on the use of tempnam
oldmask = os.umask(0o077)
SCRATCH_DIR = None
@@ -152,7 +156,9 @@ class dispatch:
protect_obj = portage.util.ConfigProtect(
config_root, config_paths,
portage.util.shlex_split(
- portage.settings.get('CONFIG_PROTECT_MASK', '')))
+ portage.settings.get('CONFIG_PROTECT_MASK', '')),
+ case_insensitive=("case-insensitive-fs"
+ in portage.settings.features))
#
# Remove new configs identical to current
diff --git a/bin/etc-update b/bin/etc-update
index 0307688..e0f7224 100755
--- a/bin/etc-update
+++ b/bin/etc-update
@@ -113,12 +113,15 @@ scan() {
[[ -d ${path%/*} ]] || continue
local name_opt=$(get_basename_find_opt "${path##*/}")
path="${path%/*}"
- find_opts=( -maxdepth 1 -name "$name_opt" )
+ find_opts=( -maxdepth 1 )
else
# Do not traverse hidden directories such as .svn or .git.
local name_opt=$(get_basename_find_opt '*')
- find_opts=( -name '.*' -type d -prune -o -name "$name_opt" )
+ find_opts=( -name '.*' -type d -prune -o )
fi
+ ${case_insensitive} && \
+ find_opts+=( -iname ) || find_opts+=( -name )
+ find_opts+=( "$name_opt" )
find_opts+=( ! -name '.*~' ! -iname '.*.bak' -print )
if [ ! -w "${path}" ] ; then
@@ -743,6 +746,7 @@ fi
portage_vars=(
CONFIG_PROTECT{,_MASK}
+ FEATURES
PORTAGE_CONFIGROOT
PORTAGE_INST_{G,U}ID
PORTAGE_TMPDIR
@@ -759,6 +763,8 @@ fi
export PORTAGE_TMPDIR
SCAN_PATHS=${*:-${CONFIG_PROTECT}}
+[[ " ${FEATURES} " == *" case-insensitive-fs "* ]] && \
+ case_insensitive=true || case_insensitive=false
TMP="${PORTAGE_TMPDIR}/etc-update-$$"
trap "die terminated" SIGTERM
diff --git a/bin/portageq b/bin/portageq
index 6a42bfd..2c4f548 100755
--- a/bin/portageq
+++ b/bin/portageq
@@ -379,8 +379,8 @@ def is_protected(argv):
protect = portage.util.shlex_split(settings.get("CONFIG_PROTECT", ""))
protect_mask = portage.util.shlex_split(
settings.get("CONFIG_PROTECT_MASK", ""))
- protect_obj = ConfigProtect(root, protect, protect_mask)
-
+ protect_obj = ConfigProtect(root, protect, protect_mask,
+ case_insensitive=("case-insensitive-fs" in settings.features))
if protect_obj.isprotected(f):
return 0
return 1
@@ -414,7 +414,8 @@ def filter_protected(argv):
protect = portage.util.shlex_split(settings.get("CONFIG_PROTECT", ""))
protect_mask = portage.util.shlex_split(
settings.get("CONFIG_PROTECT_MASK", ""))
- protect_obj = ConfigProtect(root, protect, protect_mask)
+ protect_obj = ConfigProtect(root, protect, protect_mask,
+ case_insensitive=("case-insensitive-fs" in settings.features))
errors = 0
diff --git a/bin/quickpkg b/bin/quickpkg
index cf75791..2c69a69 100755
--- a/bin/quickpkg
+++ b/bin/quickpkg
@@ -102,7 +102,9 @@ def quickpkg_atom(options, infos, arg, eout):
if not include_config:
confprot = ConfigProtect(eroot,
shlex_split(settings.get("CONFIG_PROTECT", "")),
- shlex_split(settings.get("CONFIG_PROTECT_MASK", "")))
+ shlex_split(settings.get("CONFIG_PROTECT_MASK", "")),
+ case_insensitive=("case-insensitive-fs"
+ in settings.features))
def protect(filename):
if not confprot.isprotected(filename):
return False
diff --git a/man/make.conf.5 b/man/make.conf.5
index 84e894b..69d95fc 100644
--- a/man/make.conf.5
+++ b/man/make.conf.5
@@ -265,6 +265,12 @@ Build binary packages for just packages in the system set.
Enable a special progress indicator when \fBemerge\fR(1) is calculating
dependencies.
.TP
+.B case\-insensitive\-fs
+Use case\-insensitive file name comparisions when merging and unmerging
+files. Most users should not enable this feature, since most filesystems
+are case\-sensitive. You should only enable this feature if you are
+using portage to install files to a case\-insensitive filesystem.
+.TP
.B ccache
Enable portage support for the ccache package. If the ccache dir is not
present in the user's environment, then portage will default to
diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py
index 2a839d0..6f1910d 100644
--- a/pym/_emerge/depgraph.py
+++ b/pym/_emerge/depgraph.py
@@ -7813,7 +7813,9 @@ class depgraph(object):
settings = self._frozen_config.roots[root].settings
protect_obj[root] = ConfigProtect(settings["EROOT"], \
shlex_split(settings.get("CONFIG_PROTECT", "")),
- shlex_split(settings.get("CONFIG_PROTECT_MASK", "")))
+ shlex_split(settings.get("CONFIG_PROTECT_MASK", "")),
+ case_insensitive=("case-insensitive-fs"
+ in settings.features))
def write_changes(root, changes, file_to_write_to):
file_contents = None
diff --git a/pym/portage/_global_updates.py b/pym/portage/_global_updates.py
index 17dc080..81ee484 100644
--- a/pym/portage/_global_updates.py
+++ b/pym/portage/_global_updates.py
@@ -208,7 +208,9 @@ def _do_global_updates(trees, prev_mtimes, quiet=False, if_mtime_changed=True):
update_config_files(root,
shlex_split(mysettings.get("CONFIG_PROTECT", "")),
shlex_split(mysettings.get("CONFIG_PROTECT_MASK", "")),
- repo_map, match_callback=_config_repo_match)
+ repo_map, match_callback=_config_repo_match,
+ case_insensitive="case-insensitive-fs"
+ in mysettings.features)
# The above global updates proceed quickly, so they
# are considered a single mtimedb transaction.
diff --git a/pym/portage/const.py b/pym/portage/const.py
index d472075..febdb4a 100644
--- a/pym/portage/const.py
+++ b/pym/portage/const.py
@@ -125,6 +125,7 @@ SUPPORTED_FEATURES = frozenset([
"buildpkg",
"buildsyspkg",
"candy",
+ "case-insensitive-fs",
"ccache",
"cgroup",
"chflags",
diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py
index 81059b1..0fd1bd9 100644
--- a/pym/portage/dbapi/vartree.py
+++ b/pym/portage/dbapi/vartree.py
@@ -1052,13 +1052,13 @@ class vardbapi(dbapi):
def add(self, cpv):
eroot_len = len(self._vardb._eroot)
- contents = self._vardb._dblink(cpv).getcontents()
pkg_hash = self._hash_pkg(cpv)
- if not contents:
+ db = self._vardb._dblink(cpv)
+ if not db.getcontents():
# Empty path is a code used to represent empty contents.
self._add_path("", pkg_hash)
- for x in contents:
+ for x in db._contents.keys():
self._add_path(x[eroot_len:], pkg_hash)
self._vardb._aux_cache["modified"].add(cpv)
@@ -1190,6 +1190,8 @@ class vardbapi(dbapi):
hash_pkg = owners_cache._hash_pkg
hash_str = owners_cache._hash_str
base_names = self._vardb._aux_cache["owners"]["base_names"]
+ case_insensitive = "case-insensitive-fs" \
+ in vardb.settings.features
dblink_cache = {}
@@ -1206,6 +1208,8 @@ class vardbapi(dbapi):
while path_iter:
path = path_iter.pop()
+ if case_insensitive:
+ path = path.lower()
is_basename = os.sep != path[:1]
if is_basename:
name = path
@@ -1236,12 +1240,16 @@ class vardbapi(dbapi):
continue
if is_basename:
- for p in dblink(cpv).getcontents():
+ for p in dblink(cpv)._contents.keys():
if os.path.basename(p) == name:
- owners.append((cpv, p[len(root):]))
+ owners.append((cpv, dblink(cpv).
+ _contents.unmap_key(
+ p)[len(root):]))
else:
- if dblink(cpv).isowner(path):
- owners.append((cpv, path))
+ key = dblink(cpv)._match_contents(path)
+ if key is not False:
+ owners.append(
+ (cpv, key[len(root):]))
except StopIteration:
path_iter.append(path)
@@ -1266,8 +1274,12 @@ class vardbapi(dbapi):
if not path_list:
return
+ case_insensitive = "case-insensitive-fs" \
+ in self._vardb.settings.features
path_info_list = []
for path in path_list:
+ if case_insensitive:
+ path = path.lower()
is_basename = os.sep != path[:1]
if is_basename:
name = path
@@ -1285,12 +1297,16 @@ class vardbapi(dbapi):
dblnk = self._vardb._dblink(cpv)
for path, name, is_basename in path_info_list:
if is_basename:
- for p in dblnk.getcontents():
+ for p in dblnk._contents.keys():
if os.path.basename(p) == name:
- search_pkg.results.append((dblnk, p[len(root):]))
+ search_pkg.results.append((dblnk,
+ dblnk._contents.unmap_key(
+ p)[len(root):]))
else:
- if dblnk.isowner(path):
- search_pkg.results.append((dblnk, path))
+ key = dblnk._match_contents(path)
+ if key is not False:
+ search_pkg.results.append(
+ (dblnk, key[len(root):]))
search_pkg.complete = True
return False
@@ -1542,7 +1558,9 @@ class dblink(object):
portage.util.shlex_split(
self.settings.get("CONFIG_PROTECT", "")),
portage.util.shlex_split(
- self.settings.get("CONFIG_PROTECT_MASK", "")))
+ self.settings.get("CONFIG_PROTECT_MASK", "")),
+ case_insensitive=("case-insensitive-fs"
+ in self.settings.features))
return self._protect_obj
@@ -1620,9 +1638,9 @@ class dblink(object):
"""
Get the installed files of a given package (aka what that package installed)
"""
- contents_file = os.path.join(self.dbdir, "CONTENTS")
if self.contentscache is not None:
return self.contentscache
+ contents_file = os.path.join(self.dbdir, "CONTENTS")
pkgfiles = {}
try:
with io.open(_unicode_encode(contents_file,
@@ -2764,15 +2782,18 @@ class dblink(object):
os_filename_arg.path.join(destroot,
filename.lstrip(os_filename_arg.path.sep)))
- pkgfiles = self.getcontents()
- if pkgfiles and destfile in pkgfiles:
- return destfile
- if pkgfiles:
+ if "case-insensitive-fs" in self.settings.features:
+ destfile = destfile.lower()
+
+ if self._contents.contains(destfile):
+ return self._contents.unmap_key(destfile)
+
+ if self.getcontents():
basename = os_filename_arg.path.basename(destfile)
if self._contents_basenames is None:
try:
- for x in pkgfiles:
+ for x in self._contents.keys():
_unicode_encode(x,
encoding=_encodings['merge'],
errors='strict')
@@ -2781,7 +2802,7 @@ class dblink(object):
# different value of sys.getfilesystemencoding(),
# so fall back to utf_8 if appropriate.
try:
- for x in pkgfiles:
+ for x in self._contents.keys():
_unicode_encode(x,
encoding=_encodings['fs'],
errors='strict')
@@ -2791,7 +2812,7 @@ class dblink(object):
os = portage.os
self._contents_basenames = set(
- os.path.basename(x) for x in pkgfiles)
+ os.path.basename(x) for x in self._contents.keys())
if basename not in self._contents_basenames:
# This is a shortcut that, in most cases, allows us to
# eliminate this package as an owner without the need
@@ -2812,7 +2833,7 @@ class dblink(object):
if os is _os_merge:
try:
- for x in pkgfiles:
+ for x in self._contents.keys():
_unicode_encode(x,
encoding=_encodings['merge'],
errors='strict')
@@ -2821,7 +2842,7 @@ class dblink(object):
# different value of sys.getfilesystemencoding(),
# so fall back to utf_8 if appropriate.
try:
- for x in pkgfiles:
+ for x in self._contents.keys():
_unicode_encode(x,
encoding=_encodings['fs'],
errors='strict')
@@ -2832,7 +2853,7 @@ class dblink(object):
self._contents_inodes = {}
parent_paths = set()
- for x in pkgfiles:
+ for x in self._contents.keys():
p_path = os.path.dirname(x)
if p_path in parent_paths:
continue
@@ -2857,8 +2878,8 @@ class dblink(object):
if p_path_list:
for p_path in p_path_list:
x = os_filename_arg.path.join(p_path, basename)
- if x in pkgfiles:
- return x
+ if self._contents.contains(x):
+ return self._contents.unmap_key(x)
return False
diff --git a/pym/portage/update.py b/pym/portage/update.py
index df4e11b..83fc3d2 100644
--- a/pym/portage/update.py
+++ b/pym/portage/update.py
@@ -282,7 +282,8 @@ def parse_updates(mycontent):
myupd.append(mysplit)
return myupd, errors
-def update_config_files(config_root, protect, protect_mask, update_iter, match_callback = None):
+def update_config_files(config_root, protect, protect_mask, update_iter,
+ match_callback=None, case_insensitive=False):
"""Perform global updates on /etc/portage/package.*, /etc/portage/profile/package.*,
/etc/portage/profile/packages and /etc/portage/sets.
config_root - location of files to update
@@ -406,7 +407,8 @@ def update_config_files(config_root, protect, protect_mask, update_iter, match_c
sys.stdout.flush()
protect_obj = ConfigProtect(
- config_root, protect, protect_mask)
+ config_root, protect, protect_mask,
+ case_insensitive=case_insensitive)
for x in update_files:
updating_file = os.path.join(abs_user_config, x)
if protect_obj.isprotected(updating_file):
diff --git a/pym/portage/util/__init__.py b/pym/portage/util/__init__.py
index ad3a351..d0cca5b 100644
--- a/pym/portage/util/__init__.py
+++ b/pym/portage/util/__init__.py
@@ -1555,10 +1555,12 @@ class LazyItemsDict(UserDict):
return result
class ConfigProtect(object):
- def __init__(self, myroot, protect_list, mask_list):
+ def __init__(self, myroot, protect_list, mask_list,
+ case_insensitive=False):
self.myroot = myroot
self.protect_list = protect_list
self.mask_list = mask_list
+ self.case_insensitive = case_insensitive
self.updateprotect()
def updateprotect(self):
@@ -1586,6 +1588,8 @@ class ConfigProtect(object):
for x in self.mask_list:
ppath = normalize_path(
os.path.join(self.myroot, x.lstrip(os.path.sep)))
+ if self.case_insensitive:
+ ppath = ppath.lower()
try:
"""Use lstat so that anything, even a broken symlink can be
protected."""
@@ -1606,6 +1610,8 @@ class ConfigProtect(object):
masked = 0
protected = 0
sep = os.path.sep
+ if self.case_insensitive:
+ obj = obj.lower()
for ppath in self.protect:
if len(ppath) > masked and obj.startswith(ppath):
if ppath in self._dirs:
--
2.0.4
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [gentoo-portage-dev] [PATCH v3 1/2] dblink: case insensitive support for bug #524236
2014-11-17 20:29 ` [gentoo-portage-dev] [PATCH v3 " Zac Medico
2014-11-17 20:29 ` [gentoo-portage-dev] [PATCH v3 2/2] FEATURES=case-insensitive-fs " Zac Medico
@ 2014-11-17 22:09 ` Brian Dolbec
1 sibling, 0 replies; 12+ messages in thread
From: Brian Dolbec @ 2014-11-17 22:09 UTC (permalink / raw
To: gentoo-portage-dev
On Mon, 17 Nov 2014 12:29:37 -0800
Zac Medico <zmedico@gentoo.org> wrote:
> This adds a dblink._contents attribute with methods that provide
> an interface for contents operations with "implicit" case handling.
> The new methods are implemented in a separate
> ContentsCaseSensitivityManager class, in order to avoid adding more
> bloat to vartree.py.
>
> X-Gentoo-Bug: 524236
> X-Gentoo-Url: https://bugs.gentoo.org/show_bug.cgi?id=524236
> ---
...
> +class ContentsCaseSensitivityManager(object):
> + """
> + Implicitly handles case transformations that are needed for
> + case-insensitive support.
> + """
> +
> + def __init__(self, db):
> + """
> + @param db: A dblink instance
> + @type db: vartree.dblink
> + """
Thank you :) looks much better now ;)
Both 1/2, 2/2 look good now. Clear to merge :)
> + self.getcontents = db.getcontents
> +
> + if "case-insensitive-fs" in db.settings.features:
> + self.unmap_key =
> self._unmap_key_case_insensitive
> + self.contains =
> self._contains_case_insensitive
> + self.keys = self._keys_case_insensitive
> +
> + self._contents_insensitive = None
> + self._reverse_key_map = None
> +
> + def clear_cache(self):
> + """
> + Clear all cached contents data.
> + """
> + self._contents_insensitive = None
> + self._reverse_key_map = None
> +
> + def keys(self):
> + """
> + Iterate over all contents keys, which are
> transformed to
> + lowercase when appropriate, for use in
> case-insensitive
> + comparisons.
> + @rtype: iterator
> + @return: An iterator over all the contents keys
> + """
> + return iter(self.getcontents())
> +
> + def contains(self, key):
> + """
> + Check if the given key is contained in the contents,
> using
> + case-insensitive comparison when appropriate.
> + @param key: A filesystem path (including ROOT and
> EPREFIX)
> + @type key: str
> + @rtype: bool
> + @return: True if the given key is contained in the
> contents,
> + False otherwise
> + """
> + return key in self.getcontents()
> +
> + def unmap_key(self, key):
> + """
> + Map a key (from the keys method) back to its
> case-preserved
> + form.
> + @param key: A filesystem path (including ROOT and
> EPREFIX)
> + @type key: str
> + @rtype: str
> + @return: The case-preserved form of key
> + """
> + return key
> +
> + def _case_insensitive_init(self):
> + """
> + Initialize data structures for case-insensitive
> support.
> + """
> + self._contents_insensitive = dict(
> + (k.lower(), v) for k, v in
> self.getcontents().items())
> + self._reverse_key_map = dict(
> + (k.lower(), k) for k in self.getcontents())
> +
> + def _keys_case_insensitive(self):
> + if self._contents_insensitive is None:
> + self._case_insensitive_init()
> + return iter(self._contents_insensitive)
> +
> + _keys_case_insensitive.__doc__ = keys.__doc__
> +
> + def _contains_case_insensitive(self, key):
> + if self._contents_insensitive is None:
> + self._case_insensitive_init()
> + return key.lower() in self._contents_insensitive
> +
> + _contains_case_insensitive.__doc__ = contains.__doc__
> +
> + def _unmap_key_case_insensitive(self, key):
> + if self._reverse_key_map is None:
> + self._case_insensitive_init()
> + return self._reverse_key_map[key]
> +
> + _unmap_key_case_insensitive.__doc__ = unmap_key.__doc__
> diff --git a/pym/portage/dbapi/vartree.py
> b/pym/portage/dbapi/vartree.py index 8b06f4c..81059b1 100644
> --- a/pym/portage/dbapi/vartree.py
> +++ b/pym/portage/dbapi/vartree.py
> @@ -69,6 +69,7 @@ from _emerge.EbuildPhase import EbuildPhase
> from _emerge.emergelog import emergelog
> from _emerge.MiscFunctionsProcess import MiscFunctionsProcess
> from _emerge.SpawnProcess import SpawnProcess
> +from ._ContentsCaseSensitivityManager import
> ContentsCaseSensitivityManager
> import errno
> import fnmatch
> @@ -1525,6 +1526,7 @@ class dblink(object):
> # When necessary, this attribute is modified for
> # compliance with RESTRICT=preserve-libs.
> self._preserve_libs = "preserve-libs" in
> mysettings.features
> + self._contents = ContentsCaseSensitivityManager(self)
>
> def __hash__(self):
> return hash(self._hash_key)
> @@ -1612,6 +1614,7 @@ class dblink(object):
> self.contentscache = None
> self._contents_inodes = None
> self._contents_basenames = None
> + self._contents.clear_cache()
>
> def getcontents(self):
> """
--
Brian Dolbec <dolsen>
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2014-11-17 22:09 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-11-13 1:22 [gentoo-portage-dev] [PATCH] FEATURES=case-insensitive-fs for bug #524236 Zac Medico
2014-11-13 10:29 ` Alexander Berntsen
2014-11-13 17:58 ` Zac Medico
2014-11-16 10:41 ` [gentoo-portage-dev] [PATCH 1/2] dblink: case insensitive support " Zac Medico
2014-11-16 10:41 ` [gentoo-portage-dev] [PATCH 2/2] FEATURES=case-insensitive-fs " Zac Medico
2014-11-16 18:11 ` [gentoo-portage-dev] [PATCH 1/2] dblink: case insensitive support " Brian Dolbec
2014-11-17 1:29 ` [gentoo-portage-dev] [PATCH v2 " Zac Medico
2014-11-17 1:29 ` [gentoo-portage-dev] [PATCH v2 2/2] FEATURES=case-insensitive-fs " Zac Medico
2014-11-17 4:58 ` [gentoo-portage-dev] [PATCH v2 1/2] dblink: case insensitive support " Brian Dolbec
2014-11-17 20:29 ` [gentoo-portage-dev] [PATCH v3 " Zac Medico
2014-11-17 20:29 ` [gentoo-portage-dev] [PATCH v3 2/2] FEATURES=case-insensitive-fs " Zac Medico
2014-11-17 22:09 ` [gentoo-portage-dev] [PATCH v3 1/2] dblink: case insensitive support " Brian Dolbec
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox