Update translations from Transifex
[midnight-commander.git] / src / vfs / tar / tar.c
blob95f12e73148af25990ca07d57282d51cd7d08fe1
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_class vfs_tarfs_ops;
227 /* As we open one archive at a time, it is safe to have this static */
228 static off_t current_tar_position = 0;
230 static union record rec_buf;
232 /*** file scope functions ************************************************************************/
233 /* --------------------------------------------------------------------------------------------- */
235 * Quick and dirty octal conversion.
237 * Result is -1 if the field is invalid (all blank, or nonoctal).
239 static long
240 tar_from_oct (int digs, const char *where)
242 long value;
244 while (isspace ((unsigned char) *where))
245 { /* Skip spaces */
246 where++;
247 if (--digs <= 0)
248 return -1; /* All blank field */
250 value = 0;
251 while (digs > 0 && isodigit (*where))
252 { /* Scan till nonoctal */
253 value = (value << 3) | (*where++ - '0');
254 --digs;
257 if (digs > 0 && *where && !isspace ((unsigned char) *where))
258 return -1; /* Ended on non-space/nul */
260 return value;
263 /* --------------------------------------------------------------------------------------------- */
265 static void
266 tar_free_archive (struct vfs_class *me, struct vfs_s_super *archive)
268 (void) me;
270 if (archive->data != NULL)
272 tar_super_data_t *arch = (tar_super_data_t *) archive->data;
274 if (arch->fd != -1)
275 mc_close (arch->fd);
276 g_free (archive->data);
280 /* --------------------------------------------------------------------------------------------- */
282 /* Returns fd of the open tar file */
283 static int
284 tar_open_archive_int (struct vfs_class *me, const vfs_path_t * vpath, struct vfs_s_super *archive)
286 int result, type;
287 tar_super_data_t *arch;
288 mode_t mode;
289 struct vfs_s_inode *root;
291 result = mc_open (vpath, O_RDONLY);
292 if (result == -1)
294 message (D_ERROR, MSG_ERROR, _("Cannot open tar archive\n%s"), vfs_path_as_str (vpath));
295 ERRNOR (ENOENT, -1);
298 archive->name = g_strdup (vfs_path_as_str (vpath));
299 archive->data = g_new (tar_super_data_t, 1);
300 arch = (tar_super_data_t *) archive->data;
301 mc_stat (vpath, &arch->st);
302 arch->fd = -1;
303 arch->type = TAR_UNKNOWN;
305 /* Find out the method to handle this tar file */
306 type = get_compression_type (result, archive->name);
307 if (type == COMPRESSION_NONE)
308 mc_lseek (result, 0, SEEK_SET);
309 else
311 char *s;
312 vfs_path_t *tmp_vpath;
314 mc_close (result);
315 s = g_strconcat (archive->name, decompress_extension (type), (char *) NULL);
316 tmp_vpath = vfs_path_from_str_flags (s, VPF_NO_CANON);
317 result = mc_open (tmp_vpath, O_RDONLY);
318 vfs_path_free (tmp_vpath);
319 if (result == -1)
320 message (D_ERROR, MSG_ERROR, _("Cannot open tar archive\n%s"), s);
321 g_free (s);
322 if (result == -1)
324 MC_PTR_FREE (archive->name);
325 ERRNOR (ENOENT, -1);
329 arch->fd = result;
330 mode = arch->st.st_mode & 07777;
331 if (mode & 0400)
332 mode |= 0100;
333 if (mode & 0040)
334 mode |= 0010;
335 if (mode & 0004)
336 mode |= 0001;
337 mode |= S_IFDIR;
339 root = vfs_s_new_inode (me, archive, &arch->st);
340 root->st.st_mode = mode;
341 root->data_offset = -1;
342 root->st.st_nlink++;
343 root->st.st_dev = MEDATA->rdev++;
345 archive->root = root;
347 return result;
350 /* --------------------------------------------------------------------------------------------- */
352 static union record *
353 tar_get_next_record (struct vfs_s_super *archive, int tard)
355 int n;
357 (void) archive;
359 n = mc_read (tard, rec_buf.charptr, sizeof (rec_buf.charptr));
360 if (n != sizeof (rec_buf.charptr))
361 return NULL; /* An error has occurred */
362 current_tar_position += sizeof (rec_buf.charptr);
363 return &rec_buf;
366 /* --------------------------------------------------------------------------------------------- */
368 static void
369 tar_skip_n_records (struct vfs_s_super *archive, int tard, size_t n)
371 (void) archive;
373 mc_lseek (tard, n * sizeof (rec_buf.charptr), SEEK_CUR);
374 current_tar_position += n * sizeof (rec_buf.charptr);
377 /* --------------------------------------------------------------------------------------------- */
379 static ReadStatus
380 tar_checksum (const union record *header)
382 long recsum;
383 long signed_sum = 0;
384 long sum = 0;
385 int i;
386 const char *p = header->charptr;
388 recsum = tar_from_oct (8, header->header.chksum);
390 for (i = sizeof (*header); --i >= 0;)
393 * We can't use unsigned char here because of old compilers,
394 * e.g. V7.
396 signed_sum += *p;
397 sum += 0xFF & *p++;
400 /* Adjust checksum to count the "chksum" field as blanks. */
401 for (i = sizeof (header->header.chksum); --i >= 0;)
403 sum -= 0xFF & header->header.chksum[i];
404 signed_sum -= (char) header->header.chksum[i];
407 sum += ' ' * sizeof (header->header.chksum);
408 signed_sum += ' ' * sizeof (header->header.chksum);
411 * This is a zeroed record...whole record is 0's except
412 * for the 8 blanks we faked for the checksum field.
414 if (sum == 8 * ' ')
415 return STATUS_EOFMARK;
417 if (sum != recsum && signed_sum != recsum)
418 return STATUS_BADCHECKSUM;
420 return STATUS_SUCCESS;
423 /* --------------------------------------------------------------------------------------------- */
425 static void
426 tar_fill_stat (struct vfs_s_super *archive, struct stat *st, union record *header, size_t h_size)
428 tar_super_data_t *arch = (tar_super_data_t *) archive->data;
430 st->st_mode = tar_from_oct (8, header->header.mode);
432 /* Adjust st->st_mode because there are tar-files with
433 * linkflag==LF_SYMLINK and S_ISLNK(mod)==0. I don't
434 * know about the other modes but I think I cause no new
435 * problem when I adjust them, too. -- Norbert.
437 if (header->header.linkflag == LF_DIR || header->header.linkflag == LF_DUMPDIR)
438 st->st_mode |= S_IFDIR;
439 else if (header->header.linkflag == LF_SYMLINK)
440 st->st_mode |= S_IFLNK;
441 else if (header->header.linkflag == LF_CHR)
442 st->st_mode |= S_IFCHR;
443 else if (header->header.linkflag == LF_BLK)
444 st->st_mode |= S_IFBLK;
445 else if (header->header.linkflag == LF_FIFO)
446 st->st_mode |= S_IFIFO;
447 else
448 st->st_mode |= S_IFREG;
450 st->st_dev = 0;
451 #ifdef HAVE_STRUCT_STAT_ST_RDEV
452 st->st_rdev = 0;
453 #endif
455 switch (arch->type)
457 case TAR_USTAR:
458 case TAR_POSIX:
459 case TAR_GNU:
460 /* *INDENT-OFF* */
461 st->st_uid = *header->header.uname
462 ? vfs_finduid (header->header.uname)
463 : tar_from_oct (8, header->header.uid);
464 st->st_gid = *header->header.gname
465 ? vfs_findgid (header->header.gname)
466 : tar_from_oct (8,header->header.gid);
467 /* *INDENT-ON* */
469 switch (header->header.linkflag)
471 case LF_BLK:
472 case LF_CHR:
473 #ifdef HAVE_STRUCT_STAT_ST_RDEV
474 st->st_rdev =
475 makedev (tar_from_oct (8, header->header.devmajor),
476 tar_from_oct (8, header->header.devminor));
477 #endif
478 break;
479 default:
480 break;
482 break;
484 default:
485 st->st_uid = tar_from_oct (8, header->header.uid);
486 st->st_gid = tar_from_oct (8, header->header.gid);
487 break;
490 st->st_size = h_size;
491 #ifdef HAVE_STRUCT_STAT_ST_MTIM
492 st->st_atim.tv_nsec = st->st_mtim.tv_nsec = st->st_ctim.tv_nsec = 0;
493 #endif
494 st->st_mtime = tar_from_oct (1 + 12, header->header.mtime);
495 st->st_atime = 0;
496 st->st_ctime = 0;
497 if (arch->type == TAR_GNU)
499 st->st_atime = tar_from_oct (1 + 12, header->header.unused.oldgnu.atime);
500 st->st_ctime = tar_from_oct (1 + 12, header->header.unused.oldgnu.ctime);
503 #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
504 st->st_blksize = 8 * 1024; /* FIXME */
505 #endif
506 vfs_adjust_stat (st);
509 /* --------------------------------------------------------------------------------------------- */
511 * Return 1 for success, 0 if the checksum is bad, EOF on eof,
512 * 2 for a record full of zeros (EOF marker).
515 static ReadStatus
516 tar_read_header (struct vfs_class *me, struct vfs_s_super *archive, int tard, size_t * h_size)
518 tar_super_data_t *arch = (tar_super_data_t *) archive->data;
519 ReadStatus checksum_status;
520 union record *header;
521 static char *next_long_name = NULL, *next_long_link = NULL;
523 recurse:
525 header = tar_get_next_record (archive, tard);
526 if (NULL == header)
527 return STATUS_EOF;
529 checksum_status = tar_checksum (header);
530 if (checksum_status != STATUS_SUCCESS)
531 return checksum_status;
534 * Try to determine the archive format.
536 if (arch->type == TAR_UNKNOWN)
538 if (strcmp (header->header.magic, TMAGIC) == 0)
540 if (header->header.linkflag == LF_GLOBAL_EXTHDR)
541 arch->type = TAR_POSIX;
542 else
543 arch->type = TAR_USTAR;
545 else if (strcmp (header->header.magic, OLDGNU_MAGIC) == 0)
546 arch->type = TAR_GNU;
550 * linkflag on BSDI tar (pax) always '\000'
552 if (header->header.linkflag == '\000')
554 size_t len;
556 if (header->header.arch_name[NAMSIZ - 1] != '\0')
557 len = NAMSIZ;
558 else
559 len = strlen (header->header.arch_name);
561 if (len != 0 && IS_PATH_SEP (header->header.arch_name[len - 1]))
562 header->header.linkflag = LF_DIR;
566 * Good record. Decode file size and return.
568 if (header->header.linkflag == LF_LINK || header->header.linkflag == LF_DIR)
569 *h_size = 0; /* Links 0 size on tape */
570 else
571 *h_size = tar_from_oct (1 + 12, header->header.size);
573 if (header->header.linkflag == LF_DUMPDIR)
575 if (arch->type == TAR_UNKNOWN)
576 arch->type = TAR_GNU;
580 * Skip over pax extended header and global extended
581 * header records.
583 if (header->header.linkflag == LF_EXTHDR || header->header.linkflag == LF_GLOBAL_EXTHDR)
585 if (arch->type == TAR_UNKNOWN)
586 arch->type = TAR_POSIX;
587 return STATUS_SUCCESS;
590 if (header->header.linkflag == LF_LONGNAME || header->header.linkflag == LF_LONGLINK)
592 char **longp;
593 char *bp, *data;
594 off_t size;
595 size_t written;
597 if (arch->type == TAR_UNKNOWN)
598 arch->type = TAR_GNU;
600 if (*h_size > MC_MAXPATHLEN)
602 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
603 return STATUS_BADCHECKSUM;
606 longp = ((header->header.linkflag == LF_LONGNAME) ? &next_long_name : &next_long_link);
608 g_free (*longp);
609 bp = *longp = g_malloc (*h_size + 1);
611 for (size = *h_size; size > 0; size -= written)
613 data = tar_get_next_record (archive, tard)->charptr;
614 if (data == NULL)
616 MC_PTR_FREE (*longp);
617 message (D_ERROR, MSG_ERROR, _("Unexpected EOF on archive file"));
618 return STATUS_BADCHECKSUM;
620 written = RECORDSIZE;
621 if ((off_t) written > size)
622 written = (size_t) size;
624 memcpy (bp, data, written);
625 bp += written;
628 if (bp - *longp == MC_MAXPATHLEN && bp[-1] != '\0')
630 MC_PTR_FREE (*longp);
631 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
632 return STATUS_BADCHECKSUM;
634 *bp = 0;
635 goto recurse;
637 else
639 struct stat st;
640 struct vfs_s_entry *entry;
641 struct vfs_s_inode *inode = NULL, *parent;
642 off_t data_position;
643 char *p, *q;
644 size_t len;
645 char *current_file_name, *current_link_name;
647 current_link_name =
648 (next_long_link ? next_long_link : g_strndup (header->header.arch_linkname, NAMSIZ));
649 len = strlen (current_link_name);
650 if (len > 1 && IS_PATH_SEP (current_link_name[len - 1]))
651 current_link_name[len - 1] = '\0';
653 current_file_name = NULL;
654 switch (arch->type)
656 case TAR_USTAR:
657 case TAR_POSIX:
658 /* The ustar archive format supports pathnames of upto 256
659 * characters in length. This is achieved by concatenating
660 * the contents of the 'prefix' and 'arch_name' fields like
661 * this:
663 * prefix + path_separator + arch_name
665 * If the 'prefix' field contains an empty string i.e. its
666 * first characters is '\0' the prefix field is ignored.
668 if (header->header.unused.prefix[0] != '\0')
670 char *temp_name, *temp_prefix;
672 temp_name = g_strndup (header->header.arch_name, NAMSIZ);
673 temp_prefix = g_strndup (header->header.unused.prefix, PREFIX_SIZE);
674 current_file_name = g_strconcat (temp_prefix, PATH_SEP_STR,
675 temp_name, (char *) NULL);
676 g_free (temp_name);
677 g_free (temp_prefix);
679 break;
680 case TAR_GNU:
681 if (next_long_name != NULL)
682 current_file_name = next_long_name;
683 break;
684 default:
685 break;
688 if (current_file_name == NULL)
690 if (next_long_name != NULL)
691 current_file_name = g_strdup (next_long_name);
692 else
693 current_file_name = g_strndup (header->header.arch_name, NAMSIZ);
696 canonicalize_pathname (current_file_name);
697 len = strlen (current_file_name);
699 data_position = current_tar_position;
701 p = strrchr (current_file_name, PATH_SEP);
702 if (p == NULL)
704 p = current_file_name;
705 q = current_file_name + len; /* "" */
707 else
709 *(p++) = 0;
710 q = current_file_name;
713 parent = vfs_s_find_inode (me, archive, q, LINK_NO_FOLLOW, FL_MKDIR);
714 if (parent == NULL)
716 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
717 return STATUS_BADCHECKSUM;
720 if (header->header.linkflag == LF_LINK)
722 inode = vfs_s_find_inode (me, archive, current_link_name, LINK_NO_FOLLOW, 0);
723 if (inode == NULL)
725 message (D_ERROR, MSG_ERROR, _("Inconsistent tar archive"));
727 else
729 entry = vfs_s_new_entry (me, p, inode);
730 vfs_s_insert_entry (me, parent, entry);
731 g_free (current_link_name);
732 goto done;
736 tar_fill_stat (archive, &st, header, *h_size);
737 if (S_ISDIR (st.st_mode))
739 entry = MEDATA->find_entry (me, parent, p, LINK_NO_FOLLOW, FL_NONE);
740 if (entry)
741 goto done;
743 inode = vfs_s_new_inode (me, archive, &st);
745 inode->data_offset = data_position;
746 if (*current_link_name)
748 inode->linkname = current_link_name;
750 else if (current_link_name != next_long_link)
752 g_free (current_link_name);
754 entry = vfs_s_new_entry (me, p, inode);
756 vfs_s_insert_entry (me, parent, entry);
757 g_free (current_file_name);
759 done:
760 next_long_link = next_long_name = NULL;
762 if (arch->type == TAR_GNU && header->header.unused.oldgnu.isextended)
764 while (tar_get_next_record (archive, tard)->ext_hdr.isextended != 0)
767 if (inode != NULL)
768 inode->data_offset = current_tar_position;
770 return STATUS_SUCCESS;
774 /* --------------------------------------------------------------------------------------------- */
776 * Main loop for reading an archive.
777 * Returns 0 on success, -1 on error.
779 static int
780 tar_open_archive (struct vfs_s_super *archive, const vfs_path_t * vpath,
781 const vfs_path_element_t * vpath_element)
783 /* Initial status at start of archive */
784 ReadStatus status = STATUS_EOFMARK;
785 int tard;
787 current_tar_position = 0;
788 /* Open for reading */
789 tard = tar_open_archive_int (vpath_element->class, vpath, archive);
790 if (tard == -1)
791 return -1;
793 while (TRUE)
795 size_t h_size = 0;
796 ReadStatus prev_status = status;
798 status = tar_read_header (vpath_element->class, archive, tard, &h_size);
800 switch (status)
802 case STATUS_SUCCESS:
803 tar_skip_n_records (archive, tard, (h_size + RECORDSIZE - 1) / RECORDSIZE);
804 continue;
807 * Invalid header:
809 * If the previous header was good, tell them
810 * that we are skipping bad ones.
812 case STATUS_BADCHECKSUM:
813 switch (prev_status)
815 /* Error on first record */
816 case STATUS_EOFMARK:
818 message (D_ERROR, MSG_ERROR, _("%s\ndoesn't look like a tar archive."),
819 vfs_path_as_str (vpath));
820 MC_FALLTHROUGH;
822 /* Error after header rec */
824 case STATUS_SUCCESS:
825 /* Error after error */
827 case STATUS_BADCHECKSUM:
828 return -1;
830 case STATUS_EOF:
831 return 0;
833 default:
834 break;
837 /* Record of zeroes */
838 case STATUS_EOFMARK: /* If error after 0's */
839 MC_FALLTHROUGH;
840 /* exit from loop */
841 case STATUS_EOF: /* End of archive */
842 break;
843 default:
844 break;
846 break;
848 return 0;
851 /* --------------------------------------------------------------------------------------------- */
853 static void *
854 tar_super_check (const vfs_path_t * vpath)
856 static struct stat stat_buf;
857 int stat_result;
859 stat_result = mc_stat (vpath, &stat_buf);
861 return (stat_result != 0) ? NULL : &stat_buf;
864 /* --------------------------------------------------------------------------------------------- */
866 static int
867 tar_super_same (const vfs_path_element_t * vpath_element, struct vfs_s_super *parc,
868 const vfs_path_t * vpath, void *cookie)
870 struct stat *archive_stat = cookie; /* stat of main archive */
872 (void) vpath_element;
874 if (strcmp (parc->name, vfs_path_as_str (vpath)) != 0)
875 return 0;
877 /* Has the cached archive been changed on the disk? */
878 if (((tar_super_data_t *) parc->data)->st.st_mtime < archive_stat->st_mtime)
880 /* Yes, reload! */
881 (*vfs_tarfs_ops.free) ((vfsid) parc);
882 vfs_rmstamp (&vfs_tarfs_ops, (vfsid) parc);
883 return 2;
885 /* Hasn't been modified, give it a new timeout */
886 vfs_stamp (&vfs_tarfs_ops, (vfsid) parc);
887 return 1;
890 /* --------------------------------------------------------------------------------------------- */
892 static ssize_t
893 tar_read (void *fh, char *buffer, size_t count)
895 off_t begin = FH->ino->data_offset;
896 int fd = ((tar_super_data_t *) FH_SUPER->data)->fd;
897 struct vfs_class *me = FH_SUPER->me;
898 ssize_t res;
900 if (mc_lseek (fd, begin + FH->pos, SEEK_SET) != begin + FH->pos)
901 ERRNOR (EIO, -1);
903 count = MIN (count, (size_t) (FH->ino->st.st_size - FH->pos));
905 res = mc_read (fd, buffer, count);
906 if (res == -1)
907 ERRNOR (errno, -1);
909 FH->pos += res;
910 return res;
913 /* --------------------------------------------------------------------------------------------- */
915 static int
916 tar_fh_open (struct vfs_class *me, vfs_file_handler_t * fh, int flags, mode_t mode)
918 (void) fh;
919 (void) mode;
921 if ((flags & O_ACCMODE) != O_RDONLY)
922 ERRNOR (EROFS, -1);
923 return 0;
926 /* --------------------------------------------------------------------------------------------- */
927 /*** public functions ****************************************************************************/
928 /* --------------------------------------------------------------------------------------------- */
930 void
931 init_tarfs (void)
933 static struct vfs_s_subclass tarfs_subclass;
935 tarfs_subclass.flags = VFS_S_READONLY; /* FIXME: tarfs used own temp files */
936 tarfs_subclass.archive_check = tar_super_check;
937 tarfs_subclass.archive_same = tar_super_same;
938 tarfs_subclass.open_archive = tar_open_archive;
939 tarfs_subclass.free_archive = tar_free_archive;
940 tarfs_subclass.fh_open = tar_fh_open;
942 vfs_s_init_class (&vfs_tarfs_ops, &tarfs_subclass);
943 vfs_tarfs_ops.name = "tarfs";
944 vfs_tarfs_ops.prefix = "utar";
945 vfs_tarfs_ops.read = tar_read;
946 vfs_tarfs_ops.setctl = NULL;
947 vfs_register_class (&vfs_tarfs_ops);
950 /* --------------------------------------------------------------------------------------------- */