@@ -, +, @@ there are any which will be written to and display a useful error message. Fixes bug 378869. --- pym/portage/dbapi/vartree.py | 31 ++++++++++++++++++++++++ pym/portage/util/checkwriteable.py | 49 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 pym/portage/util/checkwriteable.py --- a/pym/portage/dbapi/vartree.py +++ a/pym/portage/dbapi/vartree.py @@ -28,6 +28,7 @@ portage.proxy.lazyimport.lazyimport(globals(), 'portage.util:apply_secpass_permissions,ConfigProtect,ensure_dirs,' + \ 'writemsg,writemsg_level,write_atomic,atomic_ofstream,writedict,' + \ 'grabdict,normalize_path,new_protect_filename', + 'portage.util.checkwriteable:check_dirs_writeable', 'portage.util.digraph:digraph', 'portage.util.env_update:env_update', 'portage.util.listdir:dircache,listdir', @@ -3508,6 +3509,8 @@ class dblink(object): This function does the following: + calls check_dirs_writeable to verify that no files will be + written to read-only filesystems calls self._preserve_libs if FEATURES=preserve-libs calls self._collision_protect if FEATURES=collision-protect calls doebuild(mydo=pkg_preinst) @@ -3685,6 +3688,7 @@ class dblink(object): eagain_error = False myfilelist = [] + mydirlist = [] mylinklist = [] paths_with_newlines = [] def onerror(e): @@ -3717,6 +3721,9 @@ class dblink(object): unicode_errors.append(new_parent[ed_len:]) break + relative_path = parent[srcroot_len:] + mydirlist.append("/" + relative_path) + for fname in files: try: fname = _unicode_decode(fname, @@ -3829,6 +3836,30 @@ class dblink(object): for other in others_in_slot]) prepare_build_dirs(settings=self.settings, cleanup=cleanup) + # Check for read-only filesystems + rofilesystems = check_dirs_writeable(mydirlist) + + if rofilesystems: + msg = _("One or more files installed to this package are " + "set to be installed to read-only filesystems. " + "Please mount the filesystems below read-write " + "and retry.") + msg = textwrap.wrap(msg, 70) + msg.append("") + for f in rofilesystems: + msg.append("\t%s" % os.path.join(destroot, + f.lstrip(os.path.sep))) + msg.append("") + self._elog("eerror", "preinst", msg) + + msg = _("Package '%s' NOT merged due to read-only file systems.") % \ + self.settings.mycpv + msg += _(" If necessary, refer to your elog " + "messages for the whole content of the above message.") + msg = textwrap.wrap(msg, 70) + eerror(msg) + return 1 + # check for package collisions blockers = self._blockers if blockers is None: --- a/pym/portage/util/checkwriteable.py +++ a/pym/portage/util/checkwriteable.py @@ -0,0 +1,49 @@ +#-*- coding:utf-8 -*- +# Copyright 2014 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 + +from __future__ import unicode_literals + +import re + +from portage.util import writemsg +from portage.localization import _ + + +def check_dirs_writeable(dir_list): + """ + Use /proc/mounts to check that no directories installed by the ebuild are set to + be installed to a read-only filesystem + + @param dir_list: Directories installed by the ebuild + @type dir_list: List + @return: + 1. A list of filesystems which are both set to be written to and are mounted + read-only, may be empty. + """ + ro_filesystems = set() + ro_filesystems_written = set() + + try: + with open("/proc/mounts") as procmounts: + roregex = re.compile(r'(\A|,)ro(\Z|,)') + for line in procmounts: + if roregex.search(line.split(" ")[3].strip()) is not None: + romount = line.split(" ")[1].strip() + ro_filesystems.add(romount) + + # If /proc/mounts can't be read, assume that there are no RO + # filesystems and return + except EnvironmentError: + writemsg(_("!!! /proc/mounts cannot be read")) + return [] + + if not ro_filesystems: + return ro_filesystems + + for directory in dir_list: + for filesystem in ro_filesystems: + if re.match(filesystem, directory): + ro_filesystems_written.add(filesystem) + + return ro_filesystems_written --