]>
Commit | Line | Data |
---|---|---|
3e3d9b7e | 1 | /* Delete entries from a tar archive. |
d858a433 | 2 | |
c6f0ad51 | 3 | Copyright 1988-2024 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 | { | |
8e2898ab | 142 | set_next_block_after (current_header); |
628c4925 PE |
143 | off_t size = current_stat_info.stat.st_size; |
144 | off_t blocks_to_skip = size / BLOCKSIZE + (size % BLOCKSIZE != 0); | |
8e2898ab SP |
145 | |
146 | while (record_end - current_block <= blocks_to_skip) | |
147 | { | |
148 | blocks_to_skip -= (record_end - current_block); | |
149 | flush_archive (); | |
4177c98b SP |
150 | if (record_end == current_block) |
151 | /* Hit EOF */ | |
883f2e6d | 152 | return; |
8e2898ab SP |
153 | } |
154 | current_block += blocks_to_skip; | |
155 | } | |
156 | ||
3e3d9b7e PE |
157 | void |
158 | delete_archive_members (void) | |
159 | { | |
160 | enum read_header logical_status = HEADER_STILL_UNREAD; | |
161 | enum read_header previous_status = HEADER_STILL_UNREAD; | |
162 | ||
163 | /* FIXME: Should clean the routine before cleaning these variables :-( */ | |
164 | struct name *name; | |
d858a433 | 165 | off_t blocks_to_keep = 0; |
3e3d9b7e PE |
166 | int kept_blocks_in_record; |
167 | ||
168 | name_gather (); | |
169 | open_archive (ACCESS_UPDATE); | |
d858a433 | 170 | acting_as_filter = strcmp (archive_name_array[0], "-") == 0; |
3e3d9b7e | 171 | |
8e2898ab | 172 | /* Skip to the first member that matches the name list. */ |
d858a433 | 173 | do |
3e3d9b7e | 174 | { |
166b7c7d | 175 | enum read_header status = read_header (¤t_header, |
8e2898ab SP |
176 | ¤t_stat_info, |
177 | read_header_x_raw); | |
3e3d9b7e PE |
178 | |
179 | switch (status) | |
180 | { | |
181 | case HEADER_STILL_UNREAD: | |
182 | abort (); | |
183 | ||
184 | case HEADER_SUCCESS: | |
10954cf1 | 185 | if ((name = name_scan (current_stat_info.file_name, false)) == NULL) |
3e3d9b7e | 186 | { |
bc277c70 | 187 | skim_member (acting_as_filter); |
3e3d9b7e PE |
188 | break; |
189 | } | |
f4935ab5 | 190 | name->found_count++; |
8e2898ab | 191 | if (!ISFOUND (name)) |
f4935ab5 | 192 | { |
bc277c70 | 193 | skim_member (acting_as_filter); |
f4935ab5 SP |
194 | break; |
195 | } | |
d44d5920 | 196 | FALLTHROUGH; |
d858a433 SP |
197 | case HEADER_SUCCESS_EXTENDED: |
198 | logical_status = status; | |
3e3d9b7e PE |
199 | break; |
200 | ||
201 | case HEADER_ZERO_BLOCK: | |
d858a433 SP |
202 | if (ignore_zeros_option) |
203 | { | |
204 | set_next_block_after (current_header); | |
205 | break; | |
206 | } | |
d44d5920 | 207 | FALLTHROUGH; |
3e3d9b7e PE |
208 | case HEADER_END_OF_FILE: |
209 | logical_status = HEADER_END_OF_FILE; | |
210 | break; | |
211 | ||
212 | case HEADER_FAILURE: | |
213 | set_next_block_after (current_header); | |
214 | switch (previous_status) | |
215 | { | |
216 | case HEADER_STILL_UNREAD: | |
217 | WARN ((0, 0, _("This does not look like a tar archive"))); | |
d44d5920 | 218 | FALLTHROUGH; |
3e3d9b7e | 219 | case HEADER_SUCCESS: |
86a9b926 | 220 | case HEADER_SUCCESS_EXTENDED: |
3e3d9b7e PE |
221 | case HEADER_ZERO_BLOCK: |
222 | ERROR ((0, 0, _("Skipping to next header"))); | |
d44d5920 | 223 | FALLTHROUGH; |
3e3d9b7e PE |
224 | case HEADER_FAILURE: |
225 | break; | |
226 | ||
227 | case HEADER_END_OF_FILE: | |
228 | abort (); | |
229 | } | |
230 | break; | |
231 | } | |
232 | ||
233 | previous_status = status; | |
234 | } | |
d858a433 | 235 | while (logical_status == HEADER_STILL_UNREAD); |
3e3d9b7e | 236 | |
d858a433 SP |
237 | records_skipped = records_read - 1; |
238 | new_record = xmalloc (record_size); | |
3e3d9b7e | 239 | |
d858a433 SP |
240 | if (logical_status == HEADER_SUCCESS |
241 | || logical_status == HEADER_SUCCESS_EXTENDED) | |
3e3d9b7e | 242 | { |
829b1dc3 | 243 | write_archive_to_stdout = false; |
3e3d9b7e | 244 | |
d858a433 | 245 | /* Save away blocks before this one in this record. */ |
3e3d9b7e | 246 | |
d858a433 SP |
247 | new_blocks = current_block - record_start; |
248 | if (new_blocks) | |
249 | memcpy (new_record, record_start, new_blocks * BLOCKSIZE); | |
3e3d9b7e | 250 | |
d858a433 | 251 | if (logical_status == HEADER_SUCCESS) |
3e3d9b7e | 252 | { |
d858a433 | 253 | logical_status = HEADER_STILL_UNREAD; |
8e2898ab | 254 | flush_file (); |
3e3d9b7e PE |
255 | } |
256 | ||
8e2898ab SP |
257 | /* Skip matching members and move the rest up the archive. */ |
258 | while (logical_status != HEADER_END_OF_FILE) | |
3e3d9b7e | 259 | { |
d858a433 | 260 | enum read_header status; |
3e3d9b7e | 261 | |
d858a433 | 262 | /* Fill in a record. */ |
3e3d9b7e | 263 | |
d858a433 SP |
264 | if (current_block == record_end) |
265 | flush_archive (); | |
f2bf9f2f | 266 | |
8e2898ab SP |
267 | status = read_header (¤t_header, ¤t_stat_info, |
268 | read_header_auto); | |
3e3d9b7e | 269 | |
8e2898ab | 270 | switch (status) |
d858a433 | 271 | { |
8e2898ab SP |
272 | case HEADER_STILL_UNREAD: |
273 | case HEADER_SUCCESS_EXTENDED: | |
274 | abort (); | |
3e3d9b7e | 275 | |
8e2898ab SP |
276 | case HEADER_SUCCESS: |
277 | /* Found another header. */ | |
278 | xheader_decode (¤t_stat_info); | |
d858a433 | 279 | |
10954cf1 | 280 | if ((name = name_scan (current_stat_info.file_name, false)) != NULL) |
d858a433 | 281 | { |
8e2898ab SP |
282 | name->found_count++; |
283 | if (ISFOUND (name)) | |
f4935ab5 | 284 | { |
8e2898ab SP |
285 | flush_file (); |
286 | break; | |
f4935ab5 | 287 | } |
d858a433 | 288 | } |
8e2898ab | 289 | /* Copy header. */ |
3e3d9b7e | 290 | |
8e2898ab SP |
291 | if (current_stat_info.xhdr.size) |
292 | { | |
293 | write_recent_bytes (current_stat_info.xhdr.buffer, | |
294 | current_stat_info.xhdr.size); | |
295 | } | |
296 | else | |
297 | { | |
298 | write_recent_blocks (recent_long_name, | |
299 | recent_long_name_blocks); | |
300 | write_recent_blocks (recent_long_link, | |
301 | recent_long_link_blocks); | |
302 | } | |
303 | new_record[new_blocks] = *current_header; | |
304 | new_blocks++; | |
305 | blocks_to_keep | |
306 | = (current_stat_info.stat.st_size + BLOCKSIZE - 1) / BLOCKSIZE; | |
307 | set_next_block_after (current_header); | |
308 | if (new_blocks == blocking_factor) | |
309 | write_record (1); | |
3e3d9b7e | 310 | |
8e2898ab | 311 | /* Copy data. */ |
d858a433 | 312 | |
8e2898ab SP |
313 | kept_blocks_in_record = record_end - current_block; |
314 | if (kept_blocks_in_record > blocks_to_keep) | |
315 | kept_blocks_in_record = blocks_to_keep; | |
d858a433 | 316 | |
8e2898ab | 317 | while (blocks_to_keep) |
d858a433 | 318 | { |
8e2898ab SP |
319 | int count; |
320 | ||
321 | if (current_block == record_end) | |
322 | { | |
323 | flush_read (); | |
324 | current_block = record_start; | |
325 | kept_blocks_in_record = blocking_factor; | |
326 | if (kept_blocks_in_record > blocks_to_keep) | |
327 | kept_blocks_in_record = blocks_to_keep; | |
328 | } | |
329 | count = kept_blocks_in_record; | |
330 | if (blocking_factor - new_blocks < count) | |
331 | count = blocking_factor - new_blocks; | |
332 | ||
333 | if (! count) | |
334 | abort (); | |
335 | ||
336 | memcpy (new_record + new_blocks, current_block, | |
337 | count * BLOCKSIZE); | |
338 | new_blocks += count; | |
339 | current_block += count; | |
340 | blocks_to_keep -= count; | |
341 | kept_blocks_in_record -= count; | |
342 | ||
343 | if (new_blocks == blocking_factor) | |
344 | write_record (1); | |
d858a433 | 345 | } |
8e2898ab | 346 | break; |
d858a433 | 347 | |
8e2898ab SP |
348 | case HEADER_ZERO_BLOCK: |
349 | if (ignore_zeros_option) | |
350 | set_next_block_after (current_header); | |
351 | else | |
352 | logical_status = HEADER_END_OF_FILE; | |
353 | break; | |
d858a433 | 354 | |
8e2898ab SP |
355 | case HEADER_END_OF_FILE: |
356 | logical_status = HEADER_END_OF_FILE; | |
357 | break; | |
d858a433 | 358 | |
8e2898ab SP |
359 | case HEADER_FAILURE: |
360 | ERROR ((0, 0, _("Deleting non-header from archive"))); | |
361 | set_next_block_after (current_header); | |
362 | break; | |
363 | ||
364 | default: | |
365 | abort (); | |
3e3d9b7e | 366 | } |
8e2898ab | 367 | tar_stat_destroy (¤t_stat_info); |
3e3d9b7e | 368 | } |
3e3d9b7e | 369 | |
e6a67c2e SP |
370 | if (logical_status == HEADER_END_OF_FILE) |
371 | { | |
372 | /* Write the end of tape. FIXME: we can't use write_eot here, | |
373 | as it gets confused when the input is at end of file. */ | |
d858a433 | 374 | |
e6a67c2e | 375 | int total_zero_blocks = 0; |
d858a433 | 376 | |
e6a67c2e SP |
377 | do |
378 | { | |
379 | int zero_blocks = blocking_factor - new_blocks; | |
380 | memset (new_record + new_blocks, 0, BLOCKSIZE * zero_blocks); | |
381 | total_zero_blocks += zero_blocks; | |
382 | write_record (total_zero_blocks < 2); | |
383 | } | |
384 | while (total_zero_blocks < 2); | |
385 | } | |
386 | ||
387 | if (! acting_as_filter && ! _isrmt (archive)) | |
d858a433 | 388 | { |
e6a67c2e SP |
389 | if (sys_truncate (archive)) |
390 | truncate_warn (archive_name_array[0]); | |
d858a433 | 391 | } |
d858a433 | 392 | } |
d858a433 SP |
393 | free (new_record); |
394 | ||
3e3d9b7e PE |
395 | close_archive (); |
396 | names_notfound (); | |
397 | } |