]> git.ipfire.org Git - thirdparty/tar.git/blame - src/delete.c
Sync bootstrap from Gnulib
[thirdparty/tar.git] / src / delete.c
CommitLineData
3e3d9b7e 1/* Delete entries from a tar archive.
d858a433 2
0aa991f3 3 Copyright 1988-2025 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 25static union block *new_record;
3ffe2eb0 26static idx_t new_blocks;
d858a433 27static bool acting_as_filter;
3e3d9b7e 28
d858a433
SP
29/* The number of records skipped at the start of the archive, when
30 passing over members that are not deleted. */
05b250d4 31off_t records_skipped;
d858a433
SP
32
33/* Move archive descriptor by COUNT records worth. If COUNT is
34 positive we move forward, else we move negative. If it's a tape,
35 MTIOCTOP had better work. If it's something else, we try to seek
36 on it. If we can't seek, we lose! */
3e3d9b7e 37static void
d858a433 38move_archive (off_t count)
3e3d9b7e 39{
d858a433
SP
40 if (count == 0)
41 return;
42
66be5a78
PE
43 if (mtioseek (false, count))
44 return;
3e3d9b7e 45
66be5a78
PE
46 off_t position0 = rmtlseek (archive, 0, SEEK_CUR), position = 0;
47 if (0 <= position0)
48 {
f8e14746
PE
49 /* Pretend the starting position is at the first record
50 boundary after POSITION0. This is useful at EOF after
51 a short read. */
52 idx_t short_size = position0 % record_size;
53 idx_t start_offset = short_size ? record_size - short_size : 0;
54 off_t increment, move_start;
a9372cf0
PE
55 if (ckd_mul (&increment, record_size, count)
56 || ckd_add (&move_start, position0, start_offset)
57 || ckd_add (&position, move_start, increment)
66be5a78
PE
58 || position < 0)
59 {
0dfcfa4a 60 paxerror (EOVERFLOW, "lseek: %s", archive_name_array[0]);
d858a433 61 return;
66be5a78
PE
62 }
63 else if (rmtlseek (archive, position, SEEK_SET) == position)
64 return;
65 }
66 if (!_isrmt (archive))
67 seek_error_details (archive_name_array[0], position);
3e3d9b7e
PE
68}
69
d858a433
SP
70/* Write out the record which has been filled. If MOVE_BACK_FLAG,
71 backspace to where we started. */
3e3d9b7e 72static void
fd401e1d 73write_record (bool move_back_flag)
3e3d9b7e 74{
d858a433 75 union block *save_record = record_start;
3e3d9b7e
PE
76 record_start = new_record;
77
d858a433 78 if (acting_as_filter)
3e3d9b7e 79 {
d858a433 80 archive = STDOUT_FILENO;
3e3d9b7e 81 flush_write ();
d858a433 82 archive = STDIN_FILENO;
3e3d9b7e
PE
83 }
84 else
85 {
d858a433 86 move_archive ((records_written + records_skipped) - records_read);
3e3d9b7e
PE
87 flush_write ();
88 }
89
90 record_start = save_record;
91
92 if (move_back_flag)
93 {
94 /* Move the tape head back to where we were. */
95
d858a433
SP
96 if (! acting_as_filter)
97 move_archive (records_read - (records_written + records_skipped));
3e3d9b7e
PE
98 }
99
3e3d9b7e
PE
100 new_blocks = 0;
101}
102
d858a433 103static void
73948311 104write_recent_blocks (union block *h, idx_t blocks)
d858a433 105{
73948311 106 for (idx_t i = 0; i < blocks; i++)
d858a433
SP
107 {
108 new_record[new_blocks++] = h[i];
109 if (new_blocks == blocking_factor)
fd401e1d 110 write_record (true);
d858a433
SP
111 }
112}
3e3d9b7e 113
08780d28 114static void
73948311 115write_recent_bytes (char *data, idx_t bytes)
08780d28 116{
a6cf78b0
PE
117 idx_t blocks = bytes >> LG_BLOCKSIZE;
118 idx_t rest = bytes & (BLOCKSIZE - 1);
08780d28
SP
119
120 write_recent_blocks ((union block *)data, blocks);
121 memcpy (new_record[new_blocks].buffer, data + blocks * BLOCKSIZE, rest);
122 if (rest < BLOCKSIZE)
123 memset (new_record[new_blocks].buffer + rest, 0, BLOCKSIZE - rest);
124 new_blocks++;
125 if (new_blocks == blocking_factor)
fd401e1d 126 write_record (true);
08780d28
SP
127}
128
7fb1b687 129static void
8e2898ab
SP
130flush_file (void)
131{
8e2898ab 132 set_next_block_after (current_header);
628c4925 133 off_t size = current_stat_info.stat.st_size;
a6cf78b0 134 off_t blocks_to_skip = (size >> LG_BLOCKSIZE) + !!(size & (BLOCKSIZE - 1));
8e2898ab
SP
135
136 while (record_end - current_block <= blocks_to_skip)
137 {
138 blocks_to_skip -= (record_end - current_block);
139 flush_archive ();
4177c98b
SP
140 if (record_end == current_block)
141 /* Hit EOF */
883f2e6d 142 return;
8e2898ab
SP
143 }
144 current_block += blocks_to_skip;
145}
146
3e3d9b7e
PE
147void
148delete_archive_members (void)
149{
150 enum read_header logical_status = HEADER_STILL_UNREAD;
151 enum read_header previous_status = HEADER_STILL_UNREAD;
152
153 /* FIXME: Should clean the routine before cleaning these variables :-( */
154 struct name *name;
d858a433 155 off_t blocks_to_keep = 0;
39d315e8 156 ptrdiff_t kept_blocks_in_record;
3e3d9b7e
PE
157
158 name_gather ();
159 open_archive (ACCESS_UPDATE);
d858a433 160 acting_as_filter = strcmp (archive_name_array[0], "-") == 0;
3e3d9b7e 161
8e2898ab 162 /* Skip to the first member that matches the name list. */
d858a433 163 do
3e3d9b7e 164 {
166b7c7d 165 enum read_header status = read_header (&current_header,
8e2898ab
SP
166 &current_stat_info,
167 read_header_x_raw);
3e3d9b7e
PE
168
169 switch (status)
170 {
171 case HEADER_STILL_UNREAD:
172 abort ();
173
174 case HEADER_SUCCESS:
10954cf1 175 if ((name = name_scan (current_stat_info.file_name, false)) == NULL)
3e3d9b7e 176 {
bc277c70 177 skim_member (acting_as_filter);
3e3d9b7e
PE
178 break;
179 }
f4935ab5 180 name->found_count++;
4323e986 181 if (!isfound (name))
f4935ab5 182 {
bc277c70 183 skim_member (acting_as_filter);
f4935ab5
SP
184 break;
185 }
d44d5920 186 FALLTHROUGH;
d858a433
SP
187 case HEADER_SUCCESS_EXTENDED:
188 logical_status = status;
3e3d9b7e
PE
189 break;
190
191 case HEADER_ZERO_BLOCK:
d858a433
SP
192 if (ignore_zeros_option)
193 {
194 set_next_block_after (current_header);
195 break;
196 }
d44d5920 197 FALLTHROUGH;
3e3d9b7e
PE
198 case HEADER_END_OF_FILE:
199 logical_status = HEADER_END_OF_FILE;
200 break;
201
202 case HEADER_FAILURE:
203 set_next_block_after (current_header);
204 switch (previous_status)
205 {
206 case HEADER_STILL_UNREAD:
0dfcfa4a 207 paxwarn (0, _("This does not look like a tar archive"));
d44d5920 208 FALLTHROUGH;
3e3d9b7e 209 case HEADER_SUCCESS:
86a9b926 210 case HEADER_SUCCESS_EXTENDED:
3e3d9b7e 211 case HEADER_ZERO_BLOCK:
0dfcfa4a 212 paxerror (0, _("Skipping to next header"));
d44d5920 213 FALLTHROUGH;
3e3d9b7e
PE
214 case HEADER_FAILURE:
215 break;
216
217 case HEADER_END_OF_FILE:
218 abort ();
219 }
220 break;
221 }
222
223 previous_status = status;
224 }
d858a433 225 while (logical_status == HEADER_STILL_UNREAD);
3e3d9b7e 226
d858a433
SP
227 records_skipped = records_read - 1;
228 new_record = xmalloc (record_size);
3e3d9b7e 229
d858a433
SP
230 if (logical_status == HEADER_SUCCESS
231 || logical_status == HEADER_SUCCESS_EXTENDED)
3e3d9b7e 232 {
829b1dc3 233 write_archive_to_stdout = false;
3e3d9b7e 234
d858a433 235 /* Save away blocks before this one in this record. */
3e3d9b7e 236
d858a433
SP
237 new_blocks = current_block - record_start;
238 if (new_blocks)
239 memcpy (new_record, record_start, new_blocks * BLOCKSIZE);
3e3d9b7e 240
d858a433 241 if (logical_status == HEADER_SUCCESS)
3e3d9b7e 242 {
d858a433 243 logical_status = HEADER_STILL_UNREAD;
8e2898ab 244 flush_file ();
3e3d9b7e
PE
245 }
246
8e2898ab
SP
247 /* Skip matching members and move the rest up the archive. */
248 while (logical_status != HEADER_END_OF_FILE)
3e3d9b7e 249 {
d858a433 250 enum read_header status;
3e3d9b7e 251
d858a433 252 /* Fill in a record. */
3e3d9b7e 253
d858a433
SP
254 if (current_block == record_end)
255 flush_archive ();
f2bf9f2f 256
8e2898ab
SP
257 status = read_header (&current_header, &current_stat_info,
258 read_header_auto);
3e3d9b7e 259
8e2898ab 260 switch (status)
d858a433 261 {
8e2898ab
SP
262 case HEADER_STILL_UNREAD:
263 case HEADER_SUCCESS_EXTENDED:
264 abort ();
3e3d9b7e 265
8e2898ab
SP
266 case HEADER_SUCCESS:
267 /* Found another header. */
268 xheader_decode (&current_stat_info);
d858a433 269
10954cf1 270 if ((name = name_scan (current_stat_info.file_name, false)) != NULL)
d858a433 271 {
8e2898ab 272 name->found_count++;
4323e986 273 if (isfound (name))
f4935ab5 274 {
8e2898ab
SP
275 flush_file ();
276 break;
f4935ab5 277 }
d858a433 278 }
8e2898ab 279 /* Copy header. */
3e3d9b7e 280
8e2898ab
SP
281 if (current_stat_info.xhdr.size)
282 {
283 write_recent_bytes (current_stat_info.xhdr.buffer,
284 current_stat_info.xhdr.size);
285 }
286 else
287 {
288 write_recent_blocks (recent_long_name,
289 recent_long_name_blocks);
290 write_recent_blocks (recent_long_link,
291 recent_long_link_blocks);
292 }
293 new_record[new_blocks] = *current_header;
294 new_blocks++;
295 blocks_to_keep
a6cf78b0
PE
296 = ((current_stat_info.stat.st_size >> LG_BLOCKSIZE)
297 + !!(current_stat_info.stat.st_size & (BLOCKSIZE - 1)));
8e2898ab
SP
298 set_next_block_after (current_header);
299 if (new_blocks == blocking_factor)
fd401e1d 300 write_record (true);
3e3d9b7e 301
8e2898ab 302 /* Copy data. */
d858a433 303
8e2898ab
SP
304 kept_blocks_in_record = record_end - current_block;
305 if (kept_blocks_in_record > blocks_to_keep)
306 kept_blocks_in_record = blocks_to_keep;
d858a433 307
8e2898ab 308 while (blocks_to_keep)
d858a433 309 {
39d315e8 310 ptrdiff_t count;
8e2898ab
SP
311
312 if (current_block == record_end)
313 {
314 flush_read ();
315 current_block = record_start;
316 kept_blocks_in_record = blocking_factor;
317 if (kept_blocks_in_record > blocks_to_keep)
318 kept_blocks_in_record = blocks_to_keep;
319 }
320 count = kept_blocks_in_record;
321 if (blocking_factor - new_blocks < count)
322 count = blocking_factor - new_blocks;
323
324 if (! count)
325 abort ();
326
327 memcpy (new_record + new_blocks, current_block,
328 count * BLOCKSIZE);
329 new_blocks += count;
330 current_block += count;
331 blocks_to_keep -= count;
332 kept_blocks_in_record -= count;
333
334 if (new_blocks == blocking_factor)
fd401e1d 335 write_record (true);
d858a433 336 }
8e2898ab 337 break;
d858a433 338
8e2898ab
SP
339 case HEADER_ZERO_BLOCK:
340 if (ignore_zeros_option)
341 set_next_block_after (current_header);
342 else
343 logical_status = HEADER_END_OF_FILE;
344 break;
d858a433 345
8e2898ab
SP
346 case HEADER_END_OF_FILE:
347 logical_status = HEADER_END_OF_FILE;
348 break;
d858a433 349
8e2898ab 350 case HEADER_FAILURE:
0dfcfa4a 351 paxerror (0, _("Deleting non-header from archive"));
8e2898ab
SP
352 set_next_block_after (current_header);
353 break;
354
355 default:
356 abort ();
3e3d9b7e 357 }
8e2898ab 358 tar_stat_destroy (&current_stat_info);
3e3d9b7e 359 }
3e3d9b7e 360
e6a67c2e
SP
361 if (logical_status == HEADER_END_OF_FILE)
362 {
363 /* Write the end of tape. FIXME: we can't use write_eot here,
364 as it gets confused when the input is at end of file. */
d858a433 365
3ffe2eb0 366 idx_t total_zero_blocks = 0;
d858a433 367
e6a67c2e
SP
368 do
369 {
3ffe2eb0 370 idx_t zero_blocks = blocking_factor - new_blocks;
e6a67c2e
SP
371 memset (new_record + new_blocks, 0, BLOCKSIZE * zero_blocks);
372 total_zero_blocks += zero_blocks;
373 write_record (total_zero_blocks < 2);
374 }
375 while (total_zero_blocks < 2);
376 }
377
378 if (! acting_as_filter && ! _isrmt (archive))
d858a433 379 {
f8a679e9 380 if (sys_truncate (archive) < 0)
e6a67c2e 381 truncate_warn (archive_name_array[0]);
d858a433 382 }
d858a433 383 }
d858a433
SP
384 free (new_record);
385
3e3d9b7e
PE
386 close_archive ();
387 names_notfound ();
388}