root/src/vfs/tar/tar.c

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

DEFINITIONS

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

   1 /*
   2    Virtual File System: GNU Tar file system.
   3 
   4    Copyright (C) 1995-2025
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Jakub Jelinek, 1995
   9    Pavel Machek, 1998
  10    Slava Zanko <slavazanko@gmail.com>, 2013
  11 
  12    This file is part of the Midnight Commander.
  13 
  14    The Midnight Commander is free software: you can redistribute it
  15    and/or modify it under the terms of the GNU General Public License as
  16    published by the Free Software Foundation, either version 3 of the License,
  17    or (at your option) any later version.
  18 
  19    The Midnight Commander is distributed in the hope that it will be useful,
  20    but WITHOUT ANY WARRANTY; without even the implied warranty of
  21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22    GNU General Public License for more details.
  23 
  24    You should have received a copy of the GNU General Public License
  25    along with this program.  If not, see <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                     message (D_ERROR, MSG_ERROR, _("Unexpected EOF on archive file"));
 684                     status = HEADER_FAILURE;
 685                     goto ret;
 686                 }
 687 
 688                 written = tar_available_space_after (data_block);
 689                 if ((off_t) written > size)
 690                     written = (size_t) size;
 691 
 692                 memcpy (bp, data_block->buffer, written);
 693                 bp += written;
 694                 tar_set_next_block_after ((union block *) (data_block->buffer + written - 1));
 695             }
 696 
 697             *bp = '\0';
 698         }
 699         else if (header->header.typeflag == XHDTYPE || header->header.typeflag == SOLARIS_XHDTYPE)
 700         {
 701             if (arch->type == TAR_UNKNOWN)
 702                 arch->type = TAR_POSIX;
 703             if (!tar_xheader_read
 704                 (arch, &current_stat_info.xhdr, header, OFF_FROM_HEADER (header->header.size)))
 705             {
 706                 message (D_ERROR, MSG_ERROR, _("Unexpected EOF on archive file"));
 707                 status = HEADER_FAILURE;
 708                 goto ret;
 709             }
 710         }
 711         else if (header->header.typeflag == XGLTYPE)
 712         {
 713             struct xheader xhdr;
 714             gboolean ok;
 715 
 716             if (arch->type == TAR_UNKNOWN)
 717                 arch->type = TAR_POSIX;
 718 
 719             memset (&xhdr, 0, sizeof (xhdr));
 720             tar_xheader_read (arch, &xhdr, header, OFF_FROM_HEADER (header->header.size));
 721             ok = tar_xheader_decode_global (&xhdr);
 722             tar_xheader_destroy (&xhdr);
 723 
 724             if (!ok)
 725             {
 726                 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
 727                 status = HEADER_FAILURE;
 728                 goto ret;
 729             }
 730         }
 731         else
 732             break;
 733     }
 734 
 735     {
 736         static union block *recent_long_name = NULL, *recent_long_link = NULL;
 737         struct posix_header const *h = &header->header;
 738         char *file_name = NULL;
 739         char *link_name;
 740         struct vfs_s_inode *inode = NULL;
 741 
 742         g_free (recent_long_name);
 743 
 744         if (next_long_name != NULL)
 745         {
 746             file_name = g_strdup (next_long_name->buffer + BLOCKSIZE);
 747             recent_long_name = next_long_name;
 748         }
 749         else
 750         {
 751             /* Accept file names as specified by POSIX.1-1996 section 10.1.1. */
 752             char *s1 = NULL;
 753             char *s2;
 754 
 755             /* Don't parse TAR_OLDGNU incremental headers as POSIX prefixes. */
 756             if (h->prefix[0] != '\0' && strcmp (h->magic, TMAGIC) == 0)
 757                 s1 = g_strndup (h->prefix, sizeof (h->prefix));
 758 
 759             s2 = g_strndup (h->name, sizeof (h->name));
 760 
 761             if (s1 == NULL)
 762                 file_name = s2;
 763             else
 764             {
 765                 file_name = g_strconcat (s1, PATH_SEP_STR, s2, (char *) NULL);
 766                 g_free (s1);
 767                 g_free (s2);
 768             }
 769 
 770             recent_long_name = NULL;
 771         }
 772 
 773         tar_assign_string_dup (&current_stat_info.orig_file_name, file_name);
 774         tar_assign_string (&current_stat_info.file_name, file_name);
 775 
 776         g_free (recent_long_link);
 777 
 778         if (next_long_link != NULL)
 779         {
 780             link_name = g_strdup (next_long_link->buffer + BLOCKSIZE);
 781             recent_long_link = next_long_link;
 782         }
 783         else
 784         {
 785             link_name = g_strndup (h->linkname, sizeof (h->linkname));
 786             recent_long_link = NULL;
 787         }
 788 
 789         tar_assign_string (&current_stat_info.link_name, link_name);
 790 
 791         if (current_stat_info.xhdr.buffer != NULL && !tar_xheader_decode (&current_stat_info))
 792         {
 793             status = HEADER_FAILURE;
 794             goto ret;
 795         }
 796 
 797         if (tar_sparse_member_p (arch, &current_stat_info))
 798         {
 799             if (!tar_sparse_fixup_header (arch, &current_stat_info))
 800             {
 801                 status = HEADER_FAILURE;
 802                 goto ret;
 803             }
 804 
 805             current_stat_info.is_sparse = TRUE;
 806         }
 807         else
 808         {
 809             current_stat_info.is_sparse = FALSE;
 810 
 811             if (((arch->type == TAR_GNU || arch->type == TAR_OLDGNU)
 812                  && current_header->header.typeflag == GNUTYPE_DUMPDIR)
 813                 || current_stat_info.dumpdir != NULL)
 814                 current_stat_info.is_dumpdir = TRUE;
 815         }
 816 
 817         /* Do this after decoding of all headers occupied with long file/directory name. */
 818         canonicalize_pathname (current_stat_info.file_name);
 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 index 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 a hole 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 
1294 /* --------------------------------------------------------------------------------------------- */

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