Gentoo Archives: gentoo-portage-dev

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