From f807bb54317db5f8073f8904897cf4b9d87bf2cd Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Sun, 1 Jul 2012 00:53:52 -0700 Subject: Support FEATURES={downgrade,unmerge}-backup This will fix bug #156282 and bug #424275. --- bin/quickpkg | 15 ++++--- man/make.conf.5 | 13 ++++++- pym/portage/const.py | 4 +- pym/portage/dbapi/vartree.py | 93 +++++++++++++++++++++++++++++++++++++++----- 4 files changed, 108 insertions(+), 17 deletions(-) diff --git a/bin/quickpkg b/bin/quickpkg index d908c0346..a6bd4d4bd 100755 --- a/bin/quickpkg +++ b/bin/quickpkg @@ -1,5 +1,5 @@ #!/usr/bin/python -# Copyright 1999-2010 Gentoo Foundation +# Copyright 1999-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 from __future__ import print_function @@ -68,11 +68,14 @@ def quickpkg_atom(options, infos, arg, eout): bintree.prevent_collision(cpv) dblnk = vardb._dblink(cpv) have_lock = False - try: - dblnk.lockdb() - have_lock = True - except PermissionDenied: - pass + + if "__PORTAGE_INHERIT_VARDB_LOCK" not in settings: + try: + dblnk.lockdb() + have_lock = True + except PermissionDenied: + pass + try: if not dblnk.exists(): # unmerged by a concurrent process diff --git a/man/make.conf.5 b/man/make.conf.5 index 7d07344bd..876a8a330 100644 --- a/man/make.conf.5 +++ b/man/make.conf.5 @@ -1,4 +1,4 @@ -.TH "MAKE.CONF" "5" "May 2012" "Portage VERSION" "Portage" +.TH "MAKE.CONF" "5" "Jul 2012" "Portage VERSION" "Portage" .SH "NAME" make.conf \- custom settings for Portage .SH "SYNOPSIS" @@ -293,6 +293,12 @@ strangely configured Samba server (oplocks off, NFS re\-export). A tool /usr/lib/portage/bin/clean_locks exists to help handle lock issues when a problem arises (normally due to a crash or disconnect). .TP +.B downgrade\-backup +When a package is downgraded to a lower version, call \fBquickpkg\fR(1) +in order to create a backup of the installed version before it is +unmerged (if a binary package of the same version does not already +exist). Also see the related \fIunmerge\-backup\fR feature. +.TP .B ebuild\-locks Use locks to ensure that unsandboxed ebuild phases never execute concurrently. Also see \fIparallel\-install\fR. @@ -514,6 +520,11 @@ continue to execute the remaining phases as if the failure had not occurred. Note that the test phase for a specific package may be disabled by masking the "test" \fBUSE\fR flag in \fBpackage.use.mask\fR (see \fBportage\fR(5)). .TP +.B unmerge\-backup +Call \fBquickpkg\fR(1) to create a backup of each package before it is +unmerged (if a binary package of the same version does not already exist). +Also see the related \fIdowngrade\-backup\fR feature. +.TP .B unmerge\-logs Keep logs from successful unmerge phases. This is relevant only when \fBPORT_LOGDIR\fR is set. diff --git a/pym/portage/const.py b/pym/portage/const.py index 3607df0e0..4a077102e 100644 --- a/pym/portage/const.py +++ b/pym/portage/const.py @@ -90,7 +90,8 @@ SUPPORTED_FEATURES = frozenset([ "ccache", "chflags", "clean-logs", "collision-protect", "compress-build-logs", "compressdebug", "config-protect-if-modified", - "digest", "distcc", "distcc-pump", "distlocks", "ebuild-locks", "fakeroot", + "digest", "distcc", "distcc-pump", "distlocks", + "downgrade-backup", "ebuild-locks", "fakeroot", "fail-clean", "force-mirror", "force-prefix", "getbinpkg", "installsources", "keeptemp", "keepwork", "fixlafiles", "lmirror", "metadata-transfer", "mirror", "multilib-strict", "news", @@ -103,6 +104,7 @@ SUPPORTED_FEATURES = frozenset([ "sign", "skiprocheck", "split-elog", "split-log", "splitdebug", "strict", "stricter", "suidctl", "test", "test-fail-continue", "unknown-features-filter", "unknown-features-warn", + "unmerge-backup", "unmerge-logs", "unmerge-orphans", "userfetch", "userpriv", "usersandbox", "usersync", "webrsync-gpg", "xattr"]) diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py index 34098eab1..0d7327ad4 100644 --- a/pym/portage/dbapi/vartree.py +++ b/pym/portage/dbapi/vartree.py @@ -21,6 +21,7 @@ portage.proxy.lazyimport.lazyimport(globals(), 'portage.package.ebuild.doebuild:doebuild_environment,' + \ '_merge_unicode_error', '_spawn_phase', 'portage.package.ebuild.prepare_build_dirs:prepare_build_dirs', + 'portage.package.ebuild._ipc.QueryCommand:QueryCommand', 'portage.update:fixdbentries', 'portage.util:apply_secpass_permissions,ConfigProtect,ensure_dirs,' + \ 'writemsg,writemsg_level,write_atomic,atomic_ofstream,writedict,' + \ @@ -62,6 +63,7 @@ from _emerge.EbuildPhase import EbuildPhase from _emerge.emergelog import emergelog from _emerge.PollScheduler import PollScheduler from _emerge.MiscFunctionsProcess import MiscFunctionsProcess +from _emerge.SpawnProcess import SpawnProcess import errno import fnmatch @@ -1777,6 +1779,11 @@ class dblink(object): showMessage = self._display_merge if self.vartree.dbapi._categories is not None: self.vartree.dbapi._categories = None + + # When others_in_slot is not None, the backup has already been + # handled by the caller. + caller_handles_backup = others_in_slot is not None + # When others_in_slot is supplied, the security check has already been # done for this slot, so it shouldn't be repeated until the next # replacement or unmerge operation. @@ -1842,6 +1849,11 @@ class dblink(object): prepare_build_dirs(settings=self.settings, cleanup=True) log_path = self.settings.get("PORTAGE_LOG_FILE") + if not caller_handles_backup: + retval = self._pre_unmerge_backup(background) + if retval != os.EX_OK: + return retval + # Log the error after PORTAGE_LOG_FILE is initialized # by prepare_build_dirs above. if eapi_unsupported: @@ -3833,6 +3845,20 @@ class dblink(object): self.delete() ensure_dirs(self.dbtmpdir) + downgrade = False + if self._installed_instance is not None and \ + vercmp(self.mycpv.version, + self._installed_instance.mycpv.version) < 0: + downgrade = True + + if self._installed_instance is not None: + rval = self._pre_merge_backup(self._installed_instance, downgrade) + if rval != os.EX_OK: + showMessage(_("!!! FAILED preinst: ") + + "quickpkg: %s\n" % rval, + level=logging.ERROR, noiselevel=-1) + return rval + # run preinst script showMessage(_(">>> Merging %(cpv)s to %(destroot)s\n") % \ {"cpv":self.mycpv, "destroot":destroot}) @@ -3866,20 +3892,15 @@ class dblink(object): #if we have a file containing previously-merged config file md5sums, grab it. self.vartree.dbapi._fs_lock() try: + # Always behave like --noconfmem is enabled for downgrades + # so that people who don't know about this option are less + # likely to get confused when doing upgrade/downgrade cycles. cfgfiledict = grabdict(self.vartree.dbapi._conf_mem_file) - if "NOCONFMEM" in self.settings: + if "NOCONFMEM" in self.settings or downgrade: cfgfiledict["IGNORE"]=1 else: cfgfiledict["IGNORE"]=0 - # Always behave like --noconfmem is enabled for downgrades - # so that people who don't know about this option are less - # likely to get confused when doing upgrade/downgrade cycles. - for other in others_in_slot: - if vercmp(self.mycpv.version, other.mycpv.version) < 0: - cfgfiledict["IGNORE"] = 1 - break - rval = self._merge_contents(srcroot, destroot, cfgfiledict) if rval != os.EX_OK: return rval @@ -4682,6 +4703,60 @@ class dblink(object): "Is this a regular package (does it have a CATEGORY file? A dblink can be virtual *and* regular)" return os.path.exists(os.path.join(self.dbdir, "CATEGORY")) + def _pre_merge_backup(self, backup_dblink, downgrade): + + if ("unmerge-backup" in self.settings.features or + (downgrade and "downgrade-backup" in self.settings.features)): + return self._quickpkg_dblink(backup_dblink, False, None) + + return os.EX_OK + + def _pre_unmerge_backup(self, background): + + if "unmerge-backup" in self.settings.features : + logfile = None + if self.settings.get("PORTAGE_BACKGROUND") != "subprocess": + logfile = self.settings.get("PORTAGE_LOG_FILE") + return self._quickpkg_dblink(self, background, logfile) + + return os.EX_OK + + def _quickpkg_dblink(self, backup_dblink, background, logfile): + + trees = QueryCommand.get_db()[self.settings["EROOT"]] + bintree = trees["bintree"] + binpkg_path = bintree.getname(backup_dblink.mycpv) + if os.path.exists(binpkg_path) and \ + backup_dblink.mycpv not in bintree.invalids: + return os.EX_OK + + self.lockdb() + try: + + if not backup_dblink.exists(): + # It got unmerged by a concurrent process. + return os.EX_OK + + # Call quickpkg for support of QUICKPKG_DEFAULT_OPTS and stuff. + quickpkg_binary = os.path.join(self.settings["PORTAGE_BIN_PATH"], + "quickpkg") + + # Let quickpkg inherit the global vartree config's env. + env = dict(self.vartree.settings.items()) + env["__PORTAGE_INHERIT_VARDB_LOCK"] = "1" + + quickpkg_proc = SpawnProcess( + args=[portage._python_interpreter, quickpkg_binary, + "=%s" % (backup_dblink.mycpv,)], + background=background, env=env, + scheduler=self._scheduler, logfile=logfile) + quickpkg_proc.start() + + return quickpkg_proc.wait() + + finally: + self.unlockdb() + def merge(mycat, mypkg, pkgloc, infloc, myroot=None, settings=None, myebuild=None, mytree=None, mydbapi=None, vartree=None, prev_mtimes=None, blockers=None, -- cgit v1.2.3-1-g7c22