source: trunk/essentials/app-arch/tar/src/sparse.c

Last change on this file was 3342, checked in by bird, 18 years ago

tar 1.16.1

File size: 32.4 KB
Line 
1/* Functions for dealing with sparse files
2
3 Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
4
5 This program is free software; you can redistribute it and/or modify it
6 under the terms of the GNU General Public License as published by the
7 Free Software Foundation; either version 2, or (at your option) any later
8 version.
9
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13 Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
18
19#include <system.h>
20#include <inttostr.h>
21#include <quotearg.h>
22#include "common.h"
23
24struct tar_sparse_file;
25static bool sparse_select_optab (struct tar_sparse_file *file);
26
27enum sparse_scan_state
28 {
29 scan_begin,
30 scan_block,
31 scan_end
32 };
33
34struct tar_sparse_optab
35{
36 bool (*init) (struct tar_sparse_file *);
37 bool (*done) (struct tar_sparse_file *);
38 bool (*sparse_member_p) (struct tar_sparse_file *);
39 bool (*dump_header) (struct tar_sparse_file *);
40 bool (*fixup_header) (struct tar_sparse_file *);
41 bool (*decode_header) (struct tar_sparse_file *);
42 bool (*scan_block) (struct tar_sparse_file *, enum sparse_scan_state,
43 void *);
44 bool (*dump_region) (struct tar_sparse_file *, size_t);
45 bool (*extract_region) (struct tar_sparse_file *, size_t);
46};
47
48struct tar_sparse_file
49{
50 int fd; /* File descriptor */
51 bool seekable; /* Is fd seekable? */
52 off_t offset; /* Current offset in fd if seekable==false.
53 Otherwise unused */
54 off_t dumped_size; /* Number of bytes actually written
55 to the archive */
56 struct tar_stat_info *stat_info; /* Information about the file */
57 struct tar_sparse_optab const *optab; /* Operation table */
58 void *closure; /* Any additional data optab calls might
59 require */
60};
61
62/* Dump zeros to file->fd until offset is reached. It is used instead of
63 lseek if the output file is not seekable */
64static bool
65dump_zeros (struct tar_sparse_file *file, off_t offset)
66{
67 static char const zero_buf[BLOCKSIZE];
68
69 if (offset < file->offset)
70 {
71 errno = EINVAL;
72 return false;
73 }
74
75 while (file->offset < offset)
76 {
77 size_t size = (BLOCKSIZE < offset - file->offset
78 ? BLOCKSIZE
79 : offset - file->offset);
80 ssize_t wrbytes;
81
82 wrbytes = write (file->fd, zero_buf, size);
83 if (wrbytes <= 0)
84 {
85 if (wrbytes == 0)
86 errno = EINVAL;
87 return false;
88 }
89 file->offset += wrbytes;
90 }
91
92 return true;
93}
94
95static bool
96tar_sparse_member_p (struct tar_sparse_file *file)
97{
98 if (file->optab->sparse_member_p)
99 return file->optab->sparse_member_p (file);
100 return false;
101}
102
103static bool
104tar_sparse_init (struct tar_sparse_file *file)
105{
106 memset (file, 0, sizeof *file);
107
108 if (!sparse_select_optab (file))
109 return false;
110
111 if (file->optab->init)
112 return file->optab->init (file);
113
114 return true;
115}
116
117static bool
118tar_sparse_done (struct tar_sparse_file *file)
119{
120 if (file->optab->done)
121 return file->optab->done (file);
122 return true;
123}
124
125static bool
126tar_sparse_scan (struct tar_sparse_file *file, enum sparse_scan_state state,
127 void *block)
128{
129 if (file->optab->scan_block)
130 return file->optab->scan_block (file, state, block);
131 return true;
132}
133
134static bool
135tar_sparse_dump_region (struct tar_sparse_file *file, size_t i)
136{
137 if (file->optab->dump_region)
138 return file->optab->dump_region (file, i);
139 return false;
140}
141
142static bool
143tar_sparse_extract_region (struct tar_sparse_file *file, size_t i)
144{
145 if (file->optab->extract_region)
146 return file->optab->extract_region (file, i);
147 return false;
148}
149
150static bool
151tar_sparse_dump_header (struct tar_sparse_file *file)
152{
153 if (file->optab->dump_header)
154 return file->optab->dump_header (file);
155 return false;
156}
157
158static bool
159tar_sparse_decode_header (struct tar_sparse_file *file)
160{
161 if (file->optab->decode_header)
162 return file->optab->decode_header (file);
163 return true;
164}
165
166static bool
167tar_sparse_fixup_header (struct tar_sparse_file *file)
168{
169 if (file->optab->fixup_header)
170 return file->optab->fixup_header (file);
171 return true;
172}
173
174
175
176static bool
177lseek_or_error (struct tar_sparse_file *file, off_t offset)
178{
179 if (file->seekable
180 ? lseek (file->fd, offset, SEEK_SET) < 0
181 : ! dump_zeros (file, offset))
182 {
183 seek_diag_details (file->stat_info->orig_file_name, offset);
184 return false;
185 }
186 return true;
187}
188
189/* Takes a blockful of data and basically cruises through it to see if
190 it's made *entirely* of zeros, returning a 0 the instant it finds
191 something that is a nonzero, i.e., useful data. */
192static bool
193zero_block_p (char const *buffer, size_t size)
194{
195 while (size--)
196 if (*buffer++)
197 return false;
198 return true;
199}
200
201static void
202sparse_add_map (struct tar_stat_info *st, struct sp_array const *sp)
203{
204 struct sp_array *sparse_map = st->sparse_map;
205 size_t avail = st->sparse_map_avail;
206 if (avail == st->sparse_map_size)
207 st->sparse_map = sparse_map =
208 x2nrealloc (sparse_map, &st->sparse_map_size, sizeof *sparse_map);
209 sparse_map[avail] = *sp;
210 st->sparse_map_avail = avail + 1;
211}
212
213/* Scan the sparse file and create its map */
214static bool
215sparse_scan_file (struct tar_sparse_file *file)
216{
217 struct tar_stat_info *st = file->stat_info;
218 int fd = file->fd;
219 char buffer[BLOCKSIZE];
220 size_t count;
221 off_t offset = 0;
222 struct sp_array sp = {0, 0};
223
224 if (!lseek_or_error (file, 0))
225 return false;
226
227 st->archive_file_size = 0;
228
229 if (!tar_sparse_scan (file, scan_begin, NULL))
230 return false;
231
232 while ((count = safe_read (fd, buffer, sizeof buffer)) != 0
233 && count != SAFE_READ_ERROR)
234 {
235 /* Analyze the block. */
236 if (zero_block_p (buffer, count))
237 {
238 if (sp.numbytes)
239 {
240 sparse_add_map (st, &sp);
241 sp.numbytes = 0;
242 if (!tar_sparse_scan (file, scan_block, NULL))
243 return false;
244 }
245 }
246 else
247 {
248 if (sp.numbytes == 0)
249 sp.offset = offset;
250 sp.numbytes += count;
251 st->archive_file_size += count;
252 if (!tar_sparse_scan (file, scan_block, buffer))
253 return false;
254 }
255
256 offset += count;
257 }
258
259 if (sp.numbytes == 0)
260 sp.offset = offset;
261
262 sparse_add_map (st, &sp);
263 st->archive_file_size += count;
264 return tar_sparse_scan (file, scan_end, NULL);
265}
266
267static struct tar_sparse_optab const oldgnu_optab;
268static struct tar_sparse_optab const star_optab;
269static struct tar_sparse_optab const pax_optab;
270
271static bool
272sparse_select_optab (struct tar_sparse_file *file)
273{
274 switch (current_format == DEFAULT_FORMAT ? archive_format : current_format)
275 {
276 case V7_FORMAT:
277 case USTAR_FORMAT:
278 return false;
279
280 case OLDGNU_FORMAT:
281 case GNU_FORMAT: /*FIXME: This one should disappear? */
282 file->optab = &oldgnu_optab;
283 break;
284
285 case POSIX_FORMAT:
286 file->optab = &pax_optab;
287 break;
288
289 case STAR_FORMAT:
290 file->optab = &star_optab;
291 break;
292
293 default:
294 return false;
295 }
296 return true;
297}
298
299static bool
300sparse_dump_region (struct tar_sparse_file *file, size_t i)
301{
302 union block *blk;
303 off_t bytes_left = file->stat_info->sparse_map[i].numbytes;
304
305 if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset))
306 return false;
307
308 while (bytes_left > 0)
309 {
310 size_t bufsize = (bytes_left > BLOCKSIZE) ? BLOCKSIZE : bytes_left;
311 size_t bytes_read;
312
313 blk = find_next_block ();
314 bytes_read = safe_read (file->fd, blk->buffer, bufsize);
315 if (bytes_read == SAFE_READ_ERROR)
316 {
317 read_diag_details (file->stat_info->orig_file_name,
318 (file->stat_info->sparse_map[i].offset
319 + file->stat_info->sparse_map[i].numbytes
320 - bytes_left),
321 bufsize);
322 return false;
323 }
324
325 memset (blk->buffer + bytes_read, 0, BLOCKSIZE - bytes_read);
326 bytes_left -= bytes_read;
327 file->dumped_size += bytes_read;
328 mv_size_left (file->stat_info->archive_file_size - file->dumped_size);
329 set_next_block_after (blk);
330 }
331
332 return true;
333}
334
335static bool
336sparse_extract_region (struct tar_sparse_file *file, size_t i)
337{
338 size_t write_size;
339
340 if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset))
341 return false;
342
343 write_size = file->stat_info->sparse_map[i].numbytes;
344
345 if (write_size == 0)
346 {
347 /* Last block of the file is a hole */
348 if (file->seekable && sys_truncate (file->fd))
349 truncate_warn (file->stat_info->orig_file_name);
350 }
351 else while (write_size > 0)
352 {
353 size_t count;
354 size_t wrbytes = (write_size > BLOCKSIZE) ? BLOCKSIZE : write_size;
355 union block *blk = find_next_block ();
356 if (!blk)
357 {
358 ERROR ((0, 0, _("Unexpected EOF in archive")));
359 return false;
360 }
361 set_next_block_after (blk);
362 count = full_write (file->fd, blk->buffer, wrbytes);
363 write_size -= count;
364 file->dumped_size += count;
365 mv_size_left (file->stat_info->archive_file_size - file->dumped_size);
366 file->offset += count;
367 if (count != wrbytes)
368 {
369 write_error_details (file->stat_info->orig_file_name,
370 count, wrbytes);
371 return false;
372 }
373 }
374 return true;
375}
376
377
378
379
380/* Interface functions */
381enum dump_status
382sparse_dump_file (int fd, struct tar_stat_info *st)
383{
384 bool rc;
385 struct tar_sparse_file file;
386
387 if (!tar_sparse_init (&file))
388 return dump_status_not_implemented;
389
390 file.stat_info = st;
391 file.fd = fd;
392 file.seekable = true; /* File *must* be seekable for dump to work */
393
394 rc = sparse_scan_file (&file);
395 if (rc && file.optab->dump_region)
396 {
397 tar_sparse_dump_header (&file);
398
399 if (fd >= 0)
400 {
401 size_t i;
402
403 mv_begin (file.stat_info);
404 for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
405 rc = tar_sparse_dump_region (&file, i);
406 mv_end ();
407 }
408 }
409
410 pad_archive (file.stat_info->archive_file_size - file.dumped_size);
411 return (tar_sparse_done (&file) && rc) ? dump_status_ok : dump_status_short;
412}
413
414bool
415sparse_member_p (struct tar_stat_info *st)
416{
417 struct tar_sparse_file file;
418
419 if (!tar_sparse_init (&file))
420 return false;
421 file.stat_info = st;
422 return tar_sparse_member_p (&file);
423}
424
425bool
426sparse_fixup_header (struct tar_stat_info *st)
427{
428 struct tar_sparse_file file;
429
430 if (!tar_sparse_init (&file))
431 return false;
432 file.stat_info = st;
433 return tar_sparse_fixup_header (&file);
434}
435
436enum dump_status
437sparse_extract_file (int fd, struct tar_stat_info *st, off_t *size)
438{
439 bool rc = true;
440 struct tar_sparse_file file;
441 size_t i;
442
443 if (!tar_sparse_init (&file))
444 return dump_status_not_implemented;
445
446 file.stat_info = st;
447 file.fd = fd;
448 file.seekable = lseek (fd, 0, SEEK_SET) == 0;
449 file.offset = 0;
450
451 rc = tar_sparse_decode_header (&file);
452 for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
453 rc = tar_sparse_extract_region (&file, i);
454 *size = file.stat_info->archive_file_size - file.dumped_size;
455 return (tar_sparse_done (&file) && rc) ? dump_status_ok : dump_status_short;
456}
457
458enum dump_status
459sparse_skip_file (struct tar_stat_info *st)
460{
461 bool rc = true;
462 struct tar_sparse_file file;
463
464 if (!tar_sparse_init (&file))
465 return dump_status_not_implemented;
466
467 file.stat_info = st;
468 file.fd = -1;
469
470 rc = tar_sparse_decode_header (&file);
471 skip_file (file.stat_info->archive_file_size);
472 return (tar_sparse_done (&file) && rc) ? dump_status_ok : dump_status_short;
473}
474
475
476
477static bool
478check_sparse_region (struct tar_sparse_file *file, off_t beg, off_t end)
479{
480 if (!lseek_or_error (file, beg))
481 return false;
482
483 while (beg < end)
484 {
485 size_t bytes_read;
486 size_t rdsize = BLOCKSIZE < end - beg ? BLOCKSIZE : end - beg;
487 char diff_buffer[BLOCKSIZE];
488
489 bytes_read = safe_read (file->fd, diff_buffer, rdsize);
490 if (bytes_read == SAFE_READ_ERROR)
491 {
492 read_diag_details (file->stat_info->orig_file_name,
493 beg,
494 rdsize);
495 return false;
496 }
497 if (!zero_block_p (diff_buffer, bytes_read))
498 {
499 char begbuf[INT_BUFSIZE_BOUND (off_t)];
500 report_difference (file->stat_info,
501 _("File fragment at %s is not a hole"),
502 offtostr (beg, begbuf));
503 return false;
504 }
505
506 beg += bytes_read;
507 }
508 return true;
509}
510
511static bool
512check_data_region (struct tar_sparse_file *file, size_t i)
513{
514 size_t size_left;
515
516 if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset))
517 return false;
518 size_left = file->stat_info->sparse_map[i].numbytes;
519 mv_size_left (file->stat_info->archive_file_size - file->dumped_size);
520
521 while (size_left > 0)
522 {
523 size_t bytes_read;
524 size_t rdsize = (size_left > BLOCKSIZE) ? BLOCKSIZE : size_left;
525 char diff_buffer[BLOCKSIZE];
526
527 union block *blk = find_next_block ();
528 if (!blk)
529 {
530 ERROR ((0, 0, _("Unexpected EOF in archive")));
531 return false;
532 }
533 set_next_block_after (blk);
534 bytes_read = safe_read (file->fd, diff_buffer, rdsize);
535 if (bytes_read == SAFE_READ_ERROR)
536 {
537 read_diag_details (file->stat_info->orig_file_name,
538 (file->stat_info->sparse_map[i].offset
539 + file->stat_info->sparse_map[i].numbytes
540 - size_left),
541 rdsize);
542 return false;
543 }
544 file->dumped_size += bytes_read;
545 size_left -= bytes_read;
546 mv_size_left (file->stat_info->archive_file_size - file->dumped_size);
547 if (memcmp (blk->buffer, diff_buffer, rdsize))
548 {
549 report_difference (file->stat_info, _("Contents differ"));
550 return false;
551 }
552 }
553 return true;
554}
555
556bool
557sparse_diff_file (int fd, struct tar_stat_info *st)
558{
559 bool rc = true;
560 struct tar_sparse_file file;
561 size_t i;
562 off_t offset = 0;
563
564 if (!tar_sparse_init (&file))
565 return dump_status_not_implemented;
566
567 file.stat_info = st;
568 file.fd = fd;
569 file.seekable = true; /* File *must* be seekable for compare to work */
570
571 rc = tar_sparse_decode_header (&file);
572 mv_begin (st);
573 for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
574 {
575 rc = check_sparse_region (&file,
576 offset, file.stat_info->sparse_map[i].offset)
577 && check_data_region (&file, i);
578 offset = file.stat_info->sparse_map[i].offset
579 + file.stat_info->sparse_map[i].numbytes;
580 }
581
582 if (!rc)
583 skip_file (file.stat_info->archive_file_size - file.dumped_size);
584 mv_end ();
585
586 tar_sparse_done (&file);
587 return rc;
588}
589
590
591
592/* Old GNU Format. The sparse file information is stored in the
593 oldgnu_header in the following manner:
594
595 The header is marked with type 'S'. Its `size' field contains
596 the cumulative size of all non-empty blocks of the file. The
597 actual file size is stored in `realsize' member of oldgnu_header.
598
599 The map of the file is stored in a list of `struct sparse'.
600 Each struct contains offset to the block of data and its
601 size (both as octal numbers). The first file header contains
602 at most 4 such structs (SPARSES_IN_OLDGNU_HEADER). If the map
603 contains more structs, then the field `isextended' of the main
604 header is set to 1 (binary) and the `struct sparse_header'
605 header follows, containing at most 21 following structs
606 (SPARSES_IN_SPARSE_HEADER). If more structs follow, `isextended'
607 field of the extended header is set and next next extension header
608 follows, etc... */
609
610enum oldgnu_add_status
611 {
612 add_ok,
613 add_finish,
614 add_fail
615 };
616
617static bool
618oldgnu_sparse_member_p (struct tar_sparse_file *file __attribute__ ((unused)))
619{
620 return current_header->header.typeflag == GNUTYPE_SPARSE;
621}
622
623/* Add a sparse item to the sparse file and its obstack */
624static enum oldgnu_add_status
625oldgnu_add_sparse (struct tar_sparse_file *file, struct sparse *s)
626{
627 struct sp_array sp;
628
629 if (s->numbytes[0] == '\0')
630 return add_finish;
631 sp.offset = OFF_FROM_HEADER (s->offset);
632 sp.numbytes = SIZE_FROM_HEADER (s->numbytes);
633 if (sp.offset < 0
634 || file->stat_info->stat.st_size < sp.offset + sp.numbytes
635 || file->stat_info->archive_file_size < 0)
636 return add_fail;
637
638 sparse_add_map (file->stat_info, &sp);
639 return add_ok;
640}
641
642static bool
643oldgnu_fixup_header (struct tar_sparse_file *file)
644{
645 /* NOTE! st_size was initialized from the header
646 which actually contains archived size. The following fixes it */
647 file->stat_info->archive_file_size = file->stat_info->stat.st_size;
648 file->stat_info->stat.st_size =
649 OFF_FROM_HEADER (current_header->oldgnu_header.realsize);
650 return true;
651}
652
653/* Convert old GNU format sparse data to internal representation */
654static bool
655oldgnu_get_sparse_info (struct tar_sparse_file *file)
656{
657 size_t i;
658 union block *h = current_header;
659 int ext_p;
660 enum oldgnu_add_status rc;
661
662 file->stat_info->sparse_map_avail = 0;
663 for (i = 0; i < SPARSES_IN_OLDGNU_HEADER; i++)
664 {
665 rc = oldgnu_add_sparse (file, &h->oldgnu_header.sp[i]);
666 if (rc != add_ok)
667 break;
668 }
669
670 for (ext_p = h->oldgnu_header.isextended;
671 rc == add_ok && ext_p; ext_p = h->sparse_header.isextended)
672 {
673 h = find_next_block ();
674 if (!h)
675 {
676 ERROR ((0, 0, _("Unexpected EOF in archive")));
677 return false;
678 }
679 set_next_block_after (h);
680 for (i = 0; i < SPARSES_IN_SPARSE_HEADER && rc == add_ok; i++)
681 rc = oldgnu_add_sparse (file, &h->sparse_header.sp[i]);
682 }
683
684 if (rc == add_fail)
685 {
686 ERROR ((0, 0, _("%s: invalid sparse archive member"),
687 file->stat_info->orig_file_name));
688 return false;
689 }
690 return true;
691}
692
693static void
694oldgnu_store_sparse_info (struct tar_sparse_file *file, size_t *pindex,
695 struct sparse *sp, size_t sparse_size)
696{
697 for (; *pindex < file->stat_info->sparse_map_avail
698 && sparse_size > 0; sparse_size--, sp++, ++*pindex)
699 {
700 OFF_TO_CHARS (file->stat_info->sparse_map[*pindex].offset,
701 sp->offset);
702 SIZE_TO_CHARS (file->stat_info->sparse_map[*pindex].numbytes,
703 sp->numbytes);
704 }
705}
706
707static bool
708oldgnu_dump_header (struct tar_sparse_file *file)
709{
710 off_t block_ordinal = current_block_ordinal ();
711 union block *blk;
712 size_t i;
713
714 blk = start_header (file->stat_info);
715 blk->header.typeflag = GNUTYPE_SPARSE;
716 if (file->stat_info->sparse_map_avail > SPARSES_IN_OLDGNU_HEADER)
717 blk->oldgnu_header.isextended = 1;
718
719 /* Store the real file size */
720 OFF_TO_CHARS (file->stat_info->stat.st_size, blk->oldgnu_header.realsize);
721 /* Store the effective (shrunken) file size */
722 OFF_TO_CHARS (file->stat_info->archive_file_size, blk->header.size);
723
724 i = 0;
725 oldgnu_store_sparse_info (file, &i,
726 blk->oldgnu_header.sp,
727 SPARSES_IN_OLDGNU_HEADER);
728 blk->oldgnu_header.isextended = i < file->stat_info->sparse_map_avail;
729 finish_header (file->stat_info, blk, block_ordinal);
730
731 while (i < file->stat_info->sparse_map_avail)
732 {
733 blk = find_next_block ();
734 memset (blk->buffer, 0, BLOCKSIZE);
735 oldgnu_store_sparse_info (file, &i,
736 blk->sparse_header.sp,
737 SPARSES_IN_SPARSE_HEADER);
738 if (i < file->stat_info->sparse_map_avail)
739 blk->sparse_header.isextended = 1;
740 set_next_block_after (blk);
741 }
742 return true;
743}
744
745static struct tar_sparse_optab const oldgnu_optab = {
746 NULL, /* No init function */
747 NULL, /* No done function */
748 oldgnu_sparse_member_p,
749 oldgnu_dump_header,
750 oldgnu_fixup_header,
751 oldgnu_get_sparse_info,
752 NULL, /* No scan_block function */
753 sparse_dump_region,
754 sparse_extract_region,
755};
756
757
758
759/* Star */
760
761static bool
762star_sparse_member_p (struct tar_sparse_file *file __attribute__ ((unused)))
763{
764 return current_header->header.typeflag == GNUTYPE_SPARSE;
765}
766
767static bool
768star_fixup_header (struct tar_sparse_file *file)
769{
770 /* NOTE! st_size was initialized from the header
771 which actually contains archived size. The following fixes it */
772 file->stat_info->archive_file_size = file->stat_info->stat.st_size;
773 file->stat_info->stat.st_size =
774 OFF_FROM_HEADER (current_header->star_in_header.realsize);
775 return true;
776}
777
778/* Convert STAR format sparse data to internal representation */
779static bool
780star_get_sparse_info (struct tar_sparse_file *file)
781{
782 size_t i;
783 union block *h = current_header;
784 int ext_p;
785 enum oldgnu_add_status rc = add_ok;
786
787 file->stat_info->sparse_map_avail = 0;
788
789 if (h->star_in_header.prefix[0] == '\0'
790 && h->star_in_header.sp[0].offset[10] != '\0')
791 {
792 /* Old star format */
793 for (i = 0; i < SPARSES_IN_STAR_HEADER; i++)
794 {
795 rc = oldgnu_add_sparse (file, &h->star_in_header.sp[i]);
796 if (rc != add_ok)
797 break;
798 }
799 ext_p = h->star_in_header.isextended;
800 }
801 else
802 ext_p = 1;
803
804 for (; rc == add_ok && ext_p; ext_p = h->star_ext_header.isextended)
805 {
806 h = find_next_block ();
807 if (!h)
808 {
809 ERROR ((0, 0, _("Unexpected EOF in archive")));
810 return false;
811 }
812 set_next_block_after (h);
813 for (i = 0; i < SPARSES_IN_STAR_EXT_HEADER && rc == add_ok; i++)
814 rc = oldgnu_add_sparse (file, &h->star_ext_header.sp[i]);
815 }
816
817 if (rc == add_fail)
818 {
819 ERROR ((0, 0, _("%s: invalid sparse archive member"),
820 file->stat_info->orig_file_name));
821 return false;
822 }
823 return true;
824}
825
826
827static struct tar_sparse_optab const star_optab = {
828 NULL, /* No init function */
829 NULL, /* No done function */
830 star_sparse_member_p,
831 NULL,
832 star_fixup_header,
833 star_get_sparse_info,
834 NULL, /* No scan_block function */
835 NULL, /* No dump region function */
836 sparse_extract_region,
837};
838
839
840
841/* GNU PAX sparse file format. There are several versions:
842
843 * 0.0
844
845 The initial version of sparse format used by tar 1.14-1.15.1.
846 The sparse file map is stored in x header:
847
848 GNU.sparse.size Real size of the stored file
849 GNU.sparse.numblocks Number of blocks in the sparse map
850 repeat numblocks time
851 GNU.sparse.offset Offset of the next data block
852 GNU.sparse.numbytes Size of the next data block
853 end repeat
854
855 This has been reported as conflicting with the POSIX specs. The reason is
856 that offsets and sizes of non-zero data blocks were stored in multiple
857 instances of GNU.sparse.offset/GNU.sparse.numbytes variables, whereas
858 POSIX requires the latest occurrence of the variable to override all
859 previous occurrences.
860
861 To avoid this incompatibility two following versions were introduced.
862
863 * 0.1
864
865 Used by tar 1.15.2 -- 1.15.91 (alpha releases).
866
867 The sparse file map is stored in
868 x header:
869
870 GNU.sparse.size Real size of the stored file
871 GNU.sparse.numblocks Number of blocks in the sparse map
872 GNU.sparse.map Map of non-null data chunks. A string consisting
873 of comma-separated values "offset,size[,offset,size]..."
874
875 The resulting GNU.sparse.map string can be *very* long. While POSIX does not
876 impose any limit on the length of a x header variable, this can confuse some
877 tars.
878
879 * 1.0
880
881 Starting from this version, the exact sparse format version is specified
882 explicitely in the header using the following variables:
883
884 GNU.sparse.major Major version
885 GNU.sparse.minor Minor version
886
887 X header keeps the following variables:
888
889 GNU.sparse.name Real file name of the sparse file
890 GNU.sparse.realsize Real size of the stored file (corresponds to the old
891 GNU.sparse.size variable)
892
893 The name field of the ustar header is constructed using the pattern
894 "%d/GNUSparseFile.%p/%f".
895
896 The sparse map itself is stored in the file data block, preceding the actual
897 file data. It consists of a series of octal numbers of arbitrary length,
898 delimited by newlines. The map is padded with nulls to the nearest block
899 boundary.
900
901 The first number gives the number of entries in the map. Following are map
902 entries, each one consisting of two numbers giving the offset and size of
903 the data block it describes.
904
905 The format is designed in such a way that non-posix aware tars and tars not
906 supporting GNU.sparse.* keywords will extract each sparse file in its
907 condensed form with the file map attached and will place it into a separate
908 directory. Then, using a simple program it would be possible to expand the
909 file to its original form even without GNU tar.
910
911 Bu default, v.1.0 archives are created. To use other formats,
912 --sparse-version option is provided. Additionally, v.0.0 can be obtained
913 by deleting GNU.sparse.map from 0.1 format: --sparse-version 0.1
914 --pax-option delete=GNU.sparse.map
915*/
916
917static bool
918pax_sparse_member_p (struct tar_sparse_file *file)
919{
920 return file->stat_info->sparse_map_avail > 0
921 || file->stat_info->sparse_major > 0;
922}
923
924static bool
925pax_dump_header_0 (struct tar_sparse_file *file)
926{
927 off_t block_ordinal = current_block_ordinal ();
928 union block *blk;
929 size_t i;
930 char nbuf[UINTMAX_STRSIZE_BOUND];
931 struct sp_array *map = file->stat_info->sparse_map;
932 char *save_file_name = NULL;
933
934 /* Store the real file size */
935 xheader_store ("GNU.sparse.size", file->stat_info, NULL);
936 xheader_store ("GNU.sparse.numblocks", file->stat_info, NULL);
937
938 if (xheader_keyword_deleted_p ("GNU.sparse.map")
939 || tar_sparse_minor == 0)
940 {
941 for (i = 0; i < file->stat_info->sparse_map_avail; i++)
942 {
943 xheader_store ("GNU.sparse.offset", file->stat_info, &i);
944 xheader_store ("GNU.sparse.numbytes", file->stat_info, &i);
945 }
946 }
947 else
948 {
949 xheader_store ("GNU.sparse.name", file->stat_info, NULL);
950 save_file_name = file->stat_info->file_name;
951 file->stat_info->file_name = xheader_format_name (file->stat_info,
952 "%d/GNUSparseFile.%p/%f", 0);
953
954 xheader_string_begin ();
955 for (i = 0; i < file->stat_info->sparse_map_avail; i++)
956 {
957 if (i)
958 xheader_string_add (",");
959 xheader_string_add (umaxtostr (map[i].offset, nbuf));
960 xheader_string_add (",");
961 xheader_string_add (umaxtostr (map[i].numbytes, nbuf));
962 }
963 if (!xheader_string_end ("GNU.sparse.map"))
964 {
965 free (file->stat_info->file_name);
966 file->stat_info->file_name = save_file_name;
967 return false;
968 }
969 }
970 blk = start_header (file->stat_info);
971 /* Store the effective (shrunken) file size */
972 OFF_TO_CHARS (file->stat_info->archive_file_size, blk->header.size);
973 finish_header (file->stat_info, blk, block_ordinal);
974 if (save_file_name)
975 {
976 free (file->stat_info->file_name);
977 file->stat_info->file_name = save_file_name;
978 }
979 return true;
980}
981
982static bool
983pax_dump_header_1 (struct tar_sparse_file *file)
984{
985 off_t block_ordinal = current_block_ordinal ();
986 union block *blk;
987 char *p, *q;
988 size_t i;
989 char nbuf[UINTMAX_STRSIZE_BOUND];
990 off_t size = 0;
991 struct sp_array *map = file->stat_info->sparse_map;
992 char *save_file_name = file->stat_info->file_name;
993
994#define COPY_STRING(b,dst,src) do \
995 { \
996 char *endp = b->buffer + BLOCKSIZE; \
997 char *srcp = src; \
998 while (*srcp) \
999 { \
1000 if (dst == endp) \
1001 { \
1002 set_next_block_after (b); \
1003 b = find_next_block (); \
1004 dst = b->buffer; \
1005 endp = b->buffer + BLOCKSIZE; \
1006 } \
1007 *dst++ = *srcp++; \
1008 } \
1009 } while (0)
1010
1011 /* Compute stored file size */
1012 p = umaxtostr (file->stat_info->sparse_map_avail, nbuf);
1013 size += strlen (p) + 1;
1014 for (i = 0; i < file->stat_info->sparse_map_avail; i++)
1015 {
1016 p = umaxtostr (map[i].offset, nbuf);
1017 size += strlen (p) + 1;
1018 p = umaxtostr (map[i].numbytes, nbuf);
1019 size += strlen (p) + 1;
1020 }
1021 size = (size + BLOCKSIZE - 1) / BLOCKSIZE;
1022 file->stat_info->archive_file_size += size * BLOCKSIZE;
1023
1024 /* Store sparse file identification */
1025 xheader_store ("GNU.sparse.major", file->stat_info, NULL);
1026 xheader_store ("GNU.sparse.minor", file->stat_info, NULL);
1027 xheader_store ("GNU.sparse.name", file->stat_info, NULL);
1028 xheader_store ("GNU.sparse.realsize", file->stat_info, NULL);
1029
1030 file->stat_info->file_name = xheader_format_name (file->stat_info,
1031 "%d/GNUSparseFile.%p/%f", 0);
1032
1033 blk = start_header (file->stat_info);
1034 /* Store the effective (shrunken) file size */
1035 OFF_TO_CHARS (file->stat_info->archive_file_size, blk->header.size);
1036 finish_header (file->stat_info, blk, block_ordinal);
1037 free (file->stat_info->file_name);
1038 file->stat_info->file_name = save_file_name;
1039
1040 blk = find_next_block ();
1041 q = blk->buffer;
1042 p = umaxtostr (file->stat_info->sparse_map_avail, nbuf);
1043 COPY_STRING (blk, q, p);
1044 COPY_STRING (blk, q, "\n");
1045 for (i = 0; i < file->stat_info->sparse_map_avail; i++)
1046 {
1047 p = umaxtostr (map[i].offset, nbuf);
1048 COPY_STRING (blk, q, p);
1049 COPY_STRING (blk, q, "\n");
1050 p = umaxtostr (map[i].numbytes, nbuf);
1051 COPY_STRING (blk, q, p);
1052 COPY_STRING (blk, q, "\n");
1053 }
1054 memset (q, 0, BLOCKSIZE - (q - blk->buffer));
1055 set_next_block_after (blk);
1056 return true;
1057}
1058
1059static bool
1060pax_dump_header (struct tar_sparse_file *file)
1061{
1062 file->stat_info->sparse_major = tar_sparse_major;
1063 file->stat_info->sparse_minor = tar_sparse_minor;
1064
1065 return (file->stat_info->sparse_major == 0) ?
1066 pax_dump_header_0 (file) : pax_dump_header_1 (file);
1067}
1068
1069static bool
1070decode_num (uintmax_t *num, char const *arg, uintmax_t maxval)
1071{
1072 uintmax_t u;
1073 char *arg_lim;
1074
1075 if (!ISDIGIT (*arg))
1076 return false;
1077
1078 u = strtoumax (arg, &arg_lim, 10);
1079
1080 if (! (u <= maxval && errno != ERANGE) || *arg_lim)
1081 return false;
1082
1083 *num = u;
1084 return true;
1085}
1086
1087static bool
1088pax_decode_header (struct tar_sparse_file *file)
1089{
1090 if (file->stat_info->sparse_major > 0)
1091 {
1092 uintmax_t u;
1093 char nbuf[UINTMAX_STRSIZE_BOUND];
1094 union block *blk;
1095 char *p;
1096 size_t i;
1097
1098#define COPY_BUF(b,buf,src) do \
1099 { \
1100 char *endp = b->buffer + BLOCKSIZE; \
1101 char *dst = buf; \
1102 do \
1103 { \
1104 if (dst == buf + UINTMAX_STRSIZE_BOUND -1) \
1105 { \
1106 ERROR ((0, 0, _("%s: numeric overflow in sparse archive member"), \
1107 file->stat_info->orig_file_name)); \
1108 return false; \
1109 } \
1110 if (src == endp) \
1111 { \
1112 set_next_block_after (b); \
1113 b = find_next_block (); \
1114 src = b->buffer; \
1115 endp = b->buffer + BLOCKSIZE; \
1116 } \
1117 *dst = *src++; \
1118 } \
1119 while (*dst++ != '\n'); \
1120 dst[-1] = 0; \
1121 } while (0)
1122
1123 set_next_block_after (current_header);
1124 blk = find_next_block ();
1125 p = blk->buffer;
1126 COPY_BUF (blk,nbuf,p);
1127 if (!decode_num (&u, nbuf, TYPE_MAXIMUM (size_t)))
1128 {
1129 ERROR ((0, 0, _("%s: malformed sparse archive member"),
1130 file->stat_info->orig_file_name));
1131 return false;
1132 }
1133 file->stat_info->sparse_map_size = u;
1134 file->stat_info->sparse_map = xcalloc (file->stat_info->sparse_map_size,
1135 sizeof (*file->stat_info->sparse_map));
1136 file->stat_info->sparse_map_avail = 0;
1137 for (i = 0; i < file->stat_info->sparse_map_size; i++)
1138 {
1139 struct sp_array sp;
1140
1141 COPY_BUF (blk,nbuf,p);
1142 if (!decode_num (&u, nbuf, TYPE_MAXIMUM (off_t)))
1143 {
1144 ERROR ((0, 0, _("%s: malformed sparse archive member"),
1145 file->stat_info->orig_file_name));
1146 return false;
1147 }
1148 sp.offset = u;
1149 COPY_BUF (blk,nbuf,p);
1150 if (!decode_num (&u, nbuf, TYPE_MAXIMUM (size_t)))
1151 {
1152 ERROR ((0, 0, _("%s: malformed sparse archive member"),
1153 file->stat_info->orig_file_name));
1154 return false;
1155 }
1156 sp.numbytes = u;
1157 sparse_add_map (file->stat_info, &sp);
1158 }
1159 set_next_block_after (blk);
1160 }
1161
1162 return true;
1163}
1164
1165static struct tar_sparse_optab const pax_optab = {
1166 NULL, /* No init function */
1167 NULL, /* No done function */
1168 pax_sparse_member_p,
1169 pax_dump_header,
1170 NULL,
1171 pax_decode_header,
1172 NULL, /* No scan_block function */
1173 sparse_dump_region,
1174 sparse_extract_region,
1175};
Note: See TracBrowser for help on using the repository browser.