]>
Commit | Line | Data |
---|---|---|
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 |
25 | static union block *new_record; |
26 | static int new_blocks; | |
27 | static 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 |
31 | extern union block *record_start; |
32 | extern union block *record_end; | |
33 | extern union block *current_block; | |
d858a433 SP |
34 | extern union block *recent_long_name; |
35 | extern union block *recent_long_link; | |
f2bf9f2f | 36 | extern 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 | 40 | off_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 | 46 | static void |
d858a433 | 47 | move_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 |
81 | static void |
82 | write_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 |
112 | static void |
113 | write_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 |
124 | static void |
125 | write_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 | 139 | static void |
8e2898ab SP |
140 | flush_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 |
159 | void |
160 | delete_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 (¤t_header, |
8e2898ab SP |
178 | ¤t_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 (¤t_header, ¤t_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 (¤t_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 (¤t_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 | } |