X-Git-Url: https://git.brokenzipper.com/gitweb?a=blobdiff_plain;f=src%2Fxheader.c;h=493a955c49ff0c7e8df62d613243e4dea4cc188d;hb=48d508b78b6680b69ed63baf963d4274fc17e68c;hp=48c857f3f6fbbceda633f440a64218ee493e5f47;hpb=d9045bf40904d974eb8ebd7c317324096dbbaf1b;p=chaz%2Ftar diff --git a/src/xheader.c b/src/xheader.c index 48c857f..493a955 100644 --- a/src/xheader.c +++ b/src/xheader.c @@ -28,13 +28,6 @@ #include -#if !HAVE_DECL_STRTOIMAX && !defined strtoimax -intmax_t strtoimax (); -#endif -#if !HAVE_DECL_STRTOUMAX && !defined strtoumax -uintmax_t strtoumax (); -#endif - 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)); @@ -57,7 +50,29 @@ static size_t global_header_count; However it should wait until buffer.c is finally rewritten */ -enum { BILLION = 1000000000, LOG10_BILLION = 9 }; + +/* 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 */ @@ -88,7 +103,7 @@ static char *exthdr_name; /* Template for the name field of a 'g' type header */ static char *globexthdr_name; -static bool +bool xheader_keyword_deleted_p (const char *kw) { struct keyword_list *kp; @@ -409,8 +424,8 @@ 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 *); + struct xheader *, void const *data); + void (*decoder) (struct tar_stat_info *, char const *, size_t); bool protect; }; @@ -459,7 +474,7 @@ xheader_protected_keyword_p (const char *keyword) Return true on success, false otherwise. */ static bool decode_record (char **ptr, - void (*handler) (void *, char const *, char const *), + void (*handler) (void *, char const *, char const *, size_t), void *data) { char *start = *ptr; @@ -517,7 +532,7 @@ decode_record (char **ptr, } *p = nextp[-1] = '\0'; - handler (data, keyword, p + 1); + handler (data, keyword, p + 1, nextp - p - 2); /* '=' + trailing '\n' */ *p = '='; nextp[-1] = '\n'; *ptr = nextp; @@ -531,12 +546,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, 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; @@ -547,7 +562,10 @@ decx (void *data, char const *keyword, char const *value) t = locate_handler (keyword); if (t) - t->decoder (st, value); + t->decoder (st, value, size); + else + ERROR((0, 0, _("Ignoring unknown extended header keyword `%s'"), + keyword)); } void @@ -566,7 +584,8 @@ xheader_decode (struct tar_stat_info *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); @@ -596,14 +615,15 @@ extended_header_init (void) } void -xheader_store (char const *keyword, struct tar_stat_info const *st, void *data) +xheader_store (char const *keyword, struct tar_stat_info const *st, + void const *data) { struct xhdr_tab const *t; if (extended_header.buffer) return; t = locate_handler (keyword); - if (!t) + if (!t || !t->coder) return; if (xheader_keyword_deleted_p (keyword) || xheader_keyword_override_p (keyword)) @@ -644,9 +664,10 @@ xheader_read (union block *p, size_t size) } 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 len = strlen (keyword) + vsize + 3; /* ' ' + '=' + '\n' */ size_t p; size_t n = 0; char nbuf[UINTMAX_STRSIZE_BOUND]; @@ -660,12 +681,18 @@ xheader_print (struct xheader *xhdr, char const *keyword, char const *value) } while (n != p); - obstack_grow (xhdr->stk, np, 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, strlen (keyword)); + 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 @@ -676,9 +703,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 @@ -696,6 +721,61 @@ xheader_destroy (struct xheader *xhdr) xhdr->size = 0; } + +/* Buildable strings */ +static uintmax_t string_length; + +void +xheader_string_begin () +{ + string_length = 0; +} + +void +xheader_string_add (char const *s) +{ + if (extended_header.buffer) + return; + extended_header_init (); + string_length += strlen (s); + x_obstack_grow (&extended_header, s, strlen (s)); +} + +void +xheader_string_end (char const *keyword) +{ + size_t len; + size_t p; + size_t n = 0; + char nbuf[UINTMAX_STRSIZE_BOUND]; + char const *np; + char *cp; + + if (extended_header.buffer) + return; + extended_header_init (); + + len = strlen (keyword) + 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; + x_obstack_blank (&extended_header, p); + x_obstack_1grow (&extended_header, '\n'); + cp = obstack_next_free (extended_header.stk) - string_length - p - 1; + memmove (cp + p, cp, string_length); + cp = stpcpy (cp, np); + *cp++ = ' '; + cp = stpcpy (cp, keyword); + *cp++ = '='; +} + /* Implementations */ @@ -747,23 +827,8 @@ decode_string (char **string, char const *arg) static void code_time (struct timespec t, char const *keyword, struct xheader *xhdr) { - time_t s = t.tv_sec; - int ns = t.tv_nsec; - char sbuf[1/*"-"*/ + UINTMAX_STRSIZE_BOUND + 1/*"."*/ + LOG10_BILLION]; - char *np; - bool negative = s < 0; - - if (negative && ns != 0) - { - s++; - ns = BILLION - ns; - } - - np = umaxtostr (negative ? - (uintmax_t) s : (uintmax_t) s, sbuf + 1); - if (negative) - *--np = '-'; - code_ns_fraction (ns, sbuf + UINTMAX_STRSIZE_BOUND); - xheader_print (xhdr, keyword, np); + char buf[TIMESPEC_STRSIZE_BOUND]; + xheader_print (xhdr, keyword, code_timespec (t, buf)); } static bool @@ -886,40 +951,43 @@ 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 *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 (get_stat_atime (&st->stat), 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 *arg, + size_t size __attribute__((unused))) { struct timespec ts; if (decode_time (&ts, arg, "atime")) - set_stat_atime (&st->stat, ts); + 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); } static void -gid_decoder (struct tar_stat_info *st, char const *arg) +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), "gid")) @@ -928,69 +996,74 @@ gid_decoder (struct tar_stat_info *st, char const *arg) 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 *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 *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 (get_stat_ctime (&st->stat), 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 *arg, + size_t size __attribute__((unused))) { struct timespec ts; if (decode_time (&ts, arg, "ctime")) - set_stat_ctime (&st->stat, ts); + 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 __attribute__ ((unused))) { - code_time (get_stat_mtime (&st->stat), keyword, xhdr); + code_time (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 *arg, + size_t size __attribute__((unused))) { struct timespec ts; if (decode_time (&ts, arg, "mtime")) - set_stat_mtime (&st->stat, ts); + 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 *arg, + size_t size __attribute__((unused))) { decode_string (&st->orig_file_name, arg); decode_string (&st->file_name, arg); @@ -999,13 +1072,14 @@ 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 *arg, + size_t size __attribute__((unused))) { uintmax_t u; if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), "size")) @@ -1014,13 +1088,14 @@ size_decoder (struct tar_stat_info *st, char const *arg) 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); } static void -uid_decoder (struct tar_stat_info *st, char const *arg) +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), "uid")) @@ -1029,26 +1104,28 @@ uid_decoder (struct tar_stat_info *st, char const *arg) 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 *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 *arg, + size_t size __attribute__((unused))) { uintmax_t u; if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), "GNU.sparse.size")) @@ -1058,13 +1135,14 @@ sparse_size_decoder (struct tar_stat_info *st, char const *arg) 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 *arg, + size_t size __attribute__((unused))) { uintmax_t u; if (decode_num (&u, arg, SIZE_MAX, "GNU.sparse.numblocks")) @@ -1077,14 +1155,15 @@ sparse_numblocks_decoder (struct tar_stat_info *st, char const *arg) 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 *pi = data; + 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 *arg, + size_t size __attribute__((unused))) { uintmax_t u; if (decode_num (&u, arg, TYPE_MAXIMUM (off_t), "GNU.sparse.offset")) @@ -1099,14 +1178,15 @@ sparse_offset_decoder (struct tar_stat_info *st, char const *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 *pi = data; + 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 *arg, + size_t size __attribute__((unused))) { uintmax_t u; if (decode_num (&u, arg, SIZE_MAX, "GNU.sparse.numbytes")) @@ -1119,6 +1199,92 @@ sparse_numbytes_decoder (struct tar_stat_info *st, char const *arg) } } +static void +sparse_map_decoder (struct tar_stat_info *st, char const *arg, + size_t size __attribute__((unused))) +{ + int offset = 1; + static char *keyword = "GNU.sparse.map"; + + st->sparse_map_avail = 0; + while (1) + { + uintmax_t u; + char *delim; + struct sp_array e; + + if (!ISDIGIT (*arg)) + { + ERROR ((0, 0, _("Malformed extended header: invalid %s=%s"), + keyword, arg)); + return; + } + + errno = 0; + u = strtoumax (arg, &delim, 10); + if (offset) + { + e.offset = u; + if (!(u == e.offset && errno != ERANGE)) + { + out_of_range_header (keyword, arg, 0, TYPE_MAXIMUM (off_t)); + return; + } + } + else + { + e.numbytes = u; + if (!(u == e.numbytes && errno != ERANGE)) + { + out_of_range_header (keyword, arg, 0, TYPE_MAXIMUM (size_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"), + "GNU.sparse.numbytes", 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 *arg, + size_t size) +{ + st->dumpdir = xmalloc (size); + memcpy (st->dumpdir, arg, size); +} + struct xhdr_tab const xhdr_tab[] = { { "atime", atime_coder, atime_decoder, false }, { "comment", dummy_coder, dummy_decoder, false }, @@ -1137,18 +1303,22 @@ struct xhdr_tab const xhdr_tab[] = { { "GNU.sparse.size", sparse_size_coder, sparse_size_decoder, true }, { "GNU.sparse.numblocks", sparse_numblocks_coder, sparse_numblocks_decoder, true }, + /* tar 1.14 - 1.15.1 keywords. Multiplse 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 }, { "GNU.sparse.numbytes", sparse_numbytes_coder, sparse_numbytes_decoder, true }, + /* tar >=1.16 keyword, introduced to remove the above-mentioned conflict. */ + { "GNU.sparse.map", NULL /* Unused, see pax_dump_header() */, + sparse_map_decoder, false }, -#if 0 /* GNU private keywords (not yet implemented) */ + { "GNU.dumpdir", dumpdir_coder, dumpdir_decoder, + true }, - /* 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 }, +#if 0 /* GNU private keywords (not yet implemented) */ /* Keeps the tape/volume header. May be present only in the global headers. Equivalent to GNUTYPE_VOLHDR. */