2019-03-08 18:18:31 +01:00
|
|
|
/* SPDX-License-Identifier: ISC */
|
2019-01-19 17:26:41 +01:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
2019-04-05 12:33:50 +02:00
|
|
|
#include "pkg/pkgwriter.h"
|
2019-02-04 15:21:19 +01:00
|
|
|
#include "util/util.h"
|
2019-01-19 17:26:41 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-02-15 09:05:12 +01:00
|
|
|
pkg_writer_t *pkg_writer_open(const char *path, bool force)
|
2019-01-19 17:26:41 +01:00
|
|
|
{
|
|
|
|
pkg_writer_t *wr = calloc(1, sizeof(*wr));
|
2019-01-28 20:01:36 +01:00
|
|
|
compressor_t *cmp;
|
2019-02-15 09:05:12 +01:00
|
|
|
int flags;
|
2019-01-28 20:01:36 +01:00
|
|
|
|
|
|
|
cmp = compressor_by_id(PKG_COMPRESSION_NONE);
|
|
|
|
if (cmp == NULL) {
|
|
|
|
fputs("missing built in dummy compessor\n", stderr);
|
|
|
|
return NULL;
|
|
|
|
}
|
2019-01-19 17:26:41 +01:00
|
|
|
|
|
|
|
if (wr == NULL) {
|
|
|
|
fputs("out of memory\n", stderr);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-04-17 23:56:39 +02:00
|
|
|
wr->stream = cmp->compression_stream(cmp, NULL);
|
2019-01-28 20:01:36 +01:00
|
|
|
if (wr->stream == NULL) {
|
|
|
|
fputs("error creating compressor stream for package header\n",
|
|
|
|
stderr);
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
2019-02-15 09:05:12 +01:00
|
|
|
flags = O_WRONLY | O_CREAT;
|
|
|
|
|
|
|
|
if (force) {
|
|
|
|
flags |= O_TRUNC;
|
|
|
|
} else {
|
|
|
|
flags |= O_EXCL;
|
|
|
|
}
|
|
|
|
|
|
|
|
wr->fd = open(path, flags, 0644);
|
2019-01-19 17:26:41 +01:00
|
|
|
if (wr->fd == -1) {
|
|
|
|
perror(path);
|
2019-01-28 20:01:36 +01:00
|
|
|
goto fail_stream;
|
2019-01-19 17:26:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
wr->path = path;
|
|
|
|
|
|
|
|
wr->current.magic = PKG_MAGIC_HEADER;
|
|
|
|
wr->current.compression = PKG_COMPRESSION_NONE;
|
2019-01-28 20:01:36 +01:00
|
|
|
if (write_header(wr))
|
|
|
|
goto fail_close;
|
2019-01-19 17:26:41 +01:00
|
|
|
|
|
|
|
return wr;
|
2019-01-28 20:01:36 +01:00
|
|
|
fail_close:
|
|
|
|
close(wr->fd);
|
|
|
|
fail_stream:
|
|
|
|
wr->stream->destroy(wr->stream);
|
|
|
|
fail:
|
|
|
|
free(wr);
|
|
|
|
return NULL;
|
2019-01-19 17:26:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2019-04-17 23:56:39 +02:00
|
|
|
wr->stream = cmp->compression_stream(cmp, NULL);
|
2019-01-19 17:26:41 +01:00
|
|
|
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;
|
|
|
|
}
|