Gentoo Archives: gentoo-portage-dev

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

Replies