X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Ftar;a=blobdiff_plain;f=src%2Fxheader.c;h=c94c6d39f1e2437a3caa8ea577d436cd77275143;hp=2cfa683336d0bb63d913ebd040b53cf37f308901;hb=45ccda119355a1087450039a250359c1d0de0d08;hpb=2bda83b48d8a6807632312403561b11b79048443 diff --git a/src/xheader.c b/src/xheader.c index 2cfa683..c94c6d3 100644 --- a/src/xheader.c +++ b/src/xheader.c @@ -1,36 +1,33 @@ /* POSIX extended headers for tar. - Copyright (C) 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 2003-2007, 2009-2010, 2012-2014 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 2, or (at your option) any later - version. + This file is part of GNU tar. - 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. + GNU tar 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 of the License, or + (at your option) any later version. - 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., - 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + GNU tar 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. -#include "system.h" + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include #include #include +#include #include -#include #include "common.h" -#define obstack_chunk_alloc xmalloc -#define obstack_chunk_free free -#include - -#include - +static void xheader_init (struct xheader *xhdr); 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)); @@ -38,7 +35,6 @@ 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); -static void extended_header_init (void); /* Number of global headers written so far. */ static size_t global_header_count; @@ -53,6 +49,30 @@ static size_t global_header_count; However it should wait until buffer.c is finally rewritten */ + +/* Interface functions to obstacks */ + +static void +x_obstack_grow (struct xheader *xhdr, const char *ptr, size_t length) +{ + obstack_grow (xhdr->stk, ptr, length); + xhdr->size += length; +} + +static void +x_obstack_1grow (struct xheader *xhdr, char c) +{ + obstack_1grow (xhdr->stk, c); + xhdr->size++; +} + +static void +x_obstack_blank (struct xheader *xhdr, size_t length) +{ + obstack_blank (xhdr->stk, length); + xhdr->size += length; +} + /* Keyword options */ @@ -67,10 +87,10 @@ struct keyword_list /* 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 */ +/* 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 */ +/* 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 */ @@ -79,10 +99,16 @@ static struct keyword_list *global_header_override_list; /* Template for the name field of an 'x' type header */ static char *exthdr_name; +static char *exthdr_mtime_option; +static time_t exthdr_mtime; + /* Template for the name field of a 'g' type header */ static char *globexthdr_name; -static bool +static char *globexthdr_mtime_option; +static time_t globexthdr_mtime; + +bool xheader_keyword_deleted_p (const char *kw) { struct keyword_list *kp; @@ -136,7 +162,21 @@ xheader_list_destroy (struct keyword_list **root) static void xheader_set_single_keyword (char *kw) { - USAGE_ERROR ((0, 0, _("Keyword %s is unknown or not yet imlemented"), kw)); + USAGE_ERROR ((0, 0, _("Keyword %s is unknown or not yet implemented"), kw)); +} + +static void +assign_time_option (char **sval, time_t *tval, const char *input) +{ + char *p; + struct timespec t = decode_timespec (input, &p, false); + if (! valid_timespec (t) || *p) + ERROR ((0, 0, _("Time stamp is out of allowed range"))); + else + { + *tval = t.tv_sec; + assign_string (sval, input); + } } static void @@ -151,24 +191,28 @@ xheader_set_keyword_equal (char *kw, char *eq) global = false; } - while (p > kw && isspace (*p)) + while (p > kw && isspace ((unsigned char) *p)) p--; *p = 0; - for (p = eq + 1; *p && isspace (*p); p++) + for (p = eq + 1; *p && isspace ((unsigned char) *p); p++) ; if (strcmp (kw, "delete") == 0) { if (xheader_protected_pattern_p (p)) - USAGE_ERROR ((0, 0, _("Pattern %s cannot be used"), 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 (strcmp (kw, "exthdr.mtime") == 0) + assign_time_option (&exthdr_mtime_option, &exthdr_mtime, p); + else if (strcmp (kw, "globexthdr.mtime") == 0) + assign_time_option (&globexthdr_mtime_option, &globexthdr_mtime, p); else { if (xheader_protected_keyword_p (kw)) @@ -194,49 +238,33 @@ xheader_set_option (char *string) } } -static void -to_decimal (uintmax_t value, char *where, size_t size) -{ - size_t i = 0, j; - - where[i++] = 0; - do - { - where[i++] = '0' + value % 10; - value /= 10; - } - while (i < size && value); - for (j = 0, i--; j < i; j++, i--) - { - char c = where[j]; - where[j] = where[i]; - where[i] = c; - } -} - /* string Includes: Replaced By: %d The directory name of the file, equivalent to the result of the dirname utility on the translated - pathname. + file name. %f The filename of the file, equivalent to the result of the basename - utility on the translated pathname. + utility on the translated file name. %p The process ID of the pax process. + %n The value of the 3rd argument. %% A '%' character. */ -static char * -xheader_format_name (struct tar_stat_info *st, const char *fmt, bool allow_n) +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[64]; - char nbuf[64]; + char pidbuf[UINTMAX_STRSIZE_BOUND]; + char const *pptr = NULL; + char nbuf[UINTMAX_STRSIZE_BOUND]; + char const *nptr = NULL; for (p = fmt; *p && (p = strchr (p, '%')); ) { @@ -249,30 +277,29 @@ xheader_format_name (struct tar_stat_info *st, const char *fmt, bool allow_n) case 'd': if (st) { - dir = safer_name_suffix (dir_name (st->orig_file_name), false); - len += strlen (dir) - 1; + 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 = base_name (st->orig_file_name); - len += strlen (base) - 1; + base = last_component (st->orig_file_name); + len += strlen (base) - 2; } break; case 'p': - to_decimal (getpid (), pidbuf, sizeof pidbuf); - len += strlen (pidbuf) - 1; + pptr = umaxtostr (getpid (), pidbuf); + len += pidbuf + sizeof pidbuf - 1 - pptr - 2; break; case 'n': - if (allow_n) - { - to_decimal (global_header_count + 1, pidbuf, sizeof pidbuf); - len += strlen (nbuf) - 1; - } + nptr = umaxtostr (n, nbuf); + len += nbuf + sizeof nbuf - 1 - nptr - 2; break; } p++; @@ -303,17 +330,15 @@ xheader_format_name (struct tar_stat_info *st, const char *fmt, bool allow_n) break; case 'p': - q = stpcpy (q, pidbuf); + q = stpcpy (q, pptr); p += 2; break; case 'n': - if (allow_n) - { - q = stpcpy (q, nbuf); - p += 2; - } - /* else fall through */ + q = stpcpy (q, nptr); + p += 2; + break; + default: *q++ = *p++; @@ -325,6 +350,8 @@ xheader_format_name (struct tar_stat_info *st, const char *fmt, bool allow_n) *q++ = *p++; } + free (dirp); + /* Do not allow it to end in a slash */ while (q > buf && ISSLASH (q[-1])) q--; @@ -337,7 +364,7 @@ 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, false); + return xheader_format_name (st, exthdr_name, 0); } #define GLOBAL_HEADER_TEMPLATE "/GlobalHead.%p.%n" @@ -357,18 +384,30 @@ xheader_ghdr_name (void) strcat(globexthdr_name, GLOBAL_HEADER_TEMPLATE); } - return xheader_format_name (NULL, globexthdr_name, true); + return xheader_format_name (NULL, globexthdr_name, global_header_count + 1); } void -xheader_write (char type, char *name, struct xheader *xhdr) +xheader_write (char type, char *name, time_t t, struct xheader *xhdr) { union block *header; size_t size; char *p; size = xhdr->size; - header = start_private_header (name, size); + switch (type) + { + case XGLTYPE: + if (globexthdr_mtime_option) + t = globexthdr_mtime; + break; + + case XHDTYPE: + if (exthdr_mtime_option) + t = exthdr_mtime; + break; + } + header = start_private_header (name, size, t); header->header.typeflag = type; simple_finish_header (header); @@ -392,37 +431,164 @@ xheader_write (char type, char *name, struct xheader *xhdr) } while (size > 0); xheader_destroy (xhdr); + + if (type == XGLTYPE) + global_header_count++; } void -xheader_write_global (void) +xheader_write_global (struct xheader *xhdr) { - char *name; - struct keyword_list *kp; + if (keyword_global_override_list) + { + 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); + } + if (xhdr->stk) + { + char *name; - extended_header_init (); - for (kp = keyword_global_override_list; kp; kp = kp->next) - code_string (kp->value, kp->pattern, &extended_header); - xheader_finish (&extended_header); - xheader_write (XGLTYPE, name = xheader_ghdr_name (), - &extended_header); - free (name); - global_header_count++; + xheader_finish (xhdr); + name = xheader_ghdr_name (); + xheader_write (XGLTYPE, name, start_time.tv_sec, xhdr); + free (name); + } +} + +void +xheader_xattr_init (struct tar_stat_info *st) +{ + st->xattr_map = NULL; + st->xattr_map_size = 0; + + st->acls_a_ptr = NULL; + st->acls_a_len = 0; + st->acls_d_ptr = NULL; + st->acls_d_len = 0; + st->cntx_name = NULL; +} + +void +xheader_xattr_free (struct xattr_array *xattr_map, size_t xattr_map_size) +{ + size_t scan = 0; + + while (scan < xattr_map_size) + { + free (xattr_map[scan].xkey); + free (xattr_map[scan].xval_ptr); + + ++scan; + } + free (xattr_map); +} + +static void +xheader_xattr__add (struct xattr_array **xattr_map, + size_t *xattr_map_size, + const char *key, const char *val, size_t len) +{ + size_t pos = (*xattr_map_size)++; + + *xattr_map = xrealloc (*xattr_map, + *xattr_map_size * sizeof(struct xattr_array)); + (*xattr_map)[pos].xkey = xstrdup (key); + (*xattr_map)[pos].xval_ptr = xmemdup (val, len + 1); + (*xattr_map)[pos].xval_len = len; +} + +/* This is reversal function for xattr_encode_keyword. See comment for + xattr_encode_keyword() for more info. */ +static void +xattr_decode_keyword (char *keyword) +{ + char *kpr, *kpl; /* keyword pointer left/right */ + kpr = kpl = keyword; + + for (;;) + { + if (*kpr == '%') + { + if (kpr[1] == '3' && kpr[2] == 'D') + { + *kpl = '='; + kpr += 3; + kpl ++; + continue; + } + else if (kpr[1] == '2' && kpr[2] == '5') + { + *kpl = '%'; + kpr += 3; + kpl ++; + continue; + } + } + + *kpl = *kpr; + + if (*kpr == 0) + break; + + kpr++; + kpl++; + } +} + +void +xheader_xattr_add (struct tar_stat_info *st, + const char *key, const char *val, size_t len) +{ + size_t klen = strlen (key); + char *xkey = xmalloc (strlen("SCHILY.xattr.") + klen + 1); + char *tmp = xkey; + + tmp = stpcpy (tmp, "SCHILY.xattr."); + stpcpy (tmp, key); + + xheader_xattr__add (&st->xattr_map, &st->xattr_map_size, xkey, val, len); + + free (xkey); +} + +void +xheader_xattr_copy (const struct tar_stat_info *st, + struct xattr_array **xattr_map, size_t *xattr_map_size) +{ + size_t scan = 0; + + *xattr_map = NULL; + *xattr_map_size = 0; + + while (scan < st->xattr_map_size) + { + char *key = st->xattr_map[scan].xkey; + char *val = st->xattr_map[scan].xval_ptr; + size_t len = st->xattr_map[scan].xval_len; + + xheader_xattr__add(xattr_map, xattr_map_size, key, val, len); + + ++scan; + } } /* General Interface */ +#define XHDR_PROTECTED 0x01 +#define XHDR_GLOBAL 0x02 + struct xhdr_tab { char const *keyword; void (*coder) (struct tar_stat_info const *, char const *, - struct xheader *, void *data); - void (*decoder) (struct tar_stat_info *, char const *); - bool protect; + struct xheader *, void const *data); + void (*decoder) (struct tar_stat_info *, char const *, char const *, size_t); + int flags; + bool prefix; /* select handler comparing prefix only */ }; /* This declaration must be extern, because ISO C99 section 6.9.2 @@ -439,8 +605,17 @@ 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; + if (p->prefix) + { + if (strncmp (p->keyword, keyword, strlen(p->keyword)) == 0) + return p; + } + else + { + if (strcmp (p->keyword, keyword) == 0) + return p; + } + return NULL; } @@ -450,7 +625,8 @@ xheader_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) + if (!p->prefix && (p->flags & XHDR_PROTECTED) + && fnmatch (pattern, p->keyword, 0) == 0) return true; return false; } @@ -461,56 +637,78 @@ xheader_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) + if (!p->prefix && (p->flags & XHDR_PROTECTED) + && strcmp (p->keyword, keyword) == 0) return true; return false; } -/* Decodes a single extended header record. Advances P to the next - record. - Returns true on success, false otherwise. */ +/* Decode a single extended header record, advancing *PTR to the next record. + Return true on success, false otherwise. */ static bool -decode_record (char **p, - void (*handler) (void *, char const *, char const *), +decode_record (struct xheader *xhdr, + char **ptr, + void (*handler) (void *, char const *, char const *, size_t), void *data) { + char *start = *ptr; + char *p = start; size_t len; + char *len_lim; char const *keyword; - char *start = *p; - char endc; + char *nextp; + size_t len_max = xhdr->buffer + xhdr->size - start; - if (**p == 0) - return false; + while (*p == ' ' || *p == '\t') + p++; - len = strtoul (*p, p, 10); - if (**p != ' ') + if (! ISDIGIT (*p)) { - ERROR ((0, 0, - _("Malformed extended header: missing whitespace after the length"))); + if (*p) + ERROR ((0, 0, _("Malformed extended header: missing length"))); return false; } - keyword = ++*p; - for (;*p < start + len; ++*p) - if (**p == '=') - break; + len = strtoumax (p, &len_lim, 10); - if (**p != '=') + if (len_max < len) { - ERROR ((0, 0, _("Malformed extended header: missing equal sign"))); + int len_len = len_lim - p; + ERROR ((0, 0, _("Extended header length %*s is out of range"), + len_len, p)); return false; } - **p = 0; + nextp = start + len; - endc = start[len-1]; - start[len-1] = 0; + for (p = len_lim; *p == ' ' || *p == '\t'; p++) + continue; + if (p == len_lim) + { + ERROR ((0, 0, + _("Malformed extended header: missing blank after length"))); + return false; + } - handler (data, keyword, *p + 1); + keyword = p; + p = strchr (p, '='); + if (! (p && p < nextp)) + { + ERROR ((0, 0, _("Malformed extended header: missing equal sign"))); + return false; + } + + if (nextp[-1] != '\n') + { + ERROR ((0, 0, _("Malformed extended header: missing newline"))); + return false; + } - start[len-1] = endc; - **p = '='; - *p = &start[len]; + *p = nextp[-1] = '\0'; + handler (data, keyword, p + 1, nextp - p - 2); /* '=' + trailing '\n' */ + *p = '='; + nextp[-1] = '\n'; + *ptr = nextp; return true; } @@ -521,12 +719,12 @@ run_override_list (struct keyword_list *kp, struct tar_stat_info *st) { struct xhdr_tab const *t = locate_handler (kp->pattern); if (t) - t->decoder (st, kp->value); + t->decoder (st, t->keyword, kp->value, strlen (kp->value)); } } static void -decx (void *data, char const *keyword, char const *value) +decx (void *data, char const *keyword, char const *value, size_t size) { struct xhdr_tab const *t; struct tar_stat_info *st = data; @@ -537,7 +735,11 @@ decx (void *data, char const *keyword, char const *value) t = locate_handler (keyword); if (t) - t->decoder (st, value); + t->decoder (st, keyword, value, size); + else + WARNOPT (WARN_UNKNOWN_KEYWORD, + (0, 0, _("Ignoring unknown extended header keyword '%s'"), + keyword)); } void @@ -546,78 +748,83 @@ xheader_decode (struct tar_stat_info *st) run_override_list (keyword_global_override_list, st); run_override_list (global_header_override_list, st); - if (extended_header.size) + if (st->xhdr.size) { - char *p = extended_header.buffer + BLOCKSIZE; - char *endp = &extended_header.buffer[extended_header.size-1]; - - while (p < endp) - if (!decode_record (&p, decx, st)) - break; + char *p = st->xhdr.buffer + BLOCKSIZE; + while (decode_record (&st->xhdr, &p, decx, st)) + continue; } run_override_list (keyword_override_list, st); } static void -decg (void *data, char const *keyword, char const *value) +decg (void *data, char const *keyword, char const *value, + size_t size __attribute__((unused))) { struct keyword_list **kwl = data; - xheader_list_append (kwl, keyword, value); + struct xhdr_tab const *tab = locate_handler (keyword); + if (tab && (tab->flags & XHDR_GLOBAL)) + tab->decoder (data, keyword, value, size); + else + xheader_list_append (kwl, keyword, value); } void -xheader_decode_global (void) +xheader_decode_global (struct xheader *xhdr) { - if (extended_header.size) + if (xhdr->size) { - char *p = extended_header.buffer + BLOCKSIZE; - char *endp = &extended_header.buffer[extended_header.size-1]; + char *p = xhdr->buffer + BLOCKSIZE; xheader_list_destroy (&global_header_override_list); - while (p < endp) - if (!decode_record (&p, decg, &global_header_override_list)) - break; + while (decode_record (xhdr, &p, decg, &global_header_override_list)) + continue; } } static void -extended_header_init (void) +xheader_init (struct xheader *xhdr) { - if (!extended_header.stk) + if (!xhdr->stk) { - extended_header.stk = xmalloc (sizeof *extended_header.stk); - obstack_init (extended_header.stk); + xhdr->stk = xmalloc (sizeof *xhdr->stk); + obstack_init (xhdr->stk); } } void -xheader_store (char const *keyword, struct tar_stat_info const *st, void *data) +xheader_store (char const *keyword, struct tar_stat_info *st, + void const *data) { struct xhdr_tab const *t; - if (extended_header.buffer) + if (st->xhdr.buffer) return; t = locate_handler (keyword); - if (!t) + if (!t || !t->coder) return; if (xheader_keyword_deleted_p (keyword) || xheader_keyword_override_p (keyword)) return; - extended_header_init (); - t->coder (st, keyword, &extended_header, data); + xheader_init (&st->xhdr); + t->coder (st, keyword, &st->xhdr, data); } void -xheader_read (union block *p, size_t size) +xheader_read (struct xheader *xhdr, union block *p, off_t size) { size_t j = 0; - size_t nblocks; - free (extended_header.buffer); + if (size < 0) + size = 0; /* Already diagnosed. */ + + if (SIZE_MAX - BLOCKSIZE <= size) + xalloc_die (); + size += BLOCKSIZE; - extended_header.size = size; - nblocks = (size + BLOCKSIZE - 1) / BLOCKSIZE; - extended_header.buffer = xmalloc (size + 1); + xhdr->size = size; + xhdr->buffer = xmalloc (size + 1); + xhdr->buffer[size] = '\0'; do { @@ -626,7 +833,10 @@ xheader_read (union block *p, size_t size) if (len > BLOCKSIZE) len = BLOCKSIZE; - memcpy (&extended_header.buffer[j], p->buffer, len); + if (!p) + FATAL_ERROR ((0, 0, _("Unexpected EOF in archive"))); + + memcpy (&xhdr->buffer[j], p->buffer, len); set_next_block_after (p); p = find_next_block (); @@ -637,53 +847,92 @@ xheader_read (union block *p, size_t size) while (size > 0); } -static size_t -format_uintmax (uintmax_t val, char *buf, size_t s) +/* xattr_encode_keyword() substitutes '=' ~~> '%3D' and '%' ~~> '%25' + in extended attribute keywords. This is needed because the '=' character + has special purpose in extended attribute header - it splits keyword and + value part of header. If there was the '=' occurrence allowed inside + keyword, there would be no unambiguous way how to decode this extended + attribute. + + (http://lists.gnu.org/archive/html/bug-tar/2012-10/msg00017.html) + */ +static char * +xattr_encode_keyword(const char *keyword) { - if (!buf) + static char *encode_buffer = NULL; + static size_t encode_buffer_size = 0; + size_t bp; /* keyword/buffer pointers */ + + if (!encode_buffer) { - s = 0; - do - s++; - while ((val /= 10) != 0); + encode_buffer_size = 256; + encode_buffer = xmalloc (encode_buffer_size); } else + *encode_buffer = 0; + + for (bp = 0; *keyword != 0; ++bp, ++keyword) { - char *p = buf + s - 1; + char c = *keyword; + + if (bp + 2 /* enough for URL encoding also.. */ >= encode_buffer_size) + { + encode_buffer = x2realloc (encode_buffer, &encode_buffer_size); + } + + if (c == '%') + { + strcpy (encode_buffer + bp, "%25"); + bp += 2; + } + else if (c == '=') + { + strcpy (encode_buffer + bp, "%3D"); + bp += 2; + } + else + encode_buffer[bp] = c; + } - do - { - *p-- = val % 10 + '0'; - } - while ((val /= 10) != 0); + encode_buffer[bp] = 0; - while (p >= buf) - *p-- = '0'; - } - return s; + return encode_buffer; } static void -xheader_print (struct xheader *xhdr, char const *keyword, char const *value) +xheader_print_n (struct xheader *xhdr, char const *keyword, + char const *value, size_t vsize) { - size_t len = strlen (keyword) + strlen (value) + 3; /* ' ' + '=' + '\n' */ - size_t p, n = 0; - char nbuf[100]; + size_t p; + size_t n = 0; + char nbuf[UINTMAX_STRSIZE_BOUND]; + char const *np; + size_t len, klen; + + keyword = xattr_encode_keyword (keyword); + klen = strlen (keyword); + len = klen + vsize + 3; /* ' ' + '=' + '\n' */ do { p = n; - n = format_uintmax (len + p, NULL, 0); + np = umaxtostr (len + p, nbuf); + n = nbuf + sizeof nbuf - 1 - np; } while (n != p); - format_uintmax (len + n, nbuf, n); - obstack_grow (xhdr->stk, nbuf, n); - obstack_1grow (xhdr->stk, ' '); - obstack_grow (xhdr->stk, keyword, strlen (keyword)); - obstack_1grow (xhdr->stk, '='); - obstack_grow (xhdr->stk, value, strlen (value)); - obstack_1grow (xhdr->stk, '\n'); + x_obstack_grow (xhdr, np, n); + x_obstack_1grow (xhdr, ' '); + x_obstack_grow (xhdr, keyword, klen); + x_obstack_1grow (xhdr, '='); + x_obstack_grow (xhdr, value, vsize); + x_obstack_1grow (xhdr, '\n'); +} + +static void +xheader_print (struct xheader *xhdr, char const *keyword, char const *value) +{ + xheader_print_n (xhdr, keyword, value, strlen (value)); } void @@ -694,9 +943,7 @@ xheader_finish (struct xheader *xhdr) for (kp = keyword_override_list; kp; kp = kp->next) code_string (kp->value, kp->pattern, xhdr); - obstack_1grow (xhdr->stk, 0); xhdr->buffer = obstack_finish (xhdr->stk); - xhdr->size = strlen (xhdr->buffer); } void @@ -714,8 +961,89 @@ xheader_destroy (struct xheader *xhdr) xhdr->size = 0; } + +/* Buildable strings */ + +void +xheader_string_begin (struct xheader *xhdr) +{ + xhdr->string_length = 0; +} + +void +xheader_string_add (struct xheader *xhdr, char const *s) +{ + if (xhdr->buffer) + return; + xheader_init (xhdr); + xhdr->string_length += strlen (s); + x_obstack_grow (xhdr, s, strlen (s)); +} + +bool +xheader_string_end (struct xheader *xhdr, char const *keyword) +{ + uintmax_t len; + uintmax_t p; + uintmax_t n = 0; + size_t size; + char nbuf[UINTMAX_STRSIZE_BOUND]; + char const *np; + char *cp; + + if (xhdr->buffer) + return false; + xheader_init (xhdr); + + len = strlen (keyword) + xhdr->string_length + 3; /* ' ' + '=' + '\n' */ + + do + { + p = n; + np = umaxtostr (len + p, nbuf); + n = nbuf + sizeof nbuf - 1 - np; + } + while (n != p); + + p = strlen (keyword) + n + 2; + size = p; + if (size != p) + { + ERROR ((0, 0, + _("Generated keyword/value pair is too long (keyword=%s, length=%s)"), + keyword, nbuf)); + obstack_free (xhdr->stk, obstack_finish (xhdr->stk)); + return false; + } + x_obstack_blank (xhdr, p); + x_obstack_1grow (xhdr, '\n'); + cp = obstack_next_free (xhdr->stk) - xhdr->string_length - p - 1; + memmove (cp + p, cp, xhdr->string_length); + cp = stpcpy (cp, np); + *cp++ = ' '; + cp = stpcpy (cp, keyword); + *cp++ = '='; + return true; +} + /* Implementations */ + +static void +out_of_range_header (char const *keyword, char const *value, + intmax_t minval, uintmax_t maxval) +{ + char minval_buf[INT_BUFSIZE_BOUND (intmax_t)]; + char maxval_buf[UINTMAX_STRSIZE_BOUND]; + char *minval_string = imaxtostr (minval, minval_buf); + char *maxval_string = umaxtostr (maxval, maxval_buf); + + /* TRANSLATORS: The first %s is the pax extended header keyword + (atime, gid, etc.). */ + ERROR ((0, 0, _("Extended header %s=%s is out of range %s..%s"), + keyword, value, minval_string, maxval_string)); +} + static void code_string (char const *string, char const *keyword, struct xheader *xhdr) { @@ -745,146 +1073,218 @@ decode_string (char **string, char const *arg) } static void -code_time (time_t t, unsigned long nano, - char const *keyword, struct xheader *xhdr) +code_time (struct timespec t, char const *keyword, struct xheader *xhdr) { - char sbuf[200]; - size_t s = format_uintmax (t, NULL, 0); - if (s + 11 >= sizeof sbuf) - return; - format_uintmax (t, sbuf, s); - sbuf[s++] = '.'; - s += format_uintmax (nano, sbuf + s, 9); - sbuf[s] = 0; - xheader_print (xhdr, keyword, sbuf); + char buf[TIMESPEC_STRSIZE_BOUND]; + xheader_print (xhdr, keyword, code_timespec (t, buf)); } -static void -decode_time (char const *arg, time_t *secs, unsigned long *nsecs) +static bool +decode_time (struct timespec *ts, char const *arg, char const *keyword) { - uintmax_t u; - char *p; - if (xstrtoumax (arg, &p, 10, &u, "") == LONGINT_OK) + char *arg_lim; + struct timespec t = decode_timespec (arg, &arg_lim, true); + + if (! valid_timespec (t)) { - *secs = u; - if (*p == '.' && xstrtoumax (p+1, NULL, 10, &u, "") == LONGINT_OK) - *nsecs = u; + if (arg < arg_lim && !*arg_lim) + out_of_range_header (keyword, arg, TYPE_MINIMUM (time_t), + TYPE_MAXIMUM (time_t)); + else + ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"), + keyword, arg)); + return false; } + + *ts = t; + return true; +} + +static void +code_signed_num (uintmax_t value, char const *keyword, + intmax_t minval, uintmax_t maxval, struct xheader *xhdr) +{ + char sbuf[SYSINT_BUFSIZE]; + xheader_print (xhdr, keyword, sysinttostr (value, minval, maxval, sbuf)); } static void code_num (uintmax_t value, char const *keyword, struct xheader *xhdr) { - char sbuf[100]; - size_t s = format_uintmax (value, NULL, 0); - format_uintmax (value, sbuf, s); - sbuf[s] = 0; - xheader_print (xhdr, keyword, sbuf); + code_signed_num (value, keyword, 0, UINTMAX_MAX, xhdr); +} + +static bool +decode_signed_num (intmax_t *num, char const *arg, + intmax_t minval, uintmax_t maxval, + char const *keyword) +{ + char *arg_lim; + intmax_t u = strtosysint (arg, &arg_lim, minval, maxval); + + if (errno == EINVAL || *arg_lim) + { + ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"), + keyword, arg)); + return false; + } + + if (errno == ERANGE) + { + out_of_range_header (keyword, arg, minval, maxval); + return false; + } + + *num = u; + return true; +} + +static bool +decode_num (uintmax_t *num, char const *arg, uintmax_t maxval, + char const *keyword) +{ + intmax_t i; + if (! decode_signed_num (&i, arg, 0, maxval, keyword)) + return false; + *num = i; + return true; } static void dummy_coder (struct tar_stat_info const *st __attribute__ ((unused)), char const *keyword __attribute__ ((unused)), struct xheader *xhdr __attribute__ ((unused)), - void *data __attribute__ ((unused))) + void const *data __attribute__ ((unused))) { } static void dummy_decoder (struct tar_stat_info *st __attribute__ ((unused)), - char const *arg __attribute__ ((unused))) + char const *keyword __attribute__ ((unused)), + char const *arg __attribute__ ((unused)), + size_t size __attribute__((unused))) { } static void atime_coder (struct tar_stat_info const *st, char const *keyword, - struct xheader *xhdr, void *data __attribute__ ((unused))) + struct xheader *xhdr, void const *data __attribute__ ((unused))) { - code_time (st->stat.st_atime, st->atime_nsec, keyword, xhdr); + code_time (st->atime, keyword, xhdr); } static void -atime_decoder (struct tar_stat_info *st, char const *arg) +atime_decoder (struct tar_stat_info *st, + char const *keyword, + char const *arg, + size_t size __attribute__((unused))) { - decode_time (arg, &st->stat.st_atime, &st->atime_nsec); + struct timespec ts; + if (decode_time (&ts, arg, keyword)) + st->atime = ts; } static void gid_coder (struct tar_stat_info const *st, char const *keyword, - struct xheader *xhdr, void *data __attribute__ ((unused))) + struct xheader *xhdr, void const *data __attribute__ ((unused))) { - code_num (st->stat.st_gid, keyword, xhdr); + code_signed_num (st->stat.st_gid, keyword, + TYPE_MINIMUM (gid_t), TYPE_MAXIMUM (gid_t), xhdr); } static void -gid_decoder (struct tar_stat_info *st, char const *arg) -{ - uintmax_t u; - if (xstrtoumax (arg, NULL, 10, &u, "") == LONGINT_OK) +gid_decoder (struct tar_stat_info *st, + char const *keyword, + char const *arg, + size_t size __attribute__((unused))) +{ + intmax_t u; + if (decode_signed_num (&u, arg, TYPE_MINIMUM (gid_t), + TYPE_MAXIMUM (gid_t), keyword)) st->stat.st_gid = u; } static void gname_coder (struct tar_stat_info const *st, char const *keyword, - struct xheader *xhdr, void *data __attribute__ ((unused))) + struct xheader *xhdr, void const *data __attribute__ ((unused))) { code_string (st->gname, keyword, xhdr); } static void -gname_decoder (struct tar_stat_info *st, char const *arg) +gname_decoder (struct tar_stat_info *st, + char const *keyword __attribute__((unused)), + char const *arg, + size_t size __attribute__((unused))) { decode_string (&st->gname, arg); } static void linkpath_coder (struct tar_stat_info const *st, char const *keyword, - struct xheader *xhdr, void *data __attribute__ ((unused))) + struct xheader *xhdr, void const *data __attribute__ ((unused))) { code_string (st->link_name, keyword, xhdr); } static void -linkpath_decoder (struct tar_stat_info *st, char const *arg) +linkpath_decoder (struct tar_stat_info *st, + char const *keyword __attribute__((unused)), + char const *arg, + size_t size __attribute__((unused))) { decode_string (&st->link_name, arg); } static void ctime_coder (struct tar_stat_info const *st, char const *keyword, - struct xheader *xhdr, void *data __attribute__ ((unused))) + struct xheader *xhdr, void const *data __attribute__ ((unused))) { - code_time (st->stat.st_ctime, st->ctime_nsec, keyword, xhdr); + code_time (st->ctime, keyword, xhdr); } static void -ctime_decoder (struct tar_stat_info *st, char const *arg) +ctime_decoder (struct tar_stat_info *st, + char const *keyword, + char const *arg, + size_t size __attribute__((unused))) { - decode_time (arg, &st->stat.st_ctime, &st->ctime_nsec); + struct timespec ts; + if (decode_time (&ts, arg, keyword)) + st->ctime = ts; } static void mtime_coder (struct tar_stat_info const *st, char const *keyword, - struct xheader *xhdr, void *data __attribute__ ((unused))) + struct xheader *xhdr, void const *data) { - code_time (st->stat.st_mtime, st->mtime_nsec, keyword, xhdr); + struct timespec const *mtime = data; + code_time (mtime ? *mtime : st->mtime, keyword, xhdr); } static void -mtime_decoder (struct tar_stat_info *st, char const *arg) +mtime_decoder (struct tar_stat_info *st, + char const *keyword, + char const *arg, + size_t size __attribute__((unused))) { - decode_time (arg, &st->stat.st_mtime, &st->mtime_nsec); + struct timespec ts; + if (decode_time (&ts, arg, keyword)) + st->mtime = ts; } static void path_coder (struct tar_stat_info const *st, char const *keyword, - struct xheader *xhdr, void *data __attribute__ ((unused))) + struct xheader *xhdr, void const *data __attribute__ ((unused))) { code_string (st->file_name, keyword, xhdr); } static void -path_decoder (struct tar_stat_info *st, char const *arg) +path_decoder (struct tar_stat_info *st, + char const *keyword __attribute__((unused)), + char const *arg, + size_t size __attribute__((unused))) { decode_string (&st->orig_file_name, arg); decode_string (&st->file_name, arg); @@ -893,157 +1293,463 @@ path_decoder (struct tar_stat_info *st, char const *arg) static void size_coder (struct tar_stat_info const *st, char const *keyword, - struct xheader *xhdr, void *data __attribute__ ((unused))) + struct xheader *xhdr, void const *data __attribute__ ((unused))) { code_num (st->stat.st_size, keyword, xhdr); } static void -size_decoder (struct tar_stat_info *st, char const *arg) +size_decoder (struct tar_stat_info *st, + char const *keyword, + char const *arg, + size_t size __attribute__((unused))) { uintmax_t u; - if (xstrtoumax (arg, NULL, 10, &u, "") == LONGINT_OK) - st->archive_file_size = st->stat.st_size = u; + if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword)) + st->stat.st_size = u; } static void uid_coder (struct tar_stat_info const *st, char const *keyword, - struct xheader *xhdr, void *data __attribute__ ((unused))) + struct xheader *xhdr, void const *data __attribute__ ((unused))) { - code_num (st->stat.st_uid, keyword, xhdr); + code_signed_num (st->stat.st_uid, keyword, + TYPE_MINIMUM (uid_t), TYPE_MAXIMUM (uid_t), xhdr); } static void -uid_decoder (struct tar_stat_info *st, char const *arg) -{ - uintmax_t u; - if (xstrtoumax (arg, NULL, 10, &u, "") == LONGINT_OK) +uid_decoder (struct tar_stat_info *st, + char const *keyword, + char const *arg, + size_t size __attribute__((unused))) +{ + intmax_t u; + if (decode_signed_num (&u, arg, TYPE_MINIMUM (uid_t), + TYPE_MAXIMUM (uid_t), keyword)) st->stat.st_uid = u; } static void uname_coder (struct tar_stat_info const *st, char const *keyword, - struct xheader *xhdr, void *data __attribute__ ((unused))) + struct xheader *xhdr, void const *data __attribute__ ((unused))) { code_string (st->uname, keyword, xhdr); } static void -uname_decoder (struct tar_stat_info *st, char const *arg) +uname_decoder (struct tar_stat_info *st, + char const *keyword __attribute__((unused)), + char const *arg, + size_t size __attribute__((unused))) { decode_string (&st->uname, arg); } static void sparse_size_coder (struct tar_stat_info const *st, char const *keyword, - struct xheader *xhdr, void *data) + struct xheader *xhdr, void const *data) { size_coder (st, keyword, xhdr, data); } static void -sparse_size_decoder (struct tar_stat_info *st, char const *arg) +sparse_size_decoder (struct tar_stat_info *st, + char const *keyword, + char const *arg, + size_t size __attribute__((unused))) { uintmax_t u; - if (xstrtoumax (arg, NULL, 10, &u, "") == LONGINT_OK) + if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword)) st->stat.st_size = u; } static void sparse_numblocks_coder (struct tar_stat_info const *st, char const *keyword, struct xheader *xhdr, - void *data __attribute__ ((unused))) + void const *data __attribute__ ((unused))) { code_num (st->sparse_map_avail, keyword, xhdr); } static void -sparse_numblocks_decoder (struct tar_stat_info *st, char const *arg) +sparse_numblocks_decoder (struct tar_stat_info *st, + char const *keyword, + char const *arg, + size_t size __attribute__((unused))) { uintmax_t u; - if (xstrtoumax (arg, NULL, 10, &u, "") == LONGINT_OK) + if (decode_num (&u, arg, SIZE_MAX, keyword)) { st->sparse_map_size = u; - st->sparse_map = calloc(st->sparse_map_size, sizeof(st->sparse_map[0])); + st->sparse_map = xcalloc (u, sizeof st->sparse_map[0]); st->sparse_map_avail = 0; } } static void sparse_offset_coder (struct tar_stat_info const *st, char const *keyword, - struct xheader *xhdr, void *data) + struct xheader *xhdr, void const *data) { - size_t i = *(size_t*)data; - code_num (st->sparse_map[i].offset, keyword, xhdr); + size_t const *pi = data; + code_num (st->sparse_map[*pi].offset, keyword, xhdr); } static void -sparse_offset_decoder (struct tar_stat_info *st, char const *arg) +sparse_offset_decoder (struct tar_stat_info *st, + char const *keyword, + char const *arg, + size_t size __attribute__((unused))) { uintmax_t u; - if (xstrtoumax (arg, NULL, 10, &u, "") == LONGINT_OK) - st->sparse_map[st->sparse_map_avail].offset = u; + if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword)) + { + if (st->sparse_map_avail < st->sparse_map_size) + st->sparse_map[st->sparse_map_avail].offset = u; + else + ERROR ((0, 0, _("Malformed extended header: excess %s=%s"), + "GNU.sparse.offset", arg)); + } } static void sparse_numbytes_coder (struct tar_stat_info const *st, char const *keyword, - struct xheader *xhdr, void *data) + struct xheader *xhdr, void const *data) { - size_t i = *(size_t*)data; - code_num (st->sparse_map[i].numbytes, keyword, xhdr); + size_t const *pi = data; + code_num (st->sparse_map[*pi].numbytes, keyword, xhdr); } static void -sparse_numbytes_decoder (struct tar_stat_info *st, char const *arg) +sparse_numbytes_decoder (struct tar_stat_info *st, + char const *keyword, + char const *arg, + size_t size __attribute__((unused))) { uintmax_t u; - if (xstrtoumax (arg, NULL, 10, &u, "") == LONGINT_OK) + if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword)) + { + if (st->sparse_map_avail < st->sparse_map_size) + st->sparse_map[st->sparse_map_avail++].numbytes = u; + else + ERROR ((0, 0, _("Malformed extended header: excess %s=%s"), + keyword, arg)); + } +} + +static void +sparse_map_decoder (struct tar_stat_info *st, + char const *keyword, + char const *arg, + size_t size __attribute__((unused))) +{ + int offset = 1; + + st->sparse_map_avail = 0; + while (1) { - if (st->sparse_map_avail == st->sparse_map_size) + intmax_t u; + char *delim; + struct sp_array e; + + if (!ISDIGIT (*arg)) { - st->sparse_map_size *= 2; - st->sparse_map = xrealloc (st->sparse_map, - st->sparse_map_size - * sizeof st->sparse_map[0]); + ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"), + keyword, arg)); + return; + } + + errno = 0; + u = strtoimax (arg, &delim, 10); + if (TYPE_MAXIMUM (off_t) < u) + { + u = TYPE_MAXIMUM (off_t); + errno = ERANGE; + } + if (offset) + { + e.offset = u; + if (errno == ERANGE) + { + out_of_range_header (keyword, arg, 0, TYPE_MAXIMUM (off_t)); + return; + } } - st->sparse_map[st->sparse_map_avail++].numbytes = u; + else + { + e.numbytes = u; + if (errno == ERANGE) + { + out_of_range_header (keyword, arg, 0, TYPE_MAXIMUM (off_t)); + return; + } + if (st->sparse_map_avail < st->sparse_map_size) + st->sparse_map[st->sparse_map_avail++] = e; + else + { + ERROR ((0, 0, _("Malformed extended header: excess %s=%s"), + keyword, arg)); + return; + } + } + + offset = !offset; + + if (*delim == 0) + break; + else if (*delim != ',') + { + ERROR ((0, 0, + _("Malformed extended header: invalid %s: unexpected delimiter %c"), + keyword, *delim)); + return; + } + + arg = delim + 1; } + + if (!offset) + ERROR ((0, 0, + _("Malformed extended header: invalid %s: odd number of values"), + keyword)); +} + +static void +dumpdir_coder (struct tar_stat_info const *st, char const *keyword, + struct xheader *xhdr, void const *data) +{ + xheader_print_n (xhdr, keyword, data, dumpdir_size (data)); +} + +static void +dumpdir_decoder (struct tar_stat_info *st, + char const *keyword __attribute__((unused)), + char const *arg, + size_t size) +{ + st->dumpdir = xmalloc (size); + memcpy (st->dumpdir, arg, size); +} + +static void +volume_label_coder (struct tar_stat_info const *st, char const *keyword, + struct xheader *xhdr, void const *data) +{ + code_string (data, keyword, xhdr); +} + +static void +volume_label_decoder (struct tar_stat_info *st, + char const *keyword __attribute__((unused)), + char const *arg, + size_t size __attribute__((unused))) +{ + decode_string (&volume_label, arg); +} + +static void +volume_size_coder (struct tar_stat_info const *st, char const *keyword, + struct xheader *xhdr, void const *data) +{ + off_t const *v = data; + code_num (*v, keyword, xhdr); +} + +static void +volume_size_decoder (struct tar_stat_info *st, + char const *keyword, + char const *arg, size_t size) +{ + uintmax_t u; + if (decode_num (&u, arg, TYPE_MAXIMUM (uintmax_t), keyword)) + continued_file_size = u; +} + +/* FIXME: Merge with volume_size_coder */ +static void +volume_offset_coder (struct tar_stat_info const *st, char const *keyword, + struct xheader *xhdr, void const *data) +{ + off_t const *v = data; + code_num (*v, keyword, xhdr); +} + +static void +volume_offset_decoder (struct tar_stat_info *st, + char const *keyword, + char const *arg, size_t size) +{ + uintmax_t u; + if (decode_num (&u, arg, TYPE_MAXIMUM (uintmax_t), keyword)) + continued_file_offset = u; +} + +static void +volume_filename_decoder (struct tar_stat_info *st, + char const *keyword __attribute__((unused)), + char const *arg, + size_t size __attribute__((unused))) +{ + decode_string (&continued_file_name, arg); +} + +static void +xattr_selinux_coder (struct tar_stat_info const *st, char const *keyword, + struct xheader *xhdr, void const *data) +{ + code_string (st->cntx_name, keyword, xhdr); +} + +static void +xattr_selinux_decoder (struct tar_stat_info *st, + char const *keyword, char const *arg, size_t size) +{ + decode_string (&st->cntx_name, arg); +} + +static void +xattr_acls_a_coder (struct tar_stat_info const *st , char const *keyword, + struct xheader *xhdr, void const *data) +{ + xheader_print_n (xhdr, keyword, st->acls_a_ptr, st->acls_a_len); +} + +static void +xattr_acls_a_decoder (struct tar_stat_info *st, + char const *keyword, char const *arg, size_t size) +{ + st->acls_a_ptr = xmemdup (arg, size + 1); + st->acls_a_len = size; +} + +static void +xattr_acls_d_coder (struct tar_stat_info const *st , char const *keyword, + struct xheader *xhdr, void const *data) +{ + xheader_print_n (xhdr, keyword, st->acls_d_ptr, st->acls_d_len); +} + +static void +xattr_acls_d_decoder (struct tar_stat_info *st, + char const *keyword, char const *arg, size_t size) +{ + st->acls_d_ptr = xmemdup (arg, size + 1); + st->acls_d_len = size; +} + +static void +xattr_coder (struct tar_stat_info const *st, char const *keyword, + struct xheader *xhdr, void const *data) +{ + struct xattr_array *xattr_map = st->xattr_map; + const size_t *off = data; + xheader_print_n (xhdr, keyword, + xattr_map[*off].xval_ptr, xattr_map[*off].xval_len); +} + +static void +xattr_decoder (struct tar_stat_info *st, + char const *keyword, char const *arg, size_t size) +{ + char *xstr, *xkey; + + /* copy keyword */ + size_t klen_raw = strlen (keyword); + xkey = alloca (klen_raw + 1); + memcpy (xkey, keyword, klen_raw + 1) /* including null-terminating */; + + /* copy value */ + xstr = alloca (size + 1); + memcpy (xstr, arg, size + 1); /* separator included, for GNU tar '\n' */; + + xattr_decode_keyword (xkey); + + xheader_xattr_add (st, xkey + strlen("SCHILY.xattr."), xstr, size); +} + +static void +sparse_major_coder (struct tar_stat_info const *st, char const *keyword, + struct xheader *xhdr, void const *data) +{ + code_num (st->sparse_major, keyword, xhdr); +} + +static void +sparse_major_decoder (struct tar_stat_info *st, + char const *keyword, + char const *arg, + size_t size) +{ + uintmax_t u; + if (decode_num (&u, arg, TYPE_MAXIMUM (unsigned), keyword)) + st->sparse_major = u; +} + +static void +sparse_minor_coder (struct tar_stat_info const *st, char const *keyword, + struct xheader *xhdr, void const *data) +{ + code_num (st->sparse_minor, keyword, xhdr); +} + +static void +sparse_minor_decoder (struct tar_stat_info *st, + char const *keyword, + char const *arg, + size_t size) +{ + uintmax_t u; + if (decode_num (&u, arg, TYPE_MAXIMUM (unsigned), keyword)) + st->sparse_minor = u; } struct xhdr_tab const xhdr_tab[] = { - { "atime", atime_coder, atime_decoder, false }, - { "comment", dummy_coder, dummy_decoder, false }, - { "charset", dummy_coder, dummy_decoder, false }, - { "ctime", ctime_coder, ctime_decoder, false }, - { "gid", gid_coder, gid_decoder, false }, - { "gname", gname_coder, gname_decoder, false }, - { "linkpath", linkpath_coder, linkpath_decoder, false }, - { "mtime", mtime_coder, mtime_decoder, false }, - { "path", path_coder, path_decoder, false }, - { "size", size_coder, size_decoder, false }, - { "uid", uid_coder, uid_decoder, false }, - { "uname", uname_coder, uname_decoder, false }, + { "atime", atime_coder, atime_decoder, 0, false }, + { "comment", dummy_coder, dummy_decoder, 0, false }, + { "charset", dummy_coder, dummy_decoder, 0, false }, + { "ctime", ctime_coder, ctime_decoder, 0, false }, + { "gid", gid_coder, gid_decoder, 0, false }, + { "gname", gname_coder, gname_decoder, 0, false }, + { "linkpath", linkpath_coder, linkpath_decoder, 0, false }, + { "mtime", mtime_coder, mtime_decoder, 0, false }, + { "path", path_coder, path_decoder, 0, false }, + { "size", size_coder, size_decoder, 0, false }, + { "uid", uid_coder, uid_decoder, 0, false }, + { "uname", uname_coder, uname_decoder, 0, false }, /* Sparse file handling */ - { "GNU.sparse.size", sparse_size_coder, sparse_size_decoder, true }, + { "GNU.sparse.name", path_coder, path_decoder, + XHDR_PROTECTED, false }, + { "GNU.sparse.major", sparse_major_coder, sparse_major_decoder, + XHDR_PROTECTED, false }, + { "GNU.sparse.minor", sparse_minor_coder, sparse_minor_decoder, + XHDR_PROTECTED, false }, + { "GNU.sparse.realsize", sparse_size_coder, sparse_size_decoder, + XHDR_PROTECTED, false }, { "GNU.sparse.numblocks", sparse_numblocks_coder, sparse_numblocks_decoder, - true }, + XHDR_PROTECTED, false }, + + /* tar 1.14 - 1.15.90 keywords. */ + { "GNU.sparse.size", sparse_size_coder, sparse_size_decoder, + XHDR_PROTECTED, false }, + /* tar 1.14 - 1.15.1 keywords. Multiple instances of these appeared in 'x' + headers, and each of them was meaningful. It confilcted with POSIX specs, + which requires that "when extended header records conflict, the last one + given in the header shall take precedence." */ { "GNU.sparse.offset", sparse_offset_coder, sparse_offset_decoder, - true }, + XHDR_PROTECTED, false }, { "GNU.sparse.numbytes", sparse_numbytes_coder, sparse_numbytes_decoder, - true }, + XHDR_PROTECTED, false }, + /* tar 1.15.90 keyword, introduced to remove the above-mentioned conflict. */ + { "GNU.sparse.map", NULL /* Unused, see pax_dump_header() */, + sparse_map_decoder, 0, false }, -#if 0 /* GNU private keywords (not yet implemented) */ + { "GNU.dumpdir", dumpdir_coder, dumpdir_decoder, + XHDR_PROTECTED, false }, - /* The next directory entry actually contains the names of files - that were in the directory at the time the dump was made. - Supersedes GNUTYPE_DUMPDIR header type. */ - { "GNU.dump.name", dump_name_coder, dump_name_decoder, false }, - { "GNU.dump.status", dump_status_coder, dump_status_decoder, false }, - - /* Keeps the tape/volume header. May be present only in the global headers. + /* Keeps the tape/volume label. May be present only in the global headers. Equivalent to GNUTYPE_VOLHDR. */ - { "GNU.volume.header", volume_header_coder, volume_header_decoder, false }, + { "GNU.volume.label", volume_label_coder, volume_label_decoder, + XHDR_PROTECTED | XHDR_GLOBAL, false }, /* These may be present in a first global header of the archive. They provide the same functionality as GNUTYPE_MULTIVOL header. @@ -1051,9 +1757,29 @@ struct xhdr_tab const xhdr_tab[] = { otherwise kept in the size field of a multivolume header. The GNU.volume.offset keeps the offset of the start of this volume, otherwise kept in oldgnu_header.offset. */ - { "GNU.volume.size", volume_size_coder, volume_size_decoder, false }, - { "GNU.volume.offset", volume_offset_coder, volume_offset_decoder, false }, -#endif - - { NULL, NULL, NULL, false } + { "GNU.volume.filename", volume_label_coder, volume_filename_decoder, + XHDR_PROTECTED | XHDR_GLOBAL, false }, + { "GNU.volume.size", volume_size_coder, volume_size_decoder, + XHDR_PROTECTED | XHDR_GLOBAL, false }, + { "GNU.volume.offset", volume_offset_coder, volume_offset_decoder, + XHDR_PROTECTED | XHDR_GLOBAL, false }, + + /* We get the SELinux value from filecon, so add a namespace for SELinux + instead of storing it in SCHILY.xattr.* (which would be RAW). */ + { "RHT.security.selinux", + xattr_selinux_coder, xattr_selinux_decoder, 0, false }, + + /* ACLs, use the star format... */ + { "SCHILY.acl.access", + xattr_acls_a_coder, xattr_acls_a_decoder, 0, false }, + + { "SCHILY.acl.default", + xattr_acls_d_coder, xattr_acls_d_decoder, 0, false }, + + /* We are storing all extended attributes using this rule even if some of them + were stored by some previous rule (duplicates) -- we just have to make sure + they are restored *only once* during extraction later on. */ + { "SCHILY.xattr", xattr_coder, xattr_decoder, 0, true }, + + { NULL, NULL, NULL, 0, false } };