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