We need a valid st_dev, st_ino and st_mtime to correctly track which files have been verified and to update our notion of time. ve_utc_set(): ignore utc if it would jump our current time by more than VE_UTC_MAX_JUMP (20 years). Allow testing of install command via userboot. Need to fix its stat implementation too. bhyveload also needs stat fixed - due to change to userboot.h Call ve_error_get() from vectx_close() when hash is wrong. Track the names of files we have hashed into pcr For the purposes of measured boot, it is important to be able to reproduce the hash reflected in loader.ve.pcr so loader.ve.hashed provides a list of names in the order they were added. Reviewed by: imp MFC after: 1 week Sponsored by: Juniper Networks Differential Revision: https://reviews.freebsd.org//D24027
810 lines
17 KiB
C
810 lines
17 KiB
C
/*-
|
|
* Copyright (c) 2007-2014, Juniper Networks, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include "stand.h"
|
|
|
|
#include <sys/stat.h>
|
|
#include <sys/stdint.h>
|
|
#include <string.h>
|
|
#include <zlib.h>
|
|
|
|
#ifdef PKGFS_DEBUG
|
|
#define DBG(x) printf x
|
|
#else
|
|
#define DBG(x)
|
|
#endif
|
|
|
|
static int pkg_open(const char *, struct open_file *);
|
|
static int pkg_close(struct open_file *);
|
|
static int pkg_read(struct open_file *, void *, size_t, size_t *);
|
|
static off_t pkg_seek(struct open_file *, off_t, int);
|
|
static int pkg_stat(struct open_file *, struct stat *);
|
|
static int pkg_readdir(struct open_file *, struct dirent *);
|
|
static off_t pkg_atol(const char *, unsigned);
|
|
|
|
struct fs_ops pkgfs_fsops = {
|
|
"pkg",
|
|
pkg_open,
|
|
pkg_close,
|
|
pkg_read,
|
|
null_write,
|
|
pkg_seek,
|
|
pkg_stat,
|
|
pkg_readdir
|
|
};
|
|
|
|
#define PKG_BUFSIZE 512
|
|
#define PKG_MAXCACHESZ 16384
|
|
|
|
#define PKG_FILEEXT ".tgz"
|
|
|
|
/*
|
|
* Layout of POSIX 'ustar' header.
|
|
*/
|
|
struct ustar_hdr {
|
|
char ut_name[100];
|
|
char ut_mode[8];
|
|
char ut_uid[8];
|
|
char ut_gid[8];
|
|
char ut_size[12];
|
|
char ut_mtime[12];
|
|
char ut_checksum[8];
|
|
char ut_typeflag[1];
|
|
char ut_linkname[100];
|
|
char ut_magic[6]; /* For POSIX: "ustar\0" */
|
|
char ut_version[2]; /* For POSIX: "00" */
|
|
char ut_uname[32];
|
|
char ut_gname[32];
|
|
char ut_rdevmajor[8];
|
|
char ut_rdevminor[8];
|
|
union {
|
|
struct {
|
|
char prefix[155];
|
|
} posix;
|
|
struct {
|
|
char atime[12];
|
|
char ctime[12];
|
|
char offset[12];
|
|
char longnames[4];
|
|
char unused[1];
|
|
struct gnu_sparse {
|
|
char offset[12];
|
|
char numbytes[12];
|
|
} sparse[4];
|
|
char isextended[1];
|
|
char realsize[12];
|
|
} gnu;
|
|
} u;
|
|
u_char __padding[12];
|
|
};
|
|
|
|
struct package;
|
|
|
|
struct tarfile
|
|
{
|
|
struct package *tf_pkg;
|
|
struct tarfile *tf_next;
|
|
struct ustar_hdr tf_hdr;
|
|
off_t tf_ofs;
|
|
off_t tf_size;
|
|
off_t tf_fp;
|
|
size_t tf_cachesz;
|
|
void *tf_cache;
|
|
};
|
|
|
|
struct package
|
|
{
|
|
struct package *pkg_chain;
|
|
int pkg_fd;
|
|
off_t pkg_ofs;
|
|
z_stream pkg_zs;
|
|
struct tarfile *pkg_first;
|
|
struct tarfile *pkg_last;
|
|
u_char pkg_buf[PKG_BUFSIZE];
|
|
};
|
|
|
|
static struct package *package = NULL;
|
|
|
|
static int new_package(int, struct package **);
|
|
|
|
void
|
|
pkgfs_cleanup(void)
|
|
{
|
|
struct package *chain;
|
|
struct tarfile *tf, *tfn;
|
|
|
|
while (package != NULL) {
|
|
inflateEnd(&package->pkg_zs);
|
|
close(package->pkg_fd);
|
|
|
|
tf = package->pkg_first;
|
|
while (tf != NULL) {
|
|
tfn = tf->tf_next;
|
|
if (tf->tf_cachesz > 0)
|
|
free(tf->tf_cache);
|
|
free(tf);
|
|
tf = tfn;
|
|
}
|
|
|
|
chain = package->pkg_chain;
|
|
free(package);
|
|
package = chain;
|
|
}
|
|
}
|
|
|
|
int
|
|
pkgfs_init(const char *pkgname, struct fs_ops *proto)
|
|
{
|
|
struct package *pkg;
|
|
int error, fd;
|
|
|
|
pkg = NULL;
|
|
if (proto != &pkgfs_fsops)
|
|
pkgfs_cleanup();
|
|
|
|
exclusive_file_system = proto;
|
|
|
|
fd = open(pkgname, O_RDONLY);
|
|
|
|
exclusive_file_system = NULL;
|
|
|
|
if (fd == -1)
|
|
return (errno);
|
|
|
|
error = new_package(fd, &pkg);
|
|
if (error) {
|
|
close(fd);
|
|
return (error);
|
|
}
|
|
|
|
if (pkg == NULL)
|
|
return (EDOOFUS);
|
|
|
|
pkg->pkg_chain = package;
|
|
package = pkg;
|
|
exclusive_file_system = &pkgfs_fsops;
|
|
return (0);
|
|
}
|
|
|
|
static int get_mode(struct tarfile *);
|
|
static int get_zipped(struct package *, void *, size_t);
|
|
static int new_package(int, struct package **);
|
|
static struct tarfile *scan_tarfile(struct package *, struct tarfile *);
|
|
|
|
static int
|
|
pkg_open(const char *fn, struct open_file *f)
|
|
{
|
|
struct tarfile *tf;
|
|
|
|
if (fn == NULL || f == NULL)
|
|
return (EINVAL);
|
|
|
|
if (package == NULL)
|
|
return (ENXIO);
|
|
|
|
/*
|
|
* We can only read from a package, so reject request to open
|
|
* for write-only or read-write.
|
|
*/
|
|
if (f->f_flags != F_READ)
|
|
return (EPERM);
|
|
|
|
/*
|
|
* Scan the file headers for the named file. We stop scanning
|
|
* at the first filename that has the .pkg extension. This is
|
|
* a package within a package. We assume we have all the files
|
|
* we need up-front and without having to dig within nested
|
|
* packages.
|
|
*
|
|
* Note that we preserve streaming properties as much as possible.
|
|
*/
|
|
while (*fn == '/')
|
|
fn++;
|
|
|
|
/*
|
|
* Allow opening of the root directory for use by readdir()
|
|
* to support listing files in the package.
|
|
*/
|
|
if (*fn == '\0') {
|
|
f->f_fsdata = NULL;
|
|
return (0);
|
|
}
|
|
|
|
tf = scan_tarfile(package, NULL);
|
|
while (tf != NULL) {
|
|
if (strcmp(fn, tf->tf_hdr.ut_name) == 0) {
|
|
f->f_fsdata = tf;
|
|
tf->tf_fp = 0; /* Reset the file pointer. */
|
|
return (0);
|
|
}
|
|
tf = scan_tarfile(package, tf);
|
|
}
|
|
return (errno);
|
|
}
|
|
|
|
static int
|
|
pkg_close(struct open_file *f)
|
|
{
|
|
struct tarfile *tf;
|
|
|
|
tf = (struct tarfile *)f->f_fsdata;
|
|
if (tf == NULL)
|
|
return (0);
|
|
|
|
/*
|
|
* Free up the cache if we read all of the file.
|
|
*/
|
|
if (tf->tf_fp == tf->tf_size && tf->tf_cachesz > 0) {
|
|
free(tf->tf_cache);
|
|
tf->tf_cachesz = 0;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
pkg_read(struct open_file *f, void *buf, size_t size, size_t *res)
|
|
{
|
|
struct tarfile *tf;
|
|
char *p;
|
|
off_t fp;
|
|
size_t sz;
|
|
|
|
tf = (struct tarfile *)f->f_fsdata;
|
|
if (tf == NULL) {
|
|
if (res != NULL)
|
|
*res = size;
|
|
return (EBADF);
|
|
}
|
|
|
|
fp = tf->tf_fp;
|
|
p = buf;
|
|
sz = 0;
|
|
while (size > 0) {
|
|
sz = tf->tf_size - fp;
|
|
if (fp < tf->tf_cachesz && tf->tf_cachesz < tf->tf_size)
|
|
sz = tf->tf_cachesz - fp;
|
|
if (size < sz)
|
|
sz = size;
|
|
if (sz == 0)
|
|
break;
|
|
|
|
if (fp < tf->tf_cachesz) {
|
|
/* Satisfy the request from cache. */
|
|
memcpy(p, tf->tf_cache + fp, sz);
|
|
fp += sz;
|
|
p += sz;
|
|
size -= sz;
|
|
continue;
|
|
}
|
|
|
|
if (get_zipped(tf->tf_pkg, p, sz) == -1) {
|
|
sz = -1;
|
|
break;
|
|
}
|
|
|
|
fp += sz;
|
|
p += sz;
|
|
size -= sz;
|
|
|
|
if (tf->tf_cachesz != 0)
|
|
continue;
|
|
|
|
tf->tf_cachesz = (sz <= PKG_MAXCACHESZ) ? sz : PKG_MAXCACHESZ;
|
|
tf->tf_cache = malloc(tf->tf_cachesz);
|
|
if (tf->tf_cache != NULL)
|
|
memcpy(tf->tf_cache, buf, tf->tf_cachesz);
|
|
else
|
|
tf->tf_cachesz = 0;
|
|
}
|
|
|
|
tf->tf_fp = fp;
|
|
if (res != NULL)
|
|
*res = size;
|
|
return ((sz == -1) ? errno : 0);
|
|
}
|
|
|
|
static off_t
|
|
pkg_seek(struct open_file *f, off_t ofs, int whence)
|
|
{
|
|
char buf[512];
|
|
struct tarfile *tf;
|
|
off_t delta;
|
|
off_t nofs;
|
|
size_t sz, res;
|
|
int error;
|
|
|
|
tf = (struct tarfile *)f->f_fsdata;
|
|
if (tf == NULL) {
|
|
errno = EBADF;
|
|
return (-1);
|
|
}
|
|
|
|
switch (whence) {
|
|
case SEEK_SET:
|
|
delta = ofs - tf->tf_fp;
|
|
break;
|
|
case SEEK_CUR:
|
|
delta = ofs;
|
|
break;
|
|
case SEEK_END:
|
|
delta = tf->tf_size - tf->tf_fp + ofs;
|
|
break;
|
|
default:
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
|
|
if (delta < 0) {
|
|
/* seeking backwards - ok if within cache */
|
|
if (tf->tf_cachesz > 0 && tf->tf_fp <= tf->tf_cachesz) {
|
|
nofs = tf->tf_fp + delta;
|
|
if (nofs >= 0) {
|
|
tf->tf_fp = nofs;
|
|
return (tf->tf_fp);
|
|
}
|
|
}
|
|
DBG(("%s: negative file seek (%jd)\n", __func__,
|
|
(intmax_t)delta));
|
|
errno = ESPIPE;
|
|
return (-1);
|
|
}
|
|
|
|
while (delta > 0 && tf->tf_fp < tf->tf_size) {
|
|
sz = (delta > sizeof(buf)) ? sizeof(buf) : delta;
|
|
error = pkg_read(f, buf, sz, &res);
|
|
if (error != 0) {
|
|
errno = error;
|
|
return (-1);
|
|
}
|
|
delta -= sz - res;
|
|
}
|
|
|
|
return (tf->tf_fp);
|
|
}
|
|
|
|
static int
|
|
pkg_stat(struct open_file *f, struct stat *sb)
|
|
{
|
|
struct tarfile *tf;
|
|
|
|
tf = (struct tarfile *)f->f_fsdata;
|
|
if (tf == NULL)
|
|
return (EBADF);
|
|
memset(sb, 0, sizeof(*sb));
|
|
sb->st_mode = get_mode(tf);
|
|
if ((sb->st_mode & S_IFMT) == 0) {
|
|
/* tar file bug - assume regular file */
|
|
sb->st_mode |= S_IFREG;
|
|
}
|
|
sb->st_size = tf->tf_size;
|
|
sb->st_blocks = (tf->tf_size + 511) / 512;
|
|
sb->st_mtime = pkg_atol(tf->tf_hdr.ut_mtime, 12);
|
|
sb->st_dev = (off_t)tf->tf_pkg;
|
|
sb->st_ino = tf->tf_ofs; /* unique per tf_pkg */
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
pkg_readdir(struct open_file *f, struct dirent *d)
|
|
{
|
|
struct tarfile *tf;
|
|
|
|
tf = (struct tarfile *)f->f_fsdata;
|
|
if (tf != NULL)
|
|
return (EBADF);
|
|
|
|
tf = scan_tarfile(package, NULL);
|
|
if (tf == NULL)
|
|
return (ENOENT);
|
|
|
|
d->d_fileno = 0;
|
|
d->d_reclen = sizeof(*d);
|
|
d->d_type = DT_REG;
|
|
memcpy(d->d_name, tf->tf_hdr.ut_name, sizeof(d->d_name));
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Low-level support functions.
|
|
*/
|
|
|
|
static int
|
|
get_byte(struct package *pkg, off_t *op)
|
|
{
|
|
int c;
|
|
|
|
if (pkg->pkg_zs.avail_in == 0) {
|
|
c = read(pkg->pkg_fd, pkg->pkg_buf, PKG_BUFSIZE);
|
|
if (c <= 0)
|
|
return (-1);
|
|
pkg->pkg_zs.avail_in = c;
|
|
pkg->pkg_zs.next_in = pkg->pkg_buf;
|
|
}
|
|
|
|
c = *pkg->pkg_zs.next_in;
|
|
pkg->pkg_zs.next_in++;
|
|
pkg->pkg_zs.avail_in--;
|
|
(*op)++;
|
|
return (c);
|
|
}
|
|
|
|
static int
|
|
get_zipped(struct package *pkg, void *buf, size_t bufsz)
|
|
{
|
|
int c;
|
|
|
|
pkg->pkg_zs.next_out = buf;
|
|
pkg->pkg_zs.avail_out = bufsz;
|
|
|
|
while (pkg->pkg_zs.avail_out) {
|
|
if (pkg->pkg_zs.avail_in == 0) {
|
|
c = read(pkg->pkg_fd, pkg->pkg_buf, PKG_BUFSIZE);
|
|
if (c <= 0) {
|
|
errno = EIO;
|
|
return (-1);
|
|
}
|
|
pkg->pkg_zs.avail_in = c;
|
|
pkg->pkg_zs.next_in = pkg->pkg_buf;
|
|
}
|
|
|
|
c = inflate(&pkg->pkg_zs, Z_SYNC_FLUSH);
|
|
if (c != Z_OK && c != Z_STREAM_END) {
|
|
errno = EIO;
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
pkg->pkg_ofs += bufsz;
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
cache_data(struct tarfile *tf)
|
|
{
|
|
struct package *pkg;
|
|
size_t sz;
|
|
|
|
if (tf == NULL) {
|
|
DBG(("%s: no file to cache data for?\n", __func__));
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
|
|
pkg = tf->tf_pkg;
|
|
if (pkg == NULL) {
|
|
DBG(("%s: no package associated with file?\n", __func__));
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
|
|
if (tf->tf_ofs != pkg->pkg_ofs) {
|
|
DBG(("%s: caching after partial read of file %s?\n",
|
|
__func__, tf->tf_hdr.ut_name));
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
|
|
/* We don't cache everything... */
|
|
if (tf->tf_size > PKG_MAXCACHESZ) {
|
|
errno = ENOMEM;
|
|
return (-1);
|
|
}
|
|
|
|
/* All files are padded to a multiple of 512 bytes. */
|
|
sz = (tf->tf_size + 0x1ff) & ~0x1ff;
|
|
|
|
tf->tf_cache = malloc(sz);
|
|
if (tf->tf_cache == NULL) {
|
|
DBG(("%s: could not allocate %d bytes\n", __func__, (int)sz));
|
|
errno = ENOMEM;
|
|
return (-1);
|
|
}
|
|
|
|
tf->tf_cachesz = sz;
|
|
return (get_zipped(pkg, tf->tf_cache, sz));
|
|
}
|
|
|
|
/*
|
|
* Note that this implementation does not (and should not!) obey
|
|
* locale settings; you cannot simply substitute strtol here, since
|
|
* it does obey locale.
|
|
*/
|
|
static off_t
|
|
pkg_atol8(const char *p, unsigned char_cnt)
|
|
{
|
|
int64_t l, limit, last_digit_limit;
|
|
int digit, sign, base;
|
|
|
|
base = 8;
|
|
limit = INT64_MAX / base;
|
|
last_digit_limit = INT64_MAX % base;
|
|
|
|
while (*p == ' ' || *p == '\t')
|
|
p++;
|
|
if (*p == '-') {
|
|
sign = -1;
|
|
p++;
|
|
} else
|
|
sign = 1;
|
|
|
|
l = 0;
|
|
digit = *p - '0';
|
|
while (digit >= 0 && digit < base && char_cnt-- > 0) {
|
|
if (l>limit || (l == limit && digit > last_digit_limit)) {
|
|
l = UINT64_MAX; /* Truncate on overflow. */
|
|
break;
|
|
}
|
|
l = (l * base) + digit;
|
|
digit = *++p - '0';
|
|
}
|
|
return (sign < 0) ? -l : l;
|
|
}
|
|
|
|
/*
|
|
* Parse a base-256 integer. This is just a straight signed binary
|
|
* value in big-endian order, except that the high-order bit is
|
|
* ignored. Remember that "int64_t" may or may not be exactly 64
|
|
* bits; the implementation here tries to avoid making any assumptions
|
|
* about the actual size of an int64_t. It does assume we're using
|
|
* twos-complement arithmetic, though.
|
|
*/
|
|
static int64_t
|
|
pkg_atol256(const char *_p, unsigned char_cnt)
|
|
{
|
|
int64_t l, upper_limit, lower_limit;
|
|
const unsigned char *p = (const unsigned char *)_p;
|
|
|
|
upper_limit = INT64_MAX / 256;
|
|
lower_limit = INT64_MIN / 256;
|
|
|
|
/* Pad with 1 or 0 bits, depending on sign. */
|
|
if ((0x40 & *p) == 0x40)
|
|
l = (int64_t)-1;
|
|
else
|
|
l = 0;
|
|
l = (l << 6) | (0x3f & *p++);
|
|
while (--char_cnt > 0) {
|
|
if (l > upper_limit) {
|
|
l = INT64_MAX; /* Truncate on overflow */
|
|
break;
|
|
} else if (l < lower_limit) {
|
|
l = INT64_MIN;
|
|
break;
|
|
}
|
|
l = (l << 8) | (0xff & (int64_t)*p++);
|
|
}
|
|
return (l);
|
|
}
|
|
|
|
static off_t
|
|
pkg_atol(const char *p, unsigned char_cnt)
|
|
{
|
|
/*
|
|
* Technically, GNU pkg considers a field to be in base-256
|
|
* only if the first byte is 0xff or 0x80.
|
|
*/
|
|
if (*p & 0x80)
|
|
return (pkg_atol256(p, char_cnt));
|
|
return (pkg_atol8(p, char_cnt));
|
|
}
|
|
|
|
static int
|
|
get_mode(struct tarfile *tf)
|
|
{
|
|
return (pkg_atol(tf->tf_hdr.ut_mode, sizeof(tf->tf_hdr.ut_mode)));
|
|
}
|
|
|
|
/* GZip flag byte */
|
|
#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
|
|
#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
|
|
#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
|
|
#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
|
|
#define COMMENT 0x10 /* bit 4 set: file comment present */
|
|
#define RESERVED 0xE0 /* bits 5..7: reserved */
|
|
|
|
static int
|
|
new_package(int fd, struct package **pp)
|
|
{
|
|
struct package *pkg;
|
|
off_t ofs;
|
|
int flags, i, error;
|
|
|
|
pkg = malloc(sizeof(*pkg));
|
|
if (pkg == NULL)
|
|
return (ENOMEM);
|
|
|
|
bzero(pkg, sizeof(*pkg));
|
|
pkg->pkg_fd = fd;
|
|
|
|
/*
|
|
* Parse the header.
|
|
*/
|
|
error = EFTYPE;
|
|
ofs = 0;
|
|
|
|
/* Check megic. */
|
|
if (get_byte(pkg, &ofs) != 0x1f || get_byte(pkg, &ofs) != 0x8b)
|
|
goto fail;
|
|
/* Check method. */
|
|
if (get_byte(pkg, &ofs) != Z_DEFLATED)
|
|
goto fail;
|
|
/* Check flags. */
|
|
flags = get_byte(pkg, &ofs);
|
|
if (flags & RESERVED)
|
|
goto fail;
|
|
|
|
/* Skip time, xflags and OS code. */
|
|
for (i = 0; i < 6; i++) {
|
|
if (get_byte(pkg, &ofs) == -1)
|
|
goto fail;
|
|
}
|
|
|
|
/* Skip extra field. */
|
|
if (flags & EXTRA_FIELD) {
|
|
i = (get_byte(pkg, &ofs) & 0xff) |
|
|
((get_byte(pkg, &ofs) << 8) & 0xff);
|
|
while (i-- > 0) {
|
|
if (get_byte(pkg, &ofs) == -1)
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
/* Skip original file name. */
|
|
if (flags & ORIG_NAME) {
|
|
do {
|
|
i = get_byte(pkg, &ofs);
|
|
} while (i != 0 && i != -1);
|
|
if (i == -1)
|
|
goto fail;
|
|
}
|
|
|
|
/* Print the comment if it's there. */
|
|
if (flags & COMMENT) {
|
|
while (1) {
|
|
i = get_byte(pkg, &ofs);
|
|
if (i == -1)
|
|
goto fail;
|
|
if (i == 0)
|
|
break;
|
|
putchar(i);
|
|
}
|
|
}
|
|
|
|
/* Skip the CRC. */
|
|
if (flags & HEAD_CRC) {
|
|
if (get_byte(pkg, &ofs) == -1)
|
|
goto fail;
|
|
if (get_byte(pkg, &ofs) == -1)
|
|
goto fail;
|
|
}
|
|
|
|
/*
|
|
* Done parsing the ZIP header. Spkgt the inflation engine.
|
|
*/
|
|
error = inflateInit2(&pkg->pkg_zs, -15);
|
|
if (error != Z_OK)
|
|
goto fail;
|
|
|
|
*pp = pkg;
|
|
return (0);
|
|
|
|
fail:
|
|
free(pkg);
|
|
return (error);
|
|
}
|
|
|
|
static struct tarfile *
|
|
scan_tarfile(struct package *pkg, struct tarfile *last)
|
|
{
|
|
char buf[512];
|
|
struct tarfile *cur;
|
|
off_t ofs;
|
|
size_t sz;
|
|
|
|
cur = (last != NULL) ? last->tf_next : pkg->pkg_first;
|
|
if (cur == NULL) {
|
|
ofs = (last != NULL) ? last->tf_ofs + last->tf_size :
|
|
pkg->pkg_ofs;
|
|
ofs = (ofs + 0x1ff) & ~0x1ff;
|
|
|
|
/* Check if we've reached EOF. */
|
|
if (ofs < pkg->pkg_ofs) {
|
|
errno = ENOSPC;
|
|
return (NULL);
|
|
}
|
|
|
|
if (ofs != pkg->pkg_ofs) {
|
|
if (last != NULL && pkg->pkg_ofs == last->tf_ofs) {
|
|
if (cache_data(last) == -1)
|
|
return (NULL);
|
|
} else {
|
|
sz = ofs - pkg->pkg_ofs;
|
|
while (sz != 0) {
|
|
if (sz > sizeof(buf))
|
|
sz = sizeof(buf);
|
|
if (get_zipped(pkg, buf, sz) == -1)
|
|
return (NULL);
|
|
sz = ofs - pkg->pkg_ofs;
|
|
}
|
|
}
|
|
}
|
|
|
|
cur = malloc(sizeof(*cur));
|
|
if (cur == NULL)
|
|
return (NULL);
|
|
memset(cur, 0, sizeof(*cur));
|
|
cur->tf_pkg = pkg;
|
|
|
|
while (1) {
|
|
if (get_zipped(pkg, &cur->tf_hdr,
|
|
sizeof(cur->tf_hdr)) == -1) {
|
|
free(cur);
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* There are always 2 empty blocks appended to
|
|
* a PKG. It marks the end of the archive.
|
|
*/
|
|
if (strncmp(cur->tf_hdr.ut_magic, "ustar", 5) != 0) {
|
|
free(cur);
|
|
errno = ENOSPC;
|
|
return (NULL);
|
|
}
|
|
|
|
cur->tf_ofs = pkg->pkg_ofs;
|
|
cur->tf_size = pkg_atol(cur->tf_hdr.ut_size,
|
|
sizeof(cur->tf_hdr.ut_size));
|
|
|
|
if (cur->tf_hdr.ut_name[0] != '+')
|
|
break;
|
|
|
|
/*
|
|
* Skip package meta-files.
|
|
*/
|
|
ofs = cur->tf_ofs + cur->tf_size;
|
|
ofs = (ofs + 0x1ff) & ~0x1ff;
|
|
while (pkg->pkg_ofs < ofs) {
|
|
if (get_zipped(pkg, buf, sizeof(buf)) == -1) {
|
|
free(cur);
|
|
return (NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (last != NULL)
|
|
last->tf_next = cur;
|
|
else
|
|
pkg->pkg_first = cur;
|
|
pkg->pkg_last = cur;
|
|
}
|
|
|
|
return (cur);
|
|
}
|