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 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 
 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     unsigned int i;
 331     int unsigned_sum = 0;       /* the POSIX one :-) */
 332     int signed_sum = 0;         /* the Sun one :-( */
 333     int recorded_sum;
 334 
 335     for (i = 0; i < sizeof (*header); i++)
 336     {
 337         unsigned char uc = header->buffer[i];
 338         signed char sc = header->buffer[i];
 339 
 340         unsigned_sum += uc;
 341         signed_sum += sc;
 342     }
 343 
 344     if (unsigned_sum == 0)
 345         return HEADER_ZERO_BLOCK;
 346 
 347     /* Adjust checksum to count the "chksum" field as blanks.  */
 348     for (i = 0; i < sizeof (header->header.chksum); i++)
 349     {
 350         unsigned char uc = header->header.chksum[i];
 351         signed char sc = header->header.chksum[i];
 352 
 353         unsigned_sum -= uc;
 354         signed_sum -= sc;
 355     }
 356 
 357     unsigned_sum += ' ' * sizeof (header->header.chksum);
 358     signed_sum += ' ' * sizeof (header->header.chksum);
 359 
 360     recorded_sum =
 361         tar_from_header (header->header.chksum, sizeof (header->header.chksum), NULL, 0,
 362                          INT_MAX, TRUE);
 363 
 364     if (recorded_sum < 0)
 365         return HEADER_FAILURE;
 366 
 367     if (unsigned_sum != recorded_sum && signed_sum != recorded_sum)
 368         return HEADER_FAILURE;
 369 
 370     return HEADER_SUCCESS;
 371 }
 372 
 373 /* --------------------------------------------------------------------------------------------- */
 374 
 375 static void
 376 tar_decode_header (union block *header, tar_super_t *arch)
     /* [previous][next][first][last][top][bottom][index][help]  */
 377 {
 378     gboolean hbits = FALSE;
 379 
 380     current_stat_info.stat.st_mode = MODE_FROM_HEADER (header->header.mode, &hbits);
 381 
 382     /*
 383      * Try to determine the archive format.
 384      */
 385     if (arch->type == TAR_UNKNOWN)
 386     {
 387         if (strcmp (header->header.magic, TMAGIC) == 0)
 388         {
 389             if (header->star_header.prefix[130] == 0
 390                 && is_octal_digit (header->star_header.atime[0])
 391                 && header->star_header.atime[11] == ' '
 392                 && is_octal_digit (header->star_header.ctime[0])
 393                 && header->star_header.ctime[11] == ' ')
 394                 arch->type = TAR_STAR;
 395             else if (current_stat_info.xhdr.buffer != NULL)
 396                 arch->type = TAR_POSIX;
 397             else
 398                 arch->type = TAR_USTAR;
 399         }
 400         else if (strcmp (header->buffer + offsetof (struct posix_header, magic), OLDGNU_MAGIC) == 0)
 401             arch->type = hbits ? TAR_OLDGNU : TAR_GNU;
 402         else
 403             arch->type = TAR_V7;
 404     }
 405 
 406     /*
 407      * typeflag on BSDI tar (pax) always '\000'
 408      */
 409     if (header->header.typeflag == '\000')
 410     {
 411         size_t len;
 412 
 413         if (header->header.name[sizeof (header->header.name) - 1] != '\0')
 414             len = sizeof (header->header.name);
 415         else
 416             len = strlen (header->header.name);
 417 
 418         if (len != 0 && IS_PATH_SEP (header->header.name[len - 1]))
 419             header->header.typeflag = DIRTYPE;
 420     }
 421 
 422     if (header->header.typeflag == GNUTYPE_DUMPDIR)
 423         if (arch->type == TAR_UNKNOWN)
 424             arch->type = TAR_GNU;
 425 }
 426 
 427 /* --------------------------------------------------------------------------------------------- */
 428 
 429 static void
 430 tar_fill_stat (struct vfs_s_super *archive, union block *header)
     /* [previous][next][first][last][top][bottom][index][help]  */
 431 {
 432     tar_super_t *arch = TAR_SUPER (archive);
 433 
 434     /* Adjust current_stat_info.stat.st_mode because there are tar-files with
 435      * typeflag==SYMTYPE and S_ISLNK(mod)==0. I don't
 436      * know about the other modes but I think I cause no new
 437      * problem when I adjust them, too. -- Norbert.
 438      */
 439     if (header->header.typeflag == DIRTYPE || header->header.typeflag == GNUTYPE_DUMPDIR)
 440         current_stat_info.stat.st_mode |= S_IFDIR;
 441     else if (header->header.typeflag == SYMTYPE)
 442         current_stat_info.stat.st_mode |= S_IFLNK;
 443     else if (header->header.typeflag == CHRTYPE)
 444         current_stat_info.stat.st_mode |= S_IFCHR;
 445     else if (header->header.typeflag == BLKTYPE)
 446         current_stat_info.stat.st_mode |= S_IFBLK;
 447     else if (header->header.typeflag == FIFOTYPE)
 448         current_stat_info.stat.st_mode |= S_IFIFO;
 449     else
 450         current_stat_info.stat.st_mode |= S_IFREG;
 451 
 452     current_stat_info.stat.st_dev = 0;
 453 #ifdef HAVE_STRUCT_STAT_ST_RDEV
 454     current_stat_info.stat.st_rdev = 0;
 455 #endif
 456 
 457     switch (arch->type)
 458     {
 459     case TAR_USTAR:
 460     case TAR_POSIX:
 461     case TAR_GNU:
 462     case TAR_OLDGNU:
 463         /* *INDENT-OFF* */
 464         current_stat_info.stat.st_uid = *header->header.uname != '\0'
 465             ? (uid_t) vfs_finduid (header->header.uname)
 466             : UID_FROM_HEADER (header->header.uid);
 467         current_stat_info.stat.st_gid = *header->header.gname != '\0'
 468             ? (gid_t) vfs_findgid (header->header.gname)
 469             : GID_FROM_HEADER (header->header.gid);
 470         /* *INDENT-ON* */
 471 
 472         switch (header->header.typeflag)
 473         {
 474         case BLKTYPE:
 475         case CHRTYPE:
 476 #ifdef HAVE_STRUCT_STAT_ST_RDEV
 477             current_stat_info.stat.st_rdev =
 478                 makedev (MAJOR_FROM_HEADER (header->header.devmajor),
 479                          MINOR_FROM_HEADER (header->header.devminor));
 480 #endif
 481             break;
 482         default:
 483             break;
 484         }
 485         break;
 486 
 487     default:
 488         current_stat_info.stat.st_uid = UID_FROM_HEADER (header->header.uid);
 489         current_stat_info.stat.st_gid = GID_FROM_HEADER (header->header.gid);
 490         break;
 491     }
 492 
 493     current_stat_info.atime.tv_nsec = 0;
 494     current_stat_info.mtime.tv_nsec = 0;
 495     current_stat_info.ctime.tv_nsec = 0;
 496 
 497     current_stat_info.mtime.tv_sec = TIME_FROM_HEADER (header->header.mtime);
 498     if (arch->type == TAR_GNU || arch->type == TAR_OLDGNU)
 499     {
 500         current_stat_info.atime.tv_sec = TIME_FROM_HEADER (header->oldgnu_header.atime);
 501         current_stat_info.ctime.tv_sec = TIME_FROM_HEADER (header->oldgnu_header.ctime);
 502     }
 503     else if (arch->type == TAR_STAR)
 504     {
 505         current_stat_info.atime.tv_sec = TIME_FROM_HEADER (header->star_header.atime);
 506         current_stat_info.ctime.tv_sec = TIME_FROM_HEADER (header->star_header.ctime);
 507     }
 508     else
 509         current_stat_info.atime = current_stat_info.ctime = start_time;
 510 
 511 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
 512     current_stat_info.stat.st_blksize = 8 * 1024;       /* FIXME */
 513 #endif
 514     vfs_adjust_stat (&current_stat_info.stat);
 515 }
 516 
 517 /* --------------------------------------------------------------------------------------------- */
 518 
 519 static void
 520 tar_free_inode (struct vfs_class *me, struct vfs_s_inode *ino)
     /* [previous][next][first][last][top][bottom][index][help]  */
 521 {
 522     (void) me;
 523 
 524     /* free sparse_map */
 525     if (ino->user_data != NULL)
 526         g_array_free ((GArray *) ino->user_data, TRUE);
 527 }
 528 
 529 /* --------------------------------------------------------------------------------------------- */
 530 
 531 static read_header
 532 tar_insert_entry (struct vfs_class *me, struct vfs_s_super *archive, union block *header,
     /* [previous][next][first][last][top][bottom][index][help]  */
 533                   struct vfs_s_inode **inode)
 534 {
 535     char *p, *q;
 536     char *file_name = current_stat_info.file_name;
 537     char *link_name = current_stat_info.link_name;
 538     size_t len;
 539     struct vfs_s_inode *parent;
 540     struct vfs_s_entry *entry;
 541 
 542     p = strrchr (file_name, PATH_SEP);
 543     if (p == NULL)
 544     {
 545         len = strlen (file_name);
 546         p = file_name;
 547         q = file_name + len;    /* "" */
 548     }
 549     else
 550     {
 551         *(p++) = '\0';
 552         q = file_name;
 553     }
 554 
 555     parent = vfs_s_find_inode (me, archive, q, LINK_NO_FOLLOW, FL_MKDIR);
 556     if (parent == NULL)
 557         return HEADER_FAILURE;
 558 
 559     *inode = NULL;
 560 
 561     if (header->header.typeflag == LNKTYPE)
 562     {
 563         if (*link_name != '\0')
 564         {
 565             len = strlen (link_name);
 566             if (IS_PATH_SEP (link_name[len - 1]))
 567                 link_name[len - 1] = '\0';
 568 
 569             *inode = vfs_s_find_inode (me, archive, link_name, LINK_NO_FOLLOW, FL_NONE);
 570         }
 571 
 572         if (*inode == NULL)
 573             return HEADER_FAILURE;
 574     }
 575     else
 576     {
 577         if (S_ISDIR (current_stat_info.stat.st_mode))
 578         {
 579             entry = VFS_SUBCLASS (me)->find_entry (me, parent, p, LINK_NO_FOLLOW, FL_NONE);
 580             if (entry != NULL)
 581                 return HEADER_SUCCESS;
 582         }
 583 
 584         *inode = vfs_s_new_inode (me, archive, &current_stat_info.stat);
 585         /* assgin timestamps after decoding of extended headers */
 586         (*inode)->st.st_mtime = current_stat_info.mtime.tv_sec;
 587         (*inode)->st.st_atime = current_stat_info.atime.tv_sec;
 588         (*inode)->st.st_ctime = current_stat_info.ctime.tv_sec;
 589 
 590         if (link_name != NULL && *link_name != '\0')
 591             (*inode)->linkname = g_strdup (link_name);
 592     }
 593 
 594     entry = vfs_s_new_entry (me, p, *inode);
 595     vfs_s_insert_entry (me, parent, entry);
 596 
 597     return HEADER_SUCCESS;
 598 }
 599 
 600 /* --------------------------------------------------------------------------------------------- */
 601 
 602 static read_header
 603 tar_read_header (struct vfs_class *me, struct vfs_s_super *archive)
     /* [previous][next][first][last][top][bottom][index][help]  */
 604 {
 605     tar_super_t *arch = TAR_SUPER (archive);
 606     union block *header;
 607     union block *next_long_name = NULL, *next_long_link = NULL;
 608     read_header status = HEADER_SUCCESS;
 609 
 610     while (TRUE)
 611     {
 612         header = tar_find_next_block (arch);
 613         current_header = header;
 614         if (header == NULL)
 615         {
 616             status = HEADER_END_OF_FILE;
 617             goto ret;
 618         }
 619 
 620         status = tar_checksum (header);
 621         if (status != HEADER_SUCCESS)
 622             goto ret;
 623 
 624         if (header->header.typeflag == LNKTYPE || header->header.typeflag == DIRTYPE)
 625             current_stat_info.stat.st_size = 0; /* Links 0 size on tape */
 626         else
 627         {
 628             current_stat_info.stat.st_size = OFF_FROM_HEADER (header->header.size);
 629             if (current_stat_info.stat.st_size < 0)
 630             {
 631                 status = HEADER_FAILURE;
 632                 goto ret;
 633             }
 634         }
 635 
 636         tar_decode_header (header, arch);
 637         tar_fill_stat (archive, header);
 638 
 639         if (header->header.typeflag == GNUTYPE_LONGNAME
 640             || header->header.typeflag == GNUTYPE_LONGLINK)
 641         {
 642             off_t size;
 643             union block *header_copy;
 644             char *bp;
 645             size_t written;
 646 
 647             if (arch->type == TAR_UNKNOWN)
 648                 arch->type = TAR_GNU;
 649 
 650             if (ckd_add (&size, current_stat_info.stat.st_size, 2 * BLOCKSIZE - 1))
 651             {
 652                 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
 653                 status = HEADER_FAILURE;
 654                 goto ret;
 655             }
 656 
 657             size -= size % BLOCKSIZE;
 658 
 659             header_copy = g_malloc (size + 1);
 660 
 661             if (header->header.typeflag == GNUTYPE_LONGNAME)
 662             {
 663                 g_free (next_long_name);
 664                 next_long_name = header_copy;
 665             }
 666             else
 667             {
 668                 g_free (next_long_link);
 669                 next_long_link = header_copy;
 670             }
 671 
 672             tar_set_next_block_after (header);
 673             *header_copy = *header;
 674             bp = header_copy->buffer + BLOCKSIZE;
 675 
 676             for (size -= BLOCKSIZE; size > 0; size -= written)
 677             {
 678                 union block *data_block;
 679 
 680                 data_block = tar_find_next_block (arch);
 681                 if (data_block == NULL)
 682                 {
 683                     g_free (header_copy);
 684                     message (D_ERROR, MSG_ERROR, _("Unexpected EOF on archive file"));
 685                     status = HEADER_FAILURE;
 686                     goto ret;
 687                 }
 688 
 689                 written = tar_available_space_after (data_block);
 690                 if ((off_t) written > size)
 691                     written = (size_t) size;
 692 
 693                 memcpy (bp, data_block->buffer, written);
 694                 bp += written;
 695                 tar_set_next_block_after ((union block *) (data_block->buffer + written - 1));
 696             }
 697 
 698             *bp = '\0';
 699         }
 700         else if (header->header.typeflag == XHDTYPE || header->header.typeflag == SOLARIS_XHDTYPE)
 701         {
 702             if (arch->type == TAR_UNKNOWN)
 703                 arch->type = TAR_POSIX;
 704             if (!tar_xheader_read
 705                 (arch, &current_stat_info.xhdr, header, OFF_FROM_HEADER (header->header.size)))
 706             {
 707                 message (D_ERROR, MSG_ERROR, _("Unexpected EOF on archive file"));
 708                 status = HEADER_FAILURE;
 709                 goto ret;
 710             }
 711         }
 712         else if (header->header.typeflag == XGLTYPE)
 713         {
 714             struct xheader xhdr;
 715             gboolean ok;
 716 
 717             if (arch->type == TAR_UNKNOWN)
 718                 arch->type = TAR_POSIX;
 719 
 720             memset (&xhdr, 0, sizeof (xhdr));
 721             tar_xheader_read (arch, &xhdr, header, OFF_FROM_HEADER (header->header.size));
 722             ok = tar_xheader_decode_global (&xhdr);
 723             tar_xheader_destroy (&xhdr);
 724 
 725             if (!ok)
 726             {
 727                 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
 728                 status = HEADER_FAILURE;
 729                 goto ret;
 730             }
 731         }
 732         else
 733             break;
 734     }
 735 
 736     {
 737         static union block *recent_long_name = NULL, *recent_long_link = NULL;
 738         struct posix_header const *h = &header->header;
 739         char *file_name = NULL;
 740         char *link_name;
 741         struct vfs_s_inode *inode = NULL;
 742 
 743         g_free (recent_long_name);
 744 
 745         if (next_long_name != NULL)
 746         {
 747             file_name = g_strdup (next_long_name->buffer + BLOCKSIZE);
 748             recent_long_name = next_long_name;
 749         }
 750         else
 751         {
 752             /* Accept file names as specified by POSIX.1-1996 section 10.1.1. */
 753             char *s1 = NULL;
 754             char *s2;
 755 
 756             /* Don't parse TAR_OLDGNU incremental headers as POSIX prefixes. */
 757             if (h->prefix[0] != '\0' && strcmp (h->magic, TMAGIC) == 0)
 758                 s1 = g_strndup (h->prefix, sizeof (h->prefix));
 759 
 760             s2 = g_strndup (h->name, sizeof (h->name));
 761 
 762             if (s1 == NULL)
 763                 file_name = s2;
 764             else
 765             {
 766                 file_name = g_strconcat (s1, PATH_SEP_STR, s2, (char *) NULL);
 767                 g_free (s1);
 768                 g_free (s2);
 769             }
 770 
 771             recent_long_name = NULL;
 772         }
 773 
 774         tar_assign_string_dup (&current_stat_info.orig_file_name, file_name);
 775         tar_assign_string (&current_stat_info.file_name, file_name);
 776 
 777         g_free (recent_long_link);
 778 
 779         if (next_long_link != NULL)
 780         {
 781             link_name = g_strdup (next_long_link->buffer + BLOCKSIZE);
 782             recent_long_link = next_long_link;
 783         }
 784         else
 785         {
 786             link_name = g_strndup (h->linkname, sizeof (h->linkname));
 787             recent_long_link = NULL;
 788         }
 789 
 790         tar_assign_string (&current_stat_info.link_name, link_name);
 791 
 792         if (current_stat_info.xhdr.buffer != NULL && !tar_xheader_decode (&current_stat_info))
 793         {
 794             status = HEADER_FAILURE;
 795             goto ret;
 796         }
 797 
 798         if (tar_sparse_member_p (arch, &current_stat_info))
 799         {
 800             if (!tar_sparse_fixup_header (arch, &current_stat_info))
 801             {
 802                 status = HEADER_FAILURE;
 803                 goto ret;
 804             }
 805 
 806             current_stat_info.is_sparse = TRUE;
 807         }
 808         else
 809         {
 810             current_stat_info.is_sparse = FALSE;
 811 
 812             if (((arch->type == TAR_GNU || arch->type == TAR_OLDGNU)
 813                  && current_header->header.typeflag == GNUTYPE_DUMPDIR)
 814                 || current_stat_info.dumpdir != NULL)
 815                 current_stat_info.is_dumpdir = TRUE;
 816         }
 817 
 818         /* Do this after decoding of all headers occupied with long file/directory name. */
 819         canonicalize_pathname (current_stat_info.file_name);
 820 
 821         status = tar_insert_entry (me, archive, header, &inode);
 822         if (status != HEADER_SUCCESS)
 823         {
 824             message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
 825             goto ret;
 826         }
 827 
 828         if (recent_long_name == next_long_name)
 829             recent_long_name = NULL;
 830 
 831         if (recent_long_link == next_long_link)
 832             recent_long_link = NULL;
 833 
 834         if (tar_skip_member (arch, inode))
 835             status = HEADER_SUCCESS;
 836         else if (hit_eof)
 837             status = HEADER_END_OF_FILE;
 838         else
 839             status = HEADER_FAILURE;
 840     }
 841 
 842   ret:
 843     g_free (next_long_name);
 844     g_free (next_long_link);
 845 
 846     return status;
 847 }
 848 
 849 /* --------------------------------------------------------------------------------------------- */
 850 
 851 static struct vfs_s_super *
 852 tar_new_archive (struct vfs_class *me)
     /* [previous][next][first][last][top][bottom][index][help]  */
 853 {
 854     tar_super_t *arch;
 855     gint64 usec;
 856 
 857     arch = g_new0 (tar_super_t, 1);
 858     arch->base.me = me;
 859     arch->fd = -1;
 860     arch->type = TAR_UNKNOWN;
 861 
 862     /* Prepare global data needed for tar_find_next_block: */
 863     record_start_block = 0;
 864     arch->record_start = g_malloc (record_size);
 865     record_end = arch->record_start;    /* set up for 1st record = # 0 */
 866     current_block = arch->record_start;
 867     hit_eof = FALSE;
 868 
 869     /* time in microseconds */
 870     usec = g_get_real_time ();
 871     /* time in seconds and nanoseconds */
 872     start_time.tv_sec = usec / G_USEC_PER_SEC;
 873     start_time.tv_nsec = (usec % G_USEC_PER_SEC) * 1000;
 874 
 875     return VFS_SUPER (arch);
 876 }
 877 
 878 /* --------------------------------------------------------------------------------------------- */
 879 
 880 static void
 881 tar_free_archive (struct vfs_class *me, struct vfs_s_super *archive)
     /* [previous][next][first][last][top][bottom][index][help]  */
 882 {
 883     tar_super_t *arch = TAR_SUPER (archive);
 884 
 885     (void) me;
 886 
 887     if (arch->fd != -1)
 888     {
 889         mc_close (arch->fd);
 890         arch->fd = -1;
 891     }
 892 
 893     g_free (arch->record_start);
 894     tar_stat_destroy (&current_stat_info);
 895 }
 896 
 897 /* --------------------------------------------------------------------------------------------- */
 898 
 899 /* Returns status of the tar archive open */
 900 static gboolean
 901 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]  */
 902 {
 903     tar_super_t *arch = TAR_SUPER (archive);
 904     int result, type;
 905     mode_t mode;
 906     struct vfs_s_inode *root;
 907 
 908     result = mc_open (vpath, O_RDONLY);
 909     if (result == -1)
 910     {
 911         message (D_ERROR, MSG_ERROR, _("Cannot open tar archive\n%s"), vfs_path_as_str (vpath));
 912         ERRNOR (ENOENT, FALSE);
 913     }
 914 
 915     archive->name = g_strdup (vfs_path_as_str (vpath));
 916     mc_stat (vpath, &arch->st);
 917 
 918     /* Find out the method to handle this tar file */
 919     type = get_compression_type (result, archive->name);
 920     if (type == COMPRESSION_NONE)
 921         mc_lseek (result, 0, SEEK_SET);
 922     else
 923     {
 924         char *s;
 925         vfs_path_t *tmp_vpath;
 926 
 927         mc_close (result);
 928         s = g_strconcat (archive->name, decompress_extension (type), (char *) NULL);
 929         tmp_vpath = vfs_path_from_str_flags (s, VPF_NO_CANON);
 930         result = mc_open (tmp_vpath, O_RDONLY);
 931         vfs_path_free (tmp_vpath, TRUE);
 932         if (result == -1)
 933             message (D_ERROR, MSG_ERROR, _("Cannot open tar archive\n%s"), s);
 934         g_free (s);
 935         if (result == -1)
 936         {
 937             MC_PTR_FREE (archive->name);
 938             ERRNOR (ENOENT, FALSE);
 939         }
 940     }
 941 
 942     arch->fd = result;
 943     mode = arch->st.st_mode & 07777;
 944     if (mode & 0400)
 945         mode |= 0100;
 946     if (mode & 0040)
 947         mode |= 0010;
 948     if (mode & 0004)
 949         mode |= 0001;
 950     mode |= S_IFDIR;
 951 
 952     root = vfs_s_new_inode (me, archive, &arch->st);
 953     root->st.st_mode = mode;
 954     root->data_offset = -1;
 955     root->st.st_nlink++;
 956     root->st.st_dev = VFS_SUBCLASS (me)->rdev++;
 957 
 958     archive->root = root;
 959 
 960     return TRUE;
 961 }
 962 
 963 /* --------------------------------------------------------------------------------------------- */
 964 /**
 965  * Main loop for reading an archive.
 966  * Returns 0 on success, -1 on error.
 967  */
 968 static int
 969 tar_open_archive (struct vfs_s_super *archive, const vfs_path_t *vpath,
     /* [previous][next][first][last][top][bottom][index][help]  */
 970                   const vfs_path_element_t *vpath_element)
 971 {
 972     tar_super_t *arch = TAR_SUPER (archive);
 973     /* Initial status at start of archive */
 974     read_header status = HEADER_STILL_UNREAD;
 975 
 976     /* Open for reading */
 977     if (!tar_open_archive_int (vpath_element->class, vpath, archive))
 978         return -1;
 979 
 980     tar_find_next_block (arch);
 981 
 982     while (TRUE)
 983     {
 984         read_header prev_status;
 985 
 986         prev_status = status;
 987         tar_stat_destroy (&current_stat_info);
 988         status = tar_read_header (vpath_element->class, archive);
 989 
 990         switch (status)
 991         {
 992         case HEADER_STILL_UNREAD:
 993             message (D_ERROR, MSG_ERROR, _("%s\ndoesn't look like a tar archive"),
 994                      vfs_path_as_str (vpath));
 995             return -1;
 996 
 997         case HEADER_SUCCESS:
 998             continue;
 999 
1000             /* Record of zeroes */
1001         case HEADER_ZERO_BLOCK:
1002             tar_set_next_block_after (current_header);
1003             (void) tar_read_header (vpath_element->class, archive);
1004             status = prev_status;
1005             continue;
1006 
1007         case HEADER_END_OF_FILE:
1008             break;
1009 
1010             /* Invalid header:
1011              * If the previous header was good, tell them that we are skipping bad ones. */
1012         case HEADER_FAILURE:
1013             tar_set_next_block_after (current_header);
1014 
1015             switch (prev_status)
1016             {
1017             case HEADER_STILL_UNREAD:
1018                 message (D_ERROR, MSG_ERROR, _("%s\ndoesn't look like a tar archive"),
1019                          vfs_path_as_str (vpath));
1020                 return -1;
1021 
1022             case HEADER_ZERO_BLOCK:
1023             case HEADER_SUCCESS:
1024                 /* Skipping to next header. */
1025                 break;          /* AB: FIXME */
1026 
1027             case HEADER_END_OF_FILE:
1028             case HEADER_FAILURE:
1029                 /* We are in the middle of a cascade of errors.  */
1030                 /* AB: FIXME: TODO: show an error message here */
1031                 return -1;
1032 
1033             default:
1034                 break;
1035             }
1036             continue;
1037 
1038         default:
1039             break;
1040         }
1041         break;
1042     }
1043 
1044     return 0;
1045 }
1046 
1047 /* --------------------------------------------------------------------------------------------- */
1048 
1049 static void *
1050 tar_super_check (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1051 {
1052     static struct stat stat_buf;
1053     int stat_result;
1054 
1055     stat_result = mc_stat (vpath, &stat_buf);
1056 
1057     return (stat_result != 0) ? NULL : &stat_buf;
1058 }
1059 
1060 /* --------------------------------------------------------------------------------------------- */
1061 
1062 static int
1063 tar_super_same (const vfs_path_element_t *vpath_element, struct vfs_s_super *parc,
     /* [previous][next][first][last][top][bottom][index][help]  */
1064                 const vfs_path_t *vpath, void *cookie)
1065 {
1066     struct stat *archive_stat = cookie; /* stat of main archive */
1067 
1068     (void) vpath_element;
1069 
1070     if (strcmp (parc->name, vfs_path_as_str (vpath)) != 0)
1071         return 0;
1072 
1073     /* Has the cached archive been changed on the disk? */
1074     if (parc != NULL && TAR_SUPER (parc)->st.st_mtime < archive_stat->st_mtime)
1075     {
1076         /* Yes, reload! */
1077         vfs_tarfs_ops->free ((vfsid) parc);
1078         vfs_rmstamp (vfs_tarfs_ops, (vfsid) parc);
1079         return 2;
1080     }
1081     /* Hasn't been modified, give it a new timeout */
1082     vfs_stamp (vfs_tarfs_ops, (vfsid) parc);
1083     return 1;
1084 }
1085 
1086 /* --------------------------------------------------------------------------------------------- */
1087 /* Get indes of current data chunk in a sparse file.
1088  *
1089  * @param sparse_map map of the sparse file
1090  * @param offset offset in the sparse file
1091  *
1092  * @return an index of ahole or a data chunk
1093  *      positive: pointer to the data chunk;
1094  *      negative: pointer to the hole before data chunk;
1095  *      zero: pointer to the hole after last data chunk
1096  *
1097  *         +--------+--------+-------+--------+-----+-------+--------+---------+
1098  *         |  hole1 | chunk1 | hole2 | chunk2 | ... | holeN | chunkN | holeN+1 |
1099  *         +--------+--------+-------+--------+-----+-------+--------+---------+
1100  *             -1       1        -2      2             -N       N         0
1101  */
1102 
1103 static ssize_t
1104 tar_get_sparse_chunk_idx (const GArray *sparse_map, off_t offset)
     /* [previous][next][first][last][top][bottom][index][help]  */
1105 {
1106     size_t k;
1107 
1108     for (k = 1; k <= sparse_map->len; k++)
1109     {
1110         const struct sp_array *chunk;
1111 
1112         chunk = &g_array_index (sparse_map, struct sp_array, k - 1);
1113 
1114         /* are we in the current chunk? */
1115         if (offset >= chunk->offset && offset < chunk->offset + chunk->numbytes)
1116             return k;
1117 
1118         /* are we before the current chunk? */
1119         if (offset < chunk->offset)
1120             return -k;
1121     }
1122 
1123     /* after the last chunk */
1124     return 0;
1125 }
1126 
1127 /* --------------------------------------------------------------------------------------------- */
1128 
1129 static ssize_t
1130 tar_read_sparse (vfs_file_handler_t *fh, char *buffer, size_t count)
     /* [previous][next][first][last][top][bottom][index][help]  */
1131 {
1132     int fd = TAR_SUPER (fh->ino->super)->fd;
1133     const GArray *sm = (const GArray *) fh->ino->user_data;
1134     ssize_t chunk_idx;
1135     const struct sp_array *chunk;
1136     off_t remain;
1137     ssize_t res;
1138 
1139     chunk_idx = tar_get_sparse_chunk_idx (sm, fh->pos);
1140     if (chunk_idx > 0)
1141     {
1142         /* we are in the chunk -- read data until chunk end */
1143         chunk = &g_array_index (sm, struct sp_array, chunk_idx - 1);
1144         remain = MIN ((off_t) count, chunk->offset + chunk->numbytes - fh->pos);
1145         res = mc_read (fd, buffer, (size_t) remain);
1146     }
1147     else
1148     {
1149         if (chunk_idx == 0)
1150         {
1151             /* we are in the hole after last chunk -- return zeros until file end */
1152             remain = MIN ((off_t) count, fh->ino->st.st_size - fh->pos);
1153             /* FIXME: can remain be negative? */
1154             remain = MAX (remain, 0);
1155         }
1156         else                    /* chunk_idx < 0 */
1157         {
1158             /* we are in the hole -- return zeros until next chunk start */
1159             chunk = &g_array_index (sm, struct sp_array, -chunk_idx - 1);
1160             remain = MIN ((off_t) count, chunk->offset - fh->pos);
1161         }
1162 
1163         memset (buffer, 0, (size_t) remain);
1164         res = (ssize_t) remain;
1165     }
1166 
1167     return res;
1168 }
1169 
1170 /* --------------------------------------------------------------------------------------------- */
1171 
1172 static off_t
1173 tar_lseek_sparse (vfs_file_handler_t *fh, off_t offset)
     /* [previous][next][first][last][top][bottom][index][help]  */
1174 {
1175     off_t saved_offset = offset;
1176     int fd = TAR_SUPER (fh->ino->super)->fd;
1177     const GArray *sm = (const GArray *) fh->ino->user_data;
1178     ssize_t chunk_idx;
1179     const struct sp_array *chunk;
1180     off_t res;
1181 
1182     chunk_idx = tar_get_sparse_chunk_idx (sm, offset);
1183     if (chunk_idx > 0)
1184     {
1185         /* we are in the chunk */
1186 
1187         chunk = &g_array_index (sm, struct sp_array, chunk_idx - 1);
1188         /* offset in the chunk */
1189         offset -= chunk->offset;
1190         /* offset in the archive */
1191         offset += chunk->arch_offset;
1192     }
1193     else
1194     {
1195         /* we are in the hole */
1196 
1197         /* we cannot lseek in hole so seek to the hole begin or end */
1198         switch (chunk_idx)
1199         {
1200         case -1:
1201             offset = fh->ino->data_offset;
1202             break;
1203 
1204         case 0:
1205             chunk = &g_array_index (sm, struct sp_array, sm->len - 1);
1206             /* FIXME: can we seek beyond tar archive EOF here? */
1207             offset = chunk->arch_offset + chunk->numbytes;
1208             break;
1209 
1210         default:
1211             chunk = &g_array_index (sm, struct sp_array, -chunk_idx - 1);
1212             offset = chunk->arch_offset + chunk->numbytes;
1213             break;
1214         }
1215     }
1216 
1217     res = mc_lseek (fd, offset, SEEK_SET);
1218     /* return requested offset in success */
1219     if (res == offset)
1220         res = saved_offset;
1221 
1222     return res;
1223 }
1224 
1225 /* --------------------------------------------------------------------------------------------- */
1226 
1227 static ssize_t
1228 tar_read (void *fh, char *buffer, size_t count)
     /* [previous][next][first][last][top][bottom][index][help]  */
1229 {
1230     struct vfs_class *me = VFS_FILE_HANDLER_SUPER (fh)->me;
1231     vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
1232     int fd = TAR_SUPER (VFS_FILE_HANDLER_SUPER (fh))->fd;
1233     off_t begin = file->pos;
1234     ssize_t res;
1235 
1236     if (file->ino->user_data != NULL)
1237     {
1238         if (tar_lseek_sparse (file, begin) != begin)
1239             ERRNOR (EIO, -1);
1240 
1241         res = tar_read_sparse (file, buffer, count);
1242     }
1243     else
1244     {
1245         begin += file->ino->data_offset;
1246 
1247         if (mc_lseek (fd, begin, SEEK_SET) != begin)
1248             ERRNOR (EIO, -1);
1249 
1250         count = (size_t) MIN ((off_t) count, file->ino->st.st_size - file->pos);
1251         res = mc_read (fd, buffer, count);
1252     }
1253 
1254     if (res == -1)
1255         ERRNOR (errno, -1);
1256 
1257     file->pos += res;
1258     return res;
1259 }
1260 
1261 /* --------------------------------------------------------------------------------------------- */
1262 
1263 static int
1264 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]  */
1265 {
1266     (void) fh;
1267     (void) mode;
1268 
1269     if ((flags & O_ACCMODE) != O_RDONLY)
1270         ERRNOR (EROFS, -1);
1271     return 0;
1272 }
1273 
1274 /* --------------------------------------------------------------------------------------------- */
1275 /*** public functions ****************************************************************************/
1276 /* --------------------------------------------------------------------------------------------- */
1277 
1278 void
1279 vfs_init_tarfs (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1280 {
1281     /* FIXME: tarfs used own temp files */
1282     vfs_init_subclass (&tarfs_subclass, "tarfs", VFSF_READONLY, "utar");
1283     vfs_tarfs_ops->read = tar_read;
1284     vfs_tarfs_ops->setctl = NULL;
1285     tarfs_subclass.archive_check = tar_super_check;
1286     tarfs_subclass.archive_same = tar_super_same;
1287     tarfs_subclass.new_archive = tar_new_archive;
1288     tarfs_subclass.open_archive = tar_open_archive;
1289     tarfs_subclass.free_archive = tar_free_archive;
1290     tarfs_subclass.free_inode = tar_free_inode;
1291     tarfs_subclass.fh_open = tar_fh_open;
1292     vfs_register_class (vfs_tarfs_ops);
1293 }
1294 
1295 /* --------------------------------------------------------------------------------------------- */

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