(root)/
tar-1.35/
src/
delete.c
       1  /* Delete entries from a tar archive.
       2  
       3     Copyright 1988-2023 Free Software Foundation, Inc.
       4  
       5     This file is part of GNU tar.
       6  
       7     GNU tar is free software; you can redistribute it and/or modify
       8     it under the terms of the GNU General Public License as published by
       9     the Free Software Foundation; either version 3 of the License, or
      10     (at your option) any later version.
      11  
      12     GNU tar is distributed in the hope that it will be useful,
      13     but WITHOUT ANY WARRANTY; without even the implied warranty of
      14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      15     GNU General Public License for more details.
      16  
      17     You should have received a copy of the GNU General Public License
      18     along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
      19  
      20  #include <system.h>
      21  
      22  #include "common.h"
      23  #include <rmt.h>
      24  
      25  static union block *new_record;
      26  static int new_blocks;
      27  static bool acting_as_filter;
      28  
      29  /* FIXME: This module should not directly handle the following
      30     variables, instead, the interface should be cleaned up.  */
      31  extern union block *record_start;
      32  extern union block *record_end;
      33  extern union block *current_block;
      34  extern union block *recent_long_name;
      35  extern union block *recent_long_link;
      36  extern off_t records_read;
      37  
      38  /* The number of records skipped at the start of the archive, when
      39     passing over members that are not deleted.  */
      40  off_t records_skipped;
      41  
      42  /* Move archive descriptor by COUNT records worth.  If COUNT is
      43     positive we move forward, else we move negative.  If it's a tape,
      44     MTIOCTOP had better work.  If it's something else, we try to seek
      45     on it.  If we can't seek, we lose!  */
      46  static void
      47  move_archive (off_t count)
      48  {
      49    if (count == 0)
      50      return;
      51  
      52    if (mtioseek (false, count))
      53      return;
      54  
      55    off_t position0 = rmtlseek (archive, 0, SEEK_CUR), position = 0;
      56    if (0 <= position0)
      57      {
      58        /* Pretend the starting position is at the first record
      59  	 boundary after POSITION0.  This is useful at EOF after
      60  	 a short read.  */
      61        idx_t short_size = position0 % record_size;
      62        idx_t start_offset = short_size ? record_size - short_size : 0;
      63        off_t increment, move_start;
      64        if (INT_MULTIPLY_WRAPV (record_size, count, &increment)
      65  	  || INT_ADD_WRAPV (position0, start_offset, &move_start)
      66  	  || INT_ADD_WRAPV (move_start, increment, &position)
      67  	  || position < 0)
      68  	{
      69  	  ERROR ((0, EOVERFLOW, "lseek: %s", archive_name_array[0]));
      70  	  return;
      71  	}
      72        else if (rmtlseek (archive, position, SEEK_SET) == position)
      73  	return;
      74      }
      75    if (!_isrmt (archive))
      76      seek_error_details (archive_name_array[0], position);
      77  }
      78  
      79  /* Write out the record which has been filled.  If MOVE_BACK_FLAG,
      80     backspace to where we started.  */
      81  static void
      82  write_record (int move_back_flag)
      83  {
      84    union block *save_record = record_start;
      85    record_start = new_record;
      86  
      87    if (acting_as_filter)
      88      {
      89        archive = STDOUT_FILENO;
      90        flush_write ();
      91        archive = STDIN_FILENO;
      92      }
      93    else
      94      {
      95        move_archive ((records_written + records_skipped) - records_read);
      96        flush_write ();
      97      }
      98  
      99    record_start = save_record;
     100  
     101    if (move_back_flag)
     102      {
     103        /* Move the tape head back to where we were.  */
     104  
     105        if (! acting_as_filter)
     106  	move_archive (records_read - (records_written + records_skipped));
     107      }
     108  
     109    new_blocks = 0;
     110  }
     111  
     112  static void
     113  write_recent_blocks (union block *h, size_t blocks)
     114  {
     115    size_t i;
     116    for (i = 0; i < blocks; i++)
     117      {
     118        new_record[new_blocks++] = h[i];
     119        if (new_blocks == blocking_factor)
     120  	write_record (1);
     121      }
     122  }
     123  
     124  static void
     125  write_recent_bytes (char *data, size_t bytes)
     126  {
     127    size_t blocks = bytes / BLOCKSIZE;
     128    size_t rest = bytes - blocks * BLOCKSIZE;
     129  
     130    write_recent_blocks ((union block *)data, blocks);
     131    memcpy (new_record[new_blocks].buffer, data + blocks * BLOCKSIZE, rest);
     132    if (rest < BLOCKSIZE)
     133      memset (new_record[new_blocks].buffer + rest, 0, BLOCKSIZE - rest);
     134    new_blocks++;
     135    if (new_blocks == blocking_factor)
     136      write_record (1);
     137  }
     138  
     139  static void
     140  flush_file (void)
     141  {
     142    off_t blocks_to_skip;
     143  
     144    set_next_block_after (current_header);
     145    blocks_to_skip = (current_stat_info.stat.st_size
     146  			  + BLOCKSIZE - 1) / BLOCKSIZE;
     147  
     148    while (record_end - current_block <= blocks_to_skip)
     149      {
     150        blocks_to_skip -= (record_end - current_block);
     151        flush_archive ();
     152        if (record_end == current_block)
     153  	/* Hit EOF */
     154  	break;
     155      }
     156    current_block += blocks_to_skip;
     157  }
     158  
     159  void
     160  delete_archive_members (void)
     161  {
     162    enum read_header logical_status = HEADER_STILL_UNREAD;
     163    enum read_header previous_status = HEADER_STILL_UNREAD;
     164  
     165    /* FIXME: Should clean the routine before cleaning these variables :-( */
     166    struct name *name;
     167    off_t blocks_to_keep = 0;
     168    int kept_blocks_in_record;
     169  
     170    name_gather ();
     171    open_archive (ACCESS_UPDATE);
     172    acting_as_filter = strcmp (archive_name_array[0], "-") == 0;
     173  
     174    /* Skip to the first member that matches the name list. */
     175    do
     176      {
     177        enum read_header status = read_header (&current_header,
     178  					     &current_stat_info,
     179  					     read_header_x_raw);
     180  
     181        switch (status)
     182  	{
     183  	case HEADER_STILL_UNREAD:
     184  	  abort ();
     185  
     186  	case HEADER_SUCCESS:
     187  	  if ((name = name_scan (current_stat_info.file_name, false)) == NULL)
     188  	    {
     189  	      skim_member (acting_as_filter);
     190  	      break;
     191  	    }
     192  	  name->found_count++;
     193  	  if (!ISFOUND (name))
     194  	    {
     195  	      skim_member (acting_as_filter);
     196  	      break;
     197  	    }
     198  	  FALLTHROUGH;
     199  	case HEADER_SUCCESS_EXTENDED:
     200  	  logical_status = status;
     201  	  break;
     202  
     203  	case HEADER_ZERO_BLOCK:
     204  	  if (ignore_zeros_option)
     205  	    {
     206  	      set_next_block_after (current_header);
     207  	      break;
     208  	    }
     209  	  FALLTHROUGH;
     210  	case HEADER_END_OF_FILE:
     211  	  logical_status = HEADER_END_OF_FILE;
     212  	  break;
     213  
     214  	case HEADER_FAILURE:
     215  	  set_next_block_after (current_header);
     216  	  switch (previous_status)
     217  	    {
     218  	    case HEADER_STILL_UNREAD:
     219  	      WARN ((0, 0, _("This does not look like a tar archive")));
     220  	      FALLTHROUGH;
     221  	    case HEADER_SUCCESS:
     222  	    case HEADER_SUCCESS_EXTENDED:
     223  	    case HEADER_ZERO_BLOCK:
     224  	      ERROR ((0, 0, _("Skipping to next header")));
     225  	      FALLTHROUGH;
     226  	    case HEADER_FAILURE:
     227  	      break;
     228  
     229  	    case HEADER_END_OF_FILE:
     230  	      abort ();
     231  	    }
     232  	  break;
     233  	}
     234  
     235        previous_status = status;
     236      }
     237    while (logical_status == HEADER_STILL_UNREAD);
     238  
     239    records_skipped = records_read - 1;
     240    new_record = xmalloc (record_size);
     241  
     242    if (logical_status == HEADER_SUCCESS
     243        || logical_status == HEADER_SUCCESS_EXTENDED)
     244      {
     245        write_archive_to_stdout = false;
     246  
     247        /* Save away blocks before this one in this record.  */
     248  
     249        new_blocks = current_block - record_start;
     250        if (new_blocks)
     251  	memcpy (new_record, record_start, new_blocks * BLOCKSIZE);
     252  
     253        if (logical_status == HEADER_SUCCESS)
     254  	{
     255  	  logical_status = HEADER_STILL_UNREAD;
     256  	  flush_file ();
     257  	}
     258  
     259        /* Skip matching members and move the rest up the archive. */
     260        while (logical_status != HEADER_END_OF_FILE)
     261  	{
     262  	  enum read_header status;
     263  
     264  	  /* Fill in a record.  */
     265  
     266  	  if (current_block == record_end)
     267  	    flush_archive ();
     268  
     269  	  status = read_header (&current_header, &current_stat_info,
     270  				read_header_auto);
     271  
     272  	  switch (status)
     273  	    {
     274  	    case HEADER_STILL_UNREAD:
     275  	    case HEADER_SUCCESS_EXTENDED:
     276  	      abort ();
     277  
     278  	    case HEADER_SUCCESS:
     279  	      /* Found another header.  */
     280  	      xheader_decode (&current_stat_info);
     281  
     282  	      if ((name = name_scan (current_stat_info.file_name, false)) != NULL)
     283  		{
     284  		  name->found_count++;
     285  		  if (ISFOUND (name))
     286  		    {
     287  		      flush_file ();
     288  		      break;
     289  		    }
     290  		}
     291  	      /* Copy header.  */
     292  
     293  	      if (current_stat_info.xhdr.size)
     294  		{
     295  		  write_recent_bytes (current_stat_info.xhdr.buffer,
     296  				      current_stat_info.xhdr.size);
     297  		}
     298  	      else
     299  		{
     300  		  write_recent_blocks (recent_long_name,
     301  				       recent_long_name_blocks);
     302  		  write_recent_blocks (recent_long_link,
     303  				       recent_long_link_blocks);
     304  		}
     305  	      new_record[new_blocks] = *current_header;
     306  	      new_blocks++;
     307  	      blocks_to_keep
     308  		= (current_stat_info.stat.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
     309  	      set_next_block_after (current_header);
     310  	      if (new_blocks == blocking_factor)
     311  		write_record (1);
     312  
     313  	      /* Copy data.  */
     314  
     315  	      kept_blocks_in_record = record_end - current_block;
     316  	      if (kept_blocks_in_record > blocks_to_keep)
     317  		kept_blocks_in_record = blocks_to_keep;
     318  
     319  	      while (blocks_to_keep)
     320  		{
     321  		  int count;
     322  
     323  		  if (current_block == record_end)
     324  		    {
     325  		      flush_read ();
     326  		      current_block = record_start;
     327  		      kept_blocks_in_record = blocking_factor;
     328  		      if (kept_blocks_in_record > blocks_to_keep)
     329  			kept_blocks_in_record = blocks_to_keep;
     330  		    }
     331  		  count = kept_blocks_in_record;
     332  		  if (blocking_factor - new_blocks < count)
     333  		    count = blocking_factor - new_blocks;
     334  
     335  		  if (! count)
     336  		    abort ();
     337  
     338  		  memcpy (new_record + new_blocks, current_block,
     339  			  count * BLOCKSIZE);
     340  		  new_blocks += count;
     341  		  current_block += count;
     342  		  blocks_to_keep -= count;
     343  		  kept_blocks_in_record -= count;
     344  
     345  		  if (new_blocks == blocking_factor)
     346  		    write_record (1);
     347  		}
     348  	      break;
     349  
     350  	    case HEADER_ZERO_BLOCK:
     351  	      if (ignore_zeros_option)
     352  		set_next_block_after (current_header);
     353  	      else
     354  		logical_status = HEADER_END_OF_FILE;
     355  	      break;
     356  
     357  	    case HEADER_END_OF_FILE:
     358  	      logical_status = HEADER_END_OF_FILE;
     359  	      break;
     360  
     361  	    case HEADER_FAILURE:
     362  	      ERROR ((0, 0, _("Deleting non-header from archive")));
     363  	      set_next_block_after (current_header);
     364  	      break;
     365  
     366  	    default:
     367  	      abort ();
     368  	    }
     369  	  tar_stat_destroy (&current_stat_info);
     370  	}
     371  
     372        if (logical_status == HEADER_END_OF_FILE)
     373  	{
     374  	  /* Write the end of tape.  FIXME: we can't use write_eot here,
     375  	     as it gets confused when the input is at end of file.  */
     376  
     377  	  int total_zero_blocks = 0;
     378  
     379  	  do
     380  	    {
     381  	      int zero_blocks = blocking_factor - new_blocks;
     382  	      memset (new_record + new_blocks, 0, BLOCKSIZE * zero_blocks);
     383  	      total_zero_blocks += zero_blocks;
     384  	      write_record (total_zero_blocks < 2);
     385  	    }
     386  	  while (total_zero_blocks < 2);
     387  	}
     388  
     389        if (! acting_as_filter && ! _isrmt (archive))
     390  	{
     391  	  if (sys_truncate (archive))
     392  	    truncate_warn (archive_name_array[0]);
     393  	}
     394      }
     395    free (new_record);
     396  
     397    close_archive ();
     398    names_notfound ();
     399  }