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