First bunch of mhl_mem_free removal patches
[midnight-commander.git] / vfs / tar.c
blob12a4c8f341b0ed466a337f475eb8bf8c37b3933d
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 /* Namespace: init_tarfs */
24 #include <config.h>
26 #include <sys/types.h>
27 #include <errno.h>
28 #include <ctype.h>
30 #include <mhl/memory.h>
31 #include <mhl/string.h>
33 #ifdef hpux
34 /* major() and minor() macros (among other things) defined here for hpux */
35 #include <sys/mknod.h>
36 #endif
38 #include "../src/global.h"
39 #include "../src/tty.h" /* enable/disable interrupt key */
40 #include "../src/wtools.h" /* message() */
41 #include "../src/main.h" /* print_vfs_message */
42 #include "utilvfs.h"
43 #include "gc.h" /* vfs_rmstamp */
44 #include "xdirentry.h"
46 static struct vfs_class vfs_tarfs_ops;
48 enum {
49 TAR_UNKNOWN = 0,
50 TAR_V7,
51 TAR_USTAR,
52 TAR_POSIX,
53 TAR_GNU
57 * Header block on tape.
59 * I'm going to use traditional DP naming conventions here.
60 * A "block" is a big chunk of stuff that we do I/O on.
61 * A "record" is a piece of info that we care about.
62 * Typically many "record"s fit into a "block".
64 #define RECORDSIZE 512
65 #define NAMSIZ 100
66 #define PREFIX_SIZE 155
67 #define TUNMLEN 32
68 #define TGNMLEN 32
69 #define SPARSE_EXT_HDR 21
70 #define SPARSE_IN_HDR 4
72 struct sparse {
73 char offset[12];
74 char numbytes[12];
77 struct sp_array {
78 int offset;
79 int numbytes;
82 union record {
83 char charptr[RECORDSIZE];
84 struct header {
85 char arch_name[NAMSIZ];
86 char mode[8];
87 char uid[8];
88 char gid[8];
89 char size[12];
90 char mtime[12];
91 char chksum[8];
92 char linkflag;
93 char arch_linkname[NAMSIZ];
94 char magic[8];
95 char uname[TUNMLEN];
96 char gname[TGNMLEN];
97 char devmajor[8];
98 char devminor[8];
99 /* The following bytes of the tar header record were originally unused.
101 Archives following the ustar specification use almost all of those
102 bytes to support pathnames of 256 characters in length.
104 GNU tar archives use the "unused" space to support incremental
105 archives and sparse files. */
106 union unused {
107 char prefix[PREFIX_SIZE];
108 /* GNU extensions to the ustar (POSIX.1-1988) archive format. */
109 struct oldgnu {
110 char atime[12];
111 char ctime[12];
112 char offset[12];
113 char longnames[4];
114 char pad;
115 struct sparse sp[SPARSE_IN_HDR];
116 char isextended;
117 char realsize[12]; /* true size of the sparse file */
118 } oldgnu;
119 } unused;
120 } header;
121 struct extended_header {
122 struct sparse sp[21];
123 char isextended;
124 } ext_hdr;
127 /* The checksum field is filled with this while the checksum is computed. */
128 #define CHKBLANKS " " /* 8 blanks, no null */
130 /* The magic field is filled with this if uname and gname are valid. */
131 #define TMAGIC "ustar" /* ustar and a null */
132 #define OLDGNU_MAGIC "ustar " /* 7 chars and a null */
134 /* The linkflag defines the type of file */
135 #define LF_OLDNORMAL '\0' /* Normal disk file, Unix compat */
136 #define LF_NORMAL '0' /* Normal disk file */
137 #define LF_LINK '1' /* Link to previously dumped file */
138 #define LF_SYMLINK '2' /* Symbolic link */
139 #define LF_CHR '3' /* Character special file */
140 #define LF_BLK '4' /* Block special file */
141 #define LF_DIR '5' /* Directory */
142 #define LF_FIFO '6' /* FIFO special file */
143 #define LF_CONTIG '7' /* Contiguous file */
144 #define LF_EXTHDR 'x' /* pax Extended Header */
145 #define LF_GLOBAL_EXTHDR 'g' /* pax Global Extended Header */
146 /* Further link types may be defined later. */
148 /* Note that the standards committee allows only capital A through
149 capital Z for user-defined expansion. This means that defining something
150 as, say '8' is a *bad* idea. */
151 #define LF_DUMPDIR 'D' /* This is a dir entry that contains
152 the names of files that were in
153 the dir at the time the dump
154 was made */
155 #define LF_LONGLINK 'K' /* Identifies the NEXT file on the tape
156 as having a long linkname */
157 #define LF_LONGNAME 'L' /* Identifies the NEXT file on the tape
158 as having a long name. */
159 #define LF_MULTIVOL 'M' /* This is the continuation
160 of a file that began on another
161 volume */
162 #define LF_NAMES 'N' /* For storing filenames that didn't
163 fit in 100 characters */
164 #define LF_SPARSE 'S' /* This is for sparse files */
165 #define LF_VOLHDR 'V' /* This file is a tape/volume header */
166 /* Ignore it on extraction */
169 * Exit codes from the "tar" program
171 #define EX_SUCCESS 0 /* success! */
172 #define EX_ARGSBAD 1 /* invalid args */
173 #define EX_BADFILE 2 /* invalid filename */
174 #define EX_BADARCH 3 /* bad archive */
175 #define EX_SYSTEM 4 /* system gave unexpected error */
176 #define EX_BADVOL 5 /* Special error code means
177 Tape volume doesn't match the one
178 specified on the command line */
180 #define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )
183 * Quick and dirty octal conversion.
185 * Result is -1 if the field is invalid (all blank, or nonoctal).
187 static long tar_from_oct (int digs, char *where)
189 register long value;
191 while (isspace ((unsigned char) *where)) { /* Skip spaces */
192 where++;
193 if (--digs <= 0)
194 return -1; /* All blank field */
196 value = 0;
197 while (digs > 0 && isodigit (*where)) { /* Scan till nonoctal */
198 value = (value << 3) | (*where++ - '0');
199 --digs;
202 if (digs > 0 && *where && !isspace ((unsigned char) *where))
203 return -1; /* Ended on non-space/nul */
205 return value;
208 static void tar_free_archive (struct vfs_class *me, struct vfs_s_super *archive)
210 (void) me;
212 if (archive->u.arch.fd != -1)
213 mc_close(archive->u.arch.fd);
216 /* As we open one archive at a time, it is safe to have this static */
217 static int current_tar_position = 0;
219 /* Returns fd of the open tar file */
220 static int
221 tar_open_archive_int (struct vfs_class *me, const char *name,
222 struct vfs_s_super *archive)
224 int result, type;
225 mode_t mode;
226 struct vfs_s_inode *root;
228 result = mc_open (name, O_RDONLY);
229 if (result == -1) {
230 message (D_ERROR, MSG_ERROR, _("Cannot open tar archive\n%s"), name);
231 ERRNOR (ENOENT, -1);
234 archive->name = mhl_str_dup (name);
235 mc_stat (name, &(archive->u.arch.st));
236 archive->u.arch.fd = -1;
237 archive->u.arch.type = TAR_UNKNOWN;
239 /* Find out the method to handle this tar file */
240 type = get_compression_type (result);
241 mc_lseek (result, 0, SEEK_SET);
242 if (type != COMPRESSION_NONE) {
243 char *s;
244 mc_close (result);
245 s = g_strconcat (archive->name, decompress_extension (type), (char *) NULL);
246 result = mc_open (s, O_RDONLY);
247 if (result == -1)
248 message (D_ERROR, MSG_ERROR, _("Cannot open tar archive\n%s"), s);
249 g_free (s);
250 if (result == -1)
251 ERRNOR (ENOENT, -1);
254 archive->u.arch.fd = result;
255 mode = archive->u.arch.st.st_mode & 07777;
256 if (mode & 0400)
257 mode |= 0100;
258 if (mode & 0040)
259 mode |= 0010;
260 if (mode & 0004)
261 mode |= 0001;
262 mode |= S_IFDIR;
264 root = vfs_s_new_inode (me, archive, &archive->u.arch.st);
265 root->st.st_mode = mode;
266 root->data_offset = -1;
267 root->st.st_nlink++;
268 root->st.st_dev = MEDATA->rdev++;
270 archive->root = root;
272 return result;
275 static union record rec_buf;
277 static union record *
278 tar_get_next_record (struct vfs_s_super *archive, int tard)
280 int n;
282 (void) archive;
284 n = mc_read (tard, rec_buf.charptr, RECORDSIZE);
285 if (n != RECORDSIZE)
286 return NULL; /* An error has occurred */
287 current_tar_position += RECORDSIZE;
288 return &rec_buf;
291 static void tar_skip_n_records (struct vfs_s_super *archive, int tard, int n)
293 (void) archive;
295 mc_lseek (tard, n * RECORDSIZE, SEEK_CUR);
296 current_tar_position += n * RECORDSIZE;
299 static void
300 tar_fill_stat (struct vfs_s_super *archive, struct stat *st, union record *header,
301 size_t h_size)
303 st->st_mode = tar_from_oct (8, header->header.mode);
305 /* Adjust st->st_mode because there are tar-files with
306 * linkflag==LF_SYMLINK and S_ISLNK(mod)==0. I don't
307 * know about the other modes but I think I cause no new
308 * problem when I adjust them, too. -- Norbert.
310 if (header->header.linkflag == LF_DIR) {
311 st->st_mode |= S_IFDIR;
312 } else if (header->header.linkflag == LF_SYMLINK) {
313 st->st_mode |= S_IFLNK;
314 } else if (header->header.linkflag == LF_CHR) {
315 st->st_mode |= S_IFCHR;
316 } else if (header->header.linkflag == LF_BLK) {
317 st->st_mode |= S_IFBLK;
318 } else if (header->header.linkflag == LF_FIFO) {
319 st->st_mode |= S_IFIFO;
320 } else
321 st->st_mode |= S_IFREG;
323 st->st_rdev = 0;
324 switch (archive->u.arch.type) {
325 case TAR_USTAR:
326 case TAR_POSIX:
327 case TAR_GNU:
328 st->st_uid =
329 *header->header.uname ? vfs_finduid (header->header.
330 uname) : tar_from_oct (8,
331 header->
332 header.
333 uid);
334 st->st_gid =
335 *header->header.gname ? vfs_findgid (header->header.
336 gname) : tar_from_oct (8,
337 header->
338 header.
339 gid);
340 switch (header->header.linkflag) {
341 case LF_BLK:
342 case LF_CHR:
343 st->st_rdev =
344 (tar_from_oct (8, header->header.devmajor) << 8) |
345 tar_from_oct (8, header->header.devminor);
347 default:
348 st->st_uid = tar_from_oct (8, header->header.uid);
349 st->st_gid = tar_from_oct (8, header->header.gid);
351 st->st_size = h_size;
352 st->st_mtime = tar_from_oct (1 + 12, header->header.mtime);
353 st->st_atime = 0;
354 st->st_ctime = 0;
355 if (archive->u.arch.type == TAR_GNU) {
356 st->st_atime = tar_from_oct (1 + 12,
357 header->header.unused.oldgnu.atime);
358 st->st_ctime = tar_from_oct (1 + 12,
359 header->header.unused.oldgnu.ctime);
364 typedef enum {
365 STATUS_BADCHECKSUM,
366 STATUS_SUCCESS,
367 STATUS_EOFMARK,
368 STATUS_EOF
369 } ReadStatus;
371 * Return 1 for success, 0 if the checksum is bad, EOF on eof,
372 * 2 for a record full of zeros (EOF marker).
375 static ReadStatus
376 tar_read_header (struct vfs_class *me, struct vfs_s_super *archive,
377 int tard, size_t *h_size)
379 register int i;
380 register long sum, signed_sum, recsum;
381 register char *p;
382 register union record *header;
383 static char *next_long_name = NULL, *next_long_link = NULL;
385 recurse:
387 header = tar_get_next_record (archive, tard);
388 if (NULL == header)
389 return STATUS_EOF;
391 recsum = tar_from_oct (8, header->header.chksum);
393 sum = 0;
394 signed_sum = 0;
395 p = header->charptr;
396 for (i = sizeof (*header); --i >= 0;) {
398 * We can't use unsigned char here because of old compilers,
399 * e.g. V7.
401 signed_sum += *p;
402 sum += 0xFF & *p++;
405 /* Adjust checksum to count the "chksum" field as blanks. */
406 for (i = sizeof (header->header.chksum); --i >= 0;) {
407 sum -= 0xFF & header->header.chksum[i];
408 signed_sum -= (char) header->header.chksum[i];
410 sum += ' ' * sizeof header->header.chksum;
411 signed_sum += ' ' * sizeof header->header.chksum;
414 * This is a zeroed record...whole record is 0's except
415 * for the 8 blanks we faked for the checksum field.
417 if (sum == 8 * ' ')
418 return STATUS_EOFMARK;
420 if (sum != recsum && signed_sum != recsum)
421 return STATUS_BADCHECKSUM;
424 * Try to determine the archive format.
426 if (archive->u.arch.type == TAR_UNKNOWN) {
427 if (!strcmp (header->header.magic, TMAGIC)) {
428 if (header->header.linkflag == LF_GLOBAL_EXTHDR)
429 archive->u.arch.type = TAR_POSIX;
430 else
431 archive->u.arch.type = TAR_USTAR;
432 } else if (!strcmp (header->header.magic, OLDGNU_MAGIC)) {
433 archive->u.arch.type = TAR_GNU;
438 * linkflag on BSDI tar (pax) always '\000'
440 if (header->header.linkflag == '\000') {
441 if (header->header.arch_name[NAMSIZ - 1] != '\0')
442 i = NAMSIZ;
443 else
444 i = strlen (header->header.arch_name);
446 if (i && header->header.arch_name[i - 1] == '/')
447 header->header.linkflag = LF_DIR;
451 * Good record. Decode file size and return.
453 if (header->header.linkflag == LF_LINK
454 || header->header.linkflag == LF_DIR)
455 *h_size = 0; /* Links 0 size on tape */
456 else
457 *h_size = tar_from_oct (1 + 12, header->header.size);
460 * Skip over directory snapshot info records that
461 * are stored in incremental tar archives.
463 if (header->header.linkflag == LF_DUMPDIR) {
464 if (archive->u.arch.type == TAR_UNKNOWN)
465 archive->u.arch.type = TAR_GNU;
466 return STATUS_SUCCESS;
470 * Skip over pax extended header and global extended
471 * header records.
473 if (header->header.linkflag == LF_EXTHDR ||
474 header->header.linkflag == LF_GLOBAL_EXTHDR) {
475 if (archive->u.arch.type == TAR_UNKNOWN)
476 archive->u.arch.type = TAR_POSIX;
477 return STATUS_SUCCESS;
480 if (header->header.linkflag == LF_LONGNAME
481 || header->header.linkflag == LF_LONGLINK) {
482 char **longp;
483 char *bp, *data;
484 int size, written;
486 if (archive->u.arch.type == TAR_UNKNOWN)
487 archive->u.arch.type = TAR_GNU;
489 if (*h_size > MC_MAXPATHLEN) {
490 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
491 return STATUS_BADCHECKSUM;
494 longp = ((header->header.linkflag == LF_LONGNAME)
495 ? &next_long_name : &next_long_link);
497 g_free (*longp);
498 bp = *longp = g_malloc (*h_size + 1);
500 for (size = *h_size; size > 0; size -= written) {
501 data = tar_get_next_record (archive, tard)->charptr;
502 if (data == NULL) {
503 g_free (*longp);
504 *longp = NULL;
505 message (D_ERROR, MSG_ERROR,
506 _("Unexpected EOF on archive file"));
507 return STATUS_BADCHECKSUM;
509 written = RECORDSIZE;
510 if (written > size)
511 written = size;
513 memcpy (bp, data, written);
514 bp += written;
517 if (bp - *longp == MC_MAXPATHLEN && bp[-1] != '\0') {
518 g_free (*longp);
519 *longp = NULL;
520 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
521 return STATUS_BADCHECKSUM;
523 *bp = 0;
524 goto recurse;
525 } else {
526 struct stat st;
527 struct vfs_s_entry *entry;
528 struct vfs_s_inode *inode, *parent;
529 long data_position;
530 char *q;
531 int len;
532 char *current_file_name, *current_link_name;
534 current_link_name =
535 (next_long_link ? next_long_link :
536 g_strndup (header->header.arch_linkname, NAMSIZ));
537 len = strlen (current_link_name);
538 if (len > 1 && current_link_name[len - 1] == '/')
539 current_link_name[len - 1] = 0;
541 current_file_name = NULL;
542 switch (archive->u.arch.type) {
543 case TAR_USTAR:
544 case TAR_POSIX:
545 /* The ustar archive format supports pathnames of upto 256
546 * characters in length. This is achieved by concatenating
547 * the contents of the `prefix' and `arch_name' fields like
548 * this:
550 * prefix + path_separator + arch_name
552 * If the `prefix' field contains an empty string i.e. its
553 * first characters is '\0' the prefix field is ignored.
555 if (header->header.unused.prefix[0] != '\0') {
556 char *temp_name, *temp_prefix;
558 temp_name = g_strndup (header->header.arch_name, NAMSIZ);
559 temp_prefix = g_strndup (header->header.unused.prefix,
560 PREFIX_SIZE);
561 current_file_name = g_strconcat (temp_prefix, PATH_SEP_STR,
562 temp_name, (char *) NULL);
563 g_free (temp_name);
564 g_free (temp_prefix);
566 break;
567 case TAR_GNU:
568 if (next_long_name != NULL)
569 current_file_name = next_long_name;
570 break;
571 default:
572 break;
575 if (current_file_name == NULL)
576 current_file_name = g_strndup (header->header.arch_name, NAMSIZ);
578 canonicalize_pathname (current_file_name);
579 len = strlen (current_file_name);
581 data_position = current_tar_position;
583 p = strrchr (current_file_name, '/');
584 if (p == NULL) {
585 p = current_file_name;
586 q = current_file_name + len; /* "" */
587 } else {
588 *(p++) = 0;
589 q = current_file_name;
592 parent =
593 vfs_s_find_inode (me, archive, q, LINK_NO_FOLLOW, FL_MKDIR);
594 if (parent == NULL) {
595 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
596 return STATUS_BADCHECKSUM;
599 if (header->header.linkflag == LF_LINK) {
600 inode =
601 vfs_s_find_inode (me, archive, current_link_name,
602 LINK_NO_FOLLOW, 0);
603 if (inode == NULL) {
604 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
605 } else {
606 entry = vfs_s_new_entry (me, p, inode);
607 vfs_s_insert_entry (me, parent, entry);
608 g_free (current_link_name);
609 goto done;
613 tar_fill_stat (archive, &st, header, *h_size);
614 inode = vfs_s_new_inode (me, archive, &st);
616 inode->data_offset = data_position;
617 if (*current_link_name) {
618 inode->linkname = current_link_name;
619 } else if (current_link_name != next_long_link) {
620 g_free (current_link_name);
622 entry = vfs_s_new_entry (me, p, inode);
624 vfs_s_insert_entry (me, parent, entry);
625 g_free (current_file_name);
627 done:
628 next_long_link = next_long_name = NULL;
630 if (archive->u.arch.type == TAR_GNU &&
631 header->header.unused.oldgnu.isextended) {
632 while (tar_get_next_record (archive, tard)->ext_hdr.
633 isextended);
634 inode->data_offset = current_tar_position;
636 return STATUS_SUCCESS;
641 * Main loop for reading an archive.
642 * Returns 0 on success, -1 on error.
644 static int
645 tar_open_archive (struct vfs_class *me, struct vfs_s_super *archive,
646 const char *name, char *op)
648 /* Initial status at start of archive */
649 ReadStatus status = STATUS_EOFMARK;
650 ReadStatus prev_status;
651 int tard;
653 (void) op;
655 current_tar_position = 0;
656 /* Open for reading */
657 if ((tard = tar_open_archive_int (me, name, archive)) == -1)
658 return -1;
660 for (;;) {
661 size_t h_size;
663 prev_status = status;
664 status = tar_read_header (me, archive, tard, &h_size);
666 switch (status) {
668 case STATUS_SUCCESS:
669 tar_skip_n_records (archive, tard,
670 (h_size + RECORDSIZE -
671 1) / RECORDSIZE);
672 continue;
675 * Invalid header:
677 * If the previous header was good, tell them
678 * that we are skipping bad ones.
680 case STATUS_BADCHECKSUM:
681 switch (prev_status) {
683 /* Error on first record */
684 case STATUS_EOFMARK:
685 message (D_ERROR, MSG_ERROR,
687 ("Hmm,...\n%s\ndoesn't look like a tar archive."),
688 name);
689 /* FALL THRU */
691 /* Error after header rec */
692 case STATUS_SUCCESS:
693 /* Error after error */
695 case STATUS_BADCHECKSUM:
696 return -1;
698 case STATUS_EOF:
699 return 0;
702 /* Record of zeroes */
703 case STATUS_EOFMARK:
704 status = prev_status; /* If error after 0's */
705 /* FALL THRU */
707 case STATUS_EOF: /* End of archive */
708 break;
710 break;
712 return 0;
715 static void *
716 tar_super_check (struct vfs_class *me, const char *archive_name, char *op)
718 static struct stat stat_buf;
720 (void) me;
721 (void) op;
723 if (mc_stat (archive_name, &stat_buf))
724 return NULL;
725 return &stat_buf;
728 static int
729 tar_super_same (struct vfs_class *me, struct vfs_s_super *parc,
730 const char *archive_name, char *op, void *cookie)
732 struct stat *archive_stat = cookie; /* stat of main archive */
734 (void) me;
735 (void) op;
737 if (strcmp (parc->name, archive_name))
738 return 0;
740 /* Has the cached archive been changed on the disk? */
741 if (parc->u.arch.st.st_mtime < archive_stat->st_mtime) {
742 /* Yes, reload! */
743 (*vfs_tarfs_ops.free) ((vfsid) parc);
744 vfs_rmstamp (&vfs_tarfs_ops, (vfsid) parc);
745 return 2;
747 /* Hasn't been modified, give it a new timeout */
748 vfs_stamp (&vfs_tarfs_ops, (vfsid) parc);
749 return 1;
752 static ssize_t tar_read (void *fh, char *buffer, int count)
754 off_t begin = FH->ino->data_offset;
755 int fd = FH_SUPER->u.arch.fd;
756 struct vfs_class *me = FH_SUPER->me;
758 if (mc_lseek (fd, begin + FH->pos, SEEK_SET) !=
759 begin + FH->pos) ERRNOR (EIO, -1);
761 count = MIN(count, FH->ino->st.st_size - FH->pos);
763 if ((count = mc_read (fd, buffer, count)) == -1) ERRNOR (errno, -1);
765 FH->pos += count;
766 return count;
769 static int tar_fh_open (struct vfs_class *me, struct vfs_s_fh *fh, int flags, int mode)
771 (void) fh;
772 (void) mode;
774 if ((flags & O_ACCMODE) != O_RDONLY) ERRNOR (EROFS, -1);
775 return 0;
778 void
779 init_tarfs (void)
781 static struct vfs_s_subclass tarfs_subclass;
783 tarfs_subclass.flags = VFS_S_READONLY;
784 tarfs_subclass.archive_check = tar_super_check;
785 tarfs_subclass.archive_same = tar_super_same;
786 tarfs_subclass.open_archive = tar_open_archive;
787 tarfs_subclass.free_archive = tar_free_archive;
788 tarfs_subclass.fh_open = tar_fh_open;
790 vfs_s_init_class (&vfs_tarfs_ops, &tarfs_subclass);
791 vfs_tarfs_ops.name = "tarfs";
792 vfs_tarfs_ops.prefix = "utar";
793 vfs_tarfs_ops.read = tar_read;
794 vfs_tarfs_ops.setctl = NULL;
795 vfs_register_class (&vfs_tarfs_ops);