X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Ftar;a=blobdiff_plain;f=src%2Fxheader.c;h=c94c6d39f1e2437a3caa8ea577d436cd77275143;hp=6141748a7228dc01d4f1272147f340fb5857b358;hb=45ccda119355a1087450039a250359c1d0de0d08;hpb=d36f5a3cc3280d6c4a58367bf51b527d5c14ac04 diff --git a/src/xheader.c b/src/xheader.c index 6141748..c94c6d3 100644 --- a/src/xheader.c +++ b/src/xheader.c @@ -1,21 +1,22 @@ /* POSIX extended headers for tar. - Copyright (C) 2003, 2004, 2005, 2006, 2007, 2009, 2010, 2012 - 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 3, 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., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #include @@ -167,14 +168,13 @@ xheader_set_single_keyword (char *kw) static void assign_time_option (char **sval, time_t *tval, const char *input) { - uintmax_t u; char *p; - time_t t = u = strtoumax (input, &p, 10); - if (t != u || *p || errno == ERANGE) + 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; + *tval = t.tv_sec; assign_string (sval, input); } } @@ -262,7 +262,7 @@ xheader_format_name (struct tar_stat_info *st, const char *fmt, size_t n) char *dir = NULL; char *base = NULL; char pidbuf[UINTMAX_STRSIZE_BOUND]; - char const *pptr; + char const *pptr = NULL; char nbuf[UINTMAX_STRSIZE_BOUND]; char const *nptr = NULL; @@ -335,13 +335,10 @@ xheader_format_name (struct tar_stat_info *st, const char *fmt, size_t n) break; case 'n': - if (nptr) - { - q = stpcpy (q, nptr); - p += 2; - break; - } - /* else fall through */ + q = stpcpy (q, nptr); + p += 2; + break; + default: *q++ = *p++; @@ -455,12 +452,14 @@ xheader_write_global (struct xheader *xhdr) char *name; xheader_finish (xhdr); - xheader_write (XGLTYPE, name = xheader_ghdr_name (), time (NULL), 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) +void +xheader_xattr_init (struct tar_stat_info *st) { st->xattr_map = NULL; st->xattr_map_size = 0; @@ -469,9 +468,11 @@ void xheader_xattr_init (struct tar_stat_info *st) 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) +void +xheader_xattr_free (struct xattr_array *xattr_map, size_t xattr_map_size) { size_t scan = 0; @@ -485,9 +486,10 @@ void xheader_xattr_free (struct xattr_array *xattr_map, size_t xattr_map_size) 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) +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)++; @@ -498,8 +500,47 @@ static void xheader_xattr__add (struct xattr_array **xattr_map, (*xattr_map)[pos].xval_len = len; } -void xheader_xattr_add(struct tar_stat_info *st, - const char *key, const char *val, size_t 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); @@ -513,8 +554,9 @@ void xheader_xattr_add(struct tar_stat_info *st, free (xkey); } -void xheader_xattr_copy(const struct tar_stat_info *st, - struct xattr_array **xattr_map, size_t *xattr_map_size) +void +xheader_xattr_copy (const struct tar_stat_info *st, + struct xattr_array **xattr_map, size_t *xattr_map_size) { size_t scan = 0; @@ -611,7 +653,6 @@ decode_record (struct xheader *xhdr, { char *start = *ptr; char *p = start; - uintmax_t u; size_t len; char *len_lim; char const *keyword; @@ -628,13 +669,7 @@ decode_record (struct xheader *xhdr, 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"))); - return false; - } + len = strtoumax (p, &len_lim, 10); if (len_max < len) { @@ -776,10 +811,16 @@ xheader_store (char const *keyword, struct tar_stat_info *st, } void -xheader_read (struct xheader *xhdr, union block *p, size_t size) +xheader_read (struct xheader *xhdr, union block *p, off_t size) { size_t j = 0; + if (size < 0) + size = 0; /* Already diagnosed. */ + + if (SIZE_MAX - BLOCKSIZE <= size) + xalloc_die (); + size += BLOCKSIZE; xhdr->size = size; xhdr->buffer = xmalloc (size + 1); @@ -806,15 +847,71 @@ xheader_read (struct xheader *xhdr, union block *p, size_t size) while (size > 0); } +/* 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) +{ + static char *encode_buffer = NULL; + static size_t encode_buffer_size = 0; + size_t bp; /* keyword/buffer pointers */ + + if (!encode_buffer) + { + encode_buffer_size = 256; + encode_buffer = xmalloc (encode_buffer_size); + } + else + *encode_buffer = 0; + + for (bp = 0; *keyword != 0; ++bp, ++keyword) + { + 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; + } + + encode_buffer[bp] = 0; + + return encode_buffer; +} + static void xheader_print_n (struct xheader *xhdr, char const *keyword, char const *value, size_t vsize) { - size_t len = strlen (keyword) + vsize + 3; /* ' ' + '=' + '\n' */ 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 { @@ -826,7 +923,7 @@ xheader_print_n (struct xheader *xhdr, char const *keyword, x_obstack_grow (xhdr, np, n); x_obstack_1grow (xhdr, ' '); - x_obstack_grow (xhdr, keyword, strlen (keyword)); + x_obstack_grow (xhdr, keyword, klen); x_obstack_1grow (xhdr, '='); x_obstack_grow (xhdr, value, vsize); x_obstack_1grow (xhdr, '\n'); @@ -934,14 +1031,12 @@ xheader_string_end (struct xheader *xhdr, char const *keyword) static void out_of_range_header (char const *keyword, char const *value, - uintmax_t minus_minval, uintmax_t maxval) + intmax_t minval, uintmax_t maxval) { - char minval_buf[UINTMAX_STRSIZE_BOUND + 1]; + char minval_buf[INT_BUFSIZE_BOUND (intmax_t)]; char maxval_buf[UINTMAX_STRSIZE_BOUND]; - char *minval_string = umaxtostr (minus_minval, minval_buf + 1); + char *minval_string = imaxtostr (minval, minval_buf); char *maxval_string = umaxtostr (maxval, maxval_buf); - if (minus_minval) - *--minval_string = '-'; /* TRANSLATORS: The first %s is the pax extended header keyword (atime, gid, etc.). */ @@ -984,136 +1079,59 @@ code_time (struct timespec t, char const *keyword, struct xheader *xhdr) xheader_print (xhdr, keyword, code_timespec (t, buf)); } -enum decode_time_status - { - decode_time_success, - decode_time_range, - decode_time_bad_header - }; - -static enum decode_time_status -_decode_time (struct timespec *ts, char const *arg, char const *keyword) +static bool +decode_time (struct timespec *ts, char const *arg, char const *keyword) { - time_t s; - unsigned long int ns = 0; - char *p; char *arg_lim; - bool negative = *arg == '-'; - - errno = 0; + struct timespec t = decode_timespec (arg, &arg_lim, true); - if (ISDIGIT (arg[negative])) + if (! valid_timespec (t)) { - if (negative) - { - intmax_t i = strtoimax (arg, &arg_lim, 10); - if (TYPE_SIGNED (time_t) ? i < TYPE_MINIMUM (time_t) : i < 0) - return decode_time_range; - s = i; - } + if (arg < arg_lim && !*arg_lim) + out_of_range_header (keyword, arg, TYPE_MINIMUM (time_t), + TYPE_MAXIMUM (time_t)); else - { - uintmax_t i = strtoumax (arg, &arg_lim, 10); - if (TYPE_MAXIMUM (time_t) < i) - return decode_time_range; - s = i; - } - - p = arg_lim; - - if (errno == ERANGE) - return decode_time_range; - - if (*p == '.') - { - int digits = 0; - bool trailing_nonzero = false; - - while (ISDIGIT (*++p)) - if (digits < LOG10_BILLION) - { - ns = 10 * ns + (*p - '0'); - digits++; - } - else - trailing_nonzero |= *p != '0'; - - while (digits++ < LOG10_BILLION) - ns *= 10; - - if (negative) - { - /* Convert "-1.10000000000001" to s == -2, ns == 89999999. - I.e., truncate time stamps towards minus infinity while - converting them to internal form. */ - ns += trailing_nonzero; - if (ns != 0) - { - if (s == TYPE_MINIMUM (time_t)) - return decode_time_range; - s--; - ns = BILLION - ns; - } - } - } - - if (! *p) - { - ts->tv_sec = s; - ts->tv_nsec = ns; - return decode_time_success; - } + ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"), + keyword, arg)); + return false; } - return decode_time_bad_header; + *ts = t; + return true; } -static bool -decode_time (struct timespec *ts, char const *arg, char const *keyword) +static void +code_signed_num (uintmax_t value, char const *keyword, + intmax_t minval, uintmax_t maxval, struct xheader *xhdr) { - switch (_decode_time (ts, arg, keyword)) - { - case decode_time_success: - return true; - case decode_time_bad_header: - ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"), - keyword, arg)); - return false; - case decode_time_range: - out_of_range_header (keyword, arg, - (uintmax_t) TYPE_MINIMUM (time_t), - TYPE_MAXIMUM (time_t)); - return false; - } - return true; + 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[UINTMAX_STRSIZE_BOUND]; - xheader_print (xhdr, keyword, umaxtostr (value, sbuf)); + code_signed_num (value, keyword, 0, UINTMAX_MAX, xhdr); } static bool -decode_num (uintmax_t *num, char const *arg, uintmax_t maxval, - char const *keyword) +decode_signed_num (intmax_t *num, char const *arg, + intmax_t minval, uintmax_t maxval, + char const *keyword) { - uintmax_t u; char *arg_lim; + intmax_t u = strtosysint (arg, &arg_lim, minval, maxval); - if (! (ISDIGIT (*arg) - && (errno = 0, u = strtoumax (arg, &arg_lim, 10), !*arg_lim))) + if (errno == EINVAL || *arg_lim) { ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"), keyword, arg)); return false; } - if (! (u <= maxval && errno != ERANGE)) + if (errno == ERANGE) { - out_of_range_header (keyword, arg, 0, maxval); + out_of_range_header (keyword, arg, minval, maxval); return false; } @@ -1121,6 +1139,17 @@ decode_num (uintmax_t *num, char const *arg, uintmax_t maxval, 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)), @@ -1159,7 +1188,8 @@ static void gid_coder (struct tar_stat_info const *st, char const *keyword, 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 @@ -1168,8 +1198,9 @@ gid_decoder (struct tar_stat_info *st, char const *arg, size_t size __attribute__((unused))) { - uintmax_t u; - if (decode_num (&u, arg, TYPE_MAXIMUM (gid_t), keyword)) + intmax_t u; + if (decode_signed_num (&u, arg, TYPE_MINIMUM (gid_t), + TYPE_MAXIMUM (gid_t), keyword)) st->stat.st_gid = u; } @@ -1282,7 +1313,8 @@ static void uid_coder (struct tar_stat_info const *st, char const *keyword, 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 @@ -1291,8 +1323,9 @@ uid_decoder (struct tar_stat_info *st, char const *arg, size_t size __attribute__((unused))) { - uintmax_t u; - if (decode_num (&u, arg, TYPE_MAXIMUM (uid_t), keyword)) + intmax_t u; + if (decode_signed_num (&u, arg, TYPE_MINIMUM (uid_t), + TYPE_MAXIMUM (uid_t), keyword)) st->stat.st_uid = u; } @@ -1414,7 +1447,7 @@ sparse_map_decoder (struct tar_stat_info *st, st->sparse_map_avail = 0; while (1) { - uintmax_t u; + intmax_t u; char *delim; struct sp_array e; @@ -1426,11 +1459,16 @@ sparse_map_decoder (struct tar_stat_info *st, } errno = 0; - u = strtoumax (arg, &delim, 10); + u = strtoimax (arg, &delim, 10); + if (TYPE_MAXIMUM (off_t) < u) + { + u = TYPE_MAXIMUM (off_t); + errno = ERANGE; + } if (offset) { e.offset = u; - if (!(u == e.offset && errno != ERANGE)) + if (errno == ERANGE) { out_of_range_header (keyword, arg, 0, TYPE_MAXIMUM (off_t)); return; @@ -1439,7 +1477,7 @@ sparse_map_decoder (struct tar_stat_info *st, else { e.numbytes = u; - if (!(u == e.numbytes && errno != ERANGE)) + if (errno == ERANGE) { out_of_range_header (keyword, arg, 0, TYPE_MAXIMUM (off_t)); return; @@ -1554,6 +1592,20 @@ volume_filename_decoder (struct tar_stat_info *st, 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) @@ -1598,11 +1650,20 @@ static void xattr_decoder (struct tar_stat_info *st, char const *keyword, char const *arg, size_t size) { - char *xstr = NULL; + 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 */; - xstr = xmemdup(arg, size + 1); - xheader_xattr_add(st, keyword + strlen("SCHILY.xattr."), xstr, size); - free(xstr); + /* 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 @@ -1703,6 +1764,11 @@ struct xhdr_tab const xhdr_tab[] = { { "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 },