Revert "VFS: make VFS-specific super class as derived one from vfs_s_super."
[midnight-commander.git] / src / vfs / tar / tar.c
blob7e0981eeaf734fd0dadc1b5ff0ab766623c4f03e
1 /*
2 Virtual File System: GNU Tar file system.
4 Copyright (C) 1995-2018
5 Free Software Foundation, Inc.
7 Written by:
8 Jakub Jelinek, 1995
9 Pavel Machek, 1998
10 Slava Zanko <slavazanko@gmail.com>, 2013
12 This file is part of the Midnight Commander.
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.
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.
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/>.
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
35 * Namespace: init_tarfs
38 #include <config.h>
39 #include <sys/types.h>
40 #include <errno.h>
41 #include <ctype.h>
43 #ifdef hpux
44 /* major() and minor() macros (among other things) defined here for hpux */
45 #include <sys/mknod.h>
46 #endif
48 #include "lib/global.h"
49 #include "lib/util.h"
50 #include "lib/unixcompat.h" /* makedev() */
51 #include "lib/widget.h" /* message() */
53 #include "lib/vfs/vfs.h"
54 #include "lib/vfs/utilvfs.h"
55 #include "lib/vfs/xdirentry.h"
56 #include "lib/vfs/gc.h" /* vfs_rmstamp */
58 #include "tar.h"
60 /*** global variables ****************************************************************************/
62 /*** file scope macro definitions ****************************************************************/
65 * Header block on tape.
67 * I'm going to use traditional DP naming conventions here.
68 * A "block" is a big chunk of stuff that we do I/O on.
69 * A "record" is a piece of info that we care about.
70 * Typically many "record"s fit into a "block".
72 #define RECORDSIZE 512
73 #define NAMSIZ 100
74 #define PREFIX_SIZE 155
75 #define TUNMLEN 32
76 #define TGNMLEN 32
77 #define SPARSE_EXT_HDR 21
78 #define SPARSE_IN_HDR 4
80 /* The checksum field is filled with this while the checksum is computed. */
81 #define CHKBLANKS " " /* 8 blanks, no null */
83 /* The magic field is filled with this if uname and gname are valid. */
84 #define TMAGIC "ustar" /* ustar and a null */
85 #define OLDGNU_MAGIC "ustar " /* 7 chars and a null */
87 /* The linkflag defines the type of file */
88 #define LF_OLDNORMAL '\0' /* Normal disk file, Unix compat */
89 #define LF_NORMAL '0' /* Normal disk file */
90 #define LF_LINK '1' /* Link to previously dumped file */
91 #define LF_SYMLINK '2' /* Symbolic link */
92 #define LF_CHR '3' /* Character special file */
93 #define LF_BLK '4' /* Block special file */
94 #define LF_DIR '5' /* Directory */
95 #define LF_FIFO '6' /* FIFO special file */
96 #define LF_CONTIG '7' /* Contiguous file */
97 #define LF_EXTHDR 'x' /* pax Extended Header */
98 #define LF_GLOBAL_EXTHDR 'g' /* pax Global Extended Header */
99 /* Further link types may be defined later. */
101 /* Note that the standards committee allows only capital A through
102 capital Z for user-defined expansion. This means that defining something
103 as, say '8' is a *bad* idea. */
104 #define LF_DUMPDIR 'D' /* This is a dir entry that contains
105 the names of files that were in
106 the dir at the time the dump
107 was made */
108 #define LF_LONGLINK 'K' /* Identifies the NEXT file on the tape
109 as having a long linkname */
110 #define LF_LONGNAME 'L' /* Identifies the NEXT file on the tape
111 as having a long name. */
112 #define LF_MULTIVOL 'M' /* This is the continuation
113 of a file that began on another
114 volume */
115 #define LF_NAMES 'N' /* For storing filenames that didn't
116 fit in 100 characters */
117 #define LF_SPARSE 'S' /* This is for sparse files */
118 #define LF_VOLHDR 'V' /* This file is a tape/volume header */
119 /* Ignore it on extraction */
122 * Exit codes from the "tar" program
124 #define EX_SUCCESS 0 /* success! */
125 #define EX_ARGSBAD 1 /* invalid args */
126 #define EX_BADFILE 2 /* invalid filename */
127 #define EX_BADARCH 3 /* bad archive */
128 #define EX_SYSTEM 4 /* system gave unexpected error */
129 #define EX_BADVOL 5 /* Special error code means
130 Tape volume doesn't match the one
131 specified on the command line */
133 #define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )
135 /*** file scope type declarations ****************************************************************/
137 enum
139 TAR_UNKNOWN = 0,
140 TAR_V7,
141 TAR_USTAR,
142 TAR_POSIX,
143 TAR_GNU
146 struct sparse
148 /* cppcheck-suppress unusedStructMember */
149 char offset[12];
150 /* cppcheck-suppress unusedStructMember */
151 char numbytes[12];
154 union record
156 char charptr[RECORDSIZE];
157 struct header
159 char arch_name[NAMSIZ];
160 char mode[8];
161 char uid[8];
162 char gid[8];
163 char size[12];
164 char mtime[12];
165 char chksum[8];
166 char linkflag;
167 char arch_linkname[NAMSIZ];
168 char magic[8];
169 char uname[TUNMLEN];
170 char gname[TGNMLEN];
171 char devmajor[8];
172 char devminor[8];
173 /* The following bytes of the tar header record were originally unused.
175 Archives following the ustar specification use almost all of those
176 bytes to support pathnames of 256 characters in length.
178 GNU tar archives use the "unused" space to support incremental
179 archives and sparse files. */
180 union unused
182 char prefix[PREFIX_SIZE];
183 /* GNU extensions to the ustar (POSIX.1-1988) archive format. */
184 struct oldgnu
186 char atime[12];
187 char ctime[12];
188 /* cppcheck-suppress unusedStructMember */
189 char offset[12];
190 /* cppcheck-suppress unusedStructMember */
191 char longnames[4];
192 /* cppcheck-suppress unusedStructMember */
193 char pad;
194 struct sparse sp[SPARSE_IN_HDR];
195 char isextended;
196 /* cppcheck-suppress unusedStructMember */
197 char realsize[12]; /* true size of the sparse file */
198 } oldgnu;
199 } unused;
200 } header;
201 struct extended_header
203 struct sparse sp[21];
204 char isextended;
205 } ext_hdr;
208 typedef enum
210 STATUS_BADCHECKSUM,
211 STATUS_SUCCESS,
212 STATUS_EOFMARK,
213 STATUS_EOF
214 } ReadStatus;
216 typedef struct
218 int fd;
219 struct stat st;
220 int type; /* Type of the archive */
221 } tar_super_data_t;
223 /*** file scope variables ************************************************************************/
225 static struct vfs_s_subclass tarfs_subclass;
226 static struct vfs_class *vfs_tarfs_ops = (struct vfs_class *) &tarfs_subclass;
228 /* As we open one archive at a time, it is safe to have this static */
229 static off_t current_tar_position = 0;
231 static union record rec_buf;
233 /*** file scope functions ************************************************************************/
234 /* --------------------------------------------------------------------------------------------- */
236 * Quick and dirty octal conversion.
238 * Result is -1 if the field is invalid (all blank, or nonoctal).
240 static long
241 tar_from_oct (int digs, const char *where)
243 long value;
245 while (isspace ((unsigned char) *where))
246 { /* Skip spaces */
247 where++;
248 if (--digs <= 0)
249 return -1; /* All blank field */
251 value = 0;
252 while (digs > 0 && isodigit (*where))
253 { /* Scan till nonoctal */
254 value = (value << 3) | (*where++ - '0');
255 --digs;
258 if (digs > 0 && *where && !isspace ((unsigned char) *where))
259 return -1; /* Ended on non-space/nul */
261 return value;
264 /* --------------------------------------------------------------------------------------------- */
266 static void
267 tar_free_archive (struct vfs_class *me, struct vfs_s_super *archive)
269 (void) me;
271 if (archive->data != NULL)
273 tar_super_data_t *arch = (tar_super_data_t *) archive->data;
275 if (arch->fd != -1)
276 mc_close (arch->fd);
277 g_free (archive->data);
281 /* --------------------------------------------------------------------------------------------- */
283 /* Returns fd of the open tar file */
284 static int
285 tar_open_archive_int (struct vfs_class *me, const vfs_path_t * vpath, struct vfs_s_super *archive)
287 int result, type;
288 tar_super_data_t *arch;
289 mode_t mode;
290 struct vfs_s_inode *root;
292 result = mc_open (vpath, O_RDONLY);
293 if (result == -1)
295 message (D_ERROR, MSG_ERROR, _("Cannot open tar archive\n%s"), vfs_path_as_str (vpath));
296 ERRNOR (ENOENT, -1);
299 archive->name = g_strdup (vfs_path_as_str (vpath));
300 archive->data = g_new (tar_super_data_t, 1);
301 arch = (tar_super_data_t *) archive->data;
302 mc_stat (vpath, &arch->st);
303 arch->fd = -1;
304 arch->type = TAR_UNKNOWN;
306 /* Find out the method to handle this tar file */
307 type = get_compression_type (result, archive->name);
308 if (type == COMPRESSION_NONE)
309 mc_lseek (result, 0, SEEK_SET);
310 else
312 char *s;
313 vfs_path_t *tmp_vpath;
315 mc_close (result);
316 s = g_strconcat (archive->name, decompress_extension (type), (char *) NULL);
317 tmp_vpath = vfs_path_from_str_flags (s, VPF_NO_CANON);
318 result = mc_open (tmp_vpath, O_RDONLY);
319 vfs_path_free (tmp_vpath);
320 if (result == -1)
321 message (D_ERROR, MSG_ERROR, _("Cannot open tar archive\n%s"), s);
322 g_free (s);
323 if (result == -1)
325 MC_PTR_FREE (archive->name);
326 ERRNOR (ENOENT, -1);
330 arch->fd = result;
331 mode = arch->st.st_mode & 07777;
332 if (mode & 0400)
333 mode |= 0100;
334 if (mode & 0040)
335 mode |= 0010;
336 if (mode & 0004)
337 mode |= 0001;
338 mode |= S_IFDIR;
340 root = vfs_s_new_inode (me, archive, &arch->st);
341 root->st.st_mode = mode;
342 root->data_offset = -1;
343 root->st.st_nlink++;
344 root->st.st_dev = MEDATA->rdev++;
346 archive->root = root;
348 return result;
351 /* --------------------------------------------------------------------------------------------- */
353 static union record *
354 tar_get_next_record (struct vfs_s_super *archive, int tard)
356 int n;
358 (void) archive;
360 n = mc_read (tard, rec_buf.charptr, sizeof (rec_buf.charptr));
361 if (n != sizeof (rec_buf.charptr))
362 return NULL; /* An error has occurred */
363 current_tar_position += sizeof (rec_buf.charptr);
364 return &rec_buf;
367 /* --------------------------------------------------------------------------------------------- */
369 static void
370 tar_skip_n_records (struct vfs_s_super *archive, int tard, size_t n)
372 (void) archive;
374 mc_lseek (tard, n * sizeof (rec_buf.charptr), SEEK_CUR);
375 current_tar_position += n * sizeof (rec_buf.charptr);
378 /* --------------------------------------------------------------------------------------------- */
380 static ReadStatus
381 tar_checksum (const union record *header)
383 long recsum;
384 long signed_sum = 0;
385 long sum = 0;
386 int i;
387 const char *p = header->charptr;
389 recsum = tar_from_oct (8, header->header.chksum);
391 for (i = sizeof (*header); --i >= 0;)
394 * We can't use unsigned char here because of old compilers,
395 * e.g. V7.
397 signed_sum += *p;
398 sum += 0xFF & *p++;
401 /* Adjust checksum to count the "chksum" field as blanks. */
402 for (i = sizeof (header->header.chksum); --i >= 0;)
404 sum -= 0xFF & header->header.chksum[i];
405 signed_sum -= (char) header->header.chksum[i];
408 sum += ' ' * sizeof (header->header.chksum);
409 signed_sum += ' ' * sizeof (header->header.chksum);
412 * This is a zeroed record...whole record is 0's except
413 * for the 8 blanks we faked for the checksum field.
415 if (sum == 8 * ' ')
416 return STATUS_EOFMARK;
418 if (sum != recsum && signed_sum != recsum)
419 return STATUS_BADCHECKSUM;
421 return STATUS_SUCCESS;
424 /* --------------------------------------------------------------------------------------------- */
426 static void
427 tar_fill_stat (struct vfs_s_super *archive, struct stat *st, union record *header, size_t h_size)
429 tar_super_data_t *arch = (tar_super_data_t *) archive->data;
431 st->st_mode = tar_from_oct (8, header->header.mode);
433 /* Adjust st->st_mode because there are tar-files with
434 * linkflag==LF_SYMLINK and S_ISLNK(mod)==0. I don't
435 * know about the other modes but I think I cause no new
436 * problem when I adjust them, too. -- Norbert.
438 if (header->header.linkflag == LF_DIR || header->header.linkflag == LF_DUMPDIR)
439 st->st_mode |= S_IFDIR;
440 else if (header->header.linkflag == LF_SYMLINK)
441 st->st_mode |= S_IFLNK;
442 else if (header->header.linkflag == LF_CHR)
443 st->st_mode |= S_IFCHR;
444 else if (header->header.linkflag == LF_BLK)
445 st->st_mode |= S_IFBLK;
446 else if (header->header.linkflag == LF_FIFO)
447 st->st_mode |= S_IFIFO;
448 else
449 st->st_mode |= S_IFREG;
451 st->st_dev = 0;
452 #ifdef HAVE_STRUCT_STAT_ST_RDEV
453 st->st_rdev = 0;
454 #endif
456 switch (arch->type)
458 case TAR_USTAR:
459 case TAR_POSIX:
460 case TAR_GNU:
461 /* *INDENT-OFF* */
462 st->st_uid = *header->header.uname
463 ? vfs_finduid (header->header.uname)
464 : tar_from_oct (8, header->header.uid);
465 st->st_gid = *header->header.gname
466 ? vfs_findgid (header->header.gname)
467 : tar_from_oct (8,header->header.gid);
468 /* *INDENT-ON* */
470 switch (header->header.linkflag)
472 case LF_BLK:
473 case LF_CHR:
474 #ifdef HAVE_STRUCT_STAT_ST_RDEV
475 st->st_rdev =
476 makedev (tar_from_oct (8, header->header.devmajor),
477 tar_from_oct (8, header->header.devminor));
478 #endif
479 break;
480 default:
481 break;
483 break;
485 default:
486 st->st_uid = tar_from_oct (8, header->header.uid);
487 st->st_gid = tar_from_oct (8, header->header.gid);
488 break;
491 st->st_size = h_size;
492 #ifdef HAVE_STRUCT_STAT_ST_MTIM
493 st->st_atim.tv_nsec = st->st_mtim.tv_nsec = st->st_ctim.tv_nsec = 0;
494 #endif
495 st->st_mtime = tar_from_oct (1 + 12, header->header.mtime);
496 st->st_atime = 0;
497 st->st_ctime = 0;
498 if (arch->type == TAR_GNU)
500 st->st_atime = tar_from_oct (1 + 12, header->header.unused.oldgnu.atime);
501 st->st_ctime = tar_from_oct (1 + 12, header->header.unused.oldgnu.ctime);
504 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
505 st->st_blksize = 8 * 1024; /* FIXME */
506 #endif
507 vfs_adjust_stat (st);
510 /* --------------------------------------------------------------------------------------------- */
512 * Return 1 for success, 0 if the checksum is bad, EOF on eof,
513 * 2 for a record full of zeros (EOF marker).
516 static ReadStatus
517 tar_read_header (struct vfs_class *me, struct vfs_s_super *archive, int tard, size_t * h_size)
519 tar_super_data_t *arch = (tar_super_data_t *) archive->data;
520 ReadStatus checksum_status;
521 union record *header;
522 static char *next_long_name = NULL, *next_long_link = NULL;
524 recurse:
526 header = tar_get_next_record (archive, tard);
527 if (NULL == header)
528 return STATUS_EOF;
530 checksum_status = tar_checksum (header);
531 if (checksum_status != STATUS_SUCCESS)
532 return checksum_status;
535 * Try to determine the archive format.
537 if (arch->type == TAR_UNKNOWN)
539 if (strcmp (header->header.magic, TMAGIC) == 0)
541 if (header->header.linkflag == LF_GLOBAL_EXTHDR)
542 arch->type = TAR_POSIX;
543 else
544 arch->type = TAR_USTAR;
546 else if (strcmp (header->header.magic, OLDGNU_MAGIC) == 0)
547 arch->type = TAR_GNU;
551 * linkflag on BSDI tar (pax) always '\000'
553 if (header->header.linkflag == '\000')
555 size_t len;
557 if (header->header.arch_name[NAMSIZ - 1] != '\0')
558 len = NAMSIZ;
559 else
560 len = strlen (header->header.arch_name);
562 if (len != 0 && IS_PATH_SEP (header->header.arch_name[len - 1]))
563 header->header.linkflag = LF_DIR;
567 * Good record. Decode file size and return.
569 if (header->header.linkflag == LF_LINK || header->header.linkflag == LF_DIR)
570 *h_size = 0; /* Links 0 size on tape */
571 else
572 *h_size = tar_from_oct (1 + 12, header->header.size);
574 if (header->header.linkflag == LF_DUMPDIR)
576 if (arch->type == TAR_UNKNOWN)
577 arch->type = TAR_GNU;
581 * Skip over pax extended header and global extended
582 * header records.
584 if (header->header.linkflag == LF_EXTHDR || header->header.linkflag == LF_GLOBAL_EXTHDR)
586 if (arch->type == TAR_UNKNOWN)
587 arch->type = TAR_POSIX;
588 return STATUS_SUCCESS;
591 if (header->header.linkflag == LF_LONGNAME || header->header.linkflag == LF_LONGLINK)
593 char **longp;
594 char *bp, *data;
595 off_t size;
596 size_t written;
598 if (arch->type == TAR_UNKNOWN)
599 arch->type = TAR_GNU;
601 if (*h_size > MC_MAXPATHLEN)
603 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
604 return STATUS_BADCHECKSUM;
607 longp = ((header->header.linkflag == LF_LONGNAME) ? &next_long_name : &next_long_link);
609 g_free (*longp);
610 bp = *longp = g_malloc (*h_size + 1);
612 for (size = *h_size; size > 0; size -= written)
614 data = tar_get_next_record (archive, tard)->charptr;
615 if (data == NULL)
617 MC_PTR_FREE (*longp);
618 message (D_ERROR, MSG_ERROR, _("Unexpected EOF on archive file"));
619 return STATUS_BADCHECKSUM;
621 written = RECORDSIZE;
622 if ((off_t) written > size)
623 written = (size_t) size;
625 memcpy (bp, data, written);
626 bp += written;
629 if (bp - *longp == MC_MAXPATHLEN && bp[-1] != '\0')
631 MC_PTR_FREE (*longp);
632 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
633 return STATUS_BADCHECKSUM;
635 *bp = 0;
636 goto recurse;
638 else
640 struct stat st;
641 struct vfs_s_entry *entry;
642 struct vfs_s_inode *inode = NULL, *parent;
643 off_t data_position;
644 char *p, *q;
645 size_t len;
646 char *current_file_name, *current_link_name;
648 current_link_name =
649 (next_long_link ? next_long_link : g_strndup (header->header.arch_linkname, NAMSIZ));
650 len = strlen (current_link_name);
651 if (len > 1 && IS_PATH_SEP (current_link_name[len - 1]))
652 current_link_name[len - 1] = '\0';
654 current_file_name = NULL;
655 switch (arch->type)
657 case TAR_USTAR:
658 case TAR_POSIX:
659 /* The ustar archive format supports pathnames of upto 256
660 * characters in length. This is achieved by concatenating
661 * the contents of the 'prefix' and 'arch_name' fields like
662 * this:
664 * prefix + path_separator + arch_name
666 * If the 'prefix' field contains an empty string i.e. its
667 * first characters is '\0' the prefix field is ignored.
669 if (header->header.unused.prefix[0] != '\0')
671 char *temp_name, *temp_prefix;
673 temp_name = g_strndup (header->header.arch_name, NAMSIZ);
674 temp_prefix = g_strndup (header->header.unused.prefix, PREFIX_SIZE);
675 current_file_name = g_strconcat (temp_prefix, PATH_SEP_STR,
676 temp_name, (char *) NULL);
677 g_free (temp_name);
678 g_free (temp_prefix);
680 break;
681 case TAR_GNU:
682 if (next_long_name != NULL)
683 current_file_name = next_long_name;
684 break;
685 default:
686 break;
689 if (current_file_name == NULL)
691 if (next_long_name != NULL)
692 current_file_name = g_strdup (next_long_name);
693 else
694 current_file_name = g_strndup (header->header.arch_name, NAMSIZ);
697 canonicalize_pathname (current_file_name);
698 len = strlen (current_file_name);
700 data_position = current_tar_position;
702 p = strrchr (current_file_name, PATH_SEP);
703 if (p == NULL)
705 p = current_file_name;
706 q = current_file_name + len; /* "" */
708 else
710 *(p++) = 0;
711 q = current_file_name;
714 parent = vfs_s_find_inode (me, archive, q, LINK_NO_FOLLOW, FL_MKDIR);
715 if (parent == NULL)
717 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
718 return STATUS_BADCHECKSUM;
721 if (header->header.linkflag == LF_LINK)
723 inode = vfs_s_find_inode (me, archive, current_link_name, LINK_NO_FOLLOW, FL_NONE);
724 if (inode == NULL)
726 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
728 else
730 entry = vfs_s_new_entry (me, p, inode);
731 vfs_s_insert_entry (me, parent, entry);
732 g_free (current_link_name);
733 goto done;
737 tar_fill_stat (archive, &st, header, *h_size);
738 if (S_ISDIR (st.st_mode))
740 entry = MEDATA->find_entry (me, parent, p, LINK_NO_FOLLOW, FL_NONE);
741 if (entry)
742 goto done;
744 inode = vfs_s_new_inode (me, archive, &st);
746 inode->data_offset = data_position;
747 if (*current_link_name)
749 inode->linkname = current_link_name;
751 else if (current_link_name != next_long_link)
753 g_free (current_link_name);
755 entry = vfs_s_new_entry (me, p, inode);
757 vfs_s_insert_entry (me, parent, entry);
758 g_free (current_file_name);
760 done:
761 next_long_link = next_long_name = NULL;
763 if (arch->type == TAR_GNU && header->header.unused.oldgnu.isextended)
765 while (tar_get_next_record (archive, tard)->ext_hdr.isextended != 0)
768 if (inode != NULL)
769 inode->data_offset = current_tar_position;
771 return STATUS_SUCCESS;
775 /* --------------------------------------------------------------------------------------------- */
777 * Main loop for reading an archive.
778 * Returns 0 on success, -1 on error.
780 static int
781 tar_open_archive (struct vfs_s_super *archive, const vfs_path_t * vpath,
782 const vfs_path_element_t * vpath_element)
784 /* Initial status at start of archive */
785 ReadStatus status = STATUS_EOFMARK;
786 int tard;
788 current_tar_position = 0;
789 /* Open for reading */
790 tard = tar_open_archive_int (vpath_element->class, vpath, archive);
791 if (tard == -1)
792 return -1;
794 while (TRUE)
796 size_t h_size = 0;
797 ReadStatus prev_status = status;
799 status = tar_read_header (vpath_element->class, archive, tard, &h_size);
801 switch (status)
803 case STATUS_SUCCESS:
804 tar_skip_n_records (archive, tard, (h_size + RECORDSIZE - 1) / RECORDSIZE);
805 continue;
808 * Invalid header:
810 * If the previous header was good, tell them
811 * that we are skipping bad ones.
813 case STATUS_BADCHECKSUM:
814 switch (prev_status)
816 /* Error on first record */
817 case STATUS_EOFMARK:
819 message (D_ERROR, MSG_ERROR, _("%s\ndoesn't look like a tar archive."),
820 vfs_path_as_str (vpath));
821 MC_FALLTHROUGH;
823 /* Error after header rec */
825 case STATUS_SUCCESS:
826 /* Error after error */
828 case STATUS_BADCHECKSUM:
829 return -1;
831 case STATUS_EOF:
832 return 0;
834 default:
835 break;
838 /* Record of zeroes */
839 case STATUS_EOFMARK: /* If error after 0's */
840 MC_FALLTHROUGH;
841 /* exit from loop */
842 case STATUS_EOF: /* End of archive */
843 break;
844 default:
845 break;
847 break;
849 return 0;
852 /* --------------------------------------------------------------------------------------------- */
854 static void *
855 tar_super_check (const vfs_path_t * vpath)
857 static struct stat stat_buf;
858 int stat_result;
860 stat_result = mc_stat (vpath, &stat_buf);
862 return (stat_result != 0) ? NULL : &stat_buf;
865 /* --------------------------------------------------------------------------------------------- */
867 static int
868 tar_super_same (const vfs_path_element_t * vpath_element, struct vfs_s_super *parc,
869 const vfs_path_t * vpath, void *cookie)
871 struct stat *archive_stat = cookie; /* stat of main archive */
873 (void) vpath_element;
875 if (strcmp (parc->name, vfs_path_as_str (vpath)) != 0)
876 return 0;
878 /* Has the cached archive been changed on the disk? */
879 if (((tar_super_data_t *) parc->data)->st.st_mtime < archive_stat->st_mtime)
881 /* Yes, reload! */
882 vfs_tarfs_ops->free ((vfsid) parc);
883 vfs_rmstamp (vfs_tarfs_ops, (vfsid) parc);
884 return 2;
886 /* Hasn't been modified, give it a new timeout */
887 vfs_stamp (vfs_tarfs_ops, (vfsid) parc);
888 return 1;
891 /* --------------------------------------------------------------------------------------------- */
893 static ssize_t
894 tar_read (void *fh, char *buffer, size_t count)
896 off_t begin = FH->ino->data_offset;
897 int fd = ((tar_super_data_t *) FH_SUPER->data)->fd;
898 struct vfs_class *me = FH_SUPER->me;
899 ssize_t res;
901 if (mc_lseek (fd, begin + FH->pos, SEEK_SET) != begin + FH->pos)
902 ERRNOR (EIO, -1);
904 count = MIN (count, (size_t) (FH->ino->st.st_size - FH->pos));
906 res = mc_read (fd, buffer, count);
907 if (res == -1)
908 ERRNOR (errno, -1);
910 FH->pos += res;
911 return res;
914 /* --------------------------------------------------------------------------------------------- */
916 static int
917 tar_fh_open (struct vfs_class *me, vfs_file_handler_t * fh, int flags, mode_t mode)
919 (void) fh;
920 (void) mode;
922 if ((flags & O_ACCMODE) != O_RDONLY)
923 ERRNOR (EROFS, -1);
924 return 0;
927 /* --------------------------------------------------------------------------------------------- */
928 /*** public functions ****************************************************************************/
929 /* --------------------------------------------------------------------------------------------- */
931 void
932 init_tarfs (void)
934 tarfs_subclass.flags = VFS_S_READONLY; /* FIXME: tarfs used own temp files */
935 tarfs_subclass.archive_check = tar_super_check;
936 tarfs_subclass.archive_same = tar_super_same;
937 tarfs_subclass.open_archive = tar_open_archive;
938 tarfs_subclass.free_archive = tar_free_archive;
939 tarfs_subclass.fh_open = tar_fh_open;
941 vfs_s_init_class (&tarfs_subclass);
942 vfs_tarfs_ops->name = "tarfs";
943 vfs_tarfs_ops->prefix = "utar";
944 vfs_tarfs_ops->read = tar_read;
945 vfs_tarfs_ops->setctl = NULL;
946 vfs_register_class (vfs_tarfs_ops);
949 /* --------------------------------------------------------------------------------------------- */