]> Dogcows Code - chaz/tar/blobdiff - src/xheader.c
Update copyright years.
[chaz/tar] / src / xheader.c
index 559e60adb22be629b6e9a01c12b2400e0325b5e1..c94c6d39f1e2437a3caa8ea577d436cd77275143 100644 (file)
@@ -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 <http://www.gnu.org/licenses/>.  */
 
 #include <system.h>
 
@@ -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;
@@ -472,7 +471,8 @@ void xheader_xattr_init (struct tar_stat_info *st)
   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;
 
@@ -486,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)++;
 
@@ -499,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);
@@ -514,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;
 
@@ -612,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;
@@ -629,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)
     {
@@ -777,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);
@@ -807,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
     {
@@ -827,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');
@@ -935,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.).  */
@@ -985,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;
     }
 
@@ -1122,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)),
@@ -1160,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
@@ -1169,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;
 }
 
@@ -1283,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
@@ -1292,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;
 }
 
@@ -1415,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;
 
@@ -1427,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;
@@ -1440,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;
@@ -1613,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 */;
+
+  /* copy value */
+  xstr = alloca (size + 1);
+  memcpy (xstr, arg, size + 1); /* separator included, for GNU tar '\n' */;
+
+  xattr_decode_keyword (xkey);
 
-  xstr = xmemdup(arg, size + 1);
-  xheader_xattr_add(st, keyword + strlen("SCHILY.xattr."), xstr, size);
-  free(xstr);
+  xheader_xattr_add (st, xkey + strlen("SCHILY.xattr."), xstr, size);
 }
 
 static void
This page took 0.035159 seconds and 4 git commands to generate.