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