summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorManuel Menal <mmenal@hurdfr.org>2010-09-20 20:48:05 +0000
committerSamuel Thibault <samuel.thibault@ens-lyon.org>2010-09-20 20:54:15 +0000
commit7fc534e167e8ebf3e7267fca5db7e34c9bc15538 (patch)
tree5fb72845589f1cbedfce403f0009aa97ff6c30fe
parent34da65652018854e2de8351b6040f1f9c242a7d4 (diff)
Make tarfs actually work again
* tar.c: Import newer mc snapshot. (strconcat): New function. (tar_header_hook): Replace `off_t' parameter of function type with `struct archive *'. (tar_get_next_record): Initialize `buf' to NULL and n to 0 before calling `store_read'. * tar.c: Import newer mc snapshot. (struct archive): New structure. * tarfs.c (tar_header_hook): Replace `off_t' parameter of function type with `struct archive *'. (tarfs_add_header): Likewise. Pass `archive->current_tar_position' instead of `offset' to `tar_make_item'. Call `tar_fill_stat' instead of `tar_header2stat'.
-rw-r--r--tar.c663
-rw-r--r--tar.h65
-rw-r--r--tarfs.c10
3 files changed, 420 insertions, 318 deletions
diff --git a/tar.c b/tar.c
index 40cbfb908..e7391f978 100644
--- a/tar.c
+++ b/tar.c
@@ -1,5 +1,6 @@
/* GNU tar files parsing.
- Copyright (C) 1995 The Free Software Foundation
+ Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ 2006, 2007 Free Software Foundation, Inc.
Written by: 1995 Jakub Jelinek
Rewritten by: 1998 Pavel Machek
@@ -39,7 +40,7 @@
#include "debug.h"
/* A hook which is called each time a header has been parsed. */
-int (*tar_header_hook) (tar_record_t *, off_t) = NULL;
+int (*tar_header_hook) (tar_record_t *, struct archive *) = NULL;
#define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )
@@ -48,141 +49,102 @@ int (*tar_header_hook) (tar_record_t *, off_t) = NULL;
# define isspace(c) ( (c) == ' ' )
#endif
+/* Taken from glibc. */
+char *
+strconcat (const char *string1, ...)
+{
+ size_t l;
+ va_list args;
+ char *s;
+ char *concat;
+ char *ptr;
+
+ if (!string1)
+ return NULL;
+
+ l = 1 + strlen (string1);
+ va_start (args, string1);
+ s = va_arg (args, char *);
+ while (s)
+ {
+ l += strlen (s);
+ s = va_arg (args, char *);
+ }
+ va_end (args);
+
+ concat = malloc (l);
+ ptr = concat;
+
+ ptr = stpcpy (ptr, string1);
+ va_start (args, string1);
+ s = va_arg (args, char *);
+ while (s)
+ {
+ ptr = stpcpy (ptr, s);
+ s = va_arg (args, char *);
+ }
+ va_end (args);
+
+ return concat;
+}
+
/*
* Quick and dirty octal conversion.
*
* Result is -1 if the field is invalid (all blank, or nonoctal).
*/
-static long
-from_oct (int digs, char *where)
+static long tar_from_oct (int digs, char *where)
{
- register long value;
+ register long value;
- while (isspace (*where))
- { /* Skip spaces */
- where++;
- if (--digs <= 0)
- return -1; /* All blank field */
+ while (isspace ((unsigned char) *where)) { /* Skip spaces */
+ where++;
+ if (--digs <= 0)
+ return -1; /* All blank field */
}
- value = 0;
- while (digs > 0 && isodigit (*where))
- { /* Scan till nonoctal */
- value = (value << 3) | (*where++ - '0');
- --digs;
+ value = 0;
+ while (digs > 0 && isodigit (*where)) { /* Scan till nonoctal */
+ value = (value << 3) | (*where++ - '0');
+ --digs;
}
- if (digs > 0 && *where && !isspace (*where))
- return -1; /* Ended on non-space/nul */
+ if (digs > 0 && *where && !isspace ((unsigned char) *where))
+ return -1; /* Ended on non-space/nul */
- return value;
+ return value;
}
-/* As we open one archive at a time, it is safe to have this static */
-static store_offset_t current_tar_position = 0;
-static tar_record_t rec_buf;
-
-
-static tar_record_t *
-get_next_record (struct store *tar_file)
+static union record *
+tar_get_next_record (struct archive *archive)
{
- error_t err;
- size_t n;
- void *buf;
-
- debug (("Reading at offset %lli", current_tar_position));
- buf = rec_buf.charptr;
- err = store_read (tar_file, current_tar_position, RECORDSIZE, &buf, &n);
-
- if (err)
- error (1, err, "Read error (offset=%lli)", current_tar_position);
- assert (n <= RECORDSIZE);
-
- if (buf != rec_buf.charptr)
- {
- memcpy (rec_buf.charptr, buf, n);
- munmap (buf, n);
- }
+ error_t err;
+ void *buf = NULL;
+ size_t n = 0;
- if (n != RECORDSIZE)
- return NULL; /* An error has occurred */
+ err = store_read (archive->tar_file, archive->current_tar_position, RECORDSIZE, &buf, &n);
+ if (err)
+ error (1, err, "Read error (offset=%lli)", archive->current_tar_position);
+ if (n != RECORDSIZE)
+ return NULL; /* An error has occurred */
- current_tar_position += n;
+ if (buf != archive->rec_buf.charptr)
+ {
+ memcpy (archive->rec_buf.charptr, buf, n);
+ munmap (buf, n);
+ }
- return &rec_buf;
-}
+ archive->current_tar_position += RECORDSIZE;
-static void
-skip_n_records (struct store *tar_file, int n)
-{
- current_tar_position += n * RECORDSIZE;
+ return &(archive->rec_buf);
}
-void
-tar_header2stat (io_statbuf_t *st, tar_record_t *header)
+static void tar_skip_n_records (struct archive *archive, int n)
{
- st->st_mode = from_oct (8, header->header.mode);
-
- /* Adjust st->st_mode because there are tar-files with
- * linkflag==LF_SYMLINK and S_ISLNK(mod)==0. I don't
- * know about the other modes but I think I cause no new
- * problem when I adjust them, too. -- Norbert.
- */
- if (header->header.linkflag == LF_DIR)
- {
- st->st_mode |= S_IFDIR;
- }
- else if (header->header.linkflag == LF_SYMLINK)
- {
- st->st_mode |= S_IFLNK;
- }
- else if (header->header.linkflag == LF_CHR)
- {
- st->st_mode |= S_IFCHR;
- }
- else if (header->header.linkflag == LF_BLK)
- {
- st->st_mode |= S_IFBLK;
- }
- else if (header->header.linkflag == LF_FIFO)
- {
- st->st_mode |= S_IFIFO;
- }
- else
- st->st_mode |= S_IFREG;
-
- st->st_rdev = 0;
- if (!strcmp (header->header.magic, TMAGIC))
- {
- st->st_uid = *header->header.uname ? finduid (header->header.uname) :
- from_oct (8, header->header.uid);
- st->st_gid = *header->header.gname ? findgid (header->header.gname) :
- from_oct (8, header->header.gid);
- switch (header->header.linkflag)
- {
- case LF_BLK:
- case LF_CHR:
- st->st_rdev = (from_oct (8, header->header.devmajor) << 8) |
- from_oct (8, header->header.devminor);
- }
- }
- else
- { /* Old Unix tar */
- st->st_uid = from_oct (8, header->header.uid);
- st->st_gid = from_oct (8, header->header.gid);
- }
- //st->st_size = hstat.st_size;
- st->st_size = from_oct (1 + 12, header->header.size);
- if (st->st_size > 0)
- st->st_blocks = ((st->st_size - 1) / 512) + 1;
- else
- st->st_blocks = 0;
- st->st_mtime = from_oct (1 + 12, header->header.mtime);
- st->st_atime = from_oct (1 + 12, header->header.atime);
- st->st_ctime = from_oct (1 + 12, header->header.ctime);
+ (void) archive;
+ archive->current_tar_position += n * RECORDSIZE;
}
-
typedef enum
{
STATUS_BADCHECKSUM,
@@ -197,153 +159,282 @@ ReadStatus;
*
*/
static ReadStatus
-read_header (struct store *tar_file)
+tar_read_header (struct archive *archive, size_t *h_size)
{
- register int i;
- register long sum, signed_sum, recsum;
- register char *p;
- register tar_record_t *header;
- char *data;
- int size, written;
- static char *next_lonname = NULL, *next_lonlink = NULL;
- char *current_file_name, *current_link_name;
- struct stat hstat; /* Stat struct corresponding */
- char arch_name[NAMSIZ + 1];
- char arch_linkname[NAMSIZ + 1];
-
- memcpy (arch_name, header->header.arch_name, NAMSIZ);
- arch_name [NAMSIZ] = '\0';
- memcpy (arch_linkname, header->header.arch_linkname, NAMSIZ);
- arch_linkname [NAMSIZ] = '\0';
-
-
-recurse:
-
- header = get_next_record (tar_file);
- if (NULL == header)
- return STATUS_EOF;
-
- recsum = from_oct (8, header->header.chksum);
-
- sum = 0;
- signed_sum = 0;
- p = header->charptr;
- for (i = sizeof (*header); --i >= 0;)
- {
- /*
- * We can't use unsigned char here because of old compilers,
- * e.g. V7.
- */
- signed_sum += *p;
- sum += 0xFF & *p++;
+ register int i;
+ register long sum, signed_sum, recsum;
+ register char *p;
+ register union record *header;
+ static char *next_long_name = NULL, *next_long_link = NULL;
+
+ recurse:
+
+ header = tar_get_next_record (archive);
+ if (NULL == header)
+ return STATUS_EOF;
+
+ recsum = tar_from_oct (8, header->header.chksum);
+
+ sum = 0;
+ signed_sum = 0;
+ p = header->charptr;
+ for (i = sizeof (*header); --i >= 0;) {
+ /*
+ * We can't use unsigned char here because of old compilers,
+ * e.g. V7.
+ */
+ signed_sum += *p;
+ sum += 0xFF & *p++;
}
- /* Adjust checksum to count the "chksum" field as blanks. */
- for (i = sizeof (header->header.chksum); --i >= 0;)
- {
- sum -= 0xFF & header->header.chksum[i];
- signed_sum -= (char) header->header.chksum[i];
+ /* Adjust checksum to count the "chksum" field as blanks. */
+ for (i = sizeof (header->header.chksum); --i >= 0;) {
+ sum -= 0xFF & header->header.chksum[i];
+ signed_sum -= (char) header->header.chksum[i];
+ }
+ sum += ' ' * sizeof header->header.chksum;
+ signed_sum += ' ' * sizeof header->header.chksum;
+
+ /*
+ * This is a zeroed record...whole record is 0's except
+ * for the 8 blanks we faked for the checksum field.
+ */
+ if (sum == 8 * ' ')
+ return STATUS_EOFMARK;
+
+ if (sum != recsum && signed_sum != recsum)
+ return STATUS_BADCHECKSUM;
+
+ /*
+ * Try to determine the archive format.
+ */
+ if (archive->type == TAR_UNKNOWN) {
+ if (!strcmp (header->header.magic, TMAGIC)) {
+ if (header->header.linkflag == LF_GLOBAL_EXTHDR)
+ archive->type = TAR_POSIX;
+ else
+ archive->type = TAR_USTAR;
+ } else if (!strcmp (header->header.magic, OLDGNU_MAGIC)) {
+ archive->type = TAR_GNU;
+ }
}
- sum += ' ' * sizeof header->header.chksum;
- signed_sum += ' ' * sizeof header->header.chksum;
-
- /*
- * This is a zeroed record...whole record is 0's except
- * for the 8 blanks we faked for the checksum field.
- */
- if (sum == 8 * ' ')
- return STATUS_EOFMARK;
-
- if (sum != recsum && signed_sum != recsum)
- return STATUS_BADCHECKSUM;
-
- /*
- * linkflag on BSDI tar (pax) always '\000'
- */
-
- if (header->header.linkflag == '\000' &&
- strlen (arch_name) &&
- arch_name[strlen (arch_name) - 1] == '/')
- header->header.linkflag = LF_DIR;
- /*
- * Good record. Decode file size and return.
- */
- if (header->header.linkflag == LF_LINK || header->header.linkflag == LF_DIR)
- hstat.st_size = 0; /* Links 0 size on tape */
- else
- hstat.st_size = from_oct (1 + 12, header->header.size);
+ /*
+ * linkflag on BSDI tar (pax) always '\000'
+ */
+ if (header->header.linkflag == '\000') {
+ if (header->header.arch_name[NAMSIZ - 1] != '\0')
+ i = NAMSIZ;
+ else
+ i = strlen (header->header.arch_name);
+
+ if (i && header->header.arch_name[i - 1] == '/')
+ header->header.linkflag = LF_DIR;
+ }
- if (header->header.linkflag == LF_LONGNAME
- || header->header.linkflag == LF_LONGLINK)
- {
- for (size = hstat.st_size; size > 0; size -= written)
- {
- data = get_next_record (tar_file)->charptr;
- if (data == NULL)
- {
- error (0, 0, "Unexpected EOF on archive file");
- return STATUS_BADCHECKSUM;
- }
- written = RECORDSIZE;
- if (written > size)
- written = size;
- }
- goto recurse;
+ /*
+ * Good record. Decode file size and return.
+ */
+ if (header->header.linkflag == LF_LINK
+ || header->header.linkflag == LF_DIR)
+ *h_size = 0; /* Links 0 size on tape */
+ else
+ *h_size = tar_from_oct (1 + 12, header->header.size);
+
+ /*
+ * Skip over directory snapshot info records that
+ * are stored in incremental tar archives.
+ */
+ if (header->header.linkflag == LF_DUMPDIR) {
+ if (archive->type == TAR_UNKNOWN)
+ archive->type = TAR_GNU;
+ return STATUS_SUCCESS;
+ }
+
+ /*
+ * Skip over pax extended header and global extended
+ * header records.
+ */
+ if (header->header.linkflag == LF_EXTHDR ||
+ header->header.linkflag == LF_GLOBAL_EXTHDR) {
+ if (archive->type == TAR_UNKNOWN)
+ archive->type = TAR_POSIX;
+ return STATUS_SUCCESS;
}
- else
- {
- long data_position;
- char *p, *q;
- int len;
- int isdir = 0;
-
- current_file_name = (next_lonname
- ? next_lonname
- : strdup (arch_name));
- len = strlen (current_file_name);
- if (current_file_name[len - 1] == '/')
- {
- current_file_name[len - 1] = 0;
- isdir = 1;
- }
+ if (header->header.linkflag == LF_LONGNAME
+ || header->header.linkflag == LF_LONGLINK) {
+ char **longp;
+ char *bp, *data;
+ int size, written;
- current_link_name = (next_lonlink
- ? next_lonlink
- : strdup (arch_linkname));
- len = strlen (current_link_name);
- if (len && current_link_name[len - 1] == '/')
- current_link_name[len - 1] = 0;
+ if (archive->type == TAR_UNKNOWN)
+ archive->type = TAR_GNU;
- next_lonlink = next_lonname = NULL;
+ longp = ((header->header.linkflag == LF_LONGNAME)
+ ? &next_long_name : &next_long_link);
- data_position = current_tar_position;
+ free (*longp);
+ bp = *longp = malloc (*h_size + 1);
+
+ for (size = *h_size; size > 0; size -= written) {
+ data = tar_get_next_record (archive)->charptr;
+ if (data == NULL) {
+ free (*longp);
+ *longp = NULL;
+ error (0, 0, "Unexpected EOF on archive file");
+ return STATUS_BADCHECKSUM;
+ }
+ written = RECORDSIZE;
+ if (written > size)
+ written = size;
- p = strrchr (current_file_name, '/');
- if (p == NULL)
- {
- p = current_file_name;
- q = current_file_name + strlen (current_file_name); /* "" */
+ memcpy (bp, data, written);
+ bp += written;
}
- else
- {
- *(p++) = 0;
- q = current_file_name;
+
+ *bp = 0;
+ goto recurse;
+ } else {
+ long data_position;
+ char *q;
+ int len;
+ char *current_file_name, *current_link_name;
+
+ current_link_name =
+ (next_long_link ? next_long_link :
+ strndup (header->header.arch_linkname, NAMSIZ));
+ len = strlen (current_link_name);
+ if (len > 1 && current_link_name[len - 1] == '/')
+ current_link_name[len - 1] = 0;
+
+ current_file_name = NULL;
+ switch (archive->type) {
+ case TAR_USTAR:
+ case TAR_POSIX:
+ /* The ustar archive format supports pathnames of upto 256
+ * characters in length. This is achieved by concatenating
+ * the contents of the `prefix' and `arch_name' fields like
+ * this:
+ *
+ * prefix + path_separator + arch_name
+ *
+ * If the `prefix' field contains an empty string i.e. its
+ * first characters is '\0' the prefix field is ignored.
+ */
+ if (header->header.unused.prefix[0] != '\0') {
+ char *temp_name, *temp_prefix;
+
+ temp_name = strndup (header->header.arch_name, NAMSIZ);
+ temp_prefix = strndup (header->header.unused.prefix,
+ PREFIX_SIZE);
+ current_file_name = strconcat (temp_prefix, "/",
+ temp_name, (char *) NULL);
+ free (temp_name);
+ free (temp_prefix);
+ }
+ break;
+ case TAR_GNU:
+ if (next_long_name != NULL)
+ current_file_name = next_long_name;
+ break;
+ default:
+ break;
+ }
+
+ if (current_file_name == NULL)
+ current_file_name = strndup (header->header.arch_name, NAMSIZ);
+
+ len = strlen (current_file_name);
+
+ data_position = archive->current_tar_position;
+
+ p = strrchr (current_file_name, '/');
+ if (p == NULL) {
+ p = current_file_name;
+ q = current_file_name + len; /* "" */
+ } else {
+ *(p++) = 0;
+ q = current_file_name;
}
- if (tar_header_hook)
- tar_header_hook (header, current_tar_position);
+ if (tar_header_hook)
+ tar_header_hook (header, archive);
+
+ free (current_file_name);
- free (current_file_name);
+ /* done:*/
+ next_long_link = next_long_name = NULL;
-/* done: */
- if (header->header.isextended)
- {
- while (get_next_record (tar_file)->ext_hdr.isextended);
+ if (archive->type == TAR_GNU &&
+ header->header.unused.oldgnu.isextended) {
+ while (tar_get_next_record (archive)->ext_hdr.
+ isextended);
}
- skip_n_records (tar_file, (hstat.st_size + RECORDSIZE - 1) / RECORDSIZE);
- return STATUS_SUCCESS;
+ return STATUS_SUCCESS;
+ }
+}
+
+void
+tar_fill_stat (struct archive *archive, struct stat *st, tar_record_t *header)
+{
+ st->st_mode = tar_from_oct (8, header->header.mode);
+
+ /* Adjust st->st_mode because there are tar-files with
+ * linkflag==LF_SYMLINK and S_ISLNK(mod)==0. I don't
+ * know about the other modes but I think I cause no new
+ * problem when I adjust them, too. -- Norbert.
+ */
+ if (header->header.linkflag == LF_DIR) {
+ st->st_mode |= S_IFDIR;
+ } else if (header->header.linkflag == LF_SYMLINK) {
+ st->st_mode |= S_IFLNK;
+ } else if (header->header.linkflag == LF_CHR) {
+ st->st_mode |= S_IFCHR;
+ } else if (header->header.linkflag == LF_BLK) {
+ st->st_mode |= S_IFBLK;
+ } else if (header->header.linkflag == LF_FIFO) {
+ st->st_mode |= S_IFIFO;
+ } else
+ st->st_mode |= S_IFREG;
+
+ st->st_rdev = 0;
+ switch (archive->type) {
+ case TAR_USTAR:
+ case TAR_POSIX:
+ case TAR_GNU:
+ st->st_uid =
+ *header->header.uname ? finduid (header->header.
+ uname) : tar_from_oct (8,
+ header->
+ header.
+ uid);
+ st->st_gid =
+ *header->header.gname ? findgid (header->header.
+ gname) : tar_from_oct (8,
+ header->
+ header.
+ gid);
+ switch (header->header.linkflag) {
+ case LF_BLK:
+ case LF_CHR:
+ st->st_rdev =
+ (tar_from_oct (8, header->header.devmajor) << 8) |
+ tar_from_oct (8, header->header.devminor);
+ }
+ default:
+ st->st_uid = tar_from_oct (8, header->header.uid);
+ st->st_gid = tar_from_oct (8, header->header.gid);
+ }
+ st->st_size = tar_from_oct (1 + 12, header->header.size);
+ st->st_mtime = tar_from_oct (1 + 12, header->header.mtime);
+ st->st_atime = 0;
+ st->st_ctime = 0;
+ if (archive->type == TAR_GNU) {
+ st->st_atime = tar_from_oct (1 + 12,
+ header->header.unused.oldgnu.atime);
+ st->st_ctime = tar_from_oct (1 + 12,
+ header->header.unused.oldgnu.ctime);
}
}
@@ -354,77 +445,65 @@ recurse:
int
tar_open_archive (struct store *tar_file)
{
- ReadStatus status = STATUS_EOFMARK; /* Initial status at start of archive */
- ReadStatus prev_status = STATUS_SUCCESS;
+ /* Initial status at start of archive */
+ ReadStatus status = STATUS_EOFMARK;
+ ReadStatus prev_status;
- current_tar_position = 0;
+ struct archive archive;
+ archive.tar_file = tar_file;
+ archive.current_tar_position = 0;
+ archive.type = TAR_UNKNOWN;
- for (;;)
- {
- prev_status = status;
- status = read_header (tar_file);
+ for (;;) {
+ size_t h_size;
+ prev_status = status;
+ status = tar_read_header (&archive, &h_size);
- switch (status)
- {
+ switch (status) {
case STATUS_SUCCESS:
- continue;
-
- /*
- * Invalid header:
- *
- * If the previous header was good, tell them
- * that we are skipping bad ones.
- */
+ tar_skip_n_records (&archive,
+ (h_size + RECORDSIZE -
+ 1) / RECORDSIZE);
+ continue;
+
+ /*
+ * Invalid header:
+ *
+ * If the previous header was good, tell them
+ * that we are skipping bad ones.
+ */
case STATUS_BADCHECKSUM:
- switch (prev_status)
- {
+ switch (prev_status) {
- /* Error on first record */
+ /* Error on first record */
case STATUS_EOFMARK:
- return -1;
- /* FALL THRU */
+ /* FALL THRU */
- /* Error after header rec */
+ /* Error after header rec */
case STATUS_SUCCESS:
- prev_status = status;
- {
- /* FIXME: Bad hack */
- size_t size;
- tar_record_t *hdr;
- current_tar_position -= RECORDSIZE;
- error (0, 0, "Skipping to next header (offset=%lli)",
- current_tar_position);
- hdr = get_next_record (tar_file);
- size = from_oct (8, hdr->header.size);
- size = size % RECORDSIZE
- ? size / RECORDSIZE
- : (size / RECORDSIZE) + RECORDSIZE;
- current_tar_position += size;
- }
-
- /* Error after error */
+ /* Error after error */
case STATUS_BADCHECKSUM:
- error (1, 0, "Bad checksum (offset=%lli)", current_tar_position);
- return -1;
+ return -1;
case STATUS_EOF:
- return 0;
+ return 0;
}
- /* Record of zeroes */
+ /* Record of zeroes */
case STATUS_EOFMARK:
- status = prev_status; /* If error after 0's */
- /* FALL THRU */
+ status = prev_status; /* If error after 0's */
+ /* FALL THRU */
case STATUS_EOF: /* End of archive */
- break;
+ break;
}
- break;
+ break;
};
- return 0;
+
+ return 0;
}
@@ -497,7 +576,7 @@ tar_make_header (tar_record_t *header, io_statbuf_t *st, char *name,
else
header->header.linkflag = LF_NORMAL;
- strncpy (header->header.magic, TMAGIC, TMAGLEN);
+ strncpy (header->header.magic, TMAGIC, strlen(TMAGIC));
uid_to_uname (st->st_uid, header->header.uname);
gid_to_gname (st->st_gid, header->header.gname);
diff --git a/tar.h b/tar.h
index 860ad6f0b..9bf7fe173 100644
--- a/tar.h
+++ b/tar.h
@@ -33,6 +33,14 @@
#include <sys/mknod.h>
#endif
+enum {
+ TAR_UNKNOWN = 0,
+ TAR_V7,
+ TAR_USTAR,
+ TAR_POSIX,
+ TAR_GNU
+};
+
/*
* Header block on tape.
*
@@ -43,6 +51,7 @@
*/
#define RECORDSIZE 512
#define NAMSIZ 100
+#define PREFIX_SIZE 155
#define TUNMLEN 32
#define TGNMLEN 32
#define SPARSE_EXT_HDR 21
@@ -75,20 +84,27 @@ union record {
char gname[TGNMLEN];
char devmajor[8];
char devminor[8];
- /* these following fields were added by JF for gnu */
- /* and are NOT standard */
- char atime[12];
- char ctime[12];
- char offset[12];
- char longnames[4];
-#ifdef NEEDPAD
- char pad;
-#endif
- struct sparse sp[SPARSE_IN_HDR];
- char isextended;
- char realsize[12]; /* true size of the sparse file */
- /* char ending_blanks[12];*//* number of nulls at the
- end of the file, if any */
+ /* The following bytes of the tar header record were originally unused.
+
+ Archives following the ustar specification use almost all of those
+ bytes to support pathnames of 256 characters in length.
+
+ GNU tar archives use the "unused" space to support incremental
+ archives and sparse files. */
+ union unused {
+ char prefix[PREFIX_SIZE];
+ /* GNU extensions to the ustar (POSIX.1-1988) archive format. */
+ struct oldgnu {
+ char atime[12];
+ char ctime[12];
+ char offset[12];
+ char longnames[4];
+ char pad;
+ struct sparse sp[SPARSE_IN_HDR];
+ char isextended;
+ char realsize[12]; /* true size of the sparse file */
+ } oldgnu;
+ } unused;
} header;
struct extended_header {
struct sparse sp[21];
@@ -100,10 +116,8 @@ union record {
#define CHKBLANKS " " /* 8 blanks, no null */
/* The magic field is filled with this if uname and gname are valid. */
-#define TMAGIC "ustar " /* 7 chars and a null */
-#define TMAGLEN 6
-#define TVERSION "00" /* 00 and no null */
-#define TVERSLEN 2
+#define TMAGIC "ustar" /* ustar and a null */
+#define OLDGNU_MAGIC "ustar " /* 7 chars and a null */
/* The linkflag defines the type of file */
#define LF_OLDNORMAL '\0' /* Normal disk file, Unix compat */
@@ -115,6 +129,8 @@ union record {
#define LF_DIR '5' /* Directory */
#define LF_FIFO '6' /* FIFO special file */
#define LF_CONTIG '7' /* Contiguous file */
+#define LF_EXTHDR 'x' /* pax Extended Header */
+#define LF_GLOBAL_EXTHDR 'g' /* pax Global Extended Header */
/* Further link types may be defined later. */
/* Note that the standards committee allows only capital A through
@@ -137,8 +153,6 @@ union record {
#define LF_VOLHDR 'V' /* This file is a tape/volume header */
/* Ignore it on extraction */
-#define LF_TRANS 'T' /* GNU/Hurd passive translator */
-
/*
* Exit codes from the "tar" program
*/
@@ -151,6 +165,8 @@ union record {
Tape volume doesn't match the one
specified on the command line */
+#define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )
+
/*
* We default to Unix Standard format rather than 4.2BSD tar format.
* The code can actually produce all three:
@@ -171,8 +187,15 @@ union record {
typedef union record tar_record_t;
+struct archive {
+ struct store *tar_file;
+ int type;
+ store_offset_t current_tar_position;
+ tar_record_t rec_buf;
+};
+
extern int tar_open_archive (struct store *tar_file);
-extern void tar_header2stat (io_statbuf_t *st, tar_record_t *header);
+extern void tar_fill_stat (struct archive *archive, io_statbuf_t *st, tar_record_t *header);
/* Create a tar header based on ST and NAME where NAME is a path.
If NAME is a hard link (resp. symlink), HARDLINK (resp.
diff --git a/tarfs.c b/tarfs.c
index af282f5f0..8ff1060f5 100644
--- a/tarfs.c
+++ b/tarfs.c
@@ -80,7 +80,7 @@ static struct store *tar_file;
static struct mutex tar_file_lock;
/* Archive parsing hook (see tar.c) */
-extern int (* tar_header_hook) (tar_record_t *, off_t);
+extern int (* tar_header_hook) (tar_record_t *, struct archive *);
/* List of tar items for this file */
static struct tar_list tar_list;
@@ -325,7 +325,7 @@ error_t tarfs_create_node (struct node **newnode, struct node *dir,
It simply creates the node corresponding to the header.
OFFSET denotes the offset of the header in the archive. */
int
-tarfs_add_header (tar_record_t *hdr, off_t offset)
+tarfs_add_header (tar_record_t *hdr, struct archive *archive)
{
error_t err;
static struct tar_item *last_item = NULL;
@@ -412,7 +412,7 @@ tarfs_add_header (tar_record_t *hdr, off_t offset)
/* Add the tar item into the list. */
err = tar_make_item (&NODE_INFO(new)->tar, new,
- 0, offset);
+ 0, archive->current_tar_position);
assert_perror (err);
}
}
@@ -431,7 +431,7 @@ tarfs_add_header (tar_record_t *hdr, off_t offset)
if (new)
{
NEW_NODE_INFO (new);
- tar_header2stat (&new->nn_stat, hdr);
+ tar_fill_stat (archive, &new->nn_stat, hdr);
/* Create a cache for the new node. */
err = cache_create (new);
@@ -440,7 +440,7 @@ tarfs_add_header (tar_record_t *hdr, off_t offset)
/* Add the tar item into the list. */
err = tar_make_item (&NODE_INFO(new)->tar, new,
- new->nn_stat.st_size, offset);
+ new->nn_stat.st_size, archive->current_tar_position);
assert_perror (err);
}
}