1 | /* Buffer management for tar.
|
---|
2 |
|
---|
3 | Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001,
|
---|
4 | 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
|
---|
5 |
|
---|
6 | Written by John Gilmore, on 1985-08-25.
|
---|
7 |
|
---|
8 | This program is free software; you can redistribute it and/or modify it
|
---|
9 | under the terms of the GNU General Public License as published by the
|
---|
10 | Free Software Foundation; either version 2, or (at your option) any later
|
---|
11 | version.
|
---|
12 |
|
---|
13 | This program is distributed in the hope that it will be useful, but
|
---|
14 | WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
---|
16 | Public License for more details.
|
---|
17 |
|
---|
18 | You should have received a copy of the GNU General Public License along
|
---|
19 | with this program; if not, write to the Free Software Foundation, Inc.,
|
---|
20 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
|
---|
21 |
|
---|
22 | #include <system.h>
|
---|
23 | #include <system-ioctl.h>
|
---|
24 |
|
---|
25 | #include <signal.h>
|
---|
26 |
|
---|
27 | #include <closeout.h>
|
---|
28 | #include <fnmatch.h>
|
---|
29 | #include <getline.h>
|
---|
30 | #include <human.h>
|
---|
31 | #include <quotearg.h>
|
---|
32 |
|
---|
33 | #include "common.h"
|
---|
34 | #include <rmt.h>
|
---|
35 |
|
---|
36 | /* Number of retries before giving up on read. */
|
---|
37 | #define READ_ERROR_MAX 10
|
---|
38 |
|
---|
39 | /* Globbing pattern to append to volume label if initial match failed. */
|
---|
40 | #define VOLUME_LABEL_APPEND " Volume [1-9]*"
|
---|
41 |
|
---|
42 | /* Variables. */
|
---|
43 |
|
---|
44 | static tarlong prev_written; /* bytes written on previous volumes */
|
---|
45 | static tarlong bytes_written; /* bytes written on this volume */
|
---|
46 | static void *record_buffer[2]; /* allocated memory */
|
---|
47 | union block *record_buffer_aligned[2];
|
---|
48 | static int record_index;
|
---|
49 |
|
---|
50 | /* FIXME: The following variables should ideally be static to this
|
---|
51 | module. However, this cannot be done yet. The cleanup continues! */
|
---|
52 |
|
---|
53 | union block *record_start; /* start of record of archive */
|
---|
54 | union block *record_end; /* last+1 block of archive record */
|
---|
55 | union block *current_block; /* current block of archive */
|
---|
56 | enum access_mode access_mode; /* how do we handle the archive */
|
---|
57 | off_t records_read; /* number of records read from this archive */
|
---|
58 | off_t records_written; /* likewise, for records written */
|
---|
59 | extern off_t records_skipped; /* number of records skipped at the start
|
---|
60 | of the archive, defined in delete.c */
|
---|
61 |
|
---|
62 | static off_t record_start_block; /* block ordinal at record_start */
|
---|
63 |
|
---|
64 | /* Where we write list messages (not errors, not interactions) to. */
|
---|
65 | FILE *stdlis;
|
---|
66 |
|
---|
67 | static void backspace_output (void);
|
---|
68 |
|
---|
69 | /* PID of child program, if compress_option or remote archive access. */
|
---|
70 | static pid_t child_pid;
|
---|
71 |
|
---|
72 | /* Error recovery stuff */
|
---|
73 | static int read_error_count;
|
---|
74 |
|
---|
75 | /* Have we hit EOF yet? */
|
---|
76 | static bool hit_eof;
|
---|
77 |
|
---|
78 | /* Checkpointing counter */
|
---|
79 | static unsigned checkpoint;
|
---|
80 |
|
---|
81 | static bool read_full_records = false;
|
---|
82 |
|
---|
83 | /* We're reading, but we just read the last block and it's time to update.
|
---|
84 | Declared in update.c
|
---|
85 |
|
---|
86 | As least EXTERN like this one as possible. (?? --gray)
|
---|
87 | FIXME: Either eliminate it or move it to common.h.
|
---|
88 | */
|
---|
89 | extern bool time_to_start_writing;
|
---|
90 |
|
---|
91 | bool write_archive_to_stdout;
|
---|
92 |
|
---|
93 | void (*flush_write_ptr) (size_t);
|
---|
94 | void (*flush_read_ptr) (void);
|
---|
95 |
|
---|
96 | |
---|
97 |
|
---|
98 | char *volume_label;
|
---|
99 | char *continued_file_name;
|
---|
100 | uintmax_t continued_file_size;
|
---|
101 | uintmax_t continued_file_offset;
|
---|
102 |
|
---|
103 | |
---|
104 |
|
---|
105 | static int volno = 1; /* which volume of a multi-volume tape we're
|
---|
106 | on */
|
---|
107 | static int global_volno = 1; /* volume number to print in external
|
---|
108 | messages */
|
---|
109 |
|
---|
110 | bool write_archive_to_stdout;
|
---|
111 |
|
---|
112 | /* Used by flush_read and flush_write to store the real info about saved
|
---|
113 | names. */
|
---|
114 | static char *real_s_name;
|
---|
115 | static off_t real_s_totsize;
|
---|
116 | static off_t real_s_sizeleft;
|
---|
117 |
|
---|
118 | |
---|
119 |
|
---|
120 | /* Multi-volume tracking support */
|
---|
121 | static char *save_name; /* name of the file we are currently writing */
|
---|
122 | static off_t save_totsize; /* total size of file we are writing, only
|
---|
123 | valid if save_name is nonzero */
|
---|
124 | static off_t save_sizeleft; /* where we are in the file we are writing,
|
---|
125 | only valid if save_name is nonzero */
|
---|
126 |
|
---|
127 | void
|
---|
128 | mv_begin (struct tar_stat_info *st)
|
---|
129 | {
|
---|
130 | if (multi_volume_option)
|
---|
131 | {
|
---|
132 | assign_string (&save_name, st->orig_file_name);
|
---|
133 | save_totsize = save_sizeleft = st->stat.st_size;
|
---|
134 | }
|
---|
135 | }
|
---|
136 |
|
---|
137 | void
|
---|
138 | mv_end ()
|
---|
139 | {
|
---|
140 | if (multi_volume_option)
|
---|
141 | assign_string (&save_name, 0);
|
---|
142 | }
|
---|
143 |
|
---|
144 | void
|
---|
145 | mv_total_size (off_t size)
|
---|
146 | {
|
---|
147 | save_totsize = size;
|
---|
148 | }
|
---|
149 |
|
---|
150 | void
|
---|
151 | mv_size_left (off_t size)
|
---|
152 | {
|
---|
153 | save_sizeleft = size;
|
---|
154 | }
|
---|
155 |
|
---|
156 | |
---|
157 |
|
---|
158 | /* Functions. */
|
---|
159 |
|
---|
160 | void
|
---|
161 | clear_read_error_count (void)
|
---|
162 | {
|
---|
163 | read_error_count = 0;
|
---|
164 | }
|
---|
165 |
|
---|
166 | |
---|
167 |
|
---|
168 | /* Time-related functions */
|
---|
169 |
|
---|
170 | double duration;
|
---|
171 |
|
---|
172 | void
|
---|
173 | set_start_time ()
|
---|
174 | {
|
---|
175 | gettime (&start_time);
|
---|
176 | volume_start_time = start_time;
|
---|
177 | last_stat_time = start_time;
|
---|
178 | }
|
---|
179 |
|
---|
180 | void
|
---|
181 | set_volume_start_time ()
|
---|
182 | {
|
---|
183 | gettime (&volume_start_time);
|
---|
184 | last_stat_time = volume_start_time;
|
---|
185 | }
|
---|
186 |
|
---|
187 | void
|
---|
188 | compute_duration ()
|
---|
189 | {
|
---|
190 | struct timespec now;
|
---|
191 | gettime (&now);
|
---|
192 | duration += ((now.tv_sec - last_stat_time.tv_sec)
|
---|
193 | + (now.tv_nsec - last_stat_time.tv_nsec) / 1e9);
|
---|
194 | gettime (&last_stat_time);
|
---|
195 | }
|
---|
196 |
|
---|
197 | |
---|
198 |
|
---|
199 | /* Compression detection */
|
---|
200 |
|
---|
201 | enum compress_type {
|
---|
202 | ct_none,
|
---|
203 | ct_compress,
|
---|
204 | ct_gzip,
|
---|
205 | ct_bzip2
|
---|
206 | };
|
---|
207 |
|
---|
208 | struct zip_magic
|
---|
209 | {
|
---|
210 | enum compress_type type;
|
---|
211 | size_t length;
|
---|
212 | char *magic;
|
---|
213 | char *program;
|
---|
214 | char *option;
|
---|
215 | };
|
---|
216 |
|
---|
217 | static struct zip_magic const magic[] = {
|
---|
218 | { ct_none, },
|
---|
219 | { ct_compress, 2, "\037\235", "compress", "-Z" },
|
---|
220 | { ct_gzip, 2, "\037\213", "gzip", "-z" },
|
---|
221 | { ct_bzip2, 3, "BZh", "bzip2", "-j" },
|
---|
222 | };
|
---|
223 |
|
---|
224 | #define NMAGIC (sizeof(magic)/sizeof(magic[0]))
|
---|
225 |
|
---|
226 | #define compress_option(t) magic[t].option
|
---|
227 | #define compress_program(t) magic[t].program
|
---|
228 |
|
---|
229 | /* Check if the file ARCHIVE is a compressed archive. */
|
---|
230 | enum compress_type
|
---|
231 | check_compressed_archive ()
|
---|
232 | {
|
---|
233 | struct zip_magic const *p;
|
---|
234 | bool sfr;
|
---|
235 |
|
---|
236 | /* Prepare global data needed for find_next_block: */
|
---|
237 | record_end = record_start; /* set up for 1st record = # 0 */
|
---|
238 | sfr = read_full_records;
|
---|
239 | read_full_records = true; /* Suppress fatal error on reading a partial
|
---|
240 | record */
|
---|
241 | find_next_block ();
|
---|
242 |
|
---|
243 | /* Restore global values */
|
---|
244 | read_full_records = sfr;
|
---|
245 |
|
---|
246 | if (tar_checksum (record_start, true) == HEADER_SUCCESS)
|
---|
247 | /* Probably a valid header */
|
---|
248 | return ct_none;
|
---|
249 |
|
---|
250 | for (p = magic + 1; p < magic + NMAGIC; p++)
|
---|
251 | if (memcmp (record_start->buffer, p->magic, p->length) == 0)
|
---|
252 | return p->type;
|
---|
253 |
|
---|
254 | return ct_none;
|
---|
255 | }
|
---|
256 |
|
---|
257 | /* Open an archive named archive_name_array[0]. Detect if it is
|
---|
258 | a compressed archive of known type and use corresponding decompression
|
---|
259 | program if so */
|
---|
260 | int
|
---|
261 | open_compressed_archive ()
|
---|
262 | {
|
---|
263 | archive = rmtopen (archive_name_array[0], O_RDONLY | O_BINARY,
|
---|
264 | MODE_RW, rsh_command_option);
|
---|
265 | if (archive == -1)
|
---|
266 | return archive;
|
---|
267 |
|
---|
268 | if (!multi_volume_option)
|
---|
269 | {
|
---|
270 | enum compress_type type = check_compressed_archive ();
|
---|
271 |
|
---|
272 | if (type == ct_none)
|
---|
273 | return archive;
|
---|
274 |
|
---|
275 | /* FD is not needed any more */
|
---|
276 | rmtclose (archive);
|
---|
277 |
|
---|
278 | hit_eof = false; /* It might have been set by find_next_block in
|
---|
279 | check_compressed_archive */
|
---|
280 |
|
---|
281 | /* Open compressed archive */
|
---|
282 | use_compress_program_option = compress_program (type);
|
---|
283 | child_pid = sys_child_open_for_uncompress ();
|
---|
284 | read_full_records = true;
|
---|
285 | }
|
---|
286 |
|
---|
287 | records_read = 0;
|
---|
288 | record_end = record_start; /* set up for 1st record = # 0 */
|
---|
289 |
|
---|
290 | return archive;
|
---|
291 | }
|
---|
292 | |
---|
293 |
|
---|
294 |
|
---|
295 | static void
|
---|
296 | print_stats (FILE *fp, const char *text, tarlong numbytes)
|
---|
297 | {
|
---|
298 | char bytes[sizeof (tarlong) * CHAR_BIT];
|
---|
299 | char abbr[LONGEST_HUMAN_READABLE + 1];
|
---|
300 | char rate[LONGEST_HUMAN_READABLE + 1];
|
---|
301 |
|
---|
302 | int human_opts = human_autoscale | human_base_1024 | human_SI | human_B;
|
---|
303 |
|
---|
304 | sprintf (bytes, TARLONG_FORMAT, numbytes);
|
---|
305 |
|
---|
306 | fprintf (fp, "%s: %s (%s, %s/s)\n",
|
---|
307 | text, bytes,
|
---|
308 | human_readable (numbytes, abbr, human_opts, 1, 1),
|
---|
309 | (0 < duration && numbytes / duration < (uintmax_t) -1
|
---|
310 | ? human_readable (numbytes / duration, rate, human_opts, 1, 1)
|
---|
311 | : "?"));
|
---|
312 | }
|
---|
313 |
|
---|
314 | void
|
---|
315 | print_total_stats ()
|
---|
316 | {
|
---|
317 | switch (subcommand_option)
|
---|
318 | {
|
---|
319 | case CREATE_SUBCOMMAND:
|
---|
320 | case CAT_SUBCOMMAND:
|
---|
321 | case UPDATE_SUBCOMMAND:
|
---|
322 | case APPEND_SUBCOMMAND:
|
---|
323 | /* Amanda 2.4.1p1 looks for "Total bytes written: [0-9][0-9]*". */
|
---|
324 | print_stats (stderr, _("Total bytes written"),
|
---|
325 | prev_written + bytes_written);
|
---|
326 | break;
|
---|
327 |
|
---|
328 | case DELETE_SUBCOMMAND:
|
---|
329 | {
|
---|
330 | char buf[UINTMAX_STRSIZE_BOUND];
|
---|
331 | print_stats (stderr, _("Total bytes read"),
|
---|
332 | records_read * record_size);
|
---|
333 | print_stats (stderr, _("Total bytes written"),
|
---|
334 | prev_written + bytes_written);
|
---|
335 | fprintf (stderr, _("Total bytes deleted: %s\n"),
|
---|
336 | STRINGIFY_BIGINT ((records_read - records_skipped)
|
---|
337 | * record_size
|
---|
338 | - (prev_written + bytes_written), buf));
|
---|
339 | }
|
---|
340 | break;
|
---|
341 |
|
---|
342 | case EXTRACT_SUBCOMMAND:
|
---|
343 | case LIST_SUBCOMMAND:
|
---|
344 | case DIFF_SUBCOMMAND:
|
---|
345 | print_stats (stderr, _("Total bytes read"),
|
---|
346 | records_read * record_size);
|
---|
347 | break;
|
---|
348 |
|
---|
349 | default:
|
---|
350 | abort ();
|
---|
351 | }
|
---|
352 | }
|
---|
353 |
|
---|
354 | /* Compute and return the block ordinal at current_block. */
|
---|
355 | off_t
|
---|
356 | current_block_ordinal (void)
|
---|
357 | {
|
---|
358 | return record_start_block + (current_block - record_start);
|
---|
359 | }
|
---|
360 |
|
---|
361 | /* If the EOF flag is set, reset it, as well as current_block, etc. */
|
---|
362 | void
|
---|
363 | reset_eof (void)
|
---|
364 | {
|
---|
365 | if (hit_eof)
|
---|
366 | {
|
---|
367 | hit_eof = false;
|
---|
368 | current_block = record_start;
|
---|
369 | record_end = record_start + blocking_factor;
|
---|
370 | access_mode = ACCESS_WRITE;
|
---|
371 | }
|
---|
372 | }
|
---|
373 |
|
---|
374 | /* Return the location of the next available input or output block.
|
---|
375 | Return zero for EOF. Once we have returned zero, we just keep returning
|
---|
376 | it, to avoid accidentally going on to the next file on the tape. */
|
---|
377 | union block *
|
---|
378 | find_next_block (void)
|
---|
379 | {
|
---|
380 | if (current_block == record_end)
|
---|
381 | {
|
---|
382 | if (hit_eof)
|
---|
383 | return 0;
|
---|
384 | flush_archive ();
|
---|
385 | if (current_block == record_end)
|
---|
386 | {
|
---|
387 | hit_eof = true;
|
---|
388 | return 0;
|
---|
389 | }
|
---|
390 | }
|
---|
391 | return current_block;
|
---|
392 | }
|
---|
393 |
|
---|
394 | /* Indicate that we have used all blocks up thru BLOCK. */
|
---|
395 | void
|
---|
396 | set_next_block_after (union block *block)
|
---|
397 | {
|
---|
398 | while (block >= current_block)
|
---|
399 | current_block++;
|
---|
400 |
|
---|
401 | /* Do *not* flush the archive here. If we do, the same argument to
|
---|
402 | set_next_block_after could mean the next block (if the input record
|
---|
403 | is exactly one block long), which is not what is intended. */
|
---|
404 |
|
---|
405 | if (current_block > record_end)
|
---|
406 | abort ();
|
---|
407 | }
|
---|
408 |
|
---|
409 | /* Return the number of bytes comprising the space between POINTER
|
---|
410 | through the end of the current buffer of blocks. This space is
|
---|
411 | available for filling with data, or taking data from. POINTER is
|
---|
412 | usually (but not always) the result of previous find_next_block call. */
|
---|
413 | size_t
|
---|
414 | available_space_after (union block *pointer)
|
---|
415 | {
|
---|
416 | return record_end->buffer - pointer->buffer;
|
---|
417 | }
|
---|
418 |
|
---|
419 | /* Close file having descriptor FD, and abort if close unsuccessful. */
|
---|
420 | void
|
---|
421 | xclose (int fd)
|
---|
422 | {
|
---|
423 | if (close (fd) != 0)
|
---|
424 | close_error (_("(pipe)"));
|
---|
425 | }
|
---|
426 |
|
---|
427 | static void
|
---|
428 | init_buffer ()
|
---|
429 | {
|
---|
430 | if (! record_buffer_aligned[record_index])
|
---|
431 | record_buffer_aligned[record_index] =
|
---|
432 | page_aligned_alloc (&record_buffer[record_index], record_size);
|
---|
433 |
|
---|
434 | record_start = record_buffer_aligned[record_index];
|
---|
435 | current_block = record_start;
|
---|
436 | record_end = record_start + blocking_factor;
|
---|
437 | }
|
---|
438 |
|
---|
439 | /* Open an archive file. The argument specifies whether we are
|
---|
440 | reading or writing, or both. */
|
---|
441 | static void
|
---|
442 | _open_archive (enum access_mode wanted_access)
|
---|
443 | {
|
---|
444 | int backed_up_flag = 0;
|
---|
445 |
|
---|
446 | if (record_size == 0)
|
---|
447 | FATAL_ERROR ((0, 0, _("Invalid value for record_size")));
|
---|
448 |
|
---|
449 | if (archive_names == 0)
|
---|
450 | FATAL_ERROR ((0, 0, _("No archive name given")));
|
---|
451 |
|
---|
452 | tar_stat_destroy (¤t_stat_info);
|
---|
453 | save_name = 0;
|
---|
454 | real_s_name = 0;
|
---|
455 |
|
---|
456 | record_index = 0;
|
---|
457 | init_buffer ();
|
---|
458 |
|
---|
459 | /* When updating the archive, we start with reading. */
|
---|
460 | access_mode = wanted_access == ACCESS_UPDATE ? ACCESS_READ : wanted_access;
|
---|
461 |
|
---|
462 | read_full_records = read_full_records_option;
|
---|
463 |
|
---|
464 | records_read = 0;
|
---|
465 |
|
---|
466 | if (use_compress_program_option)
|
---|
467 | {
|
---|
468 | switch (wanted_access)
|
---|
469 | {
|
---|
470 | case ACCESS_READ:
|
---|
471 | child_pid = sys_child_open_for_uncompress ();
|
---|
472 | read_full_records = true;
|
---|
473 | record_end = record_start; /* set up for 1st record = # 0 */
|
---|
474 | break;
|
---|
475 |
|
---|
476 | case ACCESS_WRITE:
|
---|
477 | child_pid = sys_child_open_for_compress ();
|
---|
478 | break;
|
---|
479 |
|
---|
480 | case ACCESS_UPDATE:
|
---|
481 | abort (); /* Should not happen */
|
---|
482 | break;
|
---|
483 | }
|
---|
484 |
|
---|
485 | if (!index_file_name
|
---|
486 | && wanted_access == ACCESS_WRITE
|
---|
487 | && strcmp (archive_name_array[0], "-") == 0)
|
---|
488 | stdlis = stderr;
|
---|
489 | }
|
---|
490 | else if (strcmp (archive_name_array[0], "-") == 0)
|
---|
491 | {
|
---|
492 | read_full_records = true; /* could be a pipe, be safe */
|
---|
493 | if (verify_option)
|
---|
494 | FATAL_ERROR ((0, 0, _("Cannot verify stdin/stdout archive")));
|
---|
495 |
|
---|
496 | switch (wanted_access)
|
---|
497 | {
|
---|
498 | case ACCESS_READ:
|
---|
499 | {
|
---|
500 | enum compress_type type;
|
---|
501 |
|
---|
502 | archive = STDIN_FILENO;
|
---|
503 |
|
---|
504 | type = check_compressed_archive ();
|
---|
505 | if (type != ct_none)
|
---|
506 | FATAL_ERROR ((0, 0,
|
---|
507 | _("Archive is compressed. Use %s option"),
|
---|
508 | compress_option (type)));
|
---|
509 | }
|
---|
510 | break;
|
---|
511 |
|
---|
512 | case ACCESS_WRITE:
|
---|
513 | archive = STDOUT_FILENO;
|
---|
514 | if (!index_file_name)
|
---|
515 | stdlis = stderr;
|
---|
516 | break;
|
---|
517 |
|
---|
518 | case ACCESS_UPDATE:
|
---|
519 | archive = STDIN_FILENO;
|
---|
520 | write_archive_to_stdout = true;
|
---|
521 | record_end = record_start; /* set up for 1st record = # 0 */
|
---|
522 | if (!index_file_name)
|
---|
523 | stdlis = stderr;
|
---|
524 | SET_BINARY_MODE (STDOUT_FILENO); /* bird - this has to go somewhere! */
|
---|
525 | break;
|
---|
526 | }
|
---|
527 | }
|
---|
528 | else if (verify_option)
|
---|
529 | archive = rmtopen (archive_name_array[0], O_RDWR | O_CREAT | O_BINARY,
|
---|
530 | MODE_RW, rsh_command_option);
|
---|
531 | else
|
---|
532 | switch (wanted_access)
|
---|
533 | {
|
---|
534 | case ACCESS_READ:
|
---|
535 | archive = open_compressed_archive ();
|
---|
536 | break;
|
---|
537 |
|
---|
538 | case ACCESS_WRITE:
|
---|
539 | if (backup_option)
|
---|
540 | {
|
---|
541 | maybe_backup_file (archive_name_array[0], 1);
|
---|
542 | backed_up_flag = 1;
|
---|
543 | }
|
---|
544 | archive = rmtcreat (archive_name_array[0], MODE_RW,
|
---|
545 | rsh_command_option);
|
---|
546 | break;
|
---|
547 |
|
---|
548 | case ACCESS_UPDATE:
|
---|
549 | archive = rmtopen (archive_name_array[0],
|
---|
550 | O_RDWR | O_CREAT | O_BINARY,
|
---|
551 | MODE_RW, rsh_command_option);
|
---|
552 |
|
---|
553 | if (check_compressed_archive () != ct_none)
|
---|
554 | FATAL_ERROR ((0, 0,
|
---|
555 | _("Cannot update compressed archives")));
|
---|
556 | break;
|
---|
557 | }
|
---|
558 |
|
---|
559 | if (archive < 0
|
---|
560 | || (! _isrmt (archive) && !sys_get_archive_stat ()))
|
---|
561 | {
|
---|
562 | int saved_errno = errno;
|
---|
563 |
|
---|
564 | if (backed_up_flag)
|
---|
565 | undo_last_backup ();
|
---|
566 | errno = saved_errno;
|
---|
567 | open_fatal (archive_name_array[0]);
|
---|
568 | }
|
---|
569 |
|
---|
570 | sys_detect_dev_null_output ();
|
---|
571 | sys_save_archive_dev_ino ();
|
---|
572 | SET_BINARY_MODE (archive);
|
---|
573 |
|
---|
574 | switch (wanted_access)
|
---|
575 | {
|
---|
576 | case ACCESS_READ:
|
---|
577 | find_next_block (); /* read it in, check for EOF */
|
---|
578 | break;
|
---|
579 |
|
---|
580 | case ACCESS_UPDATE:
|
---|
581 | case ACCESS_WRITE:
|
---|
582 | records_written = 0;
|
---|
583 | break;
|
---|
584 | }
|
---|
585 | }
|
---|
586 |
|
---|
587 | static void
|
---|
588 | do_checkpoint (bool write)
|
---|
589 | {
|
---|
590 | if (checkpoint_option && !(++checkpoint % checkpoint_option))
|
---|
591 | {
|
---|
592 | switch (checkpoint_style)
|
---|
593 | {
|
---|
594 | case checkpoint_dot:
|
---|
595 | fputc ('.', stdlis);
|
---|
596 | fflush (stdlis);
|
---|
597 | break;
|
---|
598 |
|
---|
599 | case checkpoint_text:
|
---|
600 | if (write)
|
---|
601 | /* TRANSLATORS: This is a ``checkpoint of write operation'',
|
---|
602 | *not* ``Writing a checkpoint''.
|
---|
603 | E.g. in Spanish ``Punto de comprobaci@'on de escritura'',
|
---|
604 | *not* ``Escribiendo un punto de comprobaci@'on'' */
|
---|
605 | WARN ((0, 0, _("Write checkpoint %u"), checkpoint));
|
---|
606 | else
|
---|
607 | /* TRANSLATORS: This is a ``checkpoint of read operation'',
|
---|
608 | *not* ``Reading a checkpoint''.
|
---|
609 | E.g. in Spanish ``Punto de comprobaci@'on de lectura'',
|
---|
610 | *not* ``Leyendo un punto de comprobaci@'on'' */
|
---|
611 | WARN ((0, 0, _("Read checkpoint %u"), checkpoint));
|
---|
612 | break;
|
---|
613 | }
|
---|
614 | }
|
---|
615 | }
|
---|
616 |
|
---|
617 | /* Perform a write to flush the buffer. */
|
---|
618 | ssize_t
|
---|
619 | _flush_write (void)
|
---|
620 | {
|
---|
621 | ssize_t status;
|
---|
622 |
|
---|
623 | do_checkpoint (true);
|
---|
624 | if (tape_length_option && tape_length_option <= bytes_written)
|
---|
625 | {
|
---|
626 | errno = ENOSPC;
|
---|
627 | status = 0;
|
---|
628 | }
|
---|
629 | else if (dev_null_output)
|
---|
630 | status = record_size;
|
---|
631 | else
|
---|
632 | status = sys_write_archive_buffer ();
|
---|
633 |
|
---|
634 | return status;
|
---|
635 | }
|
---|
636 |
|
---|
637 | /* Handle write errors on the archive. Write errors are always fatal.
|
---|
638 | Hitting the end of a volume does not cause a write error unless the
|
---|
639 | write was the first record of the volume. */
|
---|
640 | void
|
---|
641 | archive_write_error (ssize_t status)
|
---|
642 | {
|
---|
643 | /* It might be useful to know how much was written before the error
|
---|
644 | occurred. */
|
---|
645 | if (totals_option)
|
---|
646 | {
|
---|
647 | int e = errno;
|
---|
648 | print_total_stats ();
|
---|
649 | errno = e;
|
---|
650 | }
|
---|
651 |
|
---|
652 | write_fatal_details (*archive_name_cursor, status, record_size);
|
---|
653 | }
|
---|
654 |
|
---|
655 | /* Handle read errors on the archive. If the read should be retried,
|
---|
656 | return to the caller. */
|
---|
657 | void
|
---|
658 | archive_read_error (void)
|
---|
659 | {
|
---|
660 | read_error (*archive_name_cursor);
|
---|
661 |
|
---|
662 | if (record_start_block == 0)
|
---|
663 | FATAL_ERROR ((0, 0, _("At beginning of tape, quitting now")));
|
---|
664 |
|
---|
665 | /* Read error in mid archive. We retry up to READ_ERROR_MAX times and
|
---|
666 | then give up on reading the archive. */
|
---|
667 |
|
---|
668 | if (read_error_count++ > READ_ERROR_MAX)
|
---|
669 | FATAL_ERROR ((0, 0, _("Too many errors, quitting")));
|
---|
670 | return;
|
---|
671 | }
|
---|
672 |
|
---|
673 | static void
|
---|
674 | short_read (size_t status)
|
---|
675 | {
|
---|
676 | size_t left; /* bytes left */
|
---|
677 | char *more; /* pointer to next byte to read */
|
---|
678 |
|
---|
679 | more = record_start->buffer + status;
|
---|
680 | left = record_size - status;
|
---|
681 |
|
---|
682 | while (left % BLOCKSIZE != 0
|
---|
683 | || (left && status && read_full_records))
|
---|
684 | {
|
---|
685 | if (status)
|
---|
686 | while ((status = rmtread (archive, more, left)) == SAFE_READ_ERROR)
|
---|
687 | archive_read_error ();
|
---|
688 |
|
---|
689 | if (status == 0)
|
---|
690 | break;
|
---|
691 |
|
---|
692 | if (! read_full_records)
|
---|
693 | {
|
---|
694 | unsigned long rest = record_size - left;
|
---|
695 |
|
---|
696 | FATAL_ERROR ((0, 0,
|
---|
697 | ngettext ("Unaligned block (%lu byte) in archive",
|
---|
698 | "Unaligned block (%lu bytes) in archive",
|
---|
699 | rest),
|
---|
700 | rest));
|
---|
701 | }
|
---|
702 |
|
---|
703 | /* User warned us about this. Fix up. */
|
---|
704 |
|
---|
705 | left -= status;
|
---|
706 | more += status;
|
---|
707 | }
|
---|
708 |
|
---|
709 | /* FIXME: for size=0, multi-volume support. On the first record, warn
|
---|
710 | about the problem. */
|
---|
711 |
|
---|
712 | if (!read_full_records && verbose_option > 1
|
---|
713 | && record_start_block == 0 && status != 0)
|
---|
714 | {
|
---|
715 | unsigned long rsize = (record_size - left) / BLOCKSIZE;
|
---|
716 | WARN ((0, 0,
|
---|
717 | ngettext ("Record size = %lu block",
|
---|
718 | "Record size = %lu blocks",
|
---|
719 | rsize),
|
---|
720 | rsize));
|
---|
721 | }
|
---|
722 |
|
---|
723 | record_end = record_start + (record_size - left) / BLOCKSIZE;
|
---|
724 | records_read++;
|
---|
725 | }
|
---|
726 |
|
---|
727 | /* Flush the current buffer to/from the archive. */
|
---|
728 | void
|
---|
729 | flush_archive (void)
|
---|
730 | {
|
---|
731 | size_t buffer_level = current_block->buffer - record_start->buffer;
|
---|
732 | record_start_block += record_end - record_start;
|
---|
733 | current_block = record_start;
|
---|
734 | record_end = record_start + blocking_factor;
|
---|
735 |
|
---|
736 | if (access_mode == ACCESS_READ && time_to_start_writing)
|
---|
737 | {
|
---|
738 | access_mode = ACCESS_WRITE;
|
---|
739 | time_to_start_writing = false;
|
---|
740 | backspace_output ();
|
---|
741 | }
|
---|
742 |
|
---|
743 | switch (access_mode)
|
---|
744 | {
|
---|
745 | case ACCESS_READ:
|
---|
746 | flush_read ();
|
---|
747 | break;
|
---|
748 |
|
---|
749 | case ACCESS_WRITE:
|
---|
750 | flush_write_ptr (buffer_level);
|
---|
751 | break;
|
---|
752 |
|
---|
753 | case ACCESS_UPDATE:
|
---|
754 | abort ();
|
---|
755 | }
|
---|
756 | }
|
---|
757 |
|
---|
758 | /* Backspace the archive descriptor by one record worth. If it's a
|
---|
759 | tape, MTIOCTOP will work. If it's something else, try to seek on
|
---|
760 | it. If we can't seek, we lose! */
|
---|
761 | static void
|
---|
762 | backspace_output (void)
|
---|
763 | {
|
---|
764 | #ifdef MTIOCTOP
|
---|
765 | {
|
---|
766 | struct mtop operation;
|
---|
767 |
|
---|
768 | operation.mt_op = MTBSR;
|
---|
769 | operation.mt_count = 1;
|
---|
770 | if (rmtioctl (archive, MTIOCTOP, (char *) &operation) >= 0)
|
---|
771 | return;
|
---|
772 | if (errno == EIO && rmtioctl (archive, MTIOCTOP, (char *) &operation) >= 0)
|
---|
773 | return;
|
---|
774 | }
|
---|
775 | #endif
|
---|
776 |
|
---|
777 | {
|
---|
778 | off_t position = rmtlseek (archive, (off_t) 0, SEEK_CUR);
|
---|
779 |
|
---|
780 | /* Seek back to the beginning of this record and start writing there. */
|
---|
781 |
|
---|
782 | position -= record_size;
|
---|
783 | if (position < 0)
|
---|
784 | position = 0;
|
---|
785 | if (rmtlseek (archive, position, SEEK_SET) != position)
|
---|
786 | {
|
---|
787 | /* Lseek failed. Try a different method. */
|
---|
788 |
|
---|
789 | WARN ((0, 0,
|
---|
790 | _("Cannot backspace archive file; it may be unreadable without -i")));
|
---|
791 |
|
---|
792 | /* Replace the first part of the record with NULs. */
|
---|
793 |
|
---|
794 | if (record_start->buffer != output_start)
|
---|
795 | memset (record_start->buffer, 0,
|
---|
796 | output_start - record_start->buffer);
|
---|
797 | }
|
---|
798 | }
|
---|
799 | }
|
---|
800 |
|
---|
801 | off_t
|
---|
802 | seek_archive (off_t size)
|
---|
803 | {
|
---|
804 | off_t start = current_block_ordinal ();
|
---|
805 | off_t offset;
|
---|
806 | off_t nrec, nblk;
|
---|
807 | off_t skipped = (blocking_factor - (current_block - record_start));
|
---|
808 |
|
---|
809 | size -= skipped * BLOCKSIZE;
|
---|
810 |
|
---|
811 | if (size < record_size)
|
---|
812 | return 0;
|
---|
813 | /* FIXME: flush? */
|
---|
814 |
|
---|
815 | /* Compute number of records to skip */
|
---|
816 | nrec = size / record_size;
|
---|
817 | offset = rmtlseek (archive, nrec * record_size, SEEK_CUR);
|
---|
818 | if (offset < 0)
|
---|
819 | return offset;
|
---|
820 |
|
---|
821 | if (offset % record_size)
|
---|
822 | FATAL_ERROR ((0, 0, _("rmtlseek not stopped at a record boundary")));
|
---|
823 |
|
---|
824 | /* Convert to number of records */
|
---|
825 | offset /= BLOCKSIZE;
|
---|
826 | /* Compute number of skipped blocks */
|
---|
827 | nblk = offset - start;
|
---|
828 |
|
---|
829 | /* Update buffering info */
|
---|
830 | records_read += nblk / blocking_factor;
|
---|
831 | record_start_block = offset - blocking_factor;
|
---|
832 | current_block = record_end;
|
---|
833 |
|
---|
834 | return nblk;
|
---|
835 | }
|
---|
836 |
|
---|
837 | /* Close the archive file. */
|
---|
838 | void
|
---|
839 | close_archive (void)
|
---|
840 | {
|
---|
841 | if (time_to_start_writing || access_mode == ACCESS_WRITE)
|
---|
842 | {
|
---|
843 | flush_archive ();
|
---|
844 | if (current_block > record_start)
|
---|
845 | flush_archive ();
|
---|
846 | }
|
---|
847 |
|
---|
848 | sys_drain_input_pipe ();
|
---|
849 |
|
---|
850 | compute_duration ();
|
---|
851 | if (verify_option)
|
---|
852 | verify_volume ();
|
---|
853 |
|
---|
854 | if (rmtclose (archive) != 0)
|
---|
855 | close_warn (*archive_name_cursor);
|
---|
856 |
|
---|
857 | sys_wait_for_child (child_pid);
|
---|
858 |
|
---|
859 | tar_stat_destroy (¤t_stat_info);
|
---|
860 | if (save_name)
|
---|
861 | free (save_name);
|
---|
862 | if (real_s_name)
|
---|
863 | free (real_s_name);
|
---|
864 | free (record_buffer[0]);
|
---|
865 | free (record_buffer[1]);
|
---|
866 | }
|
---|
867 |
|
---|
868 | /* Called to initialize the global volume number. */
|
---|
869 | void
|
---|
870 | init_volume_number (void)
|
---|
871 | {
|
---|
872 | FILE *file = fopen (volno_file_option, "r");
|
---|
873 |
|
---|
874 | if (file)
|
---|
875 | {
|
---|
876 | if (fscanf (file, "%d", &global_volno) != 1
|
---|
877 | || global_volno < 0)
|
---|
878 | FATAL_ERROR ((0, 0, _("%s: contains invalid volume number"),
|
---|
879 | quotearg_colon (volno_file_option)));
|
---|
880 | if (ferror (file))
|
---|
881 | read_error (volno_file_option);
|
---|
882 | if (fclose (file) != 0)
|
---|
883 | close_error (volno_file_option);
|
---|
884 | }
|
---|
885 | else if (errno != ENOENT)
|
---|
886 | open_error (volno_file_option);
|
---|
887 | }
|
---|
888 |
|
---|
889 | /* Called to write out the closing global volume number. */
|
---|
890 | void
|
---|
891 | closeout_volume_number (void)
|
---|
892 | {
|
---|
893 | FILE *file = fopen (volno_file_option, "w");
|
---|
894 |
|
---|
895 | if (file)
|
---|
896 | {
|
---|
897 | fprintf (file, "%d\n", global_volno);
|
---|
898 | if (ferror (file))
|
---|
899 | write_error (volno_file_option);
|
---|
900 | if (fclose (file) != 0)
|
---|
901 | close_error (volno_file_option);
|
---|
902 | }
|
---|
903 | else
|
---|
904 | open_error (volno_file_option);
|
---|
905 | }
|
---|
906 |
|
---|
907 | |
---|
908 |
|
---|
909 | static void
|
---|
910 | increase_volume_number ()
|
---|
911 | {
|
---|
912 | global_volno++;
|
---|
913 | if (global_volno < 0)
|
---|
914 | FATAL_ERROR ((0, 0, _("Volume number overflow")));
|
---|
915 | volno++;
|
---|
916 | }
|
---|
917 |
|
---|
918 | void
|
---|
919 | change_tape_menu (FILE *read_file)
|
---|
920 | {
|
---|
921 | char *input_buffer = NULL;
|
---|
922 | size_t size = 0;
|
---|
923 | bool stop = false;
|
---|
924 |
|
---|
925 | while (!stop)
|
---|
926 | {
|
---|
927 | fputc ('\007', stderr);
|
---|
928 | fprintf (stderr,
|
---|
929 | _("Prepare volume #%d for %s and hit return: "),
|
---|
930 | global_volno + 1, quote (*archive_name_cursor));
|
---|
931 | fflush (stderr);
|
---|
932 |
|
---|
933 | if (getline (&input_buffer, &size, read_file) <= 0)
|
---|
934 | {
|
---|
935 | WARN ((0, 0, _("EOF where user reply was expected")));
|
---|
936 |
|
---|
937 | if (subcommand_option != EXTRACT_SUBCOMMAND
|
---|
938 | && subcommand_option != LIST_SUBCOMMAND
|
---|
939 | && subcommand_option != DIFF_SUBCOMMAND)
|
---|
940 | WARN ((0, 0, _("WARNING: Archive is incomplete")));
|
---|
941 |
|
---|
942 | fatal_exit ();
|
---|
943 | }
|
---|
944 |
|
---|
945 | if (input_buffer[0] == '\n'
|
---|
946 | || input_buffer[0] == 'y'
|
---|
947 | || input_buffer[0] == 'Y')
|
---|
948 | break;
|
---|
949 |
|
---|
950 | switch (input_buffer[0])
|
---|
951 | {
|
---|
952 | case '?':
|
---|
953 | {
|
---|
954 | fprintf (stderr, _("\
|
---|
955 | n name Give a new file name for the next (and subsequent) volume(s)\n\
|
---|
956 | q Abort tar\n\
|
---|
957 | y or newline Continue operation\n"));
|
---|
958 | if (!restrict_option)
|
---|
959 | fprintf (stderr, _(" ! Spawn a subshell\n"));
|
---|
960 | fprintf (stderr, _(" ? Print this list\n"));
|
---|
961 | }
|
---|
962 | break;
|
---|
963 |
|
---|
964 | case 'q':
|
---|
965 | /* Quit. */
|
---|
966 |
|
---|
967 | WARN ((0, 0, _("No new volume; exiting.\n")));
|
---|
968 |
|
---|
969 | if (subcommand_option != EXTRACT_SUBCOMMAND
|
---|
970 | && subcommand_option != LIST_SUBCOMMAND
|
---|
971 | && subcommand_option != DIFF_SUBCOMMAND)
|
---|
972 | WARN ((0, 0, _("WARNING: Archive is incomplete")));
|
---|
973 |
|
---|
974 | fatal_exit ();
|
---|
975 |
|
---|
976 | case 'n':
|
---|
977 | /* Get new file name. */
|
---|
978 |
|
---|
979 | {
|
---|
980 | char *name;
|
---|
981 | char *cursor;
|
---|
982 |
|
---|
983 | for (name = input_buffer + 1;
|
---|
984 | *name == ' ' || *name == '\t';
|
---|
985 | name++)
|
---|
986 | ;
|
---|
987 |
|
---|
988 | for (cursor = name; *cursor && *cursor != '\n'; cursor++)
|
---|
989 | ;
|
---|
990 | *cursor = '\0';
|
---|
991 |
|
---|
992 | if (name[0])
|
---|
993 | {
|
---|
994 | /* FIXME: the following allocation is never reclaimed. */
|
---|
995 | *archive_name_cursor = xstrdup (name);
|
---|
996 | stop = true;
|
---|
997 | }
|
---|
998 | else
|
---|
999 | fprintf (stderr, "%s",
|
---|
1000 | _("File name not specified. Try again.\n"));
|
---|
1001 | }
|
---|
1002 | break;
|
---|
1003 |
|
---|
1004 | case '!':
|
---|
1005 | if (!restrict_option)
|
---|
1006 | {
|
---|
1007 | sys_spawn_shell ();
|
---|
1008 | break;
|
---|
1009 | }
|
---|
1010 | /* FALL THROUGH */
|
---|
1011 |
|
---|
1012 | default:
|
---|
1013 | fprintf (stderr, _("Invalid input. Type ? for help.\n"));
|
---|
1014 | }
|
---|
1015 | }
|
---|
1016 | free (input_buffer);
|
---|
1017 | }
|
---|
1018 |
|
---|
1019 | /* We've hit the end of the old volume. Close it and open the next one.
|
---|
1020 | Return nonzero on success.
|
---|
1021 | */
|
---|
1022 | static bool
|
---|
1023 | new_volume (enum access_mode mode)
|
---|
1024 | {
|
---|
1025 | static FILE *read_file;
|
---|
1026 | static int looped;
|
---|
1027 | int prompt;
|
---|
1028 |
|
---|
1029 | if (!read_file && !info_script_option)
|
---|
1030 | /* FIXME: if fopen is used, it will never be closed. */
|
---|
1031 | read_file = archive == STDIN_FILENO ? fopen (TTY_NAME, "r") : stdin;
|
---|
1032 |
|
---|
1033 | if (now_verifying)
|
---|
1034 | return false;
|
---|
1035 | if (verify_option)
|
---|
1036 | verify_volume ();
|
---|
1037 |
|
---|
1038 | assign_string (&volume_label, NULL);
|
---|
1039 | assign_string (&continued_file_name, NULL);
|
---|
1040 | continued_file_size = continued_file_offset = 0;
|
---|
1041 | current_block = record_start;
|
---|
1042 |
|
---|
1043 | if (rmtclose (archive) != 0)
|
---|
1044 | close_warn (*archive_name_cursor);
|
---|
1045 |
|
---|
1046 | archive_name_cursor++;
|
---|
1047 | if (archive_name_cursor == archive_name_array + archive_names)
|
---|
1048 | {
|
---|
1049 | archive_name_cursor = archive_name_array;
|
---|
1050 | looped = 1;
|
---|
1051 | }
|
---|
1052 | prompt = looped;
|
---|
1053 |
|
---|
1054 | tryagain:
|
---|
1055 | if (prompt)
|
---|
1056 | {
|
---|
1057 | /* We have to prompt from now on. */
|
---|
1058 |
|
---|
1059 | if (info_script_option)
|
---|
1060 | {
|
---|
1061 | if (volno_file_option)
|
---|
1062 | closeout_volume_number ();
|
---|
1063 | if (sys_exec_info_script (archive_name_cursor, global_volno+1))
|
---|
1064 | FATAL_ERROR ((0, 0, _("%s command failed"),
|
---|
1065 | quote (info_script_option)));
|
---|
1066 | }
|
---|
1067 | else
|
---|
1068 | change_tape_menu (read_file);
|
---|
1069 | }
|
---|
1070 |
|
---|
1071 | if (strcmp (archive_name_cursor[0], "-") == 0)
|
---|
1072 | {
|
---|
1073 | read_full_records = true;
|
---|
1074 | archive = STDIN_FILENO;
|
---|
1075 | }
|
---|
1076 | else if (verify_option)
|
---|
1077 | archive = rmtopen (*archive_name_cursor, O_RDWR | O_CREAT, MODE_RW,
|
---|
1078 | rsh_command_option);
|
---|
1079 | else
|
---|
1080 | switch (mode)
|
---|
1081 | {
|
---|
1082 | case ACCESS_READ:
|
---|
1083 | archive = rmtopen (*archive_name_cursor, O_RDONLY, MODE_RW,
|
---|
1084 | rsh_command_option);
|
---|
1085 | break;
|
---|
1086 |
|
---|
1087 | case ACCESS_WRITE:
|
---|
1088 | if (backup_option)
|
---|
1089 | maybe_backup_file (*archive_name_cursor, 1);
|
---|
1090 | archive = rmtcreat (*archive_name_cursor, MODE_RW,
|
---|
1091 | rsh_command_option);
|
---|
1092 | break;
|
---|
1093 |
|
---|
1094 | case ACCESS_UPDATE:
|
---|
1095 | archive = rmtopen (*archive_name_cursor, O_RDWR | O_CREAT, MODE_RW,
|
---|
1096 | rsh_command_option);
|
---|
1097 | break;
|
---|
1098 | }
|
---|
1099 |
|
---|
1100 | if (archive < 0)
|
---|
1101 | {
|
---|
1102 | open_warn (*archive_name_cursor);
|
---|
1103 | if (!verify_option && mode == ACCESS_WRITE && backup_option)
|
---|
1104 | undo_last_backup ();
|
---|
1105 | prompt = 1;
|
---|
1106 | goto tryagain;
|
---|
1107 | }
|
---|
1108 |
|
---|
1109 | SET_BINARY_MODE (archive);
|
---|
1110 |
|
---|
1111 | return true;
|
---|
1112 | }
|
---|
1113 |
|
---|
1114 | static bool
|
---|
1115 | read_header0 (struct tar_stat_info *info)
|
---|
1116 | {
|
---|
1117 | enum read_header rc;
|
---|
1118 |
|
---|
1119 | tar_stat_init (info);
|
---|
1120 | rc = read_header_primitive (false, info);
|
---|
1121 | if (rc == HEADER_SUCCESS)
|
---|
1122 | {
|
---|
1123 | set_next_block_after (current_header);
|
---|
1124 | return true;
|
---|
1125 | }
|
---|
1126 | ERROR ((0, 0, _("This does not look like a tar archive")));
|
---|
1127 | return false;
|
---|
1128 | }
|
---|
1129 |
|
---|
1130 | bool
|
---|
1131 | try_new_volume ()
|
---|
1132 | {
|
---|
1133 | size_t status;
|
---|
1134 | union block *header;
|
---|
1135 | struct tar_stat_info dummy;
|
---|
1136 | int access;
|
---|
1137 |
|
---|
1138 | switch (subcommand_option)
|
---|
1139 | {
|
---|
1140 | case APPEND_SUBCOMMAND:
|
---|
1141 | case CAT_SUBCOMMAND:
|
---|
1142 | case UPDATE_SUBCOMMAND:
|
---|
1143 | access = ACCESS_UPDATE;
|
---|
1144 | break;
|
---|
1145 |
|
---|
1146 | default:
|
---|
1147 | access = ACCESS_READ;
|
---|
1148 | break;
|
---|
1149 | }
|
---|
1150 |
|
---|
1151 | if (!new_volume (access))
|
---|
1152 | return true;
|
---|
1153 |
|
---|
1154 | while ((status = rmtread (archive, record_start->buffer, record_size))
|
---|
1155 | == SAFE_READ_ERROR)
|
---|
1156 | archive_read_error ();
|
---|
1157 |
|
---|
1158 | if (status != record_size)
|
---|
1159 | short_read (status);
|
---|
1160 |
|
---|
1161 | header = find_next_block ();
|
---|
1162 | if (!header)
|
---|
1163 | return false;
|
---|
1164 |
|
---|
1165 | switch (header->header.typeflag)
|
---|
1166 | {
|
---|
1167 | case XGLTYPE:
|
---|
1168 | {
|
---|
1169 | if (!read_header0 (&dummy))
|
---|
1170 | return false;
|
---|
1171 | xheader_decode (&dummy); /* decodes values from the global header */
|
---|
1172 | tar_stat_destroy (&dummy);
|
---|
1173 | if (!real_s_name)
|
---|
1174 | {
|
---|
1175 | /* We have read the extended header of the first member in
|
---|
1176 | this volume. Put it back, so next read_header works as
|
---|
1177 | expected. */
|
---|
1178 | current_block = record_start;
|
---|
1179 | }
|
---|
1180 | break;
|
---|
1181 | }
|
---|
1182 |
|
---|
1183 | case GNUTYPE_VOLHDR:
|
---|
1184 | if (!read_header0 (&dummy))
|
---|
1185 | return false;
|
---|
1186 | tar_stat_destroy (&dummy);
|
---|
1187 | assign_string (&volume_label, current_header->header.name);
|
---|
1188 | set_next_block_after (header);
|
---|
1189 | header = find_next_block ();
|
---|
1190 | if (header->header.typeflag != GNUTYPE_MULTIVOL)
|
---|
1191 | break;
|
---|
1192 | /* FALL THROUGH */
|
---|
1193 |
|
---|
1194 | case GNUTYPE_MULTIVOL:
|
---|
1195 | if (!read_header0 (&dummy))
|
---|
1196 | return false;
|
---|
1197 | tar_stat_destroy (&dummy);
|
---|
1198 | assign_string (&continued_file_name, current_header->header.name);
|
---|
1199 | continued_file_size =
|
---|
1200 | UINTMAX_FROM_HEADER (current_header->header.size);
|
---|
1201 | continued_file_offset =
|
---|
1202 | UINTMAX_FROM_HEADER (current_header->oldgnu_header.offset);
|
---|
1203 | break;
|
---|
1204 |
|
---|
1205 | default:
|
---|
1206 | break;
|
---|
1207 | }
|
---|
1208 |
|
---|
1209 | if (real_s_name)
|
---|
1210 | {
|
---|
1211 | uintmax_t s;
|
---|
1212 | if (!continued_file_name
|
---|
1213 | || strcmp (continued_file_name, real_s_name))
|
---|
1214 | {
|
---|
1215 | if ((archive_format == GNU_FORMAT || archive_format == OLDGNU_FORMAT)
|
---|
1216 | && strlen (real_s_name) >= NAME_FIELD_SIZE
|
---|
1217 | && strncmp (continued_file_name, real_s_name,
|
---|
1218 | NAME_FIELD_SIZE) == 0)
|
---|
1219 | WARN ((0, 0,
|
---|
1220 | _("%s is possibly continued on this volume: header contains truncated name"),
|
---|
1221 | quote (real_s_name)));
|
---|
1222 | else
|
---|
1223 | {
|
---|
1224 | WARN ((0, 0, _("%s is not continued on this volume"),
|
---|
1225 | quote (real_s_name)));
|
---|
1226 | return false;
|
---|
1227 | }
|
---|
1228 | }
|
---|
1229 |
|
---|
1230 | s = continued_file_size + continued_file_offset;
|
---|
1231 |
|
---|
1232 | if (real_s_totsize != s || s < continued_file_offset)
|
---|
1233 | {
|
---|
1234 | char totsizebuf[UINTMAX_STRSIZE_BOUND];
|
---|
1235 | char s1buf[UINTMAX_STRSIZE_BOUND];
|
---|
1236 | char s2buf[UINTMAX_STRSIZE_BOUND];
|
---|
1237 |
|
---|
1238 | WARN ((0, 0, _("%s is the wrong size (%s != %s + %s)"),
|
---|
1239 | quote (continued_file_name),
|
---|
1240 | STRINGIFY_BIGINT (save_totsize, totsizebuf),
|
---|
1241 | STRINGIFY_BIGINT (continued_file_size, s1buf),
|
---|
1242 | STRINGIFY_BIGINT (continued_file_offset, s2buf)));
|
---|
1243 | return false;
|
---|
1244 | }
|
---|
1245 |
|
---|
1246 | if (real_s_totsize - real_s_sizeleft != continued_file_offset)
|
---|
1247 | {
|
---|
1248 | WARN ((0, 0, _("This volume is out of sequence")));
|
---|
1249 | return false;
|
---|
1250 | }
|
---|
1251 | }
|
---|
1252 |
|
---|
1253 | increase_volume_number ();
|
---|
1254 | return true;
|
---|
1255 | }
|
---|
1256 |
|
---|
1257 | |
---|
1258 |
|
---|
1259 | /* Check the LABEL block against the volume label, seen as a globbing
|
---|
1260 | pattern. Return true if the pattern matches. In case of failure,
|
---|
1261 | retry matching a volume sequence number before giving up in
|
---|
1262 | multi-volume mode. */
|
---|
1263 | static bool
|
---|
1264 | check_label_pattern (union block *label)
|
---|
1265 | {
|
---|
1266 | char *string;
|
---|
1267 | bool result;
|
---|
1268 |
|
---|
1269 | if (! memchr (label->header.name, '\0', sizeof label->header.name))
|
---|
1270 | return false;
|
---|
1271 |
|
---|
1272 | if (fnmatch (volume_label_option, label->header.name, 0) == 0)
|
---|
1273 | return true;
|
---|
1274 |
|
---|
1275 | if (!multi_volume_option)
|
---|
1276 | return false;
|
---|
1277 |
|
---|
1278 | string = xmalloc (strlen (volume_label_option)
|
---|
1279 | + sizeof VOLUME_LABEL_APPEND + 1);
|
---|
1280 | strcpy (string, volume_label_option);
|
---|
1281 | strcat (string, VOLUME_LABEL_APPEND);
|
---|
1282 | result = fnmatch (string, label->header.name, 0) == 0;
|
---|
1283 | free (string);
|
---|
1284 | return result;
|
---|
1285 | }
|
---|
1286 |
|
---|
1287 | /* Check if the next block contains a volume label and if this matches
|
---|
1288 | the one given in the command line */
|
---|
1289 | static void
|
---|
1290 | match_volume_label (void)
|
---|
1291 | {
|
---|
1292 | union block *label = find_next_block ();
|
---|
1293 |
|
---|
1294 | if (!label)
|
---|
1295 | FATAL_ERROR ((0, 0, _("Archive not labeled to match %s"),
|
---|
1296 | quote (volume_label_option)));
|
---|
1297 | if (!check_label_pattern (label))
|
---|
1298 | FATAL_ERROR ((0, 0, _("Volume %s does not match %s"),
|
---|
1299 | quote_n (0, label->header.name),
|
---|
1300 | quote_n (1, volume_label_option)));
|
---|
1301 | }
|
---|
1302 |
|
---|
1303 | /* Mark the archive with volume label STR. */
|
---|
1304 | static void
|
---|
1305 | _write_volume_label (const char *str)
|
---|
1306 | {
|
---|
1307 | if (archive_format == POSIX_FORMAT)
|
---|
1308 | xheader_store ("GNU.volume.label", NULL, str);
|
---|
1309 | else
|
---|
1310 | {
|
---|
1311 | union block *label = find_next_block ();
|
---|
1312 |
|
---|
1313 | memset (label, 0, BLOCKSIZE);
|
---|
1314 |
|
---|
1315 | strcpy (label->header.name, volume_label_option);
|
---|
1316 | assign_string (¤t_stat_info.file_name,
|
---|
1317 | label->header.name);
|
---|
1318 | current_stat_info.had_trailing_slash =
|
---|
1319 | strip_trailing_slashes (current_stat_info.file_name);
|
---|
1320 |
|
---|
1321 | label->header.typeflag = GNUTYPE_VOLHDR;
|
---|
1322 | TIME_TO_CHARS (start_time.tv_sec, label->header.mtime);
|
---|
1323 | finish_header (¤t_stat_info, label, -1);
|
---|
1324 | set_next_block_after (label);
|
---|
1325 | }
|
---|
1326 | }
|
---|
1327 |
|
---|
1328 | #define VOL_SUFFIX "Volume"
|
---|
1329 |
|
---|
1330 | /* Add a volume label to a part of multi-volume archive */
|
---|
1331 | static void
|
---|
1332 | add_volume_label (void)
|
---|
1333 | {
|
---|
1334 | char buf[UINTMAX_STRSIZE_BOUND];
|
---|
1335 | char *p = STRINGIFY_BIGINT (volno, buf);
|
---|
1336 | char *s = xmalloc (strlen (volume_label_option) + sizeof VOL_SUFFIX
|
---|
1337 | + strlen (p) + 2);
|
---|
1338 | sprintf (s, "%s %s %s", volume_label_option, VOL_SUFFIX, p);
|
---|
1339 | _write_volume_label (s);
|
---|
1340 | free (s);
|
---|
1341 | }
|
---|
1342 |
|
---|
1343 | static void
|
---|
1344 | add_chunk_header ()
|
---|
1345 | {
|
---|
1346 | if (archive_format == POSIX_FORMAT)
|
---|
1347 | {
|
---|
1348 | off_t block_ordinal;
|
---|
1349 | union block *blk;
|
---|
1350 | struct tar_stat_info st;
|
---|
1351 | static size_t real_s_part_no; /* FIXME */
|
---|
1352 |
|
---|
1353 | real_s_part_no++;
|
---|
1354 | memset (&st, 0, sizeof st);
|
---|
1355 | st.orig_file_name = st.file_name = real_s_name;
|
---|
1356 | st.stat.st_mode = S_IFREG|S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;
|
---|
1357 | st.stat.st_uid = getuid ();
|
---|
1358 | st.stat.st_gid = getgid ();
|
---|
1359 | st.orig_file_name = xheader_format_name (&st,
|
---|
1360 | "%d/GNUFileParts.%p/%f.%n",
|
---|
1361 | real_s_part_no);
|
---|
1362 | st.file_name = st.orig_file_name;
|
---|
1363 | st.archive_file_size = st.stat.st_size = real_s_sizeleft;
|
---|
1364 |
|
---|
1365 | block_ordinal = current_block_ordinal ();
|
---|
1366 | blk = start_header (&st);
|
---|
1367 | if (!blk)
|
---|
1368 | abort (); /* FIXME */
|
---|
1369 | finish_header (&st, blk, block_ordinal);
|
---|
1370 | free (st.orig_file_name);
|
---|
1371 | }
|
---|
1372 | }
|
---|
1373 |
|
---|
1374 |
|
---|
1375 | /* Add a volume label to the current archive */
|
---|
1376 | static void
|
---|
1377 | write_volume_label (void)
|
---|
1378 | {
|
---|
1379 | if (multi_volume_option)
|
---|
1380 | add_volume_label ();
|
---|
1381 | else
|
---|
1382 | _write_volume_label (volume_label_option);
|
---|
1383 | }
|
---|
1384 |
|
---|
1385 | /* Write GNU multi-volume header */
|
---|
1386 | static void
|
---|
1387 | gnu_add_multi_volume_header (void)
|
---|
1388 | {
|
---|
1389 | int tmp;
|
---|
1390 | union block *block = find_next_block ();
|
---|
1391 |
|
---|
1392 | if (strlen (real_s_name) > NAME_FIELD_SIZE)
|
---|
1393 | WARN ((0, 0,
|
---|
1394 | _("%s: file name too long to be stored in a GNU multivolume header, truncated"),
|
---|
1395 | quotearg_colon (real_s_name)));
|
---|
1396 |
|
---|
1397 | memset (block, 0, BLOCKSIZE);
|
---|
1398 |
|
---|
1399 | /* FIXME: Michael P Urban writes: [a long name file] is being written
|
---|
1400 | when a new volume rolls around [...] Looks like the wrong value is
|
---|
1401 | being preserved in real_s_name, though. */
|
---|
1402 |
|
---|
1403 | strncpy (block->header.name, real_s_name, NAME_FIELD_SIZE);
|
---|
1404 | block->header.typeflag = GNUTYPE_MULTIVOL;
|
---|
1405 |
|
---|
1406 | OFF_TO_CHARS (real_s_sizeleft, block->header.size);
|
---|
1407 | OFF_TO_CHARS (real_s_totsize - real_s_sizeleft,
|
---|
1408 | block->oldgnu_header.offset);
|
---|
1409 |
|
---|
1410 | tmp = verbose_option;
|
---|
1411 | verbose_option = 0;
|
---|
1412 | finish_header (¤t_stat_info, block, -1);
|
---|
1413 | verbose_option = tmp;
|
---|
1414 | set_next_block_after (block);
|
---|
1415 | }
|
---|
1416 |
|
---|
1417 | /* Add a multi volume header to the current archive. The exact header format
|
---|
1418 | depends on the archive format. */
|
---|
1419 | static void
|
---|
1420 | add_multi_volume_header (void)
|
---|
1421 | {
|
---|
1422 | if (archive_format == POSIX_FORMAT)
|
---|
1423 | {
|
---|
1424 | off_t d = real_s_totsize - real_s_sizeleft;
|
---|
1425 | xheader_store ("GNU.volume.filename", NULL, real_s_name);
|
---|
1426 | xheader_store ("GNU.volume.size", NULL, &real_s_sizeleft);
|
---|
1427 | xheader_store ("GNU.volume.offset", NULL, &d);
|
---|
1428 | }
|
---|
1429 | else
|
---|
1430 | gnu_add_multi_volume_header ();
|
---|
1431 | }
|
---|
1432 |
|
---|
1433 | /* Synchronize multi-volume globals */
|
---|
1434 | static void
|
---|
1435 | multi_volume_sync ()
|
---|
1436 | {
|
---|
1437 | if (multi_volume_option)
|
---|
1438 | {
|
---|
1439 | if (save_name)
|
---|
1440 | {
|
---|
1441 | assign_string (&real_s_name,
|
---|
1442 | safer_name_suffix (save_name, false,
|
---|
1443 | absolute_names_option));
|
---|
1444 | real_s_totsize = save_totsize;
|
---|
1445 | real_s_sizeleft = save_sizeleft;
|
---|
1446 | }
|
---|
1447 | else
|
---|
1448 | {
|
---|
1449 | assign_string (&real_s_name, 0);
|
---|
1450 | real_s_totsize = 0;
|
---|
1451 | real_s_sizeleft = 0;
|
---|
1452 | }
|
---|
1453 | }
|
---|
1454 | }
|
---|
1455 |
|
---|
1456 | |
---|
1457 |
|
---|
1458 | /* Low-level flush functions */
|
---|
1459 |
|
---|
1460 | /* Simple flush read (no multi-volume or label extensions) */
|
---|
1461 | static void
|
---|
1462 | simple_flush_read (void)
|
---|
1463 | {
|
---|
1464 | size_t status; /* result from system call */
|
---|
1465 |
|
---|
1466 | do_checkpoint (false);
|
---|
1467 |
|
---|
1468 | /* Clear the count of errors. This only applies to a single call to
|
---|
1469 | flush_read. */
|
---|
1470 |
|
---|
1471 | read_error_count = 0; /* clear error count */
|
---|
1472 |
|
---|
1473 | if (write_archive_to_stdout && record_start_block != 0)
|
---|
1474 | {
|
---|
1475 | archive = STDOUT_FILENO;
|
---|
1476 | status = sys_write_archive_buffer ();
|
---|
1477 | archive = STDIN_FILENO;
|
---|
1478 | if (status != record_size)
|
---|
1479 | archive_write_error (status);
|
---|
1480 | }
|
---|
1481 |
|
---|
1482 | for (;;)
|
---|
1483 | {
|
---|
1484 | status = rmtread (archive, record_start->buffer, record_size);
|
---|
1485 | if (status == record_size)
|
---|
1486 | {
|
---|
1487 | records_read++;
|
---|
1488 | return;
|
---|
1489 | }
|
---|
1490 | if (status == SAFE_READ_ERROR)
|
---|
1491 | {
|
---|
1492 | archive_read_error ();
|
---|
1493 | continue; /* try again */
|
---|
1494 | }
|
---|
1495 | break;
|
---|
1496 | }
|
---|
1497 | short_read (status);
|
---|
1498 | }
|
---|
1499 |
|
---|
1500 | /* Simple flush write (no multi-volume or label extensions) */
|
---|
1501 | static void
|
---|
1502 | simple_flush_write (size_t level __attribute__((unused)))
|
---|
1503 | {
|
---|
1504 | ssize_t status;
|
---|
1505 |
|
---|
1506 | status = _flush_write ();
|
---|
1507 | if (status != record_size)
|
---|
1508 | archive_write_error (status);
|
---|
1509 | else
|
---|
1510 | {
|
---|
1511 | records_written++;
|
---|
1512 | bytes_written += status;
|
---|
1513 | }
|
---|
1514 | }
|
---|
1515 |
|
---|
1516 | |
---|
1517 |
|
---|
1518 | /* GNU flush functions. These support multi-volume and archive labels in
|
---|
1519 | GNU and PAX archive formats. */
|
---|
1520 |
|
---|
1521 | static void
|
---|
1522 | _gnu_flush_read (void)
|
---|
1523 | {
|
---|
1524 | size_t status; /* result from system call */
|
---|
1525 |
|
---|
1526 | do_checkpoint (false);
|
---|
1527 |
|
---|
1528 | /* Clear the count of errors. This only applies to a single call to
|
---|
1529 | flush_read. */
|
---|
1530 |
|
---|
1531 | read_error_count = 0; /* clear error count */
|
---|
1532 |
|
---|
1533 | if (write_archive_to_stdout && record_start_block != 0)
|
---|
1534 | {
|
---|
1535 | archive = STDOUT_FILENO;
|
---|
1536 | status = sys_write_archive_buffer ();
|
---|
1537 | archive = STDIN_FILENO;
|
---|
1538 | if (status != record_size)
|
---|
1539 | archive_write_error (status);
|
---|
1540 | }
|
---|
1541 |
|
---|
1542 | multi_volume_sync ();
|
---|
1543 |
|
---|
1544 | for (;;)
|
---|
1545 | {
|
---|
1546 | status = rmtread (archive, record_start->buffer, record_size);
|
---|
1547 | if (status == record_size)
|
---|
1548 | {
|
---|
1549 | records_read++;
|
---|
1550 | return;
|
---|
1551 | }
|
---|
1552 |
|
---|
1553 | /* The condition below used to include
|
---|
1554 | || (status > 0 && !read_full_records)
|
---|
1555 | This is incorrect since even if new_volume() succeeds, the
|
---|
1556 | subsequent call to rmtread will overwrite the chunk of data
|
---|
1557 | already read in the buffer, so the processing will fail */
|
---|
1558 | if ((status == 0
|
---|
1559 | || (status == SAFE_READ_ERROR && errno == ENOSPC))
|
---|
1560 | && multi_volume_option)
|
---|
1561 | {
|
---|
1562 | while (!try_new_volume ())
|
---|
1563 | ;
|
---|
1564 | return;
|
---|
1565 | }
|
---|
1566 | else if (status == SAFE_READ_ERROR)
|
---|
1567 | {
|
---|
1568 | archive_read_error ();
|
---|
1569 | continue;
|
---|
1570 | }
|
---|
1571 | break;
|
---|
1572 | }
|
---|
1573 | short_read (status);
|
---|
1574 | }
|
---|
1575 |
|
---|
1576 | static void
|
---|
1577 | gnu_flush_read (void)
|
---|
1578 | {
|
---|
1579 | flush_read_ptr = simple_flush_read; /* Avoid recursion */
|
---|
1580 | _gnu_flush_read ();
|
---|
1581 | flush_read_ptr = gnu_flush_read;
|
---|
1582 | }
|
---|
1583 |
|
---|
1584 | static void
|
---|
1585 | _gnu_flush_write (size_t buffer_level)
|
---|
1586 | {
|
---|
1587 | ssize_t status;
|
---|
1588 | union block *header;
|
---|
1589 | char *copy_ptr;
|
---|
1590 | size_t copy_size;
|
---|
1591 | size_t bufsize;
|
---|
1592 |
|
---|
1593 | status = _flush_write ();
|
---|
1594 | if (status != record_size && !multi_volume_option)
|
---|
1595 | archive_write_error (status);
|
---|
1596 | else
|
---|
1597 | {
|
---|
1598 | records_written++;
|
---|
1599 | bytes_written += status;
|
---|
1600 | }
|
---|
1601 |
|
---|
1602 | if (status == record_size)
|
---|
1603 | {
|
---|
1604 | multi_volume_sync ();
|
---|
1605 | return;
|
---|
1606 | }
|
---|
1607 |
|
---|
1608 | /* In multi-volume mode. */
|
---|
1609 | /* ENXIO is for the UNIX PC. */
|
---|
1610 | if (status < 0 && errno != ENOSPC && errno != EIO && errno != ENXIO)
|
---|
1611 | archive_write_error (status);
|
---|
1612 |
|
---|
1613 | if (!new_volume (ACCESS_WRITE))
|
---|
1614 | return;
|
---|
1615 |
|
---|
1616 | xheader_destroy (&extended_header);
|
---|
1617 |
|
---|
1618 | increase_volume_number ();
|
---|
1619 | prev_written += bytes_written;
|
---|
1620 | bytes_written = 0;
|
---|
1621 |
|
---|
1622 | copy_ptr = record_start->buffer + status;
|
---|
1623 | copy_size = buffer_level - status;
|
---|
1624 | /* Switch to the next buffer */
|
---|
1625 | record_index = !record_index;
|
---|
1626 | init_buffer ();
|
---|
1627 |
|
---|
1628 | if (volume_label_option)
|
---|
1629 | add_volume_label ();
|
---|
1630 |
|
---|
1631 | if (real_s_name)
|
---|
1632 | add_multi_volume_header ();
|
---|
1633 |
|
---|
1634 | write_extended (true, NULL, find_next_block ());
|
---|
1635 | if (real_s_name)
|
---|
1636 | add_chunk_header ();
|
---|
1637 | header = find_next_block ();
|
---|
1638 | bufsize = available_space_after (header);
|
---|
1639 | while (bufsize < copy_size)
|
---|
1640 | {
|
---|
1641 | memcpy (header->buffer, copy_ptr, bufsize);
|
---|
1642 | copy_ptr += bufsize;
|
---|
1643 | copy_size -= bufsize;
|
---|
1644 | set_next_block_after (header + (bufsize - 1) / BLOCKSIZE);
|
---|
1645 | header = find_next_block ();
|
---|
1646 | bufsize = available_space_after (header);
|
---|
1647 | }
|
---|
1648 | memcpy (header->buffer, copy_ptr, copy_size);
|
---|
1649 | memset (header->buffer + copy_size, 0, bufsize - copy_size);
|
---|
1650 | set_next_block_after (header + (copy_size - 1) / BLOCKSIZE);
|
---|
1651 | find_next_block ();
|
---|
1652 | }
|
---|
1653 |
|
---|
1654 | static void
|
---|
1655 | gnu_flush_write (size_t buffer_level)
|
---|
1656 | {
|
---|
1657 | flush_write_ptr = simple_flush_write; /* Avoid recursion */
|
---|
1658 | _gnu_flush_write (buffer_level);
|
---|
1659 | flush_write_ptr = gnu_flush_write;
|
---|
1660 | }
|
---|
1661 |
|
---|
1662 | void
|
---|
1663 | flush_read ()
|
---|
1664 | {
|
---|
1665 | flush_read_ptr ();
|
---|
1666 | }
|
---|
1667 |
|
---|
1668 | void
|
---|
1669 | flush_write ()
|
---|
1670 | {
|
---|
1671 | flush_write_ptr (record_size);
|
---|
1672 | }
|
---|
1673 |
|
---|
1674 | void
|
---|
1675 | open_archive (enum access_mode wanted_access)
|
---|
1676 | {
|
---|
1677 | flush_read_ptr = gnu_flush_read;
|
---|
1678 | flush_write_ptr = gnu_flush_write;
|
---|
1679 |
|
---|
1680 | _open_archive (wanted_access);
|
---|
1681 | switch (wanted_access)
|
---|
1682 | {
|
---|
1683 | case ACCESS_READ:
|
---|
1684 | if (volume_label_option)
|
---|
1685 | match_volume_label ();
|
---|
1686 | break;
|
---|
1687 |
|
---|
1688 | case ACCESS_WRITE:
|
---|
1689 | records_written = 0;
|
---|
1690 | if (volume_label_option)
|
---|
1691 | write_volume_label ();
|
---|
1692 | break;
|
---|
1693 |
|
---|
1694 | default:
|
---|
1695 | break;
|
---|
1696 | }
|
---|
1697 | set_volume_start_time ();
|
---|
1698 | }
|
---|