📄 xheader.c
字号:
/* POSIX extended headers for tar. 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 <fnmatch.h>#include <hash.h>#include <inttostr.h>#include <quotearg.h>#include "common.h"#include <fnmatch.h>static bool xheader_protected_pattern_p (char const *pattern);static bool xheader_protected_keyword_p (char const *keyword);static void xheader_set_single_keyword (char *) __attribute__ ((noreturn));/* Used by xheader_finish() */static void code_string (char const *string, char const *keyword, struct xheader *xhdr);/* Number of global headers written so far. */static size_t global_header_count;/* FIXME: Possibly it should be reset after changing the volume. POSIX %n specification says that it is expanded to the sequence number of current global header in *the* archive. However, for multi-volume archives this will yield duplicate header names in different volumes, which I'd like to avoid. The best way to solve this would be to use per-archive header count as required by POSIX *and* set globexthdr.name to, say, $TMPDIR/GlobalHead.%p.$NUMVOLUME.%n. However it should wait until buffer.c is finally rewritten *//* Interface functions to obstacks */static voidx_obstack_grow (struct xheader *xhdr, const char *ptr, size_t length){ obstack_grow (xhdr->stk, ptr, length); xhdr->size += length;}static voidx_obstack_1grow (struct xheader *xhdr, char c){ obstack_1grow (xhdr->stk, c); xhdr->size++;}static voidx_obstack_blank (struct xheader *xhdr, size_t length){ obstack_blank (xhdr->stk, length); xhdr->size += length;}/* Keyword options */struct keyword_list{ struct keyword_list *next; char *pattern; char *value;};/* List of keyword patterns set by delete= option */static struct keyword_list *keyword_pattern_list;/* List of keyword/value pairs set by `keyword=value' option */static struct keyword_list *keyword_global_override_list;/* List of keyword/value pairs set by `keyword:=value' option */static struct keyword_list *keyword_override_list;/* List of keyword/value pairs decoded from the last 'g' type header */static struct keyword_list *global_header_override_list;/* Template for the name field of an 'x' type header */static char *exthdr_name;/* Template for the name field of a 'g' type header */static char *globexthdr_name;boolxheader_keyword_deleted_p (const char *kw){ struct keyword_list *kp; for (kp = keyword_pattern_list; kp; kp = kp->next) if (fnmatch (kp->pattern, kw, 0) == 0) return true; return false;}static boolxheader_keyword_override_p (const char *keyword){ struct keyword_list *kp; for (kp = keyword_override_list; kp; kp = kp->next) if (strcmp (kp->pattern, keyword) == 0) return true; return false;}static voidxheader_list_append (struct keyword_list **root, char const *kw, char const *value){ struct keyword_list *kp = xmalloc (sizeof *kp); kp->pattern = xstrdup (kw); kp->value = value ? xstrdup (value) : NULL; kp->next = *root; *root = kp;}static voidxheader_list_destroy (struct keyword_list **root){ if (root) { struct keyword_list *kw = *root; while (kw) { struct keyword_list *next = kw->next; free (kw->pattern); free (kw->value); free (kw); kw = next; } *root = NULL; }}static voidxheader_set_single_keyword (char *kw){ USAGE_ERROR ((0, 0, _("Keyword %s is unknown or not yet implemented"), kw));}static voidxheader_set_keyword_equal (char *kw, char *eq){ bool global = true; char *p = eq; if (eq[-1] == ':') { p--; global = false; } while (p > kw && isspace (*p)) p--; *p = 0; for (p = eq + 1; *p && isspace (*p); p++) ; if (strcmp (kw, "delete") == 0) { if (xheader_protected_pattern_p (p)) USAGE_ERROR ((0, 0, _("Pattern %s cannot be used"), quote (p))); xheader_list_append (&keyword_pattern_list, p, NULL); } else if (strcmp (kw, "exthdr.name") == 0) assign_string (&exthdr_name, p); else if (strcmp (kw, "globexthdr.name") == 0) assign_string (&globexthdr_name, p); else { if (xheader_protected_keyword_p (kw)) USAGE_ERROR ((0, 0, _("Keyword %s cannot be overridden"), kw)); if (global) xheader_list_append (&keyword_global_override_list, kw, p); else xheader_list_append (&keyword_override_list, kw, p); }}voidxheader_set_option (char *string){ char *token; for (token = strtok (string, ","); token; token = strtok (NULL, ",")) { char *p = strchr (token, '='); if (!p) xheader_set_single_keyword (token); else xheader_set_keyword_equal (token, p); }}/* string Includes: Replaced By: %d The directory name of the file, equivalent to the result of the dirname utility on the translated file name. %f The filename of the file, equivalent to the result of the basename utility on the translated file name. %p The process ID of the pax process. %n The value of the 3rd argument. %% A '%' character. */char *xheader_format_name (struct tar_stat_info *st, const char *fmt, size_t n){ char *buf; size_t len = strlen (fmt); char *q; const char *p; char *dirp = NULL; char *dir = NULL; char *base = NULL; char pidbuf[UINTMAX_STRSIZE_BOUND]; char const *pptr; char nbuf[UINTMAX_STRSIZE_BOUND]; char const *nptr = NULL; for (p = fmt; *p && (p = strchr (p, '%')); ) { switch (p[1]) { case '%': len--; break; case 'd': if (st) { if (!dirp) dirp = dir_name (st->orig_file_name); dir = safer_name_suffix (dirp, false, absolute_names_option); len += strlen (dir) - 2; } break; case 'f': if (st) { base = last_component (st->orig_file_name); len += strlen (base) - 2; } break; case 'p': pptr = umaxtostr (getpid (), pidbuf); len += pidbuf + sizeof pidbuf - 1 - pptr - 2; break; case 'n': nptr = umaxtostr (n, nbuf); len += nbuf + sizeof nbuf - 1 - nptr - 2; break; } p++; } buf = xmalloc (len + 1); for (q = buf, p = fmt; *p; ) { if (*p == '%') { switch (p[1]) { case '%': *q++ = *p++; p++; break; case 'd': if (dir) q = stpcpy (q, dir); p += 2; break; case 'f': if (base) q = stpcpy (q, base); p += 2; break; case 'p': q = stpcpy (q, pptr); p += 2; break; case 'n': if (nptr) { q = stpcpy (q, nptr); p += 2; break; } /* else fall through */ default: *q++ = *p++; if (*p) *q++ = *p++; } } else *q++ = *p++; } free (dirp); /* Do not allow it to end in a slash */ while (q > buf && ISSLASH (q[-1])) q--; *q = 0; return buf;}char *xheader_xhdr_name (struct tar_stat_info *st){ if (!exthdr_name) assign_string (&exthdr_name, "%d/PaxHeaders.%p/%f"); return xheader_format_name (st, exthdr_name, 0);}#define GLOBAL_HEADER_TEMPLATE "/GlobalHead.%p.%n"char *xheader_ghdr_name (void){ if (!globexthdr_name) { size_t len; const char *tmp = getenv ("TMPDIR"); if (!tmp) tmp = "/tmp"; len = strlen (tmp) + sizeof (GLOBAL_HEADER_TEMPLATE); /* Includes nul */ globexthdr_name = xmalloc (len); strcpy(globexthdr_name, tmp); strcat(globexthdr_name, GLOBAL_HEADER_TEMPLATE); } return xheader_format_name (NULL, globexthdr_name, global_header_count + 1);}voidxheader_write (char type, char *name, struct xheader *xhdr){ union block *header; size_t size; char *p; size = xhdr->size; header = start_private_header (name, size); header->header.typeflag = type; simple_finish_header (header); p = xhdr->buffer; do { size_t len; header = find_next_block (); len = BLOCKSIZE; if (len > size) len = size; memcpy (header->buffer, p, len); if (len < BLOCKSIZE) memset (header->buffer + len, 0, BLOCKSIZE - len); p += len; size -= len; set_next_block_after (header); } while (size > 0); xheader_destroy (xhdr); if (type == XGLTYPE) global_header_count++;}voidxheader_write_global (struct xheader *xhdr){ char *name; struct keyword_list *kp; if (!keyword_global_override_list) return; xheader_init (xhdr); for (kp = keyword_global_override_list; kp; kp = kp->next) code_string (kp->value, kp->pattern, xhdr); xheader_finish (xhdr); xheader_write (XGLTYPE, name = xheader_ghdr_name (), xhdr); free (name);}/* General Interface */struct xhdr_tab{ char const *keyword; void (*coder) (struct tar_stat_info const *, char const *, struct xheader *, void const *data); void (*decoder) (struct tar_stat_info *, char const *, char const *, size_t); bool protect;};/* This declaration must be extern, because ISO C99 section 6.9.2 prohibits a tentative definition that has both internal linkage and incomplete type. If we made it static, we'd have to declare its size which would be a maintenance pain; if we put its initializer here, we'd need a boatload of forward declarations, which would be even more of a pain. */extern struct xhdr_tab const xhdr_tab[];static struct xhdr_tab const *locate_handler (char const *keyword){ struct xhdr_tab const *p; for (p = xhdr_tab; p->keyword; p++) if (strcmp (p->keyword, keyword) == 0) return p; return NULL;}static boolxheader_protected_pattern_p (const char *pattern){ struct xhdr_tab const *p; for (p = xhdr_tab; p->keyword; p++) if (p->protect && fnmatch (pattern, p->keyword, 0) == 0) return true; return false;}static boolxheader_protected_keyword_p (const char *keyword){ struct xhdr_tab const *p; for (p = xhdr_tab; p->keyword; p++) if (p->protect && strcmp (p->keyword, keyword) == 0) return true; return false;}/* Decode a single extended header record, advancing *PTR to the next record. Return true on success, false otherwise. */static booldecode_record (struct xheader *xhdr, char **ptr, void (*handler) (void *, char const *, char const *, size_t), void *data){ char *start = *ptr; char *p = start; uintmax_t u; size_t len; char *len_lim; char const *keyword; char *nextp; size_t len_max = xhdr->buffer + xhdr->size - start; while (*p == ' ' || *p == '\t') p++; if (! ISDIGIT (*p)) { if (*p) ERROR ((0, 0, _("Malformed extended header: missing length"))); return false; } errno = 0; len = u = strtoumax (p, &len_lim, 10); if (len != u || errno == ERANGE) { ERROR ((0, 0, _("Extended header length is out of allowed range")));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -