Since variables like A and AA can contain extremely large values which
may trigger E2BIG errors during attempts to execute subprocesses, delay
export until the last moment, and unexport when appropriate.

Bug: https://bugs.gentoo.org/720180
Signed-off-by: Zac Medico <zmed...@gentoo.org>
---
 bin/eapi.sh                                   |  9 +++++
 bin/isolated-functions.sh                     | 34 ++++++++++++++++
 bin/phase-functions.sh                        | 13 +++++++
 .../ebuild/_config/special_env_vars.py        |  7 +++-
 lib/portage/package/ebuild/config.py          | 39 ++++++++++++++-----
 5 files changed, 91 insertions(+), 11 deletions(-)

diff --git a/bin/eapi.sh b/bin/eapi.sh
index 29dfb008c..f56468e4a 100644
--- a/bin/eapi.sh
+++ b/bin/eapi.sh
@@ -26,6 +26,15 @@ ___eapi_has_S_WORKDIR_fallback() {
 
 # VARIABLES
 
+___eapi_exports_A() {
+       # https://bugs.gentoo.org/721088
+       true
+}
+
+___eapi_exports_AA() {
+       [[ ${1-${EAPI-0}} =~ ^(0|1|2|3)$ ]]
+}
+
 ___eapi_has_prefix_variables() {
        [[ ! ${1-${EAPI-0}} =~ ^(0|1|2)$ || " ${FEATURES} " == *" force-prefix 
"* ]]
 }
diff --git a/bin/isolated-functions.sh b/bin/isolated-functions.sh
index fde684013..973450d86 100644
--- a/bin/isolated-functions.sh
+++ b/bin/isolated-functions.sh
@@ -107,6 +107,39 @@ __bashpid() {
        sh -c 'echo ${PPID}'
 }
 
+# @FUNCTION: ___eapi_vars_export
+# @DESCRIPTION:
+# Export variables for the current EAPI. Calls to this function should
+# be delayed until the last moment, since exporting these variables
+# may trigger E2BIG errors suring attempts to execute subprocesses.
+___eapi_vars_export() {
+       source "${T}/environment.unexported" || die
+
+       if ___eapi_exports_A; then
+               export A
+       fi
+
+       if ___eapi_exports_AA; then
+               export AA
+       fi
+}
+
+# @FUNCTION: ___eapi_vars_unexport
+# @DESCRIPTION:
+# Unexport variables that were exported for the current EAPI. This
+# function should be called after an ebuild phase, in order to unexport
+# variables that may trigger E2BIG errors during attempts to execute
+# subprocesses.
+___eapi_vars_unexport() {
+       if ___eapi_exports_A; then
+               export -n A
+       fi
+
+       if ___eapi_exports_AA; then
+               export -n AA
+       fi
+}
+
 __helpers_die() {
        if ___eapi_helpers_can_die && [[ ${PORTAGE_NONFATAL} != 1 ]]; then
                die "$@"
@@ -122,6 +155,7 @@ die() {
 
        set +x # tracing only produces useless noise here
        local IFS=$' \t\n'
+       ___eapi_vars_unexport
 
        if ___eapi_die_can_respect_nonfatal && [[ $1 == -n ]]; then
                shift
diff --git a/bin/phase-functions.sh b/bin/phase-functions.sh
index 90e622e75..df2c0d8de 100644
--- a/bin/phase-functions.sh
+++ b/bin/phase-functions.sh
@@ -146,6 +146,7 @@ __filter_readonly_variables() {
                fi
        fi
 
+       ___eapi_vars_unexport
        "${PORTAGE_PYTHON:-/usr/bin/python}" 
"${PORTAGE_BIN_PATH}"/filter-bash-environment.py "${filtered_vars}" || die 
"filter-bash-environment.py failed"
 }
 
@@ -212,6 +213,7 @@ __ebuild_phase() {
 
 __ebuild_phase_with_hooks() {
        local x phase_name=${1}
+       ___eapi_vars_export
        for x in {pre_,,post_}${phase_name} ; do
                __ebuild_phase ${x}
        done
@@ -223,6 +225,7 @@ __dyn_pretend() {
                __vecho ">>> Remove '$PORTAGE_BUILDDIR/.pretended' to force 
pretend."
                return 0
        fi
+       ___eapi_vars_export
        __ebuild_phase pre_pkg_pretend
        __ebuild_phase pkg_pretend
        >> "$PORTAGE_BUILDDIR/.pretended" || \
@@ -236,6 +239,7 @@ __dyn_setup() {
                __vecho ">>> Remove '$PORTAGE_BUILDDIR/.setuped' to force 
setup."
                return 0
        fi
+       ___eapi_vars_export
        __ebuild_phase pre_pkg_setup
        __ebuild_phase pkg_setup
        >> "$PORTAGE_BUILDDIR/.setuped" || \
@@ -252,6 +256,7 @@ __dyn_unpack() {
                install -m${PORTAGE_WORKDIR_MODE:-0700} -d "${WORKDIR}" || die 
"Failed to create dir '${WORKDIR}'"
        fi
        cd "${WORKDIR}" || die "Directory change failed: \`cd '${WORKDIR}'\`"
+       ___eapi_vars_export
        __ebuild_phase pre_src_unpack
        __vecho ">>> Unpacking source..."
        __ebuild_phase src_unpack
@@ -386,6 +391,7 @@ __dyn_prepare() {
 
        trap __abort_prepare SIGINT SIGQUIT
 
+       ___eapi_vars_export
        __ebuild_phase pre_src_prepare
        __vecho ">>> Preparing source in $PWD ..."
        __ebuild_phase src_prepare
@@ -423,6 +429,7 @@ __dyn_configure() {
 
        trap __abort_configure SIGINT SIGQUIT
 
+       ___eapi_vars_export
        __ebuild_phase pre_src_configure
 
        __vecho ">>> Configuring source in $PWD ..."
@@ -456,6 +463,7 @@ __dyn_compile() {
 
        trap __abort_compile SIGINT SIGQUIT
 
+       ___eapi_vars_export
        __ebuild_phase pre_src_compile
 
        __vecho ">>> Compiling source in $PWD ..."
@@ -500,6 +508,7 @@ __dyn_test() {
        else
                local save_sp=${SANDBOX_PREDICT}
                addpredict /
+               ___eapi_vars_export
                __ebuild_phase pre_src_test
 
                __vecho ">>> Test phase: ${CATEGORY}/${PF}"
@@ -553,6 +562,7 @@ __dyn_install() {
        eval "[[ -n \$QA_PRESTRIPPED_${ARCH/-/_} ]] && \
                export QA_PRESTRIPPED_${ARCH/-/_}"
 
+       ___eapi_vars_export
        __ebuild_phase pre_src_install
 
        if ___eapi_has_prefix_variables; then
@@ -695,6 +705,7 @@ __dyn_install() {
                --filter-path --filter-sandbox --allow-extra-vars > \
                "${PORTAGE_BUILDDIR}"/build-info/environment
        assert "__save_ebuild_env failed"
+       ___eapi_vars_unexport
        cd "${PORTAGE_BUILDDIR}"/build-info || die
 
        ${PORTAGE_BZIP2_COMMAND} -f9 environment
@@ -1087,11 +1098,13 @@ __ebuild_main() {
                __save_ebuild_env | __filter_readonly_variables \
                        --filter-features > "$T/environment"
                assert "__save_ebuild_env failed"
+               ___eapi_vars_unexport
                chgrp "${PORTAGE_GRPNAME:-portage}" "$T/environment"
                chmod g+w "$T/environment"
        fi
        [[ -n $PORTAGE_EBUILD_EXIT_FILE ]] && > "$PORTAGE_EBUILD_EXIT_FILE"
        if [[ -n $PORTAGE_IPC_DAEMON ]] ; then
+               ___eapi_vars_unexport
                [[ ! -s $SANDBOX_LOG ]]
                "$PORTAGE_BIN_PATH"/ebuild-ipc exit $?
        fi
diff --git a/lib/portage/package/ebuild/_config/special_env_vars.py 
b/lib/portage/package/ebuild/_config/special_env_vars.py
index 440dd00b2..92824e15f 100644
--- a/lib/portage/package/ebuild/_config/special_env_vars.py
+++ b/lib/portage/package/ebuild/_config/special_env_vars.py
@@ -89,7 +89,7 @@ environ_whitelist += [
 ]
 
 environ_whitelist += [
-       "A", "AA", "CATEGORY", "P", "PF", "PN", "PR", "PV", "PVR"
+       "CATEGORY", "P", "PF", "PN", "PR", "PV", "PVR"
 ]
 
 # misc variables inherited from the calling environment
@@ -124,6 +124,10 @@ environ_whitelist = frozenset(environ_whitelist)
 
 environ_whitelist_re = re.compile(r'^(CCACHE_|DISTCC_).*')
 
+environ_unexported = frozenset([
+       'A', 'AA',
+])
+
 # Filter selected variables in the config.environ() method so that
 # they don't needlessly propagate down into the ebuild environment.
 environ_filter = []
@@ -131,6 +135,7 @@ environ_filter = []
 # Exclude anything that could be extremely long here (like SRC_URI)
 # since that could cause execve() calls to fail with E2BIG errors. For
 # example, see bug #262647.
+environ_filter.extend(environ_unexported)
 environ_filter += [
        'DEPEND', 'RDEPEND', 'PDEPEND', 'SRC_URI',
 ]
diff --git a/lib/portage/package/ebuild/config.py 
b/lib/portage/package/ebuild/config.py
index 47c180c12..a386dc031 100644
--- a/lib/portage/package/ebuild/config.py
+++ b/lib/portage/package/ebuild/config.py
@@ -10,6 +10,7 @@ __all__ = [
 import copy
 from itertools import chain
 import grp
+import io
 import logging
 import platform
 import pwd
@@ -29,7 +30,7 @@ portage.proxy.lazyimport.lazyimport(globals(),
        'portage.util.locale:check_locale,split_LC_ALL',
 )
 from portage import bsd_chflags, \
-       load_mod, os, selinux, _unicode_decode
+       load_mod, os, selinux, _encodings, _unicode_decode
 from portage.const import CACHE_PATH, \
        DEPCACHE_PATH, INCREMENTALS, MAKE_CONF_FILE, \
        MODULES_FILE_PATH, PORTAGE_BASE_PATH, \
@@ -2755,14 +2756,36 @@ class config(object):
                eapi_attrs = _get_eapi_attrs(eapi)
                phase = self.get('EBUILD_PHASE')
                emerge_from = self.get('EMERGE_FROM')
+               temp_dir = None
                filter_calling_env = False
                if self.mycpv is not None and \
-                       not (emerge_from == 'ebuild' and phase == 'setup') and \
+                       'PORTAGE_BUILDDIR_LOCKED' in self and \
                        phase not in ('clean', 'cleanrm', 'depend', 'fetch'):
-                       temp_dir = self.get('T')
-                       if temp_dir is not None and \
-                               os.path.exists(os.path.join(temp_dir, 
'environment')):
-                               filter_calling_env = True
+                       temp_dir = self['T']
+                       # These variables will exported by ebuild.sh if 
appropriate
+                       # for the current EAPI, but export is delayed since 
large
+                       # values may trigger E2BIG errors during attempts to 
spawn
+                       # subprocesses.
+                       unexported = []
+                       for key in special_env_vars.environ_unexported:
+                               # Don't export AA for EAPIs that forbid it.
+                               if key == 'AA' and not eapi_exports_AA(eapi):
+                                       continue
+                               value = self.get(key)
+                               if value is not None:
+                                       unexported.append((key, value))
+
+                       # Write this file even if it's empty, so that ebuild.sh 
can
+                       # rely on its existence.
+                       with io.open(os.path.join(temp_dir, 
'environment.unexported'),
+                               mode='wt', encoding=_encodings['repo.content']) 
as f:
+                               for key, value in unexported:
+                                       f.write('%s="%s"\n' % (key, 
value.replace('"', '\\"')))
+
+               if temp_dir is not None and \
+                       not (emerge_from == 'ebuild' and phase == 'setup') and \
+                       os.path.exists(os.path.join(temp_dir, 'environment')):
+                       filter_calling_env = True
 
                environ_whitelist = self._environ_whitelist
                for x, myvalue in self.iteritems():
@@ -2805,10 +2828,6 @@ class config(object):
                # Filtered by IUSE and implicit IUSE.
                mydict["USE"] = self.get("PORTAGE_USE", "")
 
-               # Don't export AA to the ebuild environment in EAPIs that 
forbid it
-               if not eapi_exports_AA(eapi):
-                       mydict.pop("AA", None)
-
                if not eapi_exports_merge_type(eapi):
                        mydict.pop("MERGE_TYPE", None)
 
-- 
2.25.3


Reply via email to