]> git.ipfire.org Git - thirdparty/tar.git/blame - src/delete.c
Fix --delete bug with short reads
[thirdparty/tar.git] / src / delete.c
CommitLineData
3e3d9b7e 1/* Delete entries from a tar archive.
d858a433 2
5c713540 3 Copyright 1988-2022 Free Software Foundation, Inc.
3e3d9b7e 4
cd7bdd40 5 This file is part of GNU tar.
3e3d9b7e 6
cd7bdd40
PE
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.
3e3d9b7e 11
cd7bdd40
PE
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/>. */
3e3d9b7e 19
7347b4f5 20#include <system.h>
3e3d9b7e 21
3e3d9b7e 22#include "common.h"
7347b4f5 23#include <rmt.h>
3e3d9b7e 24
d858a433
SP
25static union block *new_record;
26static int new_blocks;
27static bool acting_as_filter;
3e3d9b7e 28
d858a433
SP
29/* FIXME: This module should not directly handle the following
30 variables, instead, the interface should be cleaned up. */
3e3d9b7e
PE
31extern union block *record_start;
32extern union block *record_end;
33extern union block *current_block;
d858a433
SP
34extern union block *recent_long_name;
35extern union block *recent_long_link;
f2bf9f2f 36extern off_t records_read;
d858a433
SP
37
38/* The number of records skipped at the start of the archive, when
39 passing over members that are not deleted. */
05b250d4 40off_t records_skipped;
d858a433
SP
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! */
3e3d9b7e 46static void
d858a433 47move_archive (off_t count)
3e3d9b7e 48{
d858a433
SP
49 if (count == 0)
50 return;
51
66be5a78
PE
52 if (mtioseek (false, count))
53 return;
3e3d9b7e 54
66be5a78
PE
55 off_t position0 = rmtlseek (archive, 0, SEEK_CUR), position = 0;
56 if (0 <= position0)
57 {
f8e14746
PE
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;
66be5a78 64 if (INT_MULTIPLY_WRAPV (record_size, count, &increment)
f8e14746
PE
65 || INT_ADD_WRAPV (position0, start_offset, &move_start)
66 || INT_ADD_WRAPV (move_start, increment, &position)
66be5a78
PE
67 || position < 0)
68 {
69 ERROR ((0, EOVERFLOW, "lseek: %s", archive_name_array[0]));
d858a433 70 return;
66be5a78
PE
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);
3e3d9b7e
PE
77}
78
d858a433
SP
79/* Write out the record which has been filled. If MOVE_BACK_FLAG,
80 backspace to where we started. */
3e3d9b7e
PE
81static void
82write_record (int move_back_flag)
83{
d858a433 84 union block *save_record = record_start;
3e3d9b7e
PE
85 record_start = new_record;
86
d858a433 87 if (acting_as_filter)
3e3d9b7e 88 {
d858a433 89 archive = STDOUT_FILENO;
3e3d9b7e 90 flush_write ();
d858a433 91 archive = STDIN_FILENO;
3e3d9b7e
PE
92 }
93 else
94 {
d858a433 95 move_archive ((records_written + records_skipped) - records_read);
3e3d9b7e
PE
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
d858a433
SP
105 if (! acting_as_filter)
106 move_archive (records_read - (records_written + records_skipped));
3e3d9b7e
PE
107 }
108
3e3d9b7e
PE
109 new_blocks = 0;
110}
111
d858a433
SP
112static void
113write_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}
3e3d9b7e 123
08780d28
SP
124static void
125write_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
7fb1b687 139static void
8e2898ab
SP
140flush_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 }
153 current_block += blocks_to_skip;
154}
155
3e3d9b7e
PE
156void
157delete_archive_members (void)
158{
159 enum read_header logical_status = HEADER_STILL_UNREAD;
160 enum read_header previous_status = HEADER_STILL_UNREAD;
161
162 /* FIXME: Should clean the routine before cleaning these variables :-( */
163 struct name *name;
d858a433 164 off_t blocks_to_keep = 0;
3e3d9b7e
PE
165 int kept_blocks_in_record;
166
167 name_gather ();
168 open_archive (ACCESS_UPDATE);
d858a433 169 acting_as_filter = strcmp (archive_name_array[0], "-") == 0;
3e3d9b7e 170
8e2898ab 171 /* Skip to the first member that matches the name list. */
d858a433 172 do
3e3d9b7e 173 {
166b7c7d 174 enum read_header status = read_header (&current_header,
8e2898ab
SP
175 &current_stat_info,
176 read_header_x_raw);
3e3d9b7e
PE
177
178 switch (status)
179 {
180 case HEADER_STILL_UNREAD:
181 abort ();
182
183 case HEADER_SUCCESS:
6d1e9ab6 184 if ((name = name_scan (current_stat_info.file_name)) == NULL)
3e3d9b7e 185 {
d858a433 186 skip_member ();
3e3d9b7e
PE
187 break;
188 }
f4935ab5 189 name->found_count++;
8e2898ab 190 if (!ISFOUND (name))
f4935ab5
SP
191 {
192 skip_member ();
193 break;
194 }
d44d5920 195 FALLTHROUGH;
d858a433
SP
196 case HEADER_SUCCESS_EXTENDED:
197 logical_status = status;
3e3d9b7e
PE
198 break;
199
200 case HEADER_ZERO_BLOCK:
d858a433
SP
201 if (ignore_zeros_option)
202 {
203 set_next_block_after (current_header);
204 break;
205 }
d44d5920 206 FALLTHROUGH;
3e3d9b7e
PE
207 case HEADER_END_OF_FILE:
208 logical_status = HEADER_END_OF_FILE;
209 break;
210
211 case HEADER_FAILURE:
212 set_next_block_after (current_header);
213 switch (previous_status)
214 {
215 case HEADER_STILL_UNREAD:
216 WARN ((0, 0, _("This does not look like a tar archive")));
d44d5920 217 FALLTHROUGH;
3e3d9b7e 218 case HEADER_SUCCESS:
86a9b926 219 case HEADER_SUCCESS_EXTENDED:
3e3d9b7e
PE
220 case HEADER_ZERO_BLOCK:
221 ERROR ((0, 0, _("Skipping to next header")));
d44d5920 222 FALLTHROUGH;
3e3d9b7e
PE
223 case HEADER_FAILURE:
224 break;
225
226 case HEADER_END_OF_FILE:
227 abort ();
228 }
229 break;
230 }
231
232 previous_status = status;
233 }
d858a433 234 while (logical_status == HEADER_STILL_UNREAD);
3e3d9b7e 235
d858a433
SP
236 records_skipped = records_read - 1;
237 new_record = xmalloc (record_size);
3e3d9b7e 238
d858a433
SP
239 if (logical_status == HEADER_SUCCESS
240 || logical_status == HEADER_SUCCESS_EXTENDED)
3e3d9b7e 241 {
829b1dc3 242 write_archive_to_stdout = false;
3e3d9b7e 243
d858a433 244 /* Save away blocks before this one in this record. */
3e3d9b7e 245
d858a433
SP
246 new_blocks = current_block - record_start;
247 if (new_blocks)
248 memcpy (new_record, record_start, new_blocks * BLOCKSIZE);
3e3d9b7e 249
d858a433 250 if (logical_status == HEADER_SUCCESS)
3e3d9b7e 251 {
d858a433 252 logical_status = HEADER_STILL_UNREAD;
8e2898ab 253 flush_file ();
3e3d9b7e
PE
254 }
255
8e2898ab
SP
256 /* Skip matching members and move the rest up the archive. */
257 while (logical_status != HEADER_END_OF_FILE)
3e3d9b7e 258 {
d858a433 259 enum read_header status;
3e3d9b7e 260
d858a433 261 /* Fill in a record. */
3e3d9b7e 262
d858a433
SP
263 if (current_block == record_end)
264 flush_archive ();
f2bf9f2f 265
8e2898ab
SP
266 status = read_header (&current_header, &current_stat_info,
267 read_header_auto);
3e3d9b7e 268
8e2898ab 269 switch (status)
d858a433 270 {
8e2898ab
SP
271 case HEADER_STILL_UNREAD:
272 case HEADER_SUCCESS_EXTENDED:
273 abort ();
3e3d9b7e 274
8e2898ab
SP
275 case HEADER_SUCCESS:
276 /* Found another header. */
277 xheader_decode (&current_stat_info);
d858a433 278
8e2898ab 279 if ((name = name_scan (current_stat_info.file_name)) != NULL)
d858a433 280 {
8e2898ab
SP
281 name->found_count++;
282 if (ISFOUND (name))
f4935ab5 283 {
8e2898ab
SP
284 flush_file ();
285 break;
f4935ab5 286 }
d858a433 287 }
8e2898ab 288 /* Copy header. */
3e3d9b7e 289
8e2898ab
SP
290 if (current_stat_info.xhdr.size)
291 {
292 write_recent_bytes (current_stat_info.xhdr.buffer,
293 current_stat_info.xhdr.size);
294 }
295 else
296 {
297 write_recent_blocks (recent_long_name,
298 recent_long_name_blocks);
299 write_recent_blocks (recent_long_link,
300 recent_long_link_blocks);
301 }
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);
3e3d9b7e 309
8e2898ab 310 /* Copy data. */
d858a433 311
8e2898ab
SP
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;
d858a433 315
8e2898ab 316 while (blocks_to_keep)
d858a433 317 {
8e2898ab
SP
318 int count;
319
320 if (current_block == record_end)
321 {
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;
327 }
328 count = kept_blocks_in_record;
329 if (blocking_factor - new_blocks < count)
330 count = blocking_factor - new_blocks;
331
332 if (! count)
333 abort ();
334
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;
341
342 if (new_blocks == blocking_factor)
343 write_record (1);
d858a433 344 }
8e2898ab 345 break;
d858a433 346
8e2898ab
SP
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;
d858a433 353
8e2898ab
SP
354 case HEADER_END_OF_FILE:
355 logical_status = HEADER_END_OF_FILE;
356 break;
d858a433 357
8e2898ab
SP
358 case HEADER_FAILURE:
359 ERROR ((0, 0, _("Deleting non-header from archive")));
360 set_next_block_after (current_header);
361 break;
362
363 default:
364 abort ();
3e3d9b7e 365 }
8e2898ab 366 tar_stat_destroy (&current_stat_info);
3e3d9b7e 367 }
3e3d9b7e 368
e6a67c2e
SP
369 if (logical_status == HEADER_END_OF_FILE)
370 {
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. */
d858a433 373
e6a67c2e 374 int total_zero_blocks = 0;
d858a433 375
e6a67c2e
SP
376 do
377 {
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);
382 }
383 while (total_zero_blocks < 2);
384 }
385
386 if (! acting_as_filter && ! _isrmt (archive))
d858a433 387 {
e6a67c2e
SP
388 if (sys_truncate (archive))
389 truncate_warn (archive_name_array[0]);
d858a433 390 }
d858a433 391 }
d858a433
SP
392 free (new_record);
393
3e3d9b7e
PE
394 close_archive ();
395 names_notfound ();
396}