1
0
Fork 0
mirror of https://github.com/pygos/pkg-utils.git synced 2024-05-17 03:36:14 +02:00
pkg-utils/lib/pkg/pkgreader.c
David Oberhollenzer 96d4d353d4 Remove unnecessary flush from unpacking loop
The uncompressor will know when end of file is reached. Flushing
prematurely won't help. Especially the way the loop was constructed,
using an external buffer that is a lot larger than the internal
buffer of the compressor will cause multiple erroneous flushes.

Signed-off-by: David Oberhollenzer <goliath@infraroot.at>
2019-04-18 14:08:43 +02:00

299 lines
5.6 KiB
C

/* SPDX-License-Identifier: ISC */
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include "comp/compressor.h"
#include "util/util.h"
#include "pkg/pkgreader.h"
struct pkg_reader_t {
int fd;
bool have_eof;
bool have_error;
uint64_t offset_compressed;
uint64_t offset_raw;
compressor_stream_t *stream;
const char *path;
record_t current;
};
static int read_header(pkg_reader_t *rd)
{
ssize_t diff = read_retry(rd->fd, &rd->current, sizeof(rd->current));
if (diff == 0) {
rd->have_eof = true;
return 0;
}
if (diff < 0)
goto fail_io;
if ((size_t)diff < sizeof(rd->current))
goto fail_trunc;
rd->offset_raw = 0;
rd->offset_compressed = 0;
rd->current.magic = le32toh(rd->current.magic);
rd->current.compressed_size = le64toh(rd->current.compressed_size);
rd->current.raw_size = le64toh(rd->current.raw_size);
return 1;
fail_trunc:
rd->have_error = true;
fprintf(stderr, "%s: package file seems to be truncated\n", rd->path);
return -1;
fail_io:
rd->have_error = true;
fprintf(stderr, "%s: reading from package file: %s\n",
rd->path, strerror(errno));
return -1;
}
static int prefetch_compressed(pkg_reader_t *rd)
{
uint8_t buffer[1024];
compressor_t *cmp;
ssize_t ret;
size_t diff;
if (rd->stream == NULL) {
cmp = compressor_by_id(rd->current.compression);
if (cmp == NULL)
goto fail_comp;
rd->stream = cmp->uncompression_stream(cmp);
if (rd->stream == NULL) {
rd->have_error = true;
return -1;
}
}
while (rd->offset_compressed < rd->current.compressed_size) {
diff = rd->current.compressed_size - rd->offset_compressed;
if (diff > sizeof(buffer))
diff = sizeof(buffer);
ret = read_retry(rd->fd, buffer, diff);
if (ret < 0)
goto fail_io;
if ((size_t)ret < diff)
goto fail_trunc;
ret = rd->stream->write(rd->stream, buffer, diff);
if (ret < 0)
return -1;
rd->offset_compressed += ret;
if ((size_t)ret < diff) {
if (lseek(rd->fd, -((long)(diff - ret)),
SEEK_CUR) == -1)
goto fail_io;
break;
}
}
return 0;
fail_trunc:
rd->have_error = true;
fprintf(stderr, "%s: truncated record in package file\n", rd->path);
return -1;
fail_io:
rd->have_error = true;
fprintf(stderr, "%s: reading from package file: %s\n",
rd->path, strerror(errno));
return -1;
fail_comp:
fprintf(stderr, "%s: package uses unsupported compression\n",
rd->path);
rd->have_error = true;
return -1;
}
static pkg_reader_t *pkg_reader_openat(int dirfd, const char *path)
{
pkg_reader_t *rd = calloc(1, sizeof(*rd));
int ret;
if (rd == NULL) {
fputs("out of memory\n", stderr);
return NULL;
}
rd->path = path;
rd->fd = openat(dirfd, path, O_RDONLY);
if (rd->fd < 0) {
perror(path);
free(rd);
return NULL;
}
ret = read_header(rd);
if (ret < 0)
goto fail;
if (ret == 0 || rd->current.magic != PKG_MAGIC_HEADER)
goto fail_header;
return rd;
fail_header:
fprintf(stderr, "%s: missing package header\n", path);
fail:
pkg_reader_close(rd);
return NULL;
}
pkg_reader_t *pkg_reader_open(const char *path)
{
return pkg_reader_openat(AT_FDCWD, path);
}
pkg_reader_t *pkg_reader_open_repo(int dirfd, const char *name)
{
char *fname;
size_t i;
for (i = 0; name[i] != '\0'; ++i) {
if (!isalnum(name[i]) && name[i] != '_' && name[i] != '-' &&
name[i] != '+') {
fprintf(stderr,
"illegal characters in package name '%s'\n",
name);
return NULL;
}
}
fname = alloca(strlen(name) + 5);
sprintf(fname, "%s.pkg", name);
return pkg_reader_openat(dirfd, fname);
}
void pkg_reader_close(pkg_reader_t *rd)
{
if (rd->stream != NULL)
rd->stream->destroy(rd->stream);
close(rd->fd);
free(rd);
}
int pkg_reader_get_next_record(pkg_reader_t *rd)
{
uint64_t skip;
int ret;
if (rd->have_eof)
return 0;
if (rd->have_error)
return -1;
skip = rd->current.compressed_size - rd->offset_compressed;
if (lseek(rd->fd, skip, SEEK_CUR) == -1)
goto fail_io;
if (rd->stream != NULL) {
rd->stream->destroy(rd->stream);
rd->stream = NULL;
}
ret = read_header(rd);
if (ret <= 0)
return ret;
if (rd->current.magic == PKG_MAGIC_HEADER)
goto fail_second_hdr;
return 1;
fail_io:
rd->have_error = true;
fprintf(stderr, "%s: reading from packet file: %s\n",
rd->path, strerror(errno));
return -1;
fail_second_hdr:
rd->have_error = true;
fprintf(stderr, "%s: found extra header record inside package\n",
rd->path);
return -1;
}
record_t *pkg_reader_current_record_header(pkg_reader_t *rd)
{
return (rd->have_error || rd->have_eof) ? NULL : &rd->current;
}
ssize_t pkg_reader_read_payload(pkg_reader_t *rd, void *out, size_t size)
{
ssize_t ret, total = 0;
do {
if (rd->have_error)
return -1;
if (rd->have_eof || rd->offset_raw == rd->current.raw_size)
break;
if (prefetch_compressed(rd))
return -1;
ret = rd->stream->read(rd->stream, out, size);
if (ret < 0)
return -1;
rd->offset_raw += ret;
out = (char *)out + ret;
size -= ret;
total += ret;
} while (size > 0);
return total;
}
int pkg_reader_rewind(pkg_reader_t *rd)
{
int ret;
if (lseek(rd->fd, 0, SEEK_SET) == -1) {
perror(rd->path);
return -1;
}
if (rd->stream != NULL) {
rd->stream->destroy(rd->stream);
rd->stream = NULL;
}
rd->have_eof = false;
rd->have_error = false;
ret = read_header(rd);
if (ret < 0)
goto fail;
if (ret == 0 || rd->current.magic != PKG_MAGIC_HEADER)
goto fail_header;
return 0;
fail_header:
fprintf(stderr, "%s: missing package header\n", rd->path);
fail:
rd->have_error = true;
return -1;
}
const char *pkg_reader_get_filename(pkg_reader_t *rd)
{
return rd->path;
}