]> Dogcows Code - chaz/tar/blob - src/sparse.c
A sweep of the sparse code prompted by a bug report by Jim Meyering.
[chaz/tar] / src / sparse.c
1 /* Functions for dealing with sparse files
2
3 Copyright (C) 2003, 2004, 2005 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
24 struct tar_sparse_file;
25
26 enum sparse_scan_state
27 {
28 scan_begin,
29 scan_block,
30 scan_end
31 };
32
33 struct tar_sparse_optab
34 {
35 bool (*init) (struct tar_sparse_file *);
36 bool (*done) (struct tar_sparse_file *);
37 bool (*sparse_member_p) (struct tar_sparse_file *);
38 bool (*dump_header) (struct tar_sparse_file *);
39 bool (*fixup_header) (struct tar_sparse_file *);
40 bool (*decode_header) (struct tar_sparse_file *);
41 bool (*scan_block) (struct tar_sparse_file *, enum sparse_scan_state,
42 void *);
43 bool (*dump_region) (struct tar_sparse_file *, size_t);
44 bool (*extract_region) (struct tar_sparse_file *, size_t);
45 };
46
47 struct tar_sparse_file
48 {
49 int fd; /* File descriptor */
50 bool seekable; /* Is fd seekable? */
51 off_t offset; /* Current offset in fd if seekable==false.
52 Otherwise unused */
53 off_t dumped_size; /* Number of bytes actually written
54 to the archive */
55 struct tar_stat_info *stat_info; /* Information about the file */
56 struct tar_sparse_optab const *optab;
57 void *closure; /* Any additional data optab calls might
58 require */
59 };
60
61 /* Dump zeros to file->fd until offset is reached. It is used instead of
62 lseek if the output file is not seekable */
63 static bool
64 dump_zeros (struct tar_sparse_file *file, off_t offset)
65 {
66 static char const zero_buf[BLOCKSIZE];
67
68 if (offset < file->offset)
69 {
70 errno = EINVAL;
71 return false;
72 }
73
74 while (file->offset < offset)
75 {
76 size_t size = (BLOCKSIZE < offset - file->offset
77 ? BLOCKSIZE
78 : offset - file->offset);
79 ssize_t wrbytes;
80
81 wrbytes = write (file->fd, zero_buf, size);
82 if (wrbytes <= 0)
83 {
84 if (wrbytes == 0)
85 errno = EINVAL;
86 return false;
87 }
88 file->offset += wrbytes;
89 }
90
91 return true;
92 }
93
94 static bool
95 tar_sparse_member_p (struct tar_sparse_file *file)
96 {
97 if (file->optab->sparse_member_p)
98 return file->optab->sparse_member_p (file);
99 return false;
100 }
101
102 static bool
103 tar_sparse_init (struct tar_sparse_file *file)
104 {
105 if (file->optab->init)
106 return file->optab->init (file);
107 return true;
108 }
109
110 static bool
111 tar_sparse_done (struct tar_sparse_file *file)
112 {
113 if (file->optab->done)
114 return file->optab->done (file);
115 return true;
116 }
117
118 static bool
119 tar_sparse_scan (struct tar_sparse_file *file, enum sparse_scan_state state,
120 void *block)
121 {
122 if (file->optab->scan_block)
123 return file->optab->scan_block (file, state, block);
124 return true;
125 }
126
127 static bool
128 tar_sparse_dump_region (struct tar_sparse_file *file, size_t i)
129 {
130 if (file->optab->dump_region)
131 return file->optab->dump_region (file, i);
132 return false;
133 }
134
135 static bool
136 tar_sparse_extract_region (struct tar_sparse_file *file, size_t i)
137 {
138 if (file->optab->extract_region)
139 return file->optab->extract_region (file, i);
140 return false;
141 }
142
143 static bool
144 tar_sparse_dump_header (struct tar_sparse_file *file)
145 {
146 if (file->optab->dump_header)
147 return file->optab->dump_header (file);
148 return false;
149 }
150
151 static bool
152 tar_sparse_decode_header (struct tar_sparse_file *file)
153 {
154 if (file->optab->decode_header)
155 return file->optab->decode_header (file);
156 return true;
157 }
158
159 static bool
160 tar_sparse_fixup_header (struct tar_sparse_file *file)
161 {
162 if (file->optab->fixup_header)
163 return file->optab->fixup_header (file);
164 return true;
165 }
166
167 \f
168 static bool
169 lseek_or_error (struct tar_sparse_file *file, off_t offset)
170 {
171 if (file->seekable
172 ? lseek (file->fd, offset, SEEK_SET) < 0
173 : ! dump_zeros (file, offset))
174 {
175 seek_diag_details (file->stat_info->orig_file_name, offset);
176 return false;
177 }
178 return true;
179 }
180
181 /* Takes a blockful of data and basically cruises through it to see if
182 it's made *entirely* of zeros, returning a 0 the instant it finds
183 something that is a nonzero, i.e., useful data. */
184 static bool
185 zero_block_p (char const *buffer, size_t size)
186 {
187 while (size--)
188 if (*buffer++)
189 return false;
190 return true;
191 }
192
193 static void
194 sparse_add_map (struct tar_stat_info *st, struct sp_array const *sp)
195 {
196 struct sp_array *sparse_map = st->sparse_map;
197 size_t avail = st->sparse_map_avail;
198 if (avail == st->sparse_map_size)
199 st->sparse_map = sparse_map =
200 x2nrealloc (sparse_map, &st->sparse_map_size, sizeof *sparse_map);
201 sparse_map[avail] = *sp;
202 st->sparse_map_avail = avail + 1;
203 }
204
205 /* Scan the sparse file and create its map */
206 static bool
207 sparse_scan_file (struct tar_sparse_file *file)
208 {
209 struct tar_stat_info *st = file->stat_info;
210 int fd = file->fd;
211 char buffer[BLOCKSIZE];
212 size_t count;
213 off_t offset = 0;
214 struct sp_array sp = {0, 0};
215
216 if (!lseek_or_error (file, 0))
217 return false;
218
219 if (!tar_sparse_scan (file, scan_begin, NULL))
220 return false;
221
222 while ((count = safe_read (fd, buffer, sizeof buffer)) != 0
223 && count != SAFE_READ_ERROR)
224 {
225 /* Analyze the block. */
226 if (zero_block_p (buffer, count))
227 {
228 if (sp.numbytes)
229 {
230 sparse_add_map (st, &sp);
231 sp.numbytes = 0;
232 if (!tar_sparse_scan (file, scan_block, NULL))
233 return false;
234 }
235 }
236 else
237 {
238 if (sp.numbytes == 0)
239 sp.offset = offset;
240 sp.numbytes += count;
241 st->archive_file_size += count;
242 if (!tar_sparse_scan (file, scan_block, buffer))
243 return false;
244 }
245
246 offset += count;
247 }
248
249 if (sp.numbytes == 0)
250 sp.offset = offset;
251
252 sparse_add_map (st, &sp);
253 st->archive_file_size += count;
254 return tar_sparse_scan (file, scan_end, NULL);
255 }
256
257 static struct tar_sparse_optab const oldgnu_optab;
258 static struct tar_sparse_optab const star_optab;
259 static struct tar_sparse_optab const pax_optab;
260
261 static bool
262 sparse_select_optab (struct tar_sparse_file *file)
263 {
264 switch (current_format == DEFAULT_FORMAT ? archive_format : current_format)
265 {
266 case V7_FORMAT:
267 case USTAR_FORMAT:
268 return false;
269
270 case OLDGNU_FORMAT:
271 case GNU_FORMAT: /*FIXME: This one should disappear? */
272 file->optab = &oldgnu_optab;
273 break;
274
275 case POSIX_FORMAT:
276 file->optab = &pax_optab;
277 break;
278
279 case STAR_FORMAT:
280 file->optab = &star_optab;
281 break;
282
283 default:
284 return false;
285 }
286 return true;
287 }
288
289 static bool
290 sparse_dump_region (struct tar_sparse_file *file, size_t i)
291 {
292 union block *blk;
293 off_t bytes_left = file->stat_info->sparse_map[i].numbytes;
294
295 if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset))
296 return false;
297
298 while (bytes_left > 0)
299 {
300 size_t bufsize = (bytes_left > BLOCKSIZE) ? BLOCKSIZE : bytes_left;
301 size_t bytes_read;
302
303 blk = find_next_block ();
304 bytes_read = safe_read (file->fd, blk->buffer, bufsize);
305 if (bytes_read == SAFE_READ_ERROR)
306 {
307 read_diag_details (file->stat_info->orig_file_name,
308 (file->stat_info->sparse_map[i].offset
309 + file->stat_info->sparse_map[i].numbytes
310 - bytes_left),
311 bufsize);
312 return false;
313 }
314
315 memset (blk->buffer + bytes_read, 0, BLOCKSIZE - bytes_read);
316 bytes_left -= bytes_read;
317 file->dumped_size += bytes_read;
318 set_next_block_after (blk);
319 }
320
321 return true;
322 }
323
324 static bool
325 sparse_extract_region (struct tar_sparse_file *file, size_t i)
326 {
327 size_t write_size;
328
329 if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset))
330 return false;
331
332 write_size = file->stat_info->sparse_map[i].numbytes;
333
334 if (write_size == 0)
335 {
336 /* Last block of the file is a hole */
337 if (file->seekable && sys_truncate (file->fd))
338 truncate_warn (file->stat_info->orig_file_name);
339 }
340 else while (write_size > 0)
341 {
342 size_t count;
343 size_t wrbytes = (write_size > BLOCKSIZE) ? BLOCKSIZE : write_size;
344 union block *blk = find_next_block ();
345 if (!blk)
346 {
347 ERROR ((0, 0, _("Unexpected EOF in archive")));
348 return false;
349 }
350 set_next_block_after (blk);
351 count = full_write (file->fd, blk->buffer, wrbytes);
352 write_size -= count;
353 file->dumped_size += count;
354 file->offset += count;
355 if (count != wrbytes)
356 {
357 write_error_details (file->stat_info->orig_file_name,
358 count, wrbytes);
359 return false;
360 }
361 }
362 return true;
363 }
364
365 \f
366
367 /* Interface functions */
368 enum dump_status
369 sparse_dump_file (int fd, struct tar_stat_info *st)
370 {
371 bool rc;
372 struct tar_sparse_file file = { 0, };
373
374 file.stat_info = st;
375 file.fd = fd;
376 file.seekable = true; /* File *must* be seekable for dump to work */
377
378 if (!sparse_select_optab (&file)
379 || !tar_sparse_init (&file))
380 return dump_status_not_implemented;
381
382 rc = sparse_scan_file (&file);
383 if (rc && file.optab->dump_region)
384 {
385 tar_sparse_dump_header (&file);
386
387 if (fd >= 0)
388 {
389 size_t i;
390
391 for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
392 rc = tar_sparse_dump_region (&file, i);
393 }
394 }
395
396 pad_archive (file.stat_info->archive_file_size - file.dumped_size);
397 return (tar_sparse_done (&file) && rc) ? dump_status_ok : dump_status_short;
398 }
399
400 /* Returns true if the file represented by stat is a sparse one */
401 bool
402 sparse_file_p (struct tar_stat_info *st)
403 {
404 return (ST_NBLOCKS (st->stat)
405 < (st->stat.st_size / ST_NBLOCKSIZE
406 + (st->stat.st_size % ST_NBLOCKSIZE != 0)));
407 }
408
409 bool
410 sparse_member_p (struct tar_stat_info *st)
411 {
412 struct tar_sparse_file file;
413
414 if (!sparse_select_optab (&file))
415 return false;
416 file.stat_info = st;
417 return tar_sparse_member_p (&file);
418 }
419
420 bool
421 sparse_fixup_header (struct tar_stat_info *st)
422 {
423 struct tar_sparse_file file;
424
425 if (!sparse_select_optab (&file))
426 return false;
427 file.stat_info = st;
428 return tar_sparse_fixup_header (&file);
429 }
430
431 enum dump_status
432 sparse_extract_file (int fd, struct tar_stat_info *st, off_t *size)
433 {
434 bool rc = true;
435 struct tar_sparse_file file;
436 size_t i;
437
438 file.stat_info = st;
439 file.fd = fd;
440 file.seekable = lseek (fd, 0, SEEK_SET) == 0;
441 file.offset = 0;
442
443 if (!sparse_select_optab (&file)
444 || !tar_sparse_init (&file))
445 return dump_status_not_implemented;
446
447 rc = tar_sparse_decode_header (&file);
448 for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
449 rc = tar_sparse_extract_region (&file, i);
450 *size = file.stat_info->archive_file_size - file.dumped_size;
451 return (tar_sparse_done (&file) && rc) ? dump_status_ok : dump_status_short;
452 }
453
454 enum dump_status
455 sparse_skip_file (struct tar_stat_info *st)
456 {
457 bool rc = true;
458 struct tar_sparse_file file;
459
460 file.stat_info = st;
461 file.fd = -1;
462
463 if (!sparse_select_optab (&file)
464 || !tar_sparse_init (&file))
465 return dump_status_not_implemented;
466
467 rc = tar_sparse_decode_header (&file);
468 skip_file (file.stat_info->archive_file_size);
469 return (tar_sparse_done (&file) && rc) ? dump_status_ok : dump_status_short;
470 }
471
472 \f
473 static bool
474 check_sparse_region (struct tar_sparse_file *file, off_t beg, off_t end)
475 {
476 if (!lseek_or_error (file, beg))
477 return false;
478
479 while (beg < end)
480 {
481 size_t bytes_read;
482 size_t rdsize = BLOCKSIZE < end - beg ? BLOCKSIZE : end - beg;
483 char diff_buffer[BLOCKSIZE];
484
485 bytes_read = safe_read (file->fd, diff_buffer, rdsize);
486 if (bytes_read == SAFE_READ_ERROR)
487 {
488 read_diag_details (file->stat_info->orig_file_name,
489 beg,
490 rdsize);
491 return false;
492 }
493 if (!zero_block_p (diff_buffer, bytes_read))
494 {
495 char begbuf[INT_BUFSIZE_BOUND (off_t)];
496 report_difference (file->stat_info,
497 _("File fragment at %s is not a hole"),
498 offtostr (beg, begbuf));
499 return false;
500 }
501
502 beg += bytes_read;
503 }
504 return true;
505 }
506
507 static bool
508 check_data_region (struct tar_sparse_file *file, size_t i)
509 {
510 size_t size_left;
511
512 if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset))
513 return false;
514 size_left = file->stat_info->sparse_map[i].numbytes;
515 while (size_left > 0)
516 {
517 size_t bytes_read;
518 size_t rdsize = (size_left > BLOCKSIZE) ? BLOCKSIZE : size_left;
519 char diff_buffer[BLOCKSIZE];
520
521 union block *blk = find_next_block ();
522 if (!blk)
523 {
524 ERROR ((0, 0, _("Unexpected EOF in archive")));
525 return false;
526 }
527 set_next_block_after (blk);
528 bytes_read = safe_read (file->fd, diff_buffer, rdsize);
529 if (bytes_read == SAFE_READ_ERROR)
530 {
531 read_diag_details (file->stat_info->orig_file_name,
532 (file->stat_info->sparse_map[i].offset
533 + file->stat_info->sparse_map[i].numbytes
534 - size_left),
535 rdsize);
536 return false;
537 }
538 file->dumped_size += bytes_read;
539 size_left -= bytes_read;
540 if (memcmp (blk->buffer, diff_buffer, rdsize))
541 {
542 report_difference (file->stat_info, _("Contents differ"));
543 return false;
544 }
545 }
546 return true;
547 }
548
549 bool
550 sparse_diff_file (int fd, struct tar_stat_info *st)
551 {
552 bool rc = true;
553 struct tar_sparse_file file;
554 size_t i;
555 off_t offset = 0;
556
557 file.stat_info = st;
558 file.fd = fd;
559
560 if (!sparse_select_optab (&file)
561 || !tar_sparse_init (&file))
562 return dump_status_not_implemented;
563
564 rc = tar_sparse_decode_header (&file);
565 for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++)
566 {
567 rc = check_sparse_region (&file,
568 offset, file.stat_info->sparse_map[i].offset)
569 && check_data_region (&file, i);
570 offset = file.stat_info->sparse_map[i].offset
571 + file.stat_info->sparse_map[i].numbytes;
572 }
573
574 if (!rc)
575 skip_file (file.stat_info->archive_file_size - file.dumped_size);
576
577 tar_sparse_done (&file);
578 return rc;
579 }
580
581 \f
582 /* Old GNU Format. The sparse file information is stored in the
583 oldgnu_header in the following manner:
584
585 The header is marked with type 'S'. Its `size' field contains
586 the cumulative size of all non-empty blocks of the file. The
587 actual file size is stored in `realsize' member of oldgnu_header.
588
589 The map of the file is stored in a list of `struct sparse'.
590 Each struct contains offset to the block of data and its
591 size (both as octal numbers). The first file header contains
592 at most 4 such structs (SPARSES_IN_OLDGNU_HEADER). If the map
593 contains more structs, then the field `isextended' of the main
594 header is set to 1 (binary) and the `struct sparse_header'
595 header follows, containing at most 21 following structs
596 (SPARSES_IN_SPARSE_HEADER). If more structs follow, `isextended'
597 field of the extended header is set and next next extension header
598 follows, etc... */
599
600 enum oldgnu_add_status
601 {
602 add_ok,
603 add_finish,
604 add_fail
605 };
606
607 static bool
608 oldgnu_sparse_member_p (struct tar_sparse_file *file __attribute__ ((unused)))
609 {
610 return current_header->header.typeflag == GNUTYPE_SPARSE;
611 }
612
613 /* Add a sparse item to the sparse file and its obstack */
614 static enum oldgnu_add_status
615 oldgnu_add_sparse (struct tar_sparse_file *file, struct sparse *s)
616 {
617 struct sp_array sp;
618
619 if (s->numbytes[0] == '\0')
620 return add_finish;
621 sp.offset = OFF_FROM_HEADER (s->offset);
622 sp.numbytes = SIZE_FROM_HEADER (s->numbytes);
623 if (sp.offset < 0
624 || file->stat_info->stat.st_size < sp.offset + sp.numbytes
625 || file->stat_info->archive_file_size < 0)
626 return add_fail;
627
628 sparse_add_map (file->stat_info, &sp);
629 return add_ok;
630 }
631
632 static bool
633 oldgnu_fixup_header (struct tar_sparse_file *file)
634 {
635 /* NOTE! st_size was initialized from the header
636 which actually contains archived size. The following fixes it */
637 file->stat_info->archive_file_size = file->stat_info->stat.st_size;
638 file->stat_info->stat.st_size =
639 OFF_FROM_HEADER (current_header->oldgnu_header.realsize);
640 return true;
641 }
642
643 /* Convert old GNU format sparse data to internal representation */
644 static bool
645 oldgnu_get_sparse_info (struct tar_sparse_file *file)
646 {
647 size_t i;
648 union block *h = current_header;
649 int ext_p;
650 enum oldgnu_add_status rc;
651
652 file->stat_info->sparse_map_avail = 0;
653 for (i = 0; i < SPARSES_IN_OLDGNU_HEADER; i++)
654 {
655 rc = oldgnu_add_sparse (file, &h->oldgnu_header.sp[i]);
656 if (rc != add_ok)
657 break;
658 }
659
660 for (ext_p = h->oldgnu_header.isextended;
661 rc == add_ok && ext_p; ext_p = h->sparse_header.isextended)
662 {
663 h = find_next_block ();
664 if (!h)
665 {
666 ERROR ((0, 0, _("Unexpected EOF in archive")));
667 return false;
668 }
669 set_next_block_after (h);
670 for (i = 0; i < SPARSES_IN_SPARSE_HEADER && rc == add_ok; i++)
671 rc = oldgnu_add_sparse (file, &h->sparse_header.sp[i]);
672 }
673
674 if (rc == add_fail)
675 {
676 ERROR ((0, 0, _("%s: invalid sparse archive member"),
677 file->stat_info->orig_file_name));
678 return false;
679 }
680 return true;
681 }
682
683 static void
684 oldgnu_store_sparse_info (struct tar_sparse_file *file, size_t *pindex,
685 struct sparse *sp, size_t sparse_size)
686 {
687 for (; *pindex < file->stat_info->sparse_map_avail
688 && sparse_size > 0; sparse_size--, sp++, ++*pindex)
689 {
690 OFF_TO_CHARS (file->stat_info->sparse_map[*pindex].offset,
691 sp->offset);
692 SIZE_TO_CHARS (file->stat_info->sparse_map[*pindex].numbytes,
693 sp->numbytes);
694 }
695 }
696
697 static bool
698 oldgnu_dump_header (struct tar_sparse_file *file)
699 {
700 off_t block_ordinal = current_block_ordinal ();
701 union block *blk;
702 size_t i;
703
704 blk = start_header (file->stat_info);
705 blk->header.typeflag = GNUTYPE_SPARSE;
706 if (file->stat_info->sparse_map_avail > SPARSES_IN_OLDGNU_HEADER)
707 blk->oldgnu_header.isextended = 1;
708
709 /* Store the real file size */
710 OFF_TO_CHARS (file->stat_info->stat.st_size, blk->oldgnu_header.realsize);
711 /* Store the effective (shrunken) file size */
712 OFF_TO_CHARS (file->stat_info->archive_file_size, blk->header.size);
713
714 i = 0;
715 oldgnu_store_sparse_info (file, &i,
716 blk->oldgnu_header.sp,
717 SPARSES_IN_OLDGNU_HEADER);
718 blk->oldgnu_header.isextended = i < file->stat_info->sparse_map_avail;
719 finish_header (file->stat_info, blk, block_ordinal);
720
721 while (i < file->stat_info->sparse_map_avail)
722 {
723 blk = find_next_block ();
724 memset (blk->buffer, 0, BLOCKSIZE);
725 oldgnu_store_sparse_info (file, &i,
726 blk->sparse_header.sp,
727 SPARSES_IN_SPARSE_HEADER);
728 set_next_block_after (blk);
729 if (i < file->stat_info->sparse_map_avail)
730 blk->sparse_header.isextended = 1;
731 else
732 break;
733 }
734 return true;
735 }
736
737 static struct tar_sparse_optab const oldgnu_optab = {
738 NULL, /* No init function */
739 NULL, /* No done function */
740 oldgnu_sparse_member_p,
741 oldgnu_dump_header,
742 oldgnu_fixup_header,
743 oldgnu_get_sparse_info,
744 NULL, /* No scan_block function */
745 sparse_dump_region,
746 sparse_extract_region,
747 };
748
749 \f
750 /* Star */
751
752 static bool
753 star_sparse_member_p (struct tar_sparse_file *file __attribute__ ((unused)))
754 {
755 return current_header->header.typeflag == GNUTYPE_SPARSE;
756 }
757
758 static bool
759 star_fixup_header (struct tar_sparse_file *file)
760 {
761 /* NOTE! st_size was initialized from the header
762 which actually contains archived size. The following fixes it */
763 file->stat_info->archive_file_size = file->stat_info->stat.st_size;
764 file->stat_info->stat.st_size =
765 OFF_FROM_HEADER (current_header->star_in_header.realsize);
766 return true;
767 }
768
769 /* Convert STAR format sparse data to internal representation */
770 static bool
771 star_get_sparse_info (struct tar_sparse_file *file)
772 {
773 size_t i;
774 union block *h = current_header;
775 int ext_p;
776 enum oldgnu_add_status rc = add_ok;
777
778 file->stat_info->sparse_map_avail = 0;
779
780 if (h->star_in_header.prefix[0] == '\0'
781 && h->star_in_header.sp[0].offset[10] != '\0')
782 {
783 /* Old star format */
784 for (i = 0; i < SPARSES_IN_STAR_HEADER; i++)
785 {
786 rc = oldgnu_add_sparse (file, &h->star_in_header.sp[i]);
787 if (rc != add_ok)
788 break;
789 }
790 ext_p = h->star_in_header.isextended;
791 }
792 else
793 ext_p = 1;
794
795 for (; rc == add_ok && ext_p; ext_p = h->star_ext_header.isextended)
796 {
797 h = find_next_block ();
798 if (!h)
799 {
800 ERROR ((0, 0, _("Unexpected EOF in archive")));
801 return false;
802 }
803 set_next_block_after (h);
804 for (i = 0; i < SPARSES_IN_STAR_EXT_HEADER && rc == add_ok; i++)
805 rc = oldgnu_add_sparse (file, &h->star_ext_header.sp[i]);
806 }
807
808 if (rc == add_fail)
809 {
810 ERROR ((0, 0, _("%s: invalid sparse archive member"),
811 file->stat_info->orig_file_name));
812 return false;
813 }
814 return true;
815 }
816
817
818 static struct tar_sparse_optab const star_optab = {
819 NULL, /* No init function */
820 NULL, /* No done function */
821 star_sparse_member_p,
822 NULL,
823 star_fixup_header,
824 star_get_sparse_info,
825 NULL, /* No scan_block function */
826 NULL, /* No dump region function */
827 sparse_extract_region,
828 };
829
830 \f
831 /* GNU PAX sparse file format. The sparse file map is stored in
832 x header:
833
834 GNU.sparse.size Real size of the stored file
835 GNU.sparse.numblocks Number of blocks in the sparse map
836 repeat numblocks time
837 GNU.sparse.offset Offset of the next data block
838 GNU.sparse.numbytes Size of the next data block
839 end repeat
840 */
841
842 static bool
843 pax_sparse_member_p (struct tar_sparse_file *file)
844 {
845 return file->stat_info->archive_file_size != file->stat_info->stat.st_size;
846 }
847
848 static bool
849 pax_dump_header (struct tar_sparse_file *file)
850 {
851 off_t block_ordinal = current_block_ordinal ();
852 union block *blk;
853 size_t i;
854
855 /* Store the real file size */
856 xheader_store ("GNU.sparse.size", file->stat_info, NULL);
857 xheader_store ("GNU.sparse.numblocks", file->stat_info, NULL);
858 for (i = 0; i < file->stat_info->sparse_map_avail; i++)
859 {
860 xheader_store ("GNU.sparse.offset", file->stat_info, &i);
861 xheader_store ("GNU.sparse.numbytes", file->stat_info, &i);
862 }
863
864 blk = start_header (file->stat_info);
865 /* Store the effective (shrunken) file size */
866 OFF_TO_CHARS (file->stat_info->archive_file_size, blk->header.size);
867 finish_header (file->stat_info, blk, block_ordinal);
868 return true;
869 }
870
871 static struct tar_sparse_optab const pax_optab = {
872 NULL, /* No init function */
873 NULL, /* No done function */
874 pax_sparse_member_p,
875 pax_dump_header,
876 NULL, /* No decode_header function */
877 NULL, /* No fixup_header function */
878 NULL, /* No scan_block function */
879 sparse_dump_region,
880 sparse_extract_region,
881 };
This page took 0.070736 seconds and 5 git commands to generate.