Handle SIGPIPE when unpack() extracts tar files through a pipe (bug #309001).
authorZac Medico <zmedico@gentoo.org>
Tue, 24 Aug 2010 00:57:48 +0000 (17:57 -0700)
committerZac Medico <zmedico@gentoo.org>
Tue, 24 Aug 2010 00:57:48 +0000 (17:57 -0700)
When checking ${PIPESTATUS[@]} for extraction of tar files in unpack(),
use a new assert_sigpipe_ok() function which behaves the same as the
existing assert() function except that it tolerates pipe writers being
killed by SIGPIPE.

bin/ebuild.sh
bin/isolated-functions.sh
pym/_emerge/BinpkgExtractorAsync.py

index 84a83fe40f4ffb28612e6edc23aa7a57af5ef45f..b2ac5b419174bc0296da87ce91900d396b309795 100755 (executable)
@@ -348,7 +348,7 @@ unpack() {
                _unpack_tar() {
                        if [ "${y}" == "tar" ]; then
                                $1 -dc "$srcdir$x" | tar xof -
-                               assert "$myfail"
+                               assert_sigpipe_ok "$myfail"
                        else
                                $1 -dc "${srcdir}${x}" > ${x%.*} || die "$myfail"
                        fi
@@ -364,7 +364,7 @@ unpack() {
                                ;;
                        tbz|tbz2)
                                bzip2 -dc "$srcdir$x" | tar xof -
-                               assert "$myfail"
+                               assert_sigpipe_ok "$myfail"
                                ;;
                        ZIP|zip|jar)
                                unzip -qo "${srcdir}${x}" || die "$myfail"
index 53312dba8c251419826e96c7babb9fb0e9631f03..a490f27c025f7748d58550de54891ae67184d1fa 100644 (file)
@@ -15,6 +15,32 @@ assert() {
        done
 }
 
+assert_sigpipe_ok() {
+       # When extracting a tar file like this:
+       #
+       #     bzip2 -dc foo.tar.bz2 | tar xof -
+       #
+       # For some tar files (see bug #309001), tar will
+       # close its stdin pipe when the decompressor still has
+       # remaining data to be written to its stdout pipe. This
+       # causes the decompressor to be killed by SIGPIPE. In
+       # this case, we want to ignore pipe writers killed by
+       # SIGPIPE, and trust the exit status of tar. We refer
+       # to the bash manual section "3.7.5 Exit Status"
+       # which says, "When a command terminates on a fatal
+       # signal whose number is N, Bash uses the value 128+N
+       # as the exit status."
+
+       local x pipestatus=${PIPESTATUS[*]}
+       for x in $pipestatus ; do
+               # Allow SIGPIPE through (128 + 13)
+               [[ $x -ne 0 && $x -ne 141 ]] && die "$@"
+       done
+
+       # Require normal success for the last process (tar).
+       [[ $x -eq 0 ]] || die "$@"
+}
+
 shopt -s extdebug
 
 # dump_trace([number of funcs on stack to skip],
index a4941fccf06ce470f302a034aaeb4d7dede59a5b..3a2654ec11617876744e063b7ad6b9ed49cba7e8 100644 (file)
@@ -12,10 +12,12 @@ class BinpkgExtractorAsync(SpawnProcess):
        _shell_binary = portage.const.BASH_BINARY
 
        def _start(self):
+               # SIGPIPE handling (status 141) should be compatible with
+               # assert_sigpipe_ok() that's used by the ebuild unpack() helper.
                self.args = [self._shell_binary, "-c",
                        ("bzip2 -dqc -- %s | tar -xp -C %s -f - ; " + \
                        "p=(${PIPESTATUS[@]}) ; " + \
-                       "if [ ${p[0]} != 0 ] ; then " + \
+                       "if [[ ${p[0]} != 0 && ${p[0]} != 141 ]] ; then " + \
                        "echo bzip2 failed with status ${p[0]} ; exit ${p[0]} ; fi ; " + \
                        "if [ ${p[1]} != 0 ] ; then " + \
                        "echo tar failed with status ${p[1]} ; exit ${p[1]} ; fi ; " + \