Fix savannah bug #63250
[tar.git] / src / delete.c
blobdd6bc213afe37f338bf071ad264712f8f21f2a57
1 /* Delete entries from a tar archive.
3 Copyright 1988-2022 Free Software Foundation, Inc.
5 This file is part of GNU tar.
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.
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.
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/>. */
20 #include <system.h>
22 #include "common.h"
23 #include <rmt.h>
25 static union block *new_record;
26 static int new_blocks;
27 static bool acting_as_filter;
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;
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;
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)
49 if (count == 0)
50 return;
52 if (mtioseek (false, count))
53 return;
55 off_t position0 = rmtlseek (archive, 0, SEEK_CUR), position = 0;
56 if (0 <= position0)
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)
69 ERROR ((0, EOVERFLOW, "lseek: %s", archive_name_array[0]));
70 return;
72 else if (rmtlseek (archive, position, SEEK_SET) == position)
73 return;
75 if (!_isrmt (archive))
76 seek_error_details (archive_name_array[0], position);
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)
84 union block *save_record = record_start;
85 record_start = new_record;
87 if (acting_as_filter)
89 archive = STDOUT_FILENO;
90 flush_write ();
91 archive = STDIN_FILENO;
93 else
95 move_archive ((records_written + records_skipped) - records_read);
96 flush_write ();
99 record_start = save_record;
101 if (move_back_flag)
103 /* Move the tape head back to where we were. */
105 if (! acting_as_filter)
106 move_archive (records_read - (records_written + records_skipped));
109 new_blocks = 0;
112 static void
113 write_recent_blocks (union block *h, size_t blocks)
115 size_t i;
116 for (i = 0; i < blocks; i++)
118 new_record[new_blocks++] = h[i];
119 if (new_blocks == blocking_factor)
120 write_record (1);
124 static void
125 write_recent_bytes (char *data, size_t bytes)
127 size_t blocks = bytes / BLOCKSIZE;
128 size_t rest = bytes - blocks * BLOCKSIZE;
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);
139 static void
140 flush_file (void)
142 off_t blocks_to_skip;
144 set_next_block_after (current_header);
145 blocks_to_skip = (current_stat_info.stat.st_size
146 + BLOCKSIZE - 1) / BLOCKSIZE;
148 while (record_end - current_block <= blocks_to_skip)
150 blocks_to_skip -= (record_end - current_block);
151 flush_archive ();
153 current_block += blocks_to_skip;
156 void
157 delete_archive_members (void)
159 enum read_header logical_status = HEADER_STILL_UNREAD;
160 enum read_header previous_status = HEADER_STILL_UNREAD;
162 /* FIXME: Should clean the routine before cleaning these variables :-( */
163 struct name *name;
164 off_t blocks_to_keep = 0;
165 int kept_blocks_in_record;
167 name_gather ();
168 open_archive (ACCESS_UPDATE);
169 acting_as_filter = strcmp (archive_name_array[0], "-") == 0;
171 /* Skip to the first member that matches the name list. */
174 enum read_header status = read_header (&current_header,
175 &current_stat_info,
176 read_header_x_raw);
178 switch (status)
180 case HEADER_STILL_UNREAD:
181 abort ();
183 case HEADER_SUCCESS:
184 if ((name = name_scan (current_stat_info.file_name)) == NULL)
186 skim_member (acting_as_filter);
187 break;
189 name->found_count++;
190 if (!ISFOUND (name))
192 skim_member (acting_as_filter);
193 break;
195 FALLTHROUGH;
196 case HEADER_SUCCESS_EXTENDED:
197 logical_status = status;
198 break;
200 case HEADER_ZERO_BLOCK:
201 if (ignore_zeros_option)
203 set_next_block_after (current_header);
204 break;
206 FALLTHROUGH;
207 case HEADER_END_OF_FILE:
208 logical_status = HEADER_END_OF_FILE;
209 break;
211 case HEADER_FAILURE:
212 set_next_block_after (current_header);
213 switch (previous_status)
215 case HEADER_STILL_UNREAD:
216 WARN ((0, 0, _("This does not look like a tar archive")));
217 FALLTHROUGH;
218 case HEADER_SUCCESS:
219 case HEADER_SUCCESS_EXTENDED:
220 case HEADER_ZERO_BLOCK:
221 ERROR ((0, 0, _("Skipping to next header")));
222 FALLTHROUGH;
223 case HEADER_FAILURE:
224 break;
226 case HEADER_END_OF_FILE:
227 abort ();
229 break;
232 previous_status = status;
234 while (logical_status == HEADER_STILL_UNREAD);
236 records_skipped = records_read - 1;
237 new_record = xmalloc (record_size);
239 if (logical_status == HEADER_SUCCESS
240 || logical_status == HEADER_SUCCESS_EXTENDED)
242 write_archive_to_stdout = false;
244 /* Save away blocks before this one in this record. */
246 new_blocks = current_block - record_start;
247 if (new_blocks)
248 memcpy (new_record, record_start, new_blocks * BLOCKSIZE);
250 if (logical_status == HEADER_SUCCESS)
252 logical_status = HEADER_STILL_UNREAD;
253 flush_file ();
256 /* Skip matching members and move the rest up the archive. */
257 while (logical_status != HEADER_END_OF_FILE)
259 enum read_header status;
261 /* Fill in a record. */
263 if (current_block == record_end)
264 flush_archive ();
266 status = read_header (&current_header, &current_stat_info,
267 read_header_auto);
269 switch (status)
271 case HEADER_STILL_UNREAD:
272 case HEADER_SUCCESS_EXTENDED:
273 abort ();
275 case HEADER_SUCCESS:
276 /* Found another header. */
277 xheader_decode (&current_stat_info);
279 if ((name = name_scan (current_stat_info.file_name)) != NULL)
281 name->found_count++;
282 if (ISFOUND (name))
284 flush_file ();
285 break;
288 /* Copy header. */
290 if (current_stat_info.xhdr.size)
292 write_recent_bytes (current_stat_info.xhdr.buffer,
293 current_stat_info.xhdr.size);
295 else
297 write_recent_blocks (recent_long_name,
298 recent_long_name_blocks);
299 write_recent_blocks (recent_long_link,
300 recent_long_link_blocks);
302 new_record[new_blocks] = *current_header;
303 new_blocks++;
304 blocks_to_keep
305 = (current_stat_info.stat.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
306 set_next_block_after (current_header);
307 if (new_blocks == blocking_factor)
308 write_record (1);
310 /* Copy data. */
312 kept_blocks_in_record = record_end - current_block;
313 if (kept_blocks_in_record > blocks_to_keep)
314 kept_blocks_in_record = blocks_to_keep;
316 while (blocks_to_keep)
318 int count;
320 if (current_block == record_end)
322 flush_read ();
323 current_block = record_start;
324 kept_blocks_in_record = blocking_factor;
325 if (kept_blocks_in_record > blocks_to_keep)
326 kept_blocks_in_record = blocks_to_keep;
328 count = kept_blocks_in_record;
329 if (blocking_factor - new_blocks < count)
330 count = blocking_factor - new_blocks;
332 if (! count)
333 abort ();
335 memcpy (new_record + new_blocks, current_block,
336 count * BLOCKSIZE);
337 new_blocks += count;
338 current_block += count;
339 blocks_to_keep -= count;
340 kept_blocks_in_record -= count;
342 if (new_blocks == blocking_factor)
343 write_record (1);
345 break;
347 case HEADER_ZERO_BLOCK:
348 if (ignore_zeros_option)
349 set_next_block_after (current_header);
350 else
351 logical_status = HEADER_END_OF_FILE;
352 break;
354 case HEADER_END_OF_FILE:
355 logical_status = HEADER_END_OF_FILE;
356 break;
358 case HEADER_FAILURE:
359 ERROR ((0, 0, _("Deleting non-header from archive")));
360 set_next_block_after (current_header);
361 break;
363 default:
364 abort ();
366 tar_stat_destroy (&current_stat_info);
369 if (logical_status == HEADER_END_OF_FILE)
371 /* Write the end of tape. FIXME: we can't use write_eot here,
372 as it gets confused when the input is at end of file. */
374 int total_zero_blocks = 0;
378 int zero_blocks = blocking_factor - new_blocks;
379 memset (new_record + new_blocks, 0, BLOCKSIZE * zero_blocks);
380 total_zero_blocks += zero_blocks;
381 write_record (total_zero_blocks < 2);
383 while (total_zero_blocks < 2);
386 if (! acting_as_filter && ! _isrmt (archive))
388 if (sys_truncate (archive))
389 truncate_warn (archive_name_array[0]);
392 free (new_record);
394 close_archive ();
395 names_notfound ();