]> git.ipfire.org Git - thirdparty/tar.git/blame - src/delete.c
tar: fix current_block confusion
[thirdparty/tar.git] / src / delete.c
CommitLineData
3e3d9b7e 1/* Delete entries from a tar archive.
d858a433 2
c6f0ad51 3 Copyright 1988-2024 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{
8e2898ab 142 set_next_block_after (current_header);
628c4925
PE
143 off_t size = current_stat_info.stat.st_size;
144 off_t blocks_to_skip = size / BLOCKSIZE + (size % BLOCKSIZE != 0);
8e2898ab
SP
145
146 while (record_end - current_block <= blocks_to_skip)
147 {
148 blocks_to_skip -= (record_end - current_block);
149 flush_archive ();
4177c98b
SP
150 if (record_end == current_block)
151 /* Hit EOF */
883f2e6d 152 return;
8e2898ab
SP
153 }
154 current_block += blocks_to_skip;
155}
156
3e3d9b7e
PE
157void
158delete_archive_members (void)
159{
160 enum read_header logical_status = HEADER_STILL_UNREAD;
161 enum read_header previous_status = HEADER_STILL_UNREAD;
162
163 /* FIXME: Should clean the routine before cleaning these variables :-( */
164 struct name *name;
d858a433 165 off_t blocks_to_keep = 0;
3e3d9b7e
PE
166 int kept_blocks_in_record;
167
168 name_gather ();
169 open_archive (ACCESS_UPDATE);
d858a433 170 acting_as_filter = strcmp (archive_name_array[0], "-") == 0;
3e3d9b7e 171
8e2898ab 172 /* Skip to the first member that matches the name list. */
d858a433 173 do
3e3d9b7e 174 {
166b7c7d 175 enum read_header status = read_header (&current_header,
8e2898ab
SP
176 &current_stat_info,
177 read_header_x_raw);
3e3d9b7e
PE
178
179 switch (status)
180 {
181 case HEADER_STILL_UNREAD:
182 abort ();
183
184 case HEADER_SUCCESS:
10954cf1 185 if ((name = name_scan (current_stat_info.file_name, false)) == NULL)
3e3d9b7e 186 {
bc277c70 187 skim_member (acting_as_filter);
3e3d9b7e
PE
188 break;
189 }
f4935ab5 190 name->found_count++;
8e2898ab 191 if (!ISFOUND (name))
f4935ab5 192 {
bc277c70 193 skim_member (acting_as_filter);
f4935ab5
SP
194 break;
195 }
d44d5920 196 FALLTHROUGH;
d858a433
SP
197 case HEADER_SUCCESS_EXTENDED:
198 logical_status = status;
3e3d9b7e
PE
199 break;
200
201 case HEADER_ZERO_BLOCK:
d858a433
SP
202 if (ignore_zeros_option)
203 {
204 set_next_block_after (current_header);
205 break;
206 }
d44d5920 207 FALLTHROUGH;
3e3d9b7e
PE
208 case HEADER_END_OF_FILE:
209 logical_status = HEADER_END_OF_FILE;
210 break;
211
212 case HEADER_FAILURE:
213 set_next_block_after (current_header);
214 switch (previous_status)
215 {
216 case HEADER_STILL_UNREAD:
217 WARN ((0, 0, _("This does not look like a tar archive")));
d44d5920 218 FALLTHROUGH;
3e3d9b7e 219 case HEADER_SUCCESS:
86a9b926 220 case HEADER_SUCCESS_EXTENDED:
3e3d9b7e
PE
221 case HEADER_ZERO_BLOCK:
222 ERROR ((0, 0, _("Skipping to next header")));
d44d5920 223 FALLTHROUGH;
3e3d9b7e
PE
224 case HEADER_FAILURE:
225 break;
226
227 case HEADER_END_OF_FILE:
228 abort ();
229 }
230 break;
231 }
232
233 previous_status = status;
234 }
d858a433 235 while (logical_status == HEADER_STILL_UNREAD);
3e3d9b7e 236
d858a433
SP
237 records_skipped = records_read - 1;
238 new_record = xmalloc (record_size);
3e3d9b7e 239
d858a433
SP
240 if (logical_status == HEADER_SUCCESS
241 || logical_status == HEADER_SUCCESS_EXTENDED)
3e3d9b7e 242 {
829b1dc3 243 write_archive_to_stdout = false;
3e3d9b7e 244
d858a433 245 /* Save away blocks before this one in this record. */
3e3d9b7e 246
d858a433
SP
247 new_blocks = current_block - record_start;
248 if (new_blocks)
249 memcpy (new_record, record_start, new_blocks * BLOCKSIZE);
3e3d9b7e 250
d858a433 251 if (logical_status == HEADER_SUCCESS)
3e3d9b7e 252 {
d858a433 253 logical_status = HEADER_STILL_UNREAD;
8e2898ab 254 flush_file ();
3e3d9b7e
PE
255 }
256
8e2898ab
SP
257 /* Skip matching members and move the rest up the archive. */
258 while (logical_status != HEADER_END_OF_FILE)
3e3d9b7e 259 {
d858a433 260 enum read_header status;
3e3d9b7e 261
d858a433 262 /* Fill in a record. */
3e3d9b7e 263
d858a433
SP
264 if (current_block == record_end)
265 flush_archive ();
f2bf9f2f 266
8e2898ab
SP
267 status = read_header (&current_header, &current_stat_info,
268 read_header_auto);
3e3d9b7e 269
8e2898ab 270 switch (status)
d858a433 271 {
8e2898ab
SP
272 case HEADER_STILL_UNREAD:
273 case HEADER_SUCCESS_EXTENDED:
274 abort ();
3e3d9b7e 275
8e2898ab
SP
276 case HEADER_SUCCESS:
277 /* Found another header. */
278 xheader_decode (&current_stat_info);
d858a433 279
10954cf1 280 if ((name = name_scan (current_stat_info.file_name, false)) != NULL)
d858a433 281 {
8e2898ab
SP
282 name->found_count++;
283 if (ISFOUND (name))
f4935ab5 284 {
8e2898ab
SP
285 flush_file ();
286 break;
f4935ab5 287 }
d858a433 288 }
8e2898ab 289 /* Copy header. */
3e3d9b7e 290
8e2898ab
SP
291 if (current_stat_info.xhdr.size)
292 {
293 write_recent_bytes (current_stat_info.xhdr.buffer,
294 current_stat_info.xhdr.size);
295 }
296 else
297 {
298 write_recent_blocks (recent_long_name,
299 recent_long_name_blocks);
300 write_recent_blocks (recent_long_link,
301 recent_long_link_blocks);
302 }
303 new_record[new_blocks] = *current_header;
304 new_blocks++;
305 blocks_to_keep
306 = (current_stat_info.stat.st_size + BLOCKSIZE - 1) / BLOCKSIZE;
307 set_next_block_after (current_header);
308 if (new_blocks == blocking_factor)
309 write_record (1);
3e3d9b7e 310
8e2898ab 311 /* Copy data. */
d858a433 312
8e2898ab
SP
313 kept_blocks_in_record = record_end - current_block;
314 if (kept_blocks_in_record > blocks_to_keep)
315 kept_blocks_in_record = blocks_to_keep;
d858a433 316
8e2898ab 317 while (blocks_to_keep)
d858a433 318 {
8e2898ab
SP
319 int count;
320
321 if (current_block == record_end)
322 {
323 flush_read ();
324 current_block = record_start;
325 kept_blocks_in_record = blocking_factor;
326 if (kept_blocks_in_record > blocks_to_keep)
327 kept_blocks_in_record = blocks_to_keep;
328 }
329 count = kept_blocks_in_record;
330 if (blocking_factor - new_blocks < count)
331 count = blocking_factor - new_blocks;
332
333 if (! count)
334 abort ();
335
336 memcpy (new_record + new_blocks, current_block,
337 count * BLOCKSIZE);
338 new_blocks += count;
339 current_block += count;
340 blocks_to_keep -= count;
341 kept_blocks_in_record -= count;
342
343 if (new_blocks == blocking_factor)
344 write_record (1);
d858a433 345 }
8e2898ab 346 break;
d858a433 347
8e2898ab
SP
348 case HEADER_ZERO_BLOCK:
349 if (ignore_zeros_option)
350 set_next_block_after (current_header);
351 else
352 logical_status = HEADER_END_OF_FILE;
353 break;
d858a433 354
8e2898ab
SP
355 case HEADER_END_OF_FILE:
356 logical_status = HEADER_END_OF_FILE;
357 break;
d858a433 358
8e2898ab
SP
359 case HEADER_FAILURE:
360 ERROR ((0, 0, _("Deleting non-header from archive")));
361 set_next_block_after (current_header);
362 break;
363
364 default:
365 abort ();
3e3d9b7e 366 }
8e2898ab 367 tar_stat_destroy (&current_stat_info);
3e3d9b7e 368 }
3e3d9b7e 369
e6a67c2e
SP
370 if (logical_status == HEADER_END_OF_FILE)
371 {
372 /* Write the end of tape. FIXME: we can't use write_eot here,
373 as it gets confused when the input is at end of file. */
d858a433 374
e6a67c2e 375 int total_zero_blocks = 0;
d858a433 376
e6a67c2e
SP
377 do
378 {
379 int zero_blocks = blocking_factor - new_blocks;
380 memset (new_record + new_blocks, 0, BLOCKSIZE * zero_blocks);
381 total_zero_blocks += zero_blocks;
382 write_record (total_zero_blocks < 2);
383 }
384 while (total_zero_blocks < 2);
385 }
386
387 if (! acting_as_filter && ! _isrmt (archive))
d858a433 388 {
e6a67c2e
SP
389 if (sys_truncate (archive))
390 truncate_warn (archive_name_array[0]);
d858a433 391 }
d858a433 392 }
d858a433
SP
393 free (new_record);
394
3e3d9b7e
PE
395 close_archive ();
396 names_notfound ();
397}