public inbox for gentoo-commits@lists.gentoo.org
 help / color / mirror / Atom feed
* [gentoo-commits] proj/portage:master commit in: pym/portage/, bin/
@ 2014-11-03  4:42 Zac Medico
  0 siblings, 0 replies; 3+ messages in thread
From: Zac Medico @ 2014-11-03  4:42 UTC (permalink / raw
  To: gentoo-commits

commit:     f17448317166bfac42dc279b8795cd581c189582
Author:     Zac Medico <zmedico <AT> gentoo <DOT> org>
AuthorDate: Sun Oct 26 09:49:02 2014 +0000
Commit:     Zac Medico <zmedico <AT> gentoo <DOT> org>
CommitDate: Sun Nov  2 23:19:46 2014 +0000
URL:        http://sources.gentoo.org/gitweb/?p=proj/portage.git;a=commit;h=f1744831

dispatch-conf: symlink support for bug #485598

This includes numerous logic adjustments that are needed to support
protected symlinks. The new diff_mixed function is used for diffs
between arbitrary file types. For example, a diff between two symlinks
looks like this:

-SYM: /foo/bar -> baz
+SYM: /foo/bar -> blah

X-Gentoo-Bug: 485598
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=485598

---
 bin/dispatch-conf            |  45 ++++++++-----
 pym/portage/dispatch_conf.py | 157 +++++++++++++++++++++++++++++++++++++------
 2 files changed, 164 insertions(+), 38 deletions(-)

diff --git a/bin/dispatch-conf b/bin/dispatch-conf
index 6d2ae94..8058d6f 100755
--- a/bin/dispatch-conf
+++ b/bin/dispatch-conf
@@ -13,17 +13,23 @@
 
 from __future__ import print_function
 
+import atexit
+import io
+import re
+import shutil
+import sys
+
 from stat import ST_GID, ST_MODE, ST_UID
 from random import random
-import atexit, re, shutil, stat, sys
+
 from os import path as osp
 if osp.isfile(osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), ".portage_not_installed")):
 	sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym"))
 import portage
 portage._internal_caller = True
 from portage import os
-from portage import _unicode_decode
-from portage.dispatch_conf import diffstatusoutput
+from portage import _encodings, _unicode_decode
+from portage.dispatch_conf import diffstatusoutput, diff_mixed_wrapper
 from portage.process import find_binary, spawn
 
 FIND_EXTANT_CONFIGS  = "find '%s' %s -name '._cfg????_%s' ! -name '.*~' ! -iname '.*.bak' -print"
@@ -72,6 +78,8 @@ def cmd_var_is_valid(cmd):
 
     return find_binary(cmd[0]) is not None
 
+diff = diff_mixed_wrapper(diffstatusoutput, DIFF_CONTENTS)
+
 class dispatch:
     options = {}
 
@@ -89,8 +97,6 @@ class dispatch:
                or not os.path.exists(self.options["log-file"]):
                 open(self.options["log-file"], 'w').close() # Truncate it
                 os.chmod(self.options["log-file"], 0o600)
-        else:
-            self.options["log-file"] = "/dev/null"
 
         pager = self.options.get("pager")
         if pager is None or not cmd_var_is_valid(pager):
@@ -148,9 +154,6 @@ class dispatch:
             portage.util.shlex_split(
             portage.settings.get('CONFIG_PROTECT_MASK', '')))
 
-        def diff(file1, file2):
-            return diffstatusoutput(DIFF_CONTENTS, file1, file2)
-
         #
         # Remove new configs identical to current
         #                  and
@@ -166,7 +169,7 @@ class dispatch:
                 mrgfail = portage.dispatch_conf.rcs_archive(archive, conf['current'], conf['new'], mrgconf)
             else:
                 mrgfail = portage.dispatch_conf.file_archive(archive, conf['current'], conf['new'], mrgconf)
-            if os.path.exists(archive + '.dist'):
+            if os.path.lexists(archive + '.dist'):
                 unmodified = len(diff(conf['current'], archive + '.dist')[1]) == 0
             else:
                 unmodified = 0
@@ -181,7 +184,7 @@ class dispatch:
 
             if newconf == mrgconf and \
                 self.options.get('ignore-previously-merged') != 'yes' and \
-                os.path.exists(archive+'.dist') and \
+                os.path.lexists(archive+'.dist') and \
                 len(diff(archive+'.dist', conf['new'])[1]) == 0:
                 # The current update is identical to the archived .dist
                 # version that has previously been merged.
@@ -254,6 +257,13 @@ class dispatch:
 
         valid_input = "qhtnmlezu"
 
+        def diff_pager(file1, file2):
+            cmd = self.options['diff'] % (file1, file2)
+            cmd += pager
+            spawn_shell(cmd)
+
+        diff_pager = diff_mixed_wrapper(diff_pager)
+
         for conf in confs:
             count = count + 1
 
@@ -266,14 +276,10 @@ class dispatch:
             while 1:
                 clear_screen()
                 if show_new_diff:
-                    cmd = self.options['diff'] % (conf['new'], mrgconf)
-                    cmd += pager
-                    spawn_shell(cmd)
+                    diff_pager(conf['new'], mrgconf)
                     show_new_diff = 0
                 else:
-                    cmd = self.options['diff'] % (conf['current'], newconf)
-                    cmd += pager
-                    spawn_shell(cmd)
+                    diff_pager(conf['current'], newconf)
 
                 print()
                 print('>> (%i of %i) -- %s' % (count, len(confs), conf ['current']))
@@ -357,7 +363,12 @@ class dispatch:
     def replace (self, newconf, curconf):
         """Replace current config with the new/merged version.  Also logs
         the diff of what changed into the configured log file."""
-        os.system((DIFF_CONTENTS % (curconf, newconf)) + '>>' + self.options["log-file"])
+        if "log-file" in self.options:
+            status, output = diff(curconf, newconf)
+            with io.open(self.options["log-file"], mode="a",
+                encoding=_encodings["stdio"]) as f:
+                f.write(output + "\n")
+
         try:
             os.rename(newconf, curconf)
         except (IOError, os.error) as why:

diff --git a/pym/portage/dispatch_conf.py b/pym/portage/dispatch_conf.py
index 113d965..b27e68b 100644
--- a/pym/portage/dispatch_conf.py
+++ b/pym/portage/dispatch_conf.py
@@ -6,11 +6,19 @@
 # Library by Wayne Davison <gentoo@blorf.net>, derived from code
 # written by Jeremy Wohl (http://igmus.org)
 
-from __future__ import print_function
+from __future__ import print_function, unicode_literals
 
-import os, shutil, subprocess, sys
+import io
+import functools
+import os
+import shutil
+import stat
+import subprocess
+import sys
+import tempfile
 
 import portage
+from portage import _encodings
 from portage.env.loaders import KeyValuePairFileLoader
 from portage.localization import _
 from portage.util import shlex_split, varexpand
@@ -50,6 +58,66 @@ def diffstatusoutput(cmd, file1, file2):
 		output = output[:-1]
 	return (proc.wait(), output)
 
+def diff_mixed(func, file1, file2):
+	tempdir = None
+	try:
+		if os.path.islink(file1) and \
+			not os.path.islink(file2) and \
+			os.path.isfile(file1) and \
+			os.path.isfile(file2):
+			# If a regular file replaces a symlink to a regular
+			# file, then show the diff between the regular files
+			# (bug #330221).
+			diff_files = (file2, file2)
+		else:
+			files = [file1, file2]
+			diff_files = [file1, file2]
+			for i in range(len(diff_files)):
+				try:
+					st = os.lstat(diff_files[i])
+				except OSError:
+					st = None
+				if st is not None and stat.S_ISREG(st.st_mode):
+					continue
+
+				if tempdir is None:
+					tempdir = tempfile.mkdtemp()
+				diff_files[i] = os.path.join(tempdir, "%d" % i)
+				if st is None:
+					content = "/dev/null\n"
+				elif stat.S_ISLNK(st.st_mode):
+					link_dest = os.readlink(files[i])
+					content = "SYM: %s -> %s\n" % \
+						(file1, link_dest)
+				elif stat.S_ISDIR(st.st_mode):
+					content = "DIR: %s\n" % (file1,)
+				elif stat.S_ISFIFO(st.st_mode):
+					content = "FIF: %s\n" % (file1,)
+				else:
+					content = "DEV: %s\n" % (file1,)
+				with io.open(diff_files[i], mode='w',
+					encoding=_encodings['stdio']) as f:
+					f.write(content)
+
+		return func(diff_files[0], diff_files[1])
+
+	finally:
+		if tempdir is not None:
+			shutil.rmtree(tempdir)
+
+class diff_mixed_wrapper(object):
+
+	def __init__(self, f, *args):
+		self._func = f
+		self._args = args
+
+	def __call__(self, *args):
+		return diff_mixed(
+			functools.partial(self._func, *(self._args + args[:-2])),
+			*args[-2:])
+
+diffstatusoutput_mixed = diff_mixed_wrapper(diffstatusoutput)
+
 def read_config(mandatory_opts):
 	eprefix = portage.settings["EPREFIX"]
 	if portage._not_installed:
@@ -103,35 +171,57 @@ def rcs_archive(archive, curconf, newconf, mrgconf):
 	except OSError:
 		pass
 
-	if os.path.isfile(curconf):
+	try:
+		curconf_st = os.lstat(curconf)
+	except OSError:
+		curconf_st = None
+
+	if curconf_st is not None and \
+		(stat.S_ISREG(curconf_st.st_mode) or
+		stat.S_ISLNK(curconf_st.st_mode)):
 		try:
-			shutil.copy2(curconf, archive)
+			if stat.S_ISLNK(curconf_st.st_mode):
+				os.symlink(os.readlink(curconf), archive)
+			else:
+				shutil.copy2(curconf, archive)
 		except(IOError, os.error) as why:
 			print(_('dispatch-conf: Error copying %(curconf)s to %(archive)s: %(reason)s; fatal') % \
 				{"curconf": curconf, "archive": archive, "reason": str(why)}, file=sys.stderr)
 
-	if os.path.exists(archive + ',v'):
+	if os.path.lexists(archive + ',v'):
 		os.system(RCS_LOCK + ' ' + archive)
 	os.system(RCS_PUT + ' ' + archive)
 
 	ret = 0
-	if newconf != '':
+	mystat = None
+	if newconf:
+		try:
+			mystat = os.lstat(newconf)
+		except OSError:
+			pass
+
+	if mystat is not None and \
+		(stat.S_ISREG(mystat.st_mode) or
+		stat.S_ISLNK(mystat.st_mode)):
 		os.system(RCS_GET + ' -r' + RCS_BRANCH + ' ' + archive)
-		has_branch = os.path.exists(archive)
+		has_branch = os.path.lexists(archive)
 		if has_branch:
 			os.rename(archive, archive + '.dist')
 
 		try:
-			shutil.copy2(newconf, archive)
+			if stat.S_ISLNK(mystat.st_mode):
+				os.symlink(os.readlink(newconf), archive)
+			else:
+				shutil.copy2(newconf, archive)
 		except(IOError, os.error) as why:
 			print(_('dispatch-conf: Error copying %(newconf)s to %(archive)s: %(reason)s; fatal') % \
 				{"newconf": newconf, "archive": archive, "reason": str(why)}, file=sys.stderr)
 
 		if has_branch:
-			if mrgconf != '':
+			if mrgconf and os.path.isfile(archive) and \
+				os.path.isfile(mrgconf):
 				# This puts the results of the merge into mrgconf.
 				ret = os.system(RCS_MERGE % (archive, mrgconf))
-				mystat = os.lstat(newconf)
 				os.chmod(mrgconf, mystat.st_mode)
 				os.chown(mrgconf, mystat.st_uid, mystat.st_gid)
 		os.rename(archive, archive + '.dist.new')
@@ -153,10 +243,11 @@ def file_archive(archive, curconf, newconf, mrgconf):
 		pass
 
 	# Archive the current config file if it isn't already saved
-	if (os.path.exists(archive) and
-		len(diffstatusoutput("diff -aq '%s' '%s'", curconf, archive)[1]) != 0):
+	if (os.path.lexists(archive) and
+		len(diffstatusoutput_mixed(
+		"diff -aq '%s' '%s'", curconf, archive)[1]) != 0):
 		suf = 1
-		while suf < 9 and os.path.exists(archive + '.' + str(suf)):
+		while suf < 9 and os.path.lexists(archive + '.' + str(suf)):
 			suf += 1
 
 		while suf > 1:
@@ -165,26 +256,50 @@ def file_archive(archive, curconf, newconf, mrgconf):
 
 		os.rename(archive, archive + '.1')
 
-	if os.path.isfile(curconf):
+	try:
+		curconf_st = os.lstat(curconf)
+	except OSError:
+		curconf_st = None
+
+	if curconf_st is not None and \
+		(stat.S_ISREG(curconf_st.st_mode) or
+		stat.S_ISLNK(curconf_st.st_mode)):
 		try:
-			shutil.copy2(curconf, archive)
+			if stat.S_ISLNK(curconf_st.st_mode):
+				os.symlink(os.readlink(curconf), archive)
+			else:
+				shutil.copy2(curconf, archive)
 		except(IOError, os.error) as why:
 			print(_('dispatch-conf: Error copying %(curconf)s to %(archive)s: %(reason)s; fatal') % \
 				{"curconf": curconf, "archive": archive, "reason": str(why)}, file=sys.stderr)
 
-	if newconf != '':
+	mystat = None
+	if newconf:
+		try:
+			mystat = os.lstat(newconf)
+		except OSError:
+			pass
+
+	if mystat is not None and \
+		(stat.S_ISREG(mystat.st_mode) or
+		stat.S_ISLNK(mystat.st_mode)):
 		# Save off new config file in the archive dir with .dist.new suffix
+		newconf_archive = archive + '.dist.new'
 		try:
-			shutil.copy2(newconf, archive + '.dist.new')
+			if stat.S_ISLNK(mystat.st_mode):
+				os.symlink(os.readlink(newconf), newconf_archive)
+			else:
+				shutil.copy2(newconf, newconf_archive)
 		except(IOError, os.error) as why:
 			print(_('dispatch-conf: Error copying %(newconf)s to %(archive)s: %(reason)s; fatal') % \
 				{"newconf": newconf, "archive": archive + '.dist.new', "reason": str(why)}, file=sys.stderr)
 
 		ret = 0
-		if mrgconf != '' and os.path.exists(archive + '.dist'):
+		if mrgconf and os.path.isfile(curconf) and \
+			os.path.isfile(newconf) and \
+			os.path.isfile(archive + '.dist'):
 			# This puts the results of the merge into mrgconf.
 			ret = os.system(DIFF3_MERGE % (curconf, archive + '.dist', newconf, mrgconf))
-			mystat = os.lstat(newconf)
 			os.chmod(mrgconf, mystat.st_mode)
 			os.chown(mrgconf, mystat.st_uid, mystat.st_gid)
 
@@ -195,7 +310,7 @@ def rcs_archive_post_process(archive):
 	"""Check in the archive file with the .dist.new suffix on the branch
 	and remove the one with the .dist suffix."""
 	os.rename(archive + '.dist.new', archive)
-	if os.path.exists(archive + '.dist'):
+	if os.path.lexists(archive + '.dist'):
 		# Commit the last-distributed version onto the branch.
 		os.system(RCS_LOCK + RCS_BRANCH + ' ' + archive)
 		os.system(RCS_PUT + ' -r' + RCS_BRANCH + ' ' + archive)
@@ -207,5 +322,5 @@ def rcs_archive_post_process(archive):
 
 def file_archive_post_process(archive):
 	"""Rename the archive file with the .dist.new suffix to a .dist suffix"""
-	if os.path.exists(archive + '.dist.new'):
+	if os.path.lexists(archive + '.dist.new'):
 		os.rename(archive + '.dist.new', archive + '.dist')


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [gentoo-commits] proj/portage:master commit in: pym/portage/, bin/
@ 2015-04-02 19:15 Zac Medico
  0 siblings, 0 replies; 3+ messages in thread
From: Zac Medico @ 2015-04-02 19:15 UTC (permalink / raw
  To: gentoo-commits

commit:     f9a2d7025e22f5e1711f39c4740a020f6f0d2c8f
Author:     Zac Medico <zmedico <AT> gentoo <DOT> org>
AuthorDate: Wed Apr  1 22:27:42 2015 +0000
Commit:     Zac Medico <zmedico <AT> gentoo <DOT> org>
CommitDate: Wed Apr  1 23:14:52 2015 +0000
URL:        https://gitweb.gentoo.org/proj/portage.git/commit/?id=f9a2d702

dispatch-conf: fix unicode handling (bug 545270)

This avoids UnicodeDecodeError problems by using UTF-8 encoding
regardless of the locale.

X-Gentoo-Bug: 545270
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=545270

 bin/dispatch-conf            | 9 +++++----
 pym/portage/dispatch_conf.py | 4 +---
 2 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/bin/dispatch-conf b/bin/dispatch-conf
index b679910..678a66d 100755
--- a/bin/dispatch-conf
+++ b/bin/dispatch-conf
@@ -11,12 +11,11 @@
 #  dialog menus
 #
 
-from __future__ import print_function
+from __future__ import print_function, unicode_literals
 
 import atexit
 import io
 import re
-import shutil
 import sys
 
 from stat import ST_GID, ST_MODE, ST_UID
@@ -27,7 +26,7 @@ if osp.isfile(osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), ".porta
 	sys.path.insert(0, osp.join(osp.dirname(osp.dirname(osp.realpath(__file__))), "pym"))
 import portage
 portage._internal_caller = True
-from portage import os
+from portage import os, shutil
 from portage import _encodings, _unicode_decode
 from portage.dispatch_conf import diffstatusoutput, diff_mixed_wrapper
 from portage.process import find_binary, spawn
@@ -403,7 +402,9 @@ class dispatch:
         newconfigs.sort ()
 
         for nconf in newconfigs:
-            nconf = nconf.rstrip ()
+            # Use strict mode here, because we want to know if it fails,
+            # and portage only merges files with valid UTF-8 encoding.
+            nconf = _unicode_decode(nconf, errors='strict').rstrip()
             conf  = re.sub (r'\._cfg\d+_', '', nconf)
             dirname   = os.path.dirname(nconf)
             conf_map  = {

diff --git a/pym/portage/dispatch_conf.py b/pym/portage/dispatch_conf.py
index 790eacb..98939fd 100644
--- a/pym/portage/dispatch_conf.py
+++ b/pym/portage/dispatch_conf.py
@@ -10,15 +10,13 @@ from __future__ import print_function, unicode_literals
 
 import io
 import functools
-import os
-import shutil
 import stat
 import subprocess
 import sys
 import tempfile
 
 import portage
-from portage import _encodings
+from portage import _encodings, os, shutil
 from portage.env.loaders import KeyValuePairFileLoader
 from portage.localization import _
 from portage.util import shlex_split, varexpand


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [gentoo-commits] proj/portage:master commit in: pym/portage/, bin/
@ 2018-02-22 17:32 Zac Medico
  0 siblings, 0 replies; 3+ messages in thread
From: Zac Medico @ 2018-02-22 17:32 UTC (permalink / raw
  To: gentoo-commits

commit:     c01fdd27473a76d1c8b6edb1b9dfb2c29645b1c2
Author:     Zac Medico <zmedico <AT> gentoo <DOT> org>
AuthorDate: Thu Feb 22 02:44:06 2018 +0000
Commit:     Zac Medico <zmedico <AT> gentoo <DOT> org>
CommitDate: Thu Feb 22 17:30:27 2018 +0000
URL:        https://gitweb.gentoo.org/proj/portage.git/commit/?id=c01fdd27

emerge/ebuild: sanitize file descriptors on startup

In order to ensure that any unintentionally inherited file descriptors
will not be inherited by child processes, set the inheritable flag to
False on startup, except for those corresponding to stdin, stdout, and
stderr. This mitigates potential problems that might result from
making the portage.process.spawn close_fds parameter default to False
for versions of python with PEP 446 support.

Bug: https://bugs.gentoo.org/648432

 bin/ebuild             |  2 ++
 bin/emerge             |  1 +
 pym/portage/process.py | 24 ++++++++++++++++++++++++
 3 files changed, 27 insertions(+)

diff --git a/bin/ebuild b/bin/ebuild
index bda746f78..b1ef0573b 100755
--- a/bin/ebuild
+++ b/bin/ebuild
@@ -58,6 +58,8 @@ import portage.util
 from _emerge.Package import Package
 from _emerge.RootConfig import RootConfig
 
+portage.process.sanitize_fds()
+
 description = "See the ebuild(1) man page for more info"
 usage = "Usage: ebuild <ebuild file> <command> [command] ..."
 parser = argparse.ArgumentParser(description=description, usage=usage)

diff --git a/bin/emerge b/bin/emerge
index 43cfdcddb..5f08861e5 100755
--- a/bin/emerge
+++ b/bin/emerge
@@ -46,6 +46,7 @@ try:
 	if __name__ == "__main__":
 		from portage.exception import IsADirectory, ParseError, \
 				PermissionDenied
+		portage.process.sanitize_fds()
 		try:
 			retval = emerge_main()
 		except PermissionDenied as e:

diff --git a/pym/portage/process.py b/pym/portage/process.py
index 4d96f156e..2af783e22 100644
--- a/pym/portage/process.py
+++ b/pym/portage/process.py
@@ -91,6 +91,30 @@ sandbox_capable = (os.path.isfile(SANDBOX_BINARY) and
 fakeroot_capable = (os.path.isfile(FAKEROOT_BINARY) and
                     os.access(FAKEROOT_BINARY, os.X_OK))
 
+
+def sanitize_fds():
+	"""
+	Set the inheritable flag to False for all open file descriptors,
+	except for those corresponding to stdin, stdout, and stderr. This
+	ensures that any unintentionally inherited file descriptors will
+	not be inherited by child processes.
+	"""
+	if _set_inheritable is not None:
+
+		whitelist = frozenset([
+			sys.__stdin__.fileno(),
+			sys.__stdout__.fileno(),
+			sys.__stderr__.fileno(),
+		])
+
+		for fd in get_open_fds():
+			if fd not in whitelist:
+				try:
+					_set_inheritable(fd, False)
+				except OSError:
+					pass
+
+
 def spawn_bash(mycommand, debug=False, opt_name=None, **keywords):
 	"""
 	Spawns a bash shell running a specific commands


^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2018-02-22 17:32 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-04-02 19:15 [gentoo-commits] proj/portage:master commit in: pym/portage/, bin/ Zac Medico
  -- strict thread matches above, loose matches on Subject: below --
2018-02-22 17:32 Zac Medico
2014-11-03  4:42 Zac Medico

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox