1 |
commit: 47ef9a0969474f963dc8e52bfbbb8bc075e8d73c |
2 |
Author: Chris Reffett <creffett <AT> gentoo <DOT> org> |
3 |
AuthorDate: Fri Jan 10 14:03:26 2014 +0000 |
4 |
Commit: Chris Reffett <creffett <AT> gentoo <DOT> org> |
5 |
CommitDate: Mon Jan 27 04:01:35 2014 +0000 |
6 |
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/portage.git;a=commit;h=47ef9a09 |
7 |
|
8 |
Test for read-only filesystems, fixes bug 378869 |
9 |
|
10 |
If any read-only filesystems need writing, bail out and display a |
11 |
useful error message. |
12 |
|
13 |
v2: Reformat, add a function to return an appropriate read-only checker |
14 |
for the operating system, so that this can be extended to other OSes. |
15 |
|
16 |
v3: minor formatting tweaks from bernalex, including use os.path.join() |
17 |
instead of string concatenation |
18 |
|
19 |
v4: Update copyright header, change the code in rochecker to open with |
20 |
io.open in order to not break on non-ascii characters as reported by |
21 |
Arfrever. Change get_ro_checker to use a dict as suggested by vapier. |
22 |
|
23 |
v5: Fix comment format as requested by vapier. |
24 |
|
25 |
v6: Change linux_ro_checker to use set.intersection instead of the |
26 |
longer check-and-append system as suggested by vapier. |
27 |
|
28 |
v7: rename rochecker -> writeable_check |
29 |
|
30 |
--- |
31 |
pym/portage/dbapi/vartree.py | 34 +++++++++++++++- |
32 |
pym/portage/util/writeable_check.py | 79 +++++++++++++++++++++++++++++++++++++ |
33 |
2 files changed, 112 insertions(+), 1 deletion(-) |
34 |
|
35 |
diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py |
36 |
index ed62323..7a4a3d2 100644 |
37 |
--- a/pym/portage/dbapi/vartree.py |
38 |
+++ b/pym/portage/dbapi/vartree.py |
39 |
@@ -1,4 +1,4 @@ |
40 |
-# Copyright 1998-2013 Gentoo Foundation |
41 |
+# Copyright 1998-2014 Gentoo Foundation |
42 |
# Distributed under the terms of the GNU General Public License v2 |
43 |
|
44 |
from __future__ import unicode_literals |
45 |
@@ -32,6 +32,7 @@ portage.proxy.lazyimport.lazyimport(globals(), |
46 |
'portage.util.env_update:env_update', |
47 |
'portage.util.listdir:dircache,listdir', |
48 |
'portage.util.movefile:movefile', |
49 |
+ 'portage.util.writeable_check:get_ro_checker', |
50 |
'portage.util._dyn_libs.PreservedLibsRegistry:PreservedLibsRegistry', |
51 |
'portage.util._dyn_libs.LinkageMapELF:LinkageMapELF@LinkageMap', |
52 |
'portage.util._async.SchedulerInterface:SchedulerInterface', |
53 |
@@ -3508,6 +3509,8 @@ class dblink(object): |
54 |
|
55 |
This function does the following: |
56 |
|
57 |
+ calls get_ro_checker to retrieve a function for checking whether Portage |
58 |
+ will write to a read-only filesystem, then runs it against the directory list |
59 |
calls self._preserve_libs if FEATURES=preserve-libs |
60 |
calls self._collision_protect if FEATURES=collision-protect |
61 |
calls doebuild(mydo=pkg_preinst) |
62 |
@@ -3685,6 +3688,7 @@ class dblink(object): |
63 |
eagain_error = False |
64 |
|
65 |
myfilelist = [] |
66 |
+ mydirlist = [] |
67 |
mylinklist = [] |
68 |
paths_with_newlines = [] |
69 |
def onerror(e): |
70 |
@@ -3717,6 +3721,9 @@ class dblink(object): |
71 |
unicode_errors.append(new_parent[ed_len:]) |
72 |
break |
73 |
|
74 |
+ relative_path = parent[srcroot_len:] |
75 |
+ mydirlist.append(os.path.join("/", relative_path)) |
76 |
+ |
77 |
for fname in files: |
78 |
try: |
79 |
fname = _unicode_decode(fname, |
80 |
@@ -3829,6 +3836,31 @@ class dblink(object): |
81 |
for other in others_in_slot]) |
82 |
prepare_build_dirs(settings=self.settings, cleanup=cleanup) |
83 |
|
84 |
+ # Check for read-only filesystems. |
85 |
+ ro_checker = get_ro_checker() |
86 |
+ rofilesystems = ro_checker(mydirlist) |
87 |
+ |
88 |
+ if rofilesystems: |
89 |
+ msg = _("One or more files installed to this package are " |
90 |
+ "set to be installed to read-only filesystems. " |
91 |
+ "Please mount the following filesystems as read-write " |
92 |
+ "and retry.") |
93 |
+ msg = textwrap.wrap(msg, 70) |
94 |
+ msg.append("") |
95 |
+ for f in rofilesystems: |
96 |
+ msg.append("\t%s" % os.path.join(destroot, |
97 |
+ f.lstrip(os.path.sep))) |
98 |
+ msg.append("") |
99 |
+ self._elog("eerror", "preinst", msg) |
100 |
+ |
101 |
+ msg = _("Package '%s' NOT merged due to read-only file systems.") % \ |
102 |
+ self.settings.mycpv |
103 |
+ msg += _(" If necessary, refer to your elog " |
104 |
+ "messages for the whole content of the above message.") |
105 |
+ msg = textwrap.wrap(msg, 70) |
106 |
+ eerror(msg) |
107 |
+ return 1 |
108 |
+ |
109 |
# check for package collisions |
110 |
blockers = self._blockers |
111 |
if blockers is None: |
112 |
|
113 |
diff --git a/pym/portage/util/writeable_check.py b/pym/portage/util/writeable_check.py |
114 |
new file mode 100644 |
115 |
index 0000000..e6ddce6 |
116 |
--- /dev/null |
117 |
+++ b/pym/portage/util/writeable_check.py |
118 |
@@ -0,0 +1,79 @@ |
119 |
+#-*- coding:utf-8 -*- |
120 |
+# Copyright 2014 Gentoo Foundation |
121 |
+# Distributed under the terms of the GNU General Public License v2 |
122 |
+""" |
123 |
+Methods to check whether Portage is going to write to read-only filesystems. |
124 |
+Since the methods are not portable across different OSes, each OS needs its |
125 |
+own method. To expand RO checking for different OSes, add a method which |
126 |
+accepts a list of directories and returns a list of mounts which need to be |
127 |
+remounted RW, then add "elif ostype == (the ostype value for your OS)" to |
128 |
+get_ro_checker(). |
129 |
+""" |
130 |
+from __future__ import unicode_literals |
131 |
+ |
132 |
+import io |
133 |
+import logging |
134 |
+import re |
135 |
+ |
136 |
+from portage import _encodings |
137 |
+from portage.util import writemsg_level |
138 |
+from portage.localization import _ |
139 |
+from portage.data import ostype |
140 |
+ |
141 |
+ |
142 |
+def get_ro_checker(): |
143 |
+ """ |
144 |
+ Uses the system type to find an appropriate method for testing whether Portage |
145 |
+ is going to write to any read-only filesystems. |
146 |
+ |
147 |
+ @return: |
148 |
+ 1. A method for testing for RO filesystems appropriate to the current system. |
149 |
+ """ |
150 |
+ return _CHECKERS.get(ostype, empty_ro_checker) |
151 |
+ |
152 |
+ |
153 |
+def linux_ro_checker(dir_list): |
154 |
+ """ |
155 |
+ Use /proc/mounts to check that no directories installed by the ebuild are set |
156 |
+ to be installed to a read-only filesystem. |
157 |
+ |
158 |
+ @param dir_list: A list of directories installed by the ebuild. |
159 |
+ @type dir_list: List |
160 |
+ @return: |
161 |
+ 1. A list of filesystems which are both set to be written to and are mounted |
162 |
+ read-only, may be empty. |
163 |
+ """ |
164 |
+ ro_filesystems = set() |
165 |
+ |
166 |
+ try: |
167 |
+ with io.open("/proc/mounts", mode='r', encoding=_encodings['content'], |
168 |
+ errors='replace') as f: |
169 |
+ roregex = re.compile(r'(\A|,)ro(\Z|,)') |
170 |
+ for line in f: |
171 |
+ if roregex.search(line.split(" ")[3].strip()) is not None: |
172 |
+ romount = line.split(" ")[1].strip() |
173 |
+ ro_filesystems.add(romount) |
174 |
+ |
175 |
+ # If /proc/mounts can't be read, assume that there are no RO |
176 |
+ # filesystems and return. |
177 |
+ except EnvironmentError: |
178 |
+ writemsg_level(_("!!! /proc/mounts cannot be read"), |
179 |
+ level=logging.WARNING, noiselevel=-1) |
180 |
+ return [] |
181 |
+ |
182 |
+ return set.intersection(ro_filesystems, set(dir_list)) |
183 |
+ |
184 |
+ |
185 |
+def empty_ro_checker(dir_list): |
186 |
+ """ |
187 |
+ Always returns [], this is the fallback function if the system does not have |
188 |
+ an ro_checker method defined. |
189 |
+ """ |
190 |
+ return [] |
191 |
+ |
192 |
+ |
193 |
+# _CHECKERS is a map from ostype output to the appropriate function to return |
194 |
+# in get_ro_checker. |
195 |
+_CHECKERS = { |
196 |
+ "Linux": linux_ro_checker, |
197 |
+} |