1 /* Functions for dealing with sparse files
3 Copyright (C) 2003 Free Software Foundation, Inc.
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
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.
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 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
23 struct tar_sparse_file
;
25 enum sparse_scan_state
32 struct tar_sparse_optab
34 bool (*init
) (struct tar_sparse_file
*);
35 bool (*done
) (struct tar_sparse_file
*);
36 bool (*dump_header
) (struct tar_sparse_file
*);
37 bool (*decode_header
) (struct tar_sparse_file
*);
38 bool (*scan_block
) (struct tar_sparse_file
*, enum sparse_scan_state
,
40 bool (*dump_region
) (struct tar_sparse_file
*, size_t index
);
41 bool (*extract_region
) (struct tar_sparse_file
*, size_t index
);
44 struct tar_sparse_file
46 int fd
; /* File descriptor */
47 size_t dumped_size
; /* Number of bytes actually written
49 struct tar_stat_info
*stat_info
; /* Information about the file */
50 struct tar_sparse_optab
*optab
;
51 void *closure
; /* Any additional data optab calls might
56 tar_sparse_init (struct tar_sparse_file
*file
)
58 file
->dumped_size
= 0;
59 if (file
->optab
->init
)
60 return file
->optab
->init (file
);
65 tar_sparse_done (struct tar_sparse_file
*file
)
67 if (file
->optab
->done
)
68 return file
->optab
->done (file
);
73 tar_sparse_scan (struct tar_sparse_file
*file
, enum sparse_scan_state state
,
76 if (file
->optab
->scan_block
)
77 return file
->optab
->scan_block (file
, state
, block
);
82 tar_sparse_dump_region (struct tar_sparse_file
*file
, size_t index
)
84 if (file
->optab
->dump_region
)
85 return file
->optab
->dump_region (file
, index
);
90 tar_sparse_extract_region (struct tar_sparse_file
*file
, size_t index
)
92 if (file
->optab
->extract_region
)
93 return file
->optab
->extract_region (file
, index
);
98 tar_sparse_dump_header (struct tar_sparse_file
*file
)
100 if (file
->optab
->dump_header
)
101 return file
->optab
->dump_header (file
);
106 tar_sparse_decode_header (struct tar_sparse_file
*file
)
108 if (file
->optab
->decode_header
)
109 return file
->optab
->decode_header (file
);
115 lseek_or_error (struct tar_sparse_file
*file
, off_t offset
, int whence
)
117 if (lseek (file
->fd
, offset
, whence
) < 0)
119 seek_diag_details (file
->stat_info
->orig_file_name
, offset
);
125 /* Takes a blockful of data and basically cruises through it to see if
126 it's made *entirely* of zeros, returning a 0 the instant it finds
127 something that is a nonzero, i.e., useful data. */
129 zero_block_p (char *buffer
, size_t size
)
137 #define clear_block(p) memset (p, 0, BLOCKSIZE);
139 #define SPARSES_INIT_COUNT SPARSES_IN_SPARSE_HEADER
142 sparse_add_map (struct tar_sparse_file
*file
, struct sp_array
*sp
)
144 if (file
->stat_info
->sparse_map
== NULL
)
146 file
->stat_info
->sparse_map
=
147 xmalloc (SPARSES_INIT_COUNT
* sizeof file
->stat_info
->sparse_map
[0]);
148 file
->stat_info
->sparse_map_size
= SPARSES_INIT_COUNT
;
150 else if (file
->stat_info
->sparse_map_avail
== file
->stat_info
->sparse_map_size
)
152 file
->stat_info
->sparse_map_size
*= 2;
153 file
->stat_info
->sparse_map
=
154 xrealloc (file
->stat_info
->sparse_map
,
155 file
->stat_info
->sparse_map_size
156 * sizeof file
->stat_info
->sparse_map
[0]);
158 file
->stat_info
->sparse_map
[file
->stat_info
->sparse_map_avail
++] = *sp
;
161 /* Scan the sparse file and create its map */
163 sparse_scan_file (struct tar_sparse_file
*file
)
165 static char buffer
[BLOCKSIZE
];
168 struct sp_array sp
= {0, 0};
170 if (!lseek_or_error (file
, 0, SEEK_SET
))
172 clear_block (buffer
);
174 file
->stat_info
->sparse_map_size
= 0;
175 file
->stat_info
->archive_file_size
= 0;
177 if (!tar_sparse_scan (file
, scan_begin
, NULL
))
180 while ((count
= safe_read (file
->fd
, buffer
, sizeof buffer
)) > 0)
182 /* Analize the block */
183 if (zero_block_p (buffer
, count
))
187 sparse_add_map (file
, &sp
);
189 if (!tar_sparse_scan (file
, scan_block
, NULL
))
195 if (sp
.numbytes
== 0)
197 sp
.numbytes
+= count
;
198 file
->stat_info
->archive_file_size
+= count
;
199 if (!tar_sparse_scan (file
, scan_block
, buffer
))
204 clear_block (buffer
);
207 if (sp
.numbytes
== 0)
209 sp
.offset
= offset
- 1;
212 sparse_add_map (file
, &sp
);
213 file
->stat_info
->archive_file_size
+= count
;
214 return tar_sparse_scan (file
, scan_end
, NULL
);
217 static struct tar_sparse_optab oldgnu_optab
;
218 static struct tar_sparse_optab star_optab
;
221 sparse_select_optab (struct tar_sparse_file
*file
)
223 switch (current_format
== DEFAULT_FORMAT
? archive_format
: current_format
)
230 case GNU_FORMAT
: /*FIXME: This one should disappear? */
231 file
->optab
= &oldgnu_optab
;
235 /* FIXME: Add method */
239 file
->optab
= &star_optab
;
249 sparse_dump_region (struct tar_sparse_file
*file
, size_t index
)
252 off_t bytes_left
= file
->stat_info
->sparse_map
[index
].numbytes
;
254 if (!lseek_or_error (file
, file
->stat_info
->sparse_map
[index
].offset
,
260 size_t bufsize
= (bytes_left
> BLOCKSIZE
) ? BLOCKSIZE
: bytes_left
;
263 blk
= find_next_block ();
264 memset (blk
->buffer
, 0, BLOCKSIZE
);
265 bytes_read
= safe_read (file
->fd
, blk
->buffer
, bufsize
);
268 read_diag_details (file
->stat_info
->orig_file_name
,
269 file
->stat_info
->sparse_map
[index
].offset
270 + file
->stat_info
->sparse_map
[index
].numbytes
276 bytes_left
-= bytes_read
;
277 file
->dumped_size
+= bytes_read
;
278 set_next_block_after (blk
);
280 while (bytes_left
> 0);
285 sparse_extract_region (struct tar_sparse_file
*file
, size_t index
)
289 if (!lseek_or_error (file
, file
->stat_info
->sparse_map
[index
].offset
,
292 write_size
= file
->stat_info
->sparse_map
[index
].numbytes
;
293 while (write_size
> 0)
296 size_t wrbytes
= (write_size
> BLOCKSIZE
) ? BLOCKSIZE
: write_size
;
297 union block
*blk
= find_next_block ();
300 ERROR ((0, 0, _("Unexpected EOF in archive")));
303 set_next_block_after (blk
);
304 count
= full_write (file
->fd
, blk
->buffer
, wrbytes
);
306 file
->dumped_size
+= count
;
307 if (count
!= wrbytes
)
309 write_error_details (file
->stat_info
->orig_file_name
,
319 /* Interface functions */
321 sparse_dump_file (int fd
, struct tar_stat_info
*stat
)
324 struct tar_sparse_file file
;
326 file
.stat_info
= stat
;
329 if (!sparse_select_optab (&file
)
330 || !tar_sparse_init (&file
))
331 return dump_status_not_implemented
;
333 rc
= sparse_scan_file (&file
);
334 if (rc
&& file
.optab
->dump_region
)
336 tar_sparse_dump_header (&file
);
342 for (i
= 0; rc
&& i
< file
.stat_info
->sparse_map_avail
; i
++)
343 rc
= tar_sparse_dump_region (&file
, i
);
347 pad_archive(file
.stat_info
->archive_file_size
- file
.dumped_size
);
348 return (tar_sparse_done (&file
) && rc
) ? dump_status_ok
: dump_status_short
;
351 /* Returns true if the file represented by stat is a sparse one */
353 sparse_file_p (struct tar_stat_info
*stat
)
355 return (ST_NBLOCKS (stat
->stat
)
356 < (stat
->stat
.st_size
/ ST_NBLOCKSIZE
357 + (stat
->stat
.st_size
% ST_NBLOCKSIZE
!= 0)));
361 sparse_extract_file (int fd
, struct tar_stat_info
*stat
, off_t
*size
)
364 struct tar_sparse_file file
;
367 file
.stat_info
= stat
;
370 if (!sparse_select_optab (&file
)
371 || !tar_sparse_init (&file
))
372 return dump_status_not_implemented
;
374 rc
= tar_sparse_decode_header (&file
);
375 for (i
= 0; rc
&& i
< file
.stat_info
->sparse_map_avail
; i
++)
376 rc
= tar_sparse_extract_region (&file
, i
);
377 *size
= file
.stat_info
->archive_file_size
- file
.dumped_size
;
378 return (tar_sparse_done (&file
) && rc
) ? dump_status_ok
: dump_status_short
;
382 static char diff_buffer
[BLOCKSIZE
];
385 check_sparse_region (struct tar_sparse_file
*file
, off_t beg
, off_t end
)
387 if (!lseek_or_error (file
, beg
, SEEK_SET
))
393 size_t rdsize
= end
- beg
;
395 if (rdsize
> BLOCKSIZE
)
397 clear_block (diff_buffer
);
398 bytes_read
= safe_read (file
->fd
, diff_buffer
, rdsize
);
401 read_diag_details (file
->stat_info
->orig_file_name
,
406 if (!zero_block_p (diff_buffer
, bytes_read
))
408 report_difference (file
->stat_info
,
409 _("File fragment at %lu is not a hole"), beg
);
419 check_data_region (struct tar_sparse_file
*file
, size_t index
)
423 if (!lseek_or_error (file
, file
->stat_info
->sparse_map
[index
].offset
,
426 size_left
= file
->stat_info
->sparse_map
[index
].numbytes
;
427 while (size_left
> 0)
430 size_t rdsize
= (size_left
> BLOCKSIZE
) ? BLOCKSIZE
: size_left
;
432 union block
*blk
= find_next_block ();
435 ERROR ((0, 0, _("Unexpected EOF in archive")));
438 set_next_block_after (blk
);
439 bytes_read
= safe_read (file
->fd
, diff_buffer
, rdsize
);
442 read_diag_details (file
->stat_info
->orig_file_name
,
443 file
->stat_info
->sparse_map
[index
].offset
444 + file
->stat_info
->sparse_map
[index
].numbytes
449 file
->dumped_size
+= bytes_read
;
450 size_left
-= bytes_read
;
451 if (memcmp (blk
->buffer
, diff_buffer
, rdsize
))
453 report_difference (file
->stat_info
, _("Contents differ"));
461 sparse_diff_file (int fd
, struct tar_stat_info
*stat
)
464 struct tar_sparse_file file
;
468 file
.stat_info
= stat
;
471 if (!sparse_select_optab (&file
)
472 || !tar_sparse_init (&file
))
473 return dump_status_not_implemented
;
475 rc
= tar_sparse_decode_header (&file
);
476 for (i
= 0; rc
&& i
< file
.stat_info
->sparse_map_avail
; i
++)
478 rc
= check_sparse_region (&file
,
479 offset
, file
.stat_info
->sparse_map
[i
].offset
)
480 && check_data_region (&file
, i
);
481 offset
= file
.stat_info
->sparse_map
[i
].offset
482 + file
.stat_info
->sparse_map
[i
].numbytes
;
486 skip_file (file
.stat_info
->archive_file_size
- file
.dumped_size
);
488 tar_sparse_done (&file
);
493 /* Old GNU Format. The sparse file information is stored in the
494 oldgnu_header in the following manner:
496 The header is marked with type 'S'. Its `size' field contains
497 the cumulative size of all non-empty blocks of the file. The
498 actual file size is stored in `realsize' member of oldgnu_header.
500 The map of the file is stored in a list of `struct sparse'.
501 Each struct contains offset to the block of data and its
502 size (both as octal numbers). The first file header contains
503 at most 4 such structs (SPARSES_IN_OLDGNU_HEADER). If the map
504 contains more structs, then the field `isextended' of the main
505 header is set to 1 (binary) and the `struct sparse_header'
506 header follows, containing at most 21 following structs
507 (SPARSES_IN_SPARSE_HEADER). If more structs follow, `isextended'
508 field of the extended header is set and next next extension header
511 enum oldgnu_add_status
518 /* Add a sparse item to the sparse file and its obstack */
519 static enum oldgnu_add_status
520 oldgnu_add_sparse (struct tar_sparse_file
*file
, struct sparse
*s
)
524 if (s
->numbytes
[0] == '\0')
526 sp
.offset
= OFF_FROM_HEADER (s
->offset
);
527 sp
.numbytes
= SIZE_FROM_HEADER (s
->numbytes
);
529 || file
->stat_info
->stat
.st_size
< sp
.offset
+ sp
.numbytes
530 || file
->stat_info
->archive_file_size
< 0)
533 sparse_add_map (file
, &sp
);
537 /* Convert old GNU format sparse data to internal representation
538 FIXME: Clubbers current_header! */
540 oldgnu_get_sparse_info (struct tar_sparse_file
*file
)
543 union block
*h
= current_header
;
545 static enum oldgnu_add_status rc
;
547 /* FIXME: note this! st_size was initialized from the header
548 which actually contains archived size. The following fixes it */
549 file
->stat_info
->archive_file_size
= file
->stat_info
->stat
.st_size
;
550 file
->stat_info
->stat
.st_size
=
551 OFF_FROM_HEADER (current_header
->oldgnu_header
.realsize
);
553 file
->stat_info
->sparse_map_size
= 0;
554 for (i
= 0; i
< SPARSES_IN_OLDGNU_HEADER
; i
++)
556 rc
= oldgnu_add_sparse (file
, &h
->oldgnu_header
.sp
[i
]);
561 for (ext_p
= h
->oldgnu_header
.isextended
;
562 rc
== add_ok
&& ext_p
; ext_p
= h
->sparse_header
.isextended
)
564 h
= find_next_block ();
567 ERROR ((0, 0, _("Unexpected EOF in archive")));
570 set_next_block_after (h
);
571 for (i
= 0; i
< SPARSES_IN_SPARSE_HEADER
&& rc
== add_ok
; i
++)
572 rc
= oldgnu_add_sparse (file
, &h
->sparse_header
.sp
[i
]);
577 ERROR ((0, 0, _("%s: invalid sparse archive member"),
578 file
->stat_info
->orig_file_name
));
585 oldgnu_store_sparse_info (struct tar_sparse_file
*file
, size_t *pindex
,
586 struct sparse
*sp
, size_t sparse_size
)
588 for (; *pindex
< file
->stat_info
->sparse_map_avail
589 && sparse_size
> 0; sparse_size
--, sp
++, ++*pindex
)
591 OFF_TO_CHARS (file
->stat_info
->sparse_map
[*pindex
].offset
,
593 SIZE_TO_CHARS (file
->stat_info
->sparse_map
[*pindex
].numbytes
,
599 oldgnu_dump_header (struct tar_sparse_file
*file
)
601 off_t block_ordinal
= current_block_ordinal ();
605 blk
= start_header (file
->stat_info
);
606 blk
->header
.typeflag
= GNUTYPE_SPARSE
;
607 if (file
->stat_info
->sparse_map_avail
> SPARSES_IN_OLDGNU_HEADER
)
608 blk
->oldgnu_header
.isextended
= 1;
610 /* Store the real file size */
611 OFF_TO_CHARS (file
->stat_info
->stat
.st_size
, blk
->oldgnu_header
.realsize
);
612 /* Store the effective (shrunken) file size */
613 OFF_TO_CHARS (file
->stat_info
->archive_file_size
, blk
->header
.size
);
616 oldgnu_store_sparse_info (file
, &i
,
617 blk
->oldgnu_header
.sp
,
618 SPARSES_IN_OLDGNU_HEADER
);
619 blk
->oldgnu_header
.isextended
= i
< file
->stat_info
->sparse_map_avail
;
620 finish_header (file
->stat_info
, blk
, block_ordinal
);
622 while (i
< file
->stat_info
->sparse_map_avail
)
624 blk
= find_next_block ();
625 memset (blk
->buffer
, 0, BLOCKSIZE
);
626 oldgnu_store_sparse_info (file
, &i
,
627 blk
->sparse_header
.sp
,
628 SPARSES_IN_SPARSE_HEADER
);
629 set_next_block_after (blk
);
630 if (i
< file
->stat_info
->sparse_map_avail
)
631 blk
->sparse_header
.isextended
= 1;
638 static struct tar_sparse_optab oldgnu_optab
= {
639 NULL
, /* No init function */
640 NULL
, /* No done function */
642 oldgnu_get_sparse_info
,
643 NULL
, /* No scan_block function */
645 sparse_extract_region
,
651 /* Convert STAR format sparse data to internal representation
652 FIXME: Clubbers current_header! */
654 star_get_sparse_info (struct tar_sparse_file
*file
)
657 union block
*h
= current_header
;
659 static enum oldgnu_add_status rc
;
661 /* FIXME: note this! st_size was initialized from the header
662 which actually contains archived size. The following fixes it */
663 file
->stat_info
->archive_file_size
= file
->stat_info
->stat
.st_size
;
664 file
->stat_info
->stat
.st_size
=
665 OFF_FROM_HEADER (current_header
->star_in_header
.realsize
);
667 file
->stat_info
->sparse_map_size
= 0;
669 if (h
->star_in_header
.prefix
[0] == '\0'
670 && h
->star_in_header
.sp
[0].offset
[10] != '\0')
672 /* Old star format */
673 for (i
= 0; i
< SPARSES_IN_STAR_HEADER
; i
++)
675 rc
= oldgnu_add_sparse (file
, &h
->star_in_header
.sp
[i
]);
679 ext_p
= h
->star_in_header
.isextended
;
684 for (; rc
== add_ok
&& ext_p
; ext_p
= h
->star_ext_header
.isextended
)
686 h
= find_next_block ();
689 ERROR ((0, 0, _("Unexpected EOF in archive")));
692 set_next_block_after (h
);
693 for (i
= 0; i
< SPARSES_IN_STAR_EXT_HEADER
&& rc
== add_ok
; i
++)
694 rc
= oldgnu_add_sparse (file
, &h
->star_ext_header
.sp
[i
]);
699 ERROR ((0, 0, _("%s: invalid sparse archive member"),
700 file
->stat_info
->orig_file_name
));
707 static struct tar_sparse_optab star_optab
= {
708 NULL
, /* No init function */
709 NULL
, /* No done function */
711 star_get_sparse_info
,
712 NULL
, /* No scan_block function */
713 NULL
, /* No dump region function */
714 sparse_extract_region
,