📄 list.c
字号:
/* List a tar archive, with support routines for reading a tar archive. Copyright (C) 1988, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Written by John Gilmore, on 1985-08-26. 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"#define max(a, b) ((a) < (b) ? (b) : (a))union block *current_header; /* points to current archive header */enum archive_format current_format; /* recognized format */union block *recent_long_name; /* recent long name header and contents */union block *recent_long_link; /* likewise, for long link */size_t recent_long_name_blocks; /* number of blocks in recent_long_name */size_t recent_long_link_blocks; /* likewise, for long link */static uintmax_t from_header (const char *, size_t, const char *, uintmax_t, uintmax_t, bool, bool);/* Base 64 digits; see Internet RFC 2045 Table 1. */static char const base_64_digits[64] ={ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};/* Table of base-64 digit values indexed by unsigned chars. The value is 64 for unsigned chars that are not base-64 digits. */static char base64_map[UCHAR_MAX + 1];static voidbase64_init (void){ int i; memset (base64_map, 64, sizeof base64_map); for (i = 0; i < 64; i++) base64_map[(int) base_64_digits[i]] = i;}/* Main loop for reading an archive. */voidread_and (void (*do_something) (void)){ enum read_header status = HEADER_STILL_UNREAD; enum read_header prev_status; struct timespec mtime; base64_init (); name_gather (); open_archive (ACCESS_READ); do { prev_status = status; tar_stat_destroy (¤t_stat_info); status = read_header (false); switch (status) { case HEADER_STILL_UNREAD: case HEADER_SUCCESS_EXTENDED: abort (); case HEADER_SUCCESS: /* Valid header. We should decode next field (mode) first. Ensure incoming names are null terminated. */ if (! name_match (current_stat_info.file_name) || (NEWER_OPTION_INITIALIZED (newer_mtime_option) /* FIXME: We get mtime now, and again later; this causes duplicate diagnostics if header.mtime is bogus. */ && ((mtime.tv_sec = TIME_FROM_HEADER (current_header->header.mtime)), /* FIXME: Grab fractional time stamps from extended header. */ mtime.tv_nsec = 0, current_stat_info.mtime = mtime, OLDER_TAR_STAT_TIME (current_stat_info, m))) || excluded_name (current_stat_info.file_name)) { switch (current_header->header.typeflag) { case GNUTYPE_VOLHDR: case GNUTYPE_MULTIVOL: break; case DIRTYPE: if (show_omitted_dirs_option) WARN ((0, 0, _("%s: Omitting"), quotearg_colon (current_stat_info.file_name))); /* Fall through. */ default: decode_header (current_header, ¤t_stat_info, ¤t_format, 0); skip_member (); continue; } } (*do_something) (); continue; case HEADER_ZERO_BLOCK: if (block_number_option) { char buf[UINTMAX_STRSIZE_BOUND]; fprintf (stdlis, _("block %s: ** Block of NULs **\n"), STRINGIFY_BIGINT (current_block_ordinal (), buf)); } set_next_block_after (current_header); if (!ignore_zeros_option) { char buf[UINTMAX_STRSIZE_BOUND]; status = read_header (false); if (status == HEADER_ZERO_BLOCK) break; WARN ((0, 0, _("A lone zero block at %s"), STRINGIFY_BIGINT (current_block_ordinal (), buf))); break; } status = prev_status; continue; case HEADER_END_OF_FILE: if (block_number_option) { char buf[UINTMAX_STRSIZE_BOUND]; fprintf (stdlis, _("block %s: ** End of File **\n"), STRINGIFY_BIGINT (current_block_ordinal (), buf)); } break; case HEADER_FAILURE: /* If the previous header was good, tell them that we are skipping bad ones. */ set_next_block_after (current_header); switch (prev_status) { case HEADER_STILL_UNREAD: ERROR ((0, 0, _("This does not look like a tar archive"))); /* Fall through. */ case HEADER_ZERO_BLOCK: case HEADER_SUCCESS: if (block_number_option) { char buf[UINTMAX_STRSIZE_BOUND]; off_t block_ordinal = current_block_ordinal (); block_ordinal -= recent_long_name_blocks; block_ordinal -= recent_long_link_blocks; fprintf (stdlis, _("block %s: "), STRINGIFY_BIGINT (block_ordinal, buf)); } ERROR ((0, 0, _("Skipping to next header"))); break; case HEADER_END_OF_FILE: case HEADER_FAILURE: /* We are in the middle of a cascade of errors. */ break; case HEADER_SUCCESS_EXTENDED: abort (); } continue; } break; } while (!all_names_found (¤t_stat_info)); close_archive (); names_notfound (); /* print names not found */}/* Print a header block, based on tar options. */voidlist_archive (void){ off_t block_ordinal = current_block_ordinal (); /* Print the header block. */ decode_header (current_header, ¤t_stat_info, ¤t_format, 0); if (verbose_option) print_header (¤t_stat_info, block_ordinal); if (incremental_option) { if (verbose_option > 2) { if (is_dumpdir (¤t_stat_info)) list_dumpdir (current_stat_info.dumpdir, dumpdir_size (current_stat_info.dumpdir)); } } skip_member ();}/* Check header checksum *//* The standard BSD tar sources create the checksum by adding up the bytes in the header as type char. I think the type char was unsigned on the PDP-11, but it's signed on the Next and Sun. It looks like the sources to BSD tar were never changed to compute the checksum correctly, so both the Sun and Next add the bytes of the header as signed chars. This doesn't cause a problem until you get a file with a name containing characters with the high bit set. So tar_checksum computes two checksums -- signed and unsigned. */enum read_headertar_checksum (union block *header, bool silent){ size_t i; int unsigned_sum = 0; /* the POSIX one :-) */ int signed_sum = 0; /* the Sun one :-( */ int recorded_sum; uintmax_t parsed_sum; char *p; p = header->buffer; for (i = sizeof *header; i-- != 0;) { unsigned_sum += (unsigned char) *p; signed_sum += (signed char) (*p++); } if (unsigned_sum == 0) return HEADER_ZERO_BLOCK; /* Adjust checksum to count the "chksum" field as blanks. */ for (i = sizeof header->header.chksum; i-- != 0;) { unsigned_sum -= (unsigned char) header->header.chksum[i]; signed_sum -= (signed char) (header->header.chksum[i]); } unsigned_sum += ' ' * sizeof header->header.chksum; signed_sum += ' ' * sizeof header->header.chksum; parsed_sum = from_header (header->header.chksum, sizeof header->header.chksum, 0, (uintmax_t) 0, (uintmax_t) TYPE_MAXIMUM (int), true, silent); if (parsed_sum == (uintmax_t) -1) return HEADER_FAILURE; recorded_sum = parsed_sum; if (unsigned_sum != recorded_sum && signed_sum != recorded_sum) return HEADER_FAILURE; return HEADER_SUCCESS;}/* Read a block that's supposed to be a header block. Return its address in "current_header", and if it is good, the file's size and names (file name, link name) in *info. Return 1 for success, 0 if the checksum is bad, EOF on eof, 2 for a block full of zeros (EOF marker). If RAW_EXTENDED_HEADERS is nonzero, do not automagically fold the GNU long name and link headers into later headers. You must always set_next_block_after(current_header) to skip past the header which this routine reads. */enum read_headerread_header_primitive (bool raw_extended_headers, struct tar_stat_info *info){ union block *header; union block *header_copy; char *bp; union block *data_block; size_t size, written; union block *next_long_name = 0; union block *next_long_link = 0; size_t next_long_name_blocks = 0; size_t next_long_link_blocks = 0; while (1) { enum read_header status; header = find_next_block (); current_header = header; if (!header) return HEADER_END_OF_FILE; if ((status = tar_checksum (header, false)) != HEADER_SUCCESS) return status; /* Good block. Decode file size and return. */ if (header->header.typeflag == LNKTYPE) info->stat.st_size = 0; /* links 0 size on tape */ else info->stat.st_size = OFF_FROM_HEADER (header->header.size); if (header->header.typeflag == GNUTYPE_LONGNAME || header->header.typeflag == GNUTYPE_LONGLINK || header->header.typeflag == XHDTYPE || header->header.typeflag == XGLTYPE || header->header.typeflag == SOLARIS_XHDTYPE) { if (raw_extended_headers) return HEADER_SUCCESS_EXTENDED; else if (header->header.typeflag == GNUTYPE_LONGNAME || header->header.typeflag == GNUTYPE_LONGLINK) { size_t name_size = info->stat.st_size; size_t n = name_size % BLOCKSIZE; size = name_size + BLOCKSIZE; if (n) size += BLOCKSIZE - n; if (name_size != info->stat.st_size || size < name_size) xalloc_die (); header_copy = xmalloc (size + 1); if (header->header.typeflag == GNUTYPE_LONGNAME) { if (next_long_name) free (next_long_name); next_long_name = header_copy; next_long_name_blocks = size / BLOCKSIZE; } else { if (next_long_link) free (next_long_link); next_long_link = header_copy; next_long_link_blocks = size / BLOCKSIZE; } set_next_block_after (header); *header_copy = *header; bp = header_copy->buffer + BLOCKSIZE; for (size -= BLOCKSIZE; size > 0; size -= written) { data_block = find_next_block (); if (! data_block) { ERROR ((0, 0, _("Unexpected EOF in archive"))); break; } written = available_space_after (data_block); if (written > size) written = size; memcpy (bp, data_block->buffer, written); bp += written; set_next_block_after ((union block *) (data_block->buffer + written - 1)); } *bp = '\0'; } else if (header->header.typeflag == XHDTYPE || header->header.typeflag == SOLARIS_XHDTYPE) xheader_read (&info->xhdr, header, OFF_FROM_HEADER (header->header.size)); else if (header->header.typeflag == XGLTYPE) { struct xheader xhdr; memset (&xhdr, 0, sizeof xhdr); xheader_read (&xhdr, header, OFF_FROM_HEADER (header->header.size)); xheader_decode_global (&xhdr); xheader_destroy (&xhdr); } /* Loop! */ } else { char const *name; struct posix_header const *h = ¤t_header->header; char namebuf[sizeof h->prefix + 1 + NAME_FIELD_SIZE + 1]; if (recent_long_name) free (recent_long_name); if (next_long_name) { name = next_long_name->buffer + BLOCKSIZE; recent_long_name = next_long_name; recent_long_name_blocks = next_long_name_blocks; } else { /* Accept file names as specified by POSIX.1-1996 section 10.1.1. */ char *np = namebuf; if (h->prefix[0] && strcmp (h->magic, TMAGIC) == 0) { memcpy (np, h->prefix, sizeof h->prefix); np[sizeof h->prefix] = '\0'; np += strlen (np); *np++ = '/'; } memcpy (np, h->name, sizeof h->name); np[sizeof h->name] = '\0'; name = namebuf; recent_long_name = 0; recent_long_name_blocks = 0; } assign_string (&info->orig_file_name, name); assign_string (&info->file_name, name); info->had_trailing_slash = strip_trailing_slashes (info->file_name); if (recent_long_link) free (recent_long_link); if (next_long_link) { name = next_long_link->buffer + BLOCKSIZE; recent_long_link = next_long_link;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -