1 /* Virtual File System: GNU Tar file system.
2 Copyright (C) 1995 The Free Software Foundation
4 Written by: 1995 Jakub Jelinek
5 Rewritten by: 1998 Pavel Machek
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public License
9 as published by the Free Software Foundation; either version 2 of
10 the License, or (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU Library General Public License for more details.
17 You should have received a copy of the GNU Library General Public
18 License along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
21 /* Namespace: vfs_tarfs_ops */
23 #include "xdirentry.h"
26 #include <sys/timeb.h> /* alex: for struct timeb definition */
27 #endif /* SCO_FLAVOR */
32 #include "../src/dialog.h" /* For MSG_ERROR */
36 #define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )
38 * Quick and dirty octal conversion.
40 * Result is -1 if the field is invalid (all blank, or nonoctal).
42 static long from_oct (int digs
, char *where
)
46 while (isspace (*where
)) { /* Skip spaces */
49 return -1; /* All blank field */
52 while (digs
> 0 && isodigit (*where
)) { /* Scan till nonoctal */
53 value
= (value
<< 3) | (*where
++ - '0');
57 if (digs
> 0 && *where
&& !isspace (*where
))
58 return -1; /* Ended on non-space/nul */
63 static struct stat hstat
; /* Stat struct corresponding */
65 static void tar_free_archive (vfs
*me
, vfs_s_super
*archive
)
67 if (archive
->u
.tar
.fd
!= -1)
68 mc_close(archive
->u
.tar
.fd
);
71 /* As we open one archive at a time, it is safe to have this static */
72 static int current_tar_position
= 0;
74 /* Returns fd of the open tar file */
75 static int tar_open_archive (vfs
*me
, char *name
, vfs_s_super
*archive
)
80 struct vfs_s_inode
*root
;
82 result
= mc_open (name
, O_RDONLY
);
84 message_2s (1, MSG_ERROR
, _("Couldn't open tar archive\n%s"), name
);
88 archive
->name
= g_strdup (name
);
89 mc_stat (name
, &(archive
->u
.tar
.tarstat
));
90 archive
->u
.tar
.fd
= -1;
92 /* Find out the method to handle this tar file */
93 size
= is_gunzipable (result
, &type
);
94 mc_lseek (result
, 0, SEEK_SET
);
98 s
= g_strconcat ( archive
->name
, decompress_extension (type
), NULL
);
99 result
= mc_open (s
, O_RDONLY
);
101 message_2s (1, MSG_ERROR
, _("Couldn't open tar archive\n%s"), s
);
107 archive
->u
.tar
.fd
= result
;
108 mode
= archive
->u
.tar
.tarstat
.st_mode
& 07777;
109 if (mode
& 0400) mode
|= 0100;
110 if (mode
& 0040) mode
|= 0010;
111 if (mode
& 0004) mode
|= 0001;
114 root
= vfs_s_new_inode (me
, archive
, &archive
->u
.tar
.tarstat
);
115 root
->st
.st_mode
= mode
;
116 root
->u
.tar
.data_offset
= -1;
118 root
->st
.st_dev
= MEDATA
->rdev
++;
120 vfs_s_add_dots (me
, root
, NULL
);
121 archive
->root
= root
;
126 static union record rec_buf
;
128 static union record
*
129 get_next_record (vfs_s_super
*archive
, int tard
)
133 n
= mc_read (tard
, rec_buf
.charptr
, RECORDSIZE
);
135 return NULL
; /* An error has occurred */
136 current_tar_position
+= RECORDSIZE
;
140 static void skip_n_records (vfs_s_super
*archive
, int tard
, int n
)
142 mc_lseek (tard
, n
* RECORDSIZE
, SEEK_CUR
);
143 current_tar_position
+= n
* RECORDSIZE
;
146 static void fill_stat_from_header (vfs
*me
, struct stat
*st
, union record
*header
)
148 st
->st_mode
= from_oct (8, header
->header
.mode
);
150 /* Adjust st->st_mode because there are tar-files with
151 * linkflag==LF_SYMLINK and S_ISLNK(mod)==0. I don't
152 * know about the other modes but I think I cause no new
153 * problem when I adjust them, too. -- Norbert.
155 if (header
->header
.linkflag
== LF_DIR
) {
156 st
->st_mode
|= S_IFDIR
;
157 } else if (header
->header
.linkflag
== LF_SYMLINK
) {
158 st
->st_mode
|= S_IFLNK
;
159 } else if (header
->header
.linkflag
== LF_CHR
) {
160 st
->st_mode
|= S_IFCHR
;
161 } else if (header
->header
.linkflag
== LF_BLK
) {
162 st
->st_mode
|= S_IFBLK
;
163 } else if (header
->header
.linkflag
== LF_FIFO
) {
164 st
->st_mode
|= S_IFIFO
;
166 st
->st_mode
|= S_IFREG
;
169 if (!strcmp (header
->header
.magic
, TMAGIC
)) {
170 st
->st_uid
= *header
->header
.uname
? finduid (header
->header
.uname
) :
171 from_oct (8, header
->header
.uid
);
172 st
->st_gid
= *header
->header
.gname
? findgid (header
->header
.gname
) :
173 from_oct (8, header
->header
.gid
);
174 switch (header
->header
.linkflag
) {
177 st
->st_rdev
= (from_oct (8, header
->header
.devmajor
) << 8) |
178 from_oct (8, header
->header
.devminor
);
180 } else { /* Old Unix tar */
181 st
->st_uid
= from_oct (8, header
->header
.uid
);
182 st
->st_gid
= from_oct (8, header
->header
.gid
);
184 st
->st_size
= hstat
.st_size
;
185 st
->st_mtime
= from_oct (1 + 12, header
->header
.mtime
);
186 st
->st_atime
= from_oct (1 + 12, header
->header
.atime
);
187 st
->st_ctime
= from_oct (1 + 12, header
->header
.ctime
);
198 * Return 1 for success, 0 if the checksum is bad, EOF on eof,
199 * 2 for a record full of zeros (EOF marker).
203 read_header (vfs
*me
, vfs_s_super
*archive
, int tard
)
206 register long sum
, signed_sum
, recsum
;
208 register union record
*header
;
212 static char *next_long_name
= NULL
, *next_long_link
= NULL
;
213 char *current_file_name
, *current_link_name
;
217 header
= get_next_record (archive
, tard
);
221 recsum
= from_oct (8, header
->header
.chksum
);
223 sum
= 0; signed_sum
= 0;
225 for (i
= sizeof (*header
); --i
>= 0;) {
227 * We can't use unsigned char here because of old compilers,
234 /* Adjust checksum to count the "chksum" field as blanks. */
235 for (i
= sizeof (header
->header
.chksum
); --i
>= 0;) {
236 sum
-= 0xFF & header
->header
.chksum
[i
];
237 signed_sum
-= (char) header
->header
.chksum
[i
];
239 sum
+= ' ' * sizeof header
->header
.chksum
;
240 signed_sum
+= ' ' * sizeof header
->header
.chksum
;
243 * This is a zeroed record...whole record is 0's except
244 * for the 8 blanks we faked for the checksum field.
247 return STATUS_EOFMARK
;
249 if (sum
!= recsum
&& signed_sum
!= recsum
)
250 return STATUS_BADCHECKSUM
;
253 * linkflag on BSDI tar (pax) always '\000'
255 if (header
->header
.linkflag
== '\000' &&
256 strlen(header
->header
.arch_name
) &&
257 header
->header
.arch_name
[strlen(header
->header
.arch_name
) - 1] == '/')
258 header
->header
.linkflag
= LF_DIR
;
261 * Good record. Decode file size and return.
263 if (header
->header
.linkflag
== LF_LINK
|| header
->header
.linkflag
== LF_DIR
)
264 hstat
.st_size
= 0; /* Links 0 size on tape */
266 hstat
.st_size
= from_oct (1 + 12, header
->header
.size
);
268 header
->header
.arch_name
[NAMSIZ
- 1] = '\0';
269 if (header
->header
.linkflag
== LF_LONGNAME
270 || header
->header
.linkflag
== LF_LONGLINK
) {
271 longp
= ((header
->header
.linkflag
== LF_LONGNAME
)
277 bp
= *longp
= g_malloc (hstat
.st_size
);
279 for (size
= hstat
.st_size
;
282 data
= get_next_record (archive
, tard
)->charptr
;
284 message_1s (1, MSG_ERROR
, _("Unexpected EOF on archive file"));
285 return STATUS_BADCHECKSUM
;
287 written
= RECORDSIZE
;
291 bcopy (data
, bp
, written
);
295 if (hstat
.st_size
> 1)
296 bp
[hstat
.st_size
- 1] = 0; /* just to make sure */
301 struct vfs_s_entry
*entry
;
302 struct vfs_s_inode
*inode
, *parent
;
308 current_file_name
= (next_long_name
310 : g_strdup (header
->header
.arch_name
));
311 len
= strlen (current_file_name
);
312 if (current_file_name
[len
- 1] == '/') {
313 current_file_name
[len
- 1] = 0;
317 current_link_name
= (next_long_link
319 : g_strdup (header
->header
.arch_linkname
));
320 len
= strlen (current_link_name
);
321 if (len
&& current_link_name
[len
- 1] == '/')
322 current_link_name
[len
- 1] = 0;
324 next_long_link
= next_long_name
= NULL
;
326 data_position
= current_tar_position
;
328 p
= strrchr (current_file_name
, '/');
330 p
= current_file_name
;
331 q
= current_file_name
+ strlen (current_file_name
); /* "" */
334 q
= current_file_name
;
337 parent
= vfs_s_find_inode (me
, archive
->root
, q
, LINK_NO_FOLLOW
, FL_MKDIR
);
338 if (parent
== NULL
) {
339 message_1s (1, MSG_ERROR
, _("Inconsistent tar archive"));
340 return STATUS_BADCHECKSUM
;
343 if (header
->header
.linkflag
== LF_LINK
) {
344 inode
= vfs_s_find_inode (me
, archive
->root
, current_link_name
, LINK_NO_FOLLOW
, 0);
346 message_1s (1, MSG_ERROR
, _("Inconsistent tar archive"));
348 entry
= vfs_s_new_entry(me
, p
, inode
);
349 vfs_s_insert_entry(me
, parent
, entry
);
350 g_free (current_link_name
);
355 fill_stat_from_header (me
, &st
, header
);
356 inode
= vfs_s_new_inode (me
, archive
, &st
);
358 inode
->u
.tar
.data_offset
= data_position
;
359 if (*current_link_name
)
360 inode
->linkname
= current_link_name
;
361 entry
= vfs_s_new_entry (me
, p
, inode
);
363 vfs_s_insert_entry (me
, parent
, entry
);
364 g_free (current_file_name
);
367 if (header
->header
.isextended
) {
368 while (get_next_record (archive
, tard
)->ext_hdr
.isextended
);
369 inode
->u
.tar
.data_offset
= current_tar_position
;
371 return STATUS_SUCCESS
;
376 * Main loop for reading an archive.
377 * Returns 0 on success, -1 on error.
379 static int open_archive (vfs
*me
, vfs_s_super
*archive
, char *name
, char *op
)
381 ReadStatus status
= STATUS_EOFMARK
; /* Initial status at start of archive */
382 ReadStatus prev_status
;
385 current_tar_position
= 0;
386 if ((tard
= tar_open_archive (me
, name
, archive
)) == -1) /* Open for reading */
390 prev_status
= status
;
391 status
= read_header (me
, archive
, tard
);
397 skip_n_records (archive
, tard
, (hstat
.st_size
+ RECORDSIZE
- 1) / RECORDSIZE
);
403 * If the previous header was good, tell them
404 * that we are skipping bad ones.
406 case STATUS_BADCHECKSUM
:
407 switch (prev_status
){
409 /* Error on first record */
411 message_2s (1, MSG_ERROR
, _("Hmm,...\n%s\ndoesn't look like a tar archive."), name
);
414 /* Error after header rec */
416 /* Error after error */
418 case STATUS_BADCHECKSUM
:
425 /* Record of zeroes */
427 status
= prev_status
; /* If error after 0's */
430 case STATUS_EOF
: /* End of archive */
438 void *tar_super_check(vfs
*me
, char *archive_name
, char *op
)
440 static struct stat stat_buf
;
441 if (mc_stat (archive_name
, &stat_buf
))
446 int tar_super_same(vfs
*me
, struct vfs_s_super
*parc
, char *archive_name
, char *op
, void *cookie
)
448 struct stat
*archive_stat
= cookie
; /* stat of main archive */
450 if (strcmp (parc
->name
, archive_name
)) return 0;
452 if (vfs_uid
&& (!(archive_stat
->st_mode
& 0004)))
453 if ((archive_stat
->st_gid
!= vfs_gid
) || !(archive_stat
->st_mode
& 0040))
454 if ((archive_stat
->st_uid
!= vfs_uid
) || !(archive_stat
->st_mode
& 0400))
456 /* Has the cached archive been changed on the disk? */
457 if (parc
->u
.tar
.tarstat
.st_mtime
< archive_stat
->st_mtime
) { /* Yes, reload! */
458 (*vfs_tarfs_ops
.free
) ((vfsid
) parc
);
459 vfs_rmstamp (&vfs_tarfs_ops
, (vfsid
) parc
, 0);
462 /* Hasn't been modified, give it a new timeout */
463 vfs_stamp (&vfs_tarfs_ops
, (vfsid
) parc
);
467 static int tar_read (void *fh
, char *buffer
, int count
)
469 off_t begin
= FH
->ino
->u
.tar
.data_offset
;
470 int fd
= FH_SUPER
->u
.tar
.fd
;
471 vfs
*me
= FH_SUPER
->me
;
473 if (mc_lseek (fd
, begin
+ FH
->pos
, SEEK_SET
) !=
474 begin
+ FH
->pos
) ERRNOR (EIO
, -1);
476 count
= MIN(count
, FH
->ino
->st
.st_size
- FH
->pos
);
478 if ((count
= mc_read (fd
, buffer
, count
)) == -1) ERRNOR (errno
, -1);
484 static void tar_ungetlocalcopy (vfs
*me
, char *path
, char *local
, int has_changed
)
486 /* We do just nothing. (We are read only and do not need to free local,
487 since it will be freed when tar archive will be freed */
490 static int tar_fh_open (vfs
*me
, vfs_s_fh
*fh
, int flags
, int mode
)
492 if ((flags
& O_ACCMODE
) != O_RDONLY
) ERRNOR (EROFS
, -1);
496 static struct vfs_s_data tarfs_data
= {
502 NULL
, /* init_inode */
503 NULL
, /* free_inode */
504 NULL
, /* init_entry */
511 tar_fh_open
, /* fh_open */
514 vfs_s_find_entry_tree
,
521 NULL
, /* This is place of next pointer */
522 "TApe aRchiver decompressor",