root/src/vfs/tar/tar.c

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

DEFINITIONS

This source file includes following definitions.
  1. tar_from_oct
  2. tar_new_archive
  3. tar_free_archive
  4. tar_open_archive_int
  5. tar_get_next_block
  6. tar_skip_n_records
  7. tar_checksum
  8. tar_decode_header
  9. tar_fill_stat
  10. tar_read_header
  11. tar_open_archive
  12. tar_super_check
  13. tar_super_same
  14. tar_read
  15. tar_fh_open
  16. vfs_init_tarfs

   1 /*
   2    Virtual File System: GNU Tar file system.
   3 
   4    Copyright (C) 1995-2023
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Jakub Jelinek, 1995
   9    Pavel Machek, 1998
  10    Slava Zanko <slavazanko@gmail.com>, 2013
  11 
  12    This file is part of the Midnight Commander.
  13 
  14    The Midnight Commander is free software: you can redistribute it
  15    and/or modify it under the terms of the GNU General Public License as
  16    published by the Free Software Foundation, either version 3 of the License,
  17    or (at your option) any later version.
  18 
  19    The Midnight Commander is distributed in the hope that it will be useful,
  20    but WITHOUT ANY WARRANTY; without even the implied warranty of
  21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22    GNU General Public License for more details.
  23 
  24    You should have received a copy of the GNU General Public License
  25    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  26  */
  27 
  28 /**
  29  * \file
  30  * \brief Source: Virtual File System: GNU Tar file system
  31  * \author Jakub Jelinek
  32  * \author Pavel Machek
  33  * \date 1995, 1998
  34  *
  35  * Namespace: init_tarfs
  36  */
  37 
  38 #include <config.h>
  39 #include <sys/types.h>
  40 #include <errno.h>
  41 #include <ctype.h>
  42 
  43 #ifdef hpux
  44 /* major() and minor() macros (among other things) defined here for hpux */
  45 #include <sys/mknod.h>
  46 #endif
  47 
  48 #include "lib/global.h"
  49 #include "lib/util.h"
  50 #include "lib/unixcompat.h"     /* makedev() */
  51 #include "lib/widget.h"         /* message() */
  52 
  53 #include "lib/vfs/vfs.h"
  54 #include "lib/vfs/utilvfs.h"
  55 #include "lib/vfs/xdirentry.h"
  56 #include "lib/vfs/gc.h"         /* vfs_rmstamp */
  57 
  58 #include "tar.h"
  59 
  60 /*** global variables ****************************************************************************/
  61 
  62 /*** file scope macro definitions ****************************************************************/
  63 
  64 #define TAR_SUPER(super) ((tar_super_t *) (super))
  65 
  66 
  67 /* tar Header Block, from POSIX 1003.1-1990.  */
  68 
  69 /* The magic field is filled with this if uname and gname are valid. */
  70 #define TMAGIC "ustar"          /* ustar and a null */
  71 
  72 #define XHDTYPE 'x'             /* Extended header referring to the next file in the archive */
  73 #define XGLTYPE 'g'             /* Global extended header */
  74 
  75 /* Values used in typeflag field.  */
  76 #define LNKTYPE  '1'            /* link */
  77 #define SYMTYPE  '2'            /* symbolic link */
  78 #define CHRTYPE  '3'            /* character special */
  79 #define BLKTYPE  '4'            /* block special */
  80 #define DIRTYPE  '5'            /* directory */
  81 #define FIFOTYPE '6'            /* FIFO special */
  82 
  83 
  84 /* tar Header Block, GNU extensions.  */
  85 
  86 /* *BEWARE* *BEWARE* *BEWARE* that the following information is still
  87    boiling, and may change.  Even if the OLDGNU format description should be
  88    accurate, the so-called GNU format is not yet fully decided.  It is
  89    surely meant to use only extensions allowed by POSIX, but the sketch
  90    below repeats some ugliness from the OLDGNU format, which should rather
  91    go away.  Sparse files should be saved in such a way that they do *not*
  92    require two passes at archive creation time.  Huge files get some POSIX
  93    fields to overflow, alternate solutions have to be sought for this.  */
  94 
  95 
  96 /* Sparse files are not supported in POSIX ustar format.  For sparse files
  97    with a POSIX header, a GNU extra header is provided which holds overall
  98    sparse information and a few sparse descriptors.  When an old GNU header
  99    replaces both the POSIX header and the GNU extra header, it holds some
 100    sparse descriptors too.  Whether POSIX or not, if more sparse descriptors
 101    are still needed, they are put into as many successive sparse headers as
 102    necessary.  The following constants tell how many sparse descriptors fit
 103    in each kind of header able to hold them.  */
 104 
 105 #define SPARSES_IN_EXTRA_HEADER  16
 106 #define SPARSES_IN_OLDGNU_HEADER 4
 107 #define SPARSES_IN_SPARSE_HEADER 21
 108 
 109 /* OLDGNU_MAGIC uses both magic and version fields, which are contiguous.
 110    Found in an archive, it indicates an old GNU header format, which will be
 111    hopefully become obsolescent.  With OLDGNU_MAGIC, uname and gname are
 112    valid, though the header is not truly POSIX conforming.  */
 113 #define OLDGNU_MAGIC "ustar  "  /* 7 chars and a null */
 114 
 115 /* The standards committee allows only capital A through capital Z for user-defined expansion.  */
 116 
 117 /* This is a dir entry that contains the names of files that were in the
 118    dir at the time the dump was made.  */
 119 #define GNUTYPE_DUMPDIR 'D'
 120 
 121 /* Identifies the *next* file on the tape as having a long linkname.  */
 122 #define GNUTYPE_LONGLINK 'K'
 123 
 124 /* Identifies the *next* file on the tape as having a long name.  */
 125 #define GNUTYPE_LONGNAME 'L'
 126 
 127 
 128 /* tar Header Block, overall structure.  */
 129 
 130 /* tar files are made in basic blocks of this size.  */
 131 #define BLOCKSIZE 512
 132 
 133 
 134 #define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )
 135 
 136 /*** file scope type declarations ****************************************************************/
 137 
 138 /* *INDENT-OFF* */
 139 
 140 /* POSIX header */
 141 struct posix_header
 142 {                               /* byte offset */
 143     char name[100];             /*   0 */
 144     char mode[8];               /* 100 */
 145     char uid[8];                /* 108 */
 146     char gid[8];                /* 116 */
 147     char size[12];              /* 124 */
 148     char mtime[12];             /* 136 */
 149     char chksum[8];             /* 148 */
 150     char typeflag;              /* 156 */
 151     char linkname[100];         /* 157 */
 152     char magic[6];              /* 257 */
 153     char version[2];            /* 263 */
 154     char uname[32];             /* 265 */
 155     char gname[32];             /* 297 */
 156     char devmajor[8];           /* 329 */
 157     char devminor[8];           /* 337 */
 158     char prefix[155];           /* 345 */
 159                                 /* 500 */
 160 };
 161 
 162 /* Descriptor for a single file hole */
 163 struct sparse
 164 {                               /* byte offset */
 165     /* cppcheck-suppress unusedStructMember */
 166     char offset[12];            /*   0 */
 167     /* cppcheck-suppress unusedStructMember */
 168     char numbytes[12];          /*  12 */
 169                                 /*  24 */
 170 };
 171 
 172 /* The GNU extra header contains some information GNU tar needs, but not
 173    foreseen in POSIX header format.  It is only used after a POSIX header
 174    (and never with old GNU headers), and immediately follows this POSIX
 175    header, when typeflag is a letter rather than a digit, so signaling a GNU
 176    extension.  */
 177 struct extra_header
 178 {                               /* byte offset */
 179     char atime[12];             /*   0 */
 180     char ctime[12];             /*  12 */
 181     char offset[12];            /*  24 */
 182     char realsize[12];          /*  36 */
 183     char longnames[4];          /*  48 */
 184     char unused_pad1[68];       /*  52 */
 185     struct sparse sp[SPARSES_IN_EXTRA_HEADER];
 186                                 /* 120 */
 187     char isextended;            /* 504 */
 188                                 /* 505 */
 189 };
 190 
 191 /* Extension header for sparse files, used immediately after the GNU extra
 192    header, and used only if all sparse information cannot fit into that
 193    extra header.  There might even be many such extension headers, one after
 194    the other, until all sparse information has been recorded.  */
 195 struct sparse_header
 196 {                               /* byte offset */
 197     struct sparse sp[SPARSES_IN_SPARSE_HEADER];
 198                                 /*   0 */
 199     char isextended;            /* 504 */
 200                                 /* 505 */
 201 };
 202 
 203 /* The old GNU format header conflicts with POSIX format in such a way that
 204    POSIX archives may fool old GNU tar's, and POSIX tar's might well be
 205    fooled by old GNU tar archives.  An old GNU format header uses the space
 206    used by the prefix field in a POSIX header, and cumulates information
 207    normally found in a GNU extra header.  With an old GNU tar header, we
 208    never see any POSIX header nor GNU extra header.  Supplementary sparse
 209    headers are allowed, however.  */
 210 struct oldgnu_header
 211 {                               /* byte offset */
 212     char unused_pad1[345];      /*   0 */
 213     char atime[12];             /* 345 */
 214     char ctime[12];             /* 357 */
 215     char offset[12];            /* 369 */
 216     char longnames[4];          /* 381 */
 217     char unused_pad2;           /* 385 */
 218     struct sparse sp[SPARSES_IN_OLDGNU_HEADER];
 219                                 /* 386 */
 220     char isextended;            /* 482 */
 221     char realsize[12];          /* 483 */
 222                                 /* 495 */
 223 };
 224 
 225 /* *INDENT-ON* */
 226 
 227 /* tar Header Block, overall structure */
 228 union block
 229 {
 230     char buffer[BLOCKSIZE];
 231     struct posix_header header;
 232     struct extra_header extra_header;
 233     struct oldgnu_header oldgnu_header;
 234     struct sparse_header sparse_header;
 235 };
 236 
 237 enum archive_format
 238 {
 239     TAR_UNKNOWN = 0,
 240     TAR_V7,
 241     TAR_USTAR,
 242     TAR_POSIX,
 243     TAR_GNU
 244 };
 245 
 246 typedef enum
 247 {
 248     STATUS_BADCHECKSUM,
 249     STATUS_SUCCESS,
 250     STATUS_EOFMARK,
 251     STATUS_EOF
 252 } ReadStatus;
 253 
 254 typedef struct
 255 {
 256     struct vfs_s_super base;    /* base class */
 257 
 258     int fd;
 259     struct stat st;
 260     enum archive_format type;   /* Type of the archive */
 261 } tar_super_t;
 262 
 263 /*** file scope variables ************************************************************************/
 264 
 265 static struct vfs_s_subclass tarfs_subclass;
 266 static struct vfs_class *vfs_tarfs_ops = VFS_CLASS (&tarfs_subclass);
 267 
 268 /* As we open one archive at a time, it is safe to have this static */
 269 static off_t current_tar_position = 0;
 270 
 271 static union block block_buf;
 272 
 273 /* --------------------------------------------------------------------------------------------- */
 274 /*** file scope functions ************************************************************************/
 275 /* --------------------------------------------------------------------------------------------- */
 276 /**
 277  * Quick and dirty octal conversion.
 278  *
 279  * Result is -1 if the field is invalid (all blank, or nonoctal).
 280  */
 281 static long
 282 tar_from_oct (int digs, const char *where)
     /* [previous][next][first][last][top][bottom][index][help]  */
 283 {
 284     long value;
 285 
 286     while (isspace ((unsigned char) *where))
 287     {                           /* Skip spaces */
 288         where++;
 289         if (--digs <= 0)
 290             return -1;          /* All blank field */
 291     }
 292     value = 0;
 293     while (digs > 0 && isodigit (*where))
 294     {                           /* Scan till nonoctal */
 295         value = (value << 3) | (*where++ - '0');
 296         --digs;
 297     }
 298 
 299     if (digs > 0 && *where && !isspace ((unsigned char) *where))
 300         return -1;              /* Ended on non-space/nul */
 301 
 302     return value;
 303 }
 304 
 305 /* --------------------------------------------------------------------------------------------- */
 306 
 307 static struct vfs_s_super *
 308 tar_new_archive (struct vfs_class *me)
     /* [previous][next][first][last][top][bottom][index][help]  */
 309 {
 310     tar_super_t *arch;
 311 
 312     arch = g_new0 (tar_super_t, 1);
 313     arch->base.me = me;
 314     arch->fd = -1;
 315     arch->type = TAR_UNKNOWN;
 316 
 317     return VFS_SUPER (arch);
 318 }
 319 
 320 /* --------------------------------------------------------------------------------------------- */
 321 
 322 static void
 323 tar_free_archive (struct vfs_class *me, struct vfs_s_super *archive)
     /* [previous][next][first][last][top][bottom][index][help]  */
 324 {
 325     tar_super_t *arch = TAR_SUPER (archive);
 326 
 327     (void) me;
 328 
 329     if (arch->fd != -1)
 330     {
 331         mc_close (arch->fd);
 332         arch->fd = -1;
 333     }
 334 }
 335 
 336 /* --------------------------------------------------------------------------------------------- */
 337 
 338 /* Returns fd of the open tar file */
 339 static int
 340 tar_open_archive_int (struct vfs_class *me, const vfs_path_t * vpath, struct vfs_s_super *archive)
     /* [previous][next][first][last][top][bottom][index][help]  */
 341 {
 342     int result, type;
 343     tar_super_t *arch;
 344     mode_t mode;
 345     struct vfs_s_inode *root;
 346 
 347     result = mc_open (vpath, O_RDONLY);
 348     if (result == -1)
 349     {
 350         message (D_ERROR, MSG_ERROR, _("Cannot open tar archive\n%s"), vfs_path_as_str (vpath));
 351         ERRNOR (ENOENT, -1);
 352     }
 353 
 354     archive->name = g_strdup (vfs_path_as_str (vpath));
 355     arch = TAR_SUPER (archive);
 356     mc_stat (vpath, &arch->st);
 357 
 358     /* Find out the method to handle this tar file */
 359     type = get_compression_type (result, archive->name);
 360     if (type == COMPRESSION_NONE)
 361         mc_lseek (result, 0, SEEK_SET);
 362     else
 363     {
 364         char *s;
 365         vfs_path_t *tmp_vpath;
 366 
 367         mc_close (result);
 368         s = g_strconcat (archive->name, decompress_extension (type), (char *) NULL);
 369         tmp_vpath = vfs_path_from_str_flags (s, VPF_NO_CANON);
 370         result = mc_open (tmp_vpath, O_RDONLY);
 371         vfs_path_free (tmp_vpath, TRUE);
 372         if (result == -1)
 373             message (D_ERROR, MSG_ERROR, _("Cannot open tar archive\n%s"), s);
 374         g_free (s);
 375         if (result == -1)
 376         {
 377             MC_PTR_FREE (archive->name);
 378             ERRNOR (ENOENT, -1);
 379         }
 380     }
 381 
 382     arch->fd = result;
 383     mode = arch->st.st_mode & 07777;
 384     if (mode & 0400)
 385         mode |= 0100;
 386     if (mode & 0040)
 387         mode |= 0010;
 388     if (mode & 0004)
 389         mode |= 0001;
 390     mode |= S_IFDIR;
 391 
 392     root = vfs_s_new_inode (me, archive, &arch->st);
 393     root->st.st_mode = mode;
 394     root->data_offset = -1;
 395     root->st.st_nlink++;
 396     root->st.st_dev = VFS_SUBCLASS (me)->rdev++;
 397 
 398     archive->root = root;
 399 
 400     return result;
 401 }
 402 
 403 /* --------------------------------------------------------------------------------------------- */
 404 
 405 static union block *
 406 tar_get_next_block (struct vfs_s_super *archive, int tard)
     /* [previous][next][first][last][top][bottom][index][help]  */
 407 {
 408     int n;
 409 
 410     (void) archive;
 411 
 412     n = mc_read (tard, block_buf.buffer, sizeof (block_buf.buffer));
 413     if (n != sizeof (block_buf.buffer))
 414         return NULL;            /* An error has occurred */
 415     current_tar_position += sizeof (block_buf.buffer);
 416     return &block_buf;
 417 }
 418 
 419 /* --------------------------------------------------------------------------------------------- */
 420 
 421 static void
 422 tar_skip_n_records (struct vfs_s_super *archive, int tard, size_t n)
     /* [previous][next][first][last][top][bottom][index][help]  */
 423 {
 424     (void) archive;
 425 
 426     mc_lseek (tard, n * sizeof (block_buf.buffer), SEEK_CUR);
 427     current_tar_position += n * sizeof (block_buf.buffer);
 428 }
 429 
 430 /* --------------------------------------------------------------------------------------------- */
 431 
 432 static ReadStatus
 433 tar_checksum (const union block *header)
     /* [previous][next][first][last][top][bottom][index][help]  */
 434 {
 435     long recsum;
 436     long signed_sum = 0;
 437     long sum = 0;
 438     int i;
 439     const char *p = header->buffer;
 440 
 441     recsum = tar_from_oct (8, header->header.chksum);
 442 
 443     for (i = sizeof (*header); --i >= 0;)
 444     {
 445         /*
 446          * We can't use unsigned char here because of old compilers,
 447          * e.g. V7.
 448          */
 449         signed_sum += *p;
 450         sum += 0xFF & *p++;
 451     }
 452 
 453     /* Adjust checksum to count the "chksum" field as blanks. */
 454     for (i = sizeof (header->header.chksum); --i >= 0;)
 455     {
 456         sum -= 0xFF & header->header.chksum[i];
 457         signed_sum -= (char) header->header.chksum[i];
 458     }
 459 
 460     sum += ' ' * sizeof (header->header.chksum);
 461     signed_sum += ' ' * sizeof (header->header.chksum);
 462 
 463     /*
 464      * This is a zeroed block... whole block is 0's except
 465      * for the 8 blanks we faked for the checksum field.
 466      */
 467     if (sum == 8 * ' ')
 468         return STATUS_EOFMARK;
 469 
 470     if (sum != recsum && signed_sum != recsum)
 471         return STATUS_BADCHECKSUM;
 472 
 473     return STATUS_SUCCESS;
 474 }
 475 
 476 /* --------------------------------------------------------------------------------------------- */
 477 
 478 static size_t
 479 tar_decode_header (union block *header, tar_super_t * arch)
     /* [previous][next][first][last][top][bottom][index][help]  */
 480 {
 481     size_t size;
 482 
 483     /*
 484      * Try to determine the archive format.
 485      */
 486     if (arch->type == TAR_UNKNOWN)
 487     {
 488         if (strcmp (header->header.magic, TMAGIC) == 0)
 489         {
 490             if (header->header.typeflag == XGLTYPE)
 491                 arch->type = TAR_POSIX;
 492             else
 493                 arch->type = TAR_USTAR;
 494         }
 495         else if (strcmp (header->header.magic, OLDGNU_MAGIC) == 0)
 496             arch->type = TAR_GNU;
 497     }
 498 
 499     /*
 500      * typeflag on BSDI tar (pax) always '\000'
 501      */
 502     if (header->header.typeflag == '\000')
 503     {
 504         size_t len;
 505 
 506         if (header->header.name[sizeof (header->header.name) - 1] != '\0')
 507             len = sizeof (header->header.name);
 508         else
 509             len = strlen (header->header.name);
 510 
 511         if (len != 0 && IS_PATH_SEP (header->header.name[len - 1]))
 512             header->header.typeflag = DIRTYPE;
 513     }
 514 
 515     /*
 516      * Good block.  Decode file size and return.
 517      */
 518     if (header->header.typeflag == LNKTYPE || header->header.typeflag == DIRTYPE)
 519         size = 0;               /* Links 0 size on tape */
 520     else
 521         size = tar_from_oct (1 + 12, header->header.size);
 522 
 523     if (header->header.typeflag == GNUTYPE_DUMPDIR)
 524         if (arch->type == TAR_UNKNOWN)
 525             arch->type = TAR_GNU;
 526 
 527     return size;
 528 }
 529 
 530 /* --------------------------------------------------------------------------------------------- */
 531 
 532 static void
 533 tar_fill_stat (struct vfs_s_super *archive, struct stat *st, union block *header, size_t h_size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 534 {
 535     tar_super_t *arch = TAR_SUPER (archive);
 536 
 537     st->st_mode = tar_from_oct (8, header->header.mode);
 538 
 539     /* Adjust st->st_mode because there are tar-files with
 540      * typeflag==SYMTYPE and S_ISLNK(mod)==0. I don't 
 541      * know about the other modes but I think I cause no new
 542      * problem when I adjust them, too. -- Norbert.
 543      */
 544     if (header->header.typeflag == DIRTYPE || header->header.typeflag == GNUTYPE_DUMPDIR)
 545         st->st_mode |= S_IFDIR;
 546     else if (header->header.typeflag == SYMTYPE)
 547         st->st_mode |= S_IFLNK;
 548     else if (header->header.typeflag == CHRTYPE)
 549         st->st_mode |= S_IFCHR;
 550     else if (header->header.typeflag == BLKTYPE)
 551         st->st_mode |= S_IFBLK;
 552     else if (header->header.typeflag == FIFOTYPE)
 553         st->st_mode |= S_IFIFO;
 554     else
 555         st->st_mode |= S_IFREG;
 556 
 557     st->st_dev = 0;
 558 #ifdef HAVE_STRUCT_STAT_ST_RDEV
 559     st->st_rdev = 0;
 560 #endif
 561 
 562     switch (arch->type)
 563     {
 564     case TAR_USTAR:
 565     case TAR_POSIX:
 566     case TAR_GNU:
 567         /* *INDENT-OFF* */
 568         st->st_uid = *header->header.uname != '\0'
 569             ? (uid_t) vfs_finduid (header->header.uname)
 570             : tar_from_oct (8, header->header.uid);
 571         st->st_gid = *header->header.gname != '\0'
 572             ? (gid_t)  vfs_findgid (header->header.gname)
 573             : tar_from_oct (8,header->header.gid);
 574         /* *INDENT-ON* */
 575 
 576         switch (header->header.typeflag)
 577         {
 578         case BLKTYPE:
 579         case CHRTYPE:
 580 #ifdef HAVE_STRUCT_STAT_ST_RDEV
 581             st->st_rdev =
 582                 makedev (tar_from_oct (8, header->header.devmajor),
 583                          tar_from_oct (8, header->header.devminor));
 584 #endif
 585             break;
 586         default:
 587             break;
 588         }
 589         break;
 590 
 591     default:
 592         st->st_uid = tar_from_oct (8, header->header.uid);
 593         st->st_gid = tar_from_oct (8, header->header.gid);
 594         break;
 595     }
 596 
 597     st->st_size = h_size;
 598 #ifdef HAVE_STRUCT_STAT_ST_MTIM
 599     st->st_atim.tv_nsec = st->st_mtim.tv_nsec = st->st_ctim.tv_nsec = 0;
 600 #endif
 601     st->st_mtime = tar_from_oct (1 + 12, header->header.mtime);
 602     st->st_atime = 0;
 603     st->st_ctime = 0;
 604     if (arch->type == TAR_GNU)
 605     {
 606         st->st_atime = tar_from_oct (1 + 12, header->oldgnu_header.atime);
 607         st->st_ctime = tar_from_oct (1 + 12, header->oldgnu_header.ctime);
 608     }
 609 
 610 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
 611     st->st_blksize = 8 * 1024;  /* FIXME */
 612 #endif
 613     vfs_adjust_stat (st);
 614 }
 615 
 616 /* --------------------------------------------------------------------------------------------- */
 617 /**
 618  * Return 1 for success, 0 if the checksum is bad, EOF on eof,
 619  * 2 for a block full of zeros (EOF marker).
 620  *
 621  */
 622 static ReadStatus
 623 tar_read_header (struct vfs_class *me, struct vfs_s_super *archive, int tard, size_t * h_size)
     /* [previous][next][first][last][top][bottom][index][help]  */
 624 {
 625     tar_super_t *arch = TAR_SUPER (archive);
 626     ReadStatus checksum_status;
 627     union block *header;
 628     static char *next_long_name = NULL, *next_long_link = NULL;
 629 
 630     while (TRUE)
 631     {
 632         header = tar_get_next_block (archive, tard);
 633         if (header == NULL)
 634             return STATUS_EOF;
 635 
 636         checksum_status = tar_checksum (header);
 637         if (checksum_status != STATUS_SUCCESS)
 638             return checksum_status;
 639 
 640         *h_size = tar_decode_header (header, arch);
 641 
 642         /* Skip over pax extended header and global extended header records. */
 643         if (header->header.typeflag == XHDTYPE || header->header.typeflag == XGLTYPE)
 644         {
 645             if (arch->type == TAR_UNKNOWN)
 646                 arch->type = TAR_POSIX;
 647             return STATUS_SUCCESS;
 648         }
 649 
 650         if (header->header.typeflag == GNUTYPE_LONGNAME
 651             || header->header.typeflag == GNUTYPE_LONGLINK)
 652         {
 653             char **longp;
 654             char *bp, *data;
 655             off_t size;
 656             size_t written;
 657 
 658             if (arch->type == TAR_UNKNOWN)
 659                 arch->type = TAR_GNU;
 660 
 661             if (*h_size > MC_MAXPATHLEN)
 662             {
 663                 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
 664                 return STATUS_BADCHECKSUM;
 665             }
 666 
 667             longp = header->header.typeflag == GNUTYPE_LONGNAME ? &next_long_name : &next_long_link;
 668 
 669             g_free (*longp);
 670             bp = *longp = g_malloc (*h_size + 1);
 671 
 672             for (size = *h_size; size > 0; size -= written)
 673             {
 674                 data = tar_get_next_block (archive, tard)->buffer;
 675                 if (data == NULL)
 676                 {
 677                     MC_PTR_FREE (*longp);
 678                     message (D_ERROR, MSG_ERROR, _("Unexpected EOF on archive file"));
 679                     return STATUS_BADCHECKSUM;
 680                 }
 681                 written = BLOCKSIZE;
 682                 if ((off_t) written > size)
 683                     written = (size_t) size;
 684 
 685                 memcpy (bp, data, written);
 686                 bp += written;
 687             }
 688 
 689             if (bp - *longp == MC_MAXPATHLEN && bp[-1] != '\0')
 690             {
 691                 MC_PTR_FREE (*longp);
 692                 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
 693                 return STATUS_BADCHECKSUM;
 694             }
 695 
 696             *bp = '\0';
 697         }
 698         else
 699             break;
 700     }
 701 
 702     {
 703         struct stat st;
 704         struct vfs_s_entry *entry;
 705         struct vfs_s_inode *inode = NULL, *parent;
 706         off_t data_position;
 707         char *p, *q;
 708         size_t len;
 709         char *current_file_name, *current_link_name;
 710 
 711         current_link_name =
 712             next_long_link != NULL ? next_long_link : g_strndup (header->header.linkname,
 713                                                                  sizeof (header->header.linkname));
 714         len = strlen (current_link_name);
 715         if (len > 1 && IS_PATH_SEP (current_link_name[len - 1]))
 716             current_link_name[len - 1] = '\0';
 717 
 718         current_file_name = NULL;
 719         switch (arch->type)
 720         {
 721         case TAR_USTAR:
 722         case TAR_POSIX:
 723             /* The ustar archive format supports pathnames of upto 256
 724              * characters in length. This is achieved by concatenating
 725              * the contents of the 'prefix' and 'name' fields like
 726              * this:
 727              *
 728              *   prefix + path_separator + name
 729              *
 730              * If the 'prefix' field contains an empty string i.e. its
 731              * first characters is '\0' the prefix field is ignored.
 732              */
 733             if (header->header.prefix[0] != '\0')
 734             {
 735                 char *temp_name, *temp_prefix;
 736 
 737                 temp_name = g_strndup (header->header.name, sizeof (header->header.name));
 738                 temp_prefix = g_strndup (header->header.prefix, sizeof (header->header.prefix));
 739                 current_file_name = g_strconcat (temp_prefix, PATH_SEP_STR,
 740                                                  temp_name, (char *) NULL);
 741                 g_free (temp_name);
 742                 g_free (temp_prefix);
 743             }
 744             break;
 745         case TAR_GNU:
 746             if (next_long_name != NULL)
 747                 current_file_name = next_long_name;
 748             break;
 749         default:
 750             break;
 751         }
 752 
 753         if (current_file_name == NULL)
 754         {
 755             if (next_long_name != NULL)
 756                 current_file_name = g_strdup (next_long_name);
 757             else
 758                 current_file_name = g_strndup (header->header.name, sizeof (header->header.name));
 759         }
 760 
 761         canonicalize_pathname (current_file_name);
 762         len = strlen (current_file_name);
 763 
 764         data_position = current_tar_position;
 765 
 766         p = strrchr (current_file_name, PATH_SEP);
 767         if (p == NULL)
 768         {
 769             p = current_file_name;
 770             q = current_file_name + len;        /* "" */
 771         }
 772         else
 773         {
 774             *(p++) = '\0';
 775             q = current_file_name;
 776         }
 777 
 778         parent = vfs_s_find_inode (me, archive, q, LINK_NO_FOLLOW, FL_MKDIR);
 779         if (parent == NULL)
 780         {
 781             message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
 782             return STATUS_BADCHECKSUM;
 783         }
 784 
 785         if (header->header.typeflag == LNKTYPE)
 786         {
 787             inode = vfs_s_find_inode (me, archive, current_link_name, LINK_NO_FOLLOW, FL_NONE);
 788             if (inode == NULL)
 789                 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
 790             else
 791             {
 792                 entry = vfs_s_new_entry (me, p, inode);
 793                 vfs_s_insert_entry (me, parent, entry);
 794                 g_free (current_link_name);
 795                 goto done;
 796             }
 797         }
 798 
 799         memset (&st, 0, sizeof (st));
 800         tar_fill_stat (archive, &st, header, *h_size);
 801 
 802         if (S_ISDIR (st.st_mode))
 803         {
 804             entry = VFS_SUBCLASS (me)->find_entry (me, parent, p, LINK_NO_FOLLOW, FL_NONE);
 805             if (entry != NULL)
 806                 goto done;
 807         }
 808 
 809         inode = vfs_s_new_inode (me, archive, &st);
 810         inode->data_offset = data_position;
 811 
 812         if (*current_link_name != '\0')
 813             inode->linkname = current_link_name;
 814         else if (current_link_name != next_long_link)
 815             g_free (current_link_name);
 816 
 817         entry = vfs_s_new_entry (me, p, inode);
 818         vfs_s_insert_entry (me, parent, entry);
 819         g_free (current_file_name);
 820 
 821       done:
 822         next_long_link = next_long_name = NULL;
 823 
 824         if (arch->type == TAR_GNU && header->oldgnu_header.isextended)
 825         {
 826             while (tar_get_next_block (archive, tard)->sparse_header.isextended != 0)
 827                 ;
 828 
 829             if (inode != NULL)
 830                 inode->data_offset = current_tar_position;
 831         }
 832         return STATUS_SUCCESS;
 833     }
 834 }
 835 
 836 /* --------------------------------------------------------------------------------------------- */
 837 /**
 838  * Main loop for reading an archive.
 839  * Returns 0 on success, -1 on error.
 840  */
 841 static int
 842 tar_open_archive (struct vfs_s_super *archive, const vfs_path_t * vpath,
     /* [previous][next][first][last][top][bottom][index][help]  */
 843                   const vfs_path_element_t * vpath_element)
 844 {
 845     /* Initial status at start of archive */
 846     ReadStatus status = STATUS_EOFMARK;
 847     int tard;
 848 
 849     current_tar_position = 0;
 850     /* Open for reading */
 851     tard = tar_open_archive_int (vpath_element->class, vpath, archive);
 852     if (tard == -1)
 853         return -1;
 854 
 855     while (TRUE)
 856     {
 857         size_t h_size = 0;
 858         ReadStatus prev_status = status;
 859 
 860         status = tar_read_header (vpath_element->class, archive, tard, &h_size);
 861 
 862         switch (status)
 863         {
 864         case STATUS_SUCCESS:
 865             tar_skip_n_records (archive, tard, (h_size + BLOCKSIZE - 1) / BLOCKSIZE);
 866             continue;
 867 
 868             /*
 869              * Invalid header:
 870              *
 871              * If the previous header was good, tell them
 872              * that we are skipping bad ones.
 873              */
 874         case STATUS_BADCHECKSUM:
 875             switch (prev_status)
 876             {
 877                 /* Error on first block */
 878             case STATUS_EOFMARK:
 879                 {
 880                     message (D_ERROR, MSG_ERROR, _("%s\ndoesn't look like a tar archive."),
 881                              vfs_path_as_str (vpath));
 882                     MC_FALLTHROUGH;
 883 
 884                     /* Error after header rec */
 885                 }
 886             case STATUS_SUCCESS:
 887                 /* Error after error */
 888 
 889             case STATUS_BADCHECKSUM:
 890                 return -1;
 891 
 892             case STATUS_EOF:
 893                 return 0;
 894 
 895             default:
 896                 break;
 897             }
 898             MC_FALLTHROUGH;
 899 
 900             /* Record of zeroes */
 901         case STATUS_EOFMARK:   /* If error after 0's */
 902             MC_FALLTHROUGH;
 903             /* exit from loop */
 904         case STATUS_EOF:       /* End of archive */
 905             break;
 906         default:
 907             break;
 908         }
 909         break;
 910     }
 911     return 0;
 912 }
 913 
 914 /* --------------------------------------------------------------------------------------------- */
 915 
 916 static void *
 917 tar_super_check (const vfs_path_t * vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
 918 {
 919     static struct stat stat_buf;
 920     int stat_result;
 921 
 922     stat_result = mc_stat (vpath, &stat_buf);
 923 
 924     return (stat_result != 0) ? NULL : &stat_buf;
 925 }
 926 
 927 /* --------------------------------------------------------------------------------------------- */
 928 
 929 static int
 930 tar_super_same (const vfs_path_element_t * vpath_element, struct vfs_s_super *parc,
     /* [previous][next][first][last][top][bottom][index][help]  */
 931                 const vfs_path_t * vpath, void *cookie)
 932 {
 933     struct stat *archive_stat = cookie; /* stat of main archive */
 934 
 935     (void) vpath_element;
 936 
 937     if (strcmp (parc->name, vfs_path_as_str (vpath)) != 0)
 938         return 0;
 939 
 940     /* Has the cached archive been changed on the disk? */
 941     if (parc != NULL && TAR_SUPER (parc)->st.st_mtime < archive_stat->st_mtime)
 942     {
 943         /* Yes, reload! */
 944         vfs_tarfs_ops->free ((vfsid) parc);
 945         vfs_rmstamp (vfs_tarfs_ops, (vfsid) parc);
 946         return 2;
 947     }
 948     /* Hasn't been modified, give it a new timeout */
 949     vfs_stamp (vfs_tarfs_ops, (vfsid) parc);
 950     return 1;
 951 }
 952 
 953 /* --------------------------------------------------------------------------------------------- */
 954 
 955 static ssize_t
 956 tar_read (void *fh, char *buffer, size_t count)
     /* [previous][next][first][last][top][bottom][index][help]  */
 957 {
 958     struct vfs_class *me = VFS_FILE_HANDLER_SUPER (fh)->me;
 959     vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
 960     off_t begin = file->ino->data_offset;
 961     int fd = TAR_SUPER (VFS_FILE_HANDLER_SUPER (fh))->fd;
 962     ssize_t res;
 963 
 964     if (mc_lseek (fd, begin + file->pos, SEEK_SET) != begin + file->pos)
 965         ERRNOR (EIO, -1);
 966 
 967     count = MIN (count, (size_t) (file->ino->st.st_size - file->pos));
 968 
 969     res = mc_read (fd, buffer, count);
 970     if (res == -1)
 971         ERRNOR (errno, -1);
 972 
 973     file->pos += res;
 974     return res;
 975 }
 976 
 977 /* --------------------------------------------------------------------------------------------- */
 978 
 979 static int
 980 tar_fh_open (struct vfs_class *me, vfs_file_handler_t * fh, int flags, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
 981 {
 982     (void) fh;
 983     (void) mode;
 984 
 985     if ((flags & O_ACCMODE) != O_RDONLY)
 986         ERRNOR (EROFS, -1);
 987     return 0;
 988 }
 989 
 990 /* --------------------------------------------------------------------------------------------- */
 991 /*** public functions ****************************************************************************/
 992 /* --------------------------------------------------------------------------------------------- */
 993 
 994 void
 995 vfs_init_tarfs (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 996 {
 997     /* FIXME: tarfs used own temp files */
 998     vfs_init_subclass (&tarfs_subclass, "tarfs", VFSF_READONLY, "utar");
 999     vfs_tarfs_ops->read = tar_read;
1000     vfs_tarfs_ops->setctl = NULL;
1001     tarfs_subclass.archive_check = tar_super_check;
1002     tarfs_subclass.archive_same = tar_super_same;
1003     tarfs_subclass.new_archive = tar_new_archive;
1004     tarfs_subclass.open_archive = tar_open_archive;
1005     tarfs_subclass.free_archive = tar_free_archive;
1006     tarfs_subclass.fh_open = tar_fh_open;
1007     vfs_register_class (vfs_tarfs_ops);
1008 }
1009 
1010 /* --------------------------------------------------------------------------------------------- */

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