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

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