]> Dogcows Code - chaz/tar/commitdiff
Fail if archive comes from a terminal.
authorSergey Poznyakoff <gray@gnu.org.ua>
Thu, 20 Mar 2014 14:28:25 +0000 (16:28 +0200)
committerSergey Poznyakoff <gray@gnu.org.ua>
Thu, 20 Mar 2014 17:46:30 +0000 (19:46 +0200)
Based on patch from Pavel Raiskup <praiskup@redhat.com>.

* gnulib.modules: Add new modules.
* src/buffer.c (_open_archive): Refuse to read archive from a tty.
* tests/Makefile.am (TESTSUITE_AT): Add iotty.at
(check_PROGRAMS): New program ttyemu
* tests/testsuite.at: Include iotty.at
* tests/iotty.at: New file.
* tests/ttyemu.c: New file.

gnulib.modules
src/buffer.c
tests/Makefile.am
tests/iotty.at [new file with mode: 0644]
tests/testsuite.at
tests/ttyemu.c [new file with mode: 0644]

index 25e8ab378f8cc03fe1299618ba4051c426ce451e..3ba06b613f01e84640653844710b631cccb37d6b 100644 (file)
@@ -49,6 +49,7 @@ getpagesize
 gettext
 gettime
 gitlog-to-changelog
+grantpt
 hash
 human
 inttostr
@@ -64,6 +65,8 @@ modechange
 obstack
 openat
 parse-datetime
+posix_openpt
+ptsname
 priv-set
 progname
 quote
@@ -92,6 +95,7 @@ timespec
 unlinkat
 unlinkdir
 unlocked-io
+unlockpt
 utimensat
 version-etc-fsf
 xalloc
index 95e8a26de1e42a5b2b922300c57dbd3e22db25f0..248dd8804ce69e976b2187ba34bbc04b354b1fa9 100644 (file)
@@ -689,6 +689,16 @@ _open_archive (enum access_mode wanted_access)
   /* When updating the archive, we start with reading.  */
   access_mode = wanted_access == ACCESS_UPDATE ? ACCESS_READ : wanted_access;
 
+  /* Refuse to read archive from a tty.
+     Do not fail if the tar's output goes directly to tty because such
+     behavior would go against GNU Coding Standards:
+     http://lists.gnu.org/archive/html/bug-tar/2014-03/msg00042.html */
+  if (strcmp (archive_name_array[0], "-") == 0
+      && wanted_access == ACCESS_READ && isatty (STDIN_FILENO))
+    FATAL_ERROR ((0, 0,
+                  _("Refusing to read archive contents from terminal "
+                    "(missing -f option?)")));
+  
   read_full_records = read_full_records_option;
 
   records_read = 0;
@@ -731,7 +741,6 @@ _open_archive (enum access_mode wanted_access)
             enum compress_type type;
 
             archive = STDIN_FILENO;
-
             type = check_compressed_archive (&shortfile);
             if (type != ct_tar && type != ct_none)
               FATAL_ERROR ((0, 0,
index d4c936204ccb58947402228a9047f9c3ec114451..3896b7233776470e516bfd539fdacb8b709971b8 100644 (file)
@@ -116,6 +116,7 @@ TESTSUITE_AT = \
  incr09.at\
  indexfile.at\
  ignfail.at\
+ iotty.at\
  label01.at\
  label02.at\
  label03.at\
@@ -261,10 +262,12 @@ installcheck-local:
 ## genfile      ##
 ## ------------ ##
 
-check_PROGRAMS = genfile
+check_PROGRAMS = genfile ttyemu
 
 genfile_SOURCES = genfile.c argcv.c argcv.h
 
+ttyemu_SOURCES = ttyemu.c
+
 localedir = $(datadir)/locale
 AM_CPPFLAGS = \
  -I$(top_srcdir)/gnu\
diff --git a/tests/iotty.at b/tests/iotty.at
new file mode 100644 (file)
index 0000000..ea15c84
--- /dev/null
@@ -0,0 +1,47 @@
+# Process this file with autom4te to create testsuite. -*- Autotest -*-
+
+# Test suite for GNU tar.
+# Copyright 2014 Free Software Foundation, Inc.
+
+# This file is part of GNU tar.
+
+# GNU tar is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# GNU tar is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+# Description: Tar should refuse to read archive from the terminal.
+# Reported by: Pavel Raiskup
+# References: <5285498.uPPgZ77uHP@nb.usersys.redhat.com>,
+#             http://lists.gnu.org/archive/html/bug-tar/2014-03/msg00033.html
+
+AT_SETUP([terminal input])
+AT_KEYWORDS([options iotty])
+
+AT_TAR_CHECK([
+TAPE=-
+export TAPE
+ttyemu -t5 -i/dev/null tar -x
+echo $?
+ttyemu -t5 -i/dev/null tar -xz
+echo $?
+],
+[0],
+[tar: Refusing to read archive contents from terminal (missing -f option?)
+tar: Error is not recoverable: exiting now
+2
+tar: Refusing to read archive contents from terminal (missing -f option?)
+tar: Error is not recoverable: exiting now
+2
+],
+[],[],[],[posix, gnu, oldgnu])
+
+AT_CLEANUP
index c52890b9e0c92af245676de135c5c30edbb90d0a..3096e0dbde720a4509ef98d0a1ba32324c6f6ec9 100644 (file)
@@ -213,6 +213,7 @@ m4_include([gzip.at])
 m4_include([recurse.at])
 m4_include([recurs02.at])
 m4_include([shortrec.at])
+m4_include([iotty.at])
 
 AT_BANNER([The --same-order option])
 m4_include([same-order01.at])
diff --git a/tests/ttyemu.c b/tests/ttyemu.c
new file mode 100644 (file)
index 0000000..9137290
--- /dev/null
@@ -0,0 +1,459 @@
+/* Run program with its first three file descriptors attached to a tty.
+
+   Copyright 2014 Free Software Foundation, Inc.
+
+   This file is part of GNU tar.
+
+   GNU tar is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   GNU tar is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#define _XOPEN_SOURCE 600
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+
+#ifndef TCSASOFT
+# define TCSASOFT 0
+#endif
+
+#define C_EOT 4
+
+#define EX_OK 0
+#define EX_USAGE 125
+#define EX_ERR   126
+#define EX_EXEC  127
+
+#define BUF_SIZE 1024
+
+#if 0
+# define DEBUG(c) fprintf (stderr, "%s\n", c)
+#else
+# define DEBUG(c)
+#endif
+
+struct buffer
+{
+  char buf[BUF_SIZE];
+  int avail;
+  int written;
+  int cr;
+  time_t ts;
+};
+
+#define shut(fildes)                                           \
+  do                                                           \
+    {                                                          \
+      DEBUG (("closing " #fildes));                            \
+      close(fildes);                                           \
+      fildes = -1;                                             \
+    }                                                          \
+  while(0)
+
+#define bufinit(buffer,all)                            \
+  do                                                   \
+    {                                                  \
+      (buffer).avail = (buffer).written = 0;           \
+      (buffer).ts = time (NULL);                       \
+      if (all)                                         \
+       (buffer).cr = 0;                                \
+    }                                                  \
+  while(0)
+
+#define bufisempty(buffer) ((buffer).avail == (buffer).written)
+#define bufavail(buffer) (BUF_SIZE - (buffer).avail)
+
+#define bufread(buffer,fildes,tty)                                     \
+  do                                                                   \
+    {                                                                  \
+      int r = read (fildes, (buffer).buf + (buffer).avail,             \
+                   BUF_SIZE - (buffer).avail);                         \
+      (buffer).ts = time (NULL);                                       \
+      if (r < 0)                                                       \
+       {                                                               \
+         if (errno == EINTR)                                           \
+           continue;                                                   \
+         if (tty && errno == EIO)                                      \
+           shut (fildes);                                              \
+         else                                                          \
+           {                                                           \
+             fprintf (stderr, "%s:%d: reading from %s: %s",            \
+                      __FILE__,__LINE__,#fildes, strerror (errno));    \
+             exit (EX_ERR);                                            \
+           }                                                           \
+       }                                                               \
+      else if (r == 0)                                                 \
+       shut (fildes);                                                  \
+      else                                                             \
+       (buffer).avail += r;                                            \
+    }                                                                  \
+  while(0)
+
+#define bufwrite(buffer,fildes)                                                \
+  do                                                                   \
+    {                                                                  \
+      int r = write (fildes, (buffer).buf + (buffer).written,          \
+                    (buffer).avail - (buffer).written);                \
+      (buffer).ts = time (NULL);                                       \
+      if (r < 0)                                                       \
+       {                                                               \
+         if (errno == EINTR)                                           \
+           continue;                                                   \
+         if (stop)                                                     \
+           shut (fildes);                                              \
+         else                                                          \
+           {                                                           \
+             perror ("writing");                                       \
+             exit (EX_ERR);                                            \
+           }                                                           \
+       }                                                               \
+      else if (r == 0)                                                 \
+       /*shut (fildes)*/;                                              \
+      else                                                             \
+       (buffer).written += r;                                          \
+    }                                                                  \
+  while(0)
+
+void
+tr (struct buffer *bp)
+{
+  int i, j;
+
+  for (i = j = bp->written; i < bp->avail;)
+    {
+      if (bp->buf[i] == '\r')
+       {
+         bp->cr = 1;
+         i++;
+       }
+      else
+       {
+         if (bp->cr)
+           {
+             bp->cr = 0;
+             if (bp->buf[i] != '\n')
+               bp->buf[j++] = '\r';
+           }
+         bp->buf[j++] = bp->buf[i++];
+       }
+    }
+  bp->avail = j;
+}
+
+int stop;
+int status;
+
+void
+sigchld (int sig)
+{
+  DEBUG (("child exited"));
+  wait (&status);
+  stop = 1;
+}
+
+void
+noecho (int fd)
+{
+  struct termios to;
+
+  if (tcgetattr (fd, &to))
+    {
+      perror ("tcgetattr");
+      exit (EX_ERR);
+    }
+  to.c_lflag |= ICANON;
+  to.c_lflag &= ~(ECHO | ISIG);
+  to.c_cc[VEOF] = C_EOT;
+  if (tcsetattr (fd, TCSAFLUSH | TCSASOFT, &to))
+    {
+      perror ("tcgetattr");
+      exit (EX_ERR);
+    }
+}
+
+char *usage_text[] = {
+  "usage: ttyemu [-ah] [-i INFILE] [-o OUTFILE] [-t TIMEOUT] PROGRAM [ARGS...]",
+  "ttyemu runs PROGRAM with its first three file descriptors connected to a"
+  " terminal",
+  "",
+  "Options are:",
+  "",
+  "   -a            append output to OUTFILE, instead of overwriting it",
+  "   -i INFILE     read input from INFILE",
+  "   -o OUTFILE    write output to OUTFILE",
+  "   -t TIMEOUT    set I/O timeout",
+  "   -h            print this help summary",
+  "",
+  "Report bugs and suggestions to <bug-tar@gnu.org>.",
+  NULL
+};
+
+static void
+usage (void)
+{
+  int i;
+  
+  for (i = 0; usage_text[i]; i++)
+    {
+      fputs (usage_text[i], stderr);
+      fputc ('\n', stderr);
+    }
+}
+
+int
+main (int argc, char **argv)
+{
+  int i;
+  int master, slave;
+  pid_t pid;
+  fd_set rdset, wrset;
+  struct buffer ibuf, obuf;
+  int in = 0, out = 1;
+  char *infile = NULL, *outfile = NULL;
+  int outflags = O_TRUNC;
+  int maxfd;
+  int eot = C_EOT;
+  int timeout = 0;
+  
+  while ((i = getopt (argc, argv, "ai:o:t:h")) != EOF)
+    {
+      switch (i)
+       {
+       case 'a':
+         outflags &= ~O_TRUNC;
+         break;
+       
+       case 'i':
+         infile = optarg;
+         break;
+         
+       case 'o':
+         outfile = optarg;
+         break;
+
+       case 't':
+         timeout = atoi (optarg);
+         break;
+         
+       case 'h':
+         usage ();
+         return EX_OK;
+         
+       default:
+         return EX_USAGE;
+       }
+    }
+  
+  argc -= optind;
+  argv += optind;
+
+  if (argc == 0)
+    {
+      usage ();
+      return EX_USAGE;
+    }
+
+  if (infile)
+    {
+      in = open (infile, O_RDONLY);
+      if (in == -1)
+       {
+         perror (infile);
+         return EX_ERR;
+       }
+    }
+
+  if (outfile)
+    {
+      out = open (outfile, O_RDWR|O_CREAT|outflags, 0666);
+      if (out == -1)
+       {
+         perror (outfile);
+         return EX_ERR;
+       }
+    }
+  
+  master = posix_openpt (O_RDWR);
+  if (master == -1)
+    {
+      perror ("posix_openpty");
+      return EX_ERR;
+    }
+
+  if (grantpt (master))
+    {
+      perror ("grantpt");
+      return EX_ERR;
+    }
+
+  if (unlockpt (master))
+    {
+      perror ("unlockpt");
+      return EX_ERR;
+    }
+  
+  signal (SIGCHLD, sigchld);
+
+  pid = fork ();
+  if (pid == -1)
+    {
+      perror ("fork");
+      return EX_ERR;
+    }
+
+  if (pid == 0)
+    {
+      slave = open (ptsname (master), O_RDWR);
+      if (slave < 0)
+       {
+         perror ("open");
+         return EX_ERR;
+       }
+
+      noecho (slave);
+      for (i = 0; i < 3; i++)
+       {
+         if (slave != i)
+           {
+             close (i);
+             if (dup (slave) != i)
+               {
+                 perror ("dup");
+                 _exit (EX_EXEC);
+               }
+           }
+       }
+      for (i = sysconf (_SC_OPEN_MAX) - 1; i > 2; --i)
+       close (i);
+
+      setsid ();
+      ioctl (0, TIOCSCTTY, 1);
+      
+      execvp (argv[0], argv);
+      perror (argv[0]);
+      _exit (EX_EXEC);
+    }
+  sleep (1);
+
+  bufinit (ibuf, 1);
+  bufinit (obuf, 1);
+  while (1)
+    {
+      FD_ZERO (&rdset);
+      FD_ZERO (&wrset);
+      
+      maxfd = 0;
+
+      if (in != -1)
+       {
+         FD_SET (in, &rdset);
+         if (in > maxfd)
+           maxfd = in;
+       }
+
+      if (master != -1)
+       {
+         FD_SET (master, &rdset);
+         if (!stop)
+           FD_SET (master, &wrset);
+         if (master > maxfd)
+           maxfd = master;
+       }
+
+      if (maxfd == 0)
+       {
+         if (stop)
+           break;
+         pause ();
+         continue;
+       }
+      
+      if (select (maxfd + 1, &rdset, &wrset, NULL, NULL) < 0)
+       {
+         if (errno == EINTR)
+           continue;
+         perror ("select");
+         return EX_ERR;
+       }
+
+      if (timeout)
+       {
+         time_t now = time (NULL);
+         if (now - ibuf.ts > timeout || now - obuf.ts > timeout)
+           {
+             fprintf (stderr, "ttyemu: I/O timeout\n");
+             return EX_ERR;
+           }
+       }
+      
+      if (in >= 0)
+       {
+         if (bufavail (ibuf) && FD_ISSET (in, &rdset))
+           bufread (ibuf, in, 0);
+       }
+      else if (master == -1)
+       break;
+
+      if (master >= 0 && FD_ISSET (master, &wrset))
+       {
+         if (!bufisempty (ibuf))
+           bufwrite (ibuf, master);
+         else if (in == -1 && eot)
+           {
+             DEBUG (("sent EOT"));
+             if (write (master, &eot, 1) <= 0)
+               {
+                 perror ("write");
+                 return EX_ERR;
+               }
+             eot = 0;
+           }
+       }
+
+      if (master >= 0 && bufavail (obuf) && FD_ISSET (master, &rdset))
+       bufread (obuf, master, 1);
+
+      if (bufisempty (obuf))
+       bufinit (obuf, 0);
+      else
+       {
+         tr (&obuf);
+         bufwrite (obuf, out);
+       }
+      
+      if (bufisempty (ibuf))
+       bufinit (ibuf, 0);
+    }
+
+  if (WIFEXITED (status))
+    return WEXITSTATUS (status);
+
+  if (WIFSIGNALED (status))
+    fprintf (stderr, "ttyemu: child process %s failed on signal %d\n",
+            argv[0], WTERMSIG (status));
+  else
+    fprintf (stderr, "ttyemu: child process %s failed\n", argv[0]);
+  return EX_EXEC;
+}
This page took 0.032291 seconds and 4 git commands to generate.