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