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