pkg-utils/main/pkgwriter.c

199 lines
3.5 KiB
C

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include "pkgwriter.h"
#include "util/util.h"
struct pkg_writer_t {
const char *path;
int fd;
off_t start;
record_t current;
compressor_stream_t *stream;
};
static int write_header(pkg_writer_t *wr)
{
ssize_t ret;
wr->current.magic = htole32(wr->current.magic);
wr->current.compressed_size = htole64(wr->current.compressed_size);
wr->current.raw_size = htole64(wr->current.raw_size);
ret = write_retry(wr->fd, &wr->current, sizeof(wr->current));
if (ret < 0)
goto fail_fop;
if ((size_t)ret < sizeof(wr->current)) {
fprintf(stderr,
"record header was truncated while writing to %s\n",
wr->path);
return -1;
}
return 0;
fail_fop:
perror(wr->path);
return -1;
}
static int flush_to_file(pkg_writer_t *wr)
{
uint8_t buffer[2048];
ssize_t count, ret;
for (;;) {
count = wr->stream->read(wr->stream, buffer, sizeof(buffer));
if (count == 0)
break;
if (count < 0) {
fprintf(stderr, "%s: error compressing data\n",
wr->path);
return -1;
}
ret = write_retry(wr->fd, buffer, count);
if (ret < 0) {
fprintf(stderr, "%s: writing to package file: %s\n",
wr->path, strerror(errno));
return -1;
}
if (ret < count) {
fprintf(stderr,
"%s: data written to file was truncated\n",
wr->path);
return -1;
}
wr->current.compressed_size += count;
}
return 0;
}
pkg_writer_t *pkg_writer_open(const char *path)
{
pkg_writer_t *wr = calloc(1, sizeof(*wr));
compressor_t *cmp;
cmp = compressor_by_id(PKG_COMPRESSION_NONE);
if (cmp == NULL) {
fputs("missing built in dummy compessor\n", stderr);
return NULL;
}
if (wr == NULL) {
fputs("out of memory\n", stderr);
return NULL;
}
wr->stream = cmp->compression_stream(cmp);
if (wr->stream == NULL) {
fputs("error creating compressor stream for package header\n",
stderr);
goto fail;
}
wr->fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644);
if (wr->fd == -1) {
perror(path);
goto fail_stream;
}
wr->path = path;
wr->current.magic = PKG_MAGIC_HEADER;
wr->current.compression = PKG_COMPRESSION_NONE;
if (write_header(wr))
goto fail_close;
return wr;
fail_close:
close(wr->fd);
fail_stream:
wr->stream->destroy(wr->stream);
fail:
free(wr);
return NULL;
}
void pkg_writer_close(pkg_writer_t *wr)
{
close(wr->fd);
free(wr);
}
int pkg_writer_start_record(pkg_writer_t *wr, uint32_t magic,
compressor_t *cmp)
{
wr->start = lseek(wr->fd, 0, SEEK_CUR);
if (wr->start == -1) {
perror(wr->path);
return -1;
}
memset(&wr->current, 0, sizeof(wr->current));
if (write_header(wr))
return -1;
wr->stream = cmp->compression_stream(cmp);
if (wr->stream == NULL)
return -1;
wr->current.magic = magic;
wr->current.compression = cmp->id;
return 0;
}
int pkg_writer_write_payload(pkg_writer_t *wr, void *data, size_t size)
{
ssize_t ret;
while (size > 0) {
ret = wr->stream->write(wr->stream, data, size);
if (ret < 0)
return -1;
if ((size_t)ret < size) {
if (flush_to_file(wr))
return -1;
}
data = (char *)data + ret;
size -= ret;
wr->current.raw_size += ret;
}
return 0;
}
int pkg_writer_end_record(pkg_writer_t *wr)
{
wr->stream->flush(wr->stream);
if (flush_to_file(wr))
return -1;
if (lseek(wr->fd, wr->start, SEEK_SET) == -1)
goto fail_seek;
if (write_header(wr))
return -1;
if (lseek(wr->fd, 0, SEEK_END) == -1)
goto fail_seek;
wr->stream->destroy(wr->stream);
wr->stream = NULL;
return 0;
fail_seek:
perror(wr->path);
return -1;
}