]>
Commit | Line | Data |
---|---|---|
3e3d9b7e | 1 | /* Delete entries from a tar archive. |
d858a433 | 2 | |
afa743ac | 3 | Copyright 1988-2021 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> |
f2bf9f2f | 21 | #include <system-ioctl.h> |
3e3d9b7e | 22 | |
3e3d9b7e | 23 | #include "common.h" |
7347b4f5 | 24 | #include <rmt.h> |
3e3d9b7e | 25 | |
d858a433 SP |
26 | static union block *new_record; |
27 | static int new_blocks; | |
28 | static bool acting_as_filter; | |
3e3d9b7e | 29 | |
d858a433 SP |
30 | /* FIXME: This module should not directly handle the following |
31 | variables, instead, the interface should be cleaned up. */ | |
3e3d9b7e PE |
32 | extern union block *record_start; |
33 | extern union block *record_end; | |
34 | extern union block *current_block; | |
d858a433 SP |
35 | extern union block *recent_long_name; |
36 | extern union block *recent_long_link; | |
f2bf9f2f | 37 | extern off_t records_read; |
d858a433 SP |
38 | |
39 | /* The number of records skipped at the start of the archive, when | |
40 | passing over members that are not deleted. */ | |
05b250d4 | 41 | off_t records_skipped; |
d858a433 SP |
42 | |
43 | /* Move archive descriptor by COUNT records worth. If COUNT is | |
44 | positive we move forward, else we move negative. If it's a tape, | |
45 | MTIOCTOP had better work. If it's something else, we try to seek | |
46 | on it. If we can't seek, we lose! */ | |
3e3d9b7e | 47 | static void |
d858a433 | 48 | move_archive (off_t count) |
3e3d9b7e | 49 | { |
d858a433 SP |
50 | if (count == 0) |
51 | return; | |
52 | ||
3e3d9b7e PE |
53 | #ifdef MTIOCTOP |
54 | { | |
55 | struct mtop operation; | |
3e3d9b7e | 56 | |
d858a433 SP |
57 | if (count < 0 |
58 | ? (operation.mt_op = MTBSR, | |
59 | operation.mt_count = -count, | |
60 | operation.mt_count == -count) | |
61 | : (operation.mt_op = MTFSR, | |
62 | operation.mt_count = count, | |
63 | operation.mt_count == count)) | |
3e3d9b7e | 64 | { |
d858a433 SP |
65 | if (0 <= rmtioctl (archive, MTIOCTOP, (char *) &operation)) |
66 | return; | |
3e3d9b7e | 67 | |
d858a433 SP |
68 | if (errno == EIO |
69 | && 0 <= rmtioctl (archive, MTIOCTOP, (char *) &operation)) | |
70 | return; | |
71 | } | |
3e3d9b7e PE |
72 | } |
73 | #endif /* MTIOCTOP */ | |
74 | ||
75 | { | |
d858a433 SP |
76 | off_t position0 = rmtlseek (archive, (off_t) 0, SEEK_CUR); |
77 | off_t increment = record_size * (off_t) count; | |
78 | off_t position = position0 + increment; | |
3e3d9b7e | 79 | |
d858a433 SP |
80 | if (increment / count != record_size |
81 | || (position < position0) != (increment < 0) | |
82 | || (position = position < 0 ? 0 : position, | |
83 | rmtlseek (archive, position, SEEK_SET) != position)) | |
84 | seek_error_details (archive_name_array[0], position); | |
3e3d9b7e PE |
85 | |
86 | return; | |
87 | } | |
88 | } | |
89 | ||
d858a433 SP |
90 | /* Write out the record which has been filled. If MOVE_BACK_FLAG, |
91 | backspace to where we started. */ | |
3e3d9b7e PE |
92 | static void |
93 | write_record (int move_back_flag) | |
94 | { | |
d858a433 | 95 | union block *save_record = record_start; |
3e3d9b7e PE |
96 | record_start = new_record; |
97 | ||
d858a433 | 98 | if (acting_as_filter) |
3e3d9b7e | 99 | { |
d858a433 | 100 | archive = STDOUT_FILENO; |
3e3d9b7e | 101 | flush_write (); |
d858a433 | 102 | archive = STDIN_FILENO; |
3e3d9b7e PE |
103 | } |
104 | else | |
105 | { | |
d858a433 | 106 | move_archive ((records_written + records_skipped) - records_read); |
3e3d9b7e PE |
107 | flush_write (); |
108 | } | |
109 | ||
110 | record_start = save_record; | |
111 | ||
112 | if (move_back_flag) | |
113 | { | |
114 | /* Move the tape head back to where we were. */ | |
115 | ||
d858a433 SP |
116 | if (! acting_as_filter) |
117 | move_archive (records_read - (records_written + records_skipped)); | |
3e3d9b7e PE |
118 | } |
119 | ||
3e3d9b7e PE |
120 | new_blocks = 0; |
121 | } | |
122 | ||
d858a433 SP |
123 | static void |
124 | write_recent_blocks (union block *h, size_t blocks) | |
125 | { | |
126 | size_t i; | |
127 | for (i = 0; i < blocks; i++) | |
128 | { | |
129 | new_record[new_blocks++] = h[i]; | |
130 | if (new_blocks == blocking_factor) | |
131 | write_record (1); | |
132 | } | |
133 | } | |
3e3d9b7e | 134 | |
08780d28 SP |
135 | static void |
136 | write_recent_bytes (char *data, size_t bytes) | |
137 | { | |
138 | size_t blocks = bytes / BLOCKSIZE; | |
139 | size_t rest = bytes - blocks * BLOCKSIZE; | |
140 | ||
141 | write_recent_blocks ((union block *)data, blocks); | |
142 | memcpy (new_record[new_blocks].buffer, data + blocks * BLOCKSIZE, rest); | |
143 | if (rest < BLOCKSIZE) | |
144 | memset (new_record[new_blocks].buffer + rest, 0, BLOCKSIZE - rest); | |
145 | new_blocks++; | |
146 | if (new_blocks == blocking_factor) | |
147 | write_record (1); | |
148 | } | |
149 | ||
3e3d9b7e PE |
150 | void |
151 | delete_archive_members (void) | |
152 | { | |
153 | enum read_header logical_status = HEADER_STILL_UNREAD; | |
154 | enum read_header previous_status = HEADER_STILL_UNREAD; | |
155 | ||
156 | /* FIXME: Should clean the routine before cleaning these variables :-( */ | |
157 | struct name *name; | |
d858a433 SP |
158 | off_t blocks_to_skip = 0; |
159 | off_t blocks_to_keep = 0; | |
3e3d9b7e PE |
160 | int kept_blocks_in_record; |
161 | ||
162 | name_gather (); | |
163 | open_archive (ACCESS_UPDATE); | |
d858a433 | 164 | acting_as_filter = strcmp (archive_name_array[0], "-") == 0; |
3e3d9b7e | 165 | |
d858a433 | 166 | do |
3e3d9b7e | 167 | { |
166b7c7d SP |
168 | enum read_header status = read_header (¤t_header, |
169 | ¤t_stat_info, | |
c3fa22fc | 170 | read_header_x_raw); |
3e3d9b7e PE |
171 | |
172 | switch (status) | |
173 | { | |
174 | case HEADER_STILL_UNREAD: | |
175 | abort (); | |
176 | ||
177 | case HEADER_SUCCESS: | |
6d1e9ab6 | 178 | if ((name = name_scan (current_stat_info.file_name)) == NULL) |
3e3d9b7e | 179 | { |
d858a433 | 180 | skip_member (); |
3e3d9b7e PE |
181 | break; |
182 | } | |
f4935ab5 SP |
183 | name->found_count++; |
184 | if (!ISFOUND(name)) | |
185 | { | |
186 | skip_member (); | |
187 | break; | |
188 | } | |
d44d5920 | 189 | FALLTHROUGH; |
d858a433 SP |
190 | case HEADER_SUCCESS_EXTENDED: |
191 | logical_status = status; | |
3e3d9b7e PE |
192 | break; |
193 | ||
194 | case HEADER_ZERO_BLOCK: | |
d858a433 SP |
195 | if (ignore_zeros_option) |
196 | { | |
197 | set_next_block_after (current_header); | |
198 | break; | |
199 | } | |
d44d5920 | 200 | FALLTHROUGH; |
3e3d9b7e PE |
201 | case HEADER_END_OF_FILE: |
202 | logical_status = HEADER_END_OF_FILE; | |
203 | break; | |
204 | ||
205 | case HEADER_FAILURE: | |
206 | set_next_block_after (current_header); | |
207 | switch (previous_status) | |
208 | { | |
209 | case HEADER_STILL_UNREAD: | |
210 | WARN ((0, 0, _("This does not look like a tar archive"))); | |
d44d5920 | 211 | FALLTHROUGH; |
3e3d9b7e | 212 | case HEADER_SUCCESS: |
86a9b926 | 213 | case HEADER_SUCCESS_EXTENDED: |
3e3d9b7e PE |
214 | case HEADER_ZERO_BLOCK: |
215 | ERROR ((0, 0, _("Skipping to next header"))); | |
d44d5920 | 216 | FALLTHROUGH; |
3e3d9b7e PE |
217 | case HEADER_FAILURE: |
218 | break; | |
219 | ||
220 | case HEADER_END_OF_FILE: | |
221 | abort (); | |
222 | } | |
223 | break; | |
224 | } | |
225 | ||
226 | previous_status = status; | |
227 | } | |
d858a433 | 228 | while (logical_status == HEADER_STILL_UNREAD); |
3e3d9b7e | 229 | |
d858a433 SP |
230 | records_skipped = records_read - 1; |
231 | new_record = xmalloc (record_size); | |
3e3d9b7e | 232 | |
d858a433 SP |
233 | if (logical_status == HEADER_SUCCESS |
234 | || logical_status == HEADER_SUCCESS_EXTENDED) | |
3e3d9b7e | 235 | { |
829b1dc3 | 236 | write_archive_to_stdout = false; |
3e3d9b7e | 237 | |
d858a433 | 238 | /* Save away blocks before this one in this record. */ |
3e3d9b7e | 239 | |
d858a433 SP |
240 | new_blocks = current_block - record_start; |
241 | if (new_blocks) | |
242 | memcpy (new_record, record_start, new_blocks * BLOCKSIZE); | |
3e3d9b7e | 243 | |
d858a433 | 244 | if (logical_status == HEADER_SUCCESS) |
3e3d9b7e | 245 | { |
d858a433 SP |
246 | /* FIXME: Pheew! This is crufty code! */ |
247 | logical_status = HEADER_STILL_UNREAD; | |
248 | goto flush_file; | |
3e3d9b7e PE |
249 | } |
250 | ||
d858a433 SP |
251 | /* FIXME: Solaris 2.4 Sun cc (the ANSI one, not the old K&R) says: |
252 | "delete.c", line 223: warning: loop not entered at top | |
253 | Reported by Bruno Haible. */ | |
254 | while (1) | |
3e3d9b7e | 255 | { |
d858a433 | 256 | enum read_header status; |
3e3d9b7e | 257 | |
d858a433 | 258 | /* Fill in a record. */ |
3e3d9b7e | 259 | |
d858a433 SP |
260 | if (current_block == record_end) |
261 | flush_archive (); | |
0ab5e64a | 262 | status = read_header (¤t_header, ¤t_stat_info, |
c3fa22fc | 263 | read_header_auto); |
f2bf9f2f | 264 | |
64cca92a | 265 | xheader_decode (¤t_stat_info); |
f2bf9f2f | 266 | |
d858a433 | 267 | if (status == HEADER_ZERO_BLOCK && ignore_zeros_option) |
3e3d9b7e | 268 | { |
d858a433 SP |
269 | set_next_block_after (current_header); |
270 | continue; | |
271 | } | |
272 | if (status == HEADER_END_OF_FILE || status == HEADER_ZERO_BLOCK) | |
273 | { | |
274 | logical_status = HEADER_END_OF_FILE; | |
275 | break; | |
3e3d9b7e | 276 | } |
3e3d9b7e | 277 | |
d858a433 SP |
278 | if (status == HEADER_FAILURE) |
279 | { | |
280 | ERROR ((0, 0, _("Deleting non-header from archive"))); | |
281 | set_next_block_after (current_header); | |
282 | continue; | |
283 | } | |
3e3d9b7e | 284 | |
d858a433 SP |
285 | /* Found another header. */ |
286 | ||
6d1e9ab6 | 287 | if ((name = name_scan (current_stat_info.file_name)) != NULL) |
d858a433 | 288 | { |
f4935ab5 SP |
289 | name->found_count++; |
290 | if (ISFOUND(name)) | |
d858a433 | 291 | { |
f4935ab5 SP |
292 | flush_file: |
293 | set_next_block_after (current_header); | |
294 | blocks_to_skip = (current_stat_info.stat.st_size | |
295 | + BLOCKSIZE - 1) / BLOCKSIZE; | |
0ab5e64a | 296 | |
f4935ab5 SP |
297 | while (record_end - current_block <= blocks_to_skip) |
298 | { | |
299 | blocks_to_skip -= (record_end - current_block); | |
300 | flush_archive (); | |
301 | } | |
302 | current_block += blocks_to_skip; | |
303 | blocks_to_skip = 0; | |
304 | continue; | |
d858a433 | 305 | } |
d858a433 | 306 | } |
d858a433 | 307 | /* Copy header. */ |
3e3d9b7e | 308 | |
250db35f | 309 | if (current_stat_info.xhdr.size) |
08780d28 | 310 | { |
250db35f SP |
311 | write_recent_bytes (current_stat_info.xhdr.buffer, |
312 | current_stat_info.xhdr.size); | |
08780d28 SP |
313 | } |
314 | else | |
315 | { | |
316 | write_recent_blocks (recent_long_name, recent_long_name_blocks); | |
317 | write_recent_blocks (recent_long_link, recent_long_link_blocks); | |
318 | } | |
d858a433 SP |
319 | new_record[new_blocks] = *current_header; |
320 | new_blocks++; | |
321 | blocks_to_keep | |
322 | = (current_stat_info.stat.st_size + BLOCKSIZE - 1) / BLOCKSIZE; | |
323 | set_next_block_after (current_header); | |
324 | if (new_blocks == blocking_factor) | |
325 | write_record (1); | |
3e3d9b7e | 326 | |
d858a433 | 327 | /* Copy data. */ |
3e3d9b7e | 328 | |
d858a433 SP |
329 | kept_blocks_in_record = record_end - current_block; |
330 | if (kept_blocks_in_record > blocks_to_keep) | |
331 | kept_blocks_in_record = blocks_to_keep; | |
332 | ||
333 | while (blocks_to_keep) | |
3e3d9b7e | 334 | { |
d858a433 SP |
335 | int count; |
336 | ||
337 | if (current_block == record_end) | |
338 | { | |
339 | flush_read (); | |
340 | current_block = record_start; | |
341 | kept_blocks_in_record = blocking_factor; | |
342 | if (kept_blocks_in_record > blocks_to_keep) | |
343 | kept_blocks_in_record = blocks_to_keep; | |
344 | } | |
345 | count = kept_blocks_in_record; | |
346 | if (blocking_factor - new_blocks < count) | |
347 | count = blocking_factor - new_blocks; | |
348 | ||
349 | if (! count) | |
350 | abort (); | |
351 | ||
352 | memcpy (new_record + new_blocks, current_block, count * BLOCKSIZE); | |
353 | new_blocks += count; | |
354 | current_block += count; | |
355 | blocks_to_keep -= count; | |
356 | kept_blocks_in_record -= count; | |
357 | ||
358 | if (new_blocks == blocking_factor) | |
359 | write_record (1); | |
3e3d9b7e | 360 | } |
3e3d9b7e | 361 | } |
3e3d9b7e | 362 | |
e6a67c2e SP |
363 | if (logical_status == HEADER_END_OF_FILE) |
364 | { | |
365 | /* Write the end of tape. FIXME: we can't use write_eot here, | |
366 | as it gets confused when the input is at end of file. */ | |
d858a433 | 367 | |
e6a67c2e | 368 | int total_zero_blocks = 0; |
d858a433 | 369 | |
e6a67c2e SP |
370 | do |
371 | { | |
372 | int zero_blocks = blocking_factor - new_blocks; | |
373 | memset (new_record + new_blocks, 0, BLOCKSIZE * zero_blocks); | |
374 | total_zero_blocks += zero_blocks; | |
375 | write_record (total_zero_blocks < 2); | |
376 | } | |
377 | while (total_zero_blocks < 2); | |
378 | } | |
379 | ||
380 | if (! acting_as_filter && ! _isrmt (archive)) | |
d858a433 | 381 | { |
e6a67c2e SP |
382 | if (sys_truncate (archive)) |
383 | truncate_warn (archive_name_array[0]); | |
d858a433 | 384 | } |
d858a433 | 385 | } |
d858a433 SP |
386 | free (new_record); |
387 | ||
3e3d9b7e PE |
388 | close_archive (); |
389 | names_notfound (); | |
390 | } |