]> Dogcows Code - chaz/tar/blobdiff - src/system.c
Update copyright years.
[chaz/tar] / src / system.c
index b1783b1ac2ecca05280f267752ee25d05be8b011..94142336ef6d021868836cf94a97720ee9d20429 100644 (file)
@@ -1,10 +1,10 @@
 /* System-dependent calls for tar.
 
-   Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+   Copyright 2003-2008, 2010, 2013-2014 Free Software Foundation, Inc.
 
    This program 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 2, or (at your option) any later
+   Free Software Foundation; either version 3, or (at your option) any later
    version.
 
    This program is distributed in the hope that it will be useful, but
    Public License for more details.
 
    You should have received a copy of the GNU General Public License along
-   with this program; if not, write to the Free Software Foundation, Inc.,
-   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+   with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include <system.h>
 
 #include "common.h"
+#include <priv-set.h>
 #include <rmt.h>
 #include <signal.h>
+#include <wordsplit.h>
 
-void
-sys_stat_nanoseconds (struct tar_stat_info *st)
-{
-#if defined(HAVE_STRUCT_STAT_ST_SPARE1)
-  st->atime_nsec = st->stat.st_spare1 * 1000;
-  st->mtime_nsec = st->stat.st_spare2 * 1000;
-  st->ctime_nsec = st->stat.st_spare3 * 1000;
-#elif defined(HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
-  st->atime_nsec = st->stat.st_atim.tv_nsec;
-  st->mtime_nsec = st->stat.st_mtim.tv_nsec;
-  st->ctime_nsec = st->stat.st_ctim.tv_nsec;
-#elif defined(HAVE_STRUCT_STAT_ST_ATIMESPEC_TV_NSEC)
-  st->atime_nsec = st->stat.st_atimespec.tv_nsec;
-  st->mtime_nsec = st->stat.st_mtimespec.tv_nsec;
-  st->ctime_nsec = st->stat.st_ctimespec.tv_nsec;
-#elif defined(HAVE_STRUCT_STAT_ST_ATIMENSEC)
-  st->atime_nsec = st->stat.st_atimensec;
-  st->mtime_nsec = st->stat.st_mtimensec;
-  st->ctime_nsec = st->stat.st_ctimensec;
-#else
-  st->atime_nsec  = st->mtime_nsec = st->ctime_nsec = 0;
-#endif
+static _Noreturn void
+xexec (const char *cmd)
+{
+  struct wordsplit ws;
+
+  ws.ws_env = (const char **) environ;
+  if (wordsplit (cmd, &ws, (WRDSF_DEFFLAGS | WRDSF_ENV) & ~WRDSF_NOVAR))
+    FATAL_ERROR ((0, 0, _("cannot split string '%s': %s"),
+                 cmd, wordsplit_strerror (&ws)));
+  execvp (ws.ws_wordv[0], ws.ws_wordv);
+  exec_fatal (cmd);
 }
 
 #if MSDOS
@@ -75,12 +65,7 @@ sys_detect_dev_null_output (void)
 }
 
 void
-sys_drain_input_pipe (void)
-{
-}
-
-void
-sys_wait_for_child (pid_t child_pid)
+sys_wait_for_child (pid_t child_pid, bool eof)
 {
 }
 
@@ -184,26 +169,8 @@ sys_detect_dev_null_output (void)
                         && archive_stat.st_ino == dev_null_stat.st_ino));
 }
 
-/* Manage to fully drain a pipe we might be reading, so to not break it on
-   the producer after the EOF block.  FIXME: one of these days, GNU tar
-   might become clever enough to just stop working, once there is no more
-   work to do, we might have to revise this area in such time.  */
-
 void
-sys_drain_input_pipe (void)
-{
-  size_t r;
-
-  if (access_mode == ACCESS_READ
-      && ! _isrmt (archive)
-      && (S_ISFIFO (archive_stat.st_mode) || S_ISSOCK (archive_stat.st_mode)))
-    while ((r = rmtread (archive, record_start->buffer, record_size)) != 0
-          && r != SAFE_READ_ERROR)
-      continue;
-}
-
-void
-sys_wait_for_child (pid_t child_pid)
+sys_wait_for_child (pid_t child_pid, bool eof)
 {
   if (child_pid)
     {
@@ -217,11 +184,14 @@ sys_wait_for_child (pid_t child_pid)
          }
 
       if (WIFSIGNALED (wait_status))
-       ERROR ((0, 0, _("Child died with signal %d"),
-               WTERMSIG (wait_status)));
+       {
+         int sig = WTERMSIG (wait_status);
+         if (!(!eof && sig == SIGPIPE))
+           FATAL_ERROR ((0, 0, _("Child died with signal %d"), sig));
+       }
       else if (WEXITSTATUS (wait_status) != 0)
-       ERROR ((0, 0, _("Child returned status %d"),
-               WEXITSTATUS (wait_status)));
+       FATAL_ERROR ((0, 0, _("Child returned status %d"),
+                     WEXITSTATUS (wait_status)));
     }
 }
 
@@ -235,7 +205,8 @@ sys_spawn_shell (void)
   child = xfork ();
   if (child == 0)
     {
-      execlp (shell, "-sh", "-i", (char *) 0);
+      priv_set_restore_linkdir ();
+      execlp (shell, "-sh", "-i", NULL);
       exec_fatal (shell);
     }
   else
@@ -326,6 +297,30 @@ xdup2 (int from, int into)
     }
 }
 
+static void wait_for_grandchild (pid_t pid) __attribute__ ((__noreturn__));
+
+/* Propagate any failure of the grandchild back to the parent.  */
+static void
+wait_for_grandchild (pid_t pid)
+{
+  int wait_status;
+  int exit_code = 0;
+
+  while (waitpid (pid, &wait_status, 0) == -1)
+    if (errno != EINTR)
+      {
+       waitpid_error (use_compress_program_option);
+       break;
+      }
+
+  if (WIFSIGNALED (wait_status))
+    raise (WTERMSIG (wait_status));
+  else if (WEXITSTATUS (wait_status) != 0)
+    exit_code = WEXITSTATUS (wait_status);
+
+  exit (exit_code);
+}
+
 /* Set ARCHIVE for writing, then compressing an archive.  */
 pid_t
 sys_child_open_for_compress (void)
@@ -334,7 +329,6 @@ sys_child_open_for_compress (void)
   int child_pipe[2];
   pid_t grandchild_pid;
   pid_t child_pid;
-  int wait_status;
 
   xpipe (parent_pipe);
   child_pid = xfork ();
@@ -350,18 +344,17 @@ sys_child_open_for_compress (void)
 
   /* The new born child tar is here!  */
 
-  program_name = _("tar (child)");
+  set_program_name (_("tar (child)"));
+  signal (SIGPIPE, SIG_DFL);
 
   xdup2 (parent_pipe[PREAD], STDIN_FILENO);
   xclose (parent_pipe[PWRITE]);
 
   /* Check if we need a grandchild tar.  This happens only if either:
-     a) we are writing stdout: to force reblocking;
-     b) the file is to be accessed by rmt: compressor doesn't know how;
-     c) the file is not a plain file.  */
+     a) the file is to be accessed by rmt: compressor doesn't know how;
+     b) the file is not a plain file.  */
 
-  if (strcmp (archive_name_array[0], "-") != 0
-      && !_remdev (archive_name_array[0])
+  if (!_remdev (archive_name_array[0])
       && is_regular_file (archive_name_array[0]))
     {
       if (backup_option)
@@ -369,21 +362,22 @@ sys_child_open_for_compress (void)
 
       /* We don't need a grandchild tar.  Open the archive and launch the
         compressor.  */
-
-      archive = creat (archive_name_array[0], MODE_RW);
-      if (archive < 0)
+      if (strcmp (archive_name_array[0], "-"))
        {
-         int saved_errno = errno;
+         archive = creat (archive_name_array[0], MODE_RW);
+         if (archive < 0)
+           {
+             int saved_errno = errno;
 
-         if (backup_option)
-           undo_last_backup ();
-         errno = saved_errno;
-         open_fatal (archive_name_array[0]);
+             if (backup_option)
+               undo_last_backup ();
+             errno = saved_errno;
+             open_fatal (archive_name_array[0]);
+           }
+         xdup2 (archive, STDOUT_FILENO);
        }
-      xdup2 (archive, STDOUT_FILENO);
-      execlp (use_compress_program_option, use_compress_program_option,
-             (char *) 0);
-      exec_fatal (use_compress_program_option);
+      priv_set_restore_linkdir ();
+      xexec (use_compress_program_option);
     }
 
   /* We do need a grandchild tar.  */
@@ -395,13 +389,12 @@ sys_child_open_for_compress (void)
     {
       /* The newborn grandchild tar is here!  Launch the compressor.  */
 
-      program_name = _("tar (grandchild)");
+      set_program_name (_("tar (grandchild)"));
 
       xdup2 (child_pipe[PWRITE], STDOUT_FILENO);
       xclose (child_pipe[PREAD]);
-      execlp (use_compress_program_option, use_compress_program_option,
-             (char *) 0);
-      exec_fatal (use_compress_program_option);
+      priv_set_restore_linkdir ();
+      xexec (use_compress_program_option);
     }
 
   /* The child tar is still here!  */
@@ -468,24 +461,43 @@ sys_child_open_for_compress (void)
        archive_write_error (status);
     }
 
-  /* Propagate any failure of the grandchild back to the parent.  */
+  wait_for_grandchild (grandchild_pid);
+}
 
-  while (waitpid (grandchild_pid, &wait_status, 0) == -1)
-    if (errno != EINTR)
-      {
-       waitpid_error (use_compress_program_option);
-       break;
-      }
+static void
+run_decompress_program (void)
+{
+  int i;
+  const char *p, *prog = NULL;
+  struct wordsplit ws;
+  int wsflags = (WRDSF_DEFFLAGS | WRDSF_ENV | WRDSF_DOOFFS) & ~WRDSF_NOVAR;
 
-  if (WIFSIGNALED (wait_status))
+  ws.ws_env = (const char **) environ;
+  ws.ws_offs = 1;
+
+  for (p = first_decompress_program (&i); p; p = next_decompress_program (&i))
     {
-      kill (child_pid, WTERMSIG (wait_status));
-      exit_status = TAREXIT_FAILURE;
+      if (prog)
+       {
+         WARNOPT (WARN_DECOMPRESS_PROGRAM,
+                  (0, errno, _("cannot run %s"), prog));
+         WARNOPT (WARN_DECOMPRESS_PROGRAM,
+                  (0, 0, _("trying %s"), p));
+       }
+      if (wordsplit (p, &ws, wsflags))
+       FATAL_ERROR ((0, 0, _("cannot split string '%s': %s"),
+                     p, wordsplit_strerror (&ws)));
+      wsflags |= WRDSF_REUSE;
+      memmove(ws.ws_wordv, ws.ws_wordv + ws.ws_offs,
+             sizeof(ws.ws_wordv[0])*ws.ws_wordc);
+      ws.ws_wordv[ws.ws_wordc] = (char *) "-d";
+      prog = p;
+      execvp (ws.ws_wordv[0], ws.ws_wordv);
+      ws.ws_wordv[ws.ws_wordc] = NULL;
     }
-  else if (WEXITSTATUS (wait_status) != 0)
-    exit_status = WEXITSTATUS (wait_status);
-
-  exit (exit_status);
+  if (!prog)
+    FATAL_ERROR ((0, 0, _("unable to run decompression program")));
+  exec_fatal (prog);
 }
 
 /* Set ARCHIVE for uncompressing, then reading an archive.  */
@@ -496,7 +508,6 @@ sys_child_open_for_uncompress (void)
   int child_pipe[2];
   pid_t grandchild_pid;
   pid_t child_pid;
-  int wait_status;
 
   xpipe (parent_pipe);
   child_pid = xfork ();
@@ -512,7 +523,8 @@ sys_child_open_for_uncompress (void)
 
   /* The newborn child tar is here!  */
 
-  program_name = _("tar (child)");
+  set_program_name (_("tar (child)"));
+  signal (SIGPIPE, SIG_DFL);
 
   xdup2 (parent_pipe[PWRITE], STDOUT_FILENO);
   xclose (parent_pipe[PREAD]);
@@ -533,9 +545,8 @@ sys_child_open_for_uncompress (void)
       if (archive < 0)
        open_fatal (archive_name_array[0]);
       xdup2 (archive, STDIN_FILENO);
-      execlp (use_compress_program_option, use_compress_program_option,
-             "-d", (char *) 0);
-      exec_fatal (use_compress_program_option);
+      priv_set_restore_linkdir ();
+      run_decompress_program ();
     }
 
   /* We do need a grandchild tar.  */
@@ -547,13 +558,12 @@ sys_child_open_for_uncompress (void)
     {
       /* The newborn grandchild tar is here!  Launch the uncompressor.  */
 
-      program_name = _("tar (grandchild)");
+      set_program_name (_("tar (grandchild)"));
 
       xdup2 (child_pipe[PREAD], STDIN_FILENO);
       xclose (child_pipe[PWRITE]);
-      execlp (use_compress_program_option, use_compress_program_option,
-             "-d", (char *) 0);
-      exec_fatal (use_compress_program_option);
+      priv_set_restore_linkdir ();
+      run_decompress_program ();
     }
 
   /* The child tar is still here!  */
@@ -606,80 +616,84 @@ sys_child_open_for_uncompress (void)
 
   xclose (STDOUT_FILENO);
 
-  /* Propagate any failure of the grandchild back to the parent.  */
-
-  while (waitpid (grandchild_pid, &wait_status, 0) == -1)
-    if (errno != EINTR)
-      {
-       waitpid_error (use_compress_program_option);
-       break;
-      }
-
-  if (WIFSIGNALED (wait_status))
-    {
-      kill (child_pid, WTERMSIG (wait_status));
-      exit_status = TAREXIT_FAILURE;
-    }
-  else if (WEXITSTATUS (wait_status) != 0)
-    exit_status = WEXITSTATUS (wait_status);
-
-  exit (exit_status);
+  wait_for_grandchild (grandchild_pid);
 }
 
 \f
 
 static void
-dec_to_env (char *envar, uintmax_t num)
+dec_to_env (char const *envar, uintmax_t num)
 {
   char buf[UINTMAX_STRSIZE_BOUND];
   char *numstr;
 
   numstr = STRINGIFY_BIGINT (num, buf);
-  setenv (envar, numstr, 1);
+  if (setenv (envar, numstr, 1) != 0)
+    xalloc_die ();
+}
+
+static void
+time_to_env (char const *envar, struct timespec t)
+{
+  char buf[TIMESPEC_STRSIZE_BOUND];
+  if (setenv (envar, code_timespec (t, buf), 1) != 0)
+    xalloc_die ();
 }
 
 static void
-oct_to_env (char *envar, unsigned long num)
+oct_to_env (char const *envar, unsigned long num)
 {
   char buf[1+1+(sizeof(unsigned long)*CHAR_BIT+2)/3];
-  
+
   snprintf (buf, sizeof buf, "0%lo", num);
-  setenv (envar, buf, 1);
+  if (setenv (envar, buf, 1) != 0)
+    xalloc_die ();
 }
 
 static void
-str_to_env (char *envar, char const *str)
+str_to_env (char const *envar, char const *str)
 {
   if (str)
-    setenv (envar, str, 1);
+    {
+      if (setenv (envar, str, 1) != 0)
+       xalloc_die ();
+    }
   else
     unsetenv (envar);
 }
 
 static void
-chr_to_env (char *envar, char c)
+chr_to_env (char const *envar, char c)
 {
   char buf[2];
   buf[0] = c;
   buf[1] = 0;
-  setenv (envar, buf, 1);
+  if (setenv (envar, buf, 1) != 0)
+    xalloc_die ();
 }
 
 static void
 stat_to_env (char *name, char type, struct tar_stat_info *st)
 {
+  str_to_env ("TAR_VERSION", PACKAGE_VERSION);
+  str_to_env ("TAR_ARCHIVE", *archive_name_cursor);
+  dec_to_env ("TAR_VOLUME", archive_name_cursor - archive_name_array + 1);
+  dec_to_env ("TAR_BLOCKING_FACTOR", blocking_factor);
+  str_to_env ("TAR_FORMAT",
+             archive_format_string (current_format == DEFAULT_FORMAT ?
+                                    archive_format : current_format));
   chr_to_env ("TAR_FILETYPE", type);
   oct_to_env ("TAR_MODE", st->stat.st_mode);
   str_to_env ("TAR_FILENAME", name);
   str_to_env ("TAR_REALNAME", st->file_name);
   str_to_env ("TAR_UNAME", st->uname);
   str_to_env ("TAR_GNAME", st->gname);
-  dec_to_env ("TAR_MTIME", st->stat.st_mtime);
-  dec_to_env ("TAR_ATIME", st->stat.st_atime);
-  dec_to_env ("TAR_CTIME", st->stat.st_ctime);
+  time_to_env ("TAR_ATIME", st->atime);
+  time_to_env ("TAR_MTIME", st->mtime);
+  time_to_env ("TAR_CTIME", st->ctime);
   dec_to_env ("TAR_SIZE", st->stat.st_size);
   dec_to_env ("TAR_UID", st->stat.st_uid);
-  dec_to_env ("TAR_GID", st->stat.st_gid);     
+  dec_to_env ("TAR_GID", st->stat.st_gid);
 
   switch (type)
     {
@@ -705,70 +719,47 @@ stat_to_env (char *name, char type, struct tar_stat_info *st)
     }
 }
 
-static pid_t pid;
-static RETSIGTYPE (*pipe_handler) (int sig);
+static pid_t global_pid;
+static void (*pipe_handler) (int sig);
 
 int
 sys_exec_command (char *file_name, int typechar, struct tar_stat_info *st)
 {
   int p[2];
-  char *argv[4];
-  
-  if (pipe (p))
-    {
-      pipe_error (file_name);
-      return -1;
-    }
 
+  xpipe (p);
   pipe_handler = signal (SIGPIPE, SIG_IGN);
-  pid = fork ();
-
-  if (pid < 0)
-    {
-      fork_error (file_name);
-      close (p[1]);
-      close (p[0]);
-      signal (SIGPIPE, pipe_handler);
-      return -1;
-    }
+  global_pid = xfork ();
 
-  if (pid != 0)
+  if (global_pid != 0)
     {
-      close (p[0]);
-      return p[1];
+      xclose (p[PREAD]);
+      return p[PWRITE];
     }
 
   /* Child */
-  close (0);
-  dup (p[0]);
-  close (p[1]);
-  
-  stat_to_env (file_name, typechar, st);
+  xdup2 (p[PREAD], STDIN_FILENO);
+  xclose (p[PWRITE]);
 
-  argv[0] = "/bin/sh";
-  argv[1] = "-c";
-  argv[2] = to_command_option;
-  argv[3] = NULL;
-
-  execv ("/bin/sh", argv);
+  stat_to_env (file_name, typechar, st);
 
-  exec_error (file_name);
-  _exit (127);
+  priv_set_restore_linkdir ();
+  xexec (to_command_option);
 }
 
 void
 sys_wait_command (void)
 {
   int status;
-  
-  if (pid < 0)
+
+  if (global_pid < 0)
     return;
 
   signal (SIGPIPE, pipe_handler);
-  while (waitpid (pid, &status, 0) == -1)
+  while (waitpid (global_pid, &status, 0) == -1)
     if (errno != EINTR)
       {
-        pid = -1;
+        global_pid = -1;
         waitpid_error (to_command_option);
         return;
       }
@@ -777,19 +768,130 @@ sys_wait_command (void)
     {
       if (!ignore_command_error_option && WEXITSTATUS (status))
        ERROR ((0, 0, _("%lu: Child returned status %d"),
-               (unsigned long) pid, WEXITSTATUS (status)));
+               (unsigned long) global_pid, WEXITSTATUS (status)));
     }
   else if (WIFSIGNALED (status))
     {
       WARN ((0, 0, _("%lu: Child terminated on signal %d"),
-            (unsigned long) pid, WTERMSIG (status)));
+            (unsigned long) global_pid, WTERMSIG (status)));
     }
   else
     ERROR ((0, 0, _("%lu: Child terminated on unknown reason"),
-           (unsigned long) pid, WTERMSIG (status)));
+           (unsigned long) global_pid));
 
-  pid = -1;
+  global_pid = -1;
 }
 
-#endif /* not MSDOS */
+int
+sys_exec_info_script (const char **archive_name, int volume_number)
+{
+  pid_t pid;
+  char uintbuf[UINTMAX_STRSIZE_BOUND];
+  int p[2];
+  static void (*saved_handler) (int sig);
+
+  xpipe (p);
+  saved_handler = signal (SIGPIPE, SIG_IGN);
+
+  pid = xfork ();
 
+  if (pid != 0)
+    {
+      /* Master */
+
+      int rc;
+      int status;
+      char *buf = NULL;
+      size_t size = 0;
+      FILE *fp;
+
+      xclose (p[PWRITE]);
+      fp = fdopen (p[PREAD], "r");
+      rc = getline (&buf, &size, fp);
+      fclose (fp);
+
+      if (rc > 0 && buf[rc-1] == '\n')
+       buf[--rc] = 0;
+
+      while (waitpid (pid, &status, 0) == -1)
+       if (errno != EINTR)
+         {
+           signal (SIGPIPE, saved_handler);
+           waitpid_error (info_script_option);
+           return -1;
+         }
+
+      signal (SIGPIPE, saved_handler);
+
+      if (WIFEXITED (status))
+       {
+         if (WEXITSTATUS (status) == 0 && rc > 0)
+           *archive_name = buf;
+         else
+           free (buf);
+         return WEXITSTATUS (status);
+       }
+
+      free (buf);
+      return -1;
+    }
+
+  /* Child */
+  setenv ("TAR_VERSION", PACKAGE_VERSION, 1);
+  setenv ("TAR_ARCHIVE", *archive_name, 1);
+  setenv ("TAR_VOLUME", STRINGIFY_BIGINT (volume_number, uintbuf), 1);
+  setenv ("TAR_BLOCKING_FACTOR",
+         STRINGIFY_BIGINT (blocking_factor, uintbuf), 1);
+  setenv ("TAR_SUBCOMMAND", subcommand_string (subcommand_option), 1);
+  setenv ("TAR_FORMAT",
+         archive_format_string (current_format == DEFAULT_FORMAT ?
+                                archive_format : current_format), 1);
+  setenv ("TAR_FD", STRINGIFY_BIGINT (p[PWRITE], uintbuf), 1);
+
+  xclose (p[PREAD]);
+
+  priv_set_restore_linkdir ();
+  xexec (info_script_option);
+}
+
+void
+sys_exec_checkpoint_script (const char *script_name,
+                           const char *archive_name,
+                           int checkpoint_number)
+{
+  pid_t pid;
+  char uintbuf[UINTMAX_STRSIZE_BOUND];
+
+  pid = xfork ();
+
+  if (pid != 0)
+    {
+      /* Master */
+
+      int status;
+
+      while (waitpid (pid, &status, 0) == -1)
+       if (errno != EINTR)
+         {
+           waitpid_error (script_name);
+           break;
+         }
+
+      return;
+    }
+
+  /* Child */
+  setenv ("TAR_VERSION", PACKAGE_VERSION, 1);
+  setenv ("TAR_ARCHIVE", archive_name, 1);
+  setenv ("TAR_CHECKPOINT", STRINGIFY_BIGINT (checkpoint_number, uintbuf), 1);
+  setenv ("TAR_BLOCKING_FACTOR",
+         STRINGIFY_BIGINT (blocking_factor, uintbuf), 1);
+  setenv ("TAR_SUBCOMMAND", subcommand_string (subcommand_option), 1);
+  setenv ("TAR_FORMAT",
+         archive_format_string (current_format == DEFAULT_FORMAT ?
+                                archive_format : current_format), 1);
+  priv_set_restore_linkdir ();
+  xexec (script_name);
+}
+
+#endif /* not MSDOS */
This page took 0.042181 seconds and 4 git commands to generate.