Fix typos in Belorussian & Russian team names in PO-files
[midnight-commander.git] / vfs / tar.c
blobe0b6a20b75a2234f5d582a0687d7ef896a5004f1
1 /* Virtual File System: GNU Tar file system.
2 Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
3 2006, 2007 Free Software Foundation, Inc.
5 Written by: 1995 Jakub Jelinek
6 Rewritten by: 1998 Pavel Machek
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public License
10 as published by the Free Software Foundation; either version 2 of
11 the License, or (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU Library General Public License for more details.
18 You should have received a copy of the GNU Library General Public
19 License along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
22 /**
23 * \file
24 * \brief Source: Virtual File System: GNU Tar file system
25 * \author Jakub Jelinek
26 * \author Pavel Machek
27 * \date 1995, 1998
29 * Namespace: init_tarfs
32 #include <config.h>
33 #include <sys/types.h>
34 #include <errno.h>
35 #include <ctype.h>
36 #include <fcntl.h>
38 #ifdef hpux
39 /* major() and minor() macros (among other things) defined here for hpux */
40 #include <sys/mknod.h>
41 #endif
43 #include "../src/global.h"
45 #include "../src/wtools.h" /* message() */
46 #include "../src/main.h" /* print_vfs_message */
48 #include "utilvfs.h"
49 #include "gc.h" /* vfs_rmstamp */
50 #include "xdirentry.h"
52 static struct vfs_class vfs_tarfs_ops;
54 enum {
55 TAR_UNKNOWN = 0,
56 TAR_V7,
57 TAR_USTAR,
58 TAR_POSIX,
59 TAR_GNU
63 * Header block on tape.
65 * I'm going to use traditional DP naming conventions here.
66 * A "block" is a big chunk of stuff that we do I/O on.
67 * A "record" is a piece of info that we care about.
68 * Typically many "record"s fit into a "block".
70 #define RECORDSIZE 512
71 #define NAMSIZ 100
72 #define PREFIX_SIZE 155
73 #define TUNMLEN 32
74 #define TGNMLEN 32
75 #define SPARSE_EXT_HDR 21
76 #define SPARSE_IN_HDR 4
78 struct sparse {
79 char offset[12];
80 char numbytes[12];
83 struct sp_array {
84 int offset;
85 int numbytes;
88 union record {
89 char charptr[RECORDSIZE];
90 struct header {
91 char arch_name[NAMSIZ];
92 char mode[8];
93 char uid[8];
94 char gid[8];
95 char size[12];
96 char mtime[12];
97 char chksum[8];
98 char linkflag;
99 char arch_linkname[NAMSIZ];
100 char magic[8];
101 char uname[TUNMLEN];
102 char gname[TGNMLEN];
103 char devmajor[8];
104 char devminor[8];
105 /* The following bytes of the tar header record were originally unused.
107 Archives following the ustar specification use almost all of those
108 bytes to support pathnames of 256 characters in length.
110 GNU tar archives use the "unused" space to support incremental
111 archives and sparse files. */
112 union unused {
113 char prefix[PREFIX_SIZE];
114 /* GNU extensions to the ustar (POSIX.1-1988) archive format. */
115 struct oldgnu {
116 char atime[12];
117 char ctime[12];
118 char offset[12];
119 char longnames[4];
120 char pad;
121 struct sparse sp[SPARSE_IN_HDR];
122 char isextended;
123 char realsize[12]; /* true size of the sparse file */
124 } oldgnu;
125 } unused;
126 } header;
127 struct extended_header {
128 struct sparse sp[21];
129 char isextended;
130 } ext_hdr;
133 /* The checksum field is filled with this while the checksum is computed. */
134 #define CHKBLANKS " " /* 8 blanks, no null */
136 /* The magic field is filled with this if uname and gname are valid. */
137 #define TMAGIC "ustar" /* ustar and a null */
138 #define OLDGNU_MAGIC "ustar " /* 7 chars and a null */
140 /* The linkflag defines the type of file */
141 #define LF_OLDNORMAL '\0' /* Normal disk file, Unix compat */
142 #define LF_NORMAL '0' /* Normal disk file */
143 #define LF_LINK '1' /* Link to previously dumped file */
144 #define LF_SYMLINK '2' /* Symbolic link */
145 #define LF_CHR '3' /* Character special file */
146 #define LF_BLK '4' /* Block special file */
147 #define LF_DIR '5' /* Directory */
148 #define LF_FIFO '6' /* FIFO special file */
149 #define LF_CONTIG '7' /* Contiguous file */
150 #define LF_EXTHDR 'x' /* pax Extended Header */
151 #define LF_GLOBAL_EXTHDR 'g' /* pax Global Extended Header */
152 /* Further link types may be defined later. */
154 /* Note that the standards committee allows only capital A through
155 capital Z for user-defined expansion. This means that defining something
156 as, say '8' is a *bad* idea. */
157 #define LF_DUMPDIR 'D' /* This is a dir entry that contains
158 the names of files that were in
159 the dir at the time the dump
160 was made */
161 #define LF_LONGLINK 'K' /* Identifies the NEXT file on the tape
162 as having a long linkname */
163 #define LF_LONGNAME 'L' /* Identifies the NEXT file on the tape
164 as having a long name. */
165 #define LF_MULTIVOL 'M' /* This is the continuation
166 of a file that began on another
167 volume */
168 #define LF_NAMES 'N' /* For storing filenames that didn't
169 fit in 100 characters */
170 #define LF_SPARSE 'S' /* This is for sparse files */
171 #define LF_VOLHDR 'V' /* This file is a tape/volume header */
172 /* Ignore it on extraction */
175 * Exit codes from the "tar" program
177 #define EX_SUCCESS 0 /* success! */
178 #define EX_ARGSBAD 1 /* invalid args */
179 #define EX_BADFILE 2 /* invalid filename */
180 #define EX_BADARCH 3 /* bad archive */
181 #define EX_SYSTEM 4 /* system gave unexpected error */
182 #define EX_BADVOL 5 /* Special error code means
183 Tape volume doesn't match the one
184 specified on the command line */
186 #define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )
189 * Quick and dirty octal conversion.
191 * Result is -1 if the field is invalid (all blank, or nonoctal).
193 static long tar_from_oct (int digs, char *where)
195 register long value;
197 while (isspace ((unsigned char) *where)) { /* Skip spaces */
198 where++;
199 if (--digs <= 0)
200 return -1; /* All blank field */
202 value = 0;
203 while (digs > 0 && isodigit (*where)) { /* Scan till nonoctal */
204 value = (value << 3) | (*where++ - '0');
205 --digs;
208 if (digs > 0 && *where && !isspace ((unsigned char) *where))
209 return -1; /* Ended on non-space/nul */
211 return value;
214 static void tar_free_archive (struct vfs_class *me, struct vfs_s_super *archive)
216 (void) me;
218 if (archive->u.arch.fd != -1)
219 mc_close(archive->u.arch.fd);
222 /* As we open one archive at a time, it is safe to have this static */
223 static int current_tar_position = 0;
225 /* Returns fd of the open tar file */
226 static int
227 tar_open_archive_int (struct vfs_class *me, const char *name,
228 struct vfs_s_super *archive)
230 int result, type;
231 mode_t mode;
232 struct vfs_s_inode *root;
234 result = mc_open (name, O_RDONLY);
235 if (result == -1) {
236 message (D_ERROR, MSG_ERROR, _("Cannot open tar archive\n%s"), name);
237 ERRNOR (ENOENT, -1);
240 archive->name = g_strdup (name);
241 mc_stat (name, &(archive->u.arch.st));
242 archive->u.arch.fd = -1;
243 archive->u.arch.type = TAR_UNKNOWN;
245 /* Find out the method to handle this tar file */
246 type = get_compression_type (result, name);
247 mc_lseek (result, 0, SEEK_SET);
248 if (type != COMPRESSION_NONE) {
249 char *s;
250 mc_close (result);
251 s = g_strconcat (archive->name, decompress_extension (type), (char *) NULL);
252 result = mc_open (s, O_RDONLY);
253 if (result == -1)
254 message (D_ERROR, MSG_ERROR, _("Cannot open tar archive\n%s"), s);
255 g_free (s);
256 if (result == -1)
257 ERRNOR (ENOENT, -1);
260 archive->u.arch.fd = result;
261 mode = archive->u.arch.st.st_mode & 07777;
262 if (mode & 0400)
263 mode |= 0100;
264 if (mode & 0040)
265 mode |= 0010;
266 if (mode & 0004)
267 mode |= 0001;
268 mode |= S_IFDIR;
270 root = vfs_s_new_inode (me, archive, &archive->u.arch.st);
271 root->st.st_mode = mode;
272 root->data_offset = -1;
273 root->st.st_nlink++;
274 root->st.st_dev = MEDATA->rdev++;
276 archive->root = root;
278 return result;
281 static union record rec_buf;
283 static union record *
284 tar_get_next_record (struct vfs_s_super *archive, int tard)
286 int n;
288 (void) archive;
290 n = mc_read (tard, rec_buf.charptr, RECORDSIZE);
291 if (n != RECORDSIZE)
292 return NULL; /* An error has occurred */
293 current_tar_position += RECORDSIZE;
294 return &rec_buf;
297 static void tar_skip_n_records (struct vfs_s_super *archive, int tard, int n)
299 (void) archive;
301 mc_lseek (tard, n * RECORDSIZE, SEEK_CUR);
302 current_tar_position += n * RECORDSIZE;
305 static void
306 tar_fill_stat (struct vfs_s_super *archive, struct stat *st, union record *header,
307 size_t h_size)
309 st->st_mode = tar_from_oct (8, header->header.mode);
311 /* Adjust st->st_mode because there are tar-files with
312 * linkflag==LF_SYMLINK and S_ISLNK(mod)==0. I don't
313 * know about the other modes but I think I cause no new
314 * problem when I adjust them, too. -- Norbert.
316 if (header->header.linkflag == LF_DIR) {
317 st->st_mode |= S_IFDIR;
318 } else if (header->header.linkflag == LF_SYMLINK) {
319 st->st_mode |= S_IFLNK;
320 } else if (header->header.linkflag == LF_CHR) {
321 st->st_mode |= S_IFCHR;
322 } else if (header->header.linkflag == LF_BLK) {
323 st->st_mode |= S_IFBLK;
324 } else if (header->header.linkflag == LF_FIFO) {
325 st->st_mode |= S_IFIFO;
326 } else
327 st->st_mode |= S_IFREG;
329 st->st_rdev = 0;
330 switch (archive->u.arch.type) {
331 case TAR_USTAR:
332 case TAR_POSIX:
333 case TAR_GNU:
334 st->st_uid =
335 *header->header.uname ? vfs_finduid (header->header.
336 uname) : tar_from_oct (8,
337 header->
338 header.
339 uid);
340 st->st_gid =
341 *header->header.gname ? vfs_findgid (header->header.
342 gname) : tar_from_oct (8,
343 header->
344 header.
345 gid);
346 switch (header->header.linkflag) {
347 case LF_BLK:
348 case LF_CHR:
349 st->st_rdev =
350 (tar_from_oct (8, header->header.devmajor) << 8) |
351 tar_from_oct (8, header->header.devminor);
353 default:
354 st->st_uid = tar_from_oct (8, header->header.uid);
355 st->st_gid = tar_from_oct (8, header->header.gid);
357 st->st_size = h_size;
358 st->st_mtime = tar_from_oct (1 + 12, header->header.mtime);
359 st->st_atime = 0;
360 st->st_ctime = 0;
361 if (archive->u.arch.type == TAR_GNU) {
362 st->st_atime = tar_from_oct (1 + 12,
363 header->header.unused.oldgnu.atime);
364 st->st_ctime = tar_from_oct (1 + 12,
365 header->header.unused.oldgnu.ctime);
370 typedef enum {
371 STATUS_BADCHECKSUM,
372 STATUS_SUCCESS,
373 STATUS_EOFMARK,
374 STATUS_EOF
375 } ReadStatus;
377 * Return 1 for success, 0 if the checksum is bad, EOF on eof,
378 * 2 for a record full of zeros (EOF marker).
381 static ReadStatus
382 tar_read_header (struct vfs_class *me, struct vfs_s_super *archive,
383 int tard, size_t *h_size)
385 register int i;
386 register long sum, signed_sum, recsum;
387 register char *p;
388 register union record *header;
389 static char *next_long_name = NULL, *next_long_link = NULL;
391 recurse:
393 header = tar_get_next_record (archive, tard);
394 if (NULL == header)
395 return STATUS_EOF;
397 recsum = tar_from_oct (8, header->header.chksum);
399 sum = 0;
400 signed_sum = 0;
401 p = header->charptr;
402 for (i = sizeof (*header); --i >= 0;) {
404 * We can't use unsigned char here because of old compilers,
405 * e.g. V7.
407 signed_sum += *p;
408 sum += 0xFF & *p++;
411 /* Adjust checksum to count the "chksum" field as blanks. */
412 for (i = sizeof (header->header.chksum); --i >= 0;) {
413 sum -= 0xFF & header->header.chksum[i];
414 signed_sum -= (char) header->header.chksum[i];
416 sum += ' ' * sizeof header->header.chksum;
417 signed_sum += ' ' * sizeof header->header.chksum;
420 * This is a zeroed record...whole record is 0's except
421 * for the 8 blanks we faked for the checksum field.
423 if (sum == 8 * ' ')
424 return STATUS_EOFMARK;
426 if (sum != recsum && signed_sum != recsum)
427 return STATUS_BADCHECKSUM;
430 * Try to determine the archive format.
432 if (archive->u.arch.type == TAR_UNKNOWN) {
433 if (!strcmp (header->header.magic, TMAGIC)) {
434 if (header->header.linkflag == LF_GLOBAL_EXTHDR)
435 archive->u.arch.type = TAR_POSIX;
436 else
437 archive->u.arch.type = TAR_USTAR;
438 } else if (!strcmp (header->header.magic, OLDGNU_MAGIC)) {
439 archive->u.arch.type = TAR_GNU;
444 * linkflag on BSDI tar (pax) always '\000'
446 if (header->header.linkflag == '\000') {
447 if (header->header.arch_name[NAMSIZ - 1] != '\0')
448 i = NAMSIZ;
449 else
450 i = strlen (header->header.arch_name);
452 if (i && header->header.arch_name[i - 1] == '/')
453 header->header.linkflag = LF_DIR;
457 * Good record. Decode file size and return.
459 if (header->header.linkflag == LF_LINK
460 || header->header.linkflag == LF_DIR)
461 *h_size = 0; /* Links 0 size on tape */
462 else
463 *h_size = tar_from_oct (1 + 12, header->header.size);
466 * Skip over directory snapshot info records that
467 * are stored in incremental tar archives.
469 if (header->header.linkflag == LF_DUMPDIR) {
470 if (archive->u.arch.type == TAR_UNKNOWN)
471 archive->u.arch.type = TAR_GNU;
472 return STATUS_SUCCESS;
476 * Skip over pax extended header and global extended
477 * header records.
479 if (header->header.linkflag == LF_EXTHDR ||
480 header->header.linkflag == LF_GLOBAL_EXTHDR) {
481 if (archive->u.arch.type == TAR_UNKNOWN)
482 archive->u.arch.type = TAR_POSIX;
483 return STATUS_SUCCESS;
486 if (header->header.linkflag == LF_LONGNAME
487 || header->header.linkflag == LF_LONGLINK) {
488 char **longp;
489 char *bp, *data;
490 int size, written;
492 if (archive->u.arch.type == TAR_UNKNOWN)
493 archive->u.arch.type = TAR_GNU;
495 if (*h_size > MC_MAXPATHLEN) {
496 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
497 return STATUS_BADCHECKSUM;
500 longp = ((header->header.linkflag == LF_LONGNAME)
501 ? &next_long_name : &next_long_link);
503 g_free (*longp);
504 bp = *longp = g_malloc (*h_size + 1);
506 for (size = *h_size; size > 0; size -= written) {
507 data = tar_get_next_record (archive, tard)->charptr;
508 if (data == NULL) {
509 g_free (*longp);
510 *longp = NULL;
511 message (D_ERROR, MSG_ERROR,
512 _("Unexpected EOF on archive file"));
513 return STATUS_BADCHECKSUM;
515 written = RECORDSIZE;
516 if (written > size)
517 written = size;
519 memcpy (bp, data, written);
520 bp += written;
523 if (bp - *longp == MC_MAXPATHLEN && bp[-1] != '\0') {
524 g_free (*longp);
525 *longp = NULL;
526 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
527 return STATUS_BADCHECKSUM;
529 *bp = 0;
530 goto recurse;
531 } else {
532 struct stat st;
533 struct vfs_s_entry *entry;
534 struct vfs_s_inode *inode, *parent;
535 long data_position;
536 char *q;
537 int len;
538 char *current_file_name, *current_link_name;
540 current_link_name =
541 (next_long_link ? next_long_link :
542 g_strndup (header->header.arch_linkname, NAMSIZ));
543 len = strlen (current_link_name);
544 if (len > 1 && current_link_name[len - 1] == '/')
545 current_link_name[len - 1] = 0;
547 current_file_name = NULL;
548 switch (archive->u.arch.type) {
549 case TAR_USTAR:
550 case TAR_POSIX:
551 /* The ustar archive format supports pathnames of upto 256
552 * characters in length. This is achieved by concatenating
553 * the contents of the `prefix' and `arch_name' fields like
554 * this:
556 * prefix + path_separator + arch_name
558 * If the `prefix' field contains an empty string i.e. its
559 * first characters is '\0' the prefix field is ignored.
561 if (header->header.unused.prefix[0] != '\0') {
562 char *temp_name, *temp_prefix;
564 temp_name = g_strndup (header->header.arch_name, NAMSIZ);
565 temp_prefix = g_strndup (header->header.unused.prefix,
566 PREFIX_SIZE);
567 current_file_name = g_strconcat (temp_prefix, PATH_SEP_STR,
568 temp_name, (char *) NULL);
569 g_free (temp_name);
570 g_free (temp_prefix);
572 break;
573 case TAR_GNU:
574 if (next_long_name != NULL)
575 current_file_name = next_long_name;
576 break;
577 default:
578 break;
581 if (current_file_name == NULL)
582 current_file_name = g_strndup (header->header.arch_name, NAMSIZ);
584 canonicalize_pathname (current_file_name);
585 len = strlen (current_file_name);
587 data_position = current_tar_position;
589 p = strrchr (current_file_name, '/');
590 if (p == NULL) {
591 p = current_file_name;
592 q = current_file_name + len; /* "" */
593 } else {
594 *(p++) = 0;
595 q = current_file_name;
598 parent =
599 vfs_s_find_inode (me, archive, q, LINK_NO_FOLLOW, FL_MKDIR);
600 if (parent == NULL) {
601 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
602 return STATUS_BADCHECKSUM;
605 if (header->header.linkflag == LF_LINK) {
606 inode =
607 vfs_s_find_inode (me, archive, current_link_name,
608 LINK_NO_FOLLOW, 0);
609 if (inode == NULL) {
610 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
611 } else {
612 entry = vfs_s_new_entry (me, p, inode);
613 vfs_s_insert_entry (me, parent, entry);
614 g_free (current_link_name);
615 goto done;
619 tar_fill_stat (archive, &st, header, *h_size);
620 if (S_ISDIR(st.st_mode)) {
621 entry = MEDATA->find_entry (me, parent, p, LINK_NO_FOLLOW, FL_NONE);
622 if (entry)
623 goto done;
625 inode = vfs_s_new_inode (me, archive, &st);
627 inode->data_offset = data_position;
628 if (*current_link_name) {
629 inode->linkname = current_link_name;
630 } else if (current_link_name != next_long_link) {
631 g_free (current_link_name);
633 entry = vfs_s_new_entry (me, p, inode);
635 vfs_s_insert_entry (me, parent, entry);
636 g_free (current_file_name);
638 done:
639 next_long_link = next_long_name = NULL;
641 if (archive->u.arch.type == TAR_GNU &&
642 header->header.unused.oldgnu.isextended) {
643 while (tar_get_next_record (archive, tard)->ext_hdr.
644 isextended);
645 inode->data_offset = current_tar_position;
647 return STATUS_SUCCESS;
652 * Main loop for reading an archive.
653 * Returns 0 on success, -1 on error.
655 static int
656 tar_open_archive (struct vfs_class *me, struct vfs_s_super *archive,
657 const char *name, char *op)
659 /* Initial status at start of archive */
660 ReadStatus status = STATUS_EOFMARK;
661 ReadStatus prev_status;
662 int tard;
664 (void) op;
666 current_tar_position = 0;
667 /* Open for reading */
668 if ((tard = tar_open_archive_int (me, name, archive)) == -1)
669 return -1;
671 for (;;) {
672 size_t h_size;
674 prev_status = status;
675 status = tar_read_header (me, archive, tard, &h_size);
677 switch (status) {
679 case STATUS_SUCCESS:
680 tar_skip_n_records (archive, tard,
681 (h_size + RECORDSIZE -
682 1) / RECORDSIZE);
683 continue;
686 * Invalid header:
688 * If the previous header was good, tell them
689 * that we are skipping bad ones.
691 case STATUS_BADCHECKSUM:
692 switch (prev_status) {
694 /* Error on first record */
695 case STATUS_EOFMARK:
696 message (D_ERROR, MSG_ERROR,
698 ("Hmm,...\n%s\ndoesn't look like a tar archive."),
699 name);
700 /* FALL THRU */
702 /* Error after header rec */
703 case STATUS_SUCCESS:
704 /* Error after error */
706 case STATUS_BADCHECKSUM:
707 return -1;
709 case STATUS_EOF:
710 return 0;
713 /* Record of zeroes */
714 case STATUS_EOFMARK:
715 status = prev_status; /* If error after 0's */
716 /* FALL THRU */
718 case STATUS_EOF: /* End of archive */
719 break;
721 break;
723 return 0;
726 static void *
727 tar_super_check (struct vfs_class *me, const char *archive_name, char *op)
729 static struct stat stat_buf;
731 (void) me;
732 (void) op;
734 if (mc_stat (archive_name, &stat_buf))
735 return NULL;
736 return &stat_buf;
739 static int
740 tar_super_same (struct vfs_class *me, struct vfs_s_super *parc,
741 const char *archive_name, char *op, void *cookie)
743 struct stat *archive_stat = cookie; /* stat of main archive */
745 (void) me;
746 (void) op;
748 if (strcmp (parc->name, archive_name))
749 return 0;
751 /* Has the cached archive been changed on the disk? */
752 if (parc->u.arch.st.st_mtime < archive_stat->st_mtime) {
753 /* Yes, reload! */
754 (*vfs_tarfs_ops.free) ((vfsid) parc);
755 vfs_rmstamp (&vfs_tarfs_ops, (vfsid) parc);
756 return 2;
758 /* Hasn't been modified, give it a new timeout */
759 vfs_stamp (&vfs_tarfs_ops, (vfsid) parc);
760 return 1;
763 static ssize_t tar_read (void *fh, char *buffer, int count)
765 off_t begin = FH->ino->data_offset;
766 int fd = FH_SUPER->u.arch.fd;
767 struct vfs_class *me = FH_SUPER->me;
769 if (mc_lseek (fd, begin + FH->pos, SEEK_SET) !=
770 begin + FH->pos) ERRNOR (EIO, -1);
772 count = MIN(count, FH->ino->st.st_size - FH->pos);
774 if ((count = mc_read (fd, buffer, count)) == -1) ERRNOR (errno, -1);
776 FH->pos += count;
777 return count;
780 static int tar_fh_open (struct vfs_class *me, struct vfs_s_fh *fh, int flags, int mode)
782 (void) fh;
783 (void) mode;
785 if ((flags & O_ACCMODE) != O_RDONLY) ERRNOR (EROFS, -1);
786 return 0;
789 void
790 init_tarfs (void)
792 static struct vfs_s_subclass tarfs_subclass;
794 tarfs_subclass.flags = VFS_S_READONLY;
795 tarfs_subclass.archive_check = tar_super_check;
796 tarfs_subclass.archive_same = tar_super_same;
797 tarfs_subclass.open_archive = tar_open_archive;
798 tarfs_subclass.free_archive = tar_free_archive;
799 tarfs_subclass.fh_open = tar_fh_open;
801 vfs_s_init_class (&vfs_tarfs_ops, &tarfs_subclass);
802 vfs_tarfs_ops.name = "tarfs";
803 vfs_tarfs_ops.prefix = "utar";
804 vfs_tarfs_ops.read = tar_read;
805 vfs_tarfs_ops.setctl = NULL;
806 vfs_register_class (&vfs_tarfs_ops);