diff options
Diffstat (limited to 'src/vfs/tar/tar-xheader.c')
-rw-r--r-- | src/vfs/tar/tar-xheader.c | 1051 |
1 files changed, 1051 insertions, 0 deletions
diff --git a/src/vfs/tar/tar-xheader.c b/src/vfs/tar/tar-xheader.c new file mode 100644 index 0000000..5062ed1 --- /dev/null +++ b/src/vfs/tar/tar-xheader.c @@ -0,0 +1,1051 @@ +/* + Virtual File System: GNU Tar file system. + + Copyright (C) 1995-2023 + Free Software Foundation, Inc. + + Written by: + Andrew Borodin <aborodin@vmail.ru>, 2023 + + This file is part of the Midnight Commander. + + The Midnight Commander 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. + + The Midnight Commander 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/>. + */ + +/** + * \file + * \brief Source: Virtual File System: GNU Tar file system + */ + +#include <config.h> + +#include <ctype.h> /* isdigit() */ +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include "lib/global.h" +#include "lib/util.h" /* MC_PTR_FREE */ + +#include "tar-internal.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +#define XHDR_PROTECTED 0x01 +#define XHDR_GLOBAL 0x02 + +/*** file scope type declarations ****************************************************************/ + +/* General Interface */ + +/* Since tar VFS is read-only, inplement decodes only */ +/* *INDENT-OFF* */ +struct xhdr_tab +{ + const char *keyword; + gboolean (*decoder) (struct tar_stat_info * st, const char *keyword, const char *arg, size_t size); + int flags; +}; +/* *INDENT-ON* */ + +/* Keyword options */ +struct keyword_item +{ + char *pattern; + char *value; +}; + +enum decode_record_status +{ + decode_record_ok, + decode_record_finish, + decode_record_fail +}; + +/*** forward declarations (file scope functions) *************************************************/ + +static gboolean dummy_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +static gboolean atime_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +static gboolean gid_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +#if 0 +static gboolean gname_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +#endif +static gboolean linkpath_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +static gboolean mtime_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +static gboolean ctime_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +static gboolean path_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +static gboolean size_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +static gboolean uid_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +#if 0 +static gboolean uname_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +#endif +static gboolean sparse_path_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +static gboolean sparse_major_decoder (struct tar_stat_info *st, const char *keyword, + const char *arg, size_t size); +static gboolean sparse_minor_decoder (struct tar_stat_info *st, const char *keyword, + const char *arg, size_t size); +static gboolean sparse_size_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +static gboolean sparse_numblocks_decoder (struct tar_stat_info *st, const char *keyword, + const char *arg, size_t size); +static gboolean sparse_offset_decoder (struct tar_stat_info *st, const char *keyword, + const char *arg, size_t size); +static gboolean sparse_numbytes_decoder (struct tar_stat_info *st, const char *keyword, + const char *arg, size_t size); +static gboolean sparse_map_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); +static gboolean dumpdir_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size); + +/*** file scope variables ************************************************************************/ + +enum +{ + BILLION = 1000000000, + LOG10_BILLION = 9 +}; + +/* *INDENT-OFF* */ +static struct xhdr_tab xhdr_tab[] = +{ + { "atime", atime_decoder, 0 }, + { "comment", dummy_decoder, 0 }, + { "charset", dummy_decoder, 0 }, + { "ctime", ctime_decoder, 0 }, + { "gid", gid_decoder, 0 }, +#if 0 + { "gname", gname_decoder, 0 }, +#endif + { "linkpath", linkpath_decoder, 0 }, + { "mtime", mtime_decoder, 0 }, + { "path", path_decoder, 0 }, + { "size", size_decoder, 0 }, + { "uid", uid_decoder, 0 }, +#if 0 + { "uname", uname_decoder, 0 }, +#endif + + /* Sparse file handling */ + { "GNU.sparse.name", sparse_path_decoder, XHDR_PROTECTED }, + { "GNU.sparse.major", sparse_major_decoder, XHDR_PROTECTED }, + { "GNU.sparse.minor", sparse_minor_decoder, XHDR_PROTECTED }, + { "GNU.sparse.realsize", sparse_size_decoder, XHDR_PROTECTED }, + { "GNU.sparse.numblocks", sparse_numblocks_decoder, XHDR_PROTECTED }, + + { "GNU.sparse.size", sparse_size_decoder, XHDR_PROTECTED }, + /* 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_decoder, XHDR_PROTECTED }, + { "GNU.sparse.numbytes", sparse_numbytes_decoder, XHDR_PROTECTED }, + /* tar 1.15.90 keyword, introduced to remove the above-mentioned conflict. */ + { "GNU.sparse.map", sparse_map_decoder, 0 }, + + { "GNU.dumpdir", dumpdir_decoder, XHDR_PROTECTED }, + + { NULL, NULL, 0 } +}; +/* *INDENT-ON* */ + +/* List of keyword/value pairs decoded from the last 'g' type header */ +static GSList *global_header_override_list = NULL; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +/* Convert a prefix of the string @arg to a system integer type whose minimum value is @minval + and maximum @maxval. If @minval is negative, negative integers @minval .. -1 are assumed + to be represented using leading '-' in the usual way. If the represented value exceeds INTMAX_MAX, + return a negative integer V such that (uintmax_t) V yields the represented value. If @arglim is + nonnull, store into *@arglim a pointer to the first character after the prefix. + + This is the inverse of sysinttostr. + + On a normal return, set errno = 0. + On conversion error, return 0 and set errno = EINVAL. + On overflow, return an extreme value and set errno = ERANGE. + */ +#if ! (INTMAX_MAX <= UINTMAX_MAX) +#error "strtosysint: nonnegative intmax_t does not fit in uintmax_t" +#endif +static intmax_t +strtosysint (const char *arg, char **arglim, intmax_t minval, uintmax_t maxval) +{ + errno = 0; + + if (maxval <= INTMAX_MAX) + { + if (isdigit (arg[*arg == '-' ? 1 : 0])) + { + gint64 i; + + i = g_ascii_strtoll (arg, arglim, 10); + if ((gint64) minval <= i && i <= (gint64) maxval) + return (intmax_t) i; + + errno = ERANGE; + return i < (gint64) minval ? minval : (intmax_t) maxval; + } + } + else + { + if (isdigit (*arg)) + { + guint64 i; + + i = g_ascii_strtoull (arg, arglim, 10); + if (i <= (guint64) maxval) + return tar_represent_uintmax ((uintmax_t) i); + + errno = ERANGE; + return maxval; + } + } + + errno = EINVAL; + return 0; +} + +/* --------------------------------------------------------------------------------------------- */ + +static struct xhdr_tab * +locate_handler (const char *keyword) +{ + struct xhdr_tab *p; + + for (p = xhdr_tab; p->keyword != NULL; p++) + if (strcmp (p->keyword, keyword) == 0) + return p; + + return NULL; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +keyword_item_run (gpointer data, gpointer user_data) +{ + struct keyword_item *kp = (struct keyword_item *) data; + struct tar_stat_info *st = (struct tar_stat_info *) user_data; + struct xhdr_tab const *t; + + t = locate_handler (kp->pattern); + if (t != NULL) + return t->decoder (st, t->keyword, kp->value, strlen (kp->value)); + + return TRUE; /* FIXME */ +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +keyword_item_free (gpointer data) +{ + struct keyword_item *kp = (struct keyword_item *) data; + + g_free (kp->pattern); + g_free (kp->value); + g_free (kp); +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +xheader_list_append (GSList ** root, const char *kw, const char *value) +{ + struct keyword_item *kp; + + kp = g_new (struct keyword_item, 1); + kp->pattern = g_strdup (kw); + kp->value = g_strdup (value); + *root = g_slist_prepend (*root, kp); +} + +/* --------------------------------------------------------------------------------------------- */ + +static inline void +xheader_list_destroy (GSList ** root) +{ + g_slist_free_full (*root, keyword_item_free); + *root = NULL; +} + +/* --------------------------------------------------------------------------------------------- */ + +static inline void +run_override_list (GSList * kp, struct tar_stat_info *st) +{ + g_slist_foreach (kp, (GFunc) keyword_item_run, st); +} + +/* --------------------------------------------------------------------------------------------- */ + +static struct timespec +decode_timespec (const char *arg, char **arg_lim, gboolean parse_fraction) +{ + time_t s = TYPE_MINIMUM (time_t); + int ns = -1; + const char *p = arg; + gboolean negative = *arg == '-'; + struct timespec r; + + if (!isdigit (arg[negative])) + errno = EINVAL; + else + { + errno = 0; + + if (negative) + { + gint64 i; + + i = g_ascii_strtoll (arg, arg_lim, 10); + if (TYPE_SIGNED (time_t) ? TYPE_MINIMUM (time_t) <= i : 0 <= i) + s = (intmax_t) i; + else + errno = ERANGE; + } + else + { + guint64 i; + + i = g_ascii_strtoull (arg, arg_lim, 10); + if (i <= TYPE_MAXIMUM (time_t)) + s = (uintmax_t) i; + else + errno = ERANGE; + } + + p = *arg_lim; + ns = 0; + + if (parse_fraction && *p == '.') + { + int digits = 0; + gboolean trailing_nonzero = FALSE; + + while (isdigit (*++p)) + if (digits < LOG10_BILLION) + { + digits++; + ns = 10 * ns + (*p - '0'); + } + else if (*p != '0') + trailing_nonzero = TRUE; + + while (digits < LOG10_BILLION) + { + digits++; + 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. */ + if (trailing_nonzero) + ns++; + if (ns != 0) + { + if (s == TYPE_MINIMUM (time_t)) + ns = -1; + else + { + s--; + ns = BILLION - ns; + } + } + } + } + + if (errno == ERANGE) + ns = -1; + } + + *arg_lim = (char *) p; + r.tv_sec = s; + r.tv_nsec = ns; + return r; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +decode_time (struct timespec *ts, const char *arg, const char *keyword) +{ + char *arg_lim; + struct timespec t; + + (void) keyword; + + t = decode_timespec (arg, &arg_lim, TRUE); + + if (t.tv_nsec < 0) + /* Malformed extended header */ + return FALSE; + + if (*arg_lim != '\0') + /* Malformed extended header */ + return FALSE; + + *ts = t; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +decode_signed_num (intmax_t * num, const char *arg, intmax_t minval, uintmax_t maxval, + const char *keyword) +{ + char *arg_lim; + intmax_t u; + + (void) keyword; + + if (!isdigit (*arg)) + return FALSE; /* malformed extended header */ + + u = strtosysint (arg, &arg_lim, minval, maxval); + + if (errno == EINVAL || *arg_lim != '\0') + return FALSE; /* malformed extended header */ + + if (errno == ERANGE) + return FALSE; /* out of range */ + + *num = u; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +decode_num (uintmax_t * num, const char *arg, uintmax_t maxval, const char *keyword) +{ + intmax_t i; + + if (!decode_signed_num (&i, arg, 0, maxval, keyword)) + return FALSE; + + *num = i; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +raw_path_decoder (struct tar_stat_info *st, const char *arg) +{ + if (*arg != '\0') + { + tar_assign_string_dup (&st->orig_file_name, arg); + tar_assign_string_dup (&st->file_name, arg); + } + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +dummy_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + (void) st; + (void) keyword; + (void) arg; + (void) size; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +atime_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + struct timespec ts; + + (void) size; + + if (!decode_time (&ts, arg, keyword)) + return FALSE; + + st->atime = ts; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +gid_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + intmax_t u; + + (void) size; + + if (!decode_signed_num (&u, arg, TYPE_MINIMUM (gid_t), TYPE_MINIMUM (gid_t), keyword)) + return FALSE; + + st->stat.st_gid = u; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +#if 0 +static gboolean +gname_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + (void) keyword; + (void) size; + + tar_assign_string_dup (&st->gname, arg); + return TRUE; +} +#endif + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +linkpath_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + (void) keyword; + (void) size; + + tar_assign_string_dup (&st->link_name, arg); + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +ctime_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + struct timespec ts; + + (void) size; + + if (!decode_time (&ts, arg, keyword)) + return FALSE; + + st->ctime = ts; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +mtime_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + struct timespec ts; + + (void) size; + + if (!decode_time (&ts, arg, keyword)) + return FALSE; + + st->mtime = ts; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +path_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + (void) keyword; + (void) size; + + if (!st->sparse_name_done) + return raw_path_decoder (st, arg); + + return TRUE; /* FIXME */ +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +size_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + uintmax_t u; + + (void) size; + + if (!decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword)) + return FALSE; + + st->stat.st_size = u; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +uid_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + intmax_t u; + + (void) size; + + if (!decode_signed_num (&u, arg, TYPE_MINIMUM (uid_t), TYPE_MAXIMUM (uid_t), keyword)) + return FALSE; + + st->stat.st_uid = u; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +#if 0 +static gboolean +uname_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + (void) keyword; + (void) size; + + tar_assign_string_dup (&st->uname, arg); + return TRUE; +} +#endif + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +dumpdir_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + (void) keyword; + +#if GLIB_CHECK_VERSION (2, 68, 0) + st->dumpdir = g_memdup2 (arg, size); +#else + st->dumpdir = g_memdup (arg, size); +#endif + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +/** + * Decodes a single extended header record, advancing @ptr to the next record. + * + * @param p pointer to extended header record + * @param st stat info + * + * @return decode_record_ok or decode_record_finish on success, decode_record_fail otherwize + */ +static enum decode_record_status +decode_record (struct xheader *xhdr, char **ptr, + gboolean (*handler) (void *data, const char *keyword, const char *value, + size_t size), void *data) +{ + char *start = *ptr; + char *p = start; + size_t len; + char *len_lim; + const char *keyword; + char *nextp; + size_t len_max; + gboolean ret; + + len_max = xhdr->buffer + xhdr->size - start; + + while (*p == ' ' || *p == '\t') + p++; + + if (!isdigit (*p)) + return (*p != '\0' ? decode_record_fail : decode_record_finish); + + len = (uintmax_t) g_ascii_strtoull (p, &len_lim, 10); + if (len_max < len) + return decode_record_fail; + + nextp = start + len; + + for (p = len_lim; *p == ' ' || *p == '\t'; p++) + ; + + if (p == len_lim) + return decode_record_fail; + + keyword = p; + p = strchr (p, '='); + if (!(p != NULL && p < nextp)) + return decode_record_fail; + + if (nextp[-1] != '\n') + return decode_record_fail; + + *p = nextp[-1] = '\0'; + ret = handler (data, keyword, p + 1, nextp - p - 2); /* '=' + trailing '\n' */ + *p = '='; + nextp[-1] = '\n'; + *ptr = nextp; + + return (ret ? decode_record_ok : decode_record_fail); +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +decg (void *data, const char *keyword, const char *value, size_t size) +{ + GSList **kwl = (GSList **) data; + struct xhdr_tab const *tab; + + (void) size; + + tab = locate_handler (keyword); + if (tab != NULL && (tab->flags & XHDR_GLOBAL) != 0) + { + if (!tab->decoder (data, keyword, value, size)) + return FALSE; + } + else + xheader_list_append (kwl, keyword, value); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +decx (void *data, const char *keyword, const char *value, size_t size) +{ + struct keyword_item kp = { + .pattern = (char *) keyword, + .value = (char *) value + }; + + (void) size; + + return keyword_item_run (&kp, data); +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +sparse_path_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + (void) keyword; + (void) size; + + st->sparse_name_done = TRUE; + return raw_path_decoder (st, arg); +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +sparse_major_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + uintmax_t u; + + (void) size; + + if (!decode_num (&u, arg, TYPE_MAXIMUM (unsigned), keyword)) + return FALSE; + + st->sparse_major = u; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +sparse_minor_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + uintmax_t u; + + (void) size; + + if (!decode_num (&u, arg, TYPE_MAXIMUM (unsigned), keyword)) + return FALSE; + + st->sparse_minor = u; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +sparse_size_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + uintmax_t u; + + (void) size; + + if (!decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword)) + return FALSE; + + st->real_size_set = TRUE; + st->real_size = u; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +sparse_numblocks_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size) +{ + uintmax_t u; + + (void) size; + + if (!decode_num (&u, arg, SIZE_MAX, keyword)) + return FALSE; + + if (st->sparse_map == NULL) + st->sparse_map = g_array_sized_new (FALSE, FALSE, sizeof (struct sp_array), u); + else + g_array_set_size (st->sparse_map, u); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +sparse_offset_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + uintmax_t u; + struct sp_array *s; + + (void) size; + + if (!decode_num (&u, arg, TYPE_MAXIMUM (off_t), keyword)) + return FALSE; + + s = &g_array_index (st->sparse_map, struct sp_array, st->sparse_map->len - 1); + s->offset = u; + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +sparse_numbytes_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, + size_t size) +{ + uintmax_t u; + struct sp_array s; + + (void) size; + + if (!decode_num (&u, arg, SIZE_MAX, keyword)) + return FALSE; + + s.offset = 0; + s.numbytes = u; + g_array_append_val (st->sparse_map, s); + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +static gboolean +sparse_map_decoder (struct tar_stat_info *st, const char *keyword, const char *arg, size_t size) +{ + gboolean offset = TRUE; + struct sp_array e; + + (void) keyword; + (void) size; + + if (st->sparse_map != NULL) + g_array_set_size (st->sparse_map, 0); + + while (TRUE) + { + gint64 u; + char *delim; + + if (!isdigit (*arg)) + { + /* malformed extended header */ + return FALSE; + } + + errno = 0; + u = g_ascii_strtoll (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 */ + return FALSE; + } + } + else + { + e.numbytes = u; + if (errno == ERANGE) + { + /* out of range */ + return FALSE; + } + + g_array_append_val (st->sparse_map, e); + } + + offset = !offset; + + if (*delim == '\0') + break; + if (*delim != ',') + { + /* malformed extended header */ + return FALSE; + } + + arg = delim + 1; + } + + if (!offset) + { + /* malformed extended header */ + return FALSE; + } + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +/** + * Decodes an extended header. + * + * @param st stat info + * + * @return TRUE on success, FALSE otherwize + */ +gboolean +tar_xheader_decode (struct tar_stat_info * st) +{ + char *p; + enum decode_record_status status; + + run_override_list (global_header_override_list, st); + + p = st->xhdr.buffer + BLOCKSIZE; + + while ((status = decode_record (&st->xhdr, &p, decx, st)) == decode_record_ok) + ; + + if (status == decode_record_fail) + return FALSE; + + /* The archived (effective) file size is always set directly in tar header + field, possibly overridden by "size" extended header - in both cases, + result is now decoded in st->stat.st_size */ + st->archive_file_size = st->stat.st_size; + + /* The real file size (given by stat()) may be redefined for sparse + files in "GNU.sparse.realsize" extended header */ + if (st->real_size_set) + st->stat.st_size = st->real_size; + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +gboolean +tar_xheader_read (tar_super_t * archive, struct xheader * xhdr, union block * p, off_t size) +{ + size_t j = 0; + + size = MAX (0, size); + size += BLOCKSIZE; + + xhdr->size = size; + xhdr->buffer = g_malloc (size + 1); + xhdr->buffer[size] = '\0'; + + do + { + size_t len; + + if (p == NULL) + return FALSE; /* Unexpected EOF in archive */ + + len = MIN (size, BLOCKSIZE); + + memcpy (xhdr->buffer + j, p->buffer, len); + tar_set_next_block_after (p); + p = tar_find_next_block (archive); + + j += len; + size -= len; + } + while (size > 0); + + return TRUE; +} + +/* --------------------------------------------------------------------------------------------- */ + +gboolean +tar_xheader_decode_global (struct xheader * xhdr) +{ + char *p; + gboolean ret; + + p = xhdr->buffer + BLOCKSIZE; + + xheader_list_destroy (&global_header_override_list); + + while ((ret = decode_record (xhdr, &p, decg, &global_header_override_list)) == decode_record_ok) + ; + + return (ret == decode_record_finish); +} + +/* --------------------------------------------------------------------------------------------- */ + +void +tar_xheader_destroy (struct xheader *xhdr) +{ + MC_PTR_FREE (xhdr->buffer); + xhdr->size = 0; +} + +/* --------------------------------------------------------------------------------------------- */ |