From 037759bb4f57b0cb9b0daf6ffed43f90b62b8cb4 Mon Sep 17 00:00:00 2001
From: Denys Vlasenko <vda.linux@googlemail.com>
Date: Sat, 4 Aug 2018 18:15:19 +0200
Subject: tar: handle the case when opened created tarball happens to have fd#0

Reproducer:
    exec 0>&-
    exec 1>&-
    tar czf z.tar.gz FILE

function                                             old     new   delta
vfork_compressor                                     229     257     +28

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
---
 archival/tar.c | 24 ++++++++++++++++++------
 1 file changed, 18 insertions(+), 6 deletions(-)

diff --git archival/tar.c archival/tar.c
index 9239d8e..120c77f 100644
--- archival/tar.c
+++ archival/tar.c
@@ -631,6 +631,7 @@ static void NOINLINE vfork_compressor(int tar_fd, const char *gzip)
 
 	if (gzipPid == 0) {
 		/* child */
+		int tfd;
 		/* NB: close _first_, then move fds! */
 		close(gzipDataPipe.wr);
 # if WAIT_FOR_CHILD
@@ -639,8 +640,23 @@ static void NOINLINE vfork_compressor(int tar_fd, const char *gzip)
 		 * parent waits for this close to happen */
 		fcntl(gzipStatusPipe.wr, F_SETFD, FD_CLOEXEC);
 # endif
+		/* copy it: parent's tar_fd variable must not change */
+		tfd = tar_fd;
+		if (tfd == 0) {
+			/* Output tar fd may be zero.
+			 * xmove_fd(gzipDataPipe.rd, 0) would destroy it.
+			 * Reproducer:
+			 *  exec 0>&-
+			 *  exec 1>&-
+			 *  tar czf Z.tar.gz FILE
+			 * Swapping move_fd's order wouldn't work:
+			 * gzipDataPipe.rd is 1 and _it_ would be destroyed.
+			 */
+			xmove_fd(tfd, 3);
+			tfd = 3;
+		}
 		xmove_fd(gzipDataPipe.rd, 0);
-		xmove_fd(tar_fd, 1);
+		xmove_fd(tfd, 1);
 		/* exec gzip/bzip2 program/applet */
 		BB_EXECLP(gzip, gzip, "-f", (char *)0);
 		vfork_exec_errno = errno;
