📄 sparse.c
字号:
if (rc && file.optab->dump_region) { tar_sparse_dump_header (&file); if (fd >= 0) { size_t i; mv_begin (file.stat_info); for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++) rc = tar_sparse_dump_region (&file, i); mv_end (); } } pad_archive (file.stat_info->archive_file_size - file.dumped_size); return (tar_sparse_done (&file) && rc) ? dump_status_ok : dump_status_short;}boolsparse_member_p (struct tar_stat_info *st){ struct tar_sparse_file file; if (!tar_sparse_init (&file)) return false; file.stat_info = st; return tar_sparse_member_p (&file);}boolsparse_fixup_header (struct tar_stat_info *st){ struct tar_sparse_file file; if (!tar_sparse_init (&file)) return false; file.stat_info = st; return tar_sparse_fixup_header (&file);}enum dump_statussparse_extract_file (int fd, struct tar_stat_info *st, off_t *size){ bool rc = true; struct tar_sparse_file file; size_t i; if (!tar_sparse_init (&file)) return dump_status_not_implemented; file.stat_info = st; file.fd = fd; file.seekable = lseek (fd, 0, SEEK_SET) == 0; file.offset = 0; rc = tar_sparse_decode_header (&file); for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++) rc = tar_sparse_extract_region (&file, i); *size = file.stat_info->archive_file_size - file.dumped_size; return (tar_sparse_done (&file) && rc) ? dump_status_ok : dump_status_short;}enum dump_statussparse_skip_file (struct tar_stat_info *st){ bool rc = true; struct tar_sparse_file file; if (!tar_sparse_init (&file)) return dump_status_not_implemented; file.stat_info = st; file.fd = -1; rc = tar_sparse_decode_header (&file); skip_file (file.stat_info->archive_file_size - file.dumped_size); return (tar_sparse_done (&file) && rc) ? dump_status_ok : dump_status_short;}static boolcheck_sparse_region (struct tar_sparse_file *file, off_t beg, off_t end){ if (!lseek_or_error (file, beg)) return false; while (beg < end) { size_t bytes_read; size_t rdsize = BLOCKSIZE < end - beg ? BLOCKSIZE : end - beg; char diff_buffer[BLOCKSIZE]; bytes_read = safe_read (file->fd, diff_buffer, rdsize); if (bytes_read == SAFE_READ_ERROR) { read_diag_details (file->stat_info->orig_file_name, beg, rdsize); return false; } if (!zero_block_p (diff_buffer, bytes_read)) { char begbuf[INT_BUFSIZE_BOUND (off_t)]; report_difference (file->stat_info, _("File fragment at %s is not a hole"), offtostr (beg, begbuf)); return false; } beg += bytes_read; } return true;}static boolcheck_data_region (struct tar_sparse_file *file, size_t i){ size_t size_left; if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset)) return false; size_left = file->stat_info->sparse_map[i].numbytes; mv_size_left (file->stat_info->archive_file_size - file->dumped_size); while (size_left > 0) { size_t bytes_read; size_t rdsize = (size_left > BLOCKSIZE) ? BLOCKSIZE : size_left; char diff_buffer[BLOCKSIZE]; union block *blk = find_next_block (); if (!blk) { ERROR ((0, 0, _("Unexpected EOF in archive"))); return false; } set_next_block_after (blk); bytes_read = safe_read (file->fd, diff_buffer, rdsize); if (bytes_read == SAFE_READ_ERROR) { read_diag_details (file->stat_info->orig_file_name, (file->stat_info->sparse_map[i].offset + file->stat_info->sparse_map[i].numbytes - size_left), rdsize); return false; } file->dumped_size += bytes_read; size_left -= bytes_read; mv_size_left (file->stat_info->archive_file_size - file->dumped_size); if (memcmp (blk->buffer, diff_buffer, rdsize)) { report_difference (file->stat_info, _("Contents differ")); return false; } } return true;}boolsparse_diff_file (int fd, struct tar_stat_info *st){ bool rc = true; struct tar_sparse_file file; size_t i; off_t offset = 0; if (!tar_sparse_init (&file)) return dump_status_not_implemented; file.stat_info = st; file.fd = fd; file.seekable = true; /* File *must* be seekable for compare to work */ rc = tar_sparse_decode_header (&file); mv_begin (st); for (i = 0; rc && i < file.stat_info->sparse_map_avail; i++) { rc = check_sparse_region (&file, offset, file.stat_info->sparse_map[i].offset) && check_data_region (&file, i); offset = file.stat_info->sparse_map[i].offset + file.stat_info->sparse_map[i].numbytes; } if (!rc) skip_file (file.stat_info->archive_file_size - file.dumped_size); mv_end (); tar_sparse_done (&file); return rc;}/* Old GNU Format. The sparse file information is stored in the oldgnu_header in the following manner: The header is marked with type 'S'. Its `size' field contains the cumulative size of all non-empty blocks of the file. The actual file size is stored in `realsize' member of oldgnu_header. The map of the file is stored in a list of `struct sparse'. Each struct contains offset to the block of data and its size (both as octal numbers). The first file header contains at most 4 such structs (SPARSES_IN_OLDGNU_HEADER). If the map contains more structs, then the field `isextended' of the main header is set to 1 (binary) and the `struct sparse_header' header follows, containing at most 21 following structs (SPARSES_IN_SPARSE_HEADER). If more structs follow, `isextended' field of the extended header is set and next next extension header follows, etc... */enum oldgnu_add_status { add_ok, add_finish, add_fail };static boololdgnu_sparse_member_p (struct tar_sparse_file *file __attribute__ ((unused))){ return current_header->header.typeflag == GNUTYPE_SPARSE;}/* Add a sparse item to the sparse file and its obstack */static enum oldgnu_add_statusoldgnu_add_sparse (struct tar_sparse_file *file, struct sparse *s){ struct sp_array sp; if (s->numbytes[0] == '\0') return add_finish; sp.offset = OFF_FROM_HEADER (s->offset); sp.numbytes = SIZE_FROM_HEADER (s->numbytes); if (sp.offset < 0 || file->stat_info->stat.st_size < sp.offset + sp.numbytes || file->stat_info->archive_file_size < 0) return add_fail; sparse_add_map (file->stat_info, &sp); return add_ok;}static boololdgnu_fixup_header (struct tar_sparse_file *file){ /* NOTE! st_size was initialized from the header which actually contains archived size. The following fixes it */ file->stat_info->archive_file_size = file->stat_info->stat.st_size; file->stat_info->stat.st_size = OFF_FROM_HEADER (current_header->oldgnu_header.realsize); return true;}/* Convert old GNU format sparse data to internal representation */static boololdgnu_get_sparse_info (struct tar_sparse_file *file){ size_t i; union block *h = current_header; int ext_p; enum oldgnu_add_status rc; file->stat_info->sparse_map_avail = 0; for (i = 0; i < SPARSES_IN_OLDGNU_HEADER; i++) { rc = oldgnu_add_sparse (file, &h->oldgnu_header.sp[i]); if (rc != add_ok) break; } for (ext_p = h->oldgnu_header.isextended; rc == add_ok && ext_p; ext_p = h->sparse_header.isextended) { h = find_next_block (); if (!h) { ERROR ((0, 0, _("Unexpected EOF in archive"))); return false; } set_next_block_after (h); for (i = 0; i < SPARSES_IN_SPARSE_HEADER && rc == add_ok; i++) rc = oldgnu_add_sparse (file, &h->sparse_header.sp[i]); } if (rc == add_fail) { ERROR ((0, 0, _("%s: invalid sparse archive member"), file->stat_info->orig_file_name)); return false; } return true;}static voidoldgnu_store_sparse_info (struct tar_sparse_file *file, size_t *pindex, struct sparse *sp, size_t sparse_size){ for (; *pindex < file->stat_info->sparse_map_avail && sparse_size > 0; sparse_size--, sp++, ++*pindex) { OFF_TO_CHARS (file->stat_info->sparse_map[*pindex].offset, sp->offset); SIZE_TO_CHARS (file->stat_info->sparse_map[*pindex].numbytes, sp->numbytes); }}static boololdgnu_dump_header (struct tar_sparse_file *file){ off_t block_ordinal = current_block_ordinal (); union block *blk; size_t i; blk = start_header (file->stat_info); blk->header.typeflag = GNUTYPE_SPARSE; if (file->stat_info->sparse_map_avail > SPARSES_IN_OLDGNU_HEADER) blk->oldgnu_header.isextended = 1; /* Store the real file size */ OFF_TO_CHARS (file->stat_info->stat.st_size, blk->oldgnu_header.realsize); /* Store the effective (shrunken) file size */ OFF_TO_CHARS (file->stat_info->archive_file_size, blk->header.size); i = 0; oldgnu_store_sparse_info (file, &i, blk->oldgnu_header.sp, SPARSES_IN_OLDGNU_HEADER); blk->oldgnu_header.isextended = i < file->stat_info->sparse_map_avail; finish_header (file->stat_info, blk, block_ordinal); while (i < file->stat_info->sparse_map_avail) { blk = find_next_block (); memset (blk->buffer, 0, BLOCKSIZE); oldgnu_store_sparse_info (file, &i, blk->sparse_header.sp, SPARSES_IN_SPARSE_HEADER); if (i < file->stat_info->sparse_map_avail) blk->sparse_header.isextended = 1; set_next_block_after (blk); } return true;}static struct tar_sparse_optab const oldgnu_optab = { NULL, /* No init function */ NULL, /* No done function */ oldgnu_sparse_member_p, oldgnu_dump_header, oldgnu_fixup_header, oldgnu_get_sparse_info, NULL, /* No scan_block function */ sparse_dump_region, sparse_extract_region,};/* Star */static boolstar_sparse_member_p (struct tar_sparse_file *file __attribute__ ((unused))){ return current_header->header.typeflag == GNUTYPE_SPARSE;}static boolstar_fixup_header (struct tar_sparse_file *file){ /* NOTE! st_size was initialized from the header which actually contains archived size. The following fixes it */ file->stat_info->archive_file_size = file->stat_info->stat.st_size; file->stat_info->stat.st_size = OFF_FROM_HEADER (current_header->star_in_header.realsize); return true;}/* Convert STAR format sparse data to internal representation */static boolstar_get_sparse_info (struct tar_sparse_file *file){ size_t i; union block *h = current_header; int ext_p; enum oldgnu_add_status rc = add_ok; file->stat_info->sparse_map_avail = 0; if (h->star_in_header.prefix[0] == '\0'
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -