mc.sh & mc.csh creation fixed...
[midnight-commander.git] / vfs / tar.c
blob9ff270955420fcfb6fe800daff33e2194b69e11f
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
21 /* Namespace: vfs_tarfs_ops */
23 #include <config.h>
24 #include <errno.h>
26 #include "utilvfs.h"
27 #include "xdirentry.h"
29 #include "../src/dialog.h" /* For MSG_ERROR */
30 #include "tar.h"
31 #include "names.h"
33 #define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )
35 * Quick and dirty octal conversion.
37 * Result is -1 if the field is invalid (all blank, or nonoctal).
39 static long from_oct (int digs, char *where)
41 register long value;
43 while (isspace ((unsigned char) *where)) { /* Skip spaces */
44 where++;
45 if (--digs <= 0)
46 return -1; /* All blank field */
48 value = 0;
49 while (digs > 0 && isodigit (*where)) { /* Scan till nonoctal */
50 value = (value << 3) | (*where++ - '0');
51 --digs;
54 if (digs > 0 && *where && !isspace ((unsigned char) *where))
55 return -1; /* Ended on non-space/nul */
57 return value;
60 static struct stat hstat; /* Stat struct corresponding */
62 static void tar_free_archive (vfs *me, vfs_s_super *archive)
64 if (archive->u.tar.fd != -1)
65 mc_close(archive->u.tar.fd);
68 /* As we open one archive at a time, it is safe to have this static */
69 static int current_tar_position = 0;
71 /* Returns fd of the open tar file */
72 static int tar_open_archive (vfs *me, char *name, vfs_s_super *archive)
74 int result, type;
75 mode_t mode;
76 struct vfs_s_inode *root;
78 result = mc_open (name, O_RDONLY);
79 if (result == -1) {
80 message_2s (1, MSG_ERROR, _("Couldn't open tar archive\n%s"), name);
81 ERRNOR (ENOENT, -1);
84 archive->name = g_strdup (name);
85 mc_stat (name, &(archive->u.tar.tarstat));
86 archive->u.tar.fd = -1;
88 /* Find out the method to handle this tar file */
89 type = get_compression_type (result);
90 mc_lseek (result, 0, SEEK_SET);
91 if (type != COMPRESSION_NONE) {
92 char *s;
93 mc_close( result );
94 s = g_strconcat ( archive->name, decompress_extension (type), NULL );
95 result = mc_open (s, O_RDONLY);
96 if (result == -1)
97 message_2s (1, MSG_ERROR, _("Couldn't open tar archive\n%s"), s);
98 g_free(s);
99 if (result == -1)
100 ERRNOR (ENOENT, -1);
103 archive->u.tar.fd = result;
104 mode = archive->u.tar.tarstat.st_mode & 07777;
105 if (mode & 0400) mode |= 0100;
106 if (mode & 0040) mode |= 0010;
107 if (mode & 0004) mode |= 0001;
108 mode |= S_IFDIR;
110 root = vfs_s_new_inode (me, archive, &archive->u.tar.tarstat);
111 root->st.st_mode = mode;
112 root->u.tar.data_offset = -1;
113 root->st.st_nlink++;
114 root->st.st_dev = MEDATA->rdev++;
116 vfs_s_add_dots (me, root, NULL);
117 archive->root = root;
119 return result;
122 static union record rec_buf;
124 static union record *
125 get_next_record (vfs_s_super *archive, int tard)
127 int n;
129 n = mc_read (tard, rec_buf.charptr, RECORDSIZE);
130 if (n != RECORDSIZE)
131 return NULL; /* An error has occurred */
132 current_tar_position += RECORDSIZE;
133 return &rec_buf;
136 static void skip_n_records (vfs_s_super *archive, int tard, int n)
138 mc_lseek (tard, n * RECORDSIZE, SEEK_CUR);
139 current_tar_position += n * RECORDSIZE;
142 static void fill_stat_from_header (vfs *me, struct stat *st, union record *header)
144 st->st_mode = from_oct (8, header->header.mode);
146 /* Adjust st->st_mode because there are tar-files with
147 * linkflag==LF_SYMLINK and S_ISLNK(mod)==0. I don't
148 * know about the other modes but I think I cause no new
149 * problem when I adjust them, too. -- Norbert.
151 if (header->header.linkflag == LF_DIR) {
152 st->st_mode |= S_IFDIR;
153 } else if (header->header.linkflag == LF_SYMLINK) {
154 st->st_mode |= S_IFLNK;
155 } else if (header->header.linkflag == LF_CHR) {
156 st->st_mode |= S_IFCHR;
157 } else if (header->header.linkflag == LF_BLK) {
158 st->st_mode |= S_IFBLK;
159 } else if (header->header.linkflag == LF_FIFO) {
160 st->st_mode |= S_IFIFO;
161 } else
162 st->st_mode |= S_IFREG;
164 st->st_rdev = 0;
165 if (!strcmp (header->header.magic, TMAGIC)) {
166 st->st_uid = *header->header.uname ? finduid (header->header.uname) :
167 from_oct (8, header->header.uid);
168 st->st_gid = *header->header.gname ? findgid (header->header.gname) :
169 from_oct (8, header->header.gid);
170 switch (header->header.linkflag) {
171 case LF_BLK:
172 case LF_CHR:
173 st->st_rdev = (from_oct (8, header->header.devmajor) << 8) |
174 from_oct (8, header->header.devminor);
176 } else { /* Old Unix tar */
177 st->st_uid = from_oct (8, header->header.uid);
178 st->st_gid = from_oct (8, header->header.gid);
180 st->st_size = hstat.st_size;
181 st->st_mtime = from_oct (1 + 12, header->header.mtime);
182 st->st_atime = from_oct (1 + 12, header->header.atime);
183 st->st_ctime = from_oct (1 + 12, header->header.ctime);
187 typedef enum {
188 STATUS_BADCHECKSUM,
189 STATUS_SUCCESS,
190 STATUS_EOFMARK,
191 STATUS_EOF
192 } ReadStatus;
194 * Return 1 for success, 0 if the checksum is bad, EOF on eof,
195 * 2 for a record full of zeros (EOF marker).
198 static ReadStatus
199 read_header (vfs *me, vfs_s_super *archive, int tard)
201 register int i;
202 register long sum, signed_sum, recsum;
203 register char *p;
204 register union record *header;
205 static char *next_long_name = NULL, *next_long_link = NULL;
207 recurse:
209 header = get_next_record (archive, tard);
210 if (NULL == header)
211 return STATUS_EOF;
213 recsum = from_oct (8, header->header.chksum);
215 sum = 0; signed_sum = 0;
216 p = header->charptr;
217 for (i = sizeof (*header); --i >= 0;) {
219 * We can't use unsigned char here because of old compilers,
220 * e.g. V7.
222 signed_sum += *p;
223 sum += 0xFF & *p++;
226 /* Adjust checksum to count the "chksum" field as blanks. */
227 for (i = sizeof (header->header.chksum); --i >= 0;) {
228 sum -= 0xFF & header->header.chksum[i];
229 signed_sum -= (char) header->header.chksum[i];
231 sum += ' ' * sizeof header->header.chksum;
232 signed_sum += ' ' * sizeof header->header.chksum;
235 * This is a zeroed record...whole record is 0's except
236 * for the 8 blanks we faked for the checksum field.
238 if (sum == 8 * ' ')
239 return STATUS_EOFMARK;
241 if (sum != recsum && signed_sum != recsum)
242 return STATUS_BADCHECKSUM;
245 * linkflag on BSDI tar (pax) always '\000'
247 if (header->header.linkflag == '\000' &&
248 (i = strlen(header->header.arch_name)) &&
249 header->header.arch_name[i - 1] == '/')
250 header->header.linkflag = LF_DIR;
253 * Good record. Decode file size and return.
255 if (header->header.linkflag == LF_LINK || header->header.linkflag == LF_DIR)
256 hstat.st_size = 0; /* Links 0 size on tape */
257 else
258 hstat.st_size = from_oct (1 + 12, header->header.size);
260 header->header.arch_name[NAMSIZ - 1] = '\0';
261 if (header->header.linkflag == LF_LONGNAME
262 || header->header.linkflag == LF_LONGLINK) {
263 char **longp;
264 char *bp, *data;
265 int size, written;
267 longp = ((header->header.linkflag == LF_LONGNAME)
268 ? &next_long_name
269 : &next_long_link);
271 if (*longp)
272 g_free (*longp);
273 bp = *longp = g_malloc (hstat.st_size);
275 for (size = hstat.st_size;
276 size > 0;
277 size -= written) {
278 data = get_next_record (archive, tard)->charptr;
279 if (data == NULL) {
280 message_1s (1, MSG_ERROR, _("Unexpected EOF on archive file"));
281 return STATUS_BADCHECKSUM;
283 written = RECORDSIZE;
284 if (written > size)
285 written = size;
287 memcpy (bp, data, written);
288 bp += written;
290 #if 0
291 if (hstat.st_size > 1)
292 bp [hstat.st_size - 1] = 0; /* just to make sure */
293 #endif
294 goto recurse;
295 } else {
296 struct stat st;
297 struct vfs_s_entry *entry;
298 struct vfs_s_inode *inode, *parent;
299 long data_position;
300 char *q;
301 int len;
302 char *current_file_name, *current_link_name;
304 current_link_name = (next_long_link
305 ? next_long_link
306 : g_strdup (header->header.arch_linkname));
307 len = strlen (current_link_name);
308 if (len > 1 && current_link_name [len - 1] == '/')
309 current_link_name[len - 1] = 0;
311 current_file_name = (next_long_name
312 ? next_long_name
313 : g_strdup (header->header.arch_name));
314 len = strlen (current_file_name);
315 if (current_file_name[len - 1] == '/') {
316 current_file_name[len - 1] = 0;
319 data_position = current_tar_position;
321 p = strrchr (current_file_name, '/');
322 if (p == NULL) {
323 p = current_file_name;
324 q = current_file_name + len; /* "" */
325 } else {
326 *(p++) = 0;
327 q = current_file_name;
330 parent = vfs_s_find_inode (me, archive->root, q, LINK_NO_FOLLOW, FL_MKDIR);
331 if (parent == NULL) {
332 message_1s (1, MSG_ERROR, _("Inconsistent tar archive"));
333 return STATUS_BADCHECKSUM;
336 if (header->header.linkflag == LF_LINK) {
337 inode = vfs_s_find_inode (me, archive->root, current_link_name, LINK_NO_FOLLOW, 0);
338 if (inode == NULL) {
339 message_1s (1, MSG_ERROR, _("Inconsistent tar archive"));
340 } else {
341 entry = vfs_s_new_entry(me, p, inode);
342 vfs_s_insert_entry(me, parent, entry);
343 g_free (current_link_name);
344 goto done;
348 fill_stat_from_header (me, &st, header);
349 inode = vfs_s_new_inode (me, archive, &st);
351 inode->u.tar.data_offset = data_position;
352 if (*current_link_name) {
353 inode->linkname = current_link_name;
354 } else if (current_link_name != next_long_link) {
355 g_free (current_link_name);
357 entry = vfs_s_new_entry (me, p, inode);
359 vfs_s_insert_entry (me, parent, entry);
360 g_free (current_file_name);
362 done:
363 next_long_link = next_long_name = NULL;
365 if (header->header.isextended) {
366 while (get_next_record (archive, tard)->ext_hdr.isextended);
367 inode->u.tar.data_offset = current_tar_position;
369 return STATUS_SUCCESS;
374 * Main loop for reading an archive.
375 * Returns 0 on success, -1 on error.
377 static int open_archive (vfs *me, vfs_s_super *archive, char *name, char *op)
379 ReadStatus status = STATUS_EOFMARK; /* Initial status at start of archive */
380 ReadStatus prev_status;
381 int tard;
383 current_tar_position = 0;
384 if ((tard = tar_open_archive (me, name, archive)) == -1) /* Open for reading */
385 return -1;
387 for (;;) {
388 prev_status = status;
389 status = read_header (me, archive, tard);
392 switch (status) {
394 case STATUS_SUCCESS:
395 skip_n_records (archive, tard, (hstat.st_size + RECORDSIZE - 1) / RECORDSIZE);
396 continue;
399 * Invalid header:
401 * If the previous header was good, tell them
402 * that we are skipping bad ones.
404 case STATUS_BADCHECKSUM:
405 switch (prev_status){
407 /* Error on first record */
408 case STATUS_EOFMARK:
409 message_2s (1, MSG_ERROR, _("Hmm,...\n%s\ndoesn't look like a tar archive."), name);
410 /* FALL THRU */
412 /* Error after header rec */
413 case STATUS_SUCCESS:
414 /* Error after error */
416 case STATUS_BADCHECKSUM:
417 return -1;
419 case STATUS_EOF:
420 return 0;
423 /* Record of zeroes */
424 case STATUS_EOFMARK:
425 status = prev_status; /* If error after 0's */
426 /* FALL THRU */
428 case STATUS_EOF: /* End of archive */
429 break;
431 break;
433 return 0;
436 static void *tar_super_check(vfs *me, char *archive_name, char *op)
438 static struct stat stat_buf;
439 if (mc_stat (archive_name, &stat_buf))
440 return NULL;
441 return &stat_buf;
444 static int tar_super_same(vfs *me, struct vfs_s_super *parc, char *archive_name, char *op, void *cookie)
446 struct stat *archive_stat = cookie; /* stat of main archive */
448 if (strcmp (parc->name, archive_name)) return 0;
450 if (vfs_uid && (!(archive_stat->st_mode & 0004)))
451 if ((archive_stat->st_gid != vfs_gid) || !(archive_stat->st_mode & 0040))
452 if ((archive_stat->st_uid != vfs_uid) || !(archive_stat->st_mode & 0400))
453 return 0;
454 /* Has the cached archive been changed on the disk? */
455 if (parc->u.tar.tarstat.st_mtime < archive_stat->st_mtime) { /* Yes, reload! */
456 (*vfs_tarfs_ops.free) ((vfsid) parc);
457 vfs_rmstamp (&vfs_tarfs_ops, (vfsid) parc, 0);
458 return 2;
460 /* Hasn't been modified, give it a new timeout */
461 vfs_stamp (&vfs_tarfs_ops, (vfsid) parc);
462 return 1;
465 static int tar_read (void *fh, char *buffer, int count)
467 off_t begin = FH->ino->u.tar.data_offset;
468 int fd = FH_SUPER->u.tar.fd;
469 vfs *me = FH_SUPER->me;
471 if (mc_lseek (fd, begin + FH->pos, SEEK_SET) !=
472 begin + FH->pos) ERRNOR (EIO, -1);
474 count = MIN(count, FH->ino->st.st_size - FH->pos);
476 if ((count = mc_read (fd, buffer, count)) == -1) ERRNOR (errno, -1);
478 FH->pos += count;
479 return count;
482 static int tar_ungetlocalcopy (vfs *me, char *path, char *local, int has_changed)
484 /* We do just nothing. (We are read only and do not need to free local,
485 since it will be freed when tar archive will be freed */
486 /* We have to report error if file has changed */
487 ERRNOR (EROFS, -has_changed);
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);
493 return 0;
496 static struct vfs_s_data tarfs_data = {
497 NULL,
500 NULL, /* logfile */
502 NULL, /* init_inode */
503 NULL, /* free_inode */
504 NULL, /* init_entry */
506 tar_super_check,
507 tar_super_same,
508 open_archive,
509 tar_free_archive,
511 tar_fh_open, /* fh_open */
512 NULL, /* fh_close */
514 vfs_s_find_entry_tree,
515 NULL,
516 NULL
519 vfs vfs_tarfs_ops =
521 NULL, /* This is place of next pointer */
522 "tarfs",
523 0, /* flags */
524 "utar", /* prefix */
525 &tarfs_data,
526 0, /* errno */
527 NULL,
528 NULL,
529 vfs_s_fill_names,
530 NULL,
532 vfs_s_open,
533 vfs_s_close,
534 tar_read,
535 NULL,
537 vfs_s_opendir,
538 vfs_s_readdir,
539 vfs_s_closedir,
540 vfs_s_telldir,
541 vfs_s_seekdir,
543 vfs_s_stat,
544 vfs_s_lstat,
545 vfs_s_fstat,
547 NULL,
548 NULL,
549 NULL,
551 vfs_s_readlink,
552 NULL,
553 NULL,
554 NULL,
556 NULL,
557 vfs_s_chdir,
558 vfs_s_ferrno,
559 vfs_s_lseek,
560 NULL,
562 vfs_s_getid,
563 vfs_s_nothingisopen,
564 vfs_s_free,
566 vfs_s_getlocalcopy,
567 tar_ungetlocalcopy,
569 NULL, /* mkdir */
570 NULL,
571 NULL,
572 NULL
574 MMAPNULL