1 /* Delete entries from a tar archive.
3 Copyright 1988-2025 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/>. */
25 static union block
*new_record
;
26 static idx_t new_blocks
;
27 static bool acting_as_filter
;
29 /* The number of records skipped at the start of the archive, when
30 passing over members that are not deleted. */
31 off_t records_skipped
;
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! */
38 move_archive (off_t count
)
43 if (mtioseek (false, count
))
46 off_t position0
= rmtlseek (archive
, 0, SEEK_CUR
), position
= 0;
49 /* Pretend the starting position is at the first record
50 boundary after POSITION0. This is useful at EOF after
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
;
55 if (ckd_mul (&increment
, record_size
, count
)
56 || ckd_add (&move_start
, position0
, start_offset
)
57 || ckd_add (&position
, move_start
, increment
)
60 paxerror (EOVERFLOW
, "lseek: %s", archive_name_array
[0]);
63 else if (rmtlseek (archive
, position
, SEEK_SET
) == position
)
66 if (!_isrmt (archive
))
67 seek_error_details (archive_name_array
[0], position
);
70 /* Write out the record which has been filled. If MOVE_BACK_FLAG,
71 backspace to where we started. */
73 write_record (bool move_back_flag
)
75 union block
*save_record
= record_start
;
76 record_start
= new_record
;
80 archive
= STDOUT_FILENO
;
82 archive
= STDIN_FILENO
;
86 move_archive ((records_written
+ records_skipped
) - records_read
);
90 record_start
= save_record
;
94 /* Move the tape head back to where we were. */
96 if (! acting_as_filter
)
97 move_archive (records_read
- (records_written
+ records_skipped
));
104 write_recent_blocks (union block
*h
, idx_t blocks
)
106 for (idx_t i
= 0; i
< blocks
; i
++)
108 new_record
[new_blocks
++] = h
[i
];
109 if (new_blocks
== blocking_factor
)
115 write_recent_bytes (char *data
, idx_t bytes
)
117 idx_t blocks
= bytes
>> LG_BLOCKSIZE
;
118 idx_t rest
= bytes
& (BLOCKSIZE
- 1);
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
);
125 if (new_blocks
== blocking_factor
)
132 set_next_block_after (current_header
);
133 off_t size
= current_stat_info
.stat
.st_size
;
134 off_t blocks_to_skip
= (size
>> LG_BLOCKSIZE
) + !!(size
& (BLOCKSIZE
- 1));
136 while (record_end
- current_block
<= blocks_to_skip
)
138 blocks_to_skip
-= (record_end
- current_block
);
140 if (record_end
== current_block
)
144 current_block
+= blocks_to_skip
;
148 delete_archive_members (void)
150 enum read_header logical_status
= HEADER_STILL_UNREAD
;
151 enum read_header previous_status
= HEADER_STILL_UNREAD
;
153 /* FIXME: Should clean the routine before cleaning these variables :-( */
155 off_t blocks_to_keep
= 0;
156 ptrdiff_t kept_blocks_in_record
;
159 open_archive (ACCESS_UPDATE
);
160 acting_as_filter
= strcmp (archive_name_array
[0], "-") == 0;
162 /* Skip to the first member that matches the name list. */
165 enum read_header status
= read_header (¤t_header
,
171 case HEADER_STILL_UNREAD
:
175 if ((name
= name_scan (current_stat_info
.file_name
, false)) == NULL
)
177 skim_member (acting_as_filter
);
183 skim_member (acting_as_filter
);
187 case HEADER_SUCCESS_EXTENDED
:
188 logical_status
= status
;
191 case HEADER_ZERO_BLOCK
:
192 if (ignore_zeros_option
)
194 set_next_block_after (current_header
);
198 case HEADER_END_OF_FILE
:
199 logical_status
= HEADER_END_OF_FILE
;
203 set_next_block_after (current_header
);
204 switch (previous_status
)
206 case HEADER_STILL_UNREAD
:
207 paxwarn (0, _("This does not look like a tar archive"));
210 case HEADER_SUCCESS_EXTENDED
:
211 case HEADER_ZERO_BLOCK
:
212 paxerror (0, _("Skipping to next header"));
217 case HEADER_END_OF_FILE
:
223 previous_status
= status
;
225 while (logical_status
== HEADER_STILL_UNREAD
);
227 records_skipped
= records_read
- 1;
228 new_record
= xmalloc (record_size
);
230 if (logical_status
== HEADER_SUCCESS
231 || logical_status
== HEADER_SUCCESS_EXTENDED
)
233 write_archive_to_stdout
= false;
235 /* Save away blocks before this one in this record. */
237 new_blocks
= current_block
- record_start
;
239 memcpy (new_record
, record_start
, new_blocks
* BLOCKSIZE
);
241 if (logical_status
== HEADER_SUCCESS
)
243 logical_status
= HEADER_STILL_UNREAD
;
247 /* Skip matching members and move the rest up the archive. */
248 while (logical_status
!= HEADER_END_OF_FILE
)
250 enum read_header status
;
252 /* Fill in a record. */
254 if (current_block
== record_end
)
257 status
= read_header (¤t_header
, ¤t_stat_info
,
262 case HEADER_STILL_UNREAD
:
263 case HEADER_SUCCESS_EXTENDED
:
267 /* Found another header. */
268 xheader_decode (¤t_stat_info
);
270 if ((name
= name_scan (current_stat_info
.file_name
, false)) != NULL
)
281 if (current_stat_info
.xhdr
.size
)
283 write_recent_bytes (current_stat_info
.xhdr
.buffer
,
284 current_stat_info
.xhdr
.size
);
288 write_recent_blocks (recent_long_name
,
289 recent_long_name_blocks
);
290 write_recent_blocks (recent_long_link
,
291 recent_long_link_blocks
);
293 new_record
[new_blocks
] = *current_header
;
296 = ((current_stat_info
.stat
.st_size
>> LG_BLOCKSIZE
)
297 + !!(current_stat_info
.stat
.st_size
& (BLOCKSIZE
- 1)));
298 set_next_block_after (current_header
);
299 if (new_blocks
== blocking_factor
)
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
;
308 while (blocks_to_keep
)
312 if (current_block
== record_end
)
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
;
320 count
= kept_blocks_in_record
;
321 if (blocking_factor
- new_blocks
< count
)
322 count
= blocking_factor
- new_blocks
;
327 memcpy (new_record
+ new_blocks
, current_block
,
330 current_block
+= count
;
331 blocks_to_keep
-= count
;
332 kept_blocks_in_record
-= count
;
334 if (new_blocks
== blocking_factor
)
339 case HEADER_ZERO_BLOCK
:
340 if (ignore_zeros_option
)
341 set_next_block_after (current_header
);
343 logical_status
= HEADER_END_OF_FILE
;
346 case HEADER_END_OF_FILE
:
347 logical_status
= HEADER_END_OF_FILE
;
351 paxerror (0, _("Deleting non-header from archive"));
352 set_next_block_after (current_header
);
358 tar_stat_destroy (¤t_stat_info
);
361 if (logical_status
== HEADER_END_OF_FILE
)
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. */
366 idx_t total_zero_blocks
= 0;
370 idx_t zero_blocks
= blocking_factor
- new_blocks
;
371 memset (new_record
+ new_blocks
, 0, BLOCKSIZE
* zero_blocks
);
372 total_zero_blocks
+= zero_blocks
;
373 write_record (total_zero_blocks
< 2);
375 while (total_zero_blocks
< 2);
378 if (! acting_as_filter
&& ! _isrmt (archive
))
380 if (sys_truncate (archive
) < 0)
381 truncate_warn (archive_name_array
[0]);