Gentoo Archives: gentoo-commits

From: Zac Medico <zmedico@g.o>
To: gentoo-commits@l.g.o
Subject: [gentoo-commits] proj/portage:master commit in: pym/portage/dbapi/
Date: Thu, 28 Jul 2011 11:29:51
Message-Id: 4bb08136f073024c5d31dceb1618b6f4e7246369.zmedico@gentoo
1 commit: 4bb08136f073024c5d31dceb1618b6f4e7246369
2 Author: Zac Medico <zmedico <AT> gentoo <DOT> org>
3 AuthorDate: Thu Jul 28 11:29:25 2011 +0000
4 Commit: Zac Medico <zmedico <AT> gentoo <DOT> org>
5 CommitDate: Thu Jul 28 11:29:25 2011 +0000
6 URL: http://git.overlays.gentoo.org/gitweb/?p=proj/portage.git;a=commit;h=4bb08136
7
8 emerge: protect symlinks to directories sometimes
9
10 Before, it was possible to unmerge a symlink to a directory, such that
11 files installed via the path of the symlink could become inaccessible
12 via that path (and also making it impossible to unmerge them via that
13 path).
14
15 Now, the symlink will only be unmerged if the directory that it points
16 to only contains regular files which are all being unmerged. In any
17 other case, the symlink will be preserved and an eerror log message
18 will record the event. This will give the user an opportunity to take
19 further action if they deem it necessary, and such symlink preservation
20 will not be silent as it was reported in bug #326685, comment #3.
21
22 ---
23 pym/portage/dbapi/vartree.py | 90 +++++++++++++++++++++++++++++++++++++++++-
24 1 files changed, 88 insertions(+), 2 deletions(-)
25
26 diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py
27 index 47f5eb2..6bb68d3 100644
28 --- a/pym/portage/dbapi/vartree.py
29 +++ b/pym/portage/dbapi/vartree.py
30 @@ -70,6 +70,7 @@ import shutil
31 import stat
32 import sys
33 import tempfile
34 +import textwrap
35 import time
36 import warnings
37
38 @@ -1908,6 +1909,7 @@ class dblink(object):
39
40 cfgfiledict = grabdict(self.vartree.dbapi._conf_mem_file)
41 stale_confmem = []
42 + protected_symlinks = []
43
44 unmerge_orphans = "unmerge-orphans" in self.settings.features
45 calc_prelink = "prelink-checksums" in self.settings.features
46 @@ -2027,9 +2029,34 @@ class dblink(object):
47 if dblnk.isowner(relative_path):
48 is_owned = True
49 break
50 - if is_owned:
51 +
52 + if is_owned and \
53 + (islink and statobj and stat.S_ISDIR(statobj.st_mode)):
54 # A new instance of this package claims the file, so
55 - # don't unmerge it.
56 + # don't unmerge it. If the file is symlink to a
57 + # directory and the unmerging package installed it as
58 + # a symlink, but the new owner has it listed as a
59 + # directory, then we'll produce a warning since the
60 + # symlink is a sort of orphan in this case (see
61 + # bug #326685).
62 + symlink_orphan = False
63 + for dblnk in others_in_slot:
64 + parent_contents_key = \
65 + dblnk._match_contents(relative_path)
66 + if not parent_contents_key:
67 + continue
68 + if not parent_contents_key.startswith(
69 + real_root):
70 + continue
71 + if dblnk.getcontents()[
72 + parent_contents_key][0] == "dir":
73 + symlink_orphan = True
74 + break
75 +
76 + if symlink_orphan:
77 + protected_symlinks.append(relative_path)
78 +
79 + if is_owned:
80 show_unmerge("---", unmerge_desc["replaced"], file_type, obj)
81 continue
82 elif relative_path in cfgfiledict:
83 @@ -2076,6 +2103,52 @@ class dblink(object):
84 if not islink:
85 show_unmerge("---", unmerge_desc["!sym"], file_type, obj)
86 continue
87 +
88 + # If this symlink points to a direcory then we don't want
89 + # to unmerge it if there are any other packages that
90 + # installed files into the directory via this symlink
91 + # (see bug #326685).
92 + # TODO: Resolving a symlink to a directory will require
93 + # simulation if $ROOT != / and the link is not relative.
94 + if islink and statobj and stat.S_ISDIR(statobj.st_mode) \
95 + and obj.startswith(real_root):
96 +
97 + relative_path = obj[real_root_len:]
98 + try:
99 + target_dir_contents = os.listdir(obj)
100 + except OSError:
101 + pass
102 + else:
103 + if target_dir_contents:
104 + # If all the children are regular files owned
105 + # by this package, then the symlink should be
106 + # safe to unmerge.
107 + all_owned = True
108 + for child in target_dir_contents:
109 + child = os.path.join(relative_path, child)
110 + if not self.isowner(child):
111 + all_owned = False
112 + break
113 + try:
114 + child_lstat = os.lstat(os.path.join(
115 + real_root, child.lstrip(os.sep)))
116 + except OSError:
117 + continue
118 +
119 + if not stat.S_ISREG(child_lstat.st_mode):
120 + # Nested symlinks or directories make
121 + # the issue very complex, so just
122 + # preserve the symlink in order to be
123 + # on the safe side.
124 + all_owned = False
125 + break
126 +
127 + if not all_owned:
128 + protected_symlinks.append(relative_path)
129 + show_unmerge("---", unmerge_desc["!empty"],
130 + file_type, obj)
131 + continue
132 +
133 # Go ahead and unlink symlinks to directories here when
134 # they're actually recorded as symlinks in the contents.
135 # Normally, symlinks such as /lib -> lib64 are not recorded
136 @@ -2152,6 +2225,19 @@ class dblink(object):
137 show_unmerge("---", unmerge_desc["!empty"], "dir", obj)
138 del e
139
140 + if protected_symlinks:
141 + msg = "One or more symlinks to directories have been " + \
142 + "preserved in order to ensure that files installed " + \
143 + "via these symlinks remain accessible:"
144 + lines = textwrap.wrap(msg, 72)
145 + lines.append("")
146 + protected_symlinks.reverse()
147 + for f in protected_symlinks:
148 + lines.append("\t%s" % (os.path.join(real_root,
149 + f.lstrip(os.sep))))
150 + lines.append("")
151 + self._elog("eerror", "postrm", lines)
152 +
153 # Remove stale entries from config memory.
154 if stale_confmem:
155 for filename in stale_confmem: