From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) by finch.gentoo.org (Postfix) with ESMTP id 5574A138247 for ; Tue, 21 Jan 2014 00:51:36 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id A1C9AE0DA1; Tue, 21 Jan 2014 00:51:34 +0000 (UTC) Received: from smtp.gentoo.org (smtp.gentoo.org [140.211.166.183]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id 0A13BE0D6B for ; Tue, 21 Jan 2014 00:51:33 +0000 (UTC) Received: from localhost.localdomain (SSID-MASON-SECURE-217.wireless.gmu.edu [192.5.215.217]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.gentoo.org (Postfix) with ESMTPS id AC47433ECA5 for ; Tue, 21 Jan 2014 00:51:32 +0000 (UTC) From: Chris Reffett To: gentoo-portage-dev@lists.gentoo.org 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: Mon, 20 Jan 2014 19:50:54 -0500 Message-Id: <1390265454-20585-1-git-send-email-creffett@gentoo.org> X-Mailer: git-send-email 1.8.5.1 In-Reply-To: <201401190417.41690.vapier@gentoo.org> References: <201401190417.41690.vapier@gentoo.org> Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-portage-dev@lists.gentoo.org Reply-to: gentoo-portage-dev@lists.gentoo.org X-Archives-Salt: fa106eb2-7947-4fd5-b031-9b0178795a12 X-Archives-Hash: b8446623205d23b3ad2a7871e6f9abf1 v2: Reformat, add a function to return an appropriate read-only checker for the operating system, so that this can be extended to other OSes. v3: minor formatting tweaks from bernalex, including use os.path.join() instead of string concatenation v4: Update copyright header, change the code in rochecker to open with io.open in order to not break on non-ascii characters as reported by Arfrever. Change get_ro_checker to use a dict as suggested by vapier. --- pym/portage/dbapi/vartree.py | 34 ++++++++++++++++- pym/portage/util/rochecker.py | 87 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 pym/portage/util/rochecker.py diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py index ed62323..22cf222 100644 --- a/pym/portage/dbapi/vartree.py +++ b/pym/portage/dbapi/vartree.py @@ -1,4 +1,4 @@ -# Copyright 1998-2013 Gentoo Foundation +# Copyright 1998-2014 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 from __future__ import unicode_literals @@ -32,6 +32,7 @@ portage.proxy.lazyimport.lazyimport(globals(), 'portage.util.env_update:env_update', 'portage.util.listdir:dircache,listdir', 'portage.util.movefile:movefile', + 'portage.util.rochecker:get_ro_checker', 'portage.util._dyn_libs.PreservedLibsRegistry:PreservedLibsRegistry', 'portage.util._dyn_libs.LinkageMapELF:LinkageMapELF@LinkageMap', 'portage.util._async.SchedulerInterface:SchedulerInterface', @@ -3508,6 +3509,8 @@ class dblink(object): This function does the following: + calls get_ro_checker to retrieve a function for checking whether Portage + will write to a read-only filesystem, then runs it against the directory list 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(os.path.join("/", relative_path)) + for fname in files: try: fname = _unicode_decode(fname, @@ -3829,6 +3836,31 @@ class dblink(object): for other in others_in_slot]) prepare_build_dirs(settings=self.settings, cleanup=cleanup) + # Check for read-only filesystems + ro_checker = get_ro_checker() + rofilesystems = ro_checker(mydirlist) + + if rofilesystems: + msg = _("One or more files installed to this package are " + "set to be installed to read-only filesystems. " + "Please mount the following filesystems as 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: diff --git a/pym/portage/util/rochecker.py b/pym/portage/util/rochecker.py new file mode 100644 index 0000000..2bbfe9d --- /dev/null +++ b/pym/portage/util/rochecker.py @@ -0,0 +1,87 @@ +#-*- coding:utf-8 -*- +# Copyright 2014 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +""" +Methods to check whether Portage is going to write to read-only filesystems. +Since the methods are not portable across different OSes, each OS needs its +own method. To expand RO checking for different OSes, add a method which +accepts a list of directories and returns a list of mounts which need to be +remounted RW, then add "elif ostype == (the ostype value for your OS)" to +get_ro_checker(). +""" +from __future__ import unicode_literals + +import io +import logging +import re + +from portage import _encodings +from portage.util import writemsg_level +from portage.localization import _ +from portage.data import ostype + + +def get_ro_checker(): + """ + Uses the system type to find an appropriate method for testing whether Portage + is going to write to any read-only filesystems + + @return: + 1. A method for testing for RO filesystems appropriate to the current system + """ + return _CHECKERS.get(ostype, empty_ro_checker) + + +def linux_ro_checker(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 io.open("/proc/mounts", mode='r', encoding=_encodings['content'], + errors='replace') as f: + roregex = re.compile(r'(\A|,)ro(\Z|,)') + for line in f: + 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_level(_("!!! /proc/mounts cannot be read"), + level=logging.WARNING, noiselevel=-1) + return [] + + if not ro_filesystems: + return ro_filesystems + + for directory in dir_list: + for filesystem in ro_filesystems: + if filesystem == directory: + ro_filesystems_written.add(filesystem) + + return ro_filesystems_written + + +def empty_ro_checker(dir_list): + """ + Always returns [], this is the fallback function if the system does not have + an ro_checker method defined + """ + return [] + + +# A map from ostype output to the appropriate function to return in get_ro_checker +_CHECKERS = { + "Linux": linux_ro_checker, +} -- 1.8.5.1