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