?? sparse.c
字號:
/* Functions for dealing with sparse files Copyright (C) 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */#include <system.h>#include <inttostr.h>#include <quotearg.h>#include "common.h"struct tar_sparse_file;static bool sparse_select_optab (struct tar_sparse_file *file);enum sparse_scan_state { scan_begin, scan_block, scan_end };struct tar_sparse_optab{ bool (*init) (struct tar_sparse_file *); bool (*done) (struct tar_sparse_file *); bool (*sparse_member_p) (struct tar_sparse_file *); bool (*dump_header) (struct tar_sparse_file *); bool (*fixup_header) (struct tar_sparse_file *); bool (*decode_header) (struct tar_sparse_file *); bool (*scan_block) (struct tar_sparse_file *, enum sparse_scan_state, void *); bool (*dump_region) (struct tar_sparse_file *, size_t); bool (*extract_region) (struct tar_sparse_file *, size_t);};struct tar_sparse_file{ int fd; /* File descriptor */ bool seekable; /* Is fd seekable? */ off_t offset; /* Current offset in fd if seekable==false. Otherwise unused */ off_t dumped_size; /* Number of bytes actually written to the archive */ struct tar_stat_info *stat_info; /* Information about the file */ struct tar_sparse_optab const *optab; /* Operation table */ void *closure; /* Any additional data optab calls might require */};/* Dump zeros to file->fd until offset is reached. It is used instead of lseek if the output file is not seekable */static booldump_zeros (struct tar_sparse_file *file, off_t offset){ static char const zero_buf[BLOCKSIZE]; if (offset < file->offset) { errno = EINVAL; return false; } while (file->offset < offset) { size_t size = (BLOCKSIZE < offset - file->offset ? BLOCKSIZE : offset - file->offset); ssize_t wrbytes; wrbytes = write (file->fd, zero_buf, size); if (wrbytes <= 0) { if (wrbytes == 0) errno = EINVAL; return false; } file->offset += wrbytes; } return true;}static booltar_sparse_member_p (struct tar_sparse_file *file){ if (file->optab->sparse_member_p) return file->optab->sparse_member_p (file); return false;}static booltar_sparse_init (struct tar_sparse_file *file){ memset (file, 0, sizeof *file); if (!sparse_select_optab (file)) return false; if (file->optab->init) return file->optab->init (file); return true;}static booltar_sparse_done (struct tar_sparse_file *file){ if (file->optab->done) return file->optab->done (file); return true;}static booltar_sparse_scan (struct tar_sparse_file *file, enum sparse_scan_state state, void *block){ if (file->optab->scan_block) return file->optab->scan_block (file, state, block); return true;}static booltar_sparse_dump_region (struct tar_sparse_file *file, size_t i){ if (file->optab->dump_region) return file->optab->dump_region (file, i); return false;}static booltar_sparse_extract_region (struct tar_sparse_file *file, size_t i){ if (file->optab->extract_region) return file->optab->extract_region (file, i); return false;}static booltar_sparse_dump_header (struct tar_sparse_file *file){ if (file->optab->dump_header) return file->optab->dump_header (file); return false;}static booltar_sparse_decode_header (struct tar_sparse_file *file){ if (file->optab->decode_header) return file->optab->decode_header (file); return true;}static booltar_sparse_fixup_header (struct tar_sparse_file *file){ if (file->optab->fixup_header) return file->optab->fixup_header (file); return true;}static boollseek_or_error (struct tar_sparse_file *file, off_t offset){ if (file->seekable ? lseek (file->fd, offset, SEEK_SET) < 0 : ! dump_zeros (file, offset)) { seek_diag_details (file->stat_info->orig_file_name, offset); return false; } return true;}/* Takes a blockful of data and basically cruises through it to see if it's made *entirely* of zeros, returning a 0 the instant it finds something that is a nonzero, i.e., useful data. */static boolzero_block_p (char const *buffer, size_t size){ while (size--) if (*buffer++) return false; return true;}static voidsparse_add_map (struct tar_stat_info *st, struct sp_array const *sp){ struct sp_array *sparse_map = st->sparse_map; size_t avail = st->sparse_map_avail; if (avail == st->sparse_map_size) st->sparse_map = sparse_map = x2nrealloc (sparse_map, &st->sparse_map_size, sizeof *sparse_map); sparse_map[avail] = *sp; st->sparse_map_avail = avail + 1;}/* Scan the sparse file and create its map */static boolsparse_scan_file (struct tar_sparse_file *file){ struct tar_stat_info *st = file->stat_info; int fd = file->fd; char buffer[BLOCKSIZE]; size_t count; off_t offset = 0; struct sp_array sp = {0, 0}; if (!lseek_or_error (file, 0)) return false; st->archive_file_size = 0; if (!tar_sparse_scan (file, scan_begin, NULL)) return false; while ((count = safe_read (fd, buffer, sizeof buffer)) != 0 && count != SAFE_READ_ERROR) { /* Analyze the block. */ if (zero_block_p (buffer, count)) { if (sp.numbytes) { sparse_add_map (st, &sp); sp.numbytes = 0; if (!tar_sparse_scan (file, scan_block, NULL)) return false; } } else { if (sp.numbytes == 0) sp.offset = offset; sp.numbytes += count; st->archive_file_size += count; if (!tar_sparse_scan (file, scan_block, buffer)) return false; } offset += count; } if (sp.numbytes == 0) sp.offset = offset; sparse_add_map (st, &sp); st->archive_file_size += count; return tar_sparse_scan (file, scan_end, NULL);}static struct tar_sparse_optab const oldgnu_optab;static struct tar_sparse_optab const star_optab;static struct tar_sparse_optab const pax_optab;static boolsparse_select_optab (struct tar_sparse_file *file){ switch (current_format == DEFAULT_FORMAT ? archive_format : current_format) { case V7_FORMAT: case USTAR_FORMAT: return false; case OLDGNU_FORMAT: case GNU_FORMAT: /*FIXME: This one should disappear? */ file->optab = &oldgnu_optab; break; case POSIX_FORMAT: file->optab = &pax_optab; break; case STAR_FORMAT: file->optab = &star_optab; break; default: return false; } return true;}static boolsparse_dump_region (struct tar_sparse_file *file, size_t i){ union block *blk; off_t bytes_left = file->stat_info->sparse_map[i].numbytes; if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset)) return false; while (bytes_left > 0) { size_t bufsize = (bytes_left > BLOCKSIZE) ? BLOCKSIZE : bytes_left; size_t bytes_read; blk = find_next_block (); bytes_read = safe_read (file->fd, blk->buffer, bufsize); 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 - bytes_left), bufsize); return false; } memset (blk->buffer + bytes_read, 0, BLOCKSIZE - bytes_read); bytes_left -= bytes_read; file->dumped_size += bytes_read; mv_size_left (file->stat_info->archive_file_size - file->dumped_size); set_next_block_after (blk); } return true;}static boolsparse_extract_region (struct tar_sparse_file *file, size_t i){ size_t write_size; if (!lseek_or_error (file, file->stat_info->sparse_map[i].offset)) return false; write_size = file->stat_info->sparse_map[i].numbytes; if (write_size == 0) { /* Last block of the file is a hole */ if (file->seekable && sys_truncate (file->fd)) truncate_warn (file->stat_info->orig_file_name); } else while (write_size > 0) { size_t count; size_t wrbytes = (write_size > BLOCKSIZE) ? BLOCKSIZE : write_size; union block *blk = find_next_block (); if (!blk) { ERROR ((0, 0, _("Unexpected EOF in archive"))); return false; } set_next_block_after (blk); count = full_write (file->fd, blk->buffer, wrbytes); write_size -= count; file->dumped_size += count; mv_size_left (file->stat_info->archive_file_size - file->dumped_size); file->offset += count; if (count != wrbytes) { write_error_details (file->stat_info->orig_file_name, count, wrbytes); return false; } } return true;}/* Interface functions */enum dump_statussparse_dump_file (int fd, struct tar_stat_info *st){ bool rc; struct tar_sparse_file file; 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 dump to work */ rc = sparse_scan_file (&file);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -