root/src/vfs/tar/tar.c

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

DEFINITIONS

This source file includes following definitions.
  1. tar_stat_destroy
  2. gid_from_header
  3. major_from_header
  4. minor_from_header
  5. mode_from_header
  6. time_from_header
  7. uid_from_header
  8. tar_calc_sparse_offsets
  9. tar_skip_member
  10. tar_available_space_after
  11. tar_checksum
  12. tar_decode_header
  13. tar_fill_stat
  14. tar_free_inode
  15. tar_insert_entry
  16. tar_read_header
  17. tar_new_archive
  18. tar_free_archive
  19. tar_open_archive_int
  20. tar_open_archive
  21. tar_super_check
  22. tar_super_same
  23. tar_get_sparse_chunk_idx
  24. tar_read_sparse
  25. tar_lseek_sparse
  26. tar_read
  27. tar_fh_open
  28. vfs_init_tarfs

   1 /*
   2    Virtual File System: GNU Tar file system.
   3 
   4    Copyright (C) 1995-2025
   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 <https://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 
  36 #include <config.h>
  37 
  38 #include <errno.h>
  39 #include <string.h>  // memset()
  40 
  41 #ifdef hpux
  42 /* major() and minor() macros (among other things) defined here for hpux */
  43 #    include <sys/mknod.h>
  44 #endif
  45 
  46 #include "lib/global.h"
  47 #include "lib/util.h"
  48 #include "lib/unixcompat.h"  // makedev()
  49 #include "lib/widget.h"      // message()
  50 
  51 #include "lib/vfs/vfs.h"
  52 #include "lib/vfs/utilvfs.h"
  53 #include "lib/vfs/gc.h"  // vfs_rmstamp
  54 
  55 #include "tar-internal.h"
  56 #include "tar.h"
  57 
  58 /*** global variables ****************************************************************************/
  59 
  60 /* Size of each record, once in blocks, once in bytes. Those two variables are always related,
  61    the second being BLOCKSIZE times the first. */
  62 const idx_t blocking_factor = DEFAULT_BLOCKING;
  63 const idx_t record_size = DEFAULT_BLOCKING * BLOCKSIZE;
  64 
  65 /* As we open one archive at a time, it is safe to have these static */
  66 union block *record_end;     // last+1 block of archive record
  67 union block *current_block;  // current block of archive
  68 off_t record_start_block;    // block ordinal at record_start
  69 
  70 union block *current_header;
  71 
  72 /* Have we hit EOF yet?  */
  73 gboolean hit_eof;
  74 
  75 struct tar_stat_info current_stat_info;
  76 
  77 /*** file scope macro definitions ****************************************************************/
  78 
  79 #define TAR_SUPER(super) ((tar_super_t *) (super))
  80 
  81 /* tar Header Block, from POSIX 1003.1-1990.  */
  82 
  83 /* The magic field is filled with this if uname and gname are valid. */
  84 #define TMAGIC  "ustar"  // ustar and a null
  85 
  86 #define XHDTYPE 'x'  // Extended header referring to the next file in the archive
  87 #define XGLTYPE 'g'  // Global extended header
  88 
  89 /* Values used in typeflag field.  */
  90 #define REGTYPE  '0'   // regular file
  91 #define AREGTYPE '\0'  // regular file
  92 #define LNKTYPE  '1'   // link
  93 #define SYMTYPE  '2'   // symbolic link
  94 #define CHRTYPE  '3'   // character special
  95 #define BLKTYPE  '4'   // block special
  96 #define DIRTYPE  '5'   // directory
  97 #define FIFOTYPE '6'   // FIFO special
  98 
  99 /* OLDGNU_MAGIC uses both magic and version fields, which are contiguous.
 100    Found in an archive, it indicates an old GNU header format, which will be
 101    hopefully become obsolescent.  With OLDGNU_MAGIC, uname and gname are
 102    valid, though the header is not truly POSIX conforming.  */
 103 #define OLDGNU_MAGIC "ustar  "  // 7 chars and a null
 104 
 105 /* Bits used in the mode field, values in octal.  */
 106 #define TSUID 04000  // set UID on execution
 107 #define TSGID 02000  // set GID on execution
 108 #define TSVTX                                                                                      \
 109     01000                                     // reserved
 110                                               // file permissions
 111 #define TUREAD                         00400  // read by owner
 112 #define TUWRITE                        00200  // write by owner
 113 #define TUEXEC                         00100  // execute/search by owner
 114 #define TGREAD                         00040  // read by group
 115 #define TGWRITE                        00020  // write by group
 116 #define TGEXEC                         00010  // execute/search by group
 117 #define TOREAD                         00004  // read by other
 118 #define TOWRITE                        00002  // write by other
 119 #define TOEXEC                         00001  // execute/search by other
 120 
 121 #define GID_FROM_HEADER(where)         gid_from_header (where, sizeof (where))
 122 #define MAJOR_FROM_HEADER(where)       major_from_header (where, sizeof (where))
 123 #define MINOR_FROM_HEADER(where)       minor_from_header (where, sizeof (where))
 124 #define MODE_FROM_HEADER(where, hbits) mode_from_header (where, sizeof (where), hbits)
 125 #define TIME_FROM_HEADER(where)        time_from_header (where, sizeof (where))
 126 #define UID_FROM_HEADER(where)         uid_from_header (where, sizeof (where))
 127 
 128 /*** file scope type declarations ****************************************************************/
 129 
 130 typedef enum
 131 {
 132     HEADER_STILL_UNREAD,  // for when read_header has not been called
 133     HEADER_SUCCESS,       // header successfully read and checksummed
 134     HEADER_ZERO_BLOCK,    // zero block where header expected
 135     HEADER_END_OF_FILE,   // true end of file while header expected
 136     HEADER_FAILURE        // ill-formed header, or bad checksum
 137 } read_header;
 138 
 139 /*** forward declarations (file scope functions) *************************************************/
 140 
 141 /*** file scope variables ************************************************************************/
 142 
 143 static struct vfs_s_subclass tarfs_subclass;
 144 static struct vfs_class *vfs_tarfs_ops = VFS_CLASS (&tarfs_subclass);
 145 
 146 static struct timespec start_time;
 147 
 148 /* --------------------------------------------------------------------------------------------- */
 149 /*** file scope functions ************************************************************************/
 150 /* --------------------------------------------------------------------------------------------- */
 151 
 152 static void
 153 tar_stat_destroy (struct tar_stat_info *st)
     /* [previous][next][first][last][top][bottom][index][help]  */
 154 {
 155     g_free (st->orig_file_name);
 156     g_free (st->file_name);
 157     g_free (st->link_name);
 158 #if 0
 159     g_free (st->uname);
 160     g_free (st->gname);
 161 #endif
 162     if (st->sparse_map != NULL)
 163     {
 164         g_array_free (st->sparse_map, TRUE);
 165         st->sparse_map = NULL;
 166     }
 167     tar_xheader_destroy (&st->xhdr);
 168     memset (st, 0, sizeof (*st));
 169 }
 170 
 171 /* --------------------------------------------------------------------------------------------- */
 172 
 173 static inline gid_t
 174 gid_from_header (const char *p, size_t s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 175 {
 176     return tar_from_header (p, s, "gid_t", TYPE_MINIMUM (gid_t), TYPE_MAXIMUM (gid_t), FALSE);
 177 }
 178 
 179 /* --------------------------------------------------------------------------------------------- */
 180 
 181 static inline major_t
 182 major_from_header (const char *p, size_t s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 183 {
 184     return tar_from_header (p, s, "major_t", TYPE_MINIMUM (major_t), TYPE_MAXIMUM (major_t), FALSE);
 185 }
 186 
 187 /* --------------------------------------------------------------------------------------------- */
 188 
 189 static inline minor_t
 190 minor_from_header (const char *p, size_t s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 191 {
 192     return tar_from_header (p, s, "minor_t", TYPE_MINIMUM (minor_t), TYPE_MAXIMUM (minor_t), FALSE);
 193 }
 194 
 195 /* --------------------------------------------------------------------------------------------- */
 196 
 197 /**
 198  * Convert @p to the file mode, as understood by tar.
 199  * Store unrecognized mode bits (from 10th up) in @hbits.
 200  * Set *hbits if there are any unrecognized bits.
 201  * */
 202 static inline mode_t
 203 mode_from_header (const char *p, size_t s, gboolean *hbits)
     /* [previous][next][first][last][top][bottom][index][help]  */
 204 {
 205     unsigned int u;
 206     mode_t mode;
 207 
 208     // Do not complain about unrecognized mode bits.
 209     u = tar_from_header (p, s, "mode_t", INTMAX_MIN, UINTMAX_MAX, FALSE);
 210 
 211     mode =
 212         ((u & TSUID ? S_ISUID : 0) | (u & TSGID ? S_ISGID : 0) | (u & TSVTX ? S_ISVTX : 0)
 213          | (u & TUREAD ? S_IRUSR : 0) | (u & TUWRITE ? S_IWUSR : 0) | (u & TUEXEC ? S_IXUSR : 0)
 214          | (u & TGREAD ? S_IRGRP : 0) | (u & TGWRITE ? S_IWGRP : 0) | (u & TGEXEC ? S_IXGRP : 0)
 215          | (u & TOREAD ? S_IROTH : 0) | (u & TOWRITE ? S_IWOTH : 0) | (u & TOEXEC ? S_IXOTH : 0));
 216 
 217     if (hbits != NULL)
 218         *hbits = (u & ~07777) != 0;
 219 
 220     return mode;
 221 }
 222 
 223 /* --------------------------------------------------------------------------------------------- */
 224 
 225 static inline time_t
 226 time_from_header (const char *p, size_t s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 227 {
 228     return tar_from_header (p, s, "time_t", TYPE_MINIMUM (time_t), TYPE_MAXIMUM (time_t), FALSE);
 229 }
 230 
 231 /* --------------------------------------------------------------------------------------------- */
 232 
 233 static inline uid_t
 234 uid_from_header (const char *p, size_t s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 235 {
 236     return tar_from_header (p, s, "uid_t", TYPE_MINIMUM (uid_t), TYPE_MAXIMUM (uid_t), FALSE);
 237 }
 238 
 239 /* --------------------------------------------------------------------------------------------- */
 240 
 241 static void
 242 tar_calc_sparse_offsets (struct vfs_s_inode *inode)
     /* [previous][next][first][last][top][bottom][index][help]  */
 243 {
 244     off_t begin = inode->data_offset;
 245     GArray *sm = (GArray *) inode->user_data;
 246     size_t i;
 247 
 248     for (i = 0; i < sm->len; i++)
 249     {
 250         struct sp_array *sp;
 251 
 252         sp = &g_array_index (sm, struct sp_array, i);
 253         sp->arch_offset = begin;
 254         begin += BLOCKSIZE * (sp->numbytes / BLOCKSIZE + sp->numbytes % BLOCKSIZE);
 255     }
 256 }
 257 
 258 /* --------------------------------------------------------------------------------------------- */
 259 
 260 static gboolean
 261 tar_skip_member (tar_super_t *archive, struct vfs_s_inode *inode)
     /* [previous][next][first][last][top][bottom][index][help]  */
 262 {
 263     char save_typeflag;
 264 
 265     if (current_stat_info.skipped)
 266         return TRUE;
 267 
 268     save_typeflag = current_header->header.typeflag;
 269 
 270     tar_set_next_block_after (current_header);
 271 
 272     if (current_stat_info.is_sparse)
 273     {
 274         if (inode != NULL)
 275             inode->data_offset = BLOCKSIZE * tar_current_block_ordinal (archive);
 276 
 277         (void) tar_sparse_skip_file (archive, &current_stat_info);
 278 
 279         if (inode != NULL)
 280         {
 281             // use vfs_s_inode::user_data to keep the sparse map
 282             inode->user_data = current_stat_info.sparse_map;
 283             current_stat_info.sparse_map = NULL;
 284 
 285             tar_calc_sparse_offsets (inode);
 286         }
 287     }
 288     else if (save_typeflag != DIRTYPE)
 289     {
 290         if (inode != NULL && (save_typeflag == REGTYPE || save_typeflag == AREGTYPE))
 291             inode->data_offset = BLOCKSIZE * tar_current_block_ordinal (archive);
 292 
 293         return tar_skip_file (archive, current_stat_info.stat.st_size);
 294     }
 295 
 296     return TRUE;
 297 }
 298 
 299 /* --------------------------------------------------------------------------------------------- */
 300 
 301 /**
 302  * Return the number of bytes comprising the space between @pointer through the end
 303  * of the current buffer of blocks. This space is available for filling with data,
 304  * or taking data from. @pointer is usually (but not always) the result previous
 305  * tar_find_next_block() call.
 306  */
 307 static inline size_t
 308 tar_available_space_after (const union block *pointer)
     /* [previous][next][first][last][top][bottom][index][help]  */
 309 {
 310     return record_end->buffer - pointer->buffer;
 311 }
 312 
 313 /* --------------------------------------------------------------------------------------------- */
 314 
 315 /** Check header checksum.
 316  */
 317 static read_header
 318 tar_checksum (const union block *header)
     /* [previous][next][first][last][top][bottom][index][help]  */
 319 {
 320     unsigned int i;
 321     int unsigned_sum = 0;  // the POSIX one :-)
 322     int signed_sum = 0;    // the Sun one :-(
 323     int recorded_sum;
 324 
 325     for (i = 0; i < sizeof (*header); i++)
 326     {
 327         unsigned char uc = header->buffer[i];
 328         signed char sc = header->buffer[i];
 329 
 330         unsigned_sum += uc;
 331         signed_sum += sc;
 332     }
 333 
 334     if (unsigned_sum == 0)
 335         return HEADER_ZERO_BLOCK;
 336 
 337     // Adjust checksum to count the "chksum" field as blanks.
 338     for (i = 0; i < sizeof (header->header.chksum); i++)
 339     {
 340         unsigned char uc = header->header.chksum[i];
 341         signed char sc = header->header.chksum[i];
 342 
 343         unsigned_sum -= uc;
 344         signed_sum -= sc;
 345     }
 346 
 347     unsigned_sum += ' ' * sizeof (header->header.chksum);
 348     signed_sum += ' ' * sizeof (header->header.chksum);
 349 
 350     recorded_sum = tar_from_header (header->header.chksum, sizeof (header->header.chksum), NULL, 0,
 351                                     INT_MAX, TRUE);
 352 
 353     if (recorded_sum < 0)
 354         return HEADER_FAILURE;
 355 
 356     if (unsigned_sum != recorded_sum && signed_sum != recorded_sum)
 357         return HEADER_FAILURE;
 358 
 359     return HEADER_SUCCESS;
 360 }
 361 
 362 /* --------------------------------------------------------------------------------------------- */
 363 
 364 static void
 365 tar_decode_header (union block *header, tar_super_t *arch)
     /* [previous][next][first][last][top][bottom][index][help]  */
 366 {
 367     gboolean hbits = FALSE;
 368 
 369     current_stat_info.stat.st_mode = MODE_FROM_HEADER (header->header.mode, &hbits);
 370 
 371     /*
 372      * Try to determine the archive format.
 373      */
 374     if (arch->type == TAR_UNKNOWN)
 375     {
 376         if (strcmp (header->header.magic, TMAGIC) == 0)
 377         {
 378             if (header->star_header.prefix[130] == 0
 379                 && is_octal_digit (header->star_header.atime[0])
 380                 && header->star_header.atime[11] == ' '
 381                 && is_octal_digit (header->star_header.ctime[0])
 382                 && header->star_header.ctime[11] == ' ')
 383                 arch->type = TAR_STAR;
 384             else if (current_stat_info.xhdr.buffer != NULL)
 385                 arch->type = TAR_POSIX;
 386             else
 387                 arch->type = TAR_USTAR;
 388         }
 389         else if (strcmp (header->buffer + offsetof (struct posix_header, magic), OLDGNU_MAGIC) == 0)
 390             arch->type = hbits ? TAR_OLDGNU : TAR_GNU;
 391         else
 392             arch->type = TAR_V7;
 393     }
 394 
 395     /*
 396      * typeflag on BSDI tar (pax) always '\000'
 397      */
 398     if (header->header.typeflag == '\000')
 399     {
 400         size_t len;
 401 
 402         if (header->header.name[sizeof (header->header.name) - 1] != '\0')
 403             len = sizeof (header->header.name);
 404         else
 405             len = strlen (header->header.name);
 406 
 407         if (len != 0 && IS_PATH_SEP (header->header.name[len - 1]))
 408             header->header.typeflag = DIRTYPE;
 409     }
 410 
 411     if (header->header.typeflag == GNUTYPE_DUMPDIR)
 412         if (arch->type == TAR_UNKNOWN)
 413             arch->type = TAR_GNU;
 414 }
 415 
 416 /* --------------------------------------------------------------------------------------------- */
 417 
 418 static void
 419 tar_fill_stat (struct vfs_s_super *archive, union block *header)
     /* [previous][next][first][last][top][bottom][index][help]  */
 420 {
 421     tar_super_t *arch = TAR_SUPER (archive);
 422 
 423     /* Adjust current_stat_info.stat.st_mode because there are tar-files with
 424      * typeflag==SYMTYPE and S_ISLNK(mod)==0. I don't
 425      * know about the other modes but I think I cause no new
 426      * problem when I adjust them, too. -- Norbert.
 427      */
 428     if (header->header.typeflag == DIRTYPE || header->header.typeflag == GNUTYPE_DUMPDIR)
 429         current_stat_info.stat.st_mode |= S_IFDIR;
 430     else if (header->header.typeflag == SYMTYPE)
 431         current_stat_info.stat.st_mode |= S_IFLNK;
 432     else if (header->header.typeflag == CHRTYPE)
 433         current_stat_info.stat.st_mode |= S_IFCHR;
 434     else if (header->header.typeflag == BLKTYPE)
 435         current_stat_info.stat.st_mode |= S_IFBLK;
 436     else if (header->header.typeflag == FIFOTYPE)
 437         current_stat_info.stat.st_mode |= S_IFIFO;
 438     else
 439         current_stat_info.stat.st_mode |= S_IFREG;
 440 
 441     current_stat_info.stat.st_dev = 0;
 442 #ifdef HAVE_STRUCT_STAT_ST_RDEV
 443     current_stat_info.stat.st_rdev = 0;
 444 #endif
 445 
 446     switch (arch->type)
 447     {
 448     case TAR_USTAR:
 449     case TAR_POSIX:
 450     case TAR_GNU:
 451     case TAR_OLDGNU:
 452         current_stat_info.stat.st_uid = *header->header.uname != '\0'
 453             ? (uid_t) vfs_finduid (header->header.uname)
 454             : UID_FROM_HEADER (header->header.uid);
 455         current_stat_info.stat.st_gid = *header->header.gname != '\0'
 456             ? (gid_t) vfs_findgid (header->header.gname)
 457             : GID_FROM_HEADER (header->header.gid);
 458 
 459         switch (header->header.typeflag)
 460         {
 461         case BLKTYPE:
 462         case CHRTYPE:
 463 #ifdef HAVE_STRUCT_STAT_ST_RDEV
 464             current_stat_info.stat.st_rdev = makedev (MAJOR_FROM_HEADER (header->header.devmajor),
 465                                                       MINOR_FROM_HEADER (header->header.devminor));
 466 #endif
 467             break;
 468         default:
 469             break;
 470         }
 471         break;
 472 
 473     default:
 474         current_stat_info.stat.st_uid = UID_FROM_HEADER (header->header.uid);
 475         current_stat_info.stat.st_gid = GID_FROM_HEADER (header->header.gid);
 476         break;
 477     }
 478 
 479     current_stat_info.atime.tv_nsec = 0;
 480     current_stat_info.mtime.tv_nsec = 0;
 481     current_stat_info.ctime.tv_nsec = 0;
 482 
 483     current_stat_info.mtime.tv_sec = TIME_FROM_HEADER (header->header.mtime);
 484     if (arch->type == TAR_GNU || arch->type == TAR_OLDGNU)
 485     {
 486         current_stat_info.atime.tv_sec = TIME_FROM_HEADER (header->oldgnu_header.atime);
 487         current_stat_info.ctime.tv_sec = TIME_FROM_HEADER (header->oldgnu_header.ctime);
 488     }
 489     else if (arch->type == TAR_STAR)
 490     {
 491         current_stat_info.atime.tv_sec = TIME_FROM_HEADER (header->star_header.atime);
 492         current_stat_info.ctime.tv_sec = TIME_FROM_HEADER (header->star_header.ctime);
 493     }
 494     else
 495         current_stat_info.atime = current_stat_info.ctime = start_time;
 496 
 497 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
 498     current_stat_info.stat.st_blksize = 8 * 1024;  // FIXME
 499 #endif
 500     vfs_adjust_stat (&current_stat_info.stat);
 501 }
 502 
 503 /* --------------------------------------------------------------------------------------------- */
 504 
 505 static void
 506 tar_free_inode (struct vfs_class *me, struct vfs_s_inode *ino)
     /* [previous][next][first][last][top][bottom][index][help]  */
 507 {
 508     (void) me;
 509 
 510     // free sparse_map
 511     if (ino->user_data != NULL)
 512         g_array_free ((GArray *) ino->user_data, TRUE);
 513 }
 514 
 515 /* --------------------------------------------------------------------------------------------- */
 516 
 517 static read_header
 518 tar_insert_entry (struct vfs_class *me, struct vfs_s_super *archive, union block *header,
     /* [previous][next][first][last][top][bottom][index][help]  */
 519                   struct vfs_s_inode **inode)
 520 {
 521     char *p, *q;
 522     char *file_name = current_stat_info.file_name;
 523     char *link_name = current_stat_info.link_name;
 524     size_t len;
 525     struct vfs_s_inode *parent;
 526     struct vfs_s_entry *entry;
 527 
 528     p = strrchr (file_name, PATH_SEP);
 529     if (p == NULL)
 530     {
 531         len = strlen (file_name);
 532         p = file_name;
 533         q = file_name + len;  // ""
 534     }
 535     else
 536     {
 537         *(p++) = '\0';
 538         q = file_name;
 539     }
 540 
 541     parent = vfs_s_find_inode (me, archive, q, LINK_NO_FOLLOW, FL_MKDIR);
 542     if (parent == NULL)
 543         return HEADER_FAILURE;
 544 
 545     *inode = NULL;
 546 
 547     if (header->header.typeflag == LNKTYPE)
 548     {
 549         if (*link_name != '\0')
 550         {
 551             len = strlen (link_name);
 552             if (IS_PATH_SEP (link_name[len - 1]))
 553                 link_name[len - 1] = '\0';
 554 
 555             *inode = vfs_s_find_inode (me, archive, link_name, LINK_NO_FOLLOW, FL_NONE);
 556         }
 557 
 558         if (*inode == NULL)
 559             return HEADER_FAILURE;
 560     }
 561     else
 562     {
 563         if (S_ISDIR (current_stat_info.stat.st_mode))
 564         {
 565             entry = VFS_SUBCLASS (me)->find_entry (me, parent, p, LINK_NO_FOLLOW, FL_NONE);
 566             if (entry != NULL)
 567                 return HEADER_SUCCESS;
 568         }
 569 
 570         *inode = vfs_s_new_inode (me, archive, &current_stat_info.stat);
 571         // assgin timestamps after decoding of extended headers
 572         (*inode)->st.st_mtime = current_stat_info.mtime.tv_sec;
 573         (*inode)->st.st_atime = current_stat_info.atime.tv_sec;
 574         (*inode)->st.st_ctime = current_stat_info.ctime.tv_sec;
 575 
 576         if (link_name != NULL && *link_name != '\0')
 577             (*inode)->linkname = g_strdup (link_name);
 578     }
 579 
 580     entry = vfs_s_new_entry (me, p, *inode);
 581     vfs_s_insert_entry (me, parent, entry);
 582 
 583     return HEADER_SUCCESS;
 584 }
 585 
 586 /* --------------------------------------------------------------------------------------------- */
 587 
 588 static read_header
 589 tar_read_header (struct vfs_class *me, struct vfs_s_super *archive)
     /* [previous][next][first][last][top][bottom][index][help]  */
 590 {
 591     tar_super_t *arch = TAR_SUPER (archive);
 592     union block *header;
 593     union block *next_long_name = NULL, *next_long_link = NULL;
 594     read_header status = HEADER_SUCCESS;
 595 
 596     while (TRUE)
 597     {
 598         header = tar_find_next_block (arch);
 599         current_header = header;
 600         if (header == NULL)
 601         {
 602             status = HEADER_END_OF_FILE;
 603             goto ret;
 604         }
 605 
 606         status = tar_checksum (header);
 607         if (status != HEADER_SUCCESS)
 608             goto ret;
 609 
 610         if (header->header.typeflag == LNKTYPE || header->header.typeflag == DIRTYPE)
 611             current_stat_info.stat.st_size = 0;  // Links 0 size on tape
 612         else
 613         {
 614             current_stat_info.stat.st_size = OFF_FROM_HEADER (header->header.size);
 615             if (current_stat_info.stat.st_size < 0)
 616             {
 617                 status = HEADER_FAILURE;
 618                 goto ret;
 619             }
 620         }
 621 
 622         tar_decode_header (header, arch);
 623         tar_fill_stat (archive, header);
 624 
 625         if (header->header.typeflag == GNUTYPE_LONGNAME
 626             || header->header.typeflag == GNUTYPE_LONGLINK)
 627         {
 628             off_t size;
 629             union block *header_copy;
 630             char *bp;
 631             size_t written;
 632 
 633             if (arch->type == TAR_UNKNOWN)
 634                 arch->type = TAR_GNU;
 635 
 636             if (ckd_add (&size, current_stat_info.stat.st_size, 2 * BLOCKSIZE - 1))
 637             {
 638                 message (D_ERROR, MSG_ERROR, _ ("Inconsistent tar archive"));
 639                 status = HEADER_FAILURE;
 640                 goto ret;
 641             }
 642 
 643             size -= size % BLOCKSIZE;
 644 
 645             header_copy = g_malloc (size + 1);
 646 
 647             if (header->header.typeflag == GNUTYPE_LONGNAME)
 648             {
 649                 g_free (next_long_name);
 650                 next_long_name = header_copy;
 651             }
 652             else
 653             {
 654                 g_free (next_long_link);
 655                 next_long_link = header_copy;
 656             }
 657 
 658             tar_set_next_block_after (header);
 659             *header_copy = *header;
 660             bp = header_copy->buffer + BLOCKSIZE;
 661 
 662             for (size -= BLOCKSIZE; size > 0; size -= written)
 663             {
 664                 union block *data_block;
 665 
 666                 data_block = tar_find_next_block (arch);
 667                 if (data_block == NULL)
 668                 {
 669                     message (D_ERROR, MSG_ERROR, _ ("Unexpected EOF on archive file"));
 670                     status = HEADER_FAILURE;
 671                     goto ret;
 672                 }
 673 
 674                 written = tar_available_space_after (data_block);
 675                 if ((off_t) written > size)
 676                     written = (size_t) size;
 677 
 678                 memcpy (bp, data_block->buffer, written);
 679                 bp += written;
 680                 tar_set_next_block_after ((union block *) (data_block->buffer + written - 1));
 681             }
 682 
 683             *bp = '\0';
 684         }
 685         else if (header->header.typeflag == XHDTYPE || header->header.typeflag == SOLARIS_XHDTYPE)
 686         {
 687             if (arch->type == TAR_UNKNOWN)
 688                 arch->type = TAR_POSIX;
 689             if (!tar_xheader_read (arch, &current_stat_info.xhdr, header,
 690                                    OFF_FROM_HEADER (header->header.size)))
 691             {
 692                 message (D_ERROR, MSG_ERROR, _ ("Unexpected EOF on archive file"));
 693                 status = HEADER_FAILURE;
 694                 goto ret;
 695             }
 696         }
 697         else if (header->header.typeflag == XGLTYPE)
 698         {
 699             struct xheader xhdr;
 700             gboolean ok;
 701 
 702             if (arch->type == TAR_UNKNOWN)
 703                 arch->type = TAR_POSIX;
 704 
 705             memset (&xhdr, 0, sizeof (xhdr));
 706             tar_xheader_read (arch, &xhdr, header, OFF_FROM_HEADER (header->header.size));
 707             ok = tar_xheader_decode_global (&xhdr);
 708             tar_xheader_destroy (&xhdr);
 709 
 710             if (!ok)
 711             {
 712                 message (D_ERROR, MSG_ERROR, _ ("Inconsistent tar archive"));
 713                 status = HEADER_FAILURE;
 714                 goto ret;
 715             }
 716         }
 717         else
 718             break;
 719     }
 720 
 721     {
 722         static union block *recent_long_name = NULL, *recent_long_link = NULL;
 723         struct posix_header const *h = &header->header;
 724         char *file_name = NULL;
 725         char *link_name;
 726         struct vfs_s_inode *inode = NULL;
 727 
 728         g_free (recent_long_name);
 729 
 730         if (next_long_name != NULL)
 731         {
 732             file_name = g_strdup (next_long_name->buffer + BLOCKSIZE);
 733             recent_long_name = next_long_name;
 734         }
 735         else
 736         {
 737             // Accept file names as specified by POSIX.1-1996 section 10.1.1.
 738             char *s1 = NULL;
 739             char *s2;
 740 
 741             // Don't parse TAR_OLDGNU incremental headers as POSIX prefixes.
 742             if (h->prefix[0] != '\0' && strcmp (h->magic, TMAGIC) == 0)
 743                 s1 = g_strndup (h->prefix, sizeof (h->prefix));
 744 
 745             s2 = g_strndup (h->name, sizeof (h->name));
 746 
 747             if (s1 == NULL)
 748                 file_name = s2;
 749             else
 750             {
 751                 file_name = g_strconcat (s1, PATH_SEP_STR, s2, (char *) NULL);
 752                 g_free (s1);
 753                 g_free (s2);
 754             }
 755 
 756             recent_long_name = NULL;
 757         }
 758 
 759         tar_assign_string_dup (&current_stat_info.orig_file_name, file_name);
 760         tar_assign_string (&current_stat_info.file_name, file_name);
 761 
 762         g_free (recent_long_link);
 763 
 764         if (next_long_link != NULL)
 765         {
 766             link_name = g_strdup (next_long_link->buffer + BLOCKSIZE);
 767             recent_long_link = next_long_link;
 768         }
 769         else
 770         {
 771             link_name = g_strndup (h->linkname, sizeof (h->linkname));
 772             recent_long_link = NULL;
 773         }
 774 
 775         tar_assign_string (&current_stat_info.link_name, link_name);
 776 
 777         if (current_stat_info.xhdr.buffer != NULL && !tar_xheader_decode (&current_stat_info))
 778         {
 779             status = HEADER_FAILURE;
 780             goto ret;
 781         }
 782 
 783         if (tar_sparse_member_p (arch, &current_stat_info))
 784         {
 785             if (!tar_sparse_fixup_header (arch, &current_stat_info))
 786             {
 787                 status = HEADER_FAILURE;
 788                 goto ret;
 789             }
 790 
 791             current_stat_info.is_sparse = TRUE;
 792         }
 793         else
 794         {
 795             current_stat_info.is_sparse = FALSE;
 796 
 797             if (((arch->type == TAR_GNU || arch->type == TAR_OLDGNU)
 798                  && current_header->header.typeflag == GNUTYPE_DUMPDIR)
 799                 || current_stat_info.dumpdir != NULL)
 800                 current_stat_info.is_dumpdir = TRUE;
 801         }
 802 
 803         // Do this after decoding of all headers occupied with long file/directory name
 804         canonicalize_pathname (current_stat_info.file_name);
 805 
 806         status = tar_insert_entry (me, archive, header, &inode);
 807         if (status != HEADER_SUCCESS)
 808         {
 809             message (D_ERROR, MSG_ERROR, _ ("Inconsistent tar archive"));
 810             goto ret;
 811         }
 812 
 813         if (recent_long_name == next_long_name)
 814             recent_long_name = NULL;
 815 
 816         if (recent_long_link == next_long_link)
 817             recent_long_link = NULL;
 818 
 819         if (tar_skip_member (arch, inode))
 820             status = HEADER_SUCCESS;
 821         else if (hit_eof)
 822             status = HEADER_END_OF_FILE;
 823         else
 824             status = HEADER_FAILURE;
 825     }
 826 
 827 ret:
 828     g_free (next_long_name);
 829     g_free (next_long_link);
 830 
 831     return status;
 832 }
 833 
 834 /* --------------------------------------------------------------------------------------------- */
 835 
 836 static struct vfs_s_super *
 837 tar_new_archive (struct vfs_class *me)
     /* [previous][next][first][last][top][bottom][index][help]  */
 838 {
 839     tar_super_t *arch;
 840     gint64 usec;
 841 
 842     arch = g_new0 (tar_super_t, 1);
 843     arch->base.me = me;
 844     arch->fd = -1;
 845     arch->type = TAR_UNKNOWN;
 846 
 847     // Prepare global data needed for tar_find_next_block:
 848     record_start_block = 0;
 849     arch->record_start = g_malloc (record_size);
 850     record_end = arch->record_start;  // set up for 1st record = # 0
 851     current_block = arch->record_start;
 852     hit_eof = FALSE;
 853 
 854     // time in microseconds
 855     usec = g_get_real_time ();
 856     // time in seconds and nanoseconds
 857     start_time.tv_sec = usec / G_USEC_PER_SEC;
 858     start_time.tv_nsec = (usec % G_USEC_PER_SEC) * 1000;
 859 
 860     return VFS_SUPER (arch);
 861 }
 862 
 863 /* --------------------------------------------------------------------------------------------- */
 864 
 865 static void
 866 tar_free_archive (struct vfs_class *me, struct vfs_s_super *archive)
     /* [previous][next][first][last][top][bottom][index][help]  */
 867 {
 868     tar_super_t *arch = TAR_SUPER (archive);
 869 
 870     (void) me;
 871 
 872     if (arch->fd != -1)
 873     {
 874         mc_close (arch->fd);
 875         arch->fd = -1;
 876     }
 877 
 878     g_free (arch->record_start);
 879     tar_stat_destroy (&current_stat_info);
 880 }
 881 
 882 /* --------------------------------------------------------------------------------------------- */
 883 
 884 /* Returns status of the tar archive open */
 885 static gboolean
 886 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]  */
 887 {
 888     tar_super_t *arch = TAR_SUPER (archive);
 889     int result, type;
 890     mode_t mode;
 891     struct vfs_s_inode *root;
 892 
 893     result = mc_open (vpath, O_RDONLY);
 894     if (result == -1)
 895     {
 896         message (D_ERROR, MSG_ERROR, _ ("Cannot open tar archive\n%s"), vfs_path_as_str (vpath));
 897         ERRNOR (ENOENT, FALSE);
 898     }
 899 
 900     archive->name = g_strdup (vfs_path_as_str (vpath));
 901     mc_stat (vpath, &arch->st);
 902 
 903     // Find out the method to handle this tar file
 904     type = get_compression_type (result, archive->name);
 905     if (type == COMPRESSION_NONE)
 906         mc_lseek (result, 0, SEEK_SET);
 907     else
 908     {
 909         char *s;
 910         vfs_path_t *tmp_vpath;
 911 
 912         mc_close (result);
 913         s = g_strconcat (archive->name, decompress_extension (type), (char *) NULL);
 914         tmp_vpath = vfs_path_from_str_flags (s, VPF_NO_CANON);
 915         result = mc_open (tmp_vpath, O_RDONLY);
 916         vfs_path_free (tmp_vpath, TRUE);
 917         if (result == -1)
 918             message (D_ERROR, MSG_ERROR, _ ("Cannot open tar archive\n%s"), s);
 919         g_free (s);
 920         if (result == -1)
 921         {
 922             MC_PTR_FREE (archive->name);
 923             ERRNOR (ENOENT, FALSE);
 924         }
 925     }
 926 
 927     arch->fd = result;
 928     mode = arch->st.st_mode & 07777;
 929     if (mode & 0400)
 930         mode |= 0100;
 931     if (mode & 0040)
 932         mode |= 0010;
 933     if (mode & 0004)
 934         mode |= 0001;
 935     mode |= S_IFDIR;
 936 
 937     root = vfs_s_new_inode (me, archive, &arch->st);
 938     root->st.st_mode = mode;
 939     root->data_offset = -1;
 940     root->st.st_nlink++;
 941     root->st.st_dev = VFS_SUBCLASS (me)->rdev++;
 942 
 943     archive->root = root;
 944 
 945     return TRUE;
 946 }
 947 
 948 /* --------------------------------------------------------------------------------------------- */
 949 /**
 950  * Main loop for reading an archive.
 951  * Returns 0 on success, -1 on error.
 952  */
 953 static int
 954 tar_open_archive (struct vfs_s_super *archive, const vfs_path_t *vpath,
     /* [previous][next][first][last][top][bottom][index][help]  */
 955                   const vfs_path_element_t *vpath_element)
 956 {
 957     tar_super_t *arch = TAR_SUPER (archive);
 958     // Initial status at start of archive
 959     read_header status = HEADER_STILL_UNREAD;
 960 
 961     // Open for reading
 962     if (!tar_open_archive_int (vpath_element->class, vpath, archive))
 963         return -1;
 964 
 965     tar_find_next_block (arch);
 966 
 967     while (TRUE)
 968     {
 969         read_header prev_status;
 970 
 971         prev_status = status;
 972         tar_stat_destroy (&current_stat_info);
 973         status = tar_read_header (vpath_element->class, archive);
 974 
 975         switch (status)
 976         {
 977         case HEADER_STILL_UNREAD:
 978             message (D_ERROR, MSG_ERROR, _ ("%s\ndoesn't look like a tar archive"),
 979                      vfs_path_as_str (vpath));
 980             return -1;
 981 
 982         case HEADER_SUCCESS:
 983             continue;
 984 
 985             // Record of zeroes
 986         case HEADER_ZERO_BLOCK:
 987             tar_set_next_block_after (current_header);
 988             (void) tar_read_header (vpath_element->class, archive);
 989             status = prev_status;
 990             continue;
 991 
 992         case HEADER_END_OF_FILE:
 993             break;
 994 
 995             /* Invalid header:
 996              * If the previous header was good, tell them that we are skipping bad ones. */
 997         case HEADER_FAILURE:
 998             tar_set_next_block_after (current_header);
 999 
1000             switch (prev_status)
1001             {
1002             case HEADER_STILL_UNREAD:
1003                 message (D_ERROR, MSG_ERROR, _ ("%s\ndoesn't look like a tar archive"),
1004                          vfs_path_as_str (vpath));
1005                 return -1;
1006 
1007             case HEADER_ZERO_BLOCK:
1008             case HEADER_SUCCESS:
1009                 // Skipping to next header.
1010                 break;  // AB: FIXME
1011 
1012             case HEADER_END_OF_FILE:
1013             case HEADER_FAILURE:
1014                 // We are in the middle of a cascade of errors.
1015                 // AB: FIXME: TODO: show an error message here
1016                 return -1;
1017 
1018             default:
1019                 break;
1020             }
1021             continue;
1022 
1023         default:
1024             break;
1025         }
1026         break;
1027     }
1028 
1029     return 0;
1030 }
1031 
1032 /* --------------------------------------------------------------------------------------------- */
1033 
1034 static void *
1035 tar_super_check (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1036 {
1037     static struct stat stat_buf;
1038     int stat_result;
1039 
1040     stat_result = mc_stat (vpath, &stat_buf);
1041 
1042     return (stat_result != 0) ? NULL : &stat_buf;
1043 }
1044 
1045 /* --------------------------------------------------------------------------------------------- */
1046 
1047 static int
1048 tar_super_same (const vfs_path_element_t *vpath_element, struct vfs_s_super *parc,
     /* [previous][next][first][last][top][bottom][index][help]  */
1049                 const vfs_path_t *vpath, void *cookie)
1050 {
1051     struct stat *archive_stat = cookie;  // stat of main archive
1052 
1053     (void) vpath_element;
1054 
1055     if (strcmp (parc->name, vfs_path_as_str (vpath)) != 0)
1056         return 0;
1057 
1058     // Has the cached archive been changed on the disk?
1059     if (parc != NULL && TAR_SUPER (parc)->st.st_mtime < archive_stat->st_mtime)
1060     {
1061         // Yes, reload!
1062         vfs_tarfs_ops->free ((vfsid) parc);
1063         vfs_rmstamp (vfs_tarfs_ops, (vfsid) parc);
1064         return 2;
1065     }
1066     // Hasn't been modified, give it a new timeout
1067     vfs_stamp (vfs_tarfs_ops, (vfsid) parc);
1068     return 1;
1069 }
1070 
1071 /* --------------------------------------------------------------------------------------------- */
1072 /* Get index of current data chunk in a sparse file.
1073  *
1074  * @param sparse_map map of the sparse file
1075  * @param offset offset in the sparse file
1076  *
1077  * @return an index of a hole or a data chunk
1078  *      positive: pointer to the data chunk;
1079  *      negative: pointer to the hole before data chunk;
1080  *      zero: pointer to the hole after last data chunk
1081  *
1082  *         +--------+--------+-------+--------+-----+-------+--------+---------+
1083  *         |  hole1 | chunk1 | hole2 | chunk2 | ... | holeN | chunkN | holeN+1 |
1084  *         +--------+--------+-------+--------+-----+-------+--------+---------+
1085  *             -1       1        -2      2             -N       N         0
1086  */
1087 
1088 static ssize_t
1089 tar_get_sparse_chunk_idx (const GArray *sparse_map, off_t offset)
     /* [previous][next][first][last][top][bottom][index][help]  */
1090 {
1091     size_t k;
1092 
1093     for (k = 1; k <= sparse_map->len; k++)
1094     {
1095         const struct sp_array *chunk;
1096 
1097         chunk = &g_array_index (sparse_map, struct sp_array, k - 1);
1098 
1099         // are we in the current chunk?
1100         if (offset >= chunk->offset && offset < chunk->offset + chunk->numbytes)
1101             return k;
1102 
1103         // are we before the current chunk?
1104         if (offset < chunk->offset)
1105             return -k;
1106     }
1107 
1108     // after the last chunk
1109     return 0;
1110 }
1111 
1112 /* --------------------------------------------------------------------------------------------- */
1113 
1114 static ssize_t
1115 tar_read_sparse (vfs_file_handler_t *fh, char *buffer, size_t count)
     /* [previous][next][first][last][top][bottom][index][help]  */
1116 {
1117     int fd = TAR_SUPER (fh->ino->super)->fd;
1118     const GArray *sm = (const GArray *) fh->ino->user_data;
1119     ssize_t chunk_idx;
1120     const struct sp_array *chunk;
1121     off_t remain;
1122     ssize_t res;
1123 
1124     chunk_idx = tar_get_sparse_chunk_idx (sm, fh->pos);
1125     if (chunk_idx > 0)
1126     {
1127         // we are in the chunk -- read data until chunk end
1128         chunk = &g_array_index (sm, struct sp_array, chunk_idx - 1);
1129         remain = MIN ((off_t) count, chunk->offset + chunk->numbytes - fh->pos);
1130         res = mc_read (fd, buffer, (size_t) remain);
1131     }
1132     else
1133     {
1134         if (chunk_idx == 0)
1135         {
1136             // we are in the hole after last chunk -- return zeros until file end
1137             remain = MIN ((off_t) count, fh->ino->st.st_size - fh->pos);
1138             // FIXME: can remain be negative?
1139             remain = MAX (remain, 0);
1140         }
1141         else  // chunk_idx < 0
1142         {
1143             // we are in the hole -- return zeros until next chunk start
1144             chunk = &g_array_index (sm, struct sp_array, -chunk_idx - 1);
1145             remain = MIN ((off_t) count, chunk->offset - fh->pos);
1146         }
1147 
1148         memset (buffer, 0, (size_t) remain);
1149         res = (ssize_t) remain;
1150     }
1151 
1152     return res;
1153 }
1154 
1155 /* --------------------------------------------------------------------------------------------- */
1156 
1157 static off_t
1158 tar_lseek_sparse (vfs_file_handler_t *fh, off_t offset)
     /* [previous][next][first][last][top][bottom][index][help]  */
1159 {
1160     off_t saved_offset = offset;
1161     int fd = TAR_SUPER (fh->ino->super)->fd;
1162     const GArray *sm = (const GArray *) fh->ino->user_data;
1163     ssize_t chunk_idx;
1164     const struct sp_array *chunk;
1165     off_t res;
1166 
1167     chunk_idx = tar_get_sparse_chunk_idx (sm, offset);
1168     if (chunk_idx > 0)
1169     {
1170         // we are in the chunk
1171 
1172         chunk = &g_array_index (sm, struct sp_array, chunk_idx - 1);
1173         // offset in the chunk
1174         offset -= chunk->offset;
1175         // offset in the archive
1176         offset += chunk->arch_offset;
1177     }
1178     else
1179     {
1180         // we are in the hole
1181 
1182         // we cannot lseek in hole so seek to the hole begin or end
1183         switch (chunk_idx)
1184         {
1185         case -1:
1186             offset = fh->ino->data_offset;
1187             break;
1188 
1189         case 0:
1190             chunk = &g_array_index (sm, struct sp_array, sm->len - 1);
1191             // FIXME: can we seek beyond tar archive EOF here?
1192             offset = chunk->arch_offset + chunk->numbytes;
1193             break;
1194 
1195         default:
1196             chunk = &g_array_index (sm, struct sp_array, -chunk_idx - 1);
1197             offset = chunk->arch_offset + chunk->numbytes;
1198             break;
1199         }
1200     }
1201 
1202     res = mc_lseek (fd, offset, SEEK_SET);
1203     // return requested offset in success
1204     if (res == offset)
1205         res = saved_offset;
1206 
1207     return res;
1208 }
1209 
1210 /* --------------------------------------------------------------------------------------------- */
1211 
1212 static ssize_t
1213 tar_read (void *fh, char *buffer, size_t count)
     /* [previous][next][first][last][top][bottom][index][help]  */
1214 {
1215     struct vfs_class *me = VFS_FILE_HANDLER_SUPER (fh)->me;
1216     vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
1217     int fd = TAR_SUPER (VFS_FILE_HANDLER_SUPER (fh))->fd;
1218     off_t begin = file->pos;
1219     ssize_t res;
1220 
1221     if (file->ino->user_data != NULL)
1222     {
1223         if (tar_lseek_sparse (file, begin) != begin)
1224             ERRNOR (EIO, -1);
1225 
1226         res = tar_read_sparse (file, buffer, count);
1227     }
1228     else
1229     {
1230         begin += file->ino->data_offset;
1231 
1232         if (mc_lseek (fd, begin, SEEK_SET) != begin)
1233             ERRNOR (EIO, -1);
1234 
1235         count = (size_t) MIN ((off_t) count, file->ino->st.st_size - file->pos);
1236         res = mc_read (fd, buffer, count);
1237     }
1238 
1239     if (res == -1)
1240         ERRNOR (errno, -1);
1241 
1242     file->pos += res;
1243     return res;
1244 }
1245 
1246 /* --------------------------------------------------------------------------------------------- */
1247 
1248 static int
1249 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]  */
1250 {
1251     (void) fh;
1252     (void) mode;
1253 
1254     if ((flags & O_ACCMODE) != O_RDONLY)
1255         ERRNOR (EROFS, -1);
1256     return 0;
1257 }
1258 
1259 /* --------------------------------------------------------------------------------------------- */
1260 /*** public functions ****************************************************************************/
1261 /* --------------------------------------------------------------------------------------------- */
1262 
1263 void
1264 vfs_init_tarfs (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1265 {
1266     // FIXME: tarfs used own temp files
1267     vfs_init_subclass (&tarfs_subclass, "tarfs", VFSF_READONLY, "utar");
1268     vfs_tarfs_ops->read = tar_read;
1269     vfs_tarfs_ops->setctl = NULL;
1270     tarfs_subclass.archive_check = tar_super_check;
1271     tarfs_subclass.archive_same = tar_super_same;
1272     tarfs_subclass.new_archive = tar_new_archive;
1273     tarfs_subclass.open_archive = tar_open_archive;
1274     tarfs_subclass.free_archive = tar_free_archive;
1275     tarfs_subclass.free_inode = tar_free_inode;
1276     tarfs_subclass.fh_open = tar_fh_open;
1277     vfs_register_class (vfs_tarfs_ops);
1278 }
1279 
1280 /* --------------------------------------------------------------------------------------------- */

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