From 580eba6603b6b4a2d38fbe5388dabf25c805d60d Mon Sep 17 00:00:00 2001
From: Zac Medico <zmedico@gentoo.org>
Date: Fri, 31 Oct 2008 20:21:53 +0000
Subject: [PATCH] Bug #243178 - Handle file collisions with preserved libs by
 allowing the current package to assume ownership and unregistering the
 preserved libraries.

svn path=/main/trunk/; revision=11766
---
 pym/portage/dbapi/vartree.py | 76 +++++++++++++++++++++++++++++++++++-
 1 file changed, 74 insertions(+), 2 deletions(-)

diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py
index 363087bfa..5c4de76af 100644
--- a/pym/portage/dbapi/vartree.py
+++ b/pym/portage/dbapi/vartree.py
@@ -2681,6 +2681,18 @@ class dblink(object):
 			collision_ignore = set([normalize_path(myignore) for myignore in \
 				shlex.split(self.settings.get("COLLISION_IGNORE", ""))])
 
+			# For collisions with preserved libraries, the current package
+			# will assume ownership and the libraries will be unregistered.
+			plib_dict = self.vartree.dbapi.plib_registry.getPreservedLibs()
+			plib_cpv_map = {}
+			plib_paths = set()
+			for cpv, paths in plib_dict.iteritems():
+				plib_paths.update(paths)
+				for f in paths:
+					plib_cpv_map[f] = cpv
+			plib_inodes = self._lstat_inode_map(plib_paths)
+			plib_collisions = {}
+
 			showMessage = self._display_merge
 			scheduler = self._scheduler
 			stopmerge = False
@@ -2732,6 +2744,21 @@ class dblink(object):
 						raise
 				if f[0] != "/":
 					f="/"+f
+
+				plibs = plib_inodes.get((dest_lstat.st_dev, dest_lstat.st_ino))
+				if plibs:
+					for path in plibs:
+						cpv = plib_cpv_map[path]
+						paths = plib_collisions.get(cpv)
+						if paths is None:
+							paths = set()
+							plib_collisions[cpv] = paths
+						paths.add(path)
+					# The current package will assume ownership and the
+					# libraries will be unregistered, so exclude this
+					# path from the normal collisions.
+					continue
+
 				isowned = False
 				full_path = os.path.join(destroot, f.lstrip(os.path.sep))
 				for ver in mypkglist:
@@ -2752,7 +2779,33 @@ class dblink(object):
 									break
 					if stopmerge:
 						collisions.append(f)
-			return collisions
+			return collisions, plib_collisions
+
+	def _lstat_inode_map(self, path_iter):
+		"""
+		Use lstat to create a map of the form:
+		  {(st_dev, st_ino) : set([path1, path2, ...])}
+		Multiple paths may reference the same inode due to hardlinks.
+		All lstat() calls are relative to self.myroot.
+		"""
+		root = self.myroot
+		inode_map = {}
+		for f in path_iter:
+			path = os.path.join(root, f.lstrip(os.sep))
+			try:
+				st = os.lstat(path)
+			except OSError, e:
+				if e.errno not in (errno.ENOENT, errno.ENOTDIR):
+					raise
+				del e
+				continue
+			key = (st.st_dev, st.st_ino)
+			paths = inode_map.get(key)
+			if paths is None:
+				paths = set()
+				inode_map[key] = paths
+			paths.add(f)
+		return inode_map
 
 	def _security_check(self, installed_instances):
 		if not installed_instances:
@@ -3011,7 +3064,8 @@ class dblink(object):
 			blockers = self._blockers()
 		if blockers is None:
 			blockers = []
-		collisions = self._collision_protect(srcroot, destroot,
+		collisions, plib_collisions = \
+			self._collision_protect(srcroot, destroot,
 			others_in_slot + blockers, myfilelist + mylinklist)
 
 		# Make sure the ebuild environment is initialized and that ${T}/elog
@@ -3289,6 +3343,24 @@ class dblink(object):
 			self.vartree.dbapi.removeFromContents(blocker, iter(contents),
 				relative_paths=False)
 
+		# Unregister any preserved libs that this package has overwritten
+		# and update the contents of the packages that owned them.
+		plib_registry = self.vartree.dbapi.plib_registry
+		plib_dict = plib_registry.getPreservedLibs()
+		for cpv, paths in plib_collisions.iteritems():
+			if cpv not in plib_dict:
+				continue
+			if cpv == self.mycpv:
+				continue
+			try:
+				slot, counter = self.vartree.dbapi.aux_get(
+					cpv, ["SLOT", "COUNTER"])
+			except KeyError:
+				continue
+			remaining = [f for f in plib_dict[cpv] if f not in paths]
+			plib_registry.register(cpv, slot, counter, remaining)
+			self.vartree.dbapi.removeFromContents(cpv, paths)
+
 		self.vartree.dbapi._add(self)
 		contents = self.getcontents()
 
-- 
2.26.2