root/src/vfs/tar/tar-sparse.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. decode_num
  2. sparse_select_optab
  3. sparse_init
  4. sparse_done
  5. sparse_member_p
  6. sparse_fixup_header
  7. sparse_decode_header
  8. sparse_add_map
  9. oldgnu_add_sparse
  10. oldgnu_sparse_member_p
  11. oldgnu_fixup_header
  12. oldgnu_get_sparse_info
  13. star_sparse_member_p
  14. star_fixup_header
  15. star_get_sparse_info
  16. pax_sparse_member_p
  17. pax_decode_header
  18. tar_sparse_member_p
  19. tar_sparse_fixup_header
  20. tar_sparse_skip_file

   1 /*
   2    Virtual File System: GNU Tar file system.
   3 
   4    Copyright (C) 2003-2024
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Andrew Borodin <aborodin@vmail.ru>, 2023
   9 
  10    This file is part of the Midnight Commander.
  11 
  12    The Midnight Commander is free software: you can redistribute it
  13    and/or modify it under the terms of the GNU General Public License as
  14    published by the Free Software Foundation, either version 3 of the License,
  15    or (at your option) any later version.
  16 
  17    The Midnight Commander is distributed in the hope that it will be useful,
  18    but WITHOUT ANY WARRANTY; without even the implied warranty of
  19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  20    GNU General Public License for more details.
  21 
  22    You should have received a copy of the GNU General Public License
  23    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  24  */
  25 
  26 /**
  27  * \file
  28  * \brief Source: Virtual File System: GNU Tar file system
  29  */
  30 
  31 /*
  32  * Avoid following error:
  33  *   comparison of unsigned expression < 0 is always false [-Werror=type-limits]
  34  *
  35  * https://www.boost.org/doc/libs/1_55_0/libs/integer/test/cstdint_test.cpp
  36  *   We can't suppress this warning on the command line as not all GCC versions support -Wno-type-limits
  37  */
  38 #if defined(__GNUC__) && (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4))
  39 #pragma GCC diagnostic ignored "-Wtype-limits"
  40 #endif
  41 
  42 #include <config.h>
  43 
  44 #include <inttypes.h>           /* uintmax_t */
  45 
  46 #include "lib/global.h"
  47 
  48 #include "tar-internal.h"
  49 
  50 /* Old GNU Format.
  51    The sparse file information is stored in the oldgnu_header in the following manner:
  52 
  53    The header is marked with type 'S'. Its 'size' field contains the cumulative size
  54    of all non-empty blocks of the file. The actual file size is stored in `realsize'
  55    member of oldgnu_header.
  56 
  57    The map of the file is stored in a list of 'struct sparse'. Each struct contains
  58    offset to the block of data and its size (both as octal numbers). The first file
  59    header contains at most 4 such structs (SPARSES_IN_OLDGNU_HEADER). If the map
  60    contains more structs, then the field 'isextended' of the main header is set to
  61    1 (binary) and the 'struct sparse_header' header follows, containing at most
  62    21 following structs (SPARSES_IN_SPARSE_HEADER). If more structs follow, 'isextended'
  63    field of the extended header is set and next  next extension header follows, etc...
  64  */
  65 
  66 /*** global variables ****************************************************************************/
  67 
  68 /*** file scope macro definitions ****************************************************************/
  69 
  70 /* Bound on length of the string representing an unsigned integer
  71    value representable in B bits.  log10 (2.0) < 146/485.  The
  72    smallest value of B where this bound is not tight is 2621.  */
  73 #define INT_BITS_STRLEN_BOUND(b) (((b) * 146 + 484) / 485)
  74 
  75 /* Bound on buffer size needed to represent an integer type or expression T,
  76    including the terminating null.  T must not be a bit-field expression.  */
  77 #define INT_BUFSIZE_BOUND(t) (INT_STRLEN_BOUND (t) + 1)
  78 
  79 #define UINTMAX_STRSIZE_BOUND INT_BUFSIZE_BOUND (uintmax_t)
  80 
  81 #define SPARSES_INIT_COUNT SPARSES_IN_SPARSE_HEADER
  82 
  83 #define COPY_BUF(arch,b,buf,src)                                         \
  84 do                                                                       \
  85 {                                                                        \
  86     char *endp = b->buffer + BLOCKSIZE;                                  \
  87     char *dst = buf;                                                     \
  88     do                                                                   \
  89     {                                                                    \
  90         if (dst == buf + UINTMAX_STRSIZE_BOUND - 1)                      \
  91             /* numeric overflow in sparse archive member */              \
  92             return FALSE;                                                \
  93         if (src == endp)                                                 \
  94         {                                                                \
  95             tar_set_next_block_after (b);                                \
  96             b = tar_find_next_block (arch);                              \
  97             if (b == NULL)                                               \
  98                 /* unexpected EOF in archive */                          \
  99                 return FALSE;                                            \
 100             src = b->buffer;                                             \
 101             endp = b->buffer + BLOCKSIZE;                                \
 102         }                                                                \
 103         *dst = *src++;                                                   \
 104     }                                                                    \
 105     while (*dst++ != '\n');                                              \
 106     dst[-1] = '\0';                                                      \
 107 }                                                                        \
 108 while (FALSE)
 109 
 110 /*** file scope type declarations ****************************************************************/
 111 
 112 struct tar_sparse_file;
 113 
 114 struct tar_sparse_optab
 115 {
 116     gboolean (*init) (struct tar_sparse_file * file);
 117     gboolean (*done) (struct tar_sparse_file * file);
 118     gboolean (*sparse_member_p) (struct tar_sparse_file * file);
 119     gboolean (*fixup_header) (struct tar_sparse_file * file);
 120     gboolean (*decode_header) (tar_super_t * archive, struct tar_sparse_file * file);
 121 };
 122 
 123 struct tar_sparse_file
 124 {
 125     int fd;                     /**< File descriptor */
 126     off_t dumped_size;          /**< Number of bytes actually written to the archive */
 127     struct tar_stat_info *stat_info;    /**< Information about the file */
 128     struct tar_sparse_optab const *optab;
 129     void *closure;              /**< Any additional data optab calls might reqiure */
 130 };
 131 
 132 enum oldgnu_add_status
 133 {
 134     add_ok,
 135     add_finish,
 136     add_fail
 137 };
 138 
 139 /*** forward declarations (file scope functions) *************************************************/
 140 
 141 static gboolean oldgnu_sparse_member_p (struct tar_sparse_file *file);
 142 static gboolean oldgnu_fixup_header (struct tar_sparse_file *file);
 143 static gboolean oldgnu_get_sparse_info (tar_super_t * archive, struct tar_sparse_file *file);
 144 
 145 static gboolean star_sparse_member_p (struct tar_sparse_file *file);
 146 static gboolean star_fixup_header (struct tar_sparse_file *file);
 147 static gboolean star_get_sparse_info (tar_super_t * archive, struct tar_sparse_file *file);
 148 
 149 static gboolean pax_sparse_member_p (struct tar_sparse_file *file);
 150 static gboolean pax_decode_header (tar_super_t * archive, struct tar_sparse_file *file);
 151 
 152 /*** file scope variables ************************************************************************/
 153 
 154 /* *INDENT-OFF* */
 155 static struct tar_sparse_optab const oldgnu_optab =
 156 {
 157     .init = NULL,               /* No init function */
 158     .done = NULL,               /* No done function */
 159     .sparse_member_p = oldgnu_sparse_member_p,
 160     .fixup_header = oldgnu_fixup_header,
 161     .decode_header = oldgnu_get_sparse_info
 162 };
 163 /* *INDENT-ON* */
 164 
 165 /* *INDENT-OFF* */
 166 static struct tar_sparse_optab const star_optab =
 167 {
 168     .init = NULL,               /* No init function */
 169     .done = NULL,               /* No done function */
 170     .sparse_member_p = star_sparse_member_p,
 171     .fixup_header = star_fixup_header,
 172     .decode_header = star_get_sparse_info
 173 };
 174 /* *INDENT-ON* */
 175 
 176 /* GNU PAX sparse file format. There are several versions:
 177  * 0.0
 178 
 179  The initial version of sparse format used by tar 1.14-1.15.1.
 180  The sparse file map is stored in x header:
 181 
 182  GNU.sparse.size      Real size of the stored file
 183  GNU.sparse.numblocks Number of blocks in the sparse map repeat numblocks time
 184  GNU.sparse.offset    Offset of the next data block
 185  GNU.sparse.numbytes  Size of the next data block end repeat
 186 
 187  This has been reported as conflicting with the POSIX specs. The reason is
 188  that offsets and sizes of non-zero data blocks were stored in multiple instances
 189  of GNU.sparse.offset/GNU.sparse.numbytes variables, whereas POSIX requires the
 190  latest occurrence of the variable to override all previous occurrences.
 191 
 192  To avoid this incompatibility two following versions were introduced.
 193 
 194  * 0.1
 195 
 196  Used by tar 1.15.2 -- 1.15.91 (alpha releases).
 197 
 198  The sparse file map is stored in x header:
 199 
 200  GNU.sparse.size      Real size of the stored file
 201  GNU.sparse.numblocks Number of blocks in the sparse map
 202  GNU.sparse.map       Map of non-null data chunks. A string consisting of comma-separated
 203  values "offset,size[,offset,size]..."
 204 
 205  The resulting GNU.sparse.map string can be *very* long. While POSIX does not impose
 206  any limit on the length of a x header variable, this can confuse some tars.
 207 
 208  * 1.0
 209 
 210  Starting from this version, the exact sparse format version is specified explicitly
 211  in the header using the following variables:
 212 
 213  GNU.sparse.major     Major version 
 214  GNU.sparse.minor     Minor version
 215 
 216  X header keeps the following variables:
 217 
 218  GNU.sparse.name      Real file name of the sparse file
 219  GNU.sparse.realsize  Real size of the stored file (corresponds to the old GNU.sparse.size
 220  variable)
 221 
 222  The name field of the ustar header is constructed using the pattern "%d/GNUSparseFile.%p/%f".
 223 
 224  The sparse map itself is stored in the file data block, preceding the actual file data.
 225  It consists of a series of octal numbers of arbitrary length, delimited by newlines.
 226  The map is padded with nulls to the nearest block boundary.
 227 
 228  The first number gives the number of entries in the map. Following are map entries, each one
 229  consisting of two numbers giving the offset and size of the data block it describes.
 230 
 231  The format is designed in such a way that non-posix aware tars and tars not supporting
 232  GNU.sparse.* keywords will extract each sparse file in its condensed form with the file map
 233  attached and will place it into a separate directory. Then, using a simple program it would be
 234  possible to expand the file to its original form even without GNU tar.
 235 
 236  Bu default, v.1.0 archives are created. To use other formats, --sparse-version option is provided.
 237  Additionally, v.0.0 can be obtained by deleting GNU.sparse.map from 0.1 format:
 238  --sparse-version 0.1 --pax-option delete=GNU.sparse.map
 239  */
 240 
 241 static struct tar_sparse_optab const pax_optab = {
 242     .init = NULL,               /* No init function */
 243     .done = NULL,               /* No done function */
 244     .sparse_member_p = pax_sparse_member_p,
 245     .fixup_header = NULL,       /* No fixup_header function */
 246     .decode_header = pax_decode_header
 247 };
 248 
 249 /* --------------------------------------------------------------------------------------------- */
 250 /*** file scope functions ************************************************************************/
 251 /* --------------------------------------------------------------------------------------------- */
 252 
 253 static gboolean
 254 decode_num (uintmax_t *num, const char *arg, uintmax_t maxval)
     /* [previous][next][first][last][top][bottom][index][help]  */
 255 {
 256     char *arg_lim = NULL;
 257     gboolean overflow = FALSE;
 258 
 259     *num = stoint (arg, &arg_lim, &overflow, 0, maxval);
 260     return !(arg_lim == arg || *arg_lim != '\0' || overflow);
 261 }
 262 
 263 /* --------------------------------------------------------------------------------------------- */
 264 
 265 static gboolean
 266 sparse_select_optab (const tar_super_t *archive, struct tar_sparse_file *file)
     /* [previous][next][first][last][top][bottom][index][help]  */
 267 {
 268     switch (archive->type)
 269     {
 270     case TAR_V7:
 271     case TAR_USTAR:
 272         return FALSE;
 273 
 274     case TAR_OLDGNU:
 275     case TAR_GNU:              /* FIXME: This one should disappear? */
 276         file->optab = &oldgnu_optab;
 277         break;
 278 
 279     case TAR_POSIX:
 280         file->optab = &pax_optab;
 281         break;
 282 
 283     case TAR_STAR:
 284         file->optab = &star_optab;
 285         break;
 286 
 287     default:
 288         return FALSE;
 289     }
 290 
 291     return TRUE;
 292 }
 293 
 294 /* --------------------------------------------------------------------------------------------- */
 295 
 296 static gboolean
 297 sparse_init (tar_super_t *archive, struct tar_sparse_file *file)
     /* [previous][next][first][last][top][bottom][index][help]  */
 298 {
 299     memset (file, 0, sizeof (*file));
 300 
 301     if (!sparse_select_optab (archive, file))
 302         return FALSE;
 303 
 304     if (file->optab->init != NULL)
 305         return file->optab->init (file);
 306 
 307     return TRUE;
 308 }
 309 
 310 /* --------------------------------------------------------------------------------------------- */
 311 
 312 static gboolean
 313 sparse_done (struct tar_sparse_file *file)
     /* [previous][next][first][last][top][bottom][index][help]  */
 314 {
 315     if (file->optab->done != NULL)
 316         return file->optab->done (file);
 317 
 318     return TRUE;
 319 }
 320 
 321 /* --------------------------------------------------------------------------------------------- */
 322 
 323 static gboolean
 324 sparse_member_p (struct tar_sparse_file *file)
     /* [previous][next][first][last][top][bottom][index][help]  */
 325 {
 326     if (file->optab->sparse_member_p != NULL)
 327         return file->optab->sparse_member_p (file);
 328 
 329     return FALSE;
 330 }
 331 
 332 /* --------------------------------------------------------------------------------------------- */
 333 
 334 static gboolean
 335 sparse_fixup_header (struct tar_sparse_file *file)
     /* [previous][next][first][last][top][bottom][index][help]  */
 336 {
 337     if (file->optab->fixup_header != NULL)
 338         return file->optab->fixup_header (file);
 339 
 340     return TRUE;
 341 }
 342 
 343 /* --------------------------------------------------------------------------------------------- */
 344 
 345 static gboolean
 346 sparse_decode_header (tar_super_t *archive, struct tar_sparse_file *file)
     /* [previous][next][first][last][top][bottom][index][help]  */
 347 {
 348     if (file->optab->decode_header != NULL)
 349         return file->optab->decode_header (archive, file);
 350 
 351     return TRUE;
 352 }
 353 
 354 /* --------------------------------------------------------------------------------------------- */
 355 
 356 static inline void
 357 sparse_add_map (struct tar_stat_info *st, struct sp_array *sp)
     /* [previous][next][first][last][top][bottom][index][help]  */
 358 {
 359     if (st->sparse_map == NULL)
 360         st->sparse_map = g_array_sized_new (FALSE, FALSE, sizeof (struct sp_array), 1);
 361     g_array_append_val (st->sparse_map, *sp);
 362 }
 363 
 364 /* --------------------------------------------------------------------------------------------- */
 365 
 366 /**
 367  * Add a sparse item to the sparse file
 368  */
 369 static enum oldgnu_add_status
 370 oldgnu_add_sparse (struct tar_sparse_file *file, struct sparse *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 371 {
 372     struct sp_array sp;
 373     off_t size;
 374 
 375     if (s->numbytes[0] == '\0')
 376         return add_finish;
 377 
 378     sp.offset = OFF_FROM_HEADER (s->offset);
 379     sp.numbytes = OFF_FROM_HEADER (s->numbytes);
 380 
 381     if (sp.offset < 0 || sp.numbytes < 0 || ckd_add (&size, sp.offset, sp.numbytes)
 382         || file->stat_info->stat.st_size < size || file->stat_info->archive_file_size < 0)
 383         return add_fail;
 384 
 385     sparse_add_map (file->stat_info, &sp);
 386 
 387     return add_ok;
 388 }
 389 
 390 /* --------------------------------------------------------------------------------------------- */
 391 
 392 static gboolean
 393 oldgnu_sparse_member_p (struct tar_sparse_file *file)
     /* [previous][next][first][last][top][bottom][index][help]  */
 394 {
 395     (void) file;
 396 
 397     return current_header->header.typeflag == GNUTYPE_SPARSE;
 398 }
 399 
 400 /* --------------------------------------------------------------------------------------------- */
 401 
 402 static gboolean
 403 oldgnu_fixup_header (struct tar_sparse_file *file)
     /* [previous][next][first][last][top][bottom][index][help]  */
 404 {
 405     /* NOTE! st_size was initialized from the header which actually contains archived size.
 406        The following fixes it */
 407     off_t realsize;
 408 
 409     realsize = OFF_FROM_HEADER (current_header->oldgnu_header.realsize);
 410     file->stat_info->archive_file_size = file->stat_info->stat.st_size;
 411     file->stat_info->stat.st_size = MAX (0, realsize);
 412 
 413     return (realsize >= 0);
 414 }
 415 
 416 /* --------------------------------------------------------------------------------------------- */
 417 
 418 /**
 419  * Convert old GNU format sparse data to internal representation.
 420  */
 421 static gboolean
 422 oldgnu_get_sparse_info (tar_super_t *archive, struct tar_sparse_file *file)
     /* [previous][next][first][last][top][bottom][index][help]  */
 423 {
 424     size_t i;
 425     union block *h = current_header;
 426     gboolean ext_p;
 427     enum oldgnu_add_status rc = add_fail;
 428 
 429     if (file->stat_info->sparse_map != NULL)
 430         g_array_set_size (file->stat_info->sparse_map, 0);
 431 
 432     for (i = 0; i < SPARSES_IN_OLDGNU_HEADER; i++)
 433     {
 434         rc = oldgnu_add_sparse (file, &h->oldgnu_header.sp[i]);
 435         if (rc != add_ok)
 436             break;
 437     }
 438 
 439     for (ext_p = h->oldgnu_header.isextended != 0; rc == add_ok && ext_p;
 440          ext_p = h->sparse_header.isextended != 0)
 441     {
 442         h = tar_find_next_block (archive);
 443         if (h == NULL)
 444             return FALSE;
 445 
 446         tar_set_next_block_after (h);
 447 
 448         for (i = 0; i < SPARSES_IN_SPARSE_HEADER && rc == add_ok; i++)
 449             rc = oldgnu_add_sparse (file, &h->sparse_header.sp[i]);
 450     }
 451 
 452     return (rc != add_fail);
 453 }
 454 
 455 /* --------------------------------------------------------------------------------------------- */
 456 
 457 static gboolean
 458 star_sparse_member_p (struct tar_sparse_file *file)
     /* [previous][next][first][last][top][bottom][index][help]  */
 459 {
 460     (void) file;
 461 
 462     return current_header->header.typeflag == GNUTYPE_SPARSE;
 463 }
 464 
 465 /* --------------------------------------------------------------------------------------------- */
 466 
 467 static gboolean
 468 star_fixup_header (struct tar_sparse_file *file)
     /* [previous][next][first][last][top][bottom][index][help]  */
 469 {
 470     /* NOTE! st_size was initialized from the header which actually contains archived size.
 471        The following fixes it */
 472     off_t realsize;
 473 
 474     realsize = OFF_FROM_HEADER (current_header->star_in_header.realsize);
 475     file->stat_info->archive_file_size = file->stat_info->stat.st_size;
 476     file->stat_info->stat.st_size = MAX (0, realsize);
 477 
 478     return (realsize >= 0);
 479 }
 480 
 481 /* --------------------------------------------------------------------------------------------- */
 482 
 483 /**
 484  * Convert STAR format sparse data to internal representation
 485  */
 486 static gboolean
 487 star_get_sparse_info (tar_super_t *archive, struct tar_sparse_file *file)
     /* [previous][next][first][last][top][bottom][index][help]  */
 488 {
 489     size_t i;
 490     union block *h = current_header;
 491     gboolean ext_p = TRUE;
 492     enum oldgnu_add_status rc = add_ok;
 493 
 494     if (file->stat_info->sparse_map != NULL)
 495         g_array_set_size (file->stat_info->sparse_map, 0);
 496 
 497     if (h->star_in_header.prefix[0] == '\0' && h->star_in_header.sp[0].offset[10] != '\0')
 498     {
 499         /* Old star format */
 500         for (i = 0; i < SPARSES_IN_STAR_HEADER; i++)
 501         {
 502             rc = oldgnu_add_sparse (file, &h->star_in_header.sp[i]);
 503             if (rc != add_ok)
 504                 break;
 505         }
 506 
 507         ext_p = h->star_in_header.isextended != 0;
 508     }
 509 
 510     for (; rc == add_ok && ext_p; ext_p = h->star_ext_header.isextended != 0)
 511     {
 512         h = tar_find_next_block (archive);
 513         if (h == NULL)
 514             return FALSE;
 515 
 516         tar_set_next_block_after (h);
 517 
 518         for (i = 0; i < SPARSES_IN_STAR_EXT_HEADER && rc == add_ok; i++)
 519             rc = oldgnu_add_sparse (file, &h->star_ext_header.sp[i]);
 520 
 521         file->dumped_size += BLOCKSIZE;
 522     }
 523 
 524     return (rc != add_fail);
 525 }
 526 
 527 /* --------------------------------------------------------------------------------------------- */
 528 
 529 static gboolean
 530 pax_sparse_member_p (struct tar_sparse_file *file)
     /* [previous][next][first][last][top][bottom][index][help]  */
 531 {
 532     return file->stat_info->sparse_map != NULL && file->stat_info->sparse_map->len > 0
 533         && file->stat_info->sparse_major > 0;
 534 }
 535 
 536 /* --------------------------------------------------------------------------------------------- */
 537 
 538 static gboolean
 539 pax_decode_header (tar_super_t *archive, struct tar_sparse_file *file)
     /* [previous][next][first][last][top][bottom][index][help]  */
 540 {
 541     if (file->stat_info->sparse_major > 0)
 542     {
 543         uintmax_t u;
 544         char nbuf[UINTMAX_STRSIZE_BOUND];
 545         union block *blk;
 546         char *p;
 547         size_t sparse_map_len;
 548         size_t i;
 549         off_t start;
 550 
 551         start = tar_current_block_ordinal (archive);
 552         tar_set_next_block_after (current_header);
 553         blk = tar_find_next_block (archive);
 554         if (blk == NULL)
 555             /* unexpected EOF in archive */
 556             return FALSE;
 557         p = blk->buffer;
 558         COPY_BUF (archive, blk, nbuf, p);
 559 
 560         if (!decode_num (&u, nbuf, SIZE_MAX))
 561         {
 562             /* malformed sparse archive member */
 563             return FALSE;
 564         }
 565 
 566         if (file->stat_info->sparse_map == NULL)
 567             file->stat_info->sparse_map =
 568                 g_array_sized_new (FALSE, FALSE, sizeof (struct sp_array), u);
 569         else
 570             g_array_set_size (file->stat_info->sparse_map, u);
 571 
 572         sparse_map_len = u;
 573 
 574         for (i = 0; i < sparse_map_len; i++)
 575         {
 576             struct sp_array sp;
 577             off_t size;
 578 
 579             COPY_BUF (archive, blk, nbuf, p);
 580             if (!decode_num (&u, nbuf, TYPE_MAXIMUM (off_t)))
 581             {
 582                 /* malformed sparse archive member */
 583                 return FALSE;
 584             }
 585             sp.offset = u;
 586             COPY_BUF (archive, blk, nbuf, p);
 587             if (!decode_num (&u, nbuf, TYPE_MAXIMUM (off_t)) || ckd_add (&size, sp.offset, u)
 588                 || file->stat_info->stat.st_size < size)
 589             {
 590                 /* malformed sparse archive member */
 591                 return FALSE;
 592             }
 593             sp.numbytes = u;
 594             sparse_add_map (file->stat_info, &sp);
 595         }
 596 
 597         tar_set_next_block_after (blk);
 598 
 599         file->dumped_size += BLOCKSIZE * (tar_current_block_ordinal (archive) - start);
 600     }
 601 
 602     return TRUE;
 603 }
 604 
 605 /* --------------------------------------------------------------------------------------------- */
 606 /*** public functions ****************************************************************************/
 607 /* --------------------------------------------------------------------------------------------- */
 608 
 609 gboolean
 610 tar_sparse_member_p (tar_super_t *archive, struct tar_stat_info *st)
     /* [previous][next][first][last][top][bottom][index][help]  */
 611 {
 612     struct tar_sparse_file file;
 613 
 614     if (!sparse_init (archive, &file))
 615         return FALSE;
 616 
 617     file.stat_info = st;
 618     return sparse_member_p (&file);
 619 }
 620 
 621 /* --------------------------------------------------------------------------------------------- */
 622 
 623 gboolean
 624 tar_sparse_fixup_header (tar_super_t *archive, struct tar_stat_info *st)
     /* [previous][next][first][last][top][bottom][index][help]  */
 625 {
 626     struct tar_sparse_file file;
 627 
 628     if (!sparse_init (archive, &file))
 629         return FALSE;
 630 
 631     file.stat_info = st;
 632     return sparse_fixup_header (&file);
 633 }
 634 
 635 /* --------------------------------------------------------------------------------------------- */
 636 
 637 enum dump_status
 638 tar_sparse_skip_file (tar_super_t *archive, struct tar_stat_info *st)
     /* [previous][next][first][last][top][bottom][index][help]  */
 639 {
 640     gboolean rc = TRUE;
 641     struct tar_sparse_file file;
 642 
 643     if (!sparse_init (archive, &file))
 644         return dump_status_not_implemented;
 645 
 646     file.stat_info = st;
 647     file.fd = -1;
 648 
 649     rc = sparse_decode_header (archive, &file);
 650     (void) tar_skip_file (archive, file.stat_info->archive_file_size - file.dumped_size);
 651     return (sparse_done (&file) && rc) ? dump_status_ok : dump_status_short;
 652 }
 653 
 654 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */