]> git.ipfire.org Git - thirdparty/tar.git/blame - src/delete.c
doc: fix date in example
[thirdparty/tar.git] / src / delete.c
CommitLineData
3e3d9b7e 1/* Delete entries from a tar archive.
d858a433 2
719d3b44 3 Copyright 1988-2023 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 ();
4177c98b
SP
152 if (record_end == current_block)
153 /* Hit EOF */
154 break;
8e2898ab
SP
155 }
156 current_block += blocks_to_skip;
157}
158
3e3d9b7e
PE
159void
160delete_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;
d858a433 167 off_t blocks_to_keep = 0;
3e3d9b7e
PE
168 int kept_blocks_in_record;
169
170 name_gather ();
171 open_archive (ACCESS_UPDATE);
d858a433 172 acting_as_filter = strcmp (archive_name_array[0], "-") == 0;
3e3d9b7e 173
8e2898ab 174 /* Skip to the first member that matches the name list. */
d858a433 175 do
3e3d9b7e 176 {
166b7c7d 177 enum read_header status = read_header (&current_header,
8e2898ab
SP
178 &current_stat_info,
179 read_header_x_raw);
3e3d9b7e
PE
180
181 switch (status)
182 {
183 case HEADER_STILL_UNREAD:
184 abort ();
185
186 case HEADER_SUCCESS:
10954cf1 187 if ((name = name_scan (current_stat_info.file_name, false)) == NULL)
3e3d9b7e 188 {
bc277c70 189 skim_member (acting_as_filter);
3e3d9b7e
PE
190 break;
191 }
f4935ab5 192 name->found_count++;
8e2898ab 193 if (!ISFOUND (name))
f4935ab5 194 {
bc277c70 195 skim_member (acting_as_filter);
f4935ab5
SP
196 break;
197 }
d44d5920 198 FALLTHROUGH;
d858a433
SP
199 case HEADER_SUCCESS_EXTENDED:
200 logical_status = status;
3e3d9b7e
PE
201 break;
202
203 case HEADER_ZERO_BLOCK:
d858a433
SP
204 if (ignore_zeros_option)
205 {
206 set_next_block_after (current_header);
207 break;
208 }
d44d5920 209 FALLTHROUGH;
3e3d9b7e
PE
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")));
d44d5920 220 FALLTHROUGH;
3e3d9b7e 221 case HEADER_SUCCESS:
86a9b926 222 case HEADER_SUCCESS_EXTENDED:
3e3d9b7e
PE
223 case HEADER_ZERO_BLOCK:
224 ERROR ((0, 0, _("Skipping to next header")));
d44d5920 225 FALLTHROUGH;
3e3d9b7e
PE
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 }
d858a433 237 while (logical_status == HEADER_STILL_UNREAD);
3e3d9b7e 238
d858a433
SP
239 records_skipped = records_read - 1;
240 new_record = xmalloc (record_size);
3e3d9b7e 241
d858a433
SP
242 if (logical_status == HEADER_SUCCESS
243 || logical_status == HEADER_SUCCESS_EXTENDED)
3e3d9b7e 244 {
829b1dc3 245 write_archive_to_stdout = false;
3e3d9b7e 246
d858a433 247 /* Save away blocks before this one in this record. */
3e3d9b7e 248
d858a433
SP
249 new_blocks = current_block - record_start;
250 if (new_blocks)
251 memcpy (new_record, record_start, new_blocks * BLOCKSIZE);
3e3d9b7e 252
d858a433 253 if (logical_status == HEADER_SUCCESS)
3e3d9b7e 254 {
d858a433 255 logical_status = HEADER_STILL_UNREAD;
8e2898ab 256 flush_file ();
3e3d9b7e
PE
257 }
258
8e2898ab
SP
259 /* Skip matching members and move the rest up the archive. */
260 while (logical_status != HEADER_END_OF_FILE)
3e3d9b7e 261 {
d858a433 262 enum read_header status;
3e3d9b7e 263
d858a433 264 /* Fill in a record. */
3e3d9b7e 265
d858a433
SP
266 if (current_block == record_end)
267 flush_archive ();
f2bf9f2f 268
8e2898ab
SP
269 status = read_header (&current_header, &current_stat_info,
270 read_header_auto);
3e3d9b7e 271
8e2898ab 272 switch (status)
d858a433 273 {
8e2898ab
SP
274 case HEADER_STILL_UNREAD:
275 case HEADER_SUCCESS_EXTENDED:
276 abort ();
3e3d9b7e 277
8e2898ab
SP
278 case HEADER_SUCCESS:
279 /* Found another header. */
280 xheader_decode (&current_stat_info);
d858a433 281
10954cf1 282 if ((name = name_scan (current_stat_info.file_name, false)) != NULL)
d858a433 283 {
8e2898ab
SP
284 name->found_count++;
285 if (ISFOUND (name))
f4935ab5 286 {
8e2898ab
SP
287 flush_file ();
288 break;
f4935ab5 289 }
d858a433 290 }
8e2898ab 291 /* Copy header. */
3e3d9b7e 292
8e2898ab
SP
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);
3e3d9b7e 312
8e2898ab 313 /* Copy data. */
d858a433 314
8e2898ab
SP
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;
d858a433 318
8e2898ab 319 while (blocks_to_keep)
d858a433 320 {
8e2898ab
SP
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);
d858a433 347 }
8e2898ab 348 break;
d858a433 349
8e2898ab
SP
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;
d858a433 356
8e2898ab
SP
357 case HEADER_END_OF_FILE:
358 logical_status = HEADER_END_OF_FILE;
359 break;
d858a433 360
8e2898ab
SP
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 ();
3e3d9b7e 368 }
8e2898ab 369 tar_stat_destroy (&current_stat_info);
3e3d9b7e 370 }
3e3d9b7e 371
e6a67c2e
SP
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. */
d858a433 376
e6a67c2e 377 int total_zero_blocks = 0;
d858a433 378
e6a67c2e
SP
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))
d858a433 390 {
e6a67c2e
SP
391 if (sys_truncate (archive))
392 truncate_warn (archive_name_array[0]);
d858a433 393 }
d858a433 394 }
d858a433
SP
395 free (new_record);
396
3e3d9b7e
PE
397 close_archive ();
398 names_notfound ();
399}