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