Prechádzať zdrojové kódy

(<time.h>): Do not include; system.h now does this.
(make_directories): Skip filesystem prefixes.
Don't assume '/' is the only separator.
(extract_sparse_file): Use new full_write semantics.
On write error, return instead of invoking skip_file.
Do not free sparsearray; caller does this now.
(apply_nonancestor_delayed_set_stat): Do not assume '/' is the only
separator.
(extract_archive): Don't assume file name lengths fit in int.
Report what got stripped from member name; it might be more than '/'.
Use new full_write semantics.
Do not pass redundant trailing "/" to mkdir, as POSIX does not allow
mkdir to ignore it.
Do not report mkdir error if old_files_option == KEEP_OLD_FILES.

Paul Eggert 24 rokov pred
rodič
commit
eca497ef34
1 zmenil súbory, kde vykonal 69 pridanie a 52 odobranie
  1. 69 52
      src/extract.c

+ 69 - 52
src/extract.c

@@ -1,5 +1,5 @@
 /* Extract files from a tar archive.
-   Copyright 1988, 92,93,94,96,97,98,99, 2000 Free Software Foundation, Inc.
+   Copyright 1988,92,93,94,96,97,98,99,2000,2001 Free Software Foundation, Inc.
    Written by John Gilmore, on 1985-11-19.
 
    This program is free software; you can redistribute it and/or modify it
@@ -18,7 +18,6 @@
 
 #include "system.h"
 #include <quotearg.h>
-#include <time.h>
 
 #if HAVE_UTIME_H
 # include <utime.h>
@@ -321,27 +320,30 @@ repair_delayed_set_stat (char const *dir_name,
 static int
 make_directories (char *file_name)
 {
+  char *cursor0 = file_name + FILESYSTEM_PREFIX_LEN (file_name);
   char *cursor;			/* points into path */
   int did_something = 0;	/* did we do anything yet? */
   int mode;
   int invert_permissions;
   int status;
 
-  for (cursor = strchr (file_name, '/');
-       cursor;
-       cursor = strchr (cursor + 1, '/'))
+  
+  for (cursor = cursor0; *cursor; cursor++)
     {
+      if (! ISSLASH (*cursor))
+	continue;
+
       /* Avoid mkdir of empty string, if leading or double '/'.  */
 
-      if (cursor == file_name || cursor[-1] == '/')
+      if (cursor == cursor0 || ISSLASH (cursor[-1]))
 	continue;
 
       /* Avoid mkdir where last part of path is "." or "..".  */
 
       if (cursor[-1] == '.'
-	  && (cursor == file_name + 1 || cursor[-2] == '/'
+	  && (cursor == cursor0 + 1 || ISSLASH (cursor[-2])
 	      || (cursor[-2] == '.'
-		  && (cursor == file_name + 2 || cursor[-3] == '/'))))
+		  && (cursor == cursor0 + 2 || ISSLASH (cursor[-3])))))
 	continue;
 
       *cursor = '\0';		/* truncate the path there */
@@ -453,13 +455,13 @@ static void
 extract_sparse_file (int fd, off_t *sizeleft, off_t totalsize, char *name)
 {
   int sparse_ind = 0;
-  size_t written;
-  ssize_t count;
 
   /* assuming sizeleft is initially totalsize */
 
   while (*sizeleft > 0)
     {
+      size_t written;
+      size_t count;
       union block *data_block = find_next_block ();
       if (! data_block)
 	{
@@ -475,10 +477,13 @@ extract_sparse_file (int fd, off_t *sizeleft, off_t totalsize, char *name)
       while (written > BLOCKSIZE)
 	{
 	  count = full_write (fd, data_block->buffer, BLOCKSIZE);
-	  if (count < 0)
-	    write_error (name);
 	  written -= count;
 	  *sizeleft -= count;
+	  if (count != BLOCKSIZE)
+	    {
+	      write_error_details (name, count, BLOCKSIZE);
+	      return;
+	    }
 	  set_next_block_after (data_block);
 	  data_block = find_next_block ();
 	  if (! data_block)
@@ -489,21 +494,16 @@ extract_sparse_file (int fd, off_t *sizeleft, off_t totalsize, char *name)
 	}
 
       count = full_write (fd, data_block->buffer, written);
+      *sizeleft -= count;
 
-      if (count < 0)
-	write_error (name);
-      else if (count != written)
+      if (count != written)
 	{
 	  write_error_details (name, count, written);
-	  skip_file (*sizeleft);
+	  return;
 	}
 
-      written -= count;
-      *sizeleft -= count;
       set_next_block_after (data_block);
     }
-
-  free (sparsearray);
 }
 
 /* Fix the statuses of all directories whose statuses need fixing, and
@@ -518,8 +518,8 @@ apply_nonancestor_delayed_set_stat (char const *file_name)
       struct delayed_set_stat *data = delayed_set_stat_head;
       if (data->file_name_len < file_name_len
 	  && file_name[data->file_name_len]
-	  && (file_name[data->file_name_len] == '/'
-	      || file_name[data->file_name_len - 1] == '/')
+	  && (ISSLASH (file_name[data->file_name_len])
+	      || ISSLASH (file_name[data->file_name_len - 1]))
 	  && memcmp (file_name, data->file_name, data->file_name_len) == 0)
 	break;
       delayed_set_stat_head = data->next;
@@ -536,13 +536,13 @@ extract_archive (void)
   union block *data_block;
   int fd;
   int status;
-  ssize_t sstatus;
+  size_t count;
   size_t name_length;
   size_t written;
   int openflag;
   mode_t mode;
   off_t size;
-  int skipcrud;
+  size_t skipcrud;
   int counter;
   int interdir_made = 0;
   char typeflag;
@@ -569,24 +569,28 @@ extract_archive (void)
   skipcrud = 0;
   if (! absolute_names_option)
     {
-      while (CURRENT_FILE_NAME[0] == '/')
+      if (contains_dot_dot (CURRENT_FILE_NAME))
+	{
+	  ERROR ((0, 0, _("%s: Member name contains `..'"),
+		  quotearg_colon (CURRENT_FILE_NAME)));
+	  skip_member ();
+	  return;
+	}
+
+      skipcrud = FILESYSTEM_PREFIX_LEN (current_file_name);
+      while (ISSLASH (CURRENT_FILE_NAME[0]))
+	skipcrud++;
+
+      if (skipcrud)
 	{
 	  static int warned_once;
 	  
 	  if (!warned_once)
 	    {
 	      warned_once = 1;
-	      WARN ((0, 0, _("Removing leading `/' from member names")));
+	      WARN ((0, 0, _("Removing leading `%.*s' from member names"),
+		     (int) skipcrud, current_file_name));
 	    }
-	  skipcrud++;		/* force relative path */
-	}
-
-      if (contains_dot_dot (CURRENT_FILE_NAME))
-	{
-	  ERROR ((0, 0, _("%s: Member name contains `..'"),
-		  quotearg_colon (CURRENT_FILE_NAME)));
-	  skip_member ();
-	  return;
 	}
     }
 
@@ -693,7 +697,8 @@ extract_archive (void)
 	 suffix means a directory.  */
 
       name_length = strlen (CURRENT_FILE_NAME);
-      if (name_length && CURRENT_FILE_NAME[name_length - 1] == '/')
+      if (FILESYSTEM_PREFIX_LEN (CURRENT_FILE_NAME) < name_length
+	  && CURRENT_FILE_NAME[name_length - 1] == '/')
 	goto really_dir;
 
       /* FIXME: deal with protection issues.  */
@@ -773,11 +778,10 @@ extract_archive (void)
 	  memcpy (name, CURRENT_FILE_NAME, name_length_bis);
 	  size = current_stat.st_size;
 	  extract_sparse_file (fd, &size, current_stat.st_size, name);
+	  free (sparsearray);
 	}
       else
-	for (size = current_stat.st_size;
-	     size > 0;
-	     size -= written)
+	for (size = current_stat.st_size; size > 0; )
 	  {
 	    if (multi_volume_option)
 	      {
@@ -802,21 +806,20 @@ extract_archive (void)
 	    if (written > size)
 	      written = size;
 	    errno = 0;
-	    sstatus = full_write (fd, data_block->buffer, written);
+	    count = full_write (fd, data_block->buffer, written);
+	    size -= count;
 
 	    set_next_block_after ((union block *)
 				  (data_block->buffer + written - 1));
-	    if (sstatus == written)
-	      continue;
-
-	    /* Error in writing to file.  Print it, skip to next file in
-	       archive.  */
-
-	    write_error_details (CURRENT_FILE_NAME, sstatus, written);
-	    skip_file (size - written);
-	    break;		/* still do the close, mod time, chmod, etc */
+	    if (count != written)
+	      {
+		write_error_details (CURRENT_FILE_NAME, count, written);
+		break;
+	      }
 	  }
 
+      skip_file (size);
+
       if (multi_volume_option)
 	assign_string (&save_name, 0);
 
@@ -847,7 +850,8 @@ extract_archive (void)
 	break;
 
       if (absolute_names_option
-	  || (current_link_name[0] != '/'
+	  || (ISSLASH (current_link_name
+		       [FILESYSTEM_PREFIX_LEN (current_link_name)])
 	      && ! contains_dot_dot (current_link_name)))
 	{
 	  while (status = symlink (current_link_name, CURRENT_FILE_NAME),
@@ -1014,6 +1018,7 @@ extract_archive (void)
       name_length = strlen (CURRENT_FILE_NAME);
 
     really_dir:
+
       if (incremental_option)
 	{
 	  /* Read the entry and delete files that aren't listed in the
@@ -1032,7 +1037,19 @@ extract_archive (void)
 	      & MODE_RWX);
 
     again_dir:
-      status = mkdir (CURRENT_FILE_NAME, mode);
+      {
+	/* Do not pass redundant trailing "/" to mkdir, as POSIX does
+	   not allow mkdir to ignore it.  */
+	size_t len = name_length;
+	char ch = '\0';
+	while (FILESYSTEM_PREFIX_LEN (CURRENT_FILE_NAME) < len
+	       && CURRENT_FILE_NAME[len - 1] == '/')
+	  len--, ch = '/';
+	CURRENT_FILE_NAME[len] = '\0';
+	status = mkdir (CURRENT_FILE_NAME, mode);
+	CURRENT_FILE_NAME[len] = ch;
+      }
+
       if (status != 0)
 	{
 	  if (errno == EEXIST && interdir_made)
@@ -1049,7 +1066,7 @@ extract_archive (void)
 	  if (maybe_recoverable (CURRENT_FILE_NAME, &interdir_made))
 	    goto again_dir;
 
-	  if (errno != EEXIST || old_files_option == KEEP_OLD_FILES)
+	  if (errno != EEXIST)
 	    {
 	      mkdir_error (CURRENT_FILE_NAME);
 	      if (backup_option)