dblink: split out _unmerge_dirs method
authorZac Medico <zmedico@gentoo.org>
Thu, 23 Feb 2012 03:07:11 +0000 (19:07 -0800)
committerZac Medico <zmedico@gentoo.org>
Thu, 23 Feb 2012 03:07:11 +0000 (19:07 -0800)
This code will need to be called twice when safely unmerging symlinks
to directories (bug #384397).

pym/portage/dbapi/vartree.py

index 96c2530bb4ec3cb8a1d9c0df629a82ec0e045475..4b1ede121f1e41909ceeedad02e6fdcf3e60065c 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright 1998-2011 Gentoo Foundation
+# Copyright 1998-2012 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
 __all__ = [
@@ -1388,6 +1388,20 @@ class dblink(object):
                r')$'
        )
 
+       # These files are generated by emerge, so we need to remove
+       # them when they are the only thing left in a directory.
+       _infodir_cleanup = frozenset(["dir", "dir.old"])
+
+       _ignored_unlink_errnos = (
+               errno.EBUSY, errno.ENOENT,
+               errno.ENOTDIR, errno.EISDIR)
+
+       _ignored_rmdir_errnos = (
+               errno.EEXIST, errno.ENOTEMPTY,
+               errno.EBUSY, errno.ENOENT,
+               errno.ENOTDIR, errno.EISDIR,
+               errno.EPERM)
+
        def __init__(self, cat, pkg, myroot=None, settings=None, treetype=None,
                vartree=None, blockers=None, scheduler=None, pipe=None):
                """
@@ -1973,6 +1987,10 @@ class dblink(object):
                                        log_path=log_path, background=background,
                                        level=level, noiselevel=noiselevel)
 
+       def _show_unmerge(self, zing, desc, file_type, file_name):
+               self._display_merge("%s %s %s %s\n" % \
+                       (zing, desc.ljust(8), file_type, file_name))
+
        def _unmerge_pkgfiles(self, pkgfiles, others_in_slot):
                """
                
@@ -1989,6 +2007,9 @@ class dblink(object):
                os = _os_merge
                perf_md5 = perform_md5
                showMessage = self._display_merge
+               show_unmerge = self._show_unmerge
+               ignored_unlink_errnos = self._ignored_unlink_errnos
+               ignored_rmdir_errnos = self._ignored_rmdir_errnos
 
                if not pkgfiles:
                        showMessage(_("No package files given... Grabbing a set.\n"))
@@ -2024,14 +2045,6 @@ class dblink(object):
 
                        #process symlinks second-to-last, directories last.
                        mydirs = set()
-                       ignored_unlink_errnos = (
-                               errno.EBUSY, errno.ENOENT,
-                               errno.ENOTDIR, errno.EISDIR)
-                       ignored_rmdir_errnos = (
-                               errno.EEXIST, errno.ENOTEMPTY,
-                               errno.EBUSY, errno.ENOENT,
-                               errno.ENOTDIR, errno.EISDIR,
-                               errno.EPERM)
                        modprotect = os.path.join(self._eroot, "lib/modules/")
 
                        def unlink(file_name, lstatobj):
@@ -2067,10 +2080,6 @@ class dblink(object):
                                                # Restore the parent flags we saved before unlinking
                                                bsd_chflags.chflags(parent_name, pflags)
 
-                       def show_unmerge(zing, desc, file_type, file_name):
-                                       showMessage("%s %s %s %s\n" % \
-                                               (zing, desc.ljust(8), file_type, file_name))
-
                        unmerge_desc = {}
                        unmerge_desc["cfgpro"] = _("cfgpro")
                        unmerge_desc["replaced"] = _("replaced")
@@ -2088,9 +2097,6 @@ class dblink(object):
                        real_root_len = len(real_root) - 1
                        eroot = self.settings["EROOT"]
 
-                       # These files are generated by emerge, so we need to remove
-                       # them when they are the only thing left in a directory.
-                       infodir_cleanup = frozenset(["dir", "dir.old"])
                        infodirs = frozenset(infodir for infodir in chain(
                                self.settings.get("INFOPATH", "").split(":"),
                                self.settings.get("INFODIR", "").split(":")) if infodir)
@@ -2325,78 +2331,8 @@ class dblink(object):
                                elif pkgfiles[objkey][0] == "dev":
                                        show_unmerge("---", "", file_type, obj)
 
-                       mydirs = sorted(mydirs)
-                       mydirs.reverse()
-
-                       for obj, inode_key in mydirs:
-                               # Treat any directory named "info" as a candidate here,
-                               # since it might have been in INFOPATH previously even
-                               # though it may not be there now.
-                               if inode_key in infodirs_inodes or \
-                                       os.path.basename(obj) == "info":
-                                       try:
-                                               remaining = os.listdir(obj)
-                                       except OSError:
-                                               pass
-                                       else:
-                                               cleanup_info_dir = ()
-                                               if remaining and \
-                                                       len(remaining) <= len(infodir_cleanup):
-                                                       if not set(remaining).difference(infodir_cleanup):
-                                                               cleanup_info_dir = remaining
-
-                                               for child in cleanup_info_dir:
-                                                       child = os.path.join(obj, child)
-                                                       try:
-                                                               lstatobj = os.lstat(child)
-                                                               if stat.S_ISREG(lstatobj.st_mode):
-                                                                       unlink(child, lstatobj)
-                                                                       show_unmerge("<<<", "", "obj", child)
-                                                       except EnvironmentError as e:
-                                                               if e.errno not in ignored_unlink_errnos:
-                                                                       raise
-                                                               del e
-                                                               show_unmerge("!!!", "", "obj", child)
-                               try:
-                                       if bsd_chflags:
-                                               lstatobj = os.lstat(obj)
-                                               if lstatobj.st_flags != 0:
-                                                       bsd_chflags.lchflags(obj, 0)
-                                               parent_name = os.path.dirname(obj)
-                                               # Use normal stat/chflags for the parent since we want to
-                                               # follow any symlinks to the real parent directory.
-                                               pflags = os.stat(parent_name).st_flags
-                                               if pflags != 0:
-                                                       bsd_chflags.chflags(parent_name, 0)
-                                       try:
-                                               os.rmdir(obj)
-                                       finally:
-                                               if bsd_chflags and pflags != 0:
-                                                       # Restore the parent flags we saved before unlinking
-                                                       bsd_chflags.chflags(parent_name, pflags)
-                                       show_unmerge("<<<", "", "dir", obj)
-                               except EnvironmentError as e:
-                                       if e.errno not in ignored_rmdir_errnos:
-                                               raise
-                                       if e.errno != errno.ENOENT:
-                                               show_unmerge("---", unmerge_desc["!empty"], "dir", obj)
-                                       del e
-                               else:
-                                       # When a directory is successfully removed, there's
-                                       # no need to protect symlinks that point to it.
-                                       unmerge_syms = protected_symlinks.pop(inode_key, None)
-                                       if unmerge_syms is not None:
-                                               for relative_path in unmerge_syms:
-                                                       obj = os.path.join(real_root,
-                                                               relative_path.lstrip(os.sep))
-                                                       try:
-                                                               unlink(obj, os.lstat(obj))
-                                                               show_unmerge("<<<", "", "sym", obj)
-                                                       except (OSError, IOError) as e:
-                                                               if e.errno not in ignored_unlink_errnos:
-                                                                       raise
-                                                               del e
-                                                               show_unmerge("!!!", "", "sym", obj)
+                       self._unmerge_dirs(mydirs, infodirs_inodes,
+                               protected_symlinks, unmerge_desc, unlink, os)
 
                if protected_symlinks:
                        msg = "One or more symlinks to directories have been " + \
@@ -2422,6 +2358,88 @@ class dblink(object):
                #remove self from vartree database so that our own virtual gets zapped if we're the last node
                self.vartree.zap(self.mycpv)
 
+       def _unmerge_dirs(self, dirs, infodirs_inodes,
+               protected_symlinks, unmerge_desc, unlink, os):
+
+               show_unmerge = self._show_unmerge
+               infodir_cleanup = self._infodir_cleanup
+               ignored_unlink_errnos = self._ignored_unlink_errnos
+               ignored_rmdir_errnos = self._ignored_rmdir_errnos
+               real_root = self.settings['ROOT']
+
+               dirs = sorted(dirs)
+               dirs.reverse()
+
+               for obj, inode_key in dirs:
+                       # Treat any directory named "info" as a candidate here,
+                       # since it might have been in INFOPATH previously even
+                       # though it may not be there now.
+                       if inode_key in infodirs_inodes or \
+                               os.path.basename(obj) == "info":
+                               try:
+                                       remaining = os.listdir(obj)
+                               except OSError:
+                                       pass
+                               else:
+                                       cleanup_info_dir = ()
+                                       if remaining and \
+                                               len(remaining) <= len(infodir_cleanup):
+                                               if not set(remaining).difference(infodir_cleanup):
+                                                       cleanup_info_dir = remaining
+
+                                       for child in cleanup_info_dir:
+                                               child = os.path.join(obj, child)
+                                               try:
+                                                       lstatobj = os.lstat(child)
+                                                       if stat.S_ISREG(lstatobj.st_mode):
+                                                               unlink(child, lstatobj)
+                                                               show_unmerge("<<<", "", "obj", child)
+                                               except EnvironmentError as e:
+                                                       if e.errno not in ignored_unlink_errnos:
+                                                               raise
+                                                       del e
+                                                       show_unmerge("!!!", "", "obj", child)
+                       try:
+                               if bsd_chflags:
+                                       lstatobj = os.lstat(obj)
+                                       if lstatobj.st_flags != 0:
+                                               bsd_chflags.lchflags(obj, 0)
+                                       parent_name = os.path.dirname(obj)
+                                       # Use normal stat/chflags for the parent since we want to
+                                       # follow any symlinks to the real parent directory.
+                                       pflags = os.stat(parent_name).st_flags
+                                       if pflags != 0:
+                                               bsd_chflags.chflags(parent_name, 0)
+                               try:
+                                       os.rmdir(obj)
+                               finally:
+                                       if bsd_chflags and pflags != 0:
+                                               # Restore the parent flags we saved before unlinking
+                                               bsd_chflags.chflags(parent_name, pflags)
+                               show_unmerge("<<<", "", "dir", obj)
+                       except EnvironmentError as e:
+                               if e.errno not in ignored_rmdir_errnos:
+                                       raise
+                               if e.errno != errno.ENOENT:
+                                       show_unmerge("---", unmerge_desc["!empty"], "dir", obj)
+                               del e
+                       else:
+                               # When a directory is successfully removed, there's
+                               # no need to protect symlinks that point to it.
+                               unmerge_syms = protected_symlinks.pop(inode_key, None)
+                               if unmerge_syms is not None:
+                                       for relative_path in unmerge_syms:
+                                               obj = os.path.join(real_root,
+                                                       relative_path.lstrip(os.sep))
+                                               try:
+                                                       unlink(obj, os.lstat(obj))
+                                                       show_unmerge("<<<", "", "sym", obj)
+                                               except (OSError, IOError) as e:
+                                                       if e.errno not in ignored_unlink_errnos:
+                                                               raise
+                                                       del e
+                                                       show_unmerge("!!!", "", "sym", obj)
+
        def isowner(self, filename, destroot=None):
                """ 
                Check if a file belongs to this package. This may