This source file includes following definitions.
- tar_from_oct
- tar_new_archive
- tar_free_archive
- tar_open_archive_int
- tar_get_next_block
- tar_skip_n_records
- tar_checksum
- tar_decode_header
- tar_fill_stat
- tar_read_header
- tar_open_archive
- tar_super_check
- tar_super_same
- tar_read
- tar_fh_open
- vfs_init_tarfs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 #include <config.h>
39 #include <sys/types.h>
40 #include <errno.h>
41 #include <ctype.h>
42
43 #ifdef hpux
44
45 #include <sys/mknod.h>
46 #endif
47
48 #include "lib/global.h"
49 #include "lib/util.h"
50 #include "lib/unixcompat.h"
51 #include "lib/widget.h"
52
53 #include "lib/vfs/vfs.h"
54 #include "lib/vfs/utilvfs.h"
55 #include "lib/vfs/xdirentry.h"
56 #include "lib/vfs/gc.h"
57
58 #include "tar.h"
59
60
61
62
63
64 #define TAR_SUPER(super) ((tar_super_t *) (super))
65
66
67
68
69
70 #define TMAGIC "ustar"
71
72 #define XHDTYPE 'x'
73 #define XGLTYPE 'g'
74
75
76 #define LNKTYPE '1'
77 #define SYMTYPE '2'
78 #define CHRTYPE '3'
79 #define BLKTYPE '4'
80 #define DIRTYPE '5'
81 #define FIFOTYPE '6'
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105 #define SPARSES_IN_EXTRA_HEADER 16
106 #define SPARSES_IN_OLDGNU_HEADER 4
107 #define SPARSES_IN_SPARSE_HEADER 21
108
109
110
111
112
113 #define OLDGNU_MAGIC "ustar "
114
115
116
117
118
119 #define GNUTYPE_DUMPDIR 'D'
120
121
122 #define GNUTYPE_LONGLINK 'K'
123
124
125 #define GNUTYPE_LONGNAME 'L'
126
127
128
129
130
131 #define BLOCKSIZE 512
132
133
134 #define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )
135
136
137
138
139
140
141 struct posix_header
142 {
143 char name[100];
144 char mode[8];
145 char uid[8];
146 char gid[8];
147 char size[12];
148 char mtime[12];
149 char chksum[8];
150 char typeflag;
151 char linkname[100];
152 char magic[6];
153 char version[2];
154 char uname[32];
155 char gname[32];
156 char devmajor[8];
157 char devminor[8];
158 char prefix[155];
159
160 };
161
162
163 struct sparse
164 {
165
166 char offset[12];
167
168 char numbytes[12];
169
170 };
171
172
173
174
175
176
177 struct extra_header
178 {
179 char atime[12];
180 char ctime[12];
181 char offset[12];
182 char realsize[12];
183 char longnames[4];
184 char unused_pad1[68];
185 struct sparse sp[SPARSES_IN_EXTRA_HEADER];
186
187 char isextended;
188
189 };
190
191
192
193
194
195 struct sparse_header
196 {
197 struct sparse sp[SPARSES_IN_SPARSE_HEADER];
198
199 char isextended;
200
201 };
202
203
204
205
206
207
208
209
210 struct oldgnu_header
211 {
212 char unused_pad1[345];
213 char atime[12];
214 char ctime[12];
215 char offset[12];
216 char longnames[4];
217 char unused_pad2;
218 struct sparse sp[SPARSES_IN_OLDGNU_HEADER];
219
220 char isextended;
221 char realsize[12];
222
223 };
224
225
226
227
228 union block
229 {
230 char buffer[BLOCKSIZE];
231 struct posix_header header;
232 struct extra_header extra_header;
233 struct oldgnu_header oldgnu_header;
234 struct sparse_header sparse_header;
235 };
236
237 enum archive_format
238 {
239 TAR_UNKNOWN = 0,
240 TAR_V7,
241 TAR_USTAR,
242 TAR_POSIX,
243 TAR_GNU
244 };
245
246 typedef enum
247 {
248 STATUS_BADCHECKSUM,
249 STATUS_SUCCESS,
250 STATUS_EOFMARK,
251 STATUS_EOF
252 } ReadStatus;
253
254 typedef struct
255 {
256 struct vfs_s_super base;
257
258 int fd;
259 struct stat st;
260 enum archive_format type;
261 } tar_super_t;
262
263
264
265 static struct vfs_s_subclass tarfs_subclass;
266 static struct vfs_class *vfs_tarfs_ops = VFS_CLASS (&tarfs_subclass);
267
268
269 static off_t current_tar_position = 0;
270
271 static union block block_buf;
272
273
274
275
276
277
278
279
280
281 static long
282 tar_from_oct (int digs, const char *where)
283 {
284 long value;
285
286 while (isspace ((unsigned char) *where))
287 {
288 where++;
289 if (--digs <= 0)
290 return -1;
291 }
292 value = 0;
293 while (digs > 0 && isodigit (*where))
294 {
295 value = (value << 3) | (*where++ - '0');
296 --digs;
297 }
298
299 if (digs > 0 && *where && !isspace ((unsigned char) *where))
300 return -1;
301
302 return value;
303 }
304
305
306
307 static struct vfs_s_super *
308 tar_new_archive (struct vfs_class *me)
309 {
310 tar_super_t *arch;
311
312 arch = g_new0 (tar_super_t, 1);
313 arch->base.me = me;
314 arch->fd = -1;
315 arch->type = TAR_UNKNOWN;
316
317 return VFS_SUPER (arch);
318 }
319
320
321
322 static void
323 tar_free_archive (struct vfs_class *me, struct vfs_s_super *archive)
324 {
325 tar_super_t *arch = TAR_SUPER (archive);
326
327 (void) me;
328
329 if (arch->fd != -1)
330 {
331 mc_close (arch->fd);
332 arch->fd = -1;
333 }
334 }
335
336
337
338
339 static int
340 tar_open_archive_int (struct vfs_class *me, const vfs_path_t * vpath, struct vfs_s_super *archive)
341 {
342 int result, type;
343 tar_super_t *arch;
344 mode_t mode;
345 struct vfs_s_inode *root;
346
347 result = mc_open (vpath, O_RDONLY);
348 if (result == -1)
349 {
350 message (D_ERROR, MSG_ERROR, _("Cannot open tar archive\n%s"), vfs_path_as_str (vpath));
351 ERRNOR (ENOENT, -1);
352 }
353
354 archive->name = g_strdup (vfs_path_as_str (vpath));
355 arch = TAR_SUPER (archive);
356 mc_stat (vpath, &arch->st);
357
358
359 type = get_compression_type (result, archive->name);
360 if (type == COMPRESSION_NONE)
361 mc_lseek (result, 0, SEEK_SET);
362 else
363 {
364 char *s;
365 vfs_path_t *tmp_vpath;
366
367 mc_close (result);
368 s = g_strconcat (archive->name, decompress_extension (type), (char *) NULL);
369 tmp_vpath = vfs_path_from_str_flags (s, VPF_NO_CANON);
370 result = mc_open (tmp_vpath, O_RDONLY);
371 vfs_path_free (tmp_vpath, TRUE);
372 if (result == -1)
373 message (D_ERROR, MSG_ERROR, _("Cannot open tar archive\n%s"), s);
374 g_free (s);
375 if (result == -1)
376 {
377 MC_PTR_FREE (archive->name);
378 ERRNOR (ENOENT, -1);
379 }
380 }
381
382 arch->fd = result;
383 mode = arch->st.st_mode & 07777;
384 if (mode & 0400)
385 mode |= 0100;
386 if (mode & 0040)
387 mode |= 0010;
388 if (mode & 0004)
389 mode |= 0001;
390 mode |= S_IFDIR;
391
392 root = vfs_s_new_inode (me, archive, &arch->st);
393 root->st.st_mode = mode;
394 root->data_offset = -1;
395 root->st.st_nlink++;
396 root->st.st_dev = VFS_SUBCLASS (me)->rdev++;
397
398 archive->root = root;
399
400 return result;
401 }
402
403
404
405 static union block *
406 tar_get_next_block (struct vfs_s_super *archive, int tard)
407 {
408 int n;
409
410 (void) archive;
411
412 n = mc_read (tard, block_buf.buffer, sizeof (block_buf.buffer));
413 if (n != sizeof (block_buf.buffer))
414 return NULL;
415 current_tar_position += sizeof (block_buf.buffer);
416 return &block_buf;
417 }
418
419
420
421 static void
422 tar_skip_n_records (struct vfs_s_super *archive, int tard, size_t n)
423 {
424 (void) archive;
425
426 mc_lseek (tard, n * sizeof (block_buf.buffer), SEEK_CUR);
427 current_tar_position += n * sizeof (block_buf.buffer);
428 }
429
430
431
432 static ReadStatus
433 tar_checksum (const union block *header)
434 {
435 long recsum;
436 long signed_sum = 0;
437 long sum = 0;
438 int i;
439 const char *p = header->buffer;
440
441 recsum = tar_from_oct (8, header->header.chksum);
442
443 for (i = sizeof (*header); --i >= 0;)
444 {
445
446
447
448
449 signed_sum += *p;
450 sum += 0xFF & *p++;
451 }
452
453
454 for (i = sizeof (header->header.chksum); --i >= 0;)
455 {
456 sum -= 0xFF & header->header.chksum[i];
457 signed_sum -= (char) header->header.chksum[i];
458 }
459
460 sum += ' ' * sizeof (header->header.chksum);
461 signed_sum += ' ' * sizeof (header->header.chksum);
462
463
464
465
466
467 if (sum == 8 * ' ')
468 return STATUS_EOFMARK;
469
470 if (sum != recsum && signed_sum != recsum)
471 return STATUS_BADCHECKSUM;
472
473 return STATUS_SUCCESS;
474 }
475
476
477
478 static size_t
479 tar_decode_header (union block *header, tar_super_t * arch)
480 {
481 size_t size;
482
483
484
485
486 if (arch->type == TAR_UNKNOWN)
487 {
488 if (strcmp (header->header.magic, TMAGIC) == 0)
489 {
490 if (header->header.typeflag == XGLTYPE)
491 arch->type = TAR_POSIX;
492 else
493 arch->type = TAR_USTAR;
494 }
495 else if (strcmp (header->header.magic, OLDGNU_MAGIC) == 0)
496 arch->type = TAR_GNU;
497 }
498
499
500
501
502 if (header->header.typeflag == '\000')
503 {
504 size_t len;
505
506 if (header->header.name[sizeof (header->header.name) - 1] != '\0')
507 len = sizeof (header->header.name);
508 else
509 len = strlen (header->header.name);
510
511 if (len != 0 && IS_PATH_SEP (header->header.name[len - 1]))
512 header->header.typeflag = DIRTYPE;
513 }
514
515
516
517
518 if (header->header.typeflag == LNKTYPE || header->header.typeflag == DIRTYPE)
519 size = 0;
520 else
521 size = tar_from_oct (1 + 12, header->header.size);
522
523 if (header->header.typeflag == GNUTYPE_DUMPDIR)
524 if (arch->type == TAR_UNKNOWN)
525 arch->type = TAR_GNU;
526
527 return size;
528 }
529
530
531
532 static void
533 tar_fill_stat (struct vfs_s_super *archive, struct stat *st, union block *header, size_t h_size)
534 {
535 tar_super_t *arch = TAR_SUPER (archive);
536
537 st->st_mode = tar_from_oct (8, header->header.mode);
538
539
540
541
542
543
544 if (header->header.typeflag == DIRTYPE || header->header.typeflag == GNUTYPE_DUMPDIR)
545 st->st_mode |= S_IFDIR;
546 else if (header->header.typeflag == SYMTYPE)
547 st->st_mode |= S_IFLNK;
548 else if (header->header.typeflag == CHRTYPE)
549 st->st_mode |= S_IFCHR;
550 else if (header->header.typeflag == BLKTYPE)
551 st->st_mode |= S_IFBLK;
552 else if (header->header.typeflag == FIFOTYPE)
553 st->st_mode |= S_IFIFO;
554 else
555 st->st_mode |= S_IFREG;
556
557 st->st_dev = 0;
558 #ifdef HAVE_STRUCT_STAT_ST_RDEV
559 st->st_rdev = 0;
560 #endif
561
562 switch (arch->type)
563 {
564 case TAR_USTAR:
565 case TAR_POSIX:
566 case TAR_GNU:
567
568 st->st_uid = *header->header.uname != '\0'
569 ? (uid_t) vfs_finduid (header->header.uname)
570 : tar_from_oct (8, header->header.uid);
571 st->st_gid = *header->header.gname != '\0'
572 ? (gid_t) vfs_findgid (header->header.gname)
573 : tar_from_oct (8,header->header.gid);
574
575
576 switch (header->header.typeflag)
577 {
578 case BLKTYPE:
579 case CHRTYPE:
580 #ifdef HAVE_STRUCT_STAT_ST_RDEV
581 st->st_rdev =
582 makedev (tar_from_oct (8, header->header.devmajor),
583 tar_from_oct (8, header->header.devminor));
584 #endif
585 break;
586 default:
587 break;
588 }
589 break;
590
591 default:
592 st->st_uid = tar_from_oct (8, header->header.uid);
593 st->st_gid = tar_from_oct (8, header->header.gid);
594 break;
595 }
596
597 st->st_size = h_size;
598 #ifdef HAVE_STRUCT_STAT_ST_MTIM
599 st->st_atim.tv_nsec = st->st_mtim.tv_nsec = st->st_ctim.tv_nsec = 0;
600 #endif
601 st->st_mtime = tar_from_oct (1 + 12, header->header.mtime);
602 st->st_atime = 0;
603 st->st_ctime = 0;
604 if (arch->type == TAR_GNU)
605 {
606 st->st_atime = tar_from_oct (1 + 12, header->oldgnu_header.atime);
607 st->st_ctime = tar_from_oct (1 + 12, header->oldgnu_header.ctime);
608 }
609
610 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
611 st->st_blksize = 8 * 1024;
612 #endif
613 vfs_adjust_stat (st);
614 }
615
616
617
618
619
620
621
622 static ReadStatus
623 tar_read_header (struct vfs_class *me, struct vfs_s_super *archive, int tard, size_t * h_size)
624 {
625 tar_super_t *arch = TAR_SUPER (archive);
626 ReadStatus checksum_status;
627 union block *header;
628 static char *next_long_name = NULL, *next_long_link = NULL;
629
630 while (TRUE)
631 {
632 header = tar_get_next_block (archive, tard);
633 if (header == NULL)
634 return STATUS_EOF;
635
636 checksum_status = tar_checksum (header);
637 if (checksum_status != STATUS_SUCCESS)
638 return checksum_status;
639
640 *h_size = tar_decode_header (header, arch);
641
642
643 if (header->header.typeflag == XHDTYPE || header->header.typeflag == XGLTYPE)
644 {
645 if (arch->type == TAR_UNKNOWN)
646 arch->type = TAR_POSIX;
647 return STATUS_SUCCESS;
648 }
649
650 if (header->header.typeflag == GNUTYPE_LONGNAME
651 || header->header.typeflag == GNUTYPE_LONGLINK)
652 {
653 char **longp;
654 char *bp, *data;
655 off_t size;
656 size_t written;
657
658 if (arch->type == TAR_UNKNOWN)
659 arch->type = TAR_GNU;
660
661 if (*h_size > MC_MAXPATHLEN)
662 {
663 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
664 return STATUS_BADCHECKSUM;
665 }
666
667 longp = header->header.typeflag == GNUTYPE_LONGNAME ? &next_long_name : &next_long_link;
668
669 g_free (*longp);
670 bp = *longp = g_malloc (*h_size + 1);
671
672 for (size = *h_size; size > 0; size -= written)
673 {
674 data = tar_get_next_block (archive, tard)->buffer;
675 if (data == NULL)
676 {
677 MC_PTR_FREE (*longp);
678 message (D_ERROR, MSG_ERROR, _("Unexpected EOF on archive file"));
679 return STATUS_BADCHECKSUM;
680 }
681 written = BLOCKSIZE;
682 if ((off_t) written > size)
683 written = (size_t) size;
684
685 memcpy (bp, data, written);
686 bp += written;
687 }
688
689 if (bp - *longp == MC_MAXPATHLEN && bp[-1] != '\0')
690 {
691 MC_PTR_FREE (*longp);
692 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
693 return STATUS_BADCHECKSUM;
694 }
695
696 *bp = '\0';
697 }
698 else
699 break;
700 }
701
702 {
703 struct stat st;
704 struct vfs_s_entry *entry;
705 struct vfs_s_inode *inode = NULL, *parent;
706 off_t data_position;
707 char *p, *q;
708 size_t len;
709 char *current_file_name, *current_link_name;
710
711 current_link_name =
712 next_long_link != NULL ? next_long_link : g_strndup (header->header.linkname,
713 sizeof (header->header.linkname));
714 len = strlen (current_link_name);
715 if (len > 1 && IS_PATH_SEP (current_link_name[len - 1]))
716 current_link_name[len - 1] = '\0';
717
718 current_file_name = NULL;
719 switch (arch->type)
720 {
721 case TAR_USTAR:
722 case TAR_POSIX:
723
724
725
726
727
728
729
730
731
732
733 if (header->header.prefix[0] != '\0')
734 {
735 char *temp_name, *temp_prefix;
736
737 temp_name = g_strndup (header->header.name, sizeof (header->header.name));
738 temp_prefix = g_strndup (header->header.prefix, sizeof (header->header.prefix));
739 current_file_name = g_strconcat (temp_prefix, PATH_SEP_STR,
740 temp_name, (char *) NULL);
741 g_free (temp_name);
742 g_free (temp_prefix);
743 }
744 break;
745 case TAR_GNU:
746 if (next_long_name != NULL)
747 current_file_name = next_long_name;
748 break;
749 default:
750 break;
751 }
752
753 if (current_file_name == NULL)
754 {
755 if (next_long_name != NULL)
756 current_file_name = g_strdup (next_long_name);
757 else
758 current_file_name = g_strndup (header->header.name, sizeof (header->header.name));
759 }
760
761 canonicalize_pathname (current_file_name);
762 len = strlen (current_file_name);
763
764 data_position = current_tar_position;
765
766 p = strrchr (current_file_name, PATH_SEP);
767 if (p == NULL)
768 {
769 p = current_file_name;
770 q = current_file_name + len;
771 }
772 else
773 {
774 *(p++) = '\0';
775 q = current_file_name;
776 }
777
778 parent = vfs_s_find_inode (me, archive, q, LINK_NO_FOLLOW, FL_MKDIR);
779 if (parent == NULL)
780 {
781 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
782 return STATUS_BADCHECKSUM;
783 }
784
785 if (header->header.typeflag == LNKTYPE)
786 {
787 inode = vfs_s_find_inode (me, archive, current_link_name, LINK_NO_FOLLOW, FL_NONE);
788 if (inode == NULL)
789 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
790 else
791 {
792 entry = vfs_s_new_entry (me, p, inode);
793 vfs_s_insert_entry (me, parent, entry);
794 g_free (current_link_name);
795 goto done;
796 }
797 }
798
799 memset (&st, 0, sizeof (st));
800 tar_fill_stat (archive, &st, header, *h_size);
801
802 if (S_ISDIR (st.st_mode))
803 {
804 entry = VFS_SUBCLASS (me)->find_entry (me, parent, p, LINK_NO_FOLLOW, FL_NONE);
805 if (entry != NULL)
806 goto done;
807 }
808
809 inode = vfs_s_new_inode (me, archive, &st);
810 inode->data_offset = data_position;
811
812 if (*current_link_name != '\0')
813 inode->linkname = current_link_name;
814 else if (current_link_name != next_long_link)
815 g_free (current_link_name);
816
817 entry = vfs_s_new_entry (me, p, inode);
818 vfs_s_insert_entry (me, parent, entry);
819 g_free (current_file_name);
820
821 done:
822 next_long_link = next_long_name = NULL;
823
824 if (arch->type == TAR_GNU && header->oldgnu_header.isextended)
825 {
826 while (tar_get_next_block (archive, tard)->sparse_header.isextended != 0)
827 ;
828
829 if (inode != NULL)
830 inode->data_offset = current_tar_position;
831 }
832 return STATUS_SUCCESS;
833 }
834 }
835
836
837
838
839
840
841 static int
842 tar_open_archive (struct vfs_s_super *archive, const vfs_path_t * vpath,
843 const vfs_path_element_t * vpath_element)
844 {
845
846 ReadStatus status = STATUS_EOFMARK;
847 int tard;
848
849 current_tar_position = 0;
850
851 tard = tar_open_archive_int (vpath_element->class, vpath, archive);
852 if (tard == -1)
853 return -1;
854
855 while (TRUE)
856 {
857 size_t h_size = 0;
858 ReadStatus prev_status = status;
859
860 status = tar_read_header (vpath_element->class, archive, tard, &h_size);
861
862 switch (status)
863 {
864 case STATUS_SUCCESS:
865 tar_skip_n_records (archive, tard, (h_size + BLOCKSIZE - 1) / BLOCKSIZE);
866 continue;
867
868
869
870
871
872
873
874 case STATUS_BADCHECKSUM:
875 switch (prev_status)
876 {
877
878 case STATUS_EOFMARK:
879 {
880 message (D_ERROR, MSG_ERROR, _("%s\ndoesn't look like a tar archive."),
881 vfs_path_as_str (vpath));
882 MC_FALLTHROUGH;
883
884
885 }
886 case STATUS_SUCCESS:
887
888
889 case STATUS_BADCHECKSUM:
890 return -1;
891
892 case STATUS_EOF:
893 return 0;
894
895 default:
896 break;
897 }
898 MC_FALLTHROUGH;
899
900
901 case STATUS_EOFMARK:
902 MC_FALLTHROUGH;
903
904 case STATUS_EOF:
905 break;
906 default:
907 break;
908 }
909 break;
910 }
911 return 0;
912 }
913
914
915
916 static void *
917 tar_super_check (const vfs_path_t * vpath)
918 {
919 static struct stat stat_buf;
920 int stat_result;
921
922 stat_result = mc_stat (vpath, &stat_buf);
923
924 return (stat_result != 0) ? NULL : &stat_buf;
925 }
926
927
928
929 static int
930 tar_super_same (const vfs_path_element_t * vpath_element, struct vfs_s_super *parc,
931 const vfs_path_t * vpath, void *cookie)
932 {
933 struct stat *archive_stat = cookie;
934
935 (void) vpath_element;
936
937 if (strcmp (parc->name, vfs_path_as_str (vpath)) != 0)
938 return 0;
939
940
941 if (parc != NULL && TAR_SUPER (parc)->st.st_mtime < archive_stat->st_mtime)
942 {
943
944 vfs_tarfs_ops->free ((vfsid) parc);
945 vfs_rmstamp (vfs_tarfs_ops, (vfsid) parc);
946 return 2;
947 }
948
949 vfs_stamp (vfs_tarfs_ops, (vfsid) parc);
950 return 1;
951 }
952
953
954
955 static ssize_t
956 tar_read (void *fh, char *buffer, size_t count)
957 {
958 struct vfs_class *me = VFS_FILE_HANDLER_SUPER (fh)->me;
959 vfs_file_handler_t *file = VFS_FILE_HANDLER (fh);
960 off_t begin = file->ino->data_offset;
961 int fd = TAR_SUPER (VFS_FILE_HANDLER_SUPER (fh))->fd;
962 ssize_t res;
963
964 if (mc_lseek (fd, begin + file->pos, SEEK_SET) != begin + file->pos)
965 ERRNOR (EIO, -1);
966
967 count = MIN (count, (size_t) (file->ino->st.st_size - file->pos));
968
969 res = mc_read (fd, buffer, count);
970 if (res == -1)
971 ERRNOR (errno, -1);
972
973 file->pos += res;
974 return res;
975 }
976
977
978
979 static int
980 tar_fh_open (struct vfs_class *me, vfs_file_handler_t * fh, int flags, mode_t mode)
981 {
982 (void) fh;
983 (void) mode;
984
985 if ((flags & O_ACCMODE) != O_RDONLY)
986 ERRNOR (EROFS, -1);
987 return 0;
988 }
989
990
991
992
993
994 void
995 vfs_init_tarfs (void)
996 {
997
998 vfs_init_subclass (&tarfs_subclass, "tarfs", VFSF_READONLY, "utar");
999 vfs_tarfs_ops->read = tar_read;
1000 vfs_tarfs_ops->setctl = NULL;
1001 tarfs_subclass.archive_check = tar_super_check;
1002 tarfs_subclass.archive_same = tar_super_same;
1003 tarfs_subclass.new_archive = tar_new_archive;
1004 tarfs_subclass.open_archive = tar_open_archive;
1005 tarfs_subclass.free_archive = tar_free_archive;
1006 tarfs_subclass.fh_open = tar_fh_open;
1007 vfs_register_class (vfs_tarfs_ops);
1008 }
1009
1010