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

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