summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorkientzle <kientzle@FreeBSD.org>2004-06-27 01:15:31 +0000
committerkientzle <kientzle@FreeBSD.org>2004-06-27 01:15:31 +0000
commitfc2f6ee069a5f363320dec62ccf54b12ece0d438 (patch)
treed31c911cb53635ed8be6eeea16ab388e7e4a8941 /lib
parent4731df16a26e51f2f571e0222cbf4aa767d23583 (diff)
downloadFreeBSD-src-fc2f6ee069a5f363320dec62ccf54b12ece0d438.zip
FreeBSD-src-fc2f6ee069a5f363320dec62ccf54b12ece0d438.tar.gz
Read gtar-style sparse archives.
This change also pointed out one API deficiency: the archive_read_data_into_XXX functions were originally defined to return the total bytes read. This is, of course, ambiguous when dealing with non-contiguous files. Change it to just return a status value.
Diffstat (limited to 'lib')
-rw-r--r--lib/libarchive/archive.h6
-rw-r--r--lib/libarchive/archive.h.in6
-rw-r--r--lib/libarchive/archive_read.37
-rw-r--r--lib/libarchive/archive_read_data_into_buffer.c4
-rw-r--r--lib/libarchive/archive_read_data_into_fd.c6
-rw-r--r--lib/libarchive/archive_read_extract.c8
-rw-r--r--lib/libarchive/archive_read_support_format_tar.c181
7 files changed, 157 insertions, 61 deletions
diff --git a/lib/libarchive/archive.h b/lib/libarchive/archive.h
index c8f5e94..4bef0c9 100644
--- a/lib/libarchive/archive.h
+++ b/lib/libarchive/archive.h
@@ -180,12 +180,12 @@ int archive_read_data_block(struct archive *a,
* Some convenience functions that are built on archive_read_data:
* 'skip': skips entire entry
* 'into_buffer': writes data into memory buffer that you provide
- * 'into_file': writes data to specified filedes
+ * 'into_fd': writes data to specified filedes
*/
int archive_read_data_skip(struct archive *);
-ssize_t archive_read_data_into_buffer(struct archive *, void *buffer,
+int archive_read_data_into_buffer(struct archive *, void *buffer,
ssize_t len);
-ssize_t archive_read_data_into_fd(struct archive *, int fd);
+int archive_read_data_into_fd(struct archive *, int fd);
/*-
* Convenience function to recreate the current entry (whose header
diff --git a/lib/libarchive/archive.h.in b/lib/libarchive/archive.h.in
index c8f5e94..4bef0c9 100644
--- a/lib/libarchive/archive.h.in
+++ b/lib/libarchive/archive.h.in
@@ -180,12 +180,12 @@ int archive_read_data_block(struct archive *a,
* Some convenience functions that are built on archive_read_data:
* 'skip': skips entire entry
* 'into_buffer': writes data into memory buffer that you provide
- * 'into_file': writes data to specified filedes
+ * 'into_fd': writes data to specified filedes
*/
int archive_read_data_skip(struct archive *);
-ssize_t archive_read_data_into_buffer(struct archive *, void *buffer,
+int archive_read_data_into_buffer(struct archive *, void *buffer,
ssize_t len);
-ssize_t archive_read_data_into_fd(struct archive *, int fd);
+int archive_read_data_into_fd(struct archive *, int fd);
/*-
* Convenience function to recreate the current entry (whose header
diff --git a/lib/libarchive/archive_read.3 b/lib/libarchive/archive_read.3
index f195d57..15910c9 100644
--- a/lib/libarchive/archive_read.3
+++ b/lib/libarchive/archive_read.3
@@ -168,8 +168,10 @@ a
.Tn struct archive_entry .
.It Fn archive_read_data
Read data associated with the header just read.
-Internally, this is a convenience function that uses
-.Fn archive_read_data_block .
+Internally, this is a convenience function that calls
+.Fn archive_read_data_block
+and fills any gaps with nulls so that callers see a single
+continuous stream of data.
.It Fn archive_read_data_block
Return the next available block of data for this entry.
Unlike
@@ -393,4 +395,3 @@ The
library was written by
.An Tim Kientzle Aq kientzle@acm.org .
.Sh BUGS
-The support for GNU tar formats is somewhat limited and should be improved.
diff --git a/lib/libarchive/archive_read_data_into_buffer.c b/lib/libarchive/archive_read_data_into_buffer.c
index 12abb55..0b52617 100644
--- a/lib/libarchive/archive_read_data_into_buffer.c
+++ b/lib/libarchive/archive_read_data_into_buffer.c
@@ -31,7 +31,7 @@ __FBSDID("$FreeBSD$");
#include "archive.h"
-ssize_t
+int
archive_read_data_into_buffer(struct archive *a, void *d, ssize_t len)
{
char *dest;
@@ -45,5 +45,5 @@ archive_read_data_into_buffer(struct archive *a, void *d, ssize_t len)
bytes_read = archive_read_data(a, dest + total_bytes,
len - total_bytes);
}
- return (total_bytes);
+ return (ARCHIVE_OK);
}
diff --git a/lib/libarchive/archive_read_data_into_fd.c b/lib/libarchive/archive_read_data_into_fd.c
index 85ba98d..b2143ca 100644
--- a/lib/libarchive/archive_read_data_into_fd.c
+++ b/lib/libarchive/archive_read_data_into_fd.c
@@ -39,7 +39,7 @@ __FBSDID("$FreeBSD$");
/*
* This implementation minimizes copying of data and is sparse-file aware.
*/
-ssize_t
+int
archive_read_data_into_fd(struct archive *a, int fd)
{
int r;
@@ -76,6 +76,6 @@ archive_read_data_into_fd(struct archive *a, int fd)
}
if (r != ARCHIVE_EOF)
- return (-1);
- return (total_written);
+ return (r);
+ return (ARCHIVE_OK);
}
diff --git a/lib/libarchive/archive_read_extract.c b/lib/libarchive/archive_read_extract.c
index 8bb75a6..1245d4c 100644
--- a/lib/libarchive/archive_read_extract.c
+++ b/lib/libarchive/archive_read_extract.c
@@ -319,7 +319,6 @@ archive_read_extract_regular(struct archive *a, struct archive_entry *entry,
int flags)
{
int fd, r;
- ssize_t s;
r = ARCHIVE_OK;
fd = archive_read_extract_regular_open(a,
@@ -328,12 +327,7 @@ archive_read_extract_regular(struct archive *a, struct archive_entry *entry,
archive_set_error(a, errno, "Can't open");
return (ARCHIVE_WARN);
}
- s = archive_read_data_into_fd(a, fd);
- if (s < archive_entry_size(entry)) {
- /* Didn't read enough data? Complain but keep going. */
- archive_set_error(a, EIO, "Archive data truncated");
- r = ARCHIVE_WARN;
- }
+ r = archive_read_data_into_fd(a, fd);
set_ownership(a, entry, flags);
set_time(a, entry, flags);
/* Always restore permissions for regular files. */
diff --git a/lib/libarchive/archive_read_support_format_tar.c b/lib/libarchive/archive_read_support_format_tar.c
index 014ba6c..205f13d 100644
--- a/lib/libarchive/archive_read_support_format_tar.c
+++ b/lib/libarchive/archive_read_support_format_tar.c
@@ -61,8 +61,51 @@ struct archive_entry_header_ustar {
};
/*
+ * Structure of GNU tar header
+ */
+struct gnu_sparse {
+ char offset[12];
+ char numbytes[12];
+};
+
+struct archive_entry_header_gnutar {
+ char name[100];
+ char mode[8];
+ char uid[8];
+ char gid[8];
+ char size[12];
+ char mtime[12];
+ char checksum[8];
+ char typeflag[1];
+ char linkname[100];
+ char magic[8]; /* "ustar \0" (note blank/blank/null at end) */
+ char uname[32];
+ char gname[32];
+ char devmajor[8];
+ char devminor[8];
+ char atime[12];
+ char ctime[12];
+ char offset[12];
+ char longnames[4];
+ char unused[1];
+ struct gnu_sparse sparse[4];
+ char isextended[1];
+ char realsize[12];
+ /*
+ * GNU doesn't use POSIX 'prefix' field; they use the 'L' (longname)
+ * entry instead.
+ */
+};
+
+/*
* Data specific to this format.
*/
+struct sparse_block {
+ struct sparse_block *next;
+ off_t offset;
+ off_t remaining;
+};
+
struct tar {
struct archive_string acl_text;
struct archive_string entry_name;
@@ -79,10 +122,15 @@ struct tar {
off_t entry_bytes_remaining;
off_t entry_offset;
off_t entry_padding;
+ struct sparse_block *sparse_list;
};
static size_t UTF8_mbrtowc(wchar_t *pwc, const char *s, size_t n);
static int archive_block_is_null(const unsigned char *p);
+int gnu_read_sparse_data(struct archive *, struct tar *,
+ const struct archive_entry_header_gnutar *header);
+void gnu_parse_sparse_data(struct archive *, struct tar *,
+ const struct gnu_sparse *sparse, int length);
static int header_Solaris_ACL(struct archive *, struct tar *,
struct archive_entry *, struct stat *, const void *);
static int header_common(struct archive *, struct tar *,
@@ -295,6 +343,7 @@ archive_read_format_tar_read_data(struct archive *a,
{
ssize_t bytes_read;
struct tar *tar;
+ struct sparse_block *p;
tar = *(a->pformat_data);
if (tar->entry_bytes_remaining > 0) {
@@ -303,6 +352,19 @@ archive_read_format_tar_read_data(struct archive *a,
return (ARCHIVE_FATAL);
if (bytes_read > tar->entry_bytes_remaining)
bytes_read = tar->entry_bytes_remaining;
+ while (tar->sparse_list != NULL &&
+ tar->sparse_list->remaining == 0) {
+ p = tar->sparse_list;
+ tar->sparse_list = p->next;
+ free(p);
+ if (tar->sparse_list != NULL)
+ tar->entry_offset = tar->sparse_list->offset;
+ }
+ if (tar->sparse_list != NULL) {
+ if (tar->sparse_list->remaining < bytes_read)
+ bytes_read = tar->sparse_list->remaining;
+ tar->sparse_list->remaining -= bytes_read;
+ }
*size = bytes_read;
*offset = tar->entry_offset;
tar->entry_offset += bytes_read;
@@ -1132,41 +1194,6 @@ pax_time(const wchar_t *p, int64_t *ps, long *pn)
}
/*
- * Structure of GNU tar header
- */
-struct archive_entry_header_gnutar {
- char name[100];
- char mode[8];
- char uid[8];
- char gid[8];
- char size[12];
- char mtime[12];
- char checksum[8];
- char typeflag[1];
- char linkname[100];
- char magic[8]; /* "ustar \0" (note blank/blank/null at end) */
- char uname[32];
- char gname[32];
- char devmajor[8];
- char devminor[8];
- char atime[12];
- char ctime[12];
- char offset[12];
- char longnames[4];
- char unused[1];
- struct {
- char offset[12];
- char numbytes[12];
- } sparse[4];
- char isextended[1];
- char realsize[12];
- /*
- * GNU doesn't use POSIX 'prefix' field; they use the 'L' (longname)
- * entry instead.
- */
-};
-
-/*
* Parse GNU tar header
*/
static int
@@ -1212,19 +1239,93 @@ header_gnutar(struct archive *a, struct tar *tar, struct archive_entry *entry,
else
st->st_rdev = 0;
+ tar->entry_bytes_remaining = st->st_size;
+ tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
+
/* Grab GNU-specific fields. */
- /* TODO: FILL THIS IN!!! */
st->st_atime = tar_atol(header->atime, sizeof(header->atime));
st->st_ctime = tar_atol(header->ctime, sizeof(header->ctime));
+ if (header->realsize[0] != 0) {
+ st->st_size = tar_atol(header->realsize,
+ sizeof(header->realsize));
+ }
- /* XXX TODO: Recognize and skip extra GNU header blocks. */
-
- tar->entry_bytes_remaining = st->st_size;
- tar->entry_padding = 0x1ff & (-tar->entry_bytes_remaining);
+ if (header->sparse[0].offset[0] != 0) {
+ gnu_read_sparse_data(a, tar, header);
+ } else {
+ if (header->isextended[0] != 0) {
+ /* XXX WTF? XXX */
+ }
+ }
return (0);
}
+int
+gnu_read_sparse_data(struct archive *a, struct tar *tar,
+ const struct archive_entry_header_gnutar *header)
+{
+ ssize_t bytes_read;
+ const void *data;
+ struct extended {
+ struct gnu_sparse sparse[21];
+ char isextended[1];
+ char padding[7];
+ };
+ const struct extended *ext;
+
+ gnu_parse_sparse_data(a, tar, header->sparse, 4);
+ if (header->isextended[0] == 0)
+ return (ARCHIVE_OK);
+
+ do {
+ bytes_read = (a->compression_read_ahead)(a, &data, 512);
+ if (bytes_read < 0)
+ return (ARCHIVE_FATAL);
+ if (bytes_read < 512) {
+ archive_set_error(a, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated tar archive "
+ "detected while reading sparse file data");
+ return (ARCHIVE_FATAL);
+ }
+ (a->compression_read_consume)(a, 512);
+ ext = (const struct extended *)data;
+ gnu_parse_sparse_data(a, tar, ext->sparse, 21);
+ } while (ext->isextended[0] != 0);
+ if (tar->sparse_list != NULL)
+ tar->entry_offset = tar->sparse_list->offset;
+ return (ARCHIVE_OK);
+}
+
+void
+gnu_parse_sparse_data(struct archive *a, struct tar *tar,
+ const struct gnu_sparse *sparse, int length)
+{
+ struct sparse_block *last;
+ struct sparse_block *p;
+
+ (void)a; /* UNUSED */
+
+ last = tar->sparse_list;
+ while (last != NULL && last->next != NULL)
+ last = last->next;
+
+ while (length > 0 && sparse->offset[0] != 0) {
+ p = malloc(sizeof(*p));
+ memset(p, 0, sizeof(*p));
+ if (last != NULL)
+ last->next = p;
+ else
+ tar->sparse_list = p;
+ last = p;
+ p->offset = tar_atol(sparse->offset, sizeof(sparse->offset));
+ p->remaining =
+ tar_atol(sparse->numbytes, sizeof(sparse->numbytes));
+ sparse++;
+ length--;
+ }
+}
+
/*-
* Convert text->integer.
*
OpenPOWER on IntegriCloud