| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 | /* Delete entries from a tar archive.   Copyright (C) 1988, 1992, 1994, 1996, 1997, 2000, 2001, 2003, 2004,   2005, 2006, 2010 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 3, or (at your option) any later   version.   This program 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, write to the Free Software Foundation, Inc.,   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */#include <system.h>#include <system-ioctl.h>#include "common.h"#include <rmt.h>static union block *new_record;static int new_blocks;static bool acting_as_filter;/* FIXME: This module should not directly handle the following   variables, instead, the interface should be cleaned up.  */extern union block *record_start;extern union block *record_end;extern union block *current_block;extern union block *recent_long_name;extern union block *recent_long_link;extern off_t records_read;/* The number of records skipped at the start of the archive, when   passing over members that are not deleted.  */off_t records_skipped;/* Move archive descriptor by COUNT records worth.  If COUNT is   positive we move forward, else we move negative.  If it's a tape,   MTIOCTOP had better work.  If it's something else, we try to seek   on it.  If we can't seek, we lose!  */static voidmove_archive (off_t count){  if (count == 0)    return;#ifdef MTIOCTOP  {    struct mtop operation;    if (count < 0	? (operation.mt_op = MTBSR,	   operation.mt_count = -count,	   operation.mt_count == -count)	: (operation.mt_op = MTFSR,	   operation.mt_count = count,	   operation.mt_count == count))      {	if (0 <= rmtioctl (archive, MTIOCTOP, (char *) &operation))	  return;	if (errno == EIO	    && 0 <= rmtioctl (archive, MTIOCTOP, (char *) &operation))	  return;      }  }#endif /* MTIOCTOP */  {    off_t position0 = rmtlseek (archive, (off_t) 0, SEEK_CUR);    off_t increment = record_size * (off_t) count;    off_t position = position0 + increment;    if (increment / count != record_size	|| (position < position0) != (increment < 0)	|| (position = position < 0 ? 0 : position,	    rmtlseek (archive, position, SEEK_SET) != position))      seek_error_details (archive_name_array[0], position);    return;  }}/* Write out the record which has been filled.  If MOVE_BACK_FLAG,   backspace to where we started.  */static voidwrite_record (int move_back_flag){  union block *save_record = record_start;  record_start = new_record;  if (acting_as_filter)    {      archive = STDOUT_FILENO;      flush_write ();      archive = STDIN_FILENO;    }  else    {      move_archive ((records_written + records_skipped) - records_read);      flush_write ();    }  record_start = save_record;  if (move_back_flag)    {      /* Move the tape head back to where we were.  */      if (! acting_as_filter)	move_archive (records_read - (records_written + records_skipped));    }  new_blocks = 0;}static voidwrite_recent_blocks (union block *h, size_t blocks){  size_t i;  for (i = 0; i < blocks; i++)    {      new_record[new_blocks++] = h[i];      if (new_blocks == blocking_factor)	write_record (1);    }}static voidwrite_recent_bytes (char *data, size_t bytes){  size_t blocks = bytes / BLOCKSIZE;  size_t rest = bytes - blocks * BLOCKSIZE;  write_recent_blocks ((union block *)data, blocks);  memcpy (new_record[new_blocks].buffer, data + blocks * BLOCKSIZE, rest);  if (rest < BLOCKSIZE)    memset (new_record[new_blocks].buffer + rest, 0, BLOCKSIZE - rest);  new_blocks++;  if (new_blocks == blocking_factor)    write_record (1);}voiddelete_archive_members (void){  enum read_header logical_status = HEADER_STILL_UNREAD;  enum read_header previous_status = HEADER_STILL_UNREAD;  /* FIXME: Should clean the routine before cleaning these variables :-( */  struct name *name;  off_t blocks_to_skip = 0;  off_t blocks_to_keep = 0;  int kept_blocks_in_record;  name_gather ();  open_archive (ACCESS_UPDATE);  acting_as_filter = strcmp (archive_name_array[0], "-") == 0;  do    {      enum read_header status = read_header (¤t_header,                                             ¤t_stat_info,                                             read_header_x_raw);      switch (status)	{	case HEADER_STILL_UNREAD:	  abort ();	case HEADER_SUCCESS:	  if ((name = name_scan (current_stat_info.file_name)) == NULL)	    {	      skip_member ();	      break;	    }	  name->found_count++;	  if (!ISFOUND(name))	    {	      skip_member ();	      break;	    }	  /* Fall through.  */	case HEADER_SUCCESS_EXTENDED:	  logical_status = status;	  break;	case HEADER_ZERO_BLOCK:	  if (ignore_zeros_option)	    {	      set_next_block_after (current_header);	      break;	    }	  /* Fall through.  */	case HEADER_END_OF_FILE:	  logical_status = HEADER_END_OF_FILE;	  break;	case HEADER_FAILURE:	  set_next_block_after (current_header);	  switch (previous_status)	    {	    case HEADER_STILL_UNREAD:	      WARN ((0, 0, _("This does not look like a tar archive")));	      /* Fall through.  */	    case HEADER_SUCCESS:	    case HEADER_SUCCESS_EXTENDED:	    case HEADER_ZERO_BLOCK:	      ERROR ((0, 0, _("Skipping to next header")));	      /* Fall through.  */	    case HEADER_FAILURE:	      break;	    case HEADER_END_OF_FILE:	      abort ();	    }	  break;	}      previous_status = status;    }  while (logical_status == HEADER_STILL_UNREAD);  records_skipped = records_read - 1;  new_record = xmalloc (record_size);  if (logical_status == HEADER_SUCCESS      || logical_status == HEADER_SUCCESS_EXTENDED)    {      write_archive_to_stdout = false;      /* Save away blocks before this one in this record.  */      new_blocks = current_block - record_start;      if (new_blocks)	memcpy (new_record, record_start, new_blocks * BLOCKSIZE);      if (logical_status == HEADER_SUCCESS)	{	  /* FIXME: Pheew!  This is crufty code!  */	  logical_status = HEADER_STILL_UNREAD;	  goto flush_file;	}      /* FIXME: Solaris 2.4 Sun cc (the ANSI one, not the old K&R) says:	 "delete.c", line 223: warning: loop not entered at top	 Reported by Bruno Haible.  */      while (1)	{	  enum read_header status;	  /* Fill in a record.  */	  if (current_block == record_end)	    flush_archive ();	  status = read_header (¤t_header, ¤t_stat_info,	                        read_header_auto);	  xheader_decode (¤t_stat_info);	  if (status == HEADER_ZERO_BLOCK && ignore_zeros_option)	    {	      set_next_block_after (current_header);	      continue;	    }	  if (status == HEADER_END_OF_FILE || status == HEADER_ZERO_BLOCK)	    {	      logical_status = HEADER_END_OF_FILE;	      break;	    }	  if (status == HEADER_FAILURE)	    {	      ERROR ((0, 0, _("Deleting non-header from archive")));	      set_next_block_after (current_header);	      continue;	    }	  /* Found another header.  */	  if ((name = name_scan (current_stat_info.file_name)) != NULL)	    {	      name->found_count++;	      if (ISFOUND(name))		{		flush_file:		  set_next_block_after (current_header);		  blocks_to_skip = (current_stat_info.stat.st_size				    + BLOCKSIZE - 1) / BLOCKSIZE;		  while (record_end - current_block <= blocks_to_skip)		    {		      blocks_to_skip -= (record_end - current_block);		      flush_archive ();		    }		  current_block += blocks_to_skip;		  blocks_to_skip = 0;		  continue;		}	    }	  /* Copy header.  */	  if (current_stat_info.xhdr.size)	    {	      write_recent_bytes (current_stat_info.xhdr.buffer,				  current_stat_info.xhdr.size);	    }	  else	    {	      write_recent_blocks (recent_long_name, recent_long_name_blocks);	      write_recent_blocks (recent_long_link, recent_long_link_blocks);	    }	  new_record[new_blocks] = *current_header;	  new_blocks++;	  blocks_to_keep	    = (current_stat_info.stat.st_size + BLOCKSIZE - 1) / BLOCKSIZE;	  set_next_block_after (current_header);	  if (new_blocks == blocking_factor)	    write_record (1);	  /* Copy data.  */	  kept_blocks_in_record = record_end - current_block;	  if (kept_blocks_in_record > blocks_to_keep)	    kept_blocks_in_record = blocks_to_keep;	  while (blocks_to_keep)	    {	      int count;	      if (current_block == record_end)		{		  flush_read ();		  current_block = record_start;		  kept_blocks_in_record = blocking_factor;		  if (kept_blocks_in_record > blocks_to_keep)		    kept_blocks_in_record = blocks_to_keep;		}	      count = kept_blocks_in_record;	      if (blocking_factor - new_blocks < count)		count = blocking_factor - new_blocks;	      if (! count)		abort ();	      memcpy (new_record + new_blocks, current_block, count * BLOCKSIZE);	      new_blocks += count;	      current_block += count;	      blocks_to_keep -= count;	      kept_blocks_in_record -= count;	      if (new_blocks == blocking_factor)		write_record (1);	    }	}      if (logical_status == HEADER_END_OF_FILE)	{	  /* Write the end of tape.  FIXME: we can't use write_eot here,	     as it gets confused when the input is at end of file.  */	  int total_zero_blocks = 0;	  do	    {	      int zero_blocks = blocking_factor - new_blocks;	      memset (new_record + new_blocks, 0, BLOCKSIZE * zero_blocks);	      total_zero_blocks += zero_blocks;	      write_record (total_zero_blocks < 2);	    }	  while (total_zero_blocks < 2);	}      if (! acting_as_filter && ! _isrmt (archive))	{	  if (sys_truncate (archive))	    truncate_warn (archive_name_array[0]);	}    }  free (new_record);  close_archive ();  names_notfound ();}
 |