Gentoo Archives: gentoo-portage-dev

From: Chris Reffett <creffett@g.o>
To: gentoo-portage-dev@l.g.o
Subject: [gentoo-portage-dev] [PATCH v5] Test for read-only filesystems, bail out during preinst if there are any which will be written to and display a useful error message. Fixes bug 378869.
Date: Tue, 21 Jan 2014 04:01:15
Message-Id: 1390276831-1831-1-git-send-email-creffett@gentoo.org
In Reply to: Re: [gentoo-portage-dev] [PATCH v4] Test for read-only filesystems, bail out during preinst if there are any which will be written to and display a useful error message. Fixes bug 378869. by Mike Frysinger
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

Replies