📄 create.c
字号:
/* Create a tar archive. Copyright (C) 1985, 1992, 1993, 1994, 1996, 1997, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. Written by John Gilmore, on 1985-08-25. 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 <quotearg.h>#include "common.h"#include <hash.h>struct link { dev_t dev; ino_t ino; size_t nlink; char name[1]; };struct exclusion_tag{ const char *name; size_t length; enum exclusion_tag_type type; bool (*predicate) (const char *name); struct exclusion_tag *next;};static struct exclusion_tag *exclusion_tags;voidadd_exclusion_tag (const char *name, enum exclusion_tag_type type, bool (*predicate) (const char *name)){ struct exclusion_tag *tag = xmalloc (sizeof tag[0]); tag->next = exclusion_tags; tag->name = name; tag->type = type; tag->predicate = predicate; tag->length = strlen (name); exclusion_tags = tag;}voidexclusion_tag_warning (const char *dirname, const char *tagname, const char *message){ if (verbose_option) WARN ((0, 0, _("%s: contains a cache directory tag %s; %s"), quotearg_colon (dirname), quotearg_n (1, tagname), message));}enum exclusion_tag_type check_exclusion_tags (char *dirname, const char **tag_file_name){ static char *tagname; static size_t tagsize; struct exclusion_tag *tag; size_t dlen = strlen (dirname); int addslash = dirname[dlen-1] != '/'; char *nptr = NULL; for (tag = exclusion_tags; tag; tag = tag->next) { size_t size = dlen + addslash + tag->length + 1; if (size > tagsize) { tagsize = size; tagname = xrealloc (tagname, tagsize); } if (!nptr) { strcpy (tagname, dirname); nptr = tagname + dlen; if (addslash) *nptr++ = '/'; } strcpy (nptr, tag->name); if (access (tagname, F_OK) == 0 && (!tag->predicate || tag->predicate (tagname))) { if (tag_file_name) *tag_file_name = tag->name; return tag->type; } } return exclusion_tag_none;}/* Exclusion predicate to test if the named file (usually "CACHEDIR.TAG") contains a valid header, as described at: http://www.brynosaurus.com/cachedir Applications can write this file into directories they create for use as caches containing purely regenerable, non-precious data, allowing us to avoid archiving them if --exclude-caches is specified. */#define CACHEDIR_SIGNATURE "Signature: 8a477f597d28d172789f06886806bc55"#define CACHEDIR_SIGNATURE_SIZE (sizeof CACHEDIR_SIGNATURE - 1)boolcachedir_file_p (const char *name){ bool tag_present = false; int fd = open (name, O_RDONLY); if (fd >= 0) { static char tagbuf[CACHEDIR_SIGNATURE_SIZE]; if (read (fd, tagbuf, CACHEDIR_SIGNATURE_SIZE) == CACHEDIR_SIGNATURE_SIZE && memcmp (tagbuf, CACHEDIR_SIGNATURE, CACHEDIR_SIGNATURE_SIZE) == 0) tag_present = true; close (fd); } return tag_present;}/* The maximum uintmax_t value that can be represented with DIGITS digits, assuming that each digit is BITS_PER_DIGIT wide. */#define MAX_VAL_WITH_DIGITS(digits, bits_per_digit) \ ((digits) * (bits_per_digit) < sizeof (uintmax_t) * CHAR_BIT \ ? ((uintmax_t) 1 << ((digits) * (bits_per_digit))) - 1 \ : (uintmax_t) -1)/* The maximum uintmax_t value that can be represented with octal digits and a trailing NUL in BUFFER. */#define MAX_OCTAL_VAL(buffer) MAX_VAL_WITH_DIGITS (sizeof (buffer) - 1, LG_8)/* Convert VALUE to an octal representation suitable for tar headers. Output to buffer WHERE with size SIZE. The result is undefined if SIZE is 0 or if VALUE is too large to fit. */static voidto_octal (uintmax_t value, char *where, size_t size){ uintmax_t v = value; size_t i = size; do { where[--i] = '0' + (v & ((1 << LG_8) - 1)); v >>= LG_8; } while (i);}/* Copy at most LEN bytes from the string SRC to DST. Terminate with NUL unless SRC is LEN or more bytes long. */static voidtar_copy_str (char *dst, const char *src, size_t len){ size_t i; for (i = 0; i < len; i++) if (! (dst[i] = src[i])) break;}/* Same as tar_copy_str, but always terminate with NUL if using is OLDGNU format */static voidtar_name_copy_str (char *dst, const char *src, size_t len){ tar_copy_str (dst, src, len); if (archive_format == OLDGNU_FORMAT) dst[len-1] = 0;}/* Convert NEGATIVE VALUE to a base-256 representation suitable for tar headers. NEGATIVE is 1 if VALUE was negative before being cast to uintmax_t, 0 otherwise. Output to buffer WHERE with size SIZE. The result is undefined if SIZE is 0 or if VALUE is too large to fit. */static voidto_base256 (int negative, uintmax_t value, char *where, size_t size){ uintmax_t v = value; uintmax_t propagated_sign_bits = ((uintmax_t) - negative << (CHAR_BIT * sizeof v - LG_256)); size_t i = size; do { where[--i] = v & ((1 << LG_256) - 1); v = propagated_sign_bits | (v >> LG_256); } while (i);}static boolto_chars (int negative, uintmax_t value, size_t valsize, uintmax_t (*substitute) (int *), char *where, size_t size, const char *type);static boolto_chars_subst (int negative, int gnu_format, uintmax_t value, size_t valsize, uintmax_t (*substitute) (int *), char *where, size_t size, const char *type){ uintmax_t maxval = (gnu_format ? MAX_VAL_WITH_DIGITS (size - 1, LG_256) : MAX_VAL_WITH_DIGITS (size - 1, LG_8)); char valbuf[UINTMAX_STRSIZE_BOUND + 1]; char maxbuf[UINTMAX_STRSIZE_BOUND]; char minbuf[UINTMAX_STRSIZE_BOUND + 1]; char const *minval_string; char const *maxval_string = STRINGIFY_BIGINT (maxval, maxbuf); char const *value_string; if (gnu_format) { uintmax_t m = maxval + 1 ? maxval + 1 : maxval / 2 + 1; char *p = STRINGIFY_BIGINT (m, minbuf + 1); *--p = '-'; minval_string = p; } else minval_string = "0"; if (negative) { char *p = STRINGIFY_BIGINT (- value, valbuf + 1); *--p = '-'; value_string = p; } else value_string = STRINGIFY_BIGINT (value, valbuf); if (substitute) { int negsub; uintmax_t sub = substitute (&negsub) & maxval; /* NOTE: This is one of the few places where GNU_FORMAT differs from OLDGNU_FORMAT. The actual differences are: 1. In OLDGNU_FORMAT all strings in a tar header end in \0 2. Incremental archives use oldgnu_header. Apart from this they are completely identical. */ uintmax_t s = (negsub &= archive_format == GNU_FORMAT) ? - sub : sub; char subbuf[UINTMAX_STRSIZE_BOUND + 1]; char *sub_string = STRINGIFY_BIGINT (s, subbuf + 1); if (negsub) *--sub_string = '-'; WARN ((0, 0, _("value %s out of %s range %s..%s; substituting %s"), value_string, type, minval_string, maxval_string, sub_string)); return to_chars (negsub, s, valsize, 0, where, size, type); } else ERROR ((0, 0, _("value %s out of %s range %s..%s"), value_string, type, minval_string, maxval_string)); return false;}/* Convert NEGATIVE VALUE (which was originally of size VALSIZE) to external form, using SUBSTITUTE (...) if VALUE won't fit. Output to buffer WHERE with size SIZE. NEGATIVE is 1 iff VALUE was negative before being cast to uintmax_t; its original bitpattern can be deduced from VALSIZE, its original size before casting. TYPE is the kind of value being output (useful for diagnostics). Prefer the POSIX format of SIZE - 1 octal digits (with leading zero digits), followed by '\0'. If this won't work, and if GNU or OLDGNU format is allowed, use '\200' followed by base-256, or (if NEGATIVE is nonzero) '\377' followed by two's complement base-256. If neither format works, use SUBSTITUTE (...) instead. Pass to SUBSTITUTE the address of an 0-or-1 flag recording whether the substitute value is negative. */static boolto_chars (int negative, uintmax_t value, size_t valsize, uintmax_t (*substitute) (int *), char *where, size_t size, const char *type){ int gnu_format = (archive_format == GNU_FORMAT || archive_format == OLDGNU_FORMAT); /* Generate the POSIX octal representation if the number fits. */ if (! negative && value <= MAX_VAL_WITH_DIGITS (size - 1, LG_8)) { where[size - 1] = '\0'; to_octal (value, where, size - 1); return true; } else if (gnu_format) { /* Try to cope with the number by using traditional GNU format methods */ /* Generate the base-256 representation if the number fits. */ if (((negative ? -1 - value : value) <= MAX_VAL_WITH_DIGITS (size - 1, LG_256))) { where[0] = negative ? -1 : 1 << (LG_256 - 1); to_base256 (negative, value, where + 1, size - 1); return true; } /* Otherwise, if the number is negative, and if it would not cause ambiguity on this host by confusing positive with negative values, then generate the POSIX octal representation of the value modulo 2**(field bits). The resulting tar file is machine-dependent, since it depends on the host word size. Yuck! But this is the traditional behavior. */ else if (negative && valsize * CHAR_BIT <= (size - 1) * LG_8) { static int warned_once; if (! warned_once) { warned_once = 1; WARN ((0, 0, _("Generating negative octal headers"))); } where[size - 1] = '\0'; to_octal (value & MAX_VAL_WITH_DIGITS (valsize * CHAR_BIT, 1), where, size - 1); return true; } /* Otherwise fall back to substitution, if possible: */ } else substitute = NULL; /* No substitution for formats, other than GNU */ return to_chars_subst (negative, gnu_format, value, valsize, substitute, where, size, type);}static uintmax_tgid_substitute (int *negative){ gid_t r;#ifdef GID_NOBODY r = GID_NOBODY;#else static gid_t gid_nobody; if (!gid_nobody && !gname_to_gid ("nobody", &gid_nobody)) gid_nobody = -2; r = gid_nobody;#endif *negative = r < 0; return r;}boolgid_to_chars (gid_t v, char *p, size_t s){ return to_chars (v < 0, (uintmax_t) v, sizeof v, gid_substitute, p, s, "gid_t");}boolmajor_to_chars (major_t v, char *p, size_t s){ return to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "major_t");}boolminor_to_chars (minor_t v, char *p, size_t s){ return to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "minor_t");}boolmode_to_chars (mode_t v, char *p, size_t s){ /* In the common case where the internal and external mode bits are the same, and we are not using POSIX or GNU format, propagate all unknown bits to the external mode. This matches historical practice. Otherwise, just copy the bits we know about. */ int negative; uintmax_t u; if (S_ISUID == TSUID && S_ISGID == TSGID && S_ISVTX == TSVTX && S_IRUSR == TUREAD && S_IWUSR == TUWRITE && S_IXUSR == TUEXEC && S_IRGRP == TGREAD && S_IWGRP == TGWRITE && S_IXGRP == TGEXEC && S_IROTH == TOREAD && S_IWOTH == TOWRITE && S_IXOTH == TOEXEC && archive_format != POSIX_FORMAT && archive_format != USTAR_FORMAT && archive_format != GNU_FORMAT && archive_format != OLDGNU_FORMAT) { negative = v < 0; u = v; } else { negative = 0; u = ((v & S_ISUID ? TSUID : 0) | (v & S_ISGID ? TSGID : 0) | (v & S_ISVTX ? TSVTX : 0) | (v & S_IRUSR ? TUREAD : 0) | (v & S_IWUSR ? TUWRITE : 0) | (v & S_IXUSR ? TUEXEC : 0) | (v & S_IRGRP ? TGREAD : 0) | (v & S_IWGRP ? TGWRITE : 0) | (v & S_IXGRP ? TGEXEC : 0) | (v & S_IROTH ? TOREAD : 0) | (v & S_IWOTH ? TOWRITE : 0) | (v & S_IXOTH ? TOEXEC : 0)); } return to_chars (negative, u, sizeof v, 0, p, s, "mode_t");}booloff_to_chars (off_t v, char *p, size_t s){ return to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "off_t");}boolsize_to_chars (size_t v, char *p, size_t s){ return to_chars (0, (uintmax_t) v, sizeof v, 0, p, s, "size_t");}booltime_to_chars (time_t v, char *p, size_t s){ return to_chars (v < 0, (uintmax_t) v, sizeof v, 0, p, s, "time_t");}static uintmax_tuid_substitute (int *negative)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -