Gentoo Archives: gentoo-portage-dev

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

Replies