]>
Commit | Line | Data |
---|---|---|
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 | 25 | static union block *new_record; |
3ffe2eb0 | 26 | static idx_t new_blocks; |
d858a433 | 27 | static 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 | 31 | off_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 | 37 | static void |
d858a433 | 38 | move_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 | 72 | static void |
fd401e1d | 73 | write_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 | 103 | static void |
73948311 | 104 | write_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 | 114 | static void |
73948311 | 115 | write_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 | 129 | static void |
8e2898ab SP |
130 | flush_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 |
147 | void |
148 | delete_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 (¤t_header, |
8e2898ab SP |
166 | ¤t_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 (¤t_header, ¤t_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 (¤t_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 (¤t_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 | } |