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