commit:     9c33dd073220342fc30468c0156c4da109525b17
Author:     Zac Medico <zmedico <AT> gentoo <DOT> org>
AuthorDate: Thu Oct  2 17:57:11 2014 +0000
Commit:     Fabian Groffen <grobian <AT> gentoo <DOT> org>
CommitDate: Wed Nov 12 17:26:14 2014 +0000
URL:        
http://sources.gentoo.org/gitweb/?p=proj/portage.git;a=commit;h=9c33dd07

FEATURES=case-insensitive-fs for bug #524236

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

Signed-off-by: Fabian Groffen <grobian <AT> gentoo.org>

---
 bin/dispatch-conf              |  8 +++++++-
 bin/etc-update                 | 14 +++++++++++---
 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   | 10 +++++++++-
 11 files changed, 80 insertions(+), 14 deletions(-)

diff --git a/bin/dispatch-conf b/bin/dispatch-conf
index 6f06b35..286d821 100755
--- a/bin/dispatch-conf
+++ b/bin/dispatch-conf
@@ -30,6 +30,10 @@ from portage.const import EPREFIX
 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
@@ -145,7 +149,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))
 
         def diff(file1, file2):
             return diffstatusoutput(DIFF_CONTENTS, file1, file2)

diff --git a/bin/etc-update b/bin/etc-update
index b6587ce..db72d03 100755
--- a/bin/etc-update
+++ b/bin/etc-update
@@ -67,6 +67,7 @@ scan() {
        mkdir "${TMP}"/files || die "Failed mkdir command!"
        count=0
        input=0
+       local basename
        local find_opts
        local path
 
@@ -75,13 +76,17 @@ scan() {
 
                if [[ ! -d ${path} ]] ; then
                        [[ ! -f ${path} ]] && continue
-                       local my_basename="${path##*/}"
+                       basename="${path##*/}"
                        path="${path%/*}"
-                       find_opts=( -maxdepth 1 -name 
"._cfg????_${my_basename}" )
+                       find_opts=( -maxdepth 1 )
                else
+                       basename=*
                        # Do not traverse hidden directories such as .svn or 
.git.
-                       find_opts=( -name '.*' -type d -prune -o -name 
'._cfg????_*' )
+                       find_opts=( -name '.*' -type d -prune -o )
                fi
+               ${case_insensitive} && \
+                       find_opts+=( -iname ) || find_opts+=( -name )
+               find_opts+=( "._cfg????_${basename}" )
                find_opts+=( ! -name '.*~' ! -iname '.*.bak' -print )
 
                if [ ! -w "${path}" ] ; then
@@ -623,6 +628,7 @@ ${SET_X} && set -x
 type -P portageq >/dev/null || die "missing portageq"
 portage_vars=(
        CONFIG_PROTECT{,_MASK}
+       FEATURES
        PORTAGE_CONFIGROOT
        PORTAGE_INST_{G,U}ID
        PORTAGE_TMPDIR
@@ -633,6 +639,8 @@ portage_vars=(
 eval $(${PORTAGE_PYTHON:+"${PORTAGE_PYTHON}"} "$(type -P portageq)" envvar -v 
${portage_vars[@]})
 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 d383d42..2291f80 100755
--- a/bin/portageq
+++ b/bin/portageq
@@ -381,8 +381,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
@@ -416,7 +416,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 7019fb3..d090319 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 be32661..81af422 100644
--- a/man/make.conf.5
+++ b/man/make.conf.5
@@ -268,6 +268,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 2f315bb..4f01a7b 100644
--- a/pym/_emerge/depgraph.py
+++ b/pym/_emerge/depgraph.py
@@ -7799,7 +7799,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..bb39f7a 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 5f00fab..681e9c4 100644
--- a/pym/portage/const.py
+++ b/pym/portage/const.py
@@ -145,6 +145,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 deeb779..f7823b3 100644
--- a/pym/portage/dbapi/vartree.py
+++ b/pym/portage/dbapi/vartree.py
@@ -1067,6 +1067,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.
@@ -1204,6 +1209,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 = {}
 
@@ -1220,6 +1227,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
@@ -1251,6 +1260,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:
@@ -1280,8 +1291,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
@@ -1300,6 +1315,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:
@@ -1555,7 +1572,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
 
@@ -2777,7 +2796,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)
@@ -2870,6 +2898,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..7a71092 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 d48b47a..fd7995c 100644
--- a/pym/portage/util/__init__.py
+++ b/pym/portage/util/__init__.py
@@ -1556,10 +1556,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):
@@ -1573,6 +1575,8 @@ class ConfigProtect(object):
                for x in self.protect_list:
                        ppath = normalize_path(
                                os.path.join(self.myroot, 
x.lstrip(os.path.sep)))
+                       if self.case_insensitive:
+                               ppath = ppath.lower()
                        try:
                                if stat.S_ISDIR(os.stat(ppath).st_mode):
                                        self._dirs.add(ppath)
@@ -1585,6 +1589,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."""
@@ -1605,6 +1611,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:

Reply via email to