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