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
;
220 sparse_select_optab (struct tar_sparse_file
*file
)
222 switch (archive_format
)
229 case GNU_FORMAT
: /*FIXME: This one should disappear? */
230 file
->optab
= &oldgnu_optab
;
235 /* FIXME: Add methods */
245 sparse_dump_region (struct tar_sparse_file
*file
, size_t index
)
248 off_t bytes_left
= file
->stat_info
->sparse_map
[index
].numbytes
;
250 if (!lseek_or_error (file
, file
->stat_info
->sparse_map
[index
].offset
,
256 size_t bufsize
= (bytes_left
> BLOCKSIZE
) ? BLOCKSIZE
: bytes_left
;
259 blk
= find_next_block ();
260 memset (blk
->buffer
, 0, BLOCKSIZE
);
261 bytes_read
= safe_read (file
->fd
, blk
->buffer
, bufsize
);
264 read_diag_details (file
->stat_info
->orig_file_name
,
265 file
->stat_info
->sparse_map
[index
].offset
266 + file
->stat_info
->sparse_map
[index
].numbytes
272 bytes_left
-= bytes_read
;
273 file
->dumped_size
+= bytes_read
;
274 set_next_block_after (blk
);
276 while (bytes_left
> 0);
281 sparse_extract_region (struct tar_sparse_file
*file
, size_t index
)
285 if (!lseek_or_error (file
, file
->stat_info
->sparse_map
[index
].offset
,
288 write_size
= file
->stat_info
->sparse_map
[index
].numbytes
;
289 while (write_size
> 0)
292 size_t wrbytes
= (write_size
> BLOCKSIZE
) ? BLOCKSIZE
: write_size
;
293 union block
*blk
= find_next_block ();
296 ERROR ((0, 0, _("Unexpected EOF in archive")));
299 set_next_block_after (blk
);
300 count
= full_write (file
->fd
, blk
->buffer
, wrbytes
);
302 file
->dumped_size
+= count
;
303 if (count
!= wrbytes
)
305 write_error_details (file
->stat_info
->orig_file_name
,
315 /* Interface functions */
317 sparse_dump_file (int fd
, struct tar_stat_info
*stat
)
320 struct tar_sparse_file file
;
322 file
.stat_info
= stat
;
325 if (!sparse_select_optab (&file
)
326 || !tar_sparse_init (&file
))
327 return dump_status_not_implemented
;
329 rc
= sparse_scan_file (&file
);
330 if (rc
&& file
.optab
->dump_region
)
332 tar_sparse_dump_header (&file
);
338 for (i
= 0; rc
&& i
< file
.stat_info
->sparse_map_avail
; i
++)
339 rc
= tar_sparse_dump_region (&file
, i
);
343 pad_archive(file
.stat_info
->archive_file_size
- file
.dumped_size
);
344 return (tar_sparse_done (&file
) && rc
) ? dump_status_ok
: dump_status_short
;
347 /* Returns true if the file represented by stat is a sparse one */
349 sparse_file_p (struct tar_stat_info
*stat
)
351 return (ST_NBLOCKS (stat
->stat
)
352 < (stat
->stat
.st_size
/ ST_NBLOCKSIZE
353 + (stat
->stat
.st_size
% ST_NBLOCKSIZE
!= 0)));
357 sparse_extract_file (int fd
, struct tar_stat_info
*stat
, off_t
*size
)
360 struct tar_sparse_file file
;
363 file
.stat_info
= stat
;
366 if (!sparse_select_optab (&file
)
367 || !tar_sparse_init (&file
))
368 return dump_status_not_implemented
;
370 rc
= tar_sparse_decode_header (&file
);
371 for (i
= 0; rc
&& i
< file
.stat_info
->sparse_map_avail
; i
++)
372 rc
= tar_sparse_extract_region (&file
, i
);
373 *size
= file
.stat_info
->archive_file_size
- file
.dumped_size
;
374 return (tar_sparse_done (&file
) && rc
) ? dump_status_ok
: dump_status_short
;
378 static char diff_buffer
[BLOCKSIZE
];
381 check_sparse_region (struct tar_sparse_file
*file
, off_t beg
, off_t end
)
383 if (!lseek_or_error (file
, beg
, SEEK_SET
))
389 size_t rdsize
= end
- beg
;
391 if (rdsize
> BLOCKSIZE
)
393 clear_block (diff_buffer
);
394 bytes_read
= safe_read (file
->fd
, diff_buffer
, rdsize
);
397 read_diag_details (file
->stat_info
->orig_file_name
,
402 if (!zero_block_p (diff_buffer
, bytes_read
))
404 report_difference (file
->stat_info
,
405 _("File fragment at %lu is not a hole"), beg
);
415 check_data_region (struct tar_sparse_file
*file
, size_t index
)
419 if (!lseek_or_error (file
, file
->stat_info
->sparse_map
[index
].offset
,
422 size_left
= file
->stat_info
->sparse_map
[index
].numbytes
;
423 while (size_left
> 0)
426 size_t rdsize
= (size_left
> BLOCKSIZE
) ? BLOCKSIZE
: size_left
;
428 union block
*blk
= find_next_block ();
431 ERROR ((0, 0, _("Unexpected EOF in archive")));
434 set_next_block_after (blk
);
435 bytes_read
= safe_read (file
->fd
, diff_buffer
, rdsize
);
438 read_diag_details (file
->stat_info
->orig_file_name
,
439 file
->stat_info
->sparse_map
[index
].offset
440 + file
->stat_info
->sparse_map
[index
].numbytes
445 file
->dumped_size
+= bytes_read
;
446 size_left
-= bytes_read
;
447 if (memcmp (blk
->buffer
, diff_buffer
, rdsize
))
449 report_difference (file
->stat_info
, _("Contents differ"));
457 sparse_diff_file (int fd
, struct tar_stat_info
*stat
)
460 struct tar_sparse_file file
;
464 file
.stat_info
= stat
;
467 if (!sparse_select_optab (&file
)
468 || !tar_sparse_init (&file
))
469 return dump_status_not_implemented
;
471 rc
= tar_sparse_decode_header (&file
);
472 for (i
= 0; rc
&& i
< file
.stat_info
->sparse_map_avail
; i
++)
474 rc
= check_sparse_region (&file
,
475 offset
, file
.stat_info
->sparse_map
[i
].offset
)
476 && check_data_region (&file
, i
);
477 offset
= file
.stat_info
->sparse_map
[i
].offset
478 + file
.stat_info
->sparse_map
[i
].numbytes
;
482 skip_file (file
.stat_info
->archive_file_size
- file
.dumped_size
);
484 tar_sparse_done (&file
);
489 /* Old GNU Format. The sparse file information is stored in the
490 oldgnu_header in the following manner:
492 The header is marked with type 'S'. Its `size' field contains
493 the cumulative size of all non-empty blocks of the file. The
494 actual file size is stored in `realsize' member of oldgnu_header.
496 The map of the file is stored in a list of `struct sparse'.
497 Each struct contains offset to the block of data and its
498 size (both as octal numbers). The first file header contains
499 at most 4 such structs (SPARSES_IN_OLDGNU_HEADER). If the map
500 contains more structs, then the field `isextended' of the main
501 header is set to 1 (binary) and the `struct sparse_header'
502 header follows, containing at most 21 following structs
503 (SPARSES_IN_SPARSE_HEADER). If more structs follow, `isextended'
504 field of the extended header is set and next next extension header
507 enum oldgnu_add_status
514 /* Add a sparse item to the sparse file and its obstack */
515 static enum oldgnu_add_status
516 oldgnu_add_sparse (struct tar_sparse_file
*file
, struct sparse
*s
)
520 if (s
->numbytes
[0] == '\0')
522 sp
.offset
= OFF_FROM_HEADER (s
->offset
);
523 sp
.numbytes
= SIZE_FROM_HEADER (s
->numbytes
);
525 || file
->stat_info
->stat
.st_size
< sp
.offset
+ sp
.numbytes
526 || file
->stat_info
->archive_file_size
< 0)
529 sparse_add_map (file
, &sp
);
533 /* Convert old GNU format sparse data to internal representation
534 FIXME: Clubbers current_header! */
536 oldgnu_get_sparse_info (struct tar_sparse_file
*file
)
539 union block
*h
= current_header
;
541 static enum oldgnu_add_status rc
;
543 /* FIXME: note this! st_size was initialized from the header
544 which actually contains archived size. The following fixes it */
545 file
->stat_info
->archive_file_size
= file
->stat_info
->stat
.st_size
;
546 file
->stat_info
->stat
.st_size
=
547 OFF_FROM_HEADER (current_header
->oldgnu_header
.realsize
);
549 file
->stat_info
->sparse_map_size
= 0;
550 for (i
= 0; i
< SPARSES_IN_OLDGNU_HEADER
; i
++)
552 rc
= oldgnu_add_sparse (file
, &h
->oldgnu_header
.sp
[i
]);
557 for (ext_p
= h
->oldgnu_header
.isextended
;
558 rc
== add_ok
&& ext_p
; ext_p
= h
->sparse_header
.isextended
)
560 h
= find_next_block ();
563 ERROR ((0, 0, _("Unexpected EOF in archive")));
566 set_next_block_after (h
);
567 for (i
= 0; i
< SPARSES_IN_SPARSE_HEADER
&& rc
== add_ok
; i
++)
568 rc
= oldgnu_add_sparse (file
, &h
->sparse_header
.sp
[i
]);
573 ERROR ((0, 0, _("%s: invalid sparse archive member"),
574 file
->stat_info
->orig_file_name
));
581 oldgnu_store_sparse_info (struct tar_sparse_file
*file
, size_t *pindex
,
582 struct sparse
*sp
, size_t sparse_size
)
584 for (; *pindex
< file
->stat_info
->sparse_map_avail
585 && sparse_size
> 0; sparse_size
--, sp
++, ++*pindex
)
587 OFF_TO_CHARS (file
->stat_info
->sparse_map
[*pindex
].offset
,
589 SIZE_TO_CHARS (file
->stat_info
->sparse_map
[*pindex
].numbytes
,
595 oldgnu_dump_header (struct tar_sparse_file
*file
)
597 off_t block_ordinal
= current_block_ordinal ();
601 blk
= start_header (file
->stat_info
);
602 blk
->header
.typeflag
= GNUTYPE_SPARSE
;
603 if (file
->stat_info
->sparse_map_avail
> SPARSES_IN_OLDGNU_HEADER
)
604 blk
->oldgnu_header
.isextended
= 1;
606 /* Store the real file size */
607 OFF_TO_CHARS (file
->stat_info
->stat
.st_size
, blk
->oldgnu_header
.realsize
);
608 /* Store the effective (shrunken) file size */
609 OFF_TO_CHARS (file
->stat_info
->archive_file_size
, blk
->header
.size
);
612 oldgnu_store_sparse_info (file
, &i
,
613 blk
->oldgnu_header
.sp
,
614 SPARSES_IN_OLDGNU_HEADER
);
615 blk
->oldgnu_header
.isextended
= i
< file
->stat_info
->sparse_map_avail
;
616 finish_header (file
->stat_info
, blk
, block_ordinal
);
618 while (i
< file
->stat_info
->sparse_map_avail
)
620 blk
= find_next_block ();
621 memset (blk
->buffer
, 0, BLOCKSIZE
);
622 oldgnu_store_sparse_info (file
, &i
,
623 blk
->sparse_header
.sp
,
624 SPARSES_IN_SPARSE_HEADER
);
625 set_next_block_after (blk
);
626 if (i
< file
->stat_info
->sparse_map_avail
)
627 blk
->sparse_header
.isextended
= 1;
634 static struct tar_sparse_optab oldgnu_optab
= {
635 NULL
, /* No init function */
636 NULL
, /* No done function */
638 oldgnu_get_sparse_info
,
639 NULL
, /* No scan_block function */
641 sparse_extract_region
,