]> Dogcows Code - chaz/tar/blob - src/buffer.c
Relicense under GPLv3
[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
239 /* Prepare global data needed for find_next_block: */
240 record_end = record_start; /* set up for 1st record = # 0 */
241 sfr = read_full_records;
242 read_full_records = true; /* Suppress fatal error on reading a partial
243 record */
244 find_next_block ();
245
246 /* Restore global values */
247 read_full_records = sfr;
248
249 if (tar_checksum (record_start, true) == HEADER_SUCCESS)
250 /* Probably a valid header */
251 return ct_none;
252
253 for (p = magic + 1; p < magic + NMAGIC; p++)
254 if (memcmp (record_start->buffer, p->magic, p->length) == 0)
255 return p->type;
256
257 return ct_none;
258 }
259
260 /* Open an archive named archive_name_array[0]. Detect if it is
261 a compressed archive of known type and use corresponding decompression
262 program if so */
263 int
264 open_compressed_archive ()
265 {
266 archive = rmtopen (archive_name_array[0], O_RDONLY | O_BINARY,
267 MODE_RW, rsh_command_option);
268 if (archive == -1)
269 return archive;
270
271 if (!multi_volume_option)
272 {
273 enum compress_type type = check_compressed_archive ();
274
275 if (type == ct_none)
276 return archive;
277
278 /* FD is not needed any more */
279 rmtclose (archive);
280
281 hit_eof = false; /* It might have been set by find_next_block in
282 check_compressed_archive */
283
284 /* Open compressed archive */
285 use_compress_program_option = compress_program (type);
286 child_pid = sys_child_open_for_uncompress ();
287 read_full_records = true;
288 }
289
290 records_read = 0;
291 record_end = record_start; /* set up for 1st record = # 0 */
292
293 return archive;
294 }
295 \f
296
297 static void
298 print_stats (FILE *fp, const char *text, tarlong numbytes)
299 {
300 char bytes[sizeof (tarlong) * CHAR_BIT];
301 char abbr[LONGEST_HUMAN_READABLE + 1];
302 char rate[LONGEST_HUMAN_READABLE + 1];
303
304 int human_opts = human_autoscale | human_base_1024 | human_SI | human_B;
305
306 sprintf (bytes, TARLONG_FORMAT, numbytes);
307
308 fprintf (fp, "%s: %s (%s, %s/s)\n",
309 text, bytes,
310 human_readable (numbytes, abbr, human_opts, 1, 1),
311 (0 < duration && numbytes / duration < (uintmax_t) -1
312 ? human_readable (numbytes / duration, rate, human_opts, 1, 1)
313 : "?"));
314 }
315
316 void
317 print_total_stats ()
318 {
319 switch (subcommand_option)
320 {
321 case CREATE_SUBCOMMAND:
322 case CAT_SUBCOMMAND:
323 case UPDATE_SUBCOMMAND:
324 case APPEND_SUBCOMMAND:
325 /* Amanda 2.4.1p1 looks for "Total bytes written: [0-9][0-9]*". */
326 print_stats (stderr, _("Total bytes written"),
327 prev_written + bytes_written);
328 break;
329
330 case DELETE_SUBCOMMAND:
331 {
332 char buf[UINTMAX_STRSIZE_BOUND];
333 print_stats (stderr, _("Total bytes read"),
334 records_read * record_size);
335 print_stats (stderr, _("Total bytes written"),
336 prev_written + bytes_written);
337 fprintf (stderr, _("Total bytes deleted: %s\n"),
338 STRINGIFY_BIGINT ((records_read - records_skipped)
339 * record_size
340 - (prev_written + bytes_written), buf));
341 }
342 break;
343
344 case EXTRACT_SUBCOMMAND:
345 case LIST_SUBCOMMAND:
346 case DIFF_SUBCOMMAND:
347 print_stats (stderr, _("Total bytes read"),
348 records_read * record_size);
349 break;
350
351 default:
352 abort ();
353 }
354 }
355
356 /* Compute and return the block ordinal at current_block. */
357 off_t
358 current_block_ordinal (void)
359 {
360 return record_start_block + (current_block - record_start);
361 }
362
363 /* If the EOF flag is set, reset it, as well as current_block, etc. */
364 void
365 reset_eof (void)
366 {
367 if (hit_eof)
368 {
369 hit_eof = false;
370 current_block = record_start;
371 record_end = record_start + blocking_factor;
372 access_mode = ACCESS_WRITE;
373 }
374 }
375
376 /* Return the location of the next available input or output block.
377 Return zero for EOF. Once we have returned zero, we just keep returning
378 it, to avoid accidentally going on to the next file on the tape. */
379 union block *
380 find_next_block (void)
381 {
382 if (current_block == record_end)
383 {
384 if (hit_eof)
385 return 0;
386 flush_archive ();
387 if (current_block == record_end)
388 {
389 hit_eof = true;
390 return 0;
391 }
392 }
393 return current_block;
394 }
395
396 /* Indicate that we have used all blocks up thru BLOCK. */
397 void
398 set_next_block_after (union block *block)
399 {
400 while (block >= current_block)
401 current_block++;
402
403 /* Do *not* flush the archive here. If we do, the same argument to
404 set_next_block_after could mean the next block (if the input record
405 is exactly one block long), which is not what is intended. */
406
407 if (current_block > record_end)
408 abort ();
409 }
410
411 /* Return the number of bytes comprising the space between POINTER
412 through the end of the current buffer of blocks. This space is
413 available for filling with data, or taking data from. POINTER is
414 usually (but not always) the result of previous find_next_block call. */
415 size_t
416 available_space_after (union block *pointer)
417 {
418 return record_end->buffer - pointer->buffer;
419 }
420
421 /* Close file having descriptor FD, and abort if close unsuccessful. */
422 void
423 xclose (int fd)
424 {
425 if (close (fd) != 0)
426 close_error (_("(pipe)"));
427 }
428
429 static void
430 init_buffer ()
431 {
432 if (! record_buffer_aligned[record_index])
433 record_buffer_aligned[record_index] =
434 page_aligned_alloc (&record_buffer[record_index], record_size);
435
436 record_start = record_buffer_aligned[record_index];
437 current_block = record_start;
438 record_end = record_start + blocking_factor;
439 }
440
441 /* Open an archive file. The argument specifies whether we are
442 reading or writing, or both. */
443 static void
444 _open_archive (enum access_mode wanted_access)
445 {
446 int backed_up_flag = 0;
447
448 if (record_size == 0)
449 FATAL_ERROR ((0, 0, _("Invalid value for record_size")));
450
451 if (archive_names == 0)
452 FATAL_ERROR ((0, 0, _("No archive name given")));
453
454 tar_stat_destroy (&current_stat_info);
455 save_name = 0;
456 real_s_name = 0;
457
458 record_index = 0;
459 init_buffer ();
460
461 /* When updating the archive, we start with reading. */
462 access_mode = wanted_access == ACCESS_UPDATE ? ACCESS_READ : wanted_access;
463
464 read_full_records = read_full_records_option;
465
466 records_read = 0;
467
468 if (use_compress_program_option)
469 {
470 switch (wanted_access)
471 {
472 case ACCESS_READ:
473 child_pid = sys_child_open_for_uncompress ();
474 read_full_records = true;
475 record_end = record_start; /* set up for 1st record = # 0 */
476 break;
477
478 case ACCESS_WRITE:
479 child_pid = sys_child_open_for_compress ();
480 break;
481
482 case ACCESS_UPDATE:
483 abort (); /* Should not happen */
484 break;
485 }
486
487 if (!index_file_name
488 && wanted_access == ACCESS_WRITE
489 && strcmp (archive_name_array[0], "-") == 0)
490 stdlis = stderr;
491 }
492 else if (strcmp (archive_name_array[0], "-") == 0)
493 {
494 read_full_records = true; /* could be a pipe, be safe */
495 if (verify_option)
496 FATAL_ERROR ((0, 0, _("Cannot verify stdin/stdout archive")));
497
498 switch (wanted_access)
499 {
500 case ACCESS_READ:
501 {
502 enum compress_type type;
503
504 archive = STDIN_FILENO;
505
506 type = check_compressed_archive ();
507 if (type != ct_none)
508 FATAL_ERROR ((0, 0,
509 _("Archive is compressed. Use %s option"),
510 compress_option (type)));
511 }
512 break;
513
514 case ACCESS_WRITE:
515 archive = STDOUT_FILENO;
516 if (!index_file_name)
517 stdlis = stderr;
518 break;
519
520 case ACCESS_UPDATE:
521 archive = STDIN_FILENO;
522 write_archive_to_stdout = true;
523 record_end = record_start; /* set up for 1st record = # 0 */
524 if (!index_file_name)
525 stdlis = stderr;
526 break;
527 }
528 }
529 else if (verify_option)
530 archive = rmtopen (archive_name_array[0], O_RDWR | O_CREAT | O_BINARY,
531 MODE_RW, rsh_command_option);
532 else
533 switch (wanted_access)
534 {
535 case ACCESS_READ:
536 archive = open_compressed_archive ();
537 break;
538
539 case ACCESS_WRITE:
540 if (backup_option)
541 {
542 maybe_backup_file (archive_name_array[0], 1);
543 backed_up_flag = 1;
544 }
545 archive = rmtcreat (archive_name_array[0], MODE_RW,
546 rsh_command_option);
547 break;
548
549 case ACCESS_UPDATE:
550 archive = rmtopen (archive_name_array[0],
551 O_RDWR | O_CREAT | O_BINARY,
552 MODE_RW, rsh_command_option);
553
554 if (check_compressed_archive () != ct_none)
555 FATAL_ERROR ((0, 0,
556 _("Cannot update compressed archives")));
557 break;
558 }
559
560 if (archive < 0
561 || (! _isrmt (archive) && !sys_get_archive_stat ()))
562 {
563 int saved_errno = errno;
564
565 if (backed_up_flag)
566 undo_last_backup ();
567 errno = saved_errno;
568 open_fatal (archive_name_array[0]);
569 }
570
571 sys_detect_dev_null_output ();
572 sys_save_archive_dev_ino ();
573 SET_BINARY_MODE (archive);
574
575 switch (wanted_access)
576 {
577 case ACCESS_READ:
578 find_next_block (); /* read it in, check for EOF */
579 break;
580
581 case ACCESS_UPDATE:
582 case ACCESS_WRITE:
583 records_written = 0;
584 break;
585 }
586 }
587
588 static void
589 do_checkpoint (bool write)
590 {
591 if (checkpoint_option && !(++checkpoint % checkpoint_option))
592 {
593 switch (checkpoint_style)
594 {
595 case checkpoint_dot:
596 fputc ('.', stdlis);
597 fflush (stdlis);
598 break;
599
600 case checkpoint_text:
601 if (write)
602 /* TRANSLATORS: This is a ``checkpoint of write operation'',
603 *not* ``Writing a checkpoint''.
604 E.g. in Spanish ``Punto de comprobaci@'on de escritura'',
605 *not* ``Escribiendo un punto de comprobaci@'on'' */
606 WARN ((0, 0, _("Write checkpoint %u"), checkpoint));
607 else
608 /* TRANSLATORS: This is a ``checkpoint of read operation'',
609 *not* ``Reading a checkpoint''.
610 E.g. in Spanish ``Punto de comprobaci@'on de lectura'',
611 *not* ``Leyendo un punto de comprobaci@'on'' */
612 WARN ((0, 0, _("Read checkpoint %u"), checkpoint));
613 break;
614 }
615 }
616 }
617
618 /* Perform a write to flush the buffer. */
619 ssize_t
620 _flush_write (void)
621 {
622 ssize_t status;
623
624 do_checkpoint (true);
625 if (tape_length_option && tape_length_option <= bytes_written)
626 {
627 errno = ENOSPC;
628 status = 0;
629 }
630 else if (dev_null_output)
631 status = record_size;
632 else
633 status = sys_write_archive_buffer ();
634
635 return status;
636 }
637
638 /* Handle write errors on the archive. Write errors are always fatal.
639 Hitting the end of a volume does not cause a write error unless the
640 write was the first record of the volume. */
641 void
642 archive_write_error (ssize_t status)
643 {
644 /* It might be useful to know how much was written before the error
645 occurred. */
646 if (totals_option)
647 {
648 int e = errno;
649 print_total_stats ();
650 errno = e;
651 }
652
653 write_fatal_details (*archive_name_cursor, status, record_size);
654 }
655
656 /* Handle read errors on the archive. If the read should be retried,
657 return to the caller. */
658 void
659 archive_read_error (void)
660 {
661 read_error (*archive_name_cursor);
662
663 if (record_start_block == 0)
664 FATAL_ERROR ((0, 0, _("At beginning of tape, quitting now")));
665
666 /* Read error in mid archive. We retry up to READ_ERROR_MAX times and
667 then give up on reading the archive. */
668
669 if (read_error_count++ > READ_ERROR_MAX)
670 FATAL_ERROR ((0, 0, _("Too many errors, quitting")));
671 return;
672 }
673
674 static void
675 short_read (size_t status)
676 {
677 size_t left; /* bytes left */
678 char *more; /* pointer to next byte to read */
679
680 more = record_start->buffer + status;
681 left = record_size - status;
682
683 while (left % BLOCKSIZE != 0
684 || (left && status && read_full_records))
685 {
686 if (status)
687 while ((status = rmtread (archive, more, left)) == SAFE_READ_ERROR)
688 archive_read_error ();
689
690 if (status == 0)
691 break;
692
693 if (! read_full_records)
694 {
695 unsigned long rest = record_size - left;
696
697 FATAL_ERROR ((0, 0,
698 ngettext ("Unaligned block (%lu byte) in archive",
699 "Unaligned block (%lu bytes) in archive",
700 rest),
701 rest));
702 }
703
704 /* User warned us about this. Fix up. */
705
706 left -= status;
707 more += status;
708 }
709
710 /* FIXME: for size=0, multi-volume support. On the first record, warn
711 about the problem. */
712
713 if (!read_full_records && verbose_option > 1
714 && record_start_block == 0 && status != 0)
715 {
716 unsigned long rsize = (record_size - left) / BLOCKSIZE;
717 WARN ((0, 0,
718 ngettext ("Record size = %lu block",
719 "Record size = %lu blocks",
720 rsize),
721 rsize));
722 }
723
724 record_end = record_start + (record_size - left) / BLOCKSIZE;
725 records_read++;
726 }
727
728 /* Flush the current buffer to/from the archive. */
729 void
730 flush_archive (void)
731 {
732 size_t buffer_level = current_block->buffer - record_start->buffer;
733 record_start_block += record_end - record_start;
734 current_block = record_start;
735 record_end = record_start + blocking_factor;
736
737 if (access_mode == ACCESS_READ && time_to_start_writing)
738 {
739 access_mode = ACCESS_WRITE;
740 time_to_start_writing = false;
741 backspace_output ();
742 }
743
744 switch (access_mode)
745 {
746 case ACCESS_READ:
747 flush_read ();
748 break;
749
750 case ACCESS_WRITE:
751 flush_write_ptr (buffer_level);
752 break;
753
754 case ACCESS_UPDATE:
755 abort ();
756 }
757 }
758
759 /* Backspace the archive descriptor by one record worth. If it's a
760 tape, MTIOCTOP will work. If it's something else, try to seek on
761 it. If we can't seek, we lose! */
762 static void
763 backspace_output (void)
764 {
765 #ifdef MTIOCTOP
766 {
767 struct mtop operation;
768
769 operation.mt_op = MTBSR;
770 operation.mt_count = 1;
771 if (rmtioctl (archive, MTIOCTOP, (char *) &operation) >= 0)
772 return;
773 if (errno == EIO && rmtioctl (archive, MTIOCTOP, (char *) &operation) >= 0)
774 return;
775 }
776 #endif
777
778 {
779 off_t position = rmtlseek (archive, (off_t) 0, SEEK_CUR);
780
781 /* Seek back to the beginning of this record and start writing there. */
782
783 position -= record_size;
784 if (position < 0)
785 position = 0;
786 if (rmtlseek (archive, position, SEEK_SET) != position)
787 {
788 /* Lseek failed. Try a different method. */
789
790 WARN ((0, 0,
791 _("Cannot backspace archive file; it may be unreadable without -i")));
792
793 /* Replace the first part of the record with NULs. */
794
795 if (record_start->buffer != output_start)
796 memset (record_start->buffer, 0,
797 output_start - record_start->buffer);
798 }
799 }
800 }
801
802 off_t
803 seek_archive (off_t size)
804 {
805 off_t start = current_block_ordinal ();
806 off_t offset;
807 off_t nrec, nblk;
808 off_t skipped = (blocking_factor - (current_block - record_start));
809
810 size -= skipped * BLOCKSIZE;
811
812 if (size < record_size)
813 return 0;
814 /* FIXME: flush? */
815
816 /* Compute number of records to skip */
817 nrec = size / record_size;
818 offset = rmtlseek (archive, nrec * record_size, SEEK_CUR);
819 if (offset < 0)
820 return offset;
821
822 if (offset % record_size)
823 FATAL_ERROR ((0, 0, _("rmtlseek not stopped at a record boundary")));
824
825 /* Convert to number of records */
826 offset /= BLOCKSIZE;
827 /* Compute number of skipped blocks */
828 nblk = offset - start;
829
830 /* Update buffering info */
831 records_read += nblk / blocking_factor;
832 record_start_block = offset - blocking_factor;
833 current_block = record_end;
834
835 return nblk;
836 }
837
838 /* Close the archive file. */
839 void
840 close_archive (void)
841 {
842 if (time_to_start_writing || access_mode == ACCESS_WRITE)
843 {
844 flush_archive ();
845 if (current_block > record_start)
846 flush_archive ();
847 }
848
849 sys_drain_input_pipe ();
850
851 compute_duration ();
852 if (verify_option)
853 verify_volume ();
854
855 if (rmtclose (archive) != 0)
856 close_warn (*archive_name_cursor);
857
858 sys_wait_for_child (child_pid);
859
860 tar_stat_destroy (&current_stat_info);
861 if (save_name)
862 free (save_name);
863 if (real_s_name)
864 free (real_s_name);
865 free (record_buffer[0]);
866 free (record_buffer[1]);
867 }
868
869 /* Called to initialize the global volume number. */
870 void
871 init_volume_number (void)
872 {
873 FILE *file = fopen (volno_file_option, "r");
874
875 if (file)
876 {
877 if (fscanf (file, "%d", &global_volno) != 1
878 || global_volno < 0)
879 FATAL_ERROR ((0, 0, _("%s: contains invalid volume number"),
880 quotearg_colon (volno_file_option)));
881 if (ferror (file))
882 read_error (volno_file_option);
883 if (fclose (file) != 0)
884 close_error (volno_file_option);
885 }
886 else if (errno != ENOENT)
887 open_error (volno_file_option);
888 }
889
890 /* Called to write out the closing global volume number. */
891 void
892 closeout_volume_number (void)
893 {
894 FILE *file = fopen (volno_file_option, "w");
895
896 if (file)
897 {
898 fprintf (file, "%d\n", global_volno);
899 if (ferror (file))
900 write_error (volno_file_option);
901 if (fclose (file) != 0)
902 close_error (volno_file_option);
903 }
904 else
905 open_error (volno_file_option);
906 }
907
908 \f
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 int access;
1136
1137 switch (subcommand_option)
1138 {
1139 case APPEND_SUBCOMMAND:
1140 case CAT_SUBCOMMAND:
1141 case UPDATE_SUBCOMMAND:
1142 access = ACCESS_UPDATE;
1143 break;
1144
1145 default:
1146 access = ACCESS_READ;
1147 break;
1148 }
1149
1150 if (!new_volume (access))
1151 return true;
1152
1153 while ((status = rmtread (archive, record_start->buffer, record_size))
1154 == SAFE_READ_ERROR)
1155 archive_read_error ();
1156
1157 if (status != record_size)
1158 short_read (status);
1159
1160 header = find_next_block ();
1161 if (!header)
1162 return false;
1163
1164 switch (header->header.typeflag)
1165 {
1166 case XGLTYPE:
1167 {
1168 if (!read_header0 (&dummy))
1169 return false;
1170 xheader_decode (&dummy); /* decodes values from the global header */
1171 tar_stat_destroy (&dummy);
1172 if (!real_s_name)
1173 {
1174 /* We have read the extended header of the first member in
1175 this volume. Put it back, so next read_header works as
1176 expected. */
1177 current_block = record_start;
1178 }
1179 break;
1180 }
1181
1182 case GNUTYPE_VOLHDR:
1183 if (!read_header0 (&dummy))
1184 return false;
1185 tar_stat_destroy (&dummy);
1186 assign_string (&volume_label, current_header->header.name);
1187 set_next_block_after (header);
1188 header = find_next_block ();
1189 if (header->header.typeflag != GNUTYPE_MULTIVOL)
1190 break;
1191 /* FALL THROUGH */
1192
1193 case GNUTYPE_MULTIVOL:
1194 if (!read_header0 (&dummy))
1195 return false;
1196 tar_stat_destroy (&dummy);
1197 assign_string (&continued_file_name, current_header->header.name);
1198 continued_file_size =
1199 UINTMAX_FROM_HEADER (current_header->header.size);
1200 continued_file_offset =
1201 UINTMAX_FROM_HEADER (current_header->oldgnu_header.offset);
1202 break;
1203
1204 default:
1205 break;
1206 }
1207
1208 if (real_s_name)
1209 {
1210 uintmax_t s;
1211 if (!continued_file_name
1212 || strcmp (continued_file_name, real_s_name))
1213 {
1214 if ((archive_format == GNU_FORMAT || archive_format == OLDGNU_FORMAT)
1215 && strlen (real_s_name) >= NAME_FIELD_SIZE
1216 && strncmp (continued_file_name, real_s_name,
1217 NAME_FIELD_SIZE) == 0)
1218 WARN ((0, 0,
1219 _("%s is possibly continued on this volume: header contains truncated name"),
1220 quote (real_s_name)));
1221 else
1222 {
1223 WARN ((0, 0, _("%s is not continued on this volume"),
1224 quote (real_s_name)));
1225 return false;
1226 }
1227 }
1228
1229 s = continued_file_size + continued_file_offset;
1230
1231 if (real_s_totsize != s || s < continued_file_offset)
1232 {
1233 char totsizebuf[UINTMAX_STRSIZE_BOUND];
1234 char s1buf[UINTMAX_STRSIZE_BOUND];
1235 char s2buf[UINTMAX_STRSIZE_BOUND];
1236
1237 WARN ((0, 0, _("%s is the wrong size (%s != %s + %s)"),
1238 quote (continued_file_name),
1239 STRINGIFY_BIGINT (save_totsize, totsizebuf),
1240 STRINGIFY_BIGINT (continued_file_size, s1buf),
1241 STRINGIFY_BIGINT (continued_file_offset, s2buf)));
1242 return false;
1243 }
1244
1245 if (real_s_totsize - real_s_sizeleft != continued_file_offset)
1246 {
1247 WARN ((0, 0, _("This volume is out of sequence")));
1248 return false;
1249 }
1250 }
1251
1252 increase_volume_number ();
1253 return true;
1254 }
1255
1256 \f
1257 /* Check the LABEL block against the volume label, seen as a globbing
1258 pattern. Return true if the pattern matches. In case of failure,
1259 retry matching a volume sequence number before giving up in
1260 multi-volume mode. */
1261 static bool
1262 check_label_pattern (union block *label)
1263 {
1264 char *string;
1265 bool result;
1266
1267 if (! memchr (label->header.name, '\0', sizeof label->header.name))
1268 return false;
1269
1270 if (fnmatch (volume_label_option, label->header.name, 0) == 0)
1271 return true;
1272
1273 if (!multi_volume_option)
1274 return false;
1275
1276 string = xmalloc (strlen (volume_label_option)
1277 + sizeof VOLUME_LABEL_APPEND + 1);
1278 strcpy (string, volume_label_option);
1279 strcat (string, VOLUME_LABEL_APPEND);
1280 result = fnmatch (string, label->header.name, 0) == 0;
1281 free (string);
1282 return result;
1283 }
1284
1285 /* Check if the next block contains a volume label and if this matches
1286 the one given in the command line */
1287 static void
1288 match_volume_label (void)
1289 {
1290 union block *label = find_next_block ();
1291
1292 if (!label)
1293 FATAL_ERROR ((0, 0, _("Archive not labeled to match %s"),
1294 quote (volume_label_option)));
1295 if (!check_label_pattern (label))
1296 FATAL_ERROR ((0, 0, _("Volume %s does not match %s"),
1297 quote_n (0, label->header.name),
1298 quote_n (1, volume_label_option)));
1299 }
1300
1301 /* Mark the archive with volume label STR. */
1302 static void
1303 _write_volume_label (const char *str)
1304 {
1305 if (archive_format == POSIX_FORMAT)
1306 xheader_store ("GNU.volume.label", &dummy, str);
1307 else
1308 {
1309 union block *label = find_next_block ();
1310
1311 memset (label, 0, BLOCKSIZE);
1312
1313 strcpy (label->header.name, volume_label_option);
1314 assign_string (&current_stat_info.file_name,
1315 label->header.name);
1316 current_stat_info.had_trailing_slash =
1317 strip_trailing_slashes (current_stat_info.file_name);
1318
1319 label->header.typeflag = GNUTYPE_VOLHDR;
1320 TIME_TO_CHARS (start_time.tv_sec, label->header.mtime);
1321 finish_header (&current_stat_info, label, -1);
1322 set_next_block_after (label);
1323 }
1324 }
1325
1326 #define VOL_SUFFIX "Volume"
1327
1328 /* Add a volume label to a part of multi-volume archive */
1329 static void
1330 add_volume_label (void)
1331 {
1332 char buf[UINTMAX_STRSIZE_BOUND];
1333 char *p = STRINGIFY_BIGINT (volno, buf);
1334 char *s = xmalloc (strlen (volume_label_option) + sizeof VOL_SUFFIX
1335 + strlen (p) + 2);
1336 sprintf (s, "%s %s %s", volume_label_option, VOL_SUFFIX, p);
1337 _write_volume_label (s);
1338 free (s);
1339 }
1340
1341 static void
1342 add_chunk_header ()
1343 {
1344 if (archive_format == POSIX_FORMAT)
1345 {
1346 off_t block_ordinal;
1347 union block *blk;
1348 struct tar_stat_info st;
1349 static size_t real_s_part_no; /* FIXME */
1350
1351 real_s_part_no++;
1352 memset (&st, 0, sizeof st);
1353 st.orig_file_name = st.file_name = real_s_name;
1354 st.stat.st_mode = S_IFREG|S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH;
1355 st.stat.st_uid = getuid ();
1356 st.stat.st_gid = getgid ();
1357 st.orig_file_name = xheader_format_name (&st,
1358 "%d/GNUFileParts.%p/%f.%n",
1359 real_s_part_no);
1360 st.file_name = st.orig_file_name;
1361 st.archive_file_size = st.stat.st_size = real_s_sizeleft;
1362
1363 block_ordinal = current_block_ordinal ();
1364 blk = start_header (&st);
1365 if (!blk)
1366 abort (); /* FIXME */
1367 finish_header (&st, blk, block_ordinal);
1368 free (st.orig_file_name);
1369 }
1370 }
1371
1372
1373 /* Add a volume label to the current archive */
1374 static void
1375 write_volume_label (void)
1376 {
1377 if (multi_volume_option)
1378 add_volume_label ();
1379 else
1380 _write_volume_label (volume_label_option);
1381 }
1382
1383 /* Write GNU multi-volume header */
1384 static void
1385 gnu_add_multi_volume_header (void)
1386 {
1387 int tmp;
1388 union block *block = find_next_block ();
1389
1390 if (strlen (real_s_name) > NAME_FIELD_SIZE)
1391 WARN ((0, 0,
1392 _("%s: file name too long to be stored in a GNU multivolume header, truncated"),
1393 quotearg_colon (real_s_name)));
1394
1395 memset (block, 0, BLOCKSIZE);
1396
1397 /* FIXME: Michael P Urban writes: [a long name file] is being written
1398 when a new volume rolls around [...] Looks like the wrong value is
1399 being preserved in real_s_name, though. */
1400
1401 strncpy (block->header.name, real_s_name, NAME_FIELD_SIZE);
1402 block->header.typeflag = GNUTYPE_MULTIVOL;
1403
1404 OFF_TO_CHARS (real_s_sizeleft, block->header.size);
1405 OFF_TO_CHARS (real_s_totsize - real_s_sizeleft,
1406 block->oldgnu_header.offset);
1407
1408 tmp = verbose_option;
1409 verbose_option = 0;
1410 finish_header (&current_stat_info, block, -1);
1411 verbose_option = tmp;
1412 set_next_block_after (block);
1413 }
1414
1415 /* Add a multi volume header to the current archive. The exact header format
1416 depends on the archive format. */
1417 static void
1418 add_multi_volume_header (void)
1419 {
1420 if (archive_format == POSIX_FORMAT)
1421 {
1422 off_t d = real_s_totsize - real_s_sizeleft;
1423 xheader_store ("GNU.volume.filename", &dummy, real_s_name);
1424 xheader_store ("GNU.volume.size", &dummy, &real_s_sizeleft);
1425 xheader_store ("GNU.volume.offset", &dummy, &d);
1426 }
1427 else
1428 gnu_add_multi_volume_header ();
1429 }
1430
1431 /* Synchronize multi-volume globals */
1432 static void
1433 multi_volume_sync ()
1434 {
1435 if (multi_volume_option)
1436 {
1437 if (save_name)
1438 {
1439 assign_string (&real_s_name,
1440 safer_name_suffix (save_name, false,
1441 absolute_names_option));
1442 real_s_totsize = save_totsize;
1443 real_s_sizeleft = save_sizeleft;
1444 }
1445 else
1446 {
1447 assign_string (&real_s_name, 0);
1448 real_s_totsize = 0;
1449 real_s_sizeleft = 0;
1450 }
1451 }
1452 }
1453
1454 \f
1455 /* Low-level flush functions */
1456
1457 /* Simple flush read (no multi-volume or label extensions) */
1458 static void
1459 simple_flush_read (void)
1460 {
1461 size_t status; /* result from system call */
1462
1463 do_checkpoint (false);
1464
1465 /* Clear the count of errors. This only applies to a single call to
1466 flush_read. */
1467
1468 read_error_count = 0; /* clear error count */
1469
1470 if (write_archive_to_stdout && record_start_block != 0)
1471 {
1472 archive = STDOUT_FILENO;
1473 status = sys_write_archive_buffer ();
1474 archive = STDIN_FILENO;
1475 if (status != record_size)
1476 archive_write_error (status);
1477 }
1478
1479 for (;;)
1480 {
1481 status = rmtread (archive, record_start->buffer, record_size);
1482 if (status == record_size)
1483 {
1484 records_read++;
1485 return;
1486 }
1487 if (status == SAFE_READ_ERROR)
1488 {
1489 archive_read_error ();
1490 continue; /* try again */
1491 }
1492 break;
1493 }
1494 short_read (status);
1495 }
1496
1497 /* Simple flush write (no multi-volume or label extensions) */
1498 static void
1499 simple_flush_write (size_t level __attribute__((unused)))
1500 {
1501 ssize_t status;
1502
1503 status = _flush_write ();
1504 if (status != record_size)
1505 archive_write_error (status);
1506 else
1507 {
1508 records_written++;
1509 bytes_written += status;
1510 }
1511 }
1512
1513 \f
1514 /* GNU flush functions. These support multi-volume and archive labels in
1515 GNU and PAX archive formats. */
1516
1517 static void
1518 _gnu_flush_read (void)
1519 {
1520 size_t status; /* result from system call */
1521
1522 do_checkpoint (false);
1523
1524 /* Clear the count of errors. This only applies to a single call to
1525 flush_read. */
1526
1527 read_error_count = 0; /* clear error count */
1528
1529 if (write_archive_to_stdout && record_start_block != 0)
1530 {
1531 archive = STDOUT_FILENO;
1532 status = sys_write_archive_buffer ();
1533 archive = STDIN_FILENO;
1534 if (status != record_size)
1535 archive_write_error (status);
1536 }
1537
1538 multi_volume_sync ();
1539
1540 for (;;)
1541 {
1542 status = rmtread (archive, record_start->buffer, record_size);
1543 if (status == record_size)
1544 {
1545 records_read++;
1546 return;
1547 }
1548
1549 /* The condition below used to include
1550 || (status > 0 && !read_full_records)
1551 This is incorrect since even if new_volume() succeeds, the
1552 subsequent call to rmtread will overwrite the chunk of data
1553 already read in the buffer, so the processing will fail */
1554 if ((status == 0
1555 || (status == SAFE_READ_ERROR && errno == ENOSPC))
1556 && multi_volume_option)
1557 {
1558 while (!try_new_volume ())
1559 ;
1560 return;
1561 }
1562 else if (status == SAFE_READ_ERROR)
1563 {
1564 archive_read_error ();
1565 continue;
1566 }
1567 break;
1568 }
1569 short_read (status);
1570 }
1571
1572 static void
1573 gnu_flush_read (void)
1574 {
1575 flush_read_ptr = simple_flush_read; /* Avoid recursion */
1576 _gnu_flush_read ();
1577 flush_read_ptr = gnu_flush_read;
1578 }
1579
1580 static void
1581 _gnu_flush_write (size_t buffer_level)
1582 {
1583 ssize_t status;
1584 union block *header;
1585 char *copy_ptr;
1586 size_t copy_size;
1587 size_t bufsize;
1588
1589 status = _flush_write ();
1590 if (status != record_size && !multi_volume_option)
1591 archive_write_error (status);
1592 else
1593 {
1594 records_written++;
1595 bytes_written += status;
1596 }
1597
1598 if (status == record_size)
1599 {
1600 multi_volume_sync ();
1601 return;
1602 }
1603
1604 /* In multi-volume mode. */
1605 /* ENXIO is for the UNIX PC. */
1606 if (status < 0 && errno != ENOSPC && errno != EIO && errno != ENXIO)
1607 archive_write_error (status);
1608
1609 if (!new_volume (ACCESS_WRITE))
1610 return;
1611
1612 tar_stat_destroy (&dummy);
1613
1614 increase_volume_number ();
1615 prev_written += bytes_written;
1616 bytes_written = 0;
1617
1618 copy_ptr = record_start->buffer + status;
1619 copy_size = buffer_level - status;
1620 /* Switch to the next buffer */
1621 record_index = !record_index;
1622 init_buffer ();
1623
1624 if (volume_label_option)
1625 add_volume_label ();
1626
1627 if (real_s_name)
1628 add_multi_volume_header ();
1629
1630 write_extended (true, &dummy, find_next_block ());
1631 tar_stat_destroy (&dummy);
1632
1633 if (real_s_name)
1634 add_chunk_header ();
1635 header = find_next_block ();
1636 bufsize = available_space_after (header);
1637 while (bufsize < copy_size)
1638 {
1639 memcpy (header->buffer, copy_ptr, bufsize);
1640 copy_ptr += bufsize;
1641 copy_size -= bufsize;
1642 set_next_block_after (header + (bufsize - 1) / BLOCKSIZE);
1643 header = find_next_block ();
1644 bufsize = available_space_after (header);
1645 }
1646 memcpy (header->buffer, copy_ptr, copy_size);
1647 memset (header->buffer + copy_size, 0, bufsize - copy_size);
1648 set_next_block_after (header + (copy_size - 1) / BLOCKSIZE);
1649 find_next_block ();
1650 }
1651
1652 static void
1653 gnu_flush_write (size_t buffer_level)
1654 {
1655 flush_write_ptr = simple_flush_write; /* Avoid recursion */
1656 _gnu_flush_write (buffer_level);
1657 flush_write_ptr = gnu_flush_write;
1658 }
1659
1660 void
1661 flush_read ()
1662 {
1663 flush_read_ptr ();
1664 }
1665
1666 void
1667 flush_write ()
1668 {
1669 flush_write_ptr (record_size);
1670 }
1671
1672 void
1673 open_archive (enum access_mode wanted_access)
1674 {
1675 flush_read_ptr = gnu_flush_read;
1676 flush_write_ptr = gnu_flush_write;
1677
1678 _open_archive (wanted_access);
1679 switch (wanted_access)
1680 {
1681 case ACCESS_READ:
1682 if (volume_label_option)
1683 match_volume_label ();
1684 break;
1685
1686 case ACCESS_WRITE:
1687 records_written = 0;
1688 if (volume_label_option)
1689 write_volume_label ();
1690 break;
1691
1692 default:
1693 break;
1694 }
1695 set_volume_start_time ();
1696 }
This page took 0.102008 seconds and 5 git commands to generate.