tar: handle the case when opened created tarball happens to have fd#0
authorDenys Vlasenko <vda.linux@googlemail.com>
Sat, 4 Aug 2018 16:15:19 +0000 (18:15 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Sat, 4 Aug 2018 16:15:19 +0000 (18:15 +0200)
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

index 9239d8ee4067d7352dcec16ff5dc15a93aee9c39..120c77f3bea95776e8eb49dea5dab34dff8f2aaa 100644 (file)
@@ -611,6 +611,7 @@ static void NOINLINE vfork_compressor(int tar_fd, const char *gzip)
 
        if (xvfork() == 0) {
                /* child */
+               int tfd;
                /* NB: close _first_, then move fds! */
                close(data.wr);
 #  if WAIT_FOR_CHILD
@@ -619,8 +620,23 @@ static void NOINLINE vfork_compressor(int tar_fd, const char *gzip)
                 * parent waits for this close to happen */
                fcntl(status.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(data.rd, 0) would destroy it.
+                        * Reproducer:
+                        *  exec 0>&-
+                        *  exec 1>&-
+                        *  tar czf Z.tar.gz FILE
+                        * Swapping move_fd's order wouldn't work:
+                        * data.rd is 1 and _it_ would be destroyed.
+                        */
+                       xmove_fd(tfd, 3);
+                       tfd = 3;
+               }
                xmove_fd(data.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;
@@ -715,11 +731,7 @@ static NOINLINE int writeTarFile(
        return errorFlag;
 }
 
-#else /* !FEATURE_TAR_CREATE */
-
-# define writeTarFile(...) 0
-
-#endif
+#endif /* FEATURE_TAR_CREATE */
 
 #if ENABLE_FEATURE_TAR_FROM
 static llist_t *append_file_list_to_list(llist_t *list)