Traslated some chmod mgs related (for win32 support? not shure)
[midnight-commander.git] / vfs / tar.c
blob0a36737dfeb0f53e7b709e4dd88267665b7935dc
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 (*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 (*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 long size;
76 mode_t mode;
77 struct vfs_s_inode *root;
79 result = mc_open (name, O_RDONLY);
80 if (result == -1) {
81 message_2s (1, MSG_ERROR, _("Couldn't open tar archive\n%s"), name);
82 ERRNOR (ENOENT, -1);
85 archive->name = g_strdup (name);
86 mc_stat (name, &(archive->u.tar.tarstat));
87 archive->u.tar.fd = -1;
89 /* Find out the method to handle this tar file */
90 size = is_gunzipable (result, &type);
91 mc_lseek (result, 0, SEEK_SET);
92 if (size > 0) {
93 char *s;
94 mc_close( result );
95 s = g_strconcat ( archive->name, decompress_extension (type), NULL );
96 result = mc_open (s, O_RDONLY);
97 if (result == -1)
98 message_2s (1, MSG_ERROR, _("Couldn't open tar archive\n%s"), s);
99 g_free(s);
100 if (result == -1)
101 ERRNOR (ENOENT, -1);
104 archive->u.tar.fd = result;
105 mode = archive->u.tar.tarstat.st_mode & 07777;
106 if (mode & 0400) mode |= 0100;
107 if (mode & 0040) mode |= 0010;
108 if (mode & 0004) mode |= 0001;
109 mode |= S_IFDIR;
111 root = vfs_s_new_inode (me, archive, &archive->u.tar.tarstat);
112 root->st.st_mode = mode;
113 root->u.tar.data_offset = -1;
114 root->st.st_nlink++;
115 root->st.st_dev = MEDATA->rdev++;
117 vfs_s_add_dots (me, root, NULL);
118 archive->root = root;
120 return result;
123 static union record rec_buf;
125 static union record *
126 get_next_record (vfs_s_super *archive, int tard)
128 int n;
130 n = mc_read (tard, rec_buf.charptr, RECORDSIZE);
131 if (n != RECORDSIZE)
132 return NULL; /* An error has occurred */
133 current_tar_position += RECORDSIZE;
134 return &rec_buf;
137 static void skip_n_records (vfs_s_super *archive, int tard, int n)
139 mc_lseek (tard, n * RECORDSIZE, SEEK_CUR);
140 current_tar_position += n * RECORDSIZE;
143 static void fill_stat_from_header (vfs *me, struct stat *st, union record *header)
145 st->st_mode = from_oct (8, header->header.mode);
147 /* Adjust st->st_mode because there are tar-files with
148 * linkflag==LF_SYMLINK and S_ISLNK(mod)==0. I don't
149 * know about the other modes but I think I cause no new
150 * problem when I adjust them, too. -- Norbert.
152 if (header->header.linkflag == LF_DIR) {
153 st->st_mode |= S_IFDIR;
154 } else if (header->header.linkflag == LF_SYMLINK) {
155 st->st_mode |= S_IFLNK;
156 } else if (header->header.linkflag == LF_CHR) {
157 st->st_mode |= S_IFCHR;
158 } else if (header->header.linkflag == LF_BLK) {
159 st->st_mode |= S_IFBLK;
160 } else if (header->header.linkflag == LF_FIFO) {
161 st->st_mode |= S_IFIFO;
162 } else
163 st->st_mode |= S_IFREG;
165 st->st_rdev = 0;
166 if (!strcmp (header->header.magic, TMAGIC)) {
167 st->st_uid = *header->header.uname ? finduid (header->header.uname) :
168 from_oct (8, header->header.uid);
169 st->st_gid = *header->header.gname ? findgid (header->header.gname) :
170 from_oct (8, header->header.gid);
171 switch (header->header.linkflag) {
172 case LF_BLK:
173 case LF_CHR:
174 st->st_rdev = (from_oct (8, header->header.devmajor) << 8) |
175 from_oct (8, header->header.devminor);
177 } else { /* Old Unix tar */
178 st->st_uid = from_oct (8, header->header.uid);
179 st->st_gid = from_oct (8, header->header.gid);
181 st->st_size = hstat.st_size;
182 st->st_mtime = from_oct (1 + 12, header->header.mtime);
183 st->st_atime = from_oct (1 + 12, header->header.atime);
184 st->st_ctime = from_oct (1 + 12, header->header.ctime);
188 typedef enum {
189 STATUS_BADCHECKSUM,
190 STATUS_SUCCESS,
191 STATUS_EOFMARK,
192 STATUS_EOF
193 } ReadStatus;
195 * Return 1 for success, 0 if the checksum is bad, EOF on eof,
196 * 2 for a record full of zeros (EOF marker).
199 static ReadStatus
200 read_header (vfs *me, vfs_s_super *archive, int tard)
202 register int i;
203 register long sum, signed_sum, recsum;
204 register char *p;
205 register union record *header;
206 static char *next_long_name = NULL, *next_long_link = NULL;
208 recurse:
210 header = get_next_record (archive, tard);
211 if (NULL == header)
212 return STATUS_EOF;
214 recsum = from_oct (8, header->header.chksum);
216 sum = 0; signed_sum = 0;
217 p = header->charptr;
218 for (i = sizeof (*header); --i >= 0;) {
220 * We can't use unsigned char here because of old compilers,
221 * e.g. V7.
223 signed_sum += *p;
224 sum += 0xFF & *p++;
227 /* Adjust checksum to count the "chksum" field as blanks. */
228 for (i = sizeof (header->header.chksum); --i >= 0;) {
229 sum -= 0xFF & header->header.chksum[i];
230 signed_sum -= (char) header->header.chksum[i];
232 sum += ' ' * sizeof header->header.chksum;
233 signed_sum += ' ' * sizeof header->header.chksum;
236 * This is a zeroed record...whole record is 0's except
237 * for the 8 blanks we faked for the checksum field.
239 if (sum == 8 * ' ')
240 return STATUS_EOFMARK;
242 if (sum != recsum && signed_sum != recsum)
243 return STATUS_BADCHECKSUM;
246 * linkflag on BSDI tar (pax) always '\000'
248 if (header->header.linkflag == '\000' &&
249 (i = strlen(header->header.arch_name)) &&
250 header->header.arch_name[i - 1] == '/')
251 header->header.linkflag = LF_DIR;
254 * Good record. Decode file size and return.
256 if (header->header.linkflag == LF_LINK || header->header.linkflag == LF_DIR)
257 hstat.st_size = 0; /* Links 0 size on tape */
258 else
259 hstat.st_size = from_oct (1 + 12, header->header.size);
261 header->header.arch_name[NAMSIZ - 1] = '\0';
262 if (header->header.linkflag == LF_LONGNAME
263 || header->header.linkflag == LF_LONGLINK) {
264 char **longp;
265 char *bp, *data;
266 int size, written;
268 longp = ((header->header.linkflag == LF_LONGNAME)
269 ? &next_long_name
270 : &next_long_link);
272 if (*longp)
273 g_free (*longp);
274 bp = *longp = g_malloc (hstat.st_size);
276 for (size = hstat.st_size;
277 size > 0;
278 size -= written) {
279 data = get_next_record (archive, tard)->charptr;
280 if (data == NULL) {
281 message_1s (1, MSG_ERROR, _("Unexpected EOF on archive file"));
282 return STATUS_BADCHECKSUM;
284 written = RECORDSIZE;
285 if (written > size)
286 written = size;
288 bcopy (data, bp, written);
289 bp += written;
291 #if 0
292 if (hstat.st_size > 1)
293 bp [hstat.st_size - 1] = 0; /* just to make sure */
294 #endif
295 goto recurse;
296 } else {
297 struct stat st;
298 struct vfs_s_entry *entry;
299 struct vfs_s_inode *inode, *parent;
300 long data_position;
301 char *q;
302 int len;
303 char *current_file_name, *current_link_name;
305 current_link_name = (next_long_link
306 ? next_long_link
307 : g_strdup (header->header.arch_linkname));
308 len = strlen (current_link_name);
309 if (len > 1 && current_link_name [len - 1] == '/')
310 current_link_name[len - 1] = 0;
312 current_file_name = (next_long_name
313 ? next_long_name
314 : g_strdup (header->header.arch_name));
315 len = strlen (current_file_name);
316 if (current_file_name[len - 1] == '/') {
317 current_file_name[len - 1] = 0;
320 data_position = current_tar_position;
322 p = strrchr (current_file_name, '/');
323 if (p == NULL) {
324 p = current_file_name;
325 q = current_file_name + len; /* "" */
326 } else {
327 *(p++) = 0;
328 q = current_file_name;
331 parent = vfs_s_find_inode (me, archive->root, q, LINK_NO_FOLLOW, FL_MKDIR);
332 if (parent == NULL) {
333 message_1s (1, MSG_ERROR, _("Inconsistent tar archive"));
334 return STATUS_BADCHECKSUM;
337 if (header->header.linkflag == LF_LINK) {
338 inode = vfs_s_find_inode (me, archive->root, current_link_name, LINK_NO_FOLLOW, 0);
339 if (inode == NULL) {
340 message_1s (1, MSG_ERROR, _("Inconsistent tar archive"));
341 } else {
342 entry = vfs_s_new_entry(me, p, inode);
343 vfs_s_insert_entry(me, parent, entry);
344 g_free (current_link_name);
345 goto done;
349 fill_stat_from_header (me, &st, header);
350 inode = vfs_s_new_inode (me, archive, &st);
352 inode->u.tar.data_offset = data_position;
353 if (*current_link_name) {
354 inode->linkname = current_link_name;
355 } else if (current_link_name != next_long_link) {
356 g_free (current_link_name);
358 entry = vfs_s_new_entry (me, p, inode);
360 vfs_s_insert_entry (me, parent, entry);
361 g_free (current_file_name);
363 done:
364 next_long_link = next_long_name = NULL;
366 if (header->header.isextended) {
367 while (get_next_record (archive, tard)->ext_hdr.isextended);
368 inode->u.tar.data_offset = current_tar_position;
370 return STATUS_SUCCESS;
375 * Main loop for reading an archive.
376 * Returns 0 on success, -1 on error.
378 static int open_archive (vfs *me, vfs_s_super *archive, char *name, char *op)
380 ReadStatus status = STATUS_EOFMARK; /* Initial status at start of archive */
381 ReadStatus prev_status;
382 int tard;
384 current_tar_position = 0;
385 if ((tard = tar_open_archive (me, name, archive)) == -1) /* Open for reading */
386 return -1;
388 for (;;) {
389 prev_status = status;
390 status = read_header (me, archive, tard);
393 switch (status) {
395 case STATUS_SUCCESS:
396 skip_n_records (archive, tard, (hstat.st_size + RECORDSIZE - 1) / RECORDSIZE);
397 continue;
400 * Invalid header:
402 * If the previous header was good, tell them
403 * that we are skipping bad ones.
405 case STATUS_BADCHECKSUM:
406 switch (prev_status){
408 /* Error on first record */
409 case STATUS_EOFMARK:
410 message_2s (1, MSG_ERROR, _("Hmm,...\n%s\ndoesn't look like a tar archive."), name);
411 /* FALL THRU */
413 /* Error after header rec */
414 case STATUS_SUCCESS:
415 /* Error after error */
417 case STATUS_BADCHECKSUM:
418 return -1;
420 case STATUS_EOF:
421 return 0;
424 /* Record of zeroes */
425 case STATUS_EOFMARK:
426 status = prev_status; /* If error after 0's */
427 /* FALL THRU */
429 case STATUS_EOF: /* End of archive */
430 break;
432 break;
434 return 0;
437 static void *tar_super_check(vfs *me, char *archive_name, char *op)
439 static struct stat stat_buf;
440 if (mc_stat (archive_name, &stat_buf))
441 return NULL;
442 return &stat_buf;
445 static int tar_super_same(vfs *me, struct vfs_s_super *parc, char *archive_name, char *op, void *cookie)
447 struct stat *archive_stat = cookie; /* stat of main archive */
449 if (strcmp (parc->name, archive_name)) return 0;
451 if (vfs_uid && (!(archive_stat->st_mode & 0004)))
452 if ((archive_stat->st_gid != vfs_gid) || !(archive_stat->st_mode & 0040))
453 if ((archive_stat->st_uid != vfs_uid) || !(archive_stat->st_mode & 0400))
454 return 0;
455 /* Has the cached archive been changed on the disk? */
456 if (parc->u.tar.tarstat.st_mtime < archive_stat->st_mtime) { /* Yes, reload! */
457 (*vfs_tarfs_ops.free) ((vfsid) parc);
458 vfs_rmstamp (&vfs_tarfs_ops, (vfsid) parc, 0);
459 return 2;
461 /* Hasn't been modified, give it a new timeout */
462 vfs_stamp (&vfs_tarfs_ops, (vfsid) parc);
463 return 1;
466 static int tar_read (void *fh, char *buffer, int count)
468 off_t begin = FH->ino->u.tar.data_offset;
469 int fd = FH_SUPER->u.tar.fd;
470 vfs *me = FH_SUPER->me;
472 if (mc_lseek (fd, begin + FH->pos, SEEK_SET) !=
473 begin + FH->pos) ERRNOR (EIO, -1);
475 count = MIN(count, FH->ino->st.st_size - FH->pos);
477 if ((count = mc_read (fd, buffer, count)) == -1) ERRNOR (errno, -1);
479 FH->pos += count;
480 return count;
483 static int tar_ungetlocalcopy (vfs *me, char *path, char *local, int has_changed)
485 /* We do just nothing. (We are read only and do not need to free local,
486 since it will be freed when tar archive will be freed */
487 /* We have to report error if file has changed */
488 ERRNOR (EROFS, -has_changed);
491 static int tar_fh_open (vfs *me, vfs_s_fh *fh, int flags, int mode)
493 if ((flags & O_ACCMODE) != O_RDONLY) ERRNOR (EROFS, -1);
494 return 0;
497 static struct vfs_s_data tarfs_data = {
498 NULL,
501 NULL, /* logfile */
503 NULL, /* init_inode */
504 NULL, /* free_inode */
505 NULL, /* init_entry */
507 tar_super_check,
508 tar_super_same,
509 open_archive,
510 tar_free_archive,
512 tar_fh_open, /* fh_open */
513 NULL, /* fh_close */
515 vfs_s_find_entry_tree,
516 NULL,
517 NULL
520 vfs vfs_tarfs_ops =
522 NULL, /* This is place of next pointer */
523 "tarfs",
524 0, /* flags */
525 "utar", /* prefix */
526 &tarfs_data,
527 0, /* errno */
528 NULL,
529 NULL,
530 vfs_s_fill_names,
531 NULL,
533 vfs_s_open,
534 vfs_s_close,
535 tar_read,
536 NULL,
538 vfs_s_opendir,
539 vfs_s_readdir,
540 vfs_s_closedir,
541 vfs_s_telldir,
542 vfs_s_seekdir,
544 vfs_s_stat,
545 vfs_s_lstat,
546 vfs_s_fstat,
548 NULL,
549 NULL,
550 NULL,
552 vfs_s_readlink,
553 NULL,
554 NULL,
555 NULL,
557 NULL,
558 vfs_s_chdir,
559 vfs_s_ferrno,
560 vfs_s_lseek,
561 NULL,
563 vfs_s_getid,
564 vfs_s_nothingisopen,
565 vfs_s_free,
567 vfs_s_getlocalcopy,
568 tar_ungetlocalcopy,
570 NULL, /* mkdir */
571 NULL,
572 NULL,
573 NULL
575 MMAPNULL